Transferring Data from ROM to the SNES APU The method of transferring data to the sound module of the SNES is a bit screwy, but it works. I've read many docs, looked at a lot of code, and nothing I've seen seems to explain the method to this madness in a straightforward way. I'm hoping I can shed some light on the matter.
During the transfer process, the following ports apply:
$2140 = Status
$2141 = Command / Data
$2142-3 = Address
Initializing the Transfer
- Wait for port
$2140
to be$AA
and port$2141
to be$BB
. (This means the ROM program is through initializing, and is ready to begin a transfer.) - Write any value other than 0 to
$2141
. (A value of 0 has a different meaning which will be explained later.) - Write the destination address of the transfer to ports
$2142
and$2143
, with the low byte written to$2142
. - Write
$CC
to port$2140
. - Wait for
$2140
to be$CC
.
Transferring Data
- Write the first byte to be transferred to port
$2141
. - Write
$00
to port$2140
. - Wait for
$2140
to be$00
. - Write the next byte to be transferred to port
$2141
. - Increase the value in
$2140
by 1, and write it back. - Wait for
$2140
to be the same as the value written. - Goto step 4 until all bytes are transferred.
Beginning Another Transfer
- Write a non-zero value to port
$2141
. - Write the destination address of the transfer to ports
$2142
and$2143
, with the low byte written to$2142
. - Increase the value in
$2140
by 2. If it's 0, increase it again. Write it back. - Wait for
$2140
to be the same as the value written, then go to the section "Transferring Data".
Ending Transfers
- Write 0 to port
$2141
. - Write the address to begin execution to ports
$2142
and$2143
, with the low byte written to$2142
. - Increase the value in
$2140
by 2, and write it back.
Summary
Between transfers, a non-zero value sent to $2141
means to begin a transfer, and a zero value means to end a transfer and begin execution.
16-bit addresses are written to $2142-3
.
During transfers, in $2140
a value less than means the APU is busy, a value equal means it's ready for the next byte. Writing a value one greater means the next byte ($2141
) is ready to be stored. Writing a value two or more greater means to end the transfer and apply the command in $2141
. (Because of the internal workings of the ROM program, the value in $2140
must be non-zero if the command is to start another transfer.)
Example
; Wait for SPC700 to initialize
REP #$30 ; (16-bit A and X Y)
LDA #$BBAA
Init: CMP $2140
BNE Init
; Initialize transfer ---------
LDA #SPCExec ; Send starting address
STA $2142
LDA #$1CC
STA $2140 ; Write $CC to 2140 and !0 to 2141
SEP #$20 ; (8-bit A)
Start: CMP $2140 ; Wait for SPC700 to give the go
BNE Start
; Send data -------------------
LDX #0 ; X indexes the current byte being sent
LDY #SPCSize ; Y = Number of bytes that need to be sent
Next: LDA SPCData,X ; Send byte
STA $2141
TXA
STA $2140
Wait: CMP $2140 ; Wait for transfer
BNE Wait
INX
DEY
BNE Next
; Begin execution -------------
REP #$20 ; (16-bit A)
LDA #SPCExec ; Starting address of execution
STA $2142
SEP #$20 ; (8-bit A)
LDA #0 ; JMP to execution address
STA $2141
LDA $2140 ; Break transfer cycle
ADC #2
STA $2140
Copyright ©2000 Alpha-II Productions