
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/">
<channel>
 <title>NintendoAge</title>
 <description></description>
 <link>http://www.nintendoage.com/forum/index.cfm?forumid=1</link>
 <generator>-Sqooner</generator>

	<item>
		<title>Nightman vs. Justice Incarnate</title>
		<link>http://www.nintendoage.com/forum/messageview.cfm?catid=22&amp;threadid=25663</link>
		<pubDate>2009-11-12T10:38:05 -05.0 0</pubDate>
		<dc:creator>Mario's Right Nut</dc:creator>
			<slash:comments>29</slash:comments>
		<description><![CDATA[ <p>Lookin' good.&nbsp; Don't steal it or I won't post any more as I make progress.&nbsp; Don't ruin it for everyone.&nbsp; Should work up through the neck of the ship.&nbsp; Still needs some debugging.&nbsp; Use Start or Select to open the doors.</p><p>Thanks to all the people who have been helping me along!&nbsp; You guys rock.</p> ]]></description>
	</item>

	<item>
		<title>Idea for a new game</title>
		<link>http://www.nintendoage.com/forum/messageview.cfm?catid=22&amp;threadid=25608</link>
		<pubDate>2009-11-10T17:09:41 -05.0 0</pubDate>
		<dc:creator>Benihana</dc:creator>
			<slash:comments>24</slash:comments>
		<description><![CDATA[ Think it would be possible to do on top scroller type game like these? I mean you could use a certain button/d-pad combo for each chord...With the huge music craze now aday's I think an 8 bit version would be a total blast, with 80's songs only...Espicially if it is challanging with only the D-pad and the A/B buttons...<br />I know the music would be pretty weak, but hey if Journey had an Atari game, then why not on the NES?<br />What do you think?<br /> ]]></description>
	</item>

	<item>
		<title>Nerdy Nights Sound: Part 8</title>
		<link>http://www.nintendoage.com/forum/messageview.cfm?catid=22&amp;threadid=25583</link>
		<pubDate>2009-11-10T07:13:52 -05.0 0</pubDate>
		<dc:creator>MetalSlime</dc:creator>
			<slash:comments>3</slash:comments>
		<description><![CDATA[ <b>Last Week</b>: <a href="http://nintendoage.com/forum/messageview.cfm?catid=22&threadid=25253">Volume Envelopes</a><br />
<br />
<b>This Week</b>: Opcodes and Looping<br />
<br />
<font size="5"><b>Opcodes</b></font><br />
<br />So far our sound engine handles two type of data that it reads from
music data streams: notes and note lengths.&nbsp; This is enough to write
complex music but of course we are going to want more features.&nbsp; We
will want control over the sound of our notes.&nbsp; What if we want to
change duty cycles midstream?&nbsp; Or volume envelopes?&nbsp; Or keys?&nbsp; What if
we want to loop one part of the song four times?&nbsp; Or loop the entire
song continuously?&nbsp; What if we want to play a sound effect as part of a
song?<br /><br />All of these types of features, features where you are
issuing commands to the engine, are going to be done through opcodes
(also called control codes or command codes).&nbsp; An <b>opcode</b> is a
value in the data stream that tells the engine to run a specific,
specialized subroutine or piece of code.&nbsp; Most opcodes will have <b>arguments</b>
sent along with them.&nbsp; For example, an opcode that changes a stream's
volume envelope will come with an argument that specifies which volume
envelope to change to.<br /><br />We've actually been using an opcode for
weeks, I just haven't mentioned it.&nbsp; It's the opcode that ends a sound,
and we've been encoding it in our data streams as $FF.&nbsp; Here is the
code we've been using:<br /><br /><span style="font-family: Courier New;">se_fetch_byte:</span><br style="font-family: Courier New;" /><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; ;---snip--- (fetch a byte and range test)</span><br style="font-family: Courier New;" /><br style="font-family: Courier New;" /><span style="font-family: Courier New;">.opcode:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;else it's an opcode</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; ;do Opcode stuff</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; cmp #$FF</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; bne .end</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda stream_status, x&nbsp;&nbsp;&nbsp; ;if $FF, end of stream, so disable it and silence</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; and #%11111110</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sta stream_status, x&nbsp;&nbsp;&nbsp; ;clear enable flag in status byte</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda stream_channel, x</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; cmp #TRIANGLE</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; beq .silence_tri&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;triangle is silenced differently from squares and noise</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda #$30&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;squares and noise silenced with #$30</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; bne .silence</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">.silence_tri:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda #$80&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;triangle silenced with #$80</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">.silence:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sta stream_vol_duty, x&nbsp; ;store silence value in the stream's volume variable.</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; jmp .update_pointer&nbsp;&nbsp;&nbsp;&nbsp; ;done</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; ;---snip--- (do note lengths and notes, update the stream's pointer)</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; rts</span><br />&nbsp;&nbsp; &nbsp;<br />Here we check if the byte read has a value of $FF.&nbsp; If so we turn the stream off and silence it.&nbsp; That's an opcode.<br /><br />It
would be pretty messy if every opcode we had was just written straight
out like this.&nbsp; Normally we would pull this code into its own
subroutine, like this:<br /><br /><span style="font-family: Courier New;">se_fetch_byte:</span><br style="font-family: Courier New;" /><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; ;---snip--- (fetch a byte and range test)</span><br style="font-family: Courier New;" /><br style="font-family: Courier New;" /><span style="font-family: Courier New;">.opcode:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;else it's an opcode</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; ;do Opcode stuff</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; cmp #$FF&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;end sound opcode</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; bne .end</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;</span><span style="color: rgb(255, 0, 0); font-family: Courier New;">jsr se_op_endsound&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;call the endsound subroutine</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; iny</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; jmp .fetch&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;grab the next byte in the stream.</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; ;---snip--- (do note lengths and notes, update the stream's pointer)</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; rts</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">se_op_endsound:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda stream_status, x&nbsp;&nbsp;&nbsp; ;end of stream, so disable it and silence</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; and #%11111110</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sta stream_status, x&nbsp;&nbsp;&nbsp; ;clear enable flag in status byte</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda stream_channel, x</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; cmp #TRIANGLE</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; beq .silence_tri&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;triangle is silenced differently from squares and noise</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda #$30&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;squares and noise silenced with #$30</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; bne .silence</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">.silence_tri:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda #$80&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;triangle silenced with #$80</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">.silence:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sta stream_vol_duty, x&nbsp; ;store silence value in the stream's volume variable.</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; rts</span><br />&nbsp;&nbsp; &nbsp;<br />The .opcode branch is much shorter now.&nbsp; If we wanted to add more opcodes, we could just add some more compares:<br /><br /><span style="font-family: Courier New;">.opcode:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; ;do Opcode stuff</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; cmp #$FF&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;is it the end sound opcode? </span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; bne .not_FF</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; <span style="color: rgb(255, 0, 0);">jsr se_op_endsound&nbsp; ;if so, call the end sound subroutine</span></span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; jmp .end&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;and finish</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">.not_FF:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; cmp #$FE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;else is it the loop opcode?</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; bne .not_FE</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; <span style="color: rgb(255, 0, 0);">jsr se_op_loop&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;if so, call the loop subroutine</span></span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; jmp .opcode_done</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">.not_FE:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; cmp #$FD&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;else is it the change volume envelope opcode?</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; bne .not_FD</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; <span style="color: rgb(255, 0, 0);">jsr se_op_change_ve ;if so, call the change volume envelope subroutine</span></span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; jmp .opcode_done</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">.not_FD:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">.opcode_done:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; iny&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;update index to next byte in the data stream</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; jmp .fetch&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;go fetch another byte</span><br />&nbsp;&nbsp; &nbsp;<br /><br />This
will work, but it's ugly.&nbsp; The more opcodes we add to our engine, the
more checks we need to make.&nbsp; What if we have 20 opcodes?&nbsp; Do we really
want to do that many compares?&nbsp; It's a waste of ROM space and cycles.<br /><br /><b>Tables</b><br />Anytime you find yourself in a situation where you are doing a lot of CMPs on one value, the answer is to use a <b>lookup tabl</b>e.&nbsp;
It will simplify everything!&nbsp; We've done it already with notes, note
lengths, song numbers and volume envelopes.&nbsp; Could you imagine trying
to get a note's period without using the lookup table?&nbsp; It would look
like this:<br /><br /><span style="font-family: Courier New;">Is the note an A1?&nbsp; If so, use this period, else</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">Is the note an A#1?&nbsp; If so, use this period, else</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">Is the note a B1?&nbsp; If so, use this period, else</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">Is the note a C2?&nbsp; If so, use this period, else</span><br style="font-family: Courier New;" /><b style="color: rgb(255, 0, 0);"><span style="font-family: Courier New;">... (about 100 more checks)</span></b><br style="font-family: Courier New;" /><span style="font-family: Courier New;">Is the note an F#9? If so, use this period, else</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">Is the note a rest?&nbsp; If so, use this period</span><br /><br />That's
just crazy.&nbsp; It would be hundreds of lines of unreadable code and you'd
run into branch-range errors too.&nbsp; When we use a lookup table, the code
is simplified to this:<br /><br style="font-family: Courier New;" /><span style="font-family: Courier New;">.note:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; ;do Note stuff</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sty sound_temp1&nbsp;&nbsp;&nbsp;&nbsp; ;save our index</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; asl a</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; tay</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda note_table, y</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sta stream_note_LO, x</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda note_table+1, y</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sta stream_note_HI, x</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; ldy sound_temp1&nbsp;&nbsp;&nbsp;&nbsp; ;restore data stream index</span><br />&nbsp;&nbsp; &nbsp;<br />Much cleaner.&nbsp; Again, I can't stress it enough: <b>if you find yourself doing lots of CMPs on a single value, use a table instead!</b><br />&nbsp;&nbsp; &nbsp;<br />With notes and note lengths we used a straight <i>lookup table</i> of values.&nbsp; With song numbers and volume envelopes we used a special type of lookup table called a <i>pointer table</i>, which stored data addresses.&nbsp; For opcodes we have two choices.&nbsp; We can use something called a <b>jump table</b> or we can use an <b>RTS table</b>.&nbsp;
They are almost the same and the difference in performance between the
two methods is negligible so for most programmers it's a matter of
personal preference. &nbsp;<br /><br />I prefer RTS tables myself, but we're going to use jump tables because they are easier to explain and understand.<br /><br /><b>Jump Tables</b><br />Ok,
here's our problem:&nbsp; Our sound engine has opcodes.&nbsp; A lot of them,
let's say 10 or more.&nbsp; Each opcode has its own subroutine.&nbsp; When our
sound engine reads an opcode byte from the data stream, we want to
avoid a long list of CMP and BNE instructions to select the right
subroutine.&nbsp; How do we do that?&nbsp;&nbsp; We use a jump table.<br /><br />A jump
table is similar to a pointer table: it is a table of addresses.&nbsp; But
whereas a pointer table holds addresses that point to the start of
data, a <b>jump table</b> holds addresses that point to the start of <i>code</i> (ie, the start of subroutines).&nbsp; For example, suppose we have some subroutines:<br /><br /><span style="font-family: Courier New;">sub_a:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda #$00</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; ldx #$FF</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; rts</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">sub_b:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; clc</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; adc #$03</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; rts</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">sub_c:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sec</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sbc #$03</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; rts</span><br />&nbsp;&nbsp; &nbsp;<br />Here is how a jump table would look using these subroutines:<br /><br /><span style="font-family: Courier New;">sub_jump_table:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .word sub_a, sub_b, sub_c</span><br />&nbsp;&nbsp; &nbsp;<br />Hey,
that's pretty easy.&nbsp; We just use the subroutine label and the assembler
will translate that into the address where the subroutine starts.&nbsp;
Let's make a jump table for our sound opcode subroutines:<br /><br /><span style="font-family: Courier New;">se_op_endsound:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; ;do stuff</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; rts</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">se_op_infinite_loop:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; ;do stuff</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; rts</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">se_op_change_ve:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; ;do stuff</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; rts</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">;etc..&nbsp; more subroutines</span><br style="font-family: Courier New;" /><br />
<span style="font-family: Courier New;">;this is our jump table</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">sound_opcodes:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .word se_op_endsound</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .word se_op_infinite_loop</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .word se_op_change_ve</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; ;etc, one entry per subroutine</span><br />&nbsp;&nbsp; &nbsp;<br />Cool.&nbsp; We have a jump table now.&nbsp; So how do we use it?<br /><br /><b>Indirect Jumping</b><br />The 6502 let's us do some cool things.&nbsp; One of those things is called an indirect jump.&nbsp; An <b>indirect jump</b> let's you stick a destination address into a zero-page pointer variable and jump there.&nbsp; It works like this:<br /><br /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .rsset $0000</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">;first declare a pointer variable somewhere in the zero-page</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">jmp_ptr .rs 2&nbsp; ;2 bytes because an address is always a word</span><br style="font-family: Courier New;" /><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda #$00</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sta jmp_ptr</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda #$80</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sta jmp_ptr+1</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; jmp [jmp_ptr] ;will jump to $8000</span><br />&nbsp;&nbsp; &nbsp;<br />Here we stick an address ($8000, lo byte first) into our <b>jmp_ptr</b> variable.&nbsp; Then we do an indirect jump by using the JMP instruction followed by a pointer variable in brackets:<br /><br /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; jmp [jmp_ptr] ;indirect jump</span><br />&nbsp;&nbsp; &nbsp;<br />This
instruction translates into English as "Jump to the address that is
stored in jmp_ptr and jmp_ptr+1".&nbsp; It's extrememly useful.&nbsp; We can
stick any address we want in there:<br />&nbsp;&nbsp; &nbsp;<br /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda #$00</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sta jmp_ptr</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda #$C0</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sta jmp_ptr+1</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; jmp [jmp_ptr] ;will jump to $C000</span><br />&nbsp;&nbsp; &nbsp;<br />We could read an address from ROM and use that if we wanted to, for example our reset vector:<br />&nbsp;&nbsp; &nbsp;<br /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda $FFFC</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sta jmp_ptr</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda $FFFD</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sta jmp_ptr+1</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; jmp [jmp_ptr] ;will jump to our reset routine</span><br />&nbsp;&nbsp; &nbsp;<br />And we can use it in combination with our jump table:<br /><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda sound_opcodes, y&nbsp;&nbsp;&nbsp; ;read low byte of address from jump table</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sta jmp_ptr</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda sound_opcodes+1, y&nbsp; ;read high byte</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sta jmp_ptr+1</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; jmp [jmp_ptr]&nbsp;&nbsp; ;will jump to whatever address we pulled from the table.</span><br />&nbsp;&nbsp; &nbsp;<br />Pretty powerful.&nbsp; We can dynamically jump to any section of code we want!<br />&nbsp;&nbsp; &nbsp;<br /><b>Implementation</b><br />So
we know how to build a jump table and we know how to do an indirect
jump.&nbsp; Let's tie it all together and stick it into our sound engine.&nbsp;
Let's start with <b>se_fetch_byte</b>.&nbsp; se_fetch_byte reads a byte
from the data stream and range-checks it to see if it is a note, note
length or opcode.&nbsp; Recall that notes have a byte range of $00-$7F.&nbsp;
Note lengths have a range of $80-$9F.&nbsp; The opcode byte range is $A0-$FF:<br /><br style="font-family: Courier New;" /><span style="font-family: Courier New;">se_fetch_byte:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda stream_ptr_LO, x</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sta sound_ptr</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda stream_ptr_HI, x</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sta sound_ptr+1</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; ldy #$00</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda [sound_ptr], y</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; bpl .note&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;if &lt; #$80, it's a Note</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; cmp #$A0</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; bcc .note_length&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;else if &lt; #$A0, it's a Note Length</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">.opcode:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: rgb(255, 0, 0); font-family: Courier New;">;else ($A0-$FF) it's an opcode</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; ;do Opcode stuff</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">.note_length:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; ;do note length stuff</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">.note:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; ;do note stuff</span><br />&nbsp;&nbsp; &nbsp;<br />So
we need to assign our opcodes to values between $A0 and $FF.&nbsp; Just as
with notes and note lengths, the opcode byte we read from the data
stream will be used as a table index (after subtracting $A0), so we
will assign our opcodes in the same order as our table:<br /><br /><span style="font-family: Courier New;">sound_opcodes:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .word se_op_endsound&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;this should be $A0</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .word se_op_infinite_loop&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;this should be $A1</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .word se_op_change_ve&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;this should be $A2</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; ;etc, 1 entry per subroutine</span><br style="font-family: Courier New;" /><br style="font-family: Courier New;" /><span style="font-family: Courier New;">;these are aliases to use in the sound data.</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">endsound = $A0</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">loop = $A1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;be careful of conflicts here.&nbsp; this might be too generic.&nbsp; maybe song_loop is better</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">volume_envelope = $A2</span><br /><br />Now let's alter se_fetch_byte to take care of our opcodes:<br /><br /><span style="font-family: Courier New;">se_fetch_byte:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda stream_ptr_LO, x</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sta sound_ptr</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda stream_ptr_HI, x</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sta sound_ptr+1</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; ldy #$00</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">.fetch:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda [sound_ptr], y</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; bpl .note&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;if &lt; #$80, it's a Note</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; cmp #$A0</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; bcc .note_length&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;else if &lt; #$A0, it's a Note Length</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">.opcode:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;else ($A0-$FF) it's an opcode</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; <span style="color: rgb(255, 0, 0);">;do Opcode stuff</span></span><br style="font-family: Courier New; color: rgb(255, 0, 0);" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">&nbsp;&nbsp;&nbsp; jsr se_opcode_launcher&nbsp;&nbsp; ;launch our opcode!!!</span><br style="font-family: Courier New; color: rgb(255, 0, 0);" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">&nbsp;&nbsp;&nbsp; iny&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;next position in the data stream</span><br style="font-family: Courier New; color: rgb(255, 0, 0);" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">&nbsp;&nbsp;&nbsp; lda stream_status, x</span><br style="font-family: Courier New; color: rgb(255, 0, 0);" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">&nbsp;&nbsp;&nbsp; and #%00000001</span><br style="font-family: Courier New; color: rgb(255, 0, 0);" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">&nbsp;&nbsp;&nbsp; bne .fetch&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;after our opcode is done, grab another byte unless the stream is disabled</span><br style="font-family: Courier New; color: rgb(255, 0, 0);" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">&nbsp;&nbsp;&nbsp; rts&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; in which case we quit&nbsp; (explained below)</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">.note_length:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; ;do note length stuff</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">.note:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; ;do note stuff</span><br />&nbsp;&nbsp; &nbsp;<br />I
added a call to a subroutine called se_opcode_launcher and a little
branch.&nbsp; Not a big change is it?&nbsp; But there's an important detail
here.&nbsp; se_opcode_launcher will be a short, simple subroutine that will
read from the jump table and perform an indirect jump.&nbsp; It looks like
this:<br /><br /><span style="font-family: Courier New;">se_opcode_launcher:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sty sound_temp1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;save y register, because we are about to destroy it</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sec</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sbc #$A0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;turn our opcode byte into a table index by subtracting $A0</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp;&nbsp; $A0-&gt;$00, $A1-&gt;$01, $A2-&gt;$02, etc.&nbsp; Tables index from $00.</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; asl a&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;multiply by 2 because we index into a table of addresses (words)</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; tay</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda sound_opcodes, y&nbsp;&nbsp;&nbsp; ;get low byte of subroutine address</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sta jmp_ptr</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda sound_opcodes+1, y&nbsp; ;get high byte</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sta jmp_ptr+1</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; ldy sound_temp1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;restore our y register</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; iny&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;set to next position in data stream (assume an argument)</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; jmp [jmp_ptr]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;indirect jump to our opcode subroutine</span><br />&nbsp;&nbsp; &nbsp;<br />Short
and simple.&nbsp; So why did I wrap this code in its own subroutine?&nbsp; Why
not just stick this code as-is in the .opcode branch of se_fetch_byte?&nbsp;
Because we need a place to return to.<br /><br />The JSR and RTS
instructions work as a pair.&nbsp; They go hand in hand.&nbsp; They need each
other.&nbsp; Without going into too much detail, this is what goes on behind
the scenes:<br />
<br />
<b>JSR</b> sticks a return address on the stack and jumps to a
subroutine.&nbsp; One way to look at it is to think of JSR as a JMP that
remembers where it started from.<br />
<b>RTS</b> pops the return address off the stack and jumps there.&nbsp; <br />
<br />
So JSR leaves a treasure map for RTS to pick up and follow later.&nbsp; The <b>key point</b> here is that <b>RTS expects a return address to be waiting for it on the stack</b>. &nbsp;<br /><br />Now our opcode subroutines all end in an RTS instruction.&nbsp; Do you see the potential problem here?<br /><br />We
call our opcode subroutines using an indirect jump.&nbsp; This requires us
to use a JMP instruction, not a JSR instruction.&nbsp; A JMP instruction
doesn't remember where it started from.&nbsp; No return address is pushed
onto the stack with a JMP instruction.&nbsp; So when we jump to our opcode
subroutine and hit the RTS instruction at the end, there is no return
address waiting for us!&nbsp; The RTS will pull whatever random values
happen to be on the stack at the time and jump there.&nbsp; We'll end up
somewhere random and our program will surely crash!<br /><br />To fix this,
we wrap our indirect jump in a subroutine, se_opcode_launcher.&nbsp; We call
it with a JSR instruction, completing the JSR/RTS pair:<br /><br /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; jsr se_opcode_launcher&nbsp; ;this jsr will let us remember where we came from</span><br />&nbsp;&nbsp; &nbsp;<br />This
JSR instruction will stick a return address on the stack for us.&nbsp; Then
inside se_opcode_launcher we perform our indirect jump to our desired
opcode subroutine.&nbsp; Now when we hit that RTS instruction at the end of
the opcode subroutine we have a return address waiting for us on the
stack.&nbsp; Our program returns back to where we started.&nbsp; We are safe.<br /><font size="4"><b><br />
Opcode Subroutines</b></font><br />With our opcode launcher written, we are all set up to make opcodes.&nbsp; We already have one written: the <b>endsound</b>
opcode.&nbsp; This is the opcode we will use to terminate sound effects.&nbsp;
Sound effects don't loop continuously like songs do, so they need to be
stopped.&nbsp; Let's take a look again:<br /><br style="font-family: Courier New;" /><span style="font-family: Courier New;">se_op_endsound:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda stream_status, x&nbsp;&nbsp;&nbsp; ;end of stream, so disable it and silence</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; and #%11111110</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sta stream_status, x&nbsp;&nbsp;&nbsp; ;clear enable flag in status byte</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda stream_channel, x</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; cmp #TRIANGLE</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; beq .silence_tri&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;triangle is silenced differently from squares and noise</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda #$30&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;squares and noise silenced with #$30</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; bne .silence&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; (this will always branch.&nbsp; bne is cheaper than a jmp)</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">.silence_tri:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda #$80&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;triangle silenced with #$80</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">.silence:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sta stream_vol_duty, x&nbsp; ;store silence value in the stream's volume variable.</span><br style="font-family: Courier New;" /><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; rts</span><br />&nbsp;&nbsp; &nbsp;<br />This opcode is special.&nbsp; It's the reason for the check after the call to se_opcode_launcher:<br /><br /><span style="font-family: Courier New;">se_fetch_byte:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; ;---snip---</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">.opcode:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;else ($A0-$FF) it's an opcode</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; ;do Opcode stuff</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; jsr se_opcode_launcher</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; iny&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;next position in the data stream</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; <span style="color: rgb(255, 0, 0);">lda stream_status, x</span></span><br style="font-family: Courier New; color: rgb(255, 0, 0);" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">&nbsp;&nbsp;&nbsp; and #%00000001</span><br style="font-family: Courier New; color: rgb(255, 0, 0);" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">&nbsp;&nbsp;&nbsp; bne .fetch&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;after our opcode is done, grab another byte unless the stream is disabled</span><br style="font-family: Courier New; color: rgb(255, 0, 0);" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">&nbsp;&nbsp;&nbsp; rts&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; in which case we quit&nbsp; (explained below)</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; ;---snip---</span><br />&nbsp;&nbsp; &nbsp;<br />Normally,
we want se_fetch_byte to keep fetching bytes until it hits a note.&nbsp;
Recall that with note lengths we jumped back to .fetch after setting
the new note length.&nbsp; This is because after setting the length of the
note, we needed to know WHAT note to play.&nbsp; So we fetch another byte.&nbsp;
The same thing is true of opcodes.&nbsp; If we change the volume envelope
with an opcode, great!&nbsp; But we still need to know what note to play
next.&nbsp; If we use an opcode to switch our square's duty cycle, great!&nbsp;
But we still need to know what note to play next.&nbsp; If we use an opcode
to loop back to the beginning of the song, that's great!&nbsp; But we still
need to read that first note of the song.&nbsp; This is why we jump back to
fetch a byte after we run an opcode.<br /><br />The ONE exception to this
rule is when we end a sound effect.&nbsp; We are terminating the sound
effect completely, so there is no next note.&nbsp; We don't want to fetch
something that isn't there, so we need to skip the jump.&nbsp; That's why we
check the status byte after we run the opcode.&nbsp; If the stream is
disabled by the endsound opcode, we are finished.&nbsp; Otherwise, fetch
another byte.<br /><br /><b>Looping</b><br />The next opcode in our list is the <b>loop</b>
opcode.&nbsp; This is the opcode that we will stick at the end of every song
to tell the sound engine to play the song again, and again and again.&nbsp;
It is actually quite easy to implement.&nbsp; It takes a <b>2-byte argument</b>, which is <b>the address to loop back to</b>.&nbsp; The subroutine looks like this:<br /><br style="font-family: Courier New;" /><span style="font-family: Courier New;">se_op_infinite_loop:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda [sound_ptr], y&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;read LO byte of the address argument from the data stream</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sta stream_ptr_LO, x&nbsp;&nbsp;&nbsp; ;save as our new data stream position</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; iny</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda [sound_ptr], y&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;read HI byte of the address argument from the data stream</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sta stream_ptr_HI, x&nbsp;&nbsp;&nbsp; ;save as our new data stream position data stream position</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sta sound_ptr+1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;update the pointer to reflect the new position.</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda stream_ptr_LO, x</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sta sound_ptr</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; ldy #$FF&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;after opcodes return, we do an iny.&nbsp; Since we reset &nbsp;</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;the stream buffer position, we will want y to start out at 0 again.</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; rts</span><br />&nbsp;&nbsp; &nbsp;<br />The
first thing to notice about this subroutine is that it reads two bytes
from the data stream.&nbsp; This is the address argument that gets passed
along with the opcode.&nbsp; To make it clear, let's look at some example
sound data:<br /><br /><span style="font-family: Courier New;">song1_square1:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .byte eighth ;set note length to eighth notes</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .byte C5, E5, G5, C6, E6, G6, C5, Eb5, G5, C6, Eb6, half, G6 ;play some notes</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; <span style="color: rgb(255, 0, 0);">.byte loop&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;this alias evaluates to $A1, the loop opcode</span></span><br style="font-family: Courier New; color: rgb(255, 0, 0);" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">&nbsp;&nbsp;&nbsp; .word song1_square1 ;this evaluates to the address of the song1_square1 label</span><br style="font-family: Courier New; color: rgb(255, 0, 0);" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;ie, the address we want to loop to.</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;<br />After
the "loop" opcode comes a word which is the address to loop back to.&nbsp;
In this example I chose to loop back to the beginning of the stream
data.<br /><br />So what does our loop opcode do?&nbsp; It reads the first byte
of this address argument (the low byte) and stores it in
stream_ptr_LO.&nbsp; Then it reads the second byte of the address argument
(the high byte) and stores it in stream_ptr_HI.&nbsp; These are the
variables that keep track of our data stream position!&nbsp; The loop opcode
just changes these values to some address that we specify.&nbsp; Not too
complicated at all.&nbsp; The last step is to update the actual pointer (<b>sound_ptr</b>) so that the next byte we read from the data stream will be the first note we looped back to.<br /><br />In
the example sound data above I looped back to the beginning of the
stream data, but there's nothing stopping me from looping somewhere
else:<br /><br style="font-family: Courier New;" /><span style="font-family: Courier New;">song1_square1:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">;intro, don't loop this part</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .byte quarter</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .byte C4, C4, C4, C4</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;"><span style="color: rgb(255, 0, 0);">.loop_point:</span>&nbsp;&nbsp;&nbsp; ;this is where we will loop back to.</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .byte eighth ;set note length to eighth notes</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .byte C5, E5, G5, C6, E6, G6, C5, Eb5, G5, C6, Eb6, half, G6</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; <span style="color: rgb(255, 0, 0);">.byte loop</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;this alias evaluates to $A1, the loop opcode</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; <span style="color: rgb(255, 0, 0);">.word .loop_point</span> ;this evaluates to the address of the .loop_point label</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;ie, the address we want to loop to.</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;<br />Technically
we can also "loop" to a forward position, in which case it's actually
more like a jump than a loop.&nbsp; That's all a loop is really: a jump...
backwards.<br /><br /><b>Changing Volume Envelopes</b><br />Let's write the opcode subroutine to change volume envelopes.&nbsp; This one is even easier.&nbsp; It takes <b>one argument</b>, which will be <b>which volume envelope to switch to</b>:<br /><br /><span style="font-family: Courier New;">se_op_change_ve:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda [sound_ptr], y&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;read the argument</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sta stream_ve, x&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;store it in our volume envelope variable</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda #$00</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sta stream_ve_index, x&nbsp; ;reset volume envelope index to the beginning</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; rts</span><br />&nbsp;&nbsp; &nbsp;<br />That's it!<br /><br /><b>Changing Duty Cycles</b><br />Now let's add an opcode that will change the duty cycle for a square stream.&nbsp; This one also takes <b>one argument: which duty cycle to switch to</b>.<br /><br /><span style="font-family: Courier New;">se_op_duty:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda [sound_ptr], y&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;read the argument (which duty cycle to change to)</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sta stream_vol_duty, x&nbsp; ;store it.</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; rts</span><br />&nbsp;&nbsp; &nbsp;<br />Done!&nbsp; Now we have the subroutine, but we still need to add it to our jump table:<br /><br /><span style="font-family: Courier New;">sound_opcodes:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .word se_op_endsound&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;this should be $A0</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .word se_op_loop&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;this should be $A1</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .word se_op_change_ve&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;this should be $A2</span><br style="font-family: Courier New;" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">&nbsp;&nbsp;&nbsp; .word se_op_duty&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;this should be $A3</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; ;etc, 1 entry per subroutine</span><br /><br /><span style="font-family: Courier New;">;these are aliases to use in the sound data.</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">endsound = $A0</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">loop = $A1</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">volume_envelope = $A2</span><br style="font-family: Courier New;" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">duty = $A3</span><br /><br />And it's ready to use:<br /><br /><span style="font-family: Courier New;">song0_square1:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">;intro, don't loop this part</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .byte quarter</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .byte C4, C4, C4, C4</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">.loop_point:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;this is where we will loop back to.</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; <span style="color: rgb(255, 0, 0);">.byte duty, $B0</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;change the duty cycle</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; <span style="color: rgb(255, 0, 0);">.byte volume_envelope, ve_blip_echo</span> ;change the volume envelope</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .byte eighth&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;set note length to eighth notes</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .byte C5, E5, G5, C6, E6, G6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;play some notes</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; <span style="color: rgb(255, 0, 0);">.byte duty, $30&nbsp;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;change the duty cycle</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; <span style="color: rgb(255, 0, 0);">.byte volume_envelope, ve_short_staccato</span>&nbsp;&nbsp;&nbsp; ;change volume envelope</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .byte C5, Eb5, G5, C6, Eb6, half, G6&nbsp;&nbsp;&nbsp; ;play some eighth notes and a half note</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .byte loop&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;loop to .loop_point </span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .word .loop_point</span><br />&nbsp;&nbsp; &nbsp;<br /><b>Readability</b><br />sound_engine.asm
is getting pretty bulky with all these subroutines.&nbsp; It will only get
bigger as we add more opcodes.&nbsp; It's nice to have all of our opcodes
together in one place, but it's annoying to have to scroll around to
find them.&nbsp; So let's pull all of our opcodes into their own file: <b>sound_opcodes.asm</b>.&nbsp; Then, at the bottom of sound_engine.asm, we can .include it:<br /><br />&nbsp;&nbsp;&nbsp; <span style="color: rgb(255, 0, 0);">.include "sound_opcodes.asm"</span> ;our opcode subroutines, jump table and aliases<br />&nbsp;&nbsp;&nbsp; .include "note_table.i" ;period lookup table for notes<br />&nbsp;&nbsp;&nbsp; .include "note_length_table.i"<br />&nbsp;&nbsp;&nbsp; .include "vol_envelopes.i"<br />&nbsp;&nbsp;&nbsp; .include "song0.i"&nbsp; ;holds the data for song 0 (header and data streams)<br />&nbsp;&nbsp;&nbsp; .include "song1.i"&nbsp; ;holds the data for song 1<br />&nbsp;&nbsp;&nbsp; .include "song2.i"<br />&nbsp;&nbsp;&nbsp; .include "song3.i"<br />&nbsp;&nbsp;&nbsp; .include "song4.i"<br />&nbsp;&nbsp;&nbsp; .include "song5.i"<br />&nbsp;&nbsp;&nbsp; <span style="color: rgb(255, 0, 0);">.include "song6.i"</span>&nbsp; ;oooh.. new song!<br />&nbsp;&nbsp; &nbsp;<br />I
gave it the extension .asm because it contains code as well as data,
and I like to be able to tell at a glance what files have what in
them.&nbsp; Now whenever we want to add new opcodes, or tweak old ones, we
have them nice and compact in their own file.<br /><br /><b>Updating Sound Data</b><br />Whenever
we add new things to our sound engine, we have to think about how it
will affect our old sound data.&nbsp; This week we added opcodes, which will
change our songs and sound effects terminate.&nbsp; Before we were
terminating them with $FF.&nbsp; This won't work anymore because $FF doesn't
do anything.&nbsp; For songs, we should terminate with "loop" followed by an
address to loop to.&nbsp; With sound effects we should terminate with the
opcode "endsound".&nbsp; See the included songs and sound effects for
examples.<br /><br /><b>RTS Tables</b><br />We talked about jump tables and
indirect jumping this week.&nbsp; Another method for doing the same thing
involves something called an <b>RTS table</b> and the <b>RTS Trick</b>.&nbsp;
I won't cover it in these tutorials, but if you are curious to know how
this works you can read <a href="http://wiki.nesdev.com/w/index.php/RTS_Trick">this nesdev wiki article I wrote about the RTS
Trick</a>.<br /><br /><br /><b>Putting It All Together</b><br />Download and unzip the <a href="http://tummaigames.com/opcodes.zip">opcodes.zip</a> sample files.&nbsp; Make sure the following files are in the same folder as NESASM3:<br /><br />&nbsp;&nbsp;&nbsp; opcodes.asm<br />&nbsp;&nbsp;&nbsp; sound_engine.asm<br />&nbsp;&nbsp;&nbsp; sound_opcodes.asm<br />&nbsp;&nbsp;&nbsp; opcodes.chr<br />&nbsp;&nbsp;&nbsp; note_table.i<br />&nbsp;&nbsp;&nbsp; note_length_table.i<br />&nbsp;&nbsp;&nbsp; vol_envelopes.i<br />&nbsp;&nbsp;&nbsp; song0.i<br />&nbsp;&nbsp;&nbsp; song1.i<br />&nbsp;&nbsp;&nbsp; song2.i<br />&nbsp;&nbsp;&nbsp; song3.i<br />&nbsp;&nbsp;&nbsp; song4.i<br />&nbsp;&nbsp;&nbsp; song5.i<br />&nbsp;&nbsp;&nbsp; song6.i<br />&nbsp;&nbsp;&nbsp; opcodes.bat<br /><br />Double click opcodes.bat. That will run NESASM3 and should produce the opcodes.nes file. Run that NES file in FCEUXD SP.<br /><br />Use the controller to select songs and play them.&nbsp; Controls are as follows:<br />&nbsp;&nbsp; &nbsp;<br /><b>Up</b>: Play<br /><b>Down</b>: Stop <br /><b>Right</b>: Next Song/SFX<br /><b>Left</b>: Previous Song/SFX<br /><br />Song0 is a silence song.&nbsp; Not selectable.<br />Song1 is a boss song from The Guardian Legend.&nbsp; Now it loops!<br />Song2 is the same short sound effect from last week.&nbsp; Terminated with endsound.<br />Song3 is a song from Dragon Warrior.&nbsp; Now it loops!<br />Song4 is the same song4 as last week, but now it loops!<br />Song5 is a short sound effect, terminated with the endsound opcode.<br />Song6
should be familiar to readers of this forum.&nbsp; Do you recognize it?&nbsp; It
utilizes opcodes for changing duty cycles and volume envelopes.&nbsp; Plus
it loops!<br /><br />Try adding your own songs and sound effects in.&nbsp; Try to add your own opcodes too.&nbsp; Here's some ideas for opcodes:<br /><br />1. Trigger a sound effect mid-song<br />2. Implement duty cycle envelopes (similar to volume envelopes).&nbsp; Then make an opcode that allows you to change it.<br />3. Finite loops<br /><br /><b>Next Week</b>: more opcode fun.&nbsp; Finite Loops, Changing Keys and Automatic Chord Progressions.<br /> ]]></description>
	</item>

	<item>
		<title>Flash Action Script 3</title>
		<link>http://www.nintendoage.com/forum/messageview.cfm?catid=22&amp;threadid=25505</link>
		<pubDate>2009-11-08T11:23:35 -05.0 0</pubDate>
		<dc:creator>IntrglctcNinjaPirate</dc:creator>
			<slash:comments>10</slash:comments>
		<description><![CDATA[ For the past few months I have been writing simple ship shooter episodes in AS3. &nbsp;The ships load offstage, fly in, and are removed via kill, or negative X axis.<br /><br />anyone else using this method, or perhaps a better one?<div><br /></div><div><font class="Apple-style-span" color="#444444" face="Verdana, Arial"><span class="Apple-style-span" style="-webkit-border-horizontal-spacing: 1px; -webkit-border-vertical-spacing: 1px; ">http://www.intergalacticninjapirate.com<br /><br />what is everyone else working on in flash?<br /><br />ThAnx,<br />INP<br /><br /><br /></span></font></div> ]]></description>
	</item>

	<item>
		<title>NESdev wiki wants info about projects</title>
		<link>http://www.nintendoage.com/forum/messageview.cfm?catid=22&amp;threadid=25406</link>
		<pubDate>2009-11-05T23:48:37 -05.0 0</pubDate>
		<dc:creator>Sivak</dc:creator>
			<slash:comments>2</slash:comments>
		<description><![CDATA[ I've made up some pages for my projects, but others can register and do up pages for theirs if they wish.<br /><br />http://wiki.nesdev.com/w/index.php/Projects - The page where to make links.<br /><br />You do need to register and you also need permission from admins if you want to post images.&nbsp; I posted some trivia on my games.&nbsp; <img src="i/expressions/face-icon-small-smile.gif" border="0"><br /> ]]></description>
	</item>

	<item>
		<title>Another platformer by someone at nesdev</title>
		<link>http://www.nintendoage.com/forum/messageview.cfm?catid=22&amp;threadid=25381</link>
		<pubDate>2009-11-05T13:29:48 -05.0 0</pubDate>
		<dc:creator>Sivak</dc:creator>
			<slash:comments>28</slash:comments>
		<description><![CDATA[ This was interesting-looking I thought:<br /><br />http://www.youtube.com/watch?v=jFoBdeLDVc8<br /><br />Also doesn't have scrolling, except for when you touch the edges of screens.<br /> ]]></description>
	</item>

	<item>
		<title>Nerdy Nights Sound: Part 7</title>
		<link>http://www.nintendoage.com/forum/messageview.cfm?catid=22&amp;threadid=25253</link>
		<pubDate>2009-11-02T05:15:05 -05.0 0</pubDate>
		<dc:creator>MetalSlime</dc:creator>
			<slash:comments>4</slash:comments>
		<description><![CDATA[ <span style="font-weight: bold;">Last Week:</span> <a href="http://nintendoage.com/forum/messageview.cfm?catid=22&threadid=24885">Tempo, Note Lengths, Buffering and Rests</a><br /><br /><span style="font-weight: bold;">This Week</span>: Volume Envelopes<br /><font size="4"><b><br />Volume Envelopes</b></font><br /><br />This week we will
add volume envelopes to our engine.&nbsp; A <span style="font-weight: bold;">volume envelope</span> is a series of
volume values that are applied to a note one frame at a time.&nbsp; For
example, if we had a volume envelope that looked like this:<br /><br /><span style="font-family: Courier New;">F E D C 9 5 0</span><br /><br />Then
whenever we played a note, it would have a volume of F on the first
frame, a volume of E on the second frame, then D, then C, then 9, then
5 until it is finally silenced with a volume of 0 on the 7th frame.&nbsp;
Applying this volume envelope on our notes would give them a sharp,
short staccato feel.&nbsp; Conversely, if we had a volume envelope that
looked like this:<br /><br /><span style="font-family: Courier New;">1 1 2 2 3 3 4 4 7 7 8 8 A A C C D D E E F F F</span><br /><br />Each note would start very quietly and fade in to full volume.&nbsp; Look at this volume envelope:<br /><br /><span style="font-family: Courier New;">D D D C B 0 0 0 0 0 0 0 0 6 6 6 5 4 0</span><br /><br />Here
we start at a high volume (D) and let it ring for 5 frames.&nbsp; Then we
silence the note for 8 frames.&nbsp; Then the note comes back at a very low
volume for 5 frames.&nbsp; Notes using this volume envelope would sound like
they had an faint echo.<br /><br />As you can see, volume envelopes are pretty cool.&nbsp; We can get a lot of different sounds out of them.&nbsp; Let's add them in.<br /><br /><b>Channels</b><br />Volume
envelopes are best suited for the square and noise channels where we
have full control of the volume.&nbsp; The triangle channel on the other
hand doesn't allow much volume control.&nbsp; It only has two settings: full
blast and off.&nbsp; We can still apply volume envelopes in a limited way
though.&nbsp; Consider these two volume envelopes:<br /><br /><span style="font-family: Courier New;">0F 0E 0D 0C 09 05 00</span><br style="font-family: Courier New;" /><br style="font-family: Courier New;" /><span style="font-family: Courier New;">04 04 05 05 06 06 07 07 08 08 09 09 0A 0A 00</span><br /><br />These two envelopes would have a vastly different sound on the square channels, but to the triangle they look like this:<br /><br style="font-family: Courier New;" /><span style="font-family: Courier New;">On On On On On On Off</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">On On On On On On On On On On On On On On Off</span><br /><br />You
don't get the subtle shifts in volume, but you do get a different
length.&nbsp; We can use volume envelopes that end in 00 to control when the
triangle key-off occurs.&nbsp; Not as cool as full volume control, but still
useful.<br /><br /><b>Defining volume envelopes</b><br />First let's define some volume envelopes so we have some data to work with.&nbsp; We'll use some of the examples from above:<br /><br /><span style="font-family: Courier New;">se_ve_1:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .byte $0F, $0E, $0D, $0C, $09, $05, $00</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .byte $FF</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">se_ve_2:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .byte $01, $01, $02, $02, $03, $03, $04, $04, $07, $07</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .byte $08, $08, $0A, $0A, $0C, $0C, $0D, $0D, $0E, $0E</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .byte $0F, $0F</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .byte $FF</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">se_ve_3:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .byte $0D, $0D, $0D, $0C, $0B, $00, $00, $00, $00, $00</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .byte $00, $00, $00, $00, $06, $06, $06, $05, $04, $00</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .byte $FF</span><br /><br />Notice
that I terminated each envelope with $FF.&nbsp; We need some terminator
value so the engine will know when we've reached the end of the
envelope.&nbsp; We could have used any value, but $FF is pretty common.<br /><br />Next we will make a pointer table that holds the addresses of our volume envelopes:<br /><br /><span style="font-family: Courier New;">volume_envelopes:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .word se_ve_1, se_ve_2, se_ve_3</span><br />&nbsp;&nbsp; &nbsp;<br /><b>Declaring variables</b><br />In
order to apply a volume envelope to a particular stream, we will need a
variable that tells us which one to use.&nbsp; We will also need an index
variable that tells us our current position within the volume envelope:<br /><br /><span style="font-family: Courier New;">stream_ve .rs 6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;current volume envelope</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">stream_ve_index .rs 6&nbsp;&nbsp; ;current position within the volume envelope</span><br /><br />stream_ve
will tell us which volume envelope to use.&nbsp; Code-wise, it will act as
an index into our pointer table so we know where to read from.&nbsp; Sound
familiar?&nbsp; It works the same way as "song number" did for loading a
song.&nbsp; We aren't there yet, but here's a peek at how we will use these
variables to read from the volume envelopes. (x holds the stream
number):<br /><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sty sound_temp1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;save y because we are about to destroy it.</span><br style="font-family: Courier New;" /><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda stream_ve, x&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;which volume envelope?</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; asl a&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;multiply by 2 because we are indexing into a table of addresses (words)</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; tay</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda volume_envelopes, y&nbsp;&nbsp;&nbsp;&nbsp; ;get the low byte of the address from the pointer table</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sta sound_ptr</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda volume_envelopes+1, y&nbsp;&nbsp; ;get the high byte of the address</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sta sound_ptr+1</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; ldy stream_ve_index, x&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;our current position within the volume envelope.</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda [sound_ptr], y&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;grab the value.</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; ;check against $FF (our termination value)</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; ;set the volume</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; ;increment stream_ve_index</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; ;etc</span><br />&nbsp;&nbsp; &nbsp;<br />Compare this code to the beginning of the sound_load routine.&nbsp; Are you starting to see a pattern?<br />&nbsp;&nbsp; &nbsp;<br /><b>Initializing</b><br />Whenever
we add a new feature, we need to consider how we should initialize it.&nbsp;
Every stream in our music data will potentially have a different volume
envelope, so we should add a volume envelope field to our header.&nbsp;
Volume envelopes will deprecate our old "initial volume" field, but we
will still need to have duty cycle info, so we'll just rename that
field:<br /><br /><span style="font-family: Courier New;">main header:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">--------+----------------</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">byte #&nbsp; | what it tells us</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">--------+----------------</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | number of streams</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">01+&nbsp;&nbsp;&nbsp;&nbsp; | stream headers (one for each stream)</span><br style="font-family: Courier New;" /><br style="font-family: Courier New;" /><span style="font-family: Courier New;">stream headers:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">--------+----------------</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">byte #&nbsp; | what it tells us</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">--------+----------------</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | which stream (stream number)</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">01&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | status byte</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">02&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | which channel</span><br style="font-family: Courier New;" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">03&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | initial duty (for triangle, set the 7bit)</span><br style="font-family: Courier New;" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">04&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | volume envelope</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">05-06&nbsp;&nbsp; | pointer to data stream</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">07&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | initial tempo</span><br /><br />To
read this data from the header, we will have to insert the following
code into our sound_load routine (after reading the duty):<br /><br /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda [sound_ptr], y&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;the stream's volume envelope</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sta stream_ve, x</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; iny</span><br />&nbsp;&nbsp; &nbsp;<br />Notes will always start from the beginning of the volume envelope, so we can just initialize stream_ve_index to 0:<br /><br /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda #$00</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sta stream_ve_index, x</span><br />&nbsp;&nbsp; &nbsp;<br />Now we just need to make sure to assign volume envelopes to all the streams in our song data and we're ready to go:<br /><br /><span style="font-family: Courier New;">song5_header:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .byte $01&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;1 stream</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .byte SFX_1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;which stream</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .byte $01&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;status byte (stream enabled)</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .byte SQUARE_2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;which channel</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .byte $70&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;initial duty (01).&nbsp; Initial volume deprecated.</span><br style="font-family: Courier New;" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">&nbsp;&nbsp;&nbsp; .byte $00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;the first volume envelope (se_ve_1)</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .word song5_square2 ;pointer to stream</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .byte $FF&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;tempo..very fast tempo</span><br /><br />Remember
that you can always create descriptive aliases for your volume
envelopes if you don't want to remember which number is which:<br /><br /><span style="font-family: Courier New;">;volume envelope aliases</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">ve_short_staccato = $00</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">ve_fade_in = $01</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">ve_blip_echo = $02</span><br style="font-family: Courier New;" /><br style="font-family: Courier New;" /><span style="font-family: Courier New;">song5_header:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .byte $01&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;1 stream</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .byte SFX_1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;which stream</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .byte $01&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;status byte (stream enabled)</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .byte SQUARE_2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;which channel</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .byte $7F&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;initial duty (01).&nbsp; Initial volume deprecated.</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .byte <span style="color: rgb(255, 0, 0);">ve_short_staccato</span>&nbsp;&nbsp; ;the first volume envelope (se_ve_1)</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .word song5_square2 ;pointer to stream</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; .byte $FF&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;tempo..very fast tempo</span><br />&nbsp;&nbsp; &nbsp;<br />Using
aliases is a good idea because the assembler will give you an error if
you mistype your alias.&nbsp; If you mistype your number, and it is still a
valid number, the assembler won't know there's a problem and will
assemble it.&nbsp; This kind of bug in your data can be hard to trace.<br /><br /><br /><font size="3"><b>Implementing Volume Envelopes</b></font><br /><br />To
implement volume envelopes, we need to modify the code where we set the
volume.&nbsp; Instead of using a fixed value like we were doing before, we
need to read from our current position in the volume envelope and use
that value instead.&nbsp; Our volume code is starting to get a little
complicated, so let's pull it out into its own subroutine.&nbsp; This will
make our code easier to follow:<br /><br /><span style="font-family: Courier New;">;----------------------------------------------------</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">; se_set_temp_ports will copy a stream's sound data to the temporary apu variables</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; input:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; X: stream number</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">se_set_temp_ports:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda stream_channel, x</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; asl a</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; asl a</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; tay</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; <span style="color: rgb(255, 0, 0);">jsr se_set_stream_volume&nbsp;&nbsp;&nbsp; ;let's stick all of our volume code into a new subroutine</span></span><br style="font-family: Courier New; color: rgb(255, 0, 0);" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;less cluttered that way</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda #$08</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sta soft_apu_ports+1, y&nbsp;&nbsp;&nbsp;&nbsp; ;sweep</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda stream_note_LO, x</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sta soft_apu_ports+2, y&nbsp;&nbsp;&nbsp;&nbsp; ;period LO</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda stream_note_HI, x</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sta soft_apu_ports+3, y&nbsp;&nbsp;&nbsp;&nbsp; ;period HI</span><br style="font-family: Courier New;" /><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; rts&nbsp;&nbsp; &nbsp;</span><br /><br />What
should our new subroutine se_set_stream_volume do?&nbsp; First it needs to
read a value from our stream's volume envelope.&nbsp; Then it needs to
modify the stream's volume using that value.&nbsp; Then we need to update
our position within the volume envelope.&nbsp; Finally it needs to check to
see if we are resting, and silence the stream if we are (we wrote this
code last week).&nbsp; It looks something like this (new code in red):<br /><br /><br /><span style="font-family: Courier New;">se_set_stream_volume:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; <span style="color: rgb(255, 0, 0);">sty sound_temp1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;save our index into soft_apu_ports (we are about to destroy y)</span></span><br style="font-family: Courier New; color: rgb(255, 0, 0);" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">&nbsp;</span><br style="font-family: Courier New; color: rgb(255, 0, 0);" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">&nbsp;&nbsp;&nbsp; lda stream_ve, x&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;which volume envelope?</span><br style="font-family: Courier New; color: rgb(255, 0, 0);" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">&nbsp;&nbsp;&nbsp; asl a&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;multiply by 2 because we are indexing into a table of addresses (words)</span><br style="font-family: Courier New; color: rgb(255, 0, 0);" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">&nbsp;&nbsp;&nbsp; tay</span><br style="font-family: Courier New; color: rgb(255, 0, 0);" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">&nbsp;&nbsp;&nbsp; lda volume_envelopes, y&nbsp;&nbsp;&nbsp;&nbsp; ;get the low byte of the address from the pointer table</span><br style="font-family: Courier New; color: rgb(255, 0, 0);" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">&nbsp;&nbsp;&nbsp; sta sound_ptr&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;put it into our pointer variable</span><br style="font-family: Courier New; color: rgb(255, 0, 0);" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">&nbsp;&nbsp;&nbsp; lda volume_envelopes+1, y&nbsp;&nbsp; ;get the high byte of the address</span><br style="font-family: Courier New; color: rgb(255, 0, 0);" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">&nbsp;&nbsp;&nbsp; sta sound_ptr+1</span><br style="font-family: Courier New; color: rgb(255, 0, 0);" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">&nbsp;</span><br style="font-family: Courier New; color: rgb(255, 0, 0);" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">.read_ve:</span><br style="font-family: Courier New; color: rgb(255, 0, 0);" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">&nbsp;&nbsp;&nbsp; ldy stream_ve_index, x&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;our current position within the volume envelope.</span><br style="font-family: Courier New; color: rgb(255, 0, 0);" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">&nbsp;&nbsp;&nbsp; lda [sound_ptr], y&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;grab the value.</span><br style="font-family: Courier New; color: rgb(255, 0, 0);" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">&nbsp;&nbsp;&nbsp; cmp #$FF</span><br style="font-family: Courier New; color: rgb(255, 0, 0);" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">&nbsp;&nbsp;&nbsp; bne .set_vol&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;if not FF, set the volume</span><br style="font-family: Courier New; color: rgb(255, 0, 0);" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">&nbsp;&nbsp;&nbsp; dec stream_ve_index, x&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;else if FF, go back one and read again</span><br style="font-family: Courier New; color: rgb(255, 0, 0);" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">&nbsp;&nbsp;&nbsp; jmp .read_ve&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; FF essentially tells us to repeat the last</span><br style="font-family: Courier New; color: rgb(255, 0, 0);" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; volume value for the remainder of the note</span><br style="font-family: Courier New; color: rgb(255, 0, 0);" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">.set_vol:</span><br style="font-family: Courier New; color: rgb(255, 0, 0);" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">&nbsp;&nbsp;&nbsp; sta sound_temp2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;save our new volume value (about to destroy A)</span><br style="font-family: Courier New; color: rgb(255, 0, 0);" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">&nbsp;&nbsp;&nbsp; lda stream_vol_duty, x&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;get current vol/duty settings</span><br style="font-family: Courier New; color: rgb(255, 0, 0);" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">&nbsp;&nbsp;&nbsp; and #$F0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;zero out the old volume</span><br style="font-family: Courier New; color: rgb(255, 0, 0);" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">&nbsp;&nbsp;&nbsp; ora sound_temp2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;OR our new volume in.</span><br style="font-family: Courier New; color: rgb(255, 0, 0);" /><br style="font-family: Courier New; color: rgb(255, 0, 0);" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">&nbsp;&nbsp;&nbsp; ldy sound_temp1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;get our index into soft_apu_ports</span><br style="font-family: Courier New; color: rgb(255, 0, 0);" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">&nbsp;&nbsp;&nbsp; sta soft_apu_ports, y&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;store the volume in our temp port</span><br style="font-family: Courier New; color: rgb(255, 0, 0);" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">&nbsp;&nbsp;&nbsp; inc stream_ve_index, x&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;set our volume envelop index to the next position</span><br style="font-family: Courier New; color: rgb(255, 0, 0);" /><br style="font-family: Courier New;" /><span style="font-family: Courier New;">.rest_check:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; ;check the rest flag. if set, overwrite volume with silence value </span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda stream_status, x</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; and #%00000010</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; beq .done&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;if clear, no rest, so quit</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda stream_channel, x</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; cmp #TRIANGLE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;if triangle, silence with #$80</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; beq .tri&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;else, silence with #$30</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda #$30&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; bne .store&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;this always branches.&nbsp; bne is cheaper than a jmp</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">.tri:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda #$80</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">.store:&nbsp;&nbsp; &nbsp;</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sta soft_apu_ports, y</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">.done:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; rts</span><br />&nbsp;&nbsp; &nbsp;<br />After
we read a value from our volume envelope, we AND stream_vol_duty with
#$F0.&nbsp; This has the nice effect of clearing the old volume while
preserving our squares' duty cycle settings.&nbsp; But we need to be careful
here.&nbsp; Recall that the triangle channel's on/off status is controlled
by the low 7 bits of the port:<br /><br style="font-family: Courier New;" /><span style="font-family: Courier New;">TRI_CTRL ($4008)</span><br style="font-family: Courier New;" /><br style="font-family: Courier New;" /><span style="font-family: Courier New;">76543210</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">||||||||</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">|+++++++- Value</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">+-------- Control Flag (0: use internal counters; 1: disable internal counters)</span><br /><br />If
any of those Value bits are set, the triangle channel will be
considered on.&nbsp; Consider what happens if bit 4, 5 or 6 happen to be
set.&nbsp; In this case, ANDing with #$F0 won't turn the triangle channel
off.&nbsp; If the volume we pull from the volume envelope is 0, it won't
silence our triangle channel because bit 4, 5 or 6 will still be set.&nbsp;
If we are careful not to set these bits in our song headers, the
problem should never come up.&nbsp; But for completeness we should fix it:<br /><br /><span style="font-family: Courier New;">se_set_stream_volume:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sty sound_temp1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;save our index into soft_apu_ports (we are about to destroy y)</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda stream_ve, x&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;which volume envelope?</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; asl a&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;multiply by 2 because we are indexing into a table of addresses (words)</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; tay</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda volume_envelopes, y&nbsp;&nbsp;&nbsp;&nbsp; ;get the low byte of the address from the pointer table</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sta sound_ptr&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;put it into our pointer variable</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda volume_envelopes+1, y&nbsp;&nbsp; ;get the high byte of the address</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sta sound_ptr+1</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">.read_ve:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; ldy stream_ve_index, x&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;our current position within the volume envelope.</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda [sound_ptr], y&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;grab the value.</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; cmp #$FF</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; bne .set_vol&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;if not FF, set the volume</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; dec stream_ve_index, x&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;else if FF, go back one and read again</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; jmp .read_ve&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; FF essentially tells us to repeat the last</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp; volume value for the remainder of the note</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">.set_vol:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sta sound_temp2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;save our new volume value (about to destroy A)</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp;<span style="color: rgb(255, 0, 0);"> cpx #TRIANGLE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;</span></span><br style="font-family: Courier New; color: rgb(255, 0, 0);" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">&nbsp;&nbsp;&nbsp; bne .squares&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;if not triangle channel, go ahead</span><br style="font-family: Courier New; color: rgb(255, 0, 0);" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">&nbsp;&nbsp;&nbsp; lda sound_temp2</span><br style="font-family: Courier New; color: rgb(255, 0, 0);" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">&nbsp;&nbsp;&nbsp; bne .squares&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;else if volume not zero, go ahead (treat same as squares)</span><br style="font-family: Courier New; color: rgb(255, 0, 0);" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">&nbsp;&nbsp;&nbsp; lda #$80</span><br style="font-family: Courier New; color: rgb(255, 0, 0);" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">&nbsp;&nbsp;&nbsp; bmi .store_vol&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;else silence the channel with #$80</span><br style="font-family: Courier New; color: rgb(255, 0, 0);" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">.squares:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda stream_vol_duty, x&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;get current vol/duty settings</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; and #$F0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;zero out the old volume</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; ora sound_temp2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;OR our new volume in.</span><br style="font-family: Courier New;" /><br style="font-family: Courier New;" /><span style="font-family: Courier New;">.store_vol:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; ldy sound_temp1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;get our index into soft_apu_ports</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sta soft_apu_ports, y&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;store the volume in our temp port</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; inc stream_ve_index, x&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;set our volume envelop index to the next position</span><br style="font-family: Courier New;" /><br style="font-family: Courier New;" /><span style="font-family: Courier New;">.rest_check:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; ;check the rest flag. if set, overwrite volume with silence value </span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda stream_status, x</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; and #%00000010</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; beq .done&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;if clear, no rest, so quit</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda stream_channel, x</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; cmp #TRIANGLE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;if triangle, silence with #$80</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; beq .tri&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;else, silence with #$30</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda #$30&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; bne .store&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;this always branches.&nbsp; bne is cheaper than a jmp</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">.tri:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda #$80</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">.store:&nbsp;&nbsp; &nbsp;</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sta soft_apu_ports, y</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">.done:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; rts</span><br />&nbsp;&nbsp; &nbsp;<br /><b>New notes</b><br />The
last thing we need to consider is new notes.&nbsp; When an old note finishes
and we start playing a new note, we will want to reset the volume
envelope back to the beginning.&nbsp; This is as easy as setting
stream_ve_index to 0 when we read a new note:<br /><br /><span style="font-family: Courier New;">se_fetch_byte:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; ;...snip... (setup pointers, read byte, test range, etc)</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">.note:</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; ;do Note stuff</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sty sound_temp1&nbsp;&nbsp;&nbsp;&nbsp; ;save our index into the data stream</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; asl a</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; tay</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda note_table, y</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sta stream_note_LO, x</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; lda note_table+1, y</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; sta stream_note_HI, x</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; ldy sound_temp1&nbsp;&nbsp;&nbsp;&nbsp; ;restore data stream index</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; <span style="color: rgb(255, 0, 0);">lda #$00</span></span><br style="font-family: Courier New; color: rgb(255, 0, 0);" /><span style="font-family: Courier New; color: rgb(255, 0, 0);">&nbsp;&nbsp;&nbsp; sta stream_ve_index, x&nbsp; ;reset the volume envelope.</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; ;check if it's a rest and modify the status flag appropriately</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; jsr se_check_rest&nbsp;&nbsp; &nbsp;</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;</span><br style="font-family: Courier New;" /><span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; ;...snip... (update pointer)</span><br />&nbsp;&nbsp; &nbsp;<br />And now we have volume envelopes.<br /><br /><b>Putting It All Together</b><br />Download and unzip the <a href="http://tummaigames.com/envelopes.zip">envelopes.zip</a> sample files.&nbsp; Make sure the following files are in the same folder as NESASM3:<br /><br />&nbsp;&nbsp;&nbsp; envelopes.asm<br />&nbsp;&nbsp;&nbsp; sound_engine.asm<br />&nbsp;&nbsp;&nbsp; envelopes.chr<br />&nbsp;&nbsp;&nbsp; note_table.i<br />&nbsp;&nbsp;&nbsp; note_length_table.i<br />&nbsp;&nbsp;&nbsp; vol_envelopes.i<br />&nbsp;&nbsp;&nbsp; song0.i<br />&nbsp;&nbsp;&nbsp; song1.i<br />&nbsp;&nbsp;&nbsp; song2.i<br />&nbsp;&nbsp;&nbsp; song3.i<br />&nbsp;&nbsp;&nbsp; song4.i<br />&nbsp;&nbsp;&nbsp; song5.i<br />&nbsp;&nbsp;&nbsp; envelopes.bat<br /><br />Double click envelopes.bat. That will run NESASM3 and should produce the envelopes.nes file. Run that NES file in FCEUXD SP.<br /><br />Use the controller to select songs and play them.&nbsp; Controls are as follows:<br />&nbsp;&nbsp; &nbsp;<br /><b>Up</b>: Play<br /><b>Down</b>: Stop <br /><b>Right</b> : Next Song/SFX<br /><b>Left </b>: Previous Song/SFX<br /><br />Song0 is a silence song.&nbsp; Not selectable.<br />Song1 is a boss song from The Guardian Legend, almost the same as the original.<br />Song2 is the same short sound effect from last week.<br />Song3 is a song from Dragon Warrior, very close to the original.<br />Song4 is the same song4 as last week, but volume envelopes allow us to save some bytes by reducing rests.<br />Song5 is a short sound effect, same as last week.<br /><br />Try
creating your own songs and sound effects and add them into the mix.&nbsp;
To add a new song you will need to take the following steps:<br /><br />1)
create a song header and song data (use the included songs as
reference).&nbsp; Don't forget to select a volume envelope for each stream
in your header.&nbsp; Data streams are terminated with $FF.<br />2) add your header to the song_headers pointer table at the bottom of sound_engine.asm<br />3) update the constant NUM_SONGS to reflect the new song number total (also at the bottom of sound_engine.asm)<br /><br />Try
making your own volume envelopes too.&nbsp; To do so you will need to modify
vol_envelopes.i.&nbsp; Remember that volume envelopes are terminated with
$FF.<br />
<br /><b>Next Week</b>: <A href="http://nintendoage.com/forum/messageview.cfm?catid=22&threadid=25583">Opcodes, Looping</a><br /> ]]></description>
	</item>

	<item>
		<title>Gameboy Color Homebrew???</title>
		<link>http://www.nintendoage.com/forum/messageview.cfm?catid=22&amp;threadid=25248</link>
		<pubDate>2009-11-01T21:06:20 -05.0 0</pubDate>
		<dc:creator>ACE</dc:creator>
			<slash:comments>15</slash:comments>
		<description><![CDATA[ <p>I was on Wikipedia looking at the specs on the Gameboy Color and wondered if anyone was doing any homebrew projects for it. That processor in the GBC really is a serious peice of chip with the it having twice the speed&nbsp;RAM then the original Gameboy... In some respects it's just a little bit more powerful then the NES.</p><p /><p>Best regards. <img src="i/expressions/face-icon-small-smile.gif" border="0"></p> ]]></description>
	</item>

	<item>
		<title>BK credits - Verbatim</title>
		<link>http://www.nintendoage.com/forum/messageview.cfm?catid=22&amp;threadid=25105</link>
		<pubDate>2009-10-28T17:14:56 -05.0 0</pubDate>
		<dc:creator>Sivak</dc:creator>
			<slash:comments>8</slash:comments>
		<description><![CDATA[ Hey all.&nbsp; I know I posted this before, but I just wanted to do it again.&nbsp; This is just <span style="font-weight: bold;">easier</span> than going back and editing.&nbsp; <br /><br />This is a verbatim version of the credits, everything spelled the way it is in-game.&nbsp; <span style="font-weight: bold;">If you want to be called something else, please post</span>.<br /><br />It's written in <span style="font-weight: bold;">all caps in-game</span>, I just didn't want to use all caps for everything here.<br /><br /><span style="font-weight: bold;">PROGRAMMER, STORY, LEVEL DESIGNER, MUSIC, SOUND EFFECTS BY</span>: Sivak<br /><span style="font-weight: bold;">GRAPHICS BY</span>: David Fiondella, Sivak, Megan Roberts, Isildur (not on this site)<br /><span style="font-weight: bold;">ENEMY DESIGNERS</span>: Sivak, David Fiondella, Thomas Hjelm, Dangevin<br /><span style="font-weight: bold;">ASSEMBLED WITH</span>:&nbsp; NESASM<br /><span style="font-weight: bold;">MUSIC ENGINE</span>: Drag NSF<br /><span style="font-weight: bold;">CART PRODUCTION</span>: Retrozone<br /><span style="font-weight: bold;">LABEL ARTWORK</span>: Nobody Yet<br /><span style="font-weight: bold;">BETA TESTERS</span>: Rob Bryant, Ross Burdge, David Fiondella, Nathan Graybeal, Penguin<br /><span style="font-weight: bold;">INSPIRED BY</span>: I Wanna be the Guy<br /><span style="font-weight: bold;">SPECIAL THANKS</span>:&nbsp; Disch, Braveheart69, Blargg, Kayin, Setz, Famitracker, Nintendoage.com, 6502.org, Retroware TV<br /> ]]></description>
	</item>

	<item>
		<title>The day I&apos;ve been waiting for:  BK is 100% finished</title>
		<link>http://www.nintendoage.com/forum/messageview.cfm?catid=22&amp;threadid=25104</link>
		<pubDate>2009-10-28T16:58:48 -05.0 0</pubDate>
		<dc:creator>Sivak</dc:creator>
			<slash:comments>46</slash:comments>
		<description><![CDATA[ Hey one and all.&nbsp; I know that this is probably the post many of you have wanted to see, so here it is.&nbsp; Earlier today I finalized the last image for the ending and I literally have nothing left to add content-wise.&nbsp; I am pretty happy how it all came out.<br /><br />Now, this does not mean it'll be on retrousb tomorrow.&nbsp; There are still things to do.<br /><br />-<s>Firstly, I'm going to do a big-time final analysis of everything in the ROM.&nbsp; I'm confident I won't want to change anything, but that's what this analysis is for.&nbsp; If I'm sure it's fine, then from that point on nothing will be changed except for the text on the title screen.<br />-After this, I'm going to have a personal prototype made up and play on the real hardware to ensure it is fine.&nbsp; This prototype may very well be for sale someday.</s><br />-Hopefully while this is going on, I'll have obtained the label art.<br />-When I have a planned date I'll make another video, this time being a trailer showing a little of all the areas of the game.<br />-The LE signups will occur when all is satisfactory and ready.<br />-The standard edition will probably be out a week or so after that.<br /><br />That is my report.<br /> ]]></description>
	</item>

</channel>
</rss>
