SNES Development
Transferring Data from ROM to the SNES APU

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

  1. 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.)
  2. Write any value other than 0 to $2141. (A value of 0 has a different meaning which will be explained later.)
  3. Write the destination address of the transfer to ports $2142 and $2143, with the low byte written to $2142.
  4. Write $CC to port $2140.
  5. Wait for $2140 to be $CC.

Transferring Data

  1. Write the first byte to be transferred to port $2141.
  2. Write $00 to port $2140.
  3. Wait for $2140 to be $00.
  4. Write the next byte to be transferred to port $2141.
  5. Increase the value in $2140 by 1, and write it back.
  6. Wait for $2140 to be the same as the value written.
  7. Goto step 4 until all bytes are transferred.

Beginning Another Transfer

  1. Write a non-zero value to port $2141.
  2. Write the destination address of the transfer to ports $2142 and $2143, with the low byte written to $2142.
  3. Increase the value in $2140 by 2. If it’s 0, increase it again. Write it back.
  4. Wait for $2140 to be the same as the value written, then go to the section “Transferring Data”.

Ending Transfers

  1. Write 0 to port $2141.
  2. Write the address to begin execution to ports $2142 and $2143, with the low byte written to $2142.
  3. 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