News:

11 March 2016 - Forum Rules

Main Menu

Square SNES SPC Engine

Started by lytron, October 16, 2013, 12:06:53 PM

Previous topic - Next topic

lytron

Greetings!

As far as I know all/most Squaresoft SNES games use the same sound subroutines. I am looking for an disassemble of the SPC program and the supporting subroutines (data transfer to the SPC, "translating" sound commands from the 5A22 to the SPC and so on).
Is there any documentary and/or disassembly out there that you can provide? I don't want to re-invent the wheel.  ;)

Thank you.  :)

Bregalad

No, there is 4 major revisions of the sound engine, and each one is toatally incompatible from eachother (they were probably rewritten from scratch).

Revision 1 = FF4
Revision 2 = Romancing Saga
Revision 3 = FF Mystic Quest, FF5, Hanjuku Hero, Secret of Mana
Revision 4 = All the more recent games (there is in fact many sub-revisions of this one, so I don't even think it's correct to place them together)

I have a parially commented disassembly of Secret of Mana if you want I can give it to you.

lytron

Hey Bregalad,

a huge thank you for your reply. It would be great if you could give me the SoM sound program.

I saw a program that could edit the songs in many Square RPGs, at least change the instruments for different voices. So I guess that tool was adapted to each revision and special version, right?
Furthermore: Are there many things that are in common between the revision, or are they more different than comparable? Just asking out of curiosity. :)

Bregalad

PM me you mail address and I'll send you my SOM partially commented disassembly.

There is many in common between the different revisions, of course, but there is also major differencies, and binary incompatibility.

I can't list the exact differences as I don't remember (or even knew) them in the first place, but basically new features were added and some features were removed too. For example FF4 supports "software envelopes" (volume of a playing note is updated according to a data table in software instead of the S-DSP hardware), I'm not sure if RS supports them, but it's definitely removed in all the more recent versions of the engine, because it was not needed.

Legato mode (notes are not keyed on except the initial one) was added for Romancing Saga, removed for version #3 and then added again in version #4 (or did I miss it in version #2 ?).

Vibratoes' waveform is square in revisions 1, 2, 3 but triangle in version 4 (you can clearly hear the difference on big vibrato depths).

There's probably dozen of other differences I don't remember or I didn't even know.

Nightcrawler

That commented SOM dis-assembly sounds like something useful to add to the Documents section on the site. There's not too much in the way of commented SPC code there.
TransCorp - Over 20 years of community dedication.
Dual Orb 2, Wozz, Emerald Dragon, Tenshi No Uta, Glory of Heracles IV SFC/SNES Translations

lytron

Quote from: Bregalad on October 16, 2013, 03:20:44 PM
PM me you mail address and I'll send you my SOM partially commented disassembly.

Sent, thank you :)

Quote from: Nightcrawler on October 16, 2013, 06:20:18 PM
That commented SOM dis-assembly sounds like something useful to add to the Documents section on the site. There's not too much in the way of commented SPC code there.

So you might be interested in the Sound Engine of Ihatovo Monogatari as well? I already planned nontheless to offer my stuff here when I finish my Ihatovo Monogatari disassemble. :)

Bregalad

QuoteThat commented SOM dis-assembly sounds like something useful to add to the Documents section on the site
It is extremely incomplete, definitely not worth adding to the Doccuments. But pehaps lytron will complete it ^^

Nightcrawler

Quote from: lytron on October 17, 2013, 12:51:01 AM
So you might be interested in the Sound Engine of Ihatovo Monogatari as well? I already planned nontheless to offer my stuff here when I finish my Ihatovo Monogatari disassemble. :)

Of course. :) There are similar such dis-assemblies in the Documents section under the Source Code category.
TransCorp - Over 20 years of community dedication.
Dual Orb 2, Wozz, Emerald Dragon, Tenshi No Uta, Glory of Heracles IV SFC/SNES Translations

lytron

#8
Quote from: Bregalad on October 17, 2013, 04:39:24 PM
It is extremely incomplete, definitely not worth adding to the Doccuments. But pehaps lytron will complete it ^^

If I get something done, may I release it in this thread?  >:D

Quote from: Nightcrawler on October 17, 2013, 05:51:50 PM
Of course. :) There are similar such dis-assemblies in the Documents section under the Source Code category.

I will release it as soon as I am through with it. But if you are interested, you can read a few of my findings here.

October 19, 2013, 02:40:37 AM - (Auto Merged - Double Posts are not allowed before 7 days.)

Hey Bregalad,

I have a few questions, I hope you don't get annoyed by this...

1) In your disassemble, at $12DE, there is "MOV $6C, #$F2". When I logged a bit of Secret of Mana's APU's changes with Geigers debugger, in that Log it was "MOV $F2, #$6C". That variant makes more sense, because afterwards, $F3 is read (and you have to tell in $F2 which DSP reg you want to read out through $F3, as you know).
I can remember that there is a very old SPC document out there (by some guy from the C64 scene, who started his document with a "thanks for nothing!"-message) who mentioned some SPC disassembler who twisted this up... could it be that this disassembler was used here?
2) Logging showed that the execution of the APU code starts at $200. The stuff at $0-$1FF, are these the contents of the direct page registers, or am I twisting things up?
3) IIRC are the .spc-Files I can download everywhere over the internet complete rips of the sound RAM. So... the sound engine is in there, too? I mean: If I ever want to compare sound engines, I don't have to search in a big game ROM, I can easily look in these "SPC RAM dumps", correct?

... there is something I forgot to ask... meh, I can't remember...

Bregalad

Yes, the disassembler got a few instructions backwards, and this is a source of annoyance.

And yes the execution starts right at the beginning at 0x200 in this case (it's not the case of all sound engines though). 0x000-0x1ff is used as general purpose RAM and for the stack, much like it would be in a 6502. The stack page can also be adressed by zero-page instructions if the direct page bit in the PSW is set, but then the zero page itself can't be addressed by zero page instructions anymore until this bit is cleared again. I don't remember if SOM uses this feature or not.

And yes the sound engine is contained in all .spc file of course, because they're raw dumps of RAM content.

RetroHelix

You could also ask Mop over at somedit.blogspot.com for his notes on the soundengine. This is what's got him started on doing a SoM editor as far as i know.

lytron

Quote from: RetroHelix on October 19, 2013, 05:16:58 AM
You could also ask Mop over at somedit.blogspot.com for his notes on the soundengine. This is what's got him started on doing a SoM editor as far as i know.

Thanks, I guess that would be the easiest way. Couldn't bring myself to do it, yet. I'll try it in the next days. :)


Bregalad,
I'm working on it right now, I wonder about this:


1307: E4 DF       MOV      A, $DF ; ly: New Echo Value gets loaded from $DF (Gets first set in $0251)
1309: 3F 1B 06    CALL     $061B ;Change echo delay ; ly: A --> (Y), Value from $DF gets stored in DSP-reg $7D (Y-value set in Line 12FD)
130C: FD          MOV      Y, A
130D: D0 01       BNE      $1310
130F: BC          INC      A ;Compute start adress of echo buffer
1310: 1C          ASL      A
1311: 1C          ASL      A
1312: 1C          ASL      A ; ly: A * 8; if A was = 0, than A = 8
1313: 48 FF       EOR      A, #$FF
1315: BC          INC      A
1316: 60          CLRC
1317: 88 FA       ADC      A, #$FA
1319: 8D 6D       MOV      Y, #$6D
131B: 3F 1B 06    CALL     $061B ; ly: DSP-reg $6D: Echo Buffer Start Offset (value * $100)


This sets, as you wrote, the Echo Buffer Start Offset. Do I understand it correctly, that there is a fixed "End point" of the Buffer and that the start point is set flexibly according to the echo length value?

Like,
I have an echo value of E and I know that each step E is higher than 0, I need additional B bytes for buffer, but I don't want to surpass address X, so this calculates the start address
X - (E*B).

Simplified spoken.

Right?

Bregalad

Yes, basically it's exactly this. I don't remember the details, but the samples used for music eat a variable amount of space, so does the echo buffer, but the two are capped to a constant.

lytron

A short update:

I'm back digging into this code, and it gets interesting. I followed the flow of the program, and I am at the subroutine that reacts to the Main-CPU. I guess that was something that isn't documented yet?
Furthermore, I logged the APU with Geiger's Debugger and found out that there are even more Opcodes translated wrong. $C8 is translated as "MOV X, #$aa", but it is in fact "CMP X, #$aa" (like in line $0CD7, for example), so the whole thing didn't make any sense to me. I fix these mistakes as I comment it.

I accidentally found this thread by the way. Seems like you already know every command in the sound data. Good job, Bregalad! :)

Bregalad

Yes, communication with the main CPU has always been extremely obscure and I didn't understand a single part of it.
Not having a disassembly of the corresponding CPU part didn't help either.

All I can say is that while most games allows to play sound effects by simply writing to one of the 4 registers, Square games don't allow this and use a compilcated protocol instead.
Good luck to revere engineer it.

And yes I know the entiere command set, I figured it out mostly by corrupting-then-observing-changes method, but hacking the source code certainly helped to get the trickier command right.

lytron

Short info in between:

Good progress, but heck, does it take time! Spent my weekend on this. Because the communication between 5A22 and SPC is a bit more complex, I began to write a documentation that can accompany the source code file you sent me, but this takes time (the work flow is: Understanding a bit of source code, writing a description in prose text for the documentation in English, translating it back in German, realizing I left out half of the important stuff, correcting the English text). I guess the result may be interesting to read, though. If someone wants to use it for this site then, he/she is welcome.

EDIT: By the way, what program did you use to disassemble this SPC-Code? Could be useful for me for future SPC disassembles. :)

Bregalad


lytron

So, maybe someone of you can help me with this.

I'm a bit stuck at the moment, because a good portion of the code is somehow a "closed system" to me. It will take a bit until I'm into it. Fortunately, it's mainly the part you already commented, Bregalad, so I "just" have to retrace your findings.

Something I did today is this:
It multiplicates the values of the DSP registers $09, $19... $79 and sends them through the I/O-regs to the 5A22.
The multiplicants are loaded from registers $CD to $DC, and they are completely variable (for each song different and with no obvious specific pattern), and somehow set during the computing and setting of the channels' volume (at $0BEA, to be exact).

Maybe I can understand this better if I would understand for what the DSP registers at $x9 are good for. nocash's fullSNES doc isn't much of a help. Maybe someone of you can help out...?


; ly: This transfers four bytes back to the 5A22
; ly: Depending on bit 2 of $B1, the values of DSP OUTX-registers for channel 0-3 or 4-7 are...
; ly: ... multiplicated and transfered back. Here is an overview what it does:
; ly: Transfered byte: abbb.0ccc
; ly: a = is set if channels 4 to 7 are transfered, else clear
; ly: b = Bits 4-6 of the result of OUTX-register of channel x (DSP register $x9)...
; ly:     ... multiplicated with the value of ($CD + x * 2)
; ly: c = Bits 4-6 of the result of OUTX-register of channel x (DSP register $x9)...
; ly:     ... multiplicated with the value of ($CE + x * 2)
138E: E8 34       MOV      A, #$34
1390: C4 39       MOV      $39, A
1392: E8 CD       MOV      A, #$CD
1394: 8F 00 03    MOV      $03, #$00
1397: 43 B1 05    BBS      B2 $B1, $139F ; ly: Bit 2 of $B1 decide if in the upper or lower half of the DSP -OUTX reg gets stored
139A: 8F 09 B2    MOV      $B2, #$09 ; ly: $B2's value is a DSP address (see line $13B0/2): $09 = OUTX for Channel 0
139D: 2F 08       BRA      $13A7
139F: 8F 49 B2    MOV      $B2, #$49 ; ly: $B2's value is a DSP address (see line $13B0/2): $49 = OUTX for Channel 4
13A2: 60          CLRC
13A3: 88 08       ADC      A, #$08
13A5: E2 03       SET1     $03, 7
13A7: C4 3A       MOV      $3A, A ; ly: $3A = #$CD if $B2 = #$09, else $3A = #$D5 (if $B2 = #$49)
13A9: 60          CLRC
13AA: 88 08       ADC      A, #$08
13AC: C4 02       MOV      $02, A ; ly: Set up compare value for leaving the loop that follows

13AE: F8 3A       MOV      X, $3A ; ly: Load Load-register (will be between $CD and $DB)
13B0: EB B2       MOV      Y, $B2
13B2: CB F2       MOV      $F2, Y ; ly: Set DSP address to the -OUTX-register ($x9) for Channel x
13B4: EB F3       MOV      Y, $F3
13B6: 6D          PUSH     Y ; ly: Load old value and push it on the stack
13B7: BF          MOV      A, (X)+ ; ly: Load from the address that is in X into A and increment X
13B8: CF          MUL      YA ; ly: Multiply old value with loaded value
13B9: DD          MOV      A, Y ; ly: Only bits 12 to 14 of the result are important
13BA: 28 70       AND      A, #$70 ; ly: AND #%0111.0000
13BC: C4 38       MOV      $38, A ; ly: Temp-store the result
13BE: EE          POP      Y ; ly: Load old value back in Y
13BF: BF          MOV      A, (X)+ ; ly: Load value from next address into A
13C0: CF          MUL      YA ; ly: Multiply old value with the second loaded byte
13C1: DD          MOV      A, Y
13C2: D8 3A       MOV      $3A, X ; ly: Store back load-register-address
13C4: F8 39       MOV      X, $39 ; ly: $39 contains an register address. Starts at $34 at the beginning of this subroutine (see line $138E)
13C6: 9F          XCN      A ; ly: Changes high and low nybble, so the order of the bits is now 32107654
13C7: 28 07       AND      A, #$07 ; ly: so just bits 6,5 and 4 of the original multiplication result are important
13C9: 04 38       OR       A, $38 ; ly: Adds bits 6,5 and 4 of the first multiplication result to it (into the bits 6, 5 and 4)
13CB: 04 03       OR       A, $03 ; ly: if the higher registers are done, bit 7 gets set through this
13CD: AF          MOV      (X)+, A ; ly: Store value in $34-7 and increment that register address
13CE: D8 39       MOV      $39, X ; ly: Store the address back into $39
13D0: 60          CLRC
13D1: 98 10 B2    ADC      $B2, #$10 ; ly: Changes the address to the OUTX-register for the next channel ($x9, this does an x=x+1)
13D4: 69 02 3A    CMP      $02, $3A ; ly: Look up if all channels are done ($3A is the counter, for incremented see lines $13AE, $13B7, $13BF and $13C2)
13D7: D0 D5       BNE      $13AE
13D9: BA 34       MOVW     YA, $34
13DB: DA F4       MOVW     $F4, YA ; ly: This echoes back the values in $34 to $37, so the 5A22 can read them out of reg. $2140-3
13DD: BA 36       MOVW     YA, $36
13DF: DA F6       MOVW     $F6, YA
13E1: 58 04 B1    EOR      $B1, #$04 ; ly: Flip around bit 3 of $B1 (ready signal?)
13E4: 6F          RET

Bregalad

DSP registers $x9 contains the "output wave" value of each channel.
The only purpose I'd see to send them to the main CPU would be to display some kind of oscilloscope for debugging purposes, however the bandwidth would never allow a full oscilloscope so I don't know.

lytron

Quote from: Bregalad on November 17, 2013, 12:05:24 PM
DSP registers $x9 contains the "output wave" value of each channel.
The only purpose I'd see to send them to the main CPU would be to display some kind of oscilloscope for debugging purposes, however the bandwidth would never allow a full oscilloscope so I don't know.

Yeah, I could imagine some kind of automatic mixing subroutine that runs between 5A22. The 5A22 lets the SPC throw back some of these values and, according to some parameters, the 5A22 then does some additional setup (like turning down the volume or so) for these channels. Just a thesis. But that it is some debugging function is more likely, especially because Kikuta spent a lot of time on tweaking the OST.

Thanks again. :)