;==========================================================================================
;
;   UNOFFICIAL SNES POWERPAK FIRMWARE V2.02 (CODENAME: "SIMBA")
;   (c) 2012/2013 by ManuLöwe (http://www.manuloewe.de/)
;
;	*** CF MODULE: SP (SPC PLAYER) ***
;	Code in this file based on v1.0X code written by:
;	- bunnyboy (SNES PowerPak creator), (c) 2009
;
;	SPC uploader & player routines written by:
;	- blargg (Shay Green <gblargg@gmail.com>)
;	- ikari_01 (further contributions)
;	- necronomfive (further contributions ??)
;
;==========================================================================================



; ****************************** Includes ******************************

	.INCLUDE "module_rommakeup.inc"		; MemoryMap, VectorTable, HeaderInfo

	.INCLUDE "global_variables.asm"		; Global Variables



; ******************************** Main ********************************

Main:
	sep #A_8BIT				; 8 bit Accumulator, 16 bit X/Y
	rep #XY_8BIT

	lda #$01				; switch off DMA (might help increase stability)
	sta dontUseDMA

	lda #$00				; read file to beginning of SDRAM
	sta DMAWRITELO
	sta DMAWRITEHI
	sta DMAWRITEBANK

	rep #A_8BIT				; A = 16 bit

	lda gameName.gCluster			; read user-selected SPC file
	sta sourceCluster

	lda gameName.gCluster+2
	sta sourceCluster+2

	sep #A_8BIT				; A = 8 bit

	stz sectorCounter
	stz bankCounter

	jump jCardReadFile

	lda #$00				; pre-fetch some variables for spc700_load
	sta DMAWRITEBANK
	sta DMAWRITEHI
	lda #$25
	sta DMAWRITELO

	lda DMAREADDATA				; PC $25 (reminder: DMAREADDATA auto-increments)
	sta audioPC

	lda DMAREADDATA				; PC $26
	sta audioPC+1

	lda DMAREADDATA				; A = $27
	sta audioA

	lda DMAREADDATA				; X = $28
	sta audioX

	lda DMAREADDATA				; Y = $29
	sta audioY

	lda DMAREADDATA				; PSW = $2A
	sta audioPSW

	lda DMAREADDATA				; SP = $2B
	sta audioSP

	lda #$00
	sta DMAWRITEBANK
	lda #$01
	sta DMAWRITEHI
	lda #$00
	sta DMAWRITELO

	lda DMAREADDATA				; first two bytes of SPC RAM
	sta spcRAM1stBytes

	lda DMAREADDATA
	sta spcRAM1stBytes+1

	lda #$F1
	sta DMAWRITELO

	lda DMAREADDATA				; DSP CONTROL register
	sta spcCONTROLReg

	lda DMAREADDATA				; $F2 = DSP register address
	sta spcDSPRegAddr

	lda #$F4
	sta DMAWRITELO

	lda DMAREADDATA				; DSP IO port $F4
	sta spcIOPorts

	lda DMAREADDATA				; DSP IO port $F5
	sta spcIOPorts+1

	lda DMAREADDATA				; DSP IO port $F6
	sta spcIOPorts+2

	lda DMAREADDATA				; DSP IO port $F7
	sta spcIOPorts+3

	lda #$01
	sta DMAWRITEBANK
	sta DMAWRITEHI
	lda #$4C
	sta DMAWRITELO

	lda DMAREADDATA				; DSP KON register
	sta spcKONReg

	lda #$6C
	sta DMAWRITELO

	lda DMAREADDATA				; DSP FLG register
	sta spcFLGReg

	jsr spc700_load				; now load SPC file :-)

	ClearLine 11				; clear out "Loading ..." message

 	SetCursorPos 2, 9
	PrintString "~ Play I some music ~"

	lda #<gameName
	sta strPtr
	lda #>gameName
	sta strPtr+1

	ldy #strPtr

 	SetCursorPos 8, 0			; reminder: timer is on line 5!
	PrintString "%s"			; name of SPC file currently playing

	lda #$00				; next, read and display tag info
	sta DMAWRITEHI
	sta DMAWRITEBANK

	lda #$2E
	sta DMAWRITELO

	ldx #$0000

CopySongTitle:					; song title
	lda DMAREADDATA
	sta tempEntry, x
	inx
	cpx #$0020
	bne CopySongTitle

	stz tempEntry, x			; NUL-terminate song title string

	lda #<tempEntry
	sta strPtr
	lda #>tempEntry
	sta strPtr+1

	ldy #strPtr

	SetCursorPos 11, 0
	PrintString "Song title: %s"

	ldx #$0000

CopyGameTitle:					; game title
	lda DMAREADDATA
	sta tempEntry, x
	inx
	cpx #$0020
	bne CopyGameTitle

	stz tempEntry, x			; NUL-terminate game title string

	lda #<tempEntry
	sta strPtr
	lda #>tempEntry
	sta strPtr+1

	ldy #strPtr

	SetCursorPos 12, 0
	PrintString "Game title: %s"

	lda #$B1				; jump to position of artist's name
	sta DMAWRITELO

	ldx #$0000

CopyArtist:					; artist
	lda DMAREADDATA
	sta tempEntry, x
	inx
	cpx #$0020
	bne CopyArtist

	stz tempEntry, x			; NUL-terminate artist string

	lda #<tempEntry
	sta strPtr
	lda #>tempEntry
	sta strPtr+1

	ldy #strPtr

	SetCursorPos 13, 0
	PrintString "Artist(s) : %s"

	lda #$6E				; jump to position of dumper ID
	sta DMAWRITELO

	ldx #$0000

CopyDumper:					; name of dumper
	lda DMAREADDATA
	sta tempEntry, x
	inx
	cpx #$0010
	bne CopyDumper

	stz tempEntry, x			; NUL-terminate dumper string

	lda #<tempEntry
	sta strPtr
	lda #>tempEntry
	sta strPtr+1

	ldy #strPtr

	SetCursorPos 16, 0
	PrintString "Dumped by : %s"

	ldx #$0000

CopyComments:					; comments
	lda DMAREADDATA
	sta tempEntry, x
	inx
	cpx #$0020
	bne CopyComments

	stz tempEntry, x			; NUL-terminate comments string

	lda #<tempEntry
	sta strPtr
	lda #>tempEntry
	sta strPtr+1

	ldy #strPtr

	SetCursorPos 17, 0
	PrintString "Comment(s): %s"

	ldx #$0000

CopyDate:					; date
	lda DMAREADDATA
	sta tempEntry, x
	inx
	cpx #$000B
	bne CopyDate

	stz tempEntry, x			; NUL-terminate date string

	lda #<tempEntry
	sta strPtr
	lda #>tempEntry
	sta strPtr+1

	ldy #strPtr

	SetCursorPos 18, 0
	PrintString "Datestamp : %s"

	SetCursorPos 22, 7
	PrintString "X: customize  -  Start: exit"

	jsr FromMosaic



; ************************** Show music notes **************************

	lda #$5A				; X
	sta SpriteBuf1+16
	lda #$80				; Y
	sta SpriteBuf1+17
	lda #$40				; tile num, this one's empty :-)
	sta SpriteBuf1+18
	lda #$00				; vhoopppc Vert Horiz priOrity Palette Charmsb
	sta SpriteBuf1+19

	lda #$7A				; X
	sta SpriteBuf1+20
	lda #$A0				; Y
	sta SpriteBuf1+21
	lda #$40				; tile num, this one's empty :-)
	sta SpriteBuf1+22
	lda #$00				; vhoopppc Vert Horiz priOrity Palette Charmsb
	sta SpriteBuf1+23

	lda #$2A				; X
	sta SpriteBuf1+24
	lda #$20				; Y
	sta SpriteBuf1+25
	lda #$0C				; tile num (music notes 1)
	sta SpriteBuf1+26
	lda #%00000110				; vhoopppc Vert Horiz priOrity Palette Charmsb
	sta SpriteBuf1+27

	lda #$BA				; X
	sta SpriteBuf1+28
	lda #$20				; Y
	sta SpriteBuf1+29
	lda #$4C				; tile num (music notes 2)
	sta SpriteBuf1+30
	lda #$06				; vhoopppc Vert Horiz priOrity Palette Charmsb
	sta SpriteBuf1+31			; reminder: music notes use original PowerPak cursor palette!

	lda #$AA
	sta SpriteBuf2+1			; 32x32 sprites



; ************************ Reset some variables ************************

	stz spcTimer				; frame counter
	stz spcTimer+1				; seconds
	stz spcTimer+2				; minutes
	stz spcTimer+3				; unused

	stz Joy1New				; input buttons
	stz Joy1New+1
	stz Joy1Press
	stz Joy1Press+1



; ******************************** Loop ********************************

SPCPlayerLoop:
	wai

	SetCursorPos 5, 12			; timer position

	PrintHexNum spcTimer+2
	PrintString ":"
	PrintHexNum spcTimer+1
	PrintString ":"
	PrintHexNum spcTimer

	lda $213F				; check display refresh rate
	and #%00010000
	bne __IncTimerPAL			; increment seconds depending on framerate

__IncTimerNTSC:
	sed					; decimal mode on

	clc
	lda spcTimer
	adc #$01
	sta spcTimer
	cmp #$60				; if NTSC, increment seconds every 60th frame
	bcc __CalcTimerSeconds

	stz spcTimer				; carry bit set, reset frame counter

	bra __CalcTimerSeconds

__IncTimerPAL:
	sed					; decimal mode on

	clc
	lda spcTimer
	adc #$01
	sta spcTimer
	cmp #$50				; if PAL, increment seconds every 50th frame
	bcc __CalcTimerSeconds

	stz spcTimer				; carry bit set, reset frame counter

__CalcTimerSeconds:
	lda spcTimer+1				; increment seconds via carry bit
	adc #$00
	sta spcTimer+1
	cmp #$60				; check if 60 seconds have elapsed
	bcc __CalcTimerMinutes

	stz spcTimer+1				; carry bit set, reset seconds

__CalcTimerMinutes:
	lda spcTimer+2				; increment minutes via carry bit
	adc #$00
	sta spcTimer+2
	cmp #$60				; check if 60 minutes have elapsed
	bcc __CalcTimerDone

	stz spcTimer+2				; if 59:59 reached, reset timer

__CalcTimerDone:

	cld					; decimal mode off

	lda Joy1New				; check for X button
	and #%01000000
	beq __XCheckDone

	FindFile "SC.MAP"			; load customization module with SPC tune playing :-)

	jump jCardLoadModule

__XCheckDone:

	lda Joy1Press+1				; check for Start button
	and #%00010000
	beq __StartCheckDone

	lda #$0E

-	sta $2100				; make the screen fade to black ...
	wai
	dec a
	bpl -

	lda #%10000001
	sta CONFIGWRITESTATUS			; ... and reset PowerPak, stay in boot mode

__StartCheckDone:

	jmp SPCPlayerLoop



; **************************** Mosaic stuff ****************************

FromMosaic:
	lda #$F3				; enable mosaic on BG1 & BG2, start with biggest block size

-	sta $2106
	wai					; show mosaic for one frame
	sec
	sbc #$10				; reduce block size by 1
	cmp #$F3				; smallest block size ($03) processed on last iteration?
	bne -

	stz $2106				; turn off mosaic effect
rts



; ************************* Actual SPC player **************************

spc700_load:
	php
	sep #A_8BIT
	rep #XY_8BIT

	sei					; Disable NMI & IRQ
	stz REG_NMI_TIMEN			; The SPC player code is really timing sensitive ;)
	jsr InitWRAMBuffer			; buffer parts of SPC dump to avoid timing problems
	jsr upload_dsp_regs			; Upload S-DSP registers
	jsr upload_high_ram			; Upload 63.5K of SPC700 ram
	jsr upload_low_ram			; Upload rest of ram
	jsr restore_final			; Restore SPC700 state & start execution

	lda #$81				; VBlank NMI + Auto Joypad Read
	sta REG_NMI_TIMEN			; re-enable VBlank NMI
	cli
	plp
rts

;---------------------------------------
; Uploads DSP registers and some other setup code

upload_dsp_regs:

; ---- Begin upload

	ldy #$0002
	jsr spc_begin_upload

; ---- Upload loader

	ldx #$0000
-
	lda.w loader,x
	jsr spc_upload_byte
	inx
	cpy #31					; size of loader
	bne -

; ---- Upload SP, PC & PSW

	lda audioSP				; SP
	jsr spc_upload_byte
	lda audioPC+1				; PC 2
	jsr spc_upload_byte
	lda audioPC				; PC 1
	jsr spc_upload_byte
	lda audioPSW				; PSW
	jsr spc_upload_byte

; ---- Upload DSP registers

	ldx #$0000
-
; initialize FLG and KON ($6c/$4c) to avoid artifacts
	cpx #$4C
	bne +
	lda #$00
	bra upload_skip_load
+
	cpx #$6C
	bne +
	lda #$E0
	bra upload_skip_load
+
	lda spcRegBuffer, x

upload_skip_load:
	jsr spc_upload_byte
	inx
	cpx #128
	bne -

; --- Upload fixed values for $F1-$F3

	ldy #$00F1
	jsr spc_next_upload

	lda #$80				; stop timers
	jsr spc_upload_byte
	lda #$6c				; get dspaddr set for later
	jsr spc_upload_byte
	lda #$60
	jsr spc_upload_byte

; ---- Upload $f8-$1ff

	ldy #$00F8
	jsr spc_next_upload

	ldx #$00F8
-
	lda spcF8Buffer, x
	jsr spc_upload_byte
	inx
	cpx #$200
	bne -

; ---- Execute loader

	ldy #$0002
	jsr spc_execute
rts

;---------------------------------------

upload_high_ram:

	ldy #$0002
	jsr spc_begin_upload

; ---- Upload transfer routine

	ldx #$0000
-
	lda.w transfer,x
	jsr spc_upload_byte
	inx
	cpy #43					; size of transfer routine
	bne -

	ldx #$023f				; prepare transfer address

; ---- Execute transfer routine

	ldy #$0002
	sty APUIO2
	stz APUIO1
	lda APUIO0
	inc a
	inc a
	sta APUIO0
; Wait for acknowledgement
-
	cmp APUIO0
	bne -

; ---- Burst transfer of 63.5K using custom routine

outer_transfer_loop:
	ldy #$003f				; 3

inner_transfer_loop:
	lda $7F0000,x				; 5 |
	sta APUIO0				; 4 |
	lda $7F0040,x				; 5 |
	sta APUIO1				; 4 |
	lda $7F0080,x				; 5 |
	sta APUIO2				; 4 |
	lda $7F00C0,x				; 5 |
	sta APUIO3				; 4 |
	tya					; 2 >> 38 cycles
-
	cmp APUIO3				; 4 |
	bne -					; 3 |
	dex					; 2 |
	dey					; 2 |
	bpl inner_transfer_loop			; 3 >> 14 cycles

	rep #$21				; 3 |
	txa					; 2 |
	adc #$140				; 3 |
	tax					; 2 |
	sep #$20				; 3 |
	cpx #$003f				; 3 |
	bne outer_transfer_loop			; 3 >> 19 cycles

rts

;---------------------------------------

upload_low_ram:

; ---- Upload $0002-$00EF using IPL

	ldy #$0002
	jsr spc_begin_upload

	ldx #$0002
-
	lda spcIPLBuffer, x
	jsr spc_upload_byte
	inx
	cpx #$00F0
	bne -
rts

;---------------------------------------
; Executes final restoration code

restore_final:
	jsr start_exec_io			; prepare execution from I/O registers

	stz $420d				; SPC700 I/O code requires SLOW timing

; ---- Restore first two bytes of RAM

	lda spcRAM1stBytes
	xba
	lda #$e8				; MOV A,#spcRAM1stBytes
	tax
	jsr exec_instr
	ldx #$00C4				; MOV $00,A
	jsr exec_instr

	lda spcRAM1stBytes+1
	xba
	lda #$e8				; MOV A,#spcRAM1stBytes+1
	tax
	jsr exec_instr
	ldx #$01C4				; MOV $01,A
	jsr exec_instr

; ---- Restore SP

	lda audioSP
	sec
	sbc #3
	xba
	lda #$cd				; MOV X,#audioSP
	tax
	jsr exec_instr
	ldx #$bd				; MOV SP,X
	jsr exec_instr

; ---- Restore X

	lda audioX
	xba
	lda #$cd				; MOV X,#audioX
	tax
	jsr exec_instr

; ---- Restore Y

	lda audioY
	xba
	lda #$8d				; MOV Y,#audioY
	tax
	jsr exec_instr

; ---- Restore DSP FLG register

	lda spcFLGReg
	xba
	lda #$e8				; MOV A,#spcFLGReg
	tax
	jsr exec_instr
	ldx #$f3C4				; MOV $f3,A -> $f2 has been set-up before by SPC700 loader
	jsr exec_instr

; ---- wait a bit (the newer S-APU takes its time to ramp up the volume)

	lda #$10

-	pha

	jsr WaitABit

	pla
	dec a
	bne -

; ---- Restore DSP KON register

	lda #$4C
	xba
	lda #$e8				; MOV A,#$4c
	tax
	jsr exec_instr
	ldx #$f2C4				; MOV $f2,A
	jsr exec_instr
	lda spcKONReg
	xba
	lda #$e8				; MOV A,#spcKONReg
	tax
	jsr exec_instr
	ldx #$f3C4				; MOV $f3,A
	jsr exec_instr

; ---- Restore DSP register address

	lda spcDSPRegAddr
	xba
	lda #$e8				; MOV A,#spcDSPRegAddr
	tax
	jsr exec_instr
	ldx #$f2C4				; MOV dest,A
	jsr exec_instr

; ---- Restore CONTROL register

	lda spcCONTROLReg
	and #$CF				; don't clear input ports
	xba
	lda #$e8				; MOV A,#spcCONTROLReg
	tax
	jsr exec_instr
	ldx #$f1C4  				; MOV $F1,A
	jsr exec_instr

;---- Restore A

	lda audioA
	xba
	lda #$e8				; MOV A,#audioA
	tax
	jsr exec_instr

;---- Restore PSW and PC

	ldx #$7F00				; NOP; RTI
	stx APUIO0
	lda #$FC				; Patch loop to execute instruction just written
	sta APUIO3

;---- restore IO ports $f4 - $f7

	rep #$20
	lda spcIOPorts
	tax
	lda spcIOPorts+2
	sta APUIO2
	stx APUIO0				; last to avoid overwriting RETI before run
	sep #$20

	lda #$01
	sta $420d				; restore FAST CPU operation
rts

;---------------------------------------

spc_begin_upload:
	sty APUIO2				; Set address

	ldy #$BBAA				; Wait for SPC
-
	cpy APUIO0
	bne -

	lda #$CC				; Send acknowledgement
	sta APUIO1
	sta APUIO0

-       					; Wait for acknowledgement
	cmp APUIO0
	bne -

	ldy #0					; Initialize index
rts

;---------------------------------------

spc_upload_byte:
	sta APUIO1

	tya					; Signal it's ready
	sta APUIO0
-     						; Wait for acknowledgement
	cmp APUIO0
	bne -

	iny

rts

;---------------------------------------

spc_next_upload:
	sty APUIO2

; Send command
; Special case operation has been fully tested.
	lda APUIO0
	inc a
	inc a
	bne +
	inc a
+
	sta APUIO1
	sta APUIO0

; Wait for acknowledgement
-
	cmp APUIO0
	bne -

	ldy #0
	rts

;---------------------------------------

spc_execute:
	sty APUIO2

	stz APUIO1

	lda APUIO0
	inc a
	inc a
	sta APUIO0

; Wait for acknowledgement
-
	cmp APUIO0
	bne -

	rts

;---------------------------------------

start_exec_io:

; Set execution address
	ldx #$00F5
	stx APUIO2

	stz APUIO1      			; NOP
	ldx #$FE2F      			; BRA *-2

; Signal to SPC that we're ready
	lda APUIO0
	inc a
	inc a
	sta APUIO0

; Wait for acknowledgement
-
	cmp APUIO0
	bne -

; Quickly write branch
	stx APUIO2

rts

;---------------------------------------

exec_instr:

; Replace instruction
	stx APUIO0
	lda #$FC
	sta APUIO3      			; 30

        ; SPC BRA loop takes 4 cycles, so it reads
        ; the branch offset every 4 SPC cycles (84 master).
        ; We must handle the case where it read just before
        ; the write above, and when it reads just after it.
        ; If it reads just after, we have at least 7 SPC
        ; cycles (147 master) to change restore the branch
        ; offset.

        ; 48 minimum, 90 maximum
        ora #0
        ora #0
        ora #0
        nop
        nop
        nop

        ; 66 delay, about the middle of the above limits
	phd ;4
	pld ;5

        ; Give plenty of extra time if single execution
        ; isn't needed, as this avoids such tight timing
        ; requirements.

;	phd ;4
;	pld ;5
;	phd ;4
;	pld ;5

        ; Patch loop to skip first two bytes
	lda #$FE        ; 16
	sta APUIO3      ; 30

        ; 38 minimum (assuming 66 delay above)
	phd ; 4
	pld ; 5

        ; Give plenty of extra time if single execution
        ; isn't needed, as this avoids such tight timing
        ; requirements.

	phd
	pld
	phd
	pld
rts



; ************************* Build WRAM buffer **************************

; WRAM buffering (added by ManuLöwe) to avoid having to fiddle around
; with the PowerPak DMA port within the SPC uploading routine itself,
; which would break the timing.

InitWRAMBuffer:

; copy DSP registers to buffer

	lda #$01
	sta DMAWRITEBANK
	sta DMAWRITEHI
	lda #$00
	sta DMAWRITELO

	ldx #$0000
-
	lda DMAREADDATA				; reminder: DMAREADDATA auto-increments
	sta spcRegBuffer, x
	inx
	cpx #$0080
	bne -

; copy $f8-$1ff to buffer

	lda #$00
	sta DMAWRITEBANK
	lda #$01
	sta DMAWRITEHI
	lda #$F8
	sta DMAWRITELO

	ldx #$00F8
-
	lda DMAREADDATA
	sta spcF8Buffer, x
	inx
	cpx #$0200
	bne -

; copy $0002-$00EF (IPL) to buffer

	lda #$00
	sta DMAWRITEBANK
	lda #$01
	sta DMAWRITEHI
	lda #$02
	sta DMAWRITELO

	ldx #$0002
-
	lda DMAREADDATA
	sta spcIPLBuffer, x
	inx
	cpx #$00F0
	bne -

; copy SPC RAM data to buffer

	ldx #$0000

	lda #$00				; SPC RAM data starts at 00 01 00
	sta DMAWRITEBANK
	lda #$01
	sta DMAWRITEHI
	lda #$00
	sta DMAWRITELO

-	lda DMAREADDATA
	sta $7F0000, x
	inx
	bne -					; copy 65536 bytes
rts



; ***************************** Wait a bit *****************************

; based on a routine written by ikari_01

WaitABit:
-	lda HVBJOY
	and #%10000000				; Vblank period flag
	bne -

-	lda HVBJOY
	and #%10000000
	beq -
rts



; **************************** SPC700 code *****************************

; All SPC700 routines in SPC700 machine code
; SPC loader & transfer routines by Shay Green <gblargg@gmail.com>

loader:				; .org $0002
	.byt $F8,$21		;	mov x,@loader_data
	.byt $BD		;	mov sp,x
	.byt $CD,$22		;	mov x,#@loader_data+1

	; Push PC and PSW from SPC header
	.byt $BF		;	mov a,(x)+
	.byt $2D		;	push a
	.byt $BF		;	mov a,(x)+
	.byt $2D		;	push a
	.byt $BF		;	mov a,(x)+
	.byt $2D		;	push a

	; Set FLG to $60 rather than value from SPC
	.byt $E8,$60		;	mov a,#$60
	.byt $D4,$6C		;	mov FLG+x,a

	; Restore DSP registers
	.byt $8D,$00		;	mov y,#0
	.byt $BF		; next:	mov a,(x)+
	.byt $CB,$F2		;	mov $F2,y
	.byt $C4,$F3		;	mov $F3,a
	.byt $FC		;	inc y
	.byt $10,-8		;	bpl next

	.byt $8F,$6C,$F2	;	mov $F2,#FLG	; set for later

	; Rerun loader
	.byt $5F,$C0,$FF	;	jmp $FFC0

;---------------------------------------

transfer:			; .org $0002

	.byt $CD,$FE		; mov x,#$FE		; transfer 254 pages

	; Transfer four-byte chunks
	.byt $8D,$3F		; page: mov y,#$3F
	.byt $E4,$F4		; quad: mov a,$F4
	.byt $D6,$00,$02	; mov0: mov !$0200+y,a
	.byt $E4,$F5		;	mov a,$F5
	.byt $D6,$40,$02	; mov1: mov !$0240+y,a
	.byt $E4,$F6		;	mov a,$F6
	.byt $D6,$80,$02	; mov2: mov !$0280+y,a
	.byt $E4,$F7		;	mov a,$F7	; tell S-CPU we're ready for more
	.byt $CB,$F7		;	mov $F7,Y
	.byt $D6,$C0,$02	; mov3: mov !$02C0+y,a
	.byt $DC 		;	dec y
	.byt $10,-25		;	bpl quad
	; Increment MSBs of addresses
	.byt $AB,$0A		;	inc mov0+2
	.byt $AB,$0F		;	inc mov1+2
	.byt $AB,$14		;	inc mov2+2
	.byt $AB,$1B		;	inc mov3+2
	.byt $1D    		;	dec x
	.byt $D0,-38		;	bne page
	; Rerun loader
	.byt $5F,$C0,$FF	;	jmp $FFC0
