< Super NES Programming

System Overview

The SPC700 is a Sony coprocessor that coordinates SNES audio. Once it is initialized with data and code sent from the SNES CPU, it manipulates the state of its accompanying digital signal processor (DSP), which produces the output audio.

SPC700 Overview

The SPC700 has 64KB of memory for code and data. Within this memory are memory-mapped registers, used for communicating with the SNES CPU, the DSP, and three available timers.

The SPC700 has 6 registers:

  • A - An 8-bit accumulator
  • X & Y - 8-bit index registers
  • SP - 8-bit stack pointer
  • PC - 16-bit program counter
  • PSW - 8-bit "Program Status Word", which stores the status flags

The Y and A registers can be paired together for some operations to form a 16-bit register with Y as the upper byte.

DSP Overview

The DSP has eight channels, each of which can play a 16-bit sound. Each of the eight channels has separate left and right stereo volume, can be played at different pitches, and can have an Attack-Decay-Sustain-Release (ADSR) envelope applied to it. A white noise source can be set to replace the sampled data on any of the eight channels. Additionally, the DSP can apply an echo to the audio. The 16-bit audio samples are read from the SPC700's 64KB memory space, where they are stored in a packed 4-bit lossily-compressed format.

SPC700 memory map

  • 0x0000 - 0x000F - direct page 0
  • 0x00F0 - 0x00FF - memory-mapped hardware registers
  • 0x0100 - 0x01FF - direct page 1
  • 0x0100 - 0x01FF - potential stack memory
  • 0xFFC0 - 0xFFFF - IPL ROM

Direct-page addressing

Many instructions offer an addressing mode that accesses 1-byte memory addresses in the "direct page". This addressing mode yields shorter bytecode which presumably executes faster. The direct page's upper byte is either 0x00 or 0x01, corresponding to the P bit in the PSW register.

The stack

The lower byte of the stack pointer is specified by the SP register; the upper byte is always 0x01. The stack pointer is set to 0x01EF on restart by the IPL ROM and grows downward.

IPL ROM

On restart, the 64-byte chunk of memory at the end of the 64KB of RAM is initialized to the contents of the IPL ROM, which is where execution starts. The code in the IPL ROM sets the stack pointer to 0x01EF, zeroes the memory from 0x0000 to 0x00EF, and then waits for data from the SNES via the input ports..

SPC700 Registers

AddressDescriptionR/W
00F0hUnknown?
00F1hControlW
00F2hDSP Read/Write AddressR/W
00F3hDSP Read/Write DataR/W
00F4hPort 0R/W
00F5hPort 1R/W
00F6hPort 2R/W
00F7hPort 3R/W
00FAhTimer Setting 0W
00FBhTimer Setting 1W
00FChTimer Setting 2W
00FAhTimer Counter 0R
00FBhTimer Counter 1R
00FChTimer Counter 2R

00F0h Unknown

Anomie has done some tests with this register. A document on romhacking.net describes it.

00F1h Control

76543210
XXPC32PC10XST2ST1ST0
  • PC32 - Writing 1 in this bit will zero input for ports 2 and 3
  • PC10 - Writing 1 in this bit will zero input for ports 1 and 0
  • ST0-2 - These are for starting the timers.
 Warning: Writing to this register will always restart/stop all of the timers.

00F2h/00F3h DSP Registers

Writing to 00F2h sets the address of the DSP register to access. Writing to 00F3h changes the value of the register pointed to. Reading from 00F3h will return the value of the register pointed at. Writing a word to 00F2h is allowed and it can be used to simultaneously set the address and write a value to the register.

00F4h-00F7h Ports

Reading from these ports will give you the values that the SNES set at $2140-$2143. Values written to these registers will be viewable by the SNES using the same $2140-$2143. The input of these ports can be cleared using the Control register.

00FAh-00FFh Timer Settings

Registers 00FAh-00FCh are used to set the timer rate. Timers 0 & 1 have a resolution of 125 microseconds. Timer 2 has a finer resolution of ~15.6 microseconds. 00FDh-00FFh are 4-bit registers containing the timer overflow count. Here is how the timers operate.

Each timer has an internal counter which is incremented at each clock input. If it equals the number in $FA-$FC (depending on which timer you're using) the corresponding counter register is incremented and the internal counter is reset. The counter registers ($FD-$FF) are 4-bit registers and can be read only. Reading from them will cause them to reset back to zero. If you don't read the counters in the limited time frame then they will overflow and be cleared to zero as well. The timer must be stopped before setting the 00FAh-00FCh registers. To start a timer write to bits 0-2 of the Control register. To stop a timer, reset the bits. Take note that writing to the control register will restart the existing timers.

DSP Compression Format

The DSP plays a special ADPCM encoded sound format. The sample is made up of a series of 9 byte compression blocks. Each block holds 16 4-bit samples and a 1 byte header. 16-bit samples will get a 9/32 compression ratio, but 8-bit samples must be inflated to 16-bit before compression (giving them only 9/16 compression ratio). The first byte of each block contains the header.

76543210
RangeFilterLoopEnd
  • Range - This is the shift value for the data. It can be 0-12.
  • Filter - This selects the filter coefficients used in the decoding process. (see table below)
  • Loop -This bit marks the block as one that will be within a loop. The exact function of this bit is unknown, but some commercial spc samples that I have examined have this bit set for all blocks in the sample.
  • End - This bit marks the block as the last block in the sample. The DSP channel will terminate or jump to the loop point if it reaches this block.

Data

Each block contains 8 bytes of sample data (16 signed 4-bit samples). The higher nibble in each byte contains the sample that is to be decoded before the one in the lower nibble.

Decoding Process

Here is an equation to estimate the 16-bit output of the DSP. Let y and z be the last two previously decoded samples. 'a' and 'b' are the filter coefficients selected by bits 2-3 of the header byte.

 sample = S + ay + bz
 S is the shifted data:
 S = (4-bit sample) << Range

The DSP performs this procedure using minimal basic shifting operations; output accuracy won't be perfect. It also applies Gaussian interpolation to the output.

Filter Coefficients

Filter#ab
000
115/160
261/32-15/16
3115/64-13/16
This article is issued from Wikibooks. The text is licensed under Creative Commons - Attribution - Sharealike. Additional terms may apply for the media files.