Open2

How I analyze SPC files | SPC ファイルの解析手順 in 201X

loveemuloveemu

In the past, when I couldn't read the SPC700 assembly, I used to focus on the data patterns and guess the specifications of the voice commands. Now, instead of doing so, I analyze the specification from the disassembly. After all, white-box methods are faster and more reliable than black-box methods.

Tools

In the analysis of SNES SPC700, I still use the classical disassembly method. The following is a list of tools I use.

There are SPC loader and SPC700 processor plug-ins that can be used with Hex-Rays' IDA Pro. Also, recently, DiztinGUIsh has become known as an advanced assembler for SNES main CPU, but it may not yet support SPC700. I'm interested in integrating advanced disassembler into my workflow, but it hasn't happened for me.

Where to learn SPC700 disassembly

TBD

Disassemble a SNES SPC700 file

I find this part of the process inefficient, but so far I haven't found a good workflow. Ideally, I would like to get code that can be assembled with Asar or similar, with very little rework.

The dump of 64 kB RAM is stored at offset $100-$10100 (hexadecimal offset) of the SPC file. The first 512 bytes are a general-purpose memory area called the direct page, and there is almost no chance that code will be contained here.

Therefore, first save the offset 0x300 to 0xFE00 bytes of the SPC file to a file using the hex editor.

Extract RAM region from SPC file

Disassemble the extracted file as follows:

spcdas my-spc-0300.bin -load 200 -pc 200 my-disassembly.s

You will get an output in the following format:

0200: 20        clrp
0201: c0        di
0202: cd ff     mov   x,#$ff
0204: bd        mov   sp,x
0205: e8 00     mov   a,#$00
0207: 5d        mov   x,a
0208: af        mov   (x)+,a

I prefer to put a newline after ret/jmp instructions for readability. Use the regular expression pattern (\b(ret|jmp)\b.*)$ to insert a newline.

Put newline after ret/jmp instructions using the regular expression pattern

Cleanup the disassembly

The disassembly created in this way contain all binaries, not just the executable code. Since a large part of the file consists of noise, we have to remove it first. In most cases, the executable code will be less than 0x2000 bytes.

To get an approximate location of the code, check the PC register values in the SPC700 Player. It is good to know that some games may place separate code at $FF00-FFFF to use the pcall/tcall instructions.

Check the PC register value in SPC700 Player

Let's delete unnecessary lines. Incorrectly assembled code is meaningless as code, so it will have unnatural content such as a series of instructions that are not often seen.

Also, the data table referenced by the code is disassembled as an instruction, which needs to be fixed as well.

; Wrong
1932: 0c 43 0c  asl   $0c43
1935: 54 09     eor   a,$09+x
1937: 88 09     adc   a,#$09
1939: 91        tcall 9
193a: 09 e8 09  or    ($09),($e8)
193d: 23 09 2e  bbs1  $09,$196e
1940: 09 38 09  or    ($09),($38)

; Correct
1933: dw $0c43  ; 00
1935: dw $0954  ; 01
1937: dw $0988  ; 02
1939: dw $0991  ; 03
193b: dw $09e8  ; 04
193d: dw $0923  ; 05
193f: dw $092e  ; 06
1941: dw $0938  ; 07
loveemuloveemu

To be continued...?

Next steps:

  • Focus on the writes to $f2 and $f3 and comment which line updates which register in the S-DSP
  • Look for jump tables for voice commands to reveal the range of commands and live pointer locations to the current command
  • Find out the lengths/internals of voice commands by reading the reverse assembly corresponding to each command
  • The SNES SPC700 Player Memory Viewer allows you to dynamically patch sequence data and internal variables for casual observation of changes in sound driver behavior
  • By examining how sequence data pointers are initialized, we can build an algorithm to obtain the start address of sequence data
  • By paying attention to the code that sets the SRCN, you may be able to understand the process of selecting a waveform from the instrument number
  • Keep reading...