How I analyze SPC files | SPC ファイルの解析手順 in 201X
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.
- spcdas: Simple SPC700 disassembler developed by byuu (aka. Near)
- SNES SPC700 Player and SNES SPC700 Player Memory Viewer: Music player and memory viewer
- Best text editor and hex editor: My favorites are EmEditor, Visual Studio Code and 010 Editor
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.
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.
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.
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
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...