
; Super Mario World's modified SPC Program - Kipernal
; Heavily based on loveemu labo's assembly, which in turn was heavily based on C.Bongo's assembly.
; Bugs have been fixed and all addresses above $04FF have been turned into labels to make things portable.
;
; Easy way to tell whose comments are whose: My comments are indented with tabs, while loveemu labo's
; comments are indented with spaces.
;
; Notable code changes have been marked.  Or rather, they were.  Eventually the changes just got so
; massive that this stopped becoming feasible (though some marks still exist).
;
; Note: The L_XXXX labels are generic and almost none of them accurately reflect their placement in ARAM
; anymore.  Do not use them to locate a code's position.
;

; Major, major thanks to loveemu labo.  I could never have done this without this disassembly or without
; their documentation on the N-SPC engine.
!false = 0
!true = 1

!PSwitchIsSFX = !false		; If you set this to true, then the P-switch song will be a sound effect
				; instead of a song that interrupts the current music.
				; Note, however, that it is hardcoded and cannot be changed unless you
				; do it yourself.


; Some documented RAM addresses: (note that addresses with "+x" are indexed by the current channel * 2)
; $00: Current input from APU0 (only available for 1 "loop").
; $01: Current input from APU1.
; $02: Current input from APU2.
; $03: Current input from APU3.
; $04: Value currently being processed from APU0 (available until whatever it is is done processing).
; $05: Value currently being processed from APU1.
; $06: Value currently being processed from APU2.
; $07: Value currently being processed from APU3.
; $08: Previous value from APU0?
; $09: Previous value from APU1?
; $0a: Previous value from APU2?
; $0b: Previous value from APU3?

; $45: Increases each "tick"
; $0281+x: Pan
; $0241+x: Volume
; $02d1+x: Portamento...or tuning?  Seems to be tuning, but the original code calls it portamento.
; $02b1+x: Current note value (not pitch).
; $02b0+x: Portamento...?
; $81+x: Pan fade
; $80+x: Volume fade
; $a1+x: Vibrato
; $b1+x: ?
; $c0+x: Repeat counter
; $c1+x: Instrument
; $0161: Strong portamento
; $58: Global volume
; $60: Echo volume fade
; $52: Tempo fade
; $43: Global transpose
; $57: Global volume (default #$C0)
; $51: Tempo (default #$36)
; $0130: Used as an instrument "backup" for the $E5 / $F3 commands.  It is $30 bytes long, not the normal 8 (6 bytes for each channel).
; $0160: #$01 to enable Yoshi Drums. Has various purposes; originally used by AddmusicM.
; $0383: Some sort of a timer for the jump SFX.
; $0211+x: The volume part of the qXX command.
; $0386: Set if Mario is on Yoshi.  
; $0387: Amount the tempo should be increased by (used by the "time is running out!" sound effect to speed up the music).

; $48: Bitwise indicator of the current channel being processed.
; $5C: Used to indicate that a volume needs to be updated (long routine, so it's only done when necessary).
; $0166: 4 bytes; used as the output byte to send to the 5A22.  Originally AMM only used 2 of these, we can use all 4 for whatever we like.
; $13: ? Only the highest bit is ever modified or read.  Seems to do...something.

; $5E: Used to mute a channel (via Yoshi Drums, etc.).  One bit per channel, setting it stops a channel from playing.
; $6E: Which channels are affected by Yoshi drums.

!SurroundSound = $02a1
!PanFadeDuration = $81
!PanFadeDestination = $02a0
!VolumeMult = $02e1

!MasterVolume = $57

!Pan = $0281			; 
!Volume = $0241			; 
!BackupSRCN = $0220		; 
!HTuneValues = $0120		; 
!CustomInstrumentPos = $6c	; Position of the custom instrument table.

!MusicPModChannels = $4a	; \ The music channels that have pitch modulation enabled on them.
!MusicNoiseChannels = $4b	; | The same as the above, but for noise.
!MusicEchoChannels = $4c	; / The same as the above, but for echo.

!SFXPModChannels = $4d		; \ The SFX channels that have pitch modulation enabled on them.
!SFXNoiseChannels = $4e		; | (etc.)
!SFXEchoChannels = $4f		; / (etc.)
				; All of these addresses must be sequential, in this order, and in the direct page.
				; ...so basically, don't change them.

!EchoDelay = $5d		; The value for the echo delay.
!MaxEchoDelay = $038d		; The highest echo delay reached for the current song (cleared on SPC upload).
!NCKValue = $5f			; The value for the noise clock.

!PauseMusic = $0388		; Pauses the music if not zero.  Used in the pause SFX.

!ChSFXPtrs = $20		; Two bytes per channel, so $20 - $2f.
!ChSFXNoteTimer = $d0		; Actually $01d0.  Use setp/clrp.
!ChSFXTimeToStart = $d1		; Time until the SFX on this channel starts. (Same as above, use setp and clrp).
!ChSFXNoteTimerBackup = $03d1	; Used to save space when two consecutive notes use the same length.
!ChSFXPtrBackup = $03c0		; A copy of $20w, only updated when a sound effect starts.  Used by the #$FE command to restart a sound effect.

!ArpLength = $0310		; The length between each change in pitch, measured in ticks, for the arpeggio command.
!ArpNotePtrs = $0221		; The pointer for the sequence of pitch changes for the current arpeggio.
!ArpTimeLeft = $0311		; How much time until we get the next note.
!ArpNoteIndex = $0230		; What note number we're on.
!ArpNoteCount = $0261		; How many notes there are.
!ArpCurrentDelta = $0270	; The current pitch change for the current channel, in semitones.
!ArpType = $0231		; The arpeggio type.  0 = arpeggio, 1 = trill, 2 = glissando.
!ArpSpecial = $02e0		; The index of the loop point for this arpeggio, or the pitch difference for trill and glissando.

!PreviousNote = $0271		; The most recently played note for this channel.

!WaitTime = $6b			

!SecondVTable = $6f		; Set to 1 if we're using the N-SPC velocity table.

!PlayingVoices = $0382		; Each bit is set to 1 if there's currently a voice playing on that channel (either by music or SFX).
!SpeedUpBackUp = $0384		; Used to restore the speed after pausing the music.

!RestGAINReplacement = $0390	; What GAIN should be used in place of a rest.  If it's zero, then GAIN is not used in place of a rest.
!InRest = $0391			; Set if the current channel is "playing" a rest.

!PreEndGAIN = $03a0		; What GAIN should be used before a note ends.  If it's zero, then nothing changes.
!PreEndGAINLength = $03a1	;

macro DDEEFix()
	mov	a, $90+x
	beq	+
	mov	a, $02b0+x
	bra ++
+
	mov	a, $02d1+x
	mov	$02b0+x, a
++
endmacro

arch spc700-raw
org $000000
base $0400			; Do not change this.

{		; Program setup
	clrp
	mov   x, #$cf
	mov   sp, x              ; set SP to 01cf
	mov   a, #$00
	
	mov	y, #$00
	
-	mov	$0100+y, a
	dbnz	y, -
-	mov	$0200+y, a
	dbnz	y, -
-	mov	$0300+y, a
	dbnz	y, -
	
	movw	$00, ya
	

	
	
	mov   x, #$0b
L_0529:
	mov   a, DefDSPRegs+x
	mov   y, a
	mov   a, DefDSPValues+x
	call  DSPWrite             ; write A to DSP reg Y
	dec   x
	bpl   L_0529             ; set initial DSP reg values
	
	mov   $f1, #$f0		; Reset ports, disable timers
	mov   $fa, #$10		; Set Timer 0's frequency to 2 ms
	mov   $51, #$36		; Set the tempo to #$36
	mov   $f1, #$01		; Reset and start timer 0
}			
; main loop

{ ; The main program loop.
MainLoop:
	print "MainLoopPos: $",pc
	mov   y, $fd
	beq   MainLoop             ; wait for counter 0 increment
	push  y

	mov   a, #$38
	mul   ya
	clrc
	adc   a, $44
	mov   $44, a
	bcc   L_0573
	inc   $45
	
	call	ProcessSFX
	
	call  ProcessAPU0Input
	call  ProcessAPU1Input
	call  ProcessAPU3Input
	mov   x, #$00
	call  ReadInputRegister             ; read/send APU0
	mov   x, #$01
	call  ReadInputRegister             ; read/send APU1
	mov   x, #$03
	call  ReadInputRegister             ; read/send APU3
L_0573:
	mov   a, $51
	pop   y
	mul   ya
	clrc
	adc   a, $49
	mov   $49, a
	bcc   L_058D
	mov   a, !PauseMusic
	bne   L_0586
	call  ProcessAPU2Input		; Also handles playing the current music.
L_0586:
	mov   x, #$02
	call  ReadInputRegister             ; read/send APU2
	
	setp				; MODIFIED CODE
	movw  ya, $66			;
	clrp				; Send the output values two at a time.
	movw  $f4, ya			;
	setp				;
	movw  ya, $68			;	
	clrp				;
	movw  $f6, ya			;
	
	bra   MainLoop             ; restart main loop
L_058D:
	mov   a, $06             ; if writing 0 to APU2 then
	beq   MainLoop             ;   restart main loop
	
{ ; Execute code for each channel.

	
	
	mov   x, #$0e            ; foreach voice
	mov   $48, #$80
L_0596:
	mov   a, $31+x
	beq   L_059D             ; skip call if vptr == 0
	call  HandleVoice             ; do per-voice fades/dsps?
L_059D:
	lsr   $48
	dec   x
	dec   x
	bpl   L_0596             ; loop for each voice
	bra   MainLoop             ; restart main loop

}
	
}	
; send 04+X to APUX; get APUX to 00+X with "debounce"?
ReadInputRegister:
{
	mov   a, x		
	mov   y, a

L_05AC:
	mov   a, $f4+x		; \ Get the input byte
	cmp   a, $f4+x		; | Keep getting it until it's "stable"
	bne   L_05AC		; /
	mov   y, a		; \ 
	mov   a, $08+x		; |
	mov   $08+x, y		; |
	cbne  $08+x, L_05C1	; |
	mov   y, #$00		; |
	mov   $00+x, y		; |
L_05C0:				; |
	ret			; /
L_05C1:				; \
	mov   $00+x, y		; |
	mov   a, y		; |
	ret			; / 
}	

	
; handle a note vcmd
NoteVCMD:			
{	
				; X should contain the current channel * 2.
				; A should contain the note (can be percussion or a normal pitch note).
	cmp	a, #$d0
	bcs	PercNote             ; percussion note
	cmp	a,#$C6			;;;;;;;;;;;;Code change
	beq	L_05CD
	bcs	if_rest
	bra	NormalNote
if_rest:
	mov	a, #$01
	mov	!InRest+x, a
	
	mov	a, $48
	and	a, $1D
	bne	L_05CD
	
	mov	a, !RestGAINReplacement+x
	bne	UseGainInstead
	
	mov	a, $48
	;mov	y,#$5C
	call	KeyOffVoices
	;call	DSPWrite
	eor	a, #$FF
	and	a, $0162
	mov	$0162, a
;restret:
;	bcc	NormalNote             ; normal notenum OR $80
L_05CD:
	ret
UseGainInstead:
	push	a			; 
	mov	a, x			; \
	lsr	a			; | GAIN Register into y
	xcn	a			; |
	or	a, #$07			; |
	mov	y, a			; /
	pop	a			; 
	call	DSPWrite		; Write
	dec	y			; \
	dec	y			; | Clear ADSR bit to force GAIN.
	mov	a, #$7f			; |
	jmp	DSPWrite		; /
	
PercNote:
	
	mov	$c1+x, a
	setc
	sbc	a, #$d0
	mov	y, #$07
	mov	$14, #PercussionTable
	mov	$15, #PercussionTable>>8
	call	ApplyInstrument             ; set sample A-$D0 in bank $5FA5 width 6

	;bne	L_05CD             ; return if 1D vbit set
	;mov	y, #$05
	;mov	$14, #PercussionTable		; Restore the percussion table.
	;mov	$15, #PercussionTable>>8	; EffectModifier modifies 
	;mov	a, ($14)+y         ; get perc note num from instr tbl
NormalNote:						;;;;;;;;;;/ Code change
	
	and	a, #$7f		; Right now the note is somewhere between #$80 and #$C6 or so.  Get rid of the MSB to bring it down to #$00 - #$46
	push	a			; MODIFIED CODE
	
	mov	a, #$00
	mov	!InRest+x, a
	
	mov	a, !RestGAINReplacement+x
	or	a, !PreEndGAIN+x
	beq +
	call	RestoreInstrumentInformation	; If rests are using GAIN, then we need to restore the current instrument "settings".
	+
	mov	a, $48		; If $48 is 0, then this is SFX code.
	beq	NoPitchAdjust	; Don't adjust the pitch.
	
	
	mov	a, $02d1+x	; \ 
	mov	$02b0+x, a	; / Portamento (tuning?) into $02b0+x	
	
	
	pop	a
	clrc				; \ Add the global transpose
	adc	a, $43 			; /
	clrc				; \
	adc	a, !HTuneValues+x		; / Add the h tune...
	clrc				; \
	adc	a, !ArpCurrentDelta+x	; / Add the arpeggio delta...
	
	bra +
NoPitchAdjust:
	mov	a, #$00
	mov	$02b0+x, a
	pop	a
+
	mov	$02b1+x, a	; $02b1 gets the note to play.
	mov	a, #$00		; \ 
	mov	$0330+x, a	; | 
	mov	$0360+x, a	; | Zero out some addresses..?
	mov	$a0+x, a	; |
	mov	$0110+x, a	; |
	mov	$b0+x, a	; /
	or	($5c), ($48)       ; set volume changed flg
	or	($47), ($48)       ; set key on shadow vbit
	mov	a, $0300+x	; \ 
	mov	$90+x, a	; / 
	beq	L_062B
	mov	a, $0301+x	; Beyond here it gets a bit crazy.  No clue what's happening.
	mov	$91+x, a	; A bit farther below it looks like it calculates the pitch for the current note.
	mov	a, $0320+x	; At the end it returns.
	bne	L_0621		;
	mov	a, $02b1+x	;
	setc			;
	sbc	a, $0321+x	;
	mov	$02b1+x, a	;
L_0621:				;
	mov	a, $0321+x	;
	clrc			;
	adc	a, $02b1+x	;
	call	CalcPortamentoDelta
L_062B:
	mov	a, $02b1+x	;
	mov	y, a		;
	%DDEEFix()
	;mov	a, $02b0+x	;
	movw	$10, ya		;
; set DSP pitch from $10/11
SetPitch:			;
	push	x
	mov	a, $11
	asl	a
	mov	y, #$00
	mov	x, #$18
	div	ya, x
	mov	x, a
	mov	a, PitchTable+1+y
	mov	$15, a
	mov	a, PitchTable+0+y
	mov	$14, a             ; set $14/5 from pitch table
	mov	a, PitchTable+3+y
	push	a
	mov	a, PitchTable+2+y
	pop	y
	subw	ya, $14
	mov	y, $10
	mul	ya
	mov	a, y
	mov	y, #$00
	addw	ya, $14
	mov	$15, y
	asl	a
	rol	$15
	mov	$14, a
	bra	+
-	lsr	$15
	ror	a
	inc	x
+	cmp	x, #$06
	bne	-
	mov	$14, a
	pop	x
	mov	a, $02f0+x
	mov	y, $15
	mul	ya
	movw	$16, ya
	mov	a, $02f0+x
	mov	y, $14
	mul	ya
	push	y
	mov	a, $0210+x
	mov	y, $14
	mul	ya
	addw	ya, $16
	movw	$16, ya
	mov	a, $0210+x
	mov	y, $15
	mul	ya
	mov	y, a
	pop	a
	addw	ya, $16
	movw	$16, ya
	mov	a, x               ; set voice X pitch DSP reg from $16/7
	xcn	a                 ;  (if vbit clear in $1a)
	lsr	a
	or	a, #$02
	mov	y, a               ; Y = voice X pitch DSP reg
	mov	a, $16
	call	DSPWriteWithCheck
	inc	y
	mov	a, $17
				; write A to DSP reg Y if vbit clear in $1d
DSPWriteWithCheck:	
	push	a
	mov	a, $48
	and	a, $1d
	pop	a
	bne	+
				; write A to DSP reg Y
DSPWrite:
	mov	$f2, y
	mov	$f3, a
+	
	ret
	
}


EffectModifier:					; Call this whenever either $1d or the various echo, noise, or pitch modulaion addresses are modified.
{	
	push	x
	mov	$10, #!MusicPModChannels	;
	mov	$12, #!SFXPModChannels		;
	mov	$14, #$d1			; The DSP register for pitch modulation - $10 and reversed.
	mov	y, #$00				;
	mov	$11, y				;
	mov	$13, y				;
	
						; $10 = the current music whatever
						; $12 = the current SFX whatever
-						
						; Formula: The output for the DSP register is
						; S'M + SE
						; Where 
						; M is !WhateverMusicChannels,
						; E is !WhateverSFXChannels.
						; and S is $1d (the current channels for which SFX are enabled)
						; Yay logic!
						
	mov	a, $1d				; \ a = S
	eor	a, #$ff				; | a = S'
	and	a, ($10)+y			; / a = S'M
	
	mov	$15, a

	mov	a, ($12)+y			; \ a = S
	and	a, $1d				; | a = SE
	or	a, $15				; / a = S'M + SE
	
	push	y

	mov	y, a

	inc	$14				; \
	mov	a, $14				; | Get the next DSP register into a.
	xcn	a				; /
	
	mov	$f2, a				; \ Write to the relevant DSP register.
	mov	$f3, y				; / (coincidentally, the order is the opposite of DSPWrite)
	
	pop	y				; \ Do this three times.
	;inc	y				; |
	inc	y				; |
	cmp	y, #$03				; |
	bne	-				; /

	pop	x
	ret
}



}
ProcessSFX:				; Major code changes ahead.
{					; Originally, the SMW SFX were handled within their port handling routines.
					; This meant that there was a near duplicate copy of the 1DF9 code for 1DFC SFX.
					; It also meant that SFX were limited to just #4 and #6 (#7 was a special case).
					; This fixes that.
	
	mov	$48, #$00		; Let NoteVCMD know that this is SFX code.
	
	mov	x, #$0e			; For each voice (x = current channel * 2)
	mov	$18, #$80		; $18 = bitwise indicator of current channel
.loop					;
	mov	a, !ChSFXPtrs+1+x	; If the high byte is zero, then there's no SFX here.  Skip it.
	beq	.nothing		;
	mov	$46, x			; $46 gets the channel currently being processed.
	call	HandleSFXVoice		;
.nothing				;
	lsr	$18			;
	dec	x			;
	dec	x			;
	bpl	.loop			;
	jmp	EffectModifier		;
}

GetNextSFXByte:
{
	mov	x, $46			; Ensure x contains the channel number * 2.
	mov	a, (!ChSFXPtrs+x)	; Get the byte.
	push	p			; Remember the flags (negative and zero).
	inc	!ChSFXPtrs+x		; \ 
	bne	+			; | Increase the correct SFX pointer.
	inc	!ChSFXPtrs+1+x		; /
+					;
	pop	p			; Get the flags back.  inc modifies them.
	ret				;
}


	
	

EndSFX:
{
	; Original code here would have cleared out $04, etc.
	; We can't do that, though, since it's impossible to know what input port was responsible for this sound effect.
	; Not that it's much of an issue...just useful for sanity checks and such, really.
	
	mov	a, #$00			; \ Zero out the high byte of this SFX
	mov	!ChSFXPtrs+1+x, a	; / This will ensure that it's no longer processed.
					; Old code simply set $04+inputport to 0 to do this.
					; Of course, that doesn't work so well when some channels don't map to input ports...
				
	
	mov	a, $18			; \
	eor	a, #$ff			; | Clear the bit of $1d that this SFX corresponds to.
	mov	$10, a			; |
	and	a, $1d			; |
	mov	$1d, a			; /
	
	mov	a, $10			; \
	and	a, !SFXNoiseChannels	; | Turn noise off for this channel's SFX.
	mov	!SFXNoiseChannels, a	; /

	call	EffectModifier
	


	mov	x, $46			; \ 

RestoreInstrumentInformation:		; Call this with x = currentchannel*2 to restore all instrument properties for that channel.
	
	mov	a, !BackupSRCN+x	; |
	bne	.restoreSample		; |
	mov	a, $c1+x		; | Fix instrument.
	beq	+			; |
	dec	a			; |
	jmp	SetInstrument		; |
+					; /
	ret				;
					;
.restoreSample				; \ 
	jmp   RestoreMusicSample	; | Fix sample.
}
	
HandleSFXVoice:
{
	setp
	dec	!ChSFXNoteTimer+x
	clrp
	beq	+	
	jmp	.processSFXPitch
+
.getMoreSFXData
	call	GetNextSFXByte
	beq	EndSFX			; If the current byte is zero, then end it.
	bmi	.noteOrCommand		; If it's negative, then it's a command or note.
	mov	!ChSFXNoteTimerBackup+x, a			
					; The current byte is the duration.
	
	call	GetNextSFXByte		; Get the next byte.  It's either a volume or a command/note.
	bmi	.noteOrCommand		; If it's negative, then it's a command or a note.
	push	a			; \ This is a volume command.  Remember it for a moment.
	mov	a, $46			; | 
	lsr	a			; |
	xcn	a			; | Put the left volume DSP register for this channel into y.
	mov	y, a			; |
	pop	a			; |
	call	DSPWrite		; / Set the volume for the left speaker.
	inc	y			; \
	call	DSPWrite		; / Set the volume for the right speaker.  We might change it later, but this saves space.
	call	GetNextSFXByte		;
	bmi	.noteOrCommand		; If the byte is positive, then set the right volume to the byte we just got.

	call	DSPWrite		; > Set the volume for the right speaker.
	call	GetNextSFXByte		;
	bra	.noteOrCommand		; At this point, we must have gotten a command/note.  Assume that it is, even if it's not.
	
.executeCode				; 
	call	GetNextSFXByte		; \ 
	mov	$14, a			; | Get the address of the code to execute and put it into $14w
	call	GetNextSFXByte		; |
	mov 	$15, a			; / 
	push	x			; \ 
	mov	x, #$00			; | Jump to that address
	call	+			; | (no "call (d+x)")
	pop	x			; / 
	bra	.getMoreSFXData		;
+					;
	jmp	($14+x)			;

.noteOrCommand				; SFX commands!
	cmp	a, #$da			; \ 
	beq	.instrumentCommand	; / $DA is the instrument command.
	cmp	a, #$dd			; \ 
	beq	.pitchBendCommand	; / $DD is the pitch bend command.
	cmp	a, #$eb			; \ 
	beq	.pitchBendCommand2	; / $EB is...another pitch bend command.
	cmp	a, #$fd			; \ 
	beq	.executeCode		; / $FD is the code execution command.
	cmp	a, #$fe			; \
	beq	.loopSFX		; / $FE is the restart SFX command.
	cmp	a, #$ff			; \ 
	bne	.playNote		; / Play a note.
	mov	y, #$03			; Move back three bytes.
	;mov	x, $46			; \
-	mov	a, !ChSFXPtrs+x		; |
	bne	+			; |
	dec	!ChSFXPtrs+1+x		; | #$FF is the loop the last note command.
+					; |
	dec	!ChSFXPtrs+x		; |
	dbnz	y, -
	bra	.getMoreSFXData		; /
; other $80+
.loopSFX
	mov	a, !ChSFXPtrBackup+1+x	; \
	mov	!ChSFXPtrs+1+x, a	; | Set the current pointer to the backup pointer,
	mov	a, !ChSFXPtrBackup+x	; | Thus restarting this sound effect.
	mov	!ChSFXPtrs+x, a		; /
	bra	.getMoreSFXData
	
.playNote
	call	NoteVCMD		; Loooooooong routine that starts playing the note in A on channel (X/2).
	mov	a, $18
	call	KeyOnVoices		; Key on the voice.
.setNoteLength
	mov	a, !ChSFXNoteTimerBackup+x	
					; \ Get the length of the note back
	setp
	mov	!ChSFXNoteTimer+x, a	; / And since it was actually a length, store it.
	clrp
.processSFXPitch
	clr1	$13.7			; I...still don't know what $13.7 does...
	mov	a, $90+x		; pitch slide counter
	beq	+
	call	L_09CD			; add pitch slide delta and set DSP pitch
	bra	.return1
+
	mov	a, #$02			; \
	;setp				; |
	cmp	a, !ChSFXNoteTimer|$0100+x	; |
	;clrp				; |
	bne	.return1		; | If the time between notes is 2 ticks
	mov	a, $18			; | Then key off this channel in preparation for the next note.
	;mov	y, #$5c			; |
	;call	DSPWrite		; /
	call	KeyOffVoices
.return1
	ret
; DD
.pitchBendCommand			; This command is all sorts of weird.
	call	GetNextSFXByte		; The pitch of the note is this byte.
	call	NoteVCMD		; 
	mov	a, $18			; \
	call	KeyOnVoices		; /
; EB
.pitchBendCommand2
	call	GetNextSFXByte		;
	mov	$91+x, a		; Dunno what $91 does.
	call	GetNextSFXByte		;
	mov	$90+x, a		; Dunno what $90 does.
	push	a			;
	call	GetNextSFXByte		;
	pop	y			; I DON'T KNOW WHAT ANY OF THIS DOES! *sobs*
	call	CalcPortamentoDelta	; \ Calculate the pitch difference.
	bra	.setNoteLength		; /

; DA
.instrumentCommand
	mov	a, #$00			; \ Disable sub-tuning
	mov	$02f0+x, a		; /
	
	mov	a, $18			; \
	eor	a, #$ff			; |
	and	a, !SFXNoiseChannels	; | Disable noise for this channel.
	mov	!SFXNoiseChannels, a	; /
					; (EffectModifier is called a bit later)
.getInstrumentByte
	call	GetNextSFXByte		; Get the parameter for the instrument command.
	bmi	.noise			; If it's negative, then it's a noise command.
	mov	y, #$09			; \ No noise here!
	mul	ya			; | Set up the instrument table for SFX
	mov	x, a			; |
	mov	a, $46			; | \
	xcn	a			; | | Get the correct DSP register "base" into y.
	lsr	a			; | |
	mov	y, a			; | /
	mov	$12, #$08		; / 9 bytes of instrument data.
-					; \
	mov	a, SFXInstrumentTable+x	; |
	call	DSPWrite		; | Loop that sets various DSP registers.
	inc	x			; |
	inc	y			; |
	dbnz	$12, -    		; / 
	mov	a, SFXInstrumentTable+x ; \
	mov	x, $46			; |
	mov	$0210+x, a		; / Something to do with pitch...?
	jmp	.getMoreSFXData		; / We're done here; get the next SFX command.
.noise	
	and	a, #$1f			; \ Noise can only be from #$00 - #$1F
	call	ModifyNoise
	;mov	$f2, #$6c		; |
	;or	a, $f3			; | Set 6c to whatever it is, but with the correct noise frequency.
	;mov	$f3, a			; /
	;mov	y, #$6c			;
	;call	DSPWrite		;
	
	or	(!SFXNoiseChannels), ($18)

	bra	.getInstrumentByte	; Now we...go back until we find an actual instrument?  Odd way of doing it, but I guess that works.
}

if !PSwitchIsSFX = !true
PSwitchCh5:
	db $DA, $02			; @2
	db $30, $00,      $C6 		; r=24
	db $20, $00, $26, $A4		; y0o4c=16
	db $10, $0A, $1D, $9F		; y5o3g=8
	db $10, $00,      $C6		; r=8
	db $10, $13, $13, $AB		; y10o4g=8
	db $10, $17, $0F, $9F		; y12o3c=8
	db $20, $1D, $0A, $A4		; y15o4c=4
	db $10, $26, $00, $9F		; y20o3g=8
	
	db $30, $00,      $C6		; r=24
	db $20, $26, $00, $A5		; y20o4c+=16
	db $10, $1D, $0A, $A0		; y15o3g+=8
	db $10, $00,      $C6		; r=8
	db $10, $13, $13, $AC		; y10o4g+=8
	db $10, $0C, $18, $A0		; y7o3c+=8
	db $20, $1D, $0A, $A5		; y5o4c+=4
	db $10, $00, $26, $A0		; y0o3g+=8
	db $FE				; loop
	
PSwitchCh6:
	db $DA, $02	; @2
	db $20, $26, $00, $8C		; y0o2c=16	
	db $40,           $93		; y0o2g=8^24
	db $30,           $98		; y0o3c=24
	db                $93		; y0o2g=24

	db $20, $04, $22, $8D		; y2o2c+=16	
	db $40, $0A, $1D, $94		; y5o2g+=8^24
	db $30, $13, $13, $99		; y10o3c+=24
	db $30, $1E, $08, $94		; y16o2g+=24
	db $FE
	
PSwitchCh7:
	db $DA, $09			; @9
	db $10, $0D, $B0		; o4g=8
	db 	$B0			; o4g=8
	db	$B9			; o5e=8
	db	$B9			; o5e=8
	db $FE			
	
endif


SpeedUpMusic:
	mov	a, #$0a
	mov	$0387, a
	mov	a, $51
	call	L_0E14             ; add #$0A to tempo; zero tempo low      ;ERROR * 2
	mov	a, #$1d
	mov	$03, a
	mov	$00, a
	mov	a, #$00
	mov	$04, a
	mov	$07, a
	;ret
;
ProcessAPU0Input:
	mov	a, $00				; \ If the value from $1DF9 was $80+, then play the "time is running out!" jingle.
	bmi	SpeedUpMusic			; /
	cmp	$04, #$1d			; \ No sound effects can overwrite #$1d
	beq	+				; /
.speedUpSFXIsOff
	mov	x, #$0c				; \ 
	mov	y, #$00				; | 
	mov	$10, #$40			; | 
--						; | 
	bra 	ProcessSFXInput			; / Actually a subroutine.
+
	cmp	!ChSFXPtrs+$0d, #$00		; \ But if #$1d is no longer playing... 
	beq	.speedUpSFXIsOff		; /
	ret
	
if !PSwitchIsSFX = !true
PlayPSwitchSFX:
	mov	!ChSFXPtrs+$0a, #PSwitchCh5
	mov	!ChSFXPtrs+$0b, #PSwitchCh5>>8
	mov	!ChSFXPtrs+$0c, #PSwitchCh6
	mov	!ChSFXPtrs+$0d, #PSwitchCh6>>8
	mov	!ChSFXPtrs+$0e, #PSwitchCh7
	mov	!ChSFXPtrs+$0f, #PSwitchCh7>>8

	mov	y, #$06				; \
-						; |
	mov	a, !ChSFXPtrs+$0009+y		; | Copy the SFX pointers to the backup pointers.
	mov	!ChSFXPtrBackup+$09+y, a	; |
	dbnz	y, -				; /
	
	mov	a, #$03
	setp
	mov	!ChSFXNoteTimer+$0e, a		; \
	mov	!ChSFXNoteTimer+$0c, a		; | Set the timers to 3, not 1.
	mov	!ChSFXNoteTimer+$0a, a		; /
	clrp
	;mov	$f2, #$5c			; \ Key off these 3 voices.
	;mov	$f3, #$e0			; /
	mov	a, #$e0
	call	KeyOffVoices
	
	or	$1d, #$e0			; Mute these channels.
	ret

endif
	
ProcessAPU3Input:
if !PSwitchIsSFX = !true
	mov	a, $03				;
	bmi	PlayPSwitchSFX			;
endif
	cmp	$07, #$1d			; \ No sound effects can overwrite #$1d
	beq	+				; /
.speedUpSFXIsOff
	mov	x, #$0e				; \
	mov	y, #$03				; | 
	mov	$10, #$80			; |
	bra	--				; / Saving 1 whole byte!
+
	cmp	!ChSFXPtrs+$0f, #$00		; \ But if #$1d is no longer playing... 
	beq	.speedUpSFXIsOff		; /
	ret


ProcessSFXInput:				; X = channel number * 2 to play a potential SFX on, y = input port to process, $10 = bitwise indicator of the current channel.
{
	mov	a, $0000+y			; \ If we've just received data from the SNES, prepare to process it.
	bne	.prepareForSFX			; / This involves keying off the correct channel.
						;
	setp					; \
	mov	a, !ChSFXTimeToStart+x		; | Check to see if we've waited long enough to start the SFX.
	clrp					; |
	bne	.waitingToStart			; /
						;
;	mov	a, $0004+y			;
;	beq	.return				;
;	call	ProcessSFX			;
;;return						;
	ret					;
		
.prepareForSFX					;
	mov	$0004+y, a			; > Tell the SPC to process this SFX.
	setp					; \
	mov	a, #$01				; | We need to wait 2 ticks before processing SFX.
	mov	!ChSFXTimeToStart+x, a		; |
	clrp					; /
	mov	a, $10				; \
	;mov	y, #$5c				; | Key off for this voice.
	;call	DSPWrite			; /
	call	KeyOffVoices
	;mov	a, $1d				; \
	;or	a, $10				; | Set the correct bit of $1D.
	;mov	$1d, a				; /
	or	($1d), ($10)			; Man, the SPC rocks.
	
	
						;
	call	EffectModifier
	
	mov	a, #$00				; \
	mov	$0300+x, a			; /
.return						;
	ret					;
						;
.waitingToStart					;
	setp					;
	dec	!ChSFXTimeToStart+x		;
	clrp					;
	bne	.return				;
						;
	push	y				;
	mov	a, $0004+y			; \ 
	asl	a				; |
	mov	y, a				; | Y = SFX * 2, index to a table.
	pop	a				; | If a is 0, then the table we load from table 1.
	cmp	a, #$00				; | Otherwise, we load from table 2.
	beq	+				; /
						;
	mov	a, SFXTable1-2+y		; \
	push	a				; | Move the pointer to the current SFX to the correct pointer.
	mov	a, SFXTable1-1+y		; |
	bra	.gottenPointer			;
						;
+						;
	mov	a, SFXTable0-2+y		; \
	push	a				; |
	mov	a, SFXTable0-1+y		; /
	
.gottenPointer
	mov	!ChSFXPtrs+1+x, a		; Store to current pointer
	mov	!ChSFXPtrBackup+1+x, a		; And backup pointer.
	pop	a				;
	mov	!ChSFXPtrs+x, a			; Store to current pointer.
	mov	!ChSFXPtrBackup+x, a		; And backup pointer.
	
	mov	a, #$01
	setp
	mov	!ChSFXNoteTimer+x, a		; Prevent an edge case.
	clrp
	ret

}
	

HandleYoshiDrums:				; Subroutine.  Call it any time anything Yoshi-drum related happens.

	mov	a, $0386			;
	bne	.drumsOn			;
					
					
	mov	a, $6e				; 
	or	a, $5e				;
	bra	+
	
.drumsOn					
	mov	a, $6e				; \ $5E = ($5E --/--> $6E)
	eor	a, #$ff				; | (Or $5E = $5E & ~$5C)
	and	a, $5e				; / Basically, we're reverting whatever the Yoshi drums did to $5E.
+
	mov	$5e, a
	;mov	y, #$5c				; \ Key off the voices in $5e.
	;call	DSPWrite			; /
	call	KeyOffVoices
	ret

EnableYoshiDrums:				; Enable Yoshi drums.
	mov	a, #$01
	bra	+


DisableYoshiDrums:				; And disable them.
	mov	a, #$00
+
	mov	$0386, a
	call	HandleYoshiDrums

	bra	ProcessAPU1SFX

L_099C:
	mov	$f2, #$6c		; Mute, disable echo.  We don't want any rogue sounds during upload
	mov	$f3, #$60		; and we ESPECIALLY don't want the echo buffer to overwrite anything.
	mov	!NCKValue, #$60
	
	;mov	$f2, #$5c         ; key off all voices now
	;mov	$f3, #$ff
	mov	a, #$ff
	call	KeyOffVoices
	
	mov	$f2, #$7d		; Also set the delay to 0.
	mov	$f3, #$00		; 


	mov	a, #$00
	mov	$02, a			; 
	mov	$06, a			; Reset the song number
	mov	$0A, a			; 
	mov	$1d, a
	mov	a, !MaxEchoDelay	;
	call	EffectModifier

	jmp	L_12F2             ; do standardish SPC transfer                                ;ERROR
				; Note that after this, the program is "reset"; it jumps to wherever the 5A22 tells it to.
				; The stack is also cleared.
	;ret
; add pitch slide delta and set DSP pitch
L_09CD:
	mov	a, #$b0
	mov	y, #$02            ; pitch (notenum fixed-point)
	dec	$90+x
	call	L_1075             ; add pitch slide delta to value                                ;ERROR
	mov	a, $02b1+x
	mov	y, a
	
	%DDEEFix()
	
	;mov	a, $02b0+x
	movw	$10, ya
	mov	$48, #$00          ; vbit flags = 0 (to force DSP set)
	jmp	SetPitch             ; force voice DSP pitch from 02B0/1
;

ForceSFXEchoOff:
	mov	a, #$00
	bra	+
ForceSFXEchoOn:
	mov	a, #$ff
+	mov	!SFXEchoChannels, a
	call	EffectModifier
	bra	ProcessAPU1SFX
	
ProcessAPU1Input:				; Input from SMW $1DFA
	mov	a, $01
	cmp	a, #$ff
	beq	L_099C
	cmp	a, #$02		; 02 = turn on Yoshi drums
	beq	EnableYoshiDrums	;
	cmp	a, #$03		; 03 = turn off Yoshi drums
	beq	DisableYoshiDrums	;
	cmp	a, #$05		;
	beq	ForceSFXEchoOff	;
	cmp	a, #$06		;
	beq	ForceSFXEchoOn	;
	cmp	a, #$01		; 01 = jump SFX
	beq	L_0A14		;
	mov	a, $05		;
	cmp	a, #$01		;
	beq	ProcessAPU1SFX
	mov	a, $01
	cmp	a, #$04
	beq	L_0A0E             ; (jmp $0ace)
;
ProcessAPU1SFX:
	mov	a, $05		; 
	cmp	a, #$01		; \ If the currently playing SFX is the jump SFX
	beq	L_0A51		; / Then process that.
	cmp	a, #$04		; Else, if it's the girder SFX, then do that stuff.
	beq	L_0A11             ; (jmp $0b08)
L_0A0D:
	ret
L_0A0E:
	jmp	L_0ACE
L_0A11:
	jmp	L_0B08
	
; $01 = 01
L_0A14:
	mov	$05, a		;
	mov	a, #$04		; \
	mov	$0383, a	; / $0383 is a timer for the jump sound effect?
	mov	a, #$80		; \ Key off channel 7.
	;mov	y, #$5c		; |
	;call	DSPWrite	; /
	call	KeyOffVoices
	set1	$1d.7		; Turn off channel 7's music
	mov	a, #$00		;
	mov	y, #$20		;
L_0A28:				; \
	mov	$02ff+y, a	; | $0300-$0320 = #$00.  Dunno why, though.
	dbnz	y, L_0A28	; | 
	ret			; /
L_0A2E:
	dec	$0383
	bne	L_0A0D
	mov	$1c, #$30
	bra	L_0A68
L_0A38:
	cmp	$1c, #$2a
	bne	L_0A99
	mov	$46, #$0e
	mov	x, #$0e
	mov	y, #$00
	mov	$9f, y
	mov	y, #$12
	mov	$9e, y
	mov	a, #$b9
	call	CalcPortamentoDelta
	bra	L_0A99
;
L_0A51:						;;;;;;;;/ Code change
	mov	a, $0383			; Process the jump SFX.
	bne	L_0A2E			; I don't really know what's going on here, so I won't pretend to.
	dbnz	$1c, L_0A38
	mov	$05, #$00
	clr1	$1d.7
	mov	x, #$0e
	mov	a, !BackupSRCN+$0e
	bne	RestoreSample7
	mov	a, $cf
	beq	L_0A67
	jmp	L_0D4B			; Restore the current instrument on the channel?
L_0A67:
	ret
RestoreSample7:
	jmp	RestoreMusicSample
L_0A68:
	call	L_0AB1
	mov	a, #$b2
	mov	$46, #$0e
	mov	x, #$0e
	call	NoteVCMD
	mov	y, #$00
	mov	$9f, y
	mov	y, #$05
	mov	$9e, y
	mov	a, #$b5
	call	CalcPortamentoDelta
	mov	a, #$38
	mov	$10, a
	mov	y, #$70
	call	DSPWrite
	mov	a, #$38
	mov	$10, a
	mov	y, #$71
	call	DSPWrite
	mov	a, #$80
	call	KeyOnVoices
L_0A99:
	mov	a, #$02
	cbne	$1c, L_0AA5
	mov	a, #$80
	;mov	y, #$5c
	call	KeyOffVoices
L_0AA5:
	clr1	$13.7
	mov	a, $9e
	beq	L_0AB0
	mov	x, #$0e
	call	L_09CD
L_0AB0:
	ret
L_0AB1:
	mov	a, #$08
L_0AB3:
	mov	y, #$09
	mul	ya
	mov	x, a
	mov	y, #$70
	mov	$12, #$08
L_0ABC:
	mov	a, SFXInstrumentTable+x
	call	DSPWrite
	inc	x
	inc	y
	dbnz	$12, L_0ABC
	mov	a, SFXInstrumentTable+x
	mov	$021e, a
	ret
; $01 = 04 && $05 != 01
L_0ACE:
	mov	$05, a
	mov	a, #$04
	mov	$0383, a
	mov	a, #$80
	;mov	y, #$5c
	call	KeyOffVoices             ; key off voice 7 now
	set1	$1d.7
	mov	a, #$00
	mov	y, #$20
L_0AE2:
	mov	$02ff+y, a
	dbnz	y, L_0AE2
L_0AE7:
	ret
;
L_0AE8:
	dec	$0383
	bne	L_0AE7
	mov	$1c, #$18
	bra	L_0AF7
L_0AF2:
	cmp	$1c, #$0c
	bne	L_0B33
L_0AF7:
	mov	a, #$07
	call	L_0AB3
	mov	a, #$a4
	mov	$46, #$0e
	mov	x, #$0e
	call	NoteVCMD
	bra	L_0B1C
L_0B08:
	mov	a, $0383
	bne	L_0AE8
	dbnz	$1c, L_0AF2
	mov	$05, #$00
	clr1	$1d.7
	mov	x, #$0e
	mov	a, $cf
	jmp	L_0D4B
L_0B1C:
	mov	a, #$28
	mov	$10, a
	mov	y, #$70
	call	DSPWrite
	mov	a, #$28
	mov	$10, a
	mov	y, #$71
	call	DSPWrite
	mov	a, #$80
	call	KeyOnVoices
L_0B33:
	mov	a, #$02
	cbne	$1c, L_0B3F
	mov	a, #$80
	;mov	y, #$5c
	call	KeyOffVoices
L_0B3F:
	ret
				; Call this routine to play the song currently in A.
PlaySong:

	;mov	y, #$00		
	;mov	$0387, y		; Zero out the tempo modifier.
	
	mov	!SFXEchoChannels, y
L_0B5A:
	mov	$06, a		; Song number goes into $06.
	push	a
	; MODIFIED CODE START
	mov	a,#$00			; Clear various new addresses.
	mov	x,#$07			; These weren't used before, so they weren't cleared before.
-					;
	mov	$0160+x,a		;
	;mov	$245A+x,a		; These did something in AMM, but I don't know what.
	;mov	$2463+x,a		; They can't be used now, since chances are music or something else is there now.
	dec	x			;
	bpl	-			;
					;
	mov	!WaitTime, #$02		;
	;mov	WaitTimeByte-1,a	;
					;
	mov	$0c,#$02		;
	pop	a
	; MODIFIED CODE END
	asl	a			; Turn A from a song number into a pointer
	mov	y, a		
	mov	a, SongPointers-$02+y	; Get the pointer for the current song
	push	a				; MODIFIED
	mov	$40, a
	mov	a, SongPointers-$01+y
	push	a				; MODIFIED
	mov	$41, a		; $40.w now points to the current song.
	
	; MODIFIED CODE START
-	call	L_0BF0		; Get the first measure address.
	movw	$16, ya		; This is guaranteed to be valid, so save it and get the next one.
	mov	a, y		;
	bne	-			; Loop until the high byte of the measure address is 0
	
	mov	a, $16		; If the low byte of the current measure is #$FF, then we have one more to skip.
	beq	+			;
	call	L_0BF0		;
	+
	
	movw	ya, $40
	movw	!CustomInstrumentPos, ya
	
	pop	a
	mov	$41, a
	pop	a
	mov	$40, a
	; MODIFIED CODE END
	
	mov	x, #$0e            ; Loop through every channel
L_0B6D:
	mov	a, #$0a
	mov	!Pan+x, a         ; Pan[ch] = #$0A
	mov	a, #$ff
	mov	!Volume+x, a         ; Volume[ch] = #$FF
	mov	a, #$00
	mov	$02d1+x, a         ; Portamento[ch] = 0
	mov	!PanFadeDuration+x, a           ; PanFade[ch] = 0
	mov	$80+x, a           ; VolVade[ch] = 0
	mov	$a1+x, a		; Vibrato[ch] = 0
	mov	$b1+x, a		; ?
	mov	$c0+x, a           ; repeat ctr
	mov	$c1+x, a           ; Instrument[ch] = 0
	mov	$0161+x, a	; Strong portamento
	mov	!HTuneValues+x, a	
	
	mov	!ArpLength+x, a		; \
	mov	!ArpNotePtrs+x, a	; |
	mov	!ArpNotePtrs+1+x, a	; |
	mov	!ArpTimeLeft+x, a	; | All things arpeggio-related.
	mov	!ArpNoteIndex+x, a	; |
	mov	!ArpNoteCount+x, a	; |
	mov	!ArpCurrentDelta+x, a	; |
	mov	!ArpSpecial+x, a	; /
	mov	!RestGAINReplacement+x,a ;> Rests are rests, not GAIN.
	mov	!PreEndGAIN+x, a
	mov	!PreEndGAINLength+x, a
	mov	!VolumeMult+x, a	
	mov	$02f0+x, a	
	mov	$0210+x, a
	dec	x
	dec	x
	bpl	L_0B6D	
	; MODIFIED CODE START
	mov	!MusicPModChannels, a
	mov	!MusicEchoChannels, a
	mov	!MusicNoiseChannels, a
	mov	!SecondVTable, a
	; MODIFIED CODE END	
	mov	$58, a             ; MasterVolumeFade = 0
	mov	$60, a             ; EchoVolumeFade = 0
	mov	$52, a             ; TempoFade = 0
	mov	$43, a             ; GlobalTranspose = 0
	mov	!PauseMusic, a	; Unpause the music, if it's been paused.
	mov	$6e,a						; MODIFIED, channels affected by Yoshi drums.
	mov	$5e,a						; Also MODIFIED
	mov	!MasterVolume, #$c0          ; MasterVolume = #$C0
	mov	$51, #$36          ; Tempo = #$36

	
	

	mov	y, #$20
	
L_0B9C:
	mov	$02ff+y, a		
	dbnz	y, L_0B9C		; Clear out 0300-031f (this is a useful opcode...)
	
	call	EffectModifier
	bra	L_0BA5
;
L_0BA3:
	mov	$06, a		; ???
L_0BA5:
	mov	a, !NCKValue		; \ 
	and	a, #$20			; | Disable mute and reset, reset the noise clock, keep echo off.
	mov	!NCKValue, a		; |
	mov	a, #$00			; |
	call	ModifyNoise		; /
	mov	a, $1d		
	eor	a, #$ff		
	;mov	y, #$5c
	jmp	KeyOffVoices		; Set the key off for each voice to ~$1D.  Note that there is a ret in DSPWrite, so execution ends here. (goto L_0586?)
; fade volume out over 240 counts
FadeOut:
	mov	x, #$f0
	mov	$58, x
	mov	a, #$00
	mov	$59, a
	setc
	sbc	a, !MasterVolume
	call	Divide16
	movw	$5a, ya            ; set volume fade out after 240 counts
	bra	L_0BE7
;
ProcessAPU2Input:

	; MODIFIED CODE START
	
	mov	a,$0160		; Get the special AMM byte.
	and	a,#$02		; If the second bit is set, then we've enabled sync.
	beq	.nothing	; Otherwise, do nothing.
	setp			; \ 
	incw	$66		; | Increase $166.
	clrp			; / 
				; Note that this is different from AMM's code.
				; The old code never let the low byte go above #$C0.
				; A good idea in theory, but it both assumes that all
				; songs use 4/4 time, and it makes, for example,
				; using the song's time as an index to a table more difficult.
				; If the 5A22 needs 0 <= value < #$C0, it can limit the value itself.
.nothing			; 

	mov	a, $02
	bmi	FadeOut		
	beq	L_0BE7
	jmp	PlaySong             ; play song in A
L_0BE7:
	mov	a, $0c
	bne	L_0BFE
	mov	a, $06
	bne	L_0C46
L_0BEF:
	ret
; read next word at $40 into YA
L_0BF0:
	mov	y, #$00
	mov	a, ($40)+y
	incw	$40
	push	a
	mov	a, ($40)+y
	incw	$40
	mov	y, a
	pop	a
	ret
;
L_0BFE:
	dbnz	$0c, L_0BEF
L_0C01:
	call	L_0BF0             ; read next word at $40
	movw	$16, ya            ; save in $16/17
	mov	a, y               ; high byte zero?
	bne	L_0C22
	mov	a, $16             ; refetch lo byte
	beq	L_0BA3             ; key off, return if also zero
	dec	$42
	beq	L_0C1C
	bpl	L_0C15
	mov	$42, a
L_0C15:
	call	L_0BF0             ; read next word at $40
	movw	$40, ya            ; "goto" that address
	bra	L_0C01             ; and continue
L_0C1C:
	incw	$40
	incw	$40               ; skip goto address
	bra	L_0C01             ; continue
L_0C22:
	mov	y, #$0f            ; high byte not zero:
	
L_0C24:				; This short loop sets $30 to contain the pointers to each track's starting "measure."
	mov	a, ($16)+y
	mov	$30+y, a
	dec	y
	bpl	L_0C24             ; set vptrs from [$16]


	mov	x, #$0e
	mov	$48, #$80          ; foreach voice
L_0C31:
	mov	a, $31+x
	beq	L_0C40             ;  next if vptr hi = 0
	mov	a, #$01
	mov	$70+x, a           ;  set duration counter to 1
	mov	a, $c1+x
	bne	L_0C40
	call	SetInstrument             ;  set instr to 0 if no instr set
L_0C40:
	lsr	$48
	dec	x
	dec	x
	bpl	L_0C31             ; loop
L_0C46:
	mov	x, #$00
	mov	$47, x
	mov	$48, #$01          ; foreach voice
L_0C4D:
	mov	$46, x
	mov	a, $31+x
	bne	+			; (fix an out-of-range error)
	jmp	L_0CC9             ; next if vptr hi zero
+
	dec	$70+x             ; dec duration counter
	beq	L_0C57		; (fix another out-of-range error)
	jmp	L_0CC6             ; if not zero, skip to voice readahead
L_0C57:
	call	GetCommandDataFast             ; get next vbyte
	bne	L_0C7A
	mov	a, $c0+x           ; vcmd 00: end repeat/return
	beq	L_0C01             ;  goto next $40 section if rpt count 0
	dec	$c0+x             ;  dec repeat count
	bne	L_0C6E             ;  if zero then
	mov	a, $03e0+x
	mov	$30+x, a
	mov	a, $03e1+x
	bra	L_0C76             ;   goto 03E0/1
L_0C6E:
	mov	a, $03f0+x         ;  else
	mov	$30+x, a
	mov	a, $03f1+x         ;   goto 03F0/1
L_0C76:
	mov	$31+x, a
	bra	L_0C57             ;  continue to next vbyte
L_0C7A:
	bmi	L_0C9F             ; vcmds 01-7f
	
	mov	$0200+x, a         ;  set cmd as duration
	call	GetCommandDataFast             ;  get next vcmd
	bmi	L_0C9F             ;  if not note then
	push	a
	xcn	a
	and	a, #$07
	mov	y, a
	mov	a, NoteDurations+y
	mov	$0201+x, a         ; set dur% from high nybble
	pop	a
	and	a, #$0f			
	cmp	!SecondVTable, #$00	; \ 
	beq	+			; | Get the correct velocity table index
	or	a, #$10			; |
+					; |
	mov	y, a			; /
	mov	a, VelocityValues+y
	mov	$0211+x, a         ; set per-note vol from low nybble
	or	($5c), ($48)       ; mark vol changed?
	call	GetCommandDataFast             ; get next vbyte
L_0C9F:
	cmp	a, #$da
	bcc	L_0CA8             ; vcmd da-ff:
	
	call	L_0D40             ; dispatch vcmd
	bra	L_0C57             ; do next vcmd
L_0CA8:
	push	a                 ; vcmd 80-d9 (note)
	
	mov	a, $48		; Current channel being processed.
	and	a, $1d		; Check if there's a sound effect on the current channel.
	mov	$10, a		; Save it. (Modified code up until the pop a).
	mov	a, $48		; Current channel
	and	a, $5e		; Check if this channel's music is muted.
	or	a, $10		; If it's muted or there's a sound effect playing...
	mov	$10, a		; Save this status.
	
					; Warning: The code ahead gets messy thanks to arpeggio modifications.
	
	pop	a				; \ Get the current note value back
	mov	y, a			; / Put it into y for  now.

	
	cmp	y, #$c6			; \ If the note is a rest or tie, then don't save the current note pitch.
	bcs	+				; /
.anythingGoes
	mov	!PreviousNote+x, a	; Save the current note pitch.  The arpeggio command needs it.
+
	mov	a, $10
	bne	L_0CB3
	cmp	y, #$c6			; \ Ties and rests shouldn't affect anything arpeggio related.
	bcs	+			; /
	mov	a, !ArpNoteCount+x	; \ If there's currently an arpeggio playing (which handles its own notes)...
	beq	+			; | Then don't play a note.  The arpeggio handler up ahead will do it automatically.
	mov	a, #$01			; | But we do have to restart the timer so that it will work correctly (and start right now).
	mov	!ArpTimeLeft+x, a	; /
	dec	a
	dec	a
	mov	!ArpNoteIndex+x, a	; Set the note index to -1 (which will be increased to 0, where it should be for the first note).
	
	mov	a, y			; \
	cmp	a, #$c6			; | Ties/rests don't mess up glissando.
	bcs	.notGlissando		; /
	mov	a, !ArpType		; \
	cmp	a, #$02			; |
	bne	.notGlissando		; | If the arpeggio type is glissando,
	mov	a, !ArpNoteCount+x	; | We can only play one note with this on.
	dec	a			; |
	mov	!ArpNoteCount+x, a	; |
	bne	.glissandoIsStillOn	; |
	mov	!ArpCurrentDelta, a	; | If we're turning it off, then reset the delta.
	bra	.glissandoOver		; / And actually play the next note.
.glissandoIsStillOn
.notGlissando
	bra	L_0CB3
	+
.glissandoOver
	mov	a, y
	call	NoteVCMD             ; handle note cmd if vbit 1D clear
L_0CB3:
	mov	a, $0200+x
	mov	$70+x, a           ; set duration counter from duration
	mov	y, a
	mov	a, $0201+x
	mul	ya
	mov	a, y
	bne	L_0CC1
	inc	a
L_0CC1:
	mov	$0100+x, a         ; set note dur counter from dur * dur%
	bra	L_0CC9
L_0CC6:
	call	L_10A1             ; do voice readahead
L_0CC9:	
	call	HandleArpeggio	; Handle all things related to arpeggio.
	inc	x
	inc	x
	asl	$48
	bcs	L_0CD2
	jmp	L_0C4D             ; loop
L_0CD2:		
	mov	a, $52             ; do global fades
	beq	L_0CE3
	dbnz	$52, L_0CDC
	movw	ya, $52
	bra	L_0CE1
L_0CDC:
	movw	ya, $54
	addw	ya, $50
L_0CE1:
	movw	$50, ya
L_0CE3:
	mov	a, $60
	beq	L_0D03
	dbnz	$60, L_0CF4
	mov	a, #$00
	mov	y, $62
	movw	$61, ya
	mov	y, $64
	bra	L_0CFE
L_0CF4:
	movw	ya, $65
	addw	ya, $61
	movw	$61, ya
	movw	ya, $67
	addw	ya, $63
L_0CFE:
	movw	$63, ya
	call	L_0EEB
L_0D03:
	mov	a, $58
	beq	L_0D17
	dbnz	$58, L_0D0E
	movw	ya, $58
	bra	L_0D12
L_0D0E:
	movw	ya, $5a
	addw	ya, $56
L_0D12:
	movw	$56, ya
	mov	$5c, #$ff          ; set all vol chg flags
L_0D17:
	mov	x, #$0e
	mov	$48, #$80
L_0D1C:
	mov	a, $31+x
	beq	L_0D23
	call	L_0FDB             ; per-voice fades?
L_0D23:
	lsr	$48
	dec	x
	dec	x
	bpl	L_0D1C
	mov	$5c, #$00          ; clear volchg flags
	mov	a, $1d
	eor	a, #$FF		;;;;;;;;;;;;;;;Code change
	and	a, $47
	mov	$12, a
	mov	a, $0162
	push	a
	or	a, $12
	mov	$0162, a
	pop	a
	and	a, $0161
	eor	a, #$FF
	and	a, $12		
	;and     a, $47
; key on voices in A
KeyOnVoices:
	push	a
	;mov	y, #$5c
	mov	a, #$00
	call	KeyOffVoices             ; key off none
	pop	a
	mov	y, #$4c
	call	DSPWrite             ; key on voices from A
	or	a, !PlayingVoices
	mov	!PlayingVoices, a
	ret

KeyOffVoicesWithCheck:
	push	a
	mov	a, $48
	and	a, $1d
	pop	a
	bne	+
KeyOffVoices:
	push	a
	eor	a, #$ff
	and	a, !PlayingVoices
	mov	!PlayingVoices, a
	pop	a
	mov	y, #$5c
	jmp	DSPWrite
+
	ret
	
; dispatch vcmd in A
L_0D40:
	asl	a
	mov	x, a
	mov	a, #$00
	jmp	(CommandDispatchTable-$B4+x)         ; $DA minimum? (F90)

	incsrc "Commands.asm"
	
; vcmd DA: set instrument
; vcmd DB: set pan
; vcmd DC: pan fade
; vcmd DE: vibrato on
; vcmd DF: vibrato off
; vcmd EA: vibrato fade
; vcmd E0: set master volume
; vcmd E1: master vol fade
; vcmd E2: tempo
; vcmd E3: tempo fade
; vcmd E4: transpose (global)
; vcmd E5: tremolo on
; vcmd E6: tremolo off
; vcmd EB: pitch envelope (release)
; vcmd EC: pitch envelope (attack)
; vcmd E7: set voice volume base
; vcmd E8: voice volume base fade
; vcmd EE: tuning
; vcmd E9: call subroutine
; vcmd EF: set echo vbits/volume
; vcmd F2: echo volume fade
; vcmd F0: disable echo
; vcmd F1: set echo delay, feedback, filter

; calculate portamento delta
CalcPortamentoDelta:
	and	a, #$7f
	mov	$02d0+x, a         ; final portamento value
	setc
	sbc	a, $02b1+x         ; note number
	push	a
	mov	a, $90+x           ; portamento steps
	mov	x, a
	pop	a
	call	Divide16
	mov	$02c0+x, a
	mov	a, y
	mov	$02c1+x, a         ; portamento delta
	ret
; signed 16 bit division
Divide16:
	bcs	L_0F85
	eor	a, #$ff
	inc	a
	call	L_0F85
	movw	$14, ya
	movw	ya, $0e
	subw	ya, $14
	ret
L_0F85:
	mov	y, #$00
	div	ya, x
	push	a
	mov	a, #$00
	div	ya, x
	pop	y
	mov	x, $46
	ret
; dispatch table for 0d44 (vcmds)
		;106B
incsrc "CommandTable.asm"

;
L_0FDB:	
	
	mov	a, $80+x
	beq	L_0FEB
	or	($5c), ($48)
	mov	a, #$40
	mov	y, #$02
	dec	$80+x
	call	L_1075
L_0FEB:
	mov	a, $b1+x
	mov	y, a
	beq	L_1013
	mov	a, $0370+x
	cbne	$b0+x, L_1011
	or	($5c), ($48)
	mov	a, $0360+x
	bpl	L_1005
	inc	y
	bne	L_1005
	mov	a, #$80
	bra	L_1009
L_1005:
	clrc
	adc	a, $0361+x
L_1009:
	mov	$0360+x, a
	call	L_123A
	bra	L_1019
L_1011:
	inc	$b0+x
L_1013:
	mov	a, $0211+x         ; volume from note
	call	L_124D             ; set voice vol from master/base/note
L_1019:
	mov	a, !PanFadeDuration+x
	bne	L_1024
	mov	a, $48
	and	a, $5c
	bne	L_102D
	ret
; do: pan fade and set volume
L_1024:
	mov	a, #$80
	mov	y, #$02
	dec	!PanFadeDuration+x
	call	L_1075
L_102D:
	mov	a, !Pan+x		; Get the pan for this channel.
	mov	y, a		;
	mov	a, $0280+x	;
	movw	$10, ya            ; set $10/1 from voice pan
; set voice volume DSP regs with pan value from $10/1
L_1036:
	mov	a, x		;
	xcn	a			;
	lsr	a			;
	mov	$12, a             ; $12 = voice X volume DSP reg
L_103B:
	mov	y, $11
	mov	a, PanValues+$01+y         ; next pan val from table
	setc
	sbc	a, PanValues+y         ; pan val
	mov	y, $10
	mul	ya
	mov	a, y
	mov	y, $11
	clrc
	adc	a, PanValues+y         ; add integer part to pan val
	mov	y, a
	mov	a, $0371+x         ; volume
	mul	ya
	
	mov	$14, y			; \ 
	mov	a, !VolumeMult+x	; | Add the computed volume to (computedvolume * volumemultipier / $100)
	mul	ya			; |
	mov	a, y			; |
	clrc				; |
	adc	a, $14			; |
	mov	y, a			; /
	
	mov	a, !SurroundSound+x         ; bits 7/6 will negate volume L/R
	bbc1	$12.0, L_105A
	asl	a
L_105A:
	bpl	L_1061
	mov	a, y
	eor	a, #$ff
	inc	a
	mov	y, a
L_1061:
	mov	a, y
	mov	y, $12
	call	DSPWriteWithCheck             ; set DSP vol if vbit 1D clear
	mov	a, #$00
	mov	y, #$14
	subw	ya, $10
	movw	$10, ya            ; $10/11 = #$1400 - $10/11
	inc	$12               ; go back and do R chan vol
	bbc1	$12.1, L_103B
	ret
; add fade delta to value (set final value at end)
L_1075:
	movw	$14, ya		;
	bne	L_1088		; The zero flag isn't modified by movw d, ya, so I'm not sure what this is for...?
	clrc			; \
	adc	a, #$20		; | 16 gets the passed pointer plus #$20
	movw	$16, ya		; /
	mov	a, x		; \ mov y, x 
	mov	y, a		; / mov y, $48
	mov	a, #$00		;
	push	a			;
	mov	a, ($16)+y	; a gets this channel's value.
	inc	y			; 
	bra	L_109A		;
L_1088:				;
	clrc			;
	adc	a, #$10		;
	movw	$16, ya		;
	mov	a, x		;
	mov	y, a		;
	mov	a, ($14)+y	;
	clrc			;
	adc	a, ($16)+y	;
	push	a			;
	inc	y			;
	mov	a, ($14)+y	;
	adc	a, ($16)+y	;
L_109A:				;
	mov	($14)+y, a	;
	dec	y			;
	pop	a			;
	mov	($14)+y, a	;
	ret			;

ShouldSkipKeyOff:		; Returns with carry set if the key off should be skipped.  Otherwise, the key off should be performed.	
{
;L_10AC:							;
	mov	a, $30+x				; \ 
	mov	y, $31+x				; |
	movw	$14, ya					; |
	mov	y, #$00					; |
L_10B4:							; |
	mov	a, ($14)+y				; | Loop until the next byte is a note/command.
	beq	L_10D1					; |
	bmi	L_10BF					; |
L_10BA:							; |
	inc	y					; |
	mov	a, ($14)+y				; |
	bpl	L_10BA					; /
L_10BF:
	cmp	a, #$c6					; \ C6 is a tie.
	beq	skip_keyoff				; / So we shouldn't key off the voice.
	cmp	a, #$da					; \ Anything less than $DA is a note (or percussion, which counts as a note)
	bcc	L_10D1					; / So we have to key off in preparation
	push	y					;
	cmp	a, #$fb					; \ FB is a variable-length command.
	bne	.normalCommand				; / So it has special handling.
	pop	y					; y = the current "offset".
	inc	y					; \ 
	mov	a, ($14)+y				; / Get the next byte
	bpl	.normal					; \ 
	mov	a, y
	clrc
	adc	a, #$03
	bra	+
.normal
	mov	$10, a					; Store it for a moment...
	mov	a, y					; Now a has the offset.
	clrc						; \
	adc	a, $10					; / Add the number of bytes in the command.
	inc	a					; \
	inc	a					; / Plus the number of bytes the command itself takes up .
	bra	+					;
	
.normalCommand
	mov	y, a					; \ 
	pop	a					; |
	clrc						; |
	adc	a, CommandLengthTable-$DA+y		; | Add the length of the current command to y (so we get the next note/command/whatever).
+							; |
	mov	y, a					; |
	bra	L_10B4					; /
L_10D1:							;
	mov	$10, a
	mov	a, $48					; \ 
	;mov	y, #$5c					; |
	and	a,$0161					; | Key off the current voice (with conditions).
	and	a,$0162					; |
	bne	skip_keyoff				; |

	mov	a, !InRest+x
	bne	+
	mov	a, !RestGAINReplacement+x
	beq	keyoff
	mov	a, $10
	cmp	a, #$c7
	beq	skip_keyoff
+
keyoff:
	setc
	ret
skip_keyoff:
	clrc
	ret
}



ApplyPreGAIN:
	call	ShouldSkipKeyOff
	bcc	++
	mov	a, !PreEndGAIN+x
	call	UseGainInstead
	bra	++

; do: voice readahead
L_10A1:
	setp						;
	dec	$00+x					;
	clrp						;
	beq	.doKeyOffCheck 			;L_10AC					;
	
	mov	a, !PreEndGAIN+x
	beq	++
	mov	a, !PreEndGAINLength+x
	cmp	a, $70+x
	beq	ApplyPreGAIN
++		
	mov	a, !WaitTime				;
	cbne	$70+x, +				;
.doKeyOffCheck
	call	ShouldSkipKeyOff
	
	bcc	+
	mov	a,$48
	call	KeyOffVoicesWithCheck 
+
	
	
	clr1	$13.7					;
	mov	a, $90+x				;
	beq	L_10E4					;
	mov	a, $48					;
	and	a, $1d					;
	beq	L_1111					;
L_10E4:
	mov	a, ($30+x)				; Code for handling the $DD command.
	cmp	a, #$dd					; I don't know why this is here instead of in its dispatch table.
	bne	L_112A					; Maybe so that it can properly do the "read-ahead" effect?
	mov	a, $48					; \ 
	and	a, $1d					; | Check to see if the current channel is disabled with a sound effect.
	beq	L_10FB					; /
	mov	$10, #$04
L_10F3:
	call	L_1260
	dbnz	$10, L_10F3
	bra	L_1111
L_10FB:
	call	L_1260					; \ 
	call	GetCommandDataFast			; |
	mov	$91+x, a				; | Get the $DD parameters.
	call	GetCommandDataFast			; |
	mov	$90+x, a				; |
	call	GetCommandDataFast			; /
	clrc
	adc	a, $43
	call	CalcPortamentoDelta
L_1111:
	mov	a, $91+x
	beq	L_1119
	dec	$91+x
	bra	L_112A
L_1119:
	mov	a, $1d			; \ Check to see if this channel is muted (by a sound effect or whatever)
	and	a, $48			; |
	bne	L_112A			; /
	set1	$13.7			;
	mov	a, #$b0			;
	mov	y, #$02			;
	dec	$90+x			;
	call	L_1075			;
L_112A:
	mov	a, $02b1+x
	mov	y, a
	%DDEEFix()
	;mov	a, $02b0+x
	movw	$10, ya            ; note num -> $10/11
	mov	a, $a1+x
	beq	L_1140
	mov	a, $0340+x
	cmp	a, $a0+x
	beq	L_1144
	inc	$a0+x
L_1140:
	bbs1	$13.7, L_1195
-
	ret
L_1144:					; This seems to handle things related to vibrato and pitch slides?

	mov	a, !PlayingVoices	; \ 
	and	a, $48			; | If there's no voice playing on this channel,
	beq	-			; / then don't do all these time-consuming calculations.
	
	mov	a, $0341+x
	beq	L_1166
	cmp	a, $0110+x
	bne	L_1155
	mov	a, $0351+x
	mov	$a1+x, a
	bra	L_1166
L_1155:
	mov	a, $0110+x
	beq	L_115C
	mov	a, $a1+x
L_115C:
	clrc
	adc	a, $0350+x
	mov	$a1+x, a
	setp
	inc	$10+x
	clrp
L_1166:
	mov	a, $0330+x
	clrc
	adc	a, $0331+x
	mov	$0330+x, a
;
L_1170:
	mov	$12, a
	asl	a
	asl	a
	bcc	L_1178
	eor	a, #$ff
L_1178:
	mov	y, a
	mov	a, $a1+x
	cmp	a, #$f1
	bcs	L_1185
	mul	ya
	mov	a, y
	mov	y, #$00
	bra	L_1188
L_1185:
	and	a, #$0f
	mul	ya
L_1188:
	bbc1	$12.7, L_1191
	movw	$12, ya
	movw	ya, $0e
	subw	ya, $12
L_1191:
	addw	ya, $10
	movw	$10, ya
L_1195:
	jmp	SetPitch
; per-voice fades/dsps?


HandleVoice:
	clr1	$13.7
	
	mov	a, $b1+x
	beq	L_11A7
	mov	a, $0370+x
	cbne	$b0+x, L_11A7
	call	L_122D             ; voice vol calculations
L_11A7:
	mov	a, !Pan+x
	mov	y, a
	mov	a, $0280+x
	movw	$10, ya            ; $10/11 = voice pan value
	mov	a, !PanFadeDuration+x           ; voice pan fade counter
	bne	L_11B9
	bbs1	$13.7, L_11C3
	bra	L_11C6
L_11B9:
	mov	a, $0291+x
	mov	y, a
	mov	a, $0290+x         ; pan fade delta
	call	L_1201             ; add delta (with mutations)?
L_11C3:
	call	L_1036             ; set voice DSP regs, pan from $10/11
L_11C6:
	clr1	$13.7
	mov	a, $02b1+x
	mov	y, a
	%DDEEFix()
	;mov	a, $02b0+x
	movw	$10, ya            ; notenum to $10/11
	mov	a, $90+x           ; pitch slide counter
	beq	L_11E3
	mov	a, $91+x
	bne	L_11E3
	mov	a, $02c1+x
	mov	y, a
	mov	a, $02c0+x
	call	L_11FF             ; add pitch slide delta
L_11E3:
	mov	a, $a1+x
	bne	L_11EB
L_11E7:
	bbs1	$13.7, L_1195
	ret
L_11EB:
	mov	a, $0340+x
	cbne	$a0+x, L_11E7
	mov	y, $49
	mov	a, $0331+x
	mul	ya
	mov	a, y
	clrc
	adc	a, $0330+x
	jmp	L_1170
;
L_11FF:
	set1	$13.7
;
L_1201:
	movw	$16, ya
	mov	$12, y
	bbc1	$12.7, L_120E
	movw	ya, $0e
	subw	ya, $16
	movw	$16, ya
L_120E:
	mov	y, $49
	mov	a, $16
	mul	ya
	mov	$14, y
	mov	$15, #$00
	mov	y, $49
	mov	a, $17
	mul	ya
	addw	ya, $14
	bbc1	$12.7, L_1228
	movw	$14, ya
	movw	ya, $0e
	subw	ya, $14
L_1228:
	addw	ya, $10
	movw	$10, ya
	ret
;
L_122D:
	set1	$13.7
	mov	y, $49
	mov	a, $0361+x
	mul	ya
	mov	a, y
	clrc
	adc	a, $0360+x
L_123A:
	asl	a
	bcc	L_123F
	eor	a, #$ff
L_123F:
	mov	y, $b1+x
	mul	ya
	mov	a, $0211+x
	mul	ya
	mov	a, y
	eor	a, #$ff
	setc
	adc	a, $0211+x
L_124D:
	mov	y, a
; set voice volume from master/base/A
	mov	a, !Volume+x	; Get volume
	mul	ya		; Multiply by qX
	mov	a, !MasterVolume             ; master volume
	mul	ya		; Multiply by master volume.
	mov	a, y		;
	mul	ya		; \ Vol = [(Vol^2) / 2] ?
	mov	a, y		; /
	mov	$0371+x, a         ; voice volume
	ret
; get next vcmd stream byte for voice $46
GetCommandData:
	mov	x, $46
; get next vcmd stream byte into A/Y
GetCommandDataFast:
	mov	a, ($30+x)
L_1260:
	inc	$30+x
	bne	L_1266
	inc	$31+x
L_1266:
	mov	y, a
	ret
	
ModifyNoise:				; A should contain the noise value.
	and	a, #$1f
	and	!NCKValue, #$e0		; Clear the current noise bits.
	or	a, !NCKValue		; \ Set and save the current noise bits.
	mov	!NCKValue, a		; / 
	mov	y, #$6c			; \ Write
	jmp	DSPWrite		; /

; for 0C89 - note dur%'s
NoteDurations:
	db $33, $66, $80, $99, $B3, $CC, $E6, $FF

; per-note velocity values
VelocityValues:
	db $08, $12, $1B, $24, $2C, $35, $3E, $47, $51, $5A, $62, $6B, $7D, $8F, $A1, $B3	; Normal, SMW velocities.
	db $19, $33, $4C, $66, $72, $7F, $8C, $99, $A5, $B2, $Bf, $CC, $D8, $E5, $F2, $FC	; Standard N-SPC velocities.

; pan table (max pan full L = $14.00)
PanValues:
	db $00, $01, $03, $07, $0D, $15, $1E, $29, $34, $42, $51, $5E, $67, $6E, $73, $77
	db $7A, $7C, $7D, $7E, $7F






; default values (1295) for DSP regs (12A1)
;  mvol L/R max, echo vol L/R zero, FLG = echo off/noise 400HZ
;  echo feedback = $60, echo/pitchmod/noise vbits off
;  source dir = $8000, echo ram = $6000, echo delay = 32ms

DefDSPValues:
		db $7F, $7F, $00, $00, $2F, $60, $00, $00, $00, $2F, $60, $00 

DefDSPRegs:
		db $0C, $1C, $2C, $3C, $6C, $0D, $2D, $3D, $4D, $5D, $6D, $7D

; echo filters 0 and 1
EchoFilter0:
	db $FF, $08, $17, $24, $24, $17, $08, $FF
EchoFilter1:
	db $7F, $00, $00, $00, $00, $00, $00, $00

; get pitch from note number in A (with octave correction) to YA
;L_12BD:
;	mov	y, #$00
;	asl	a
;	mov	x, #$18
;	div	ya, x
;	mov	x, a
;	mov	a, PitchTable+1+y
;	mov	$16, a
;	mov	a, PitchTable+y
;	bra	L_12D2
;L_12CE:
;	lsr	$16
;	ror	a
;	inc	x
;L_12D2:
;	cmp	x, #$06
;	bne	L_12CE
;	mov	y, $16
;	ret

; pitch table
PitchTable:
	;dw $10BE
	;dw $11BD
	;dw $12CB
	;dw $13E9
	;dw $1518
	;dw $1659
	;dw $17AD
	;dw $1916
	;dw $1A94
	;dw $1C28
	;dw $1DD5
	;dw $1F9B
	;db $00
	dw $085f
	dw $08de
	dw $0965
	dw $09f4
	dw $0a8c
	dw $0b2c
	dw $0bd6
	dw $0c8b
	dw $0d4a
	dw $0e14
	dw $0eea
	dw $0fcd
	dw $10be

; do: standardish SPU transfer
L_12F2:
	print "ReuploadPos: $",pc		
				; This is where the engine should jump to when downloading consecutive blocks of data.
	
				; The following is a near identical copy of the SPC IPL ROM, just modified a touch to emulate
				; SMW sending data to ($14)+y instead of ($00)+y.  See "http:;www.romhacking.net/documents/197/".
			
			

		
	
	mov	$F4, #$AA		; Signal "ready" to 5A22: $2140-1 will return #$BBAA
	mov	$F5, #$BB
Wait:
	cmp	$F4, #$CC		; wait for 5A22 to write #$CC to $2140
	bne	Wait
	bra	Start
Trans:
	mov	y, $F4		; *** TRANSFER ROUTINE ***
	bne	Trans		; First, wait for 5A22 to indicate Byte 0 ready on $2140
StartLoop:
	cmp	y, $F4		; start loop: wait for "next byte/end" signal on $2140
	bne	Unexpected
	mov	a, $F5		; Got "next byte" ($2140 matches expected byte index)
	mov	$F4, y		; Read byte-to-write from $2141, echo $2140 to signal
	mov	($14)+y, a		; ready, and write the byte and update the counter.
	inc	y
	bne	StartLoop
	inc	$15			; (handle $xxFF->$xx00 overflow case on increment)
Unexpected:
	bpl	StartLoop
	cmp	y, $F4		; If "next byte/end" is not equal to expected next byte
	bpl	StartLoop		; index, it's "end": drop back into the main loop.
Start:
	movw	ya, $F6		; *** MAIN LOOP ***
	movw	$14, ya		; Get address from 5A22's $2142-3, 
	movw	ya, $F4		; mode from $2141, and echo $2140 back
	mov	$F4, a
	mov	a, y
	mov	x, a
	bne	Trans		; Mode non-0: begin transfer	
	
				; reset ports, keep timer running
	mov	$f1, #$31		; Has to be done quickly or else subsequent writes
				; to the SPC will be ignored (playing music right
				; after loading it, for example).  Important to note:
				; even despite doing this as quickly as possible, the
				; 5A22 will still have to wait a bit (6 NOPs or so)
				; before sending any data.
	
	mov	a, #$00
	mov	y, a
	movw	$f4, ya
	movw	$f6, ya
	movw	$04, ya
	movw	$06, ya
	setp		; Clear the output ports
	movw	$66, ya	;
	movw	$68, ya	;
	clrp		;
	
	;mov	$0386, a
	;mov	$0387, a
	mov	!PauseMusic, a
	mov	$0389, a
	
	mov	!MaxEchoDelay, a
	
	mov	x, #$cf		; Reset the stack pointer.
	mov	sp, x
	
	mov	x, #$00
	mov	$01, x
	
	mov	!NCKValue, #$20
	mov	a, #$00
	call	ModifyNoise
	;mov	y, #$6c
	;mov	$f2, y
	;mov	a, $f3
	;and	a, #$3f		; Continue operation, output sound, but DON'T allow echo yet. 
	;call	DSPWrite		; Echo is turned on within the songs if it's necessary.
	
	mov	y, #$10
	mov	a, #$00
-
	mov	!ChSFXPtrs-1+y, a	; \ Turn off sound effects
	dbnz	y, -			; /
	
	jmp	($0014+x)		; Jump to address
	
GetSampleTableLocation:

	print "SRCNTableCodePos: $",pc		
				; This is where the engine should jump to after uploading samples.

-	cmp	$f4, #$CC	; Wait for the 5A22 to send #$CC to $2140.
	bne -			; By then it should have also written DIR to $2141
				; as well as the jump address to $2142-$2143.
				
	mov	y, #$5d	
	mov	$f2, y
	mov	a, $f5
	call	DSPWrite		; Set DIR to the 5A22's $2141
	push	a
	
	movw	ya, $f6
	movw	$14, ya
	mov	$f1, #$31		; Reset input ports
	pop	a
	mov	$f5, a		; Echo back DIR
	mov	y, #$00
	jmp	($0014+x)		; Jump to the upload location.
	

	incsrc "InstrumentData.asm"
	

	SFXTable0:
	SFXTable1:
	SongPointers:

	
	