123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676 |
- #include "memmap.i65"
- ; SPC Player
- ; SPC700 transfer and IO routines by Shay Green <gblargg@gmail.com>
- spcplayer:
- php
- sep #$30 : .as : .xs
- ldx #$0a ; Check if SPC header is present
- -
- lda @SPC_HEADER,x
- cmp @text_spcid,x
- beq +
- jmp spc_exit
- +
- dey
- bne -
- rep #$10 : .xl ; Now draw lots of stuff
- stz bar_wl
- dec bar_wl
- stz bar_xl
- dec bar_xl
- stz bar_yl
- dec bar_yl
- jsr backup_screen
- lda #^text_spcplay ; Loading window
- sta window_tbank
- ldx #!text_spcplay
- stx window_taddr
- lda @spcplay_win_x
- sta window_x
- lda @spcplay_win_y
- sta window_y
- lda @spcplay_win_w
- sta window_w
- lda @spcplay_win_h
- sta window_h
- jsr draw_window
- lda #^text_spcload ; Loading text
- ldx #!text_spcload
- sta print_bank
- stx print_src
- stz print_pal
- lda #29
- sta print_count
- lda #17
- sta print_y
- lda #17
- sta print_x
- jsr hiprint
- stz isr_done
- -
- lda isr_done ; Wait until text is being printed...
- beq -
- jsr spc700_load ; Load SPC into SPC700
- lda #^text_spcplay
- sta window_tbank
- ldx #!text_spcplay
- stx window_taddr
- lda @spcstart_win_x
- sta window_x
- lda @spcstart_win_y
- sta window_y
- lda @spcstart_win_w
- sta window_w
- lda @spcstart_win_h
- sta window_h
- jsr draw_window
- lda #^text_spcstarta
- ldx #!text_spcstarta
- sta print_bank
- stx print_src
- lda #$01
- sta print_pal
- lda #30
- sta print_count
- lda #15
- sta print_y
- lda #17
- sta print_x
- jsr hiprint
- lda #^text_spcstartb
- ldx #!text_spcstartb
- sta print_bank
- stx print_src
- lda #$01
- sta print_pal
- lda #07
- sta print_count
- lda #17
- sta print_y
- lda #12
- sta print_x
- jsr hiprint
- lda #$fe
- ldx #$004e
- sta print_bank
- stx print_src
- stz print_pal
- lda #32
- sta print_count
- lda #17
- sta print_y
- lda #20
- sta print_x
- jsr hiprint
- lda #^text_spcstartc
- ldx #!text_spcstartc
- sta print_bank
- stx print_src
- lda #$01
- sta print_pal
- lda #07
- sta print_count
- lda #18
- sta print_y
- lda #12
- sta print_x
- jsr hiprint
- lda #$fe
- ldx #$002e
- sta print_bank
- stx print_src
- stz print_pal
- lda #32
- sta print_count
- lda #18
- sta print_y
- lda #20
- sta print_x
- jsr hiprint
- lda #^text_spcstartd
- ldx #!text_spcstartd
- sta print_bank
- stx print_src
- lda #$01
- sta print_pal
- lda #07
- sta print_count
- lda #19
- sta print_y
- lda #12
- sta print_x
- jsr hiprint
- lda #$fe
- ldx #$00b0
- sta longptr+2
- sta print_bank
- stx longptr
- ldy #$00
- lda [longptr], y
- cmp #$41
- bpl +
- inx
- + stx print_src
- stz print_pal
- lda #32
- sta print_count
- lda #19
- sta print_y
- lda #20
- sta print_x
- jsr hiprint
- spc_playloop:
- lda isr_done ; SPC player loop
- lsr
- bcc spc_playloop
- jsr printtime
- stz isr_done
- jsr read_pad
- lda #$80
- and pad1trig+1
- bne spc_key_b
- bra spc_playloop
- spc_key_b:
- rep #$20 : .al
- tsc
- sta saved_sp ; Save SP for later re-entry
- lda #$fa50 ; Write reset signature
- sta @warm_signature
- sep #$20 : .as
- sei ; Blank screen & issue CMD_RESET command to Microcontroller...
- stz $2100 ; ...this is required, because there is no other way to stop S-SMP & S-DSP
- lda #CMD_RESET
- sta @MCU_CMD
- -
- bra - ; At this point, the SNES waits for an external reset from the Microcontroller
- spc_exit: ; Return from player in case of wrong SPC file data
- plp
- rts
- ;---------------------------------------
- spc700_load:
- php
- sep #$20 : .as
- rep #$10 : .xl
- sei ; Disable NMI & IRQ
- stz $4200 ; The SPC player code is really timing sensitive ;)
- 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 $4200 ; enable V-BLANK 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 @loader,x
- jsr spc_upload_byte
- inx
- cpy #31 ; size of loader
- bne -
- ; ---- Upload SP, PC & PSW
- lda @SPC_HEADER+43
- jsr spc_upload_byte
- lda @SPC_HEADER+38
- jsr spc_upload_byte
- lda @SPC_HEADER+37
- jsr spc_upload_byte
- lda @SPC_HEADER+42
- 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 @SPC_DSP_REGS,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 @SPC_DATA,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 @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
- inc
- 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 @SPC_DATA,x ; 5 |
- sta APUIO0 ; 4 |
- lda @SPC_DATA+$40,x ; 5 |
- sta APUIO1 ; 4 |
- lda @SPC_DATA+$80,x ; 5 |
- sta APUIO2 ; 4 |
- lda @SPC_DATA+$C0,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 : .al ; 3 |
- txa ; 2 |
- adc #$140 ; 3 |
- tax ; 2 |
- sep #$20 : .as ; 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 @SPC_DATA,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 @SPC_DATA
- xba
- lda #$e8 ; MOV A,#@SPC_DATA
- tax
- jsr exec_instr
- ldx #$00C4 ; MOV $00,A
- jsr exec_instr
- lda @SPC_DATA+1
- xba
- lda #$e8 ; MOV A,#@SPC_DATA+1
- tax
- jsr exec_instr
- ldx #$01C4 ; MOV $01,A
- jsr exec_instr
- ; ---- Restore SP
- lda @SPC_HEADER+43
- sec
- sbc #3
- xba
- lda #$cd ; MOV X,#@SPC_HEADER+43
- tax
- jsr exec_instr
- ldx #$bd ; MOV SP,X
- jsr exec_instr
- ; ---- Restore X
- lda @SPC_HEADER+40
- xba
- lda #$cd ; MOV X,#@SPC_HEADER+40
- tax
- jsr exec_instr
- ; ---- Restore Y
- lda @SPC_HEADER+41
- xba
- lda #$8d ; MOV Y,#@SPC_HEADER+41
- tax
- jsr exec_instr
- ; ---- Restore DSP FLG register
- lda @SPC_DSP_REGS+$6c
- xba
- lda #$e8 ; MOV A,#@SPC_DSP_REGS+$6c
- 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 waitblank
- pla
- dec
- 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 @SPC_DSP_REGS+$4C
- xba
- lda #$e8 ; MOV A,#@SPC_DSP_REGS+$4c
- tax
- jsr exec_instr
- ldx #$f3C4 ; MOV $f3,A
- jsr exec_instr
- ; ---- Restore DSP register address
- lda @SPC_DATA+$F2
- xba
- lda #$e8 ; MOV A,#@SPC_DATA+$F2
- tax
- jsr exec_instr
- ldx #$f2C4 ; MOV dest,A
- jsr exec_instr
- ; ---- Restore CONTROL register
- lda @SPC_DATA+$F1
- and #$CF ; don't clear input ports
- xba
- lda #$e8 ; MOV A,#@SPC_DATA+$F1
- tax
- jsr exec_instr
- ldx #$f1C4 ; MOV $F1,A
- jsr exec_instr
- ;---- Restore A
- lda @SPC_HEADER+39
- xba
- lda #$e8 ; MOV A,#@SPC_HEADER+39
- 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 : .al
- lda @SPC_DATA+$F4
- tax
- lda @SPC_DATA+$F6
- sta APUIO2
- stx APUIO0 ; last to avoid overwriting RETI before run
- sep #$20 : .as
- 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
- inc
- bne +
- inc
- +
- sta APUIO1
- sta APUIO0
- ; Wait for acknowledgement
- -
- cmp APUIO0
- bne -
- ldy #0
- rts
- ;---------------------------------------
- spc_execute:
- sty APUIO2
- stz APUIO1
- lda APUIO0
- inc
- inc
- 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
- inc
- 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
|