Ok, here's another problem: I'm trying to upload data to the SPC using the code available at the SNES Programming wiki. It works fine in Snes9x (at least for the first upload), but it hangs on a real SNES. After placing some printouts in the code I've come to the conclusion that it most likely hangs in this loop which does the actual data transfer:
Code:
CopyBlockToSPC_loop:
; Load the high byte of a with the destination byte.
xba
lda [musicSourceAddr],y
xba
; Load the low byte of a with the counter.
tya
; Send the counter/byte.
rep #A_8BIT
sta AUDIO_R0
sep #A_8BIT
; Wait for counter to echo back.
waitForAudio0M
; Update counter and number of bytes left to send.
iny
dex
bne CopyBlockToSPC_loop
The waitForAudio0M macro simply does -: cmp AUDIO_R0 / bne -
I really don't know why this would hang. Interrupts are disabled at this point, and the data to transfer is in RAM.
Any ideas?
Is this communicating with the SPC's built-in loader, or a different loader? Because that seems too simple to be for the initial loader. I could dig up the code I used if it'd help.
It also may be useful to disassemble the 64-byte ROM built in to the SPC, to see the loader itself.
I assume it's using some built-in loader since it doesn't send anything else to the SPC prior to this. Note that I only pasted the loop where I suspect that it hangs, but the program doesn't just enter that loop right away. There's some initial hand-shaking, setting the destination address etc.
If you have some SPC communication code that you know works on a real SNES then that'd be helpful, sure.
mic_ wrote:
If you have some SPC communication code that you know works on a real SNES then that'd be helpful, sure.
You might want to check out d4s's
N-Warp Daisakusen Sourcecode. It contains a full-fledged SPC700 mod player. Have fun.
Here is another thread with the code and more info:
http://nesdev.com/bbs/viewtopic.php?t=5362
Using BSNES (0.60) I've managed to track down what's happening, though I haven't yet figured out why.
After 240 bytes have been transfered (it always happens after 240 bytes regardless of what block size I use) the value that the SPC writes to Port0 fails to get mirrored on the SNES side, so the S-CPU gets stuck in an infinite loop waiting for the SPC to mirror the last value written by the S-CPU.
Here's a trace done with BSNES. I've replaced the SPC output with the actual instructions, since the BSNES debugger only outputs NOPs for the SPC for some reason. The block size I used here was $E0, so that's why Y=$10 when it freezes ($E0+$10 = $F0 = 240 bytes).
Code:
..ffe4 inc y A:ff X:01 Y:0e SP:01ef YA:0eff NvpbhizC
00848d cpy #$00e0 A:000e X:fecf Y:000f S:1ff9 D:0000 DB:00 NvMxdIzC V:230 H:1116
008490 beq $84ab [0084ab] A:000e X:fecf Y:000f S:1ff9 D:0000 DB:00 NvMxdIzc V:230 H:1140
..ffe5 bne $ffda A:ff X:01 Y:0f SP:01ef YA:0fff nvpbhizC
008492 lda [$fd],y [7f00f1] A:000e X:fecf Y:000f S:1ff9 D:0000 DB:00 NvMxdIzc V:230 H:1156
008494 sta $2141 [002141] A:0031 X:fecf Y:000f S:1ff9 D:0000 DB:00 nvMxdIzc V:230 H:1204
008497 tya A:0031 X:fecf Y:000f S:1ff9 D:0000 DB:00 nvMxdIzc V:230 H:1234
..ffda cmp y,Port0 A:ff X:01 Y:0f SP:01ef YA:0fff nvpbhizC
008498 sta $2140 [002140] A:000f X:fecf Y:000f S:1ff9 D:0000 DB:00 nvMxdIzc V:230 H:1248
00849b sta $f1 [0000f1] A:000f X:fecf Y:000f S:1ff9 D:0000 DB:00 nvMxdIzc V:230 H:1278
..ffdc bne $ffe9 A:ff X:01 Y:0f SP:01ef YA:0fff nvpbhiZC
00849d lda $2140 [002140] A:000f X:fecf Y:000f S:1ff9 D:0000 DB:00 nvMxdIzc V:230 H:1302
0084a0 cmp $f1 [0000f1] A:000e X:fecf Y:000f S:1ff9 D:0000 DB:00 nvMxdIzc V:230 H:1332
..ffde mov a,Port1 A:ff X:01 Y:0f SP:01ef YA:0fff nvpbhiZC
0084a2 bne $849d [00849d] A:000e X:fecf Y:000f S:1ff9 D:0000 DB:00 NvMxdIzc V:230 H:1356
00849d lda $2140 [002140] A:000e X:fecf Y:000f S:1ff9 D:0000 DB:00 NvMxdIzc V:231 H: 14
..ffe0 mov Port0,y A:31 X:01 Y:0f SP:01ef YA:0f31 nvpbhizC
0084a0 cmp $f1 [0000f1] A:000e X:fecf Y:000f S:1ff9 D:0000 DB:00 nvMxdIzc V:231 H: 44
0084a2 bne $849d [00849d] A:000e X:fecf Y:000f S:1ff9 D:0000 DB:00 NvMxdIzc V:231 H: 68
00849d lda $2140 [002140] A:000e X:fecf Y:000f S:1ff9 D:0000 DB:00 NvMxdIzc V:231 H: 90
0084a0 cmp $f1 [0000f1] A:000e X:fecf Y:000f S:1ff9 D:0000 DB:00 nvMxdIzc V:231 H: 120
..ffe2 mov [DestAddr]+y,a A:31 X:01 Y:0f SP:01ef YA:0f31 nvpbhizC
0084a2 bne $849d [00849d] A:000e X:fecf Y:000f S:1ff9 D:0000 DB:00 NvMxdIzc V:231 H: 144
00849d lda $2140 [002140] A:000e X:fecf Y:000f S:1ff9 D:0000 DB:00 NvMxdIzc V:231 H: 166
0084a0 cmp $f1 [0000f1] A:000e X:fecf Y:000f S:1ff9 D:0000 DB:00 nvMxdIzc V:231 H: 196
0084a2 bne $849d [00849d] A:000e X:fecf Y:000f S:1ff9 D:0000 DB:00 NvMxdIzc V:231 H: 220
00849d lda $2140 [002140] A:000e X:fecf Y:000f S:1ff9 D:0000 DB:00 NvMxdIzc V:231 H: 242
..ffe4 inc y A:31 X:01 Y:0f SP:01ef YA:0f31 nvpbhizC
0084a0 cmp $f1 [0000f1] A:000e X:fecf Y:000f S:1ff9 D:0000 DB:00 nvMxdIzc V:231 H: 272
0084a2 bne $849d [00849d] A:000e X:fecf Y:000f S:1ff9 D:0000 DB:00 NvMxdIzc V:231 H: 296
..ffe5 bne $ffda A:31 X:01 Y:10 SP:01ef YA:1031 nvpbhizC
00849d lda $2140 [002140] A:000e X:fecf Y:000f S:1ff9 D:0000 DB:00 NvMxdIzc V:231 H: 318
0084a0 cmp $f1 [0000f1] A:000e X:fecf Y:000f S:1ff9 D:0000 DB:00 nvMxdIzc V:231 H: 348
As you can see on the line
..ffe0 mov Port0,y A:31 X:01 Y:0f SP:01ef YA:0f31 nvpbhizC, the SPC actually writes $0f to Port0, but the S-CPU still keeps getting $0e when reading Port0 after that no matter how many times it tries.
Ack.. Now it became obvious why it happens after 240 bytes. $F0 is Port0 on the SPC side, so it's overwriting the real value of Port0.
This is the same thing I was fighting with last week!
F0 is the test register, I figured its best to just not touch it.
F1 can disable the IPL, so make sure you keep D7 set when you write to it. It can also clear the IO ports which probably will do bad stuff.
F2/F3 are the DSP regs, to be used later
F4-F7 are the IO ports, which you also shouldn't touch.
I got all this working on real hardware, but the DSP register uploads would frequently stall. No idea whats going wrong and debugging messages would make it happen more often.
mic_ wrote:
the BSNES debugger only outputs NOPs for the SPC for some reason.
The debugger can't call any of the memory access functions that consume time or affect I/O states, specifically I need to avoid $f0-ff, just as you found out in this case :)
But the same function that reads $f0-ff also handles the switch to override reading $ffc0-ffff when the IPLROM is enabled.
So the debugger only reads from raw APURAM, bypassing the IPLROM enable bit for now.
I do need to come up with a workaround, of course. It's quite annoying seeing NOPs for the initial loading.
Here's a short set of routines for uploading and executing data on the SPC-700:
spc_util.s
For example, to do the following:
upload $1234 bytes of data from $7E1200 in main RAM to $5678 in the SPC
upload $2345 bytes of data from $7F1111 in main RAM to $4321 in the SPC
execute at $7654 in the SPC
do this:
ldy #$5678 ; addr in SPC
jsr spc_first_addr
ldx #$1234 ; length
lda #$7E ; 24-bit addr
ldy #$1200
jsr spc_upload
ldy #$4321 ; addr in SPC
jsr spc_next_addr
ldx #$2345 ; length
lda #$7F ; 24-bit addr
ldy #$1111
jsr spc_upload
ldy #$7654 ; exec addr in SPC
jsr spc_execute
Here's another fun fact: if you're initializing the DSP registers with data from an .SPC file you need to do it in reverse order (or at least make sure that you write to register $6D (ESA) before register $6C (FLG)).
If you write a value with bit5==0 to FLG it seems like the echo buffer will begin to be filled with data almost immediately. If ESA hasn't been initalized to a proper value at that point, the DSP will write echo data starting at $0000, which makes the IPL routine go crazy since it uses the word at $0000 as a buffer for indirect writes/jumps.
This was confusing me a bit at first, since the fact that the DSP was overwriting $0000 meant that it would go undetected by the breakpoint I had put on writes to $0000 over the S-SMP bus
Thats great info! Will have to try it out with my loader. Would bet there are lots more little problems like that when loading SPCs on real hardware. Someone needs to make a good NSF type ripped format instead...
There is a format like that. It's called SNSF, but it's not widely used.
Quote:
Thats great info! Will have to try it out with my loader. Would bet there are lots more little problems like that when loading SPCs on real hardware. Someone needs to make a good NSF type ripped format instead...
Will a SPC player be added to the super powerpak ? I'd be great
Else I think there is already a programm called SPC2ROM or something I've used it before so if it works on real HW it'd do the trick too altough it'd be a slower process.
bunnyboy wrote:
Someone needs to make a good NSF type ripped format instead...
Yeah, seriously. SPC format is perfect if you want to play something just on the actual sound module (
like this), but it's not really an SNES format because it excludes most of the SNES.
As far I know
this is the only song that can't be played with .spc because it relies on constant SPC updates from the CPU (the SNES equivalent to constant $4011 writes hehe).
Bregalad wrote:
As far I know
this is the only song that can't be played with .spc
That and Wario's Woods (and the rest of
this list), including a lot of games that use the ARDI music engine. These rely on the host CPU to send NoteOn messages and the like. Or were you talking about rewriting its music engine from 65C816 to SPC700?
There's also stuff like my NSF player, and Sid Mania by Censor. I can't imagine why anyone
wouldn't want to write an SNES demo with NES or C64 sound.
But the main thing is that it is absurd to have to load 64kB of data to play even a sound effect. Other than the general-purpose RAM, there's probably only a few bytes real difference between a lot of SPC files.
Memblers wrote:
But the main thing is that it is absurd to have to load 64kB of data to play even a sound effect.
You mean from the perspective of a generic SNES program, or from the perspective of an SPC player? As I understand it, a generic SNES program can load a couple instruments and start the SPC700 playing an opening drum fill while the rest of the instruments load, but most don't.
Quote:
Other than the general-purpose RAM, there's probably only a few bytes real difference between a lot of SPC files.
Which is why SPC sets are distributed in a solid archive such as .rar or .7z: compression recognizes this and stores the majority of each file as references to the previous file in the set.
I mean from the perspective of an SNES program. Trying to play a set of SPC files is just painful. There would be too much load time, even if there was solid RAR type compression on SNES.
tepples wrote:
As I understand it, a generic SNES program can load a couple instruments and start the SPC700 playing an opening drum fill while the rest of the instruments load, but most don't.
That's interesting, what games do that?
I'm relieved to say that after a lot of BSNES tracing, looking at SPC files in hex editors, reading eKid's and Anomie's documentation, and good old-fashioned trial and error my loader now works with every single one of the songs in my test set (currently 19). I still need to do hardware tests with my latest code and clean things up a bit.
That is great news! Can't wait to see what I am doing wrong
Did you look at any production games to see what they were doing?
If you mean which songs I'm testing with, they're all from production games.
But for the loading code I don't think it'd be useful to look at how the games were doing it, because they obviously weren't loading .SPC files.
I could put together the code I use. What is your test set (so I can be sure they work)?
Here is my code:
http://jiggawatt.org/badc0de/spcplayer-1.zip
I've verified it on a real PAL SNES. If you don't get any audio output at first (occasionally happens on HW); press the reset button.
And here's my test set of .SPC files:
http://jiggawatt.org/muzak/spctestset.zip
Looks like we started from the same flawed sample code
Good idea putting F0/F1 in the init code. Just a couple questions:
How did you decide on $FF70 for init code?
Where do the io port init values come from?
Any idea why it sometimes doesn't start playing on real hardware? I noticed this too on files I knew worked.
Quote:
How did you decide on $FF70 for init code?
It was $FFA0 in the original code, but I looked at some .SPC files in a hex editor and saw that at least one didn't have padding (all 00/FF) in that area. But all seemed to contain padding at $FF70..$FF8F, so I picked $FF70 as my starting address.
Quote:
Where do the io port init values come from?
From $1F4..$1F7 in the .SPC file.
Quote:
Any idea why it sometimes doesn't start playing on real hardware? I noticed this too on files I knew worked.
Who the hell knows..
It seems to happen a lot with songs from Squaresoft games, but not with songs from e.g. Capcom or Konami games.
Quote:
From $1F4..$1F7 in the .SPC file.
OK duh
Wow, your test set kicked my player's ass. These either sometimes played as silence, or always as silence:
bof2-10.spc
ff6-307.spc
gp-20.spc
gp-32f.spc
Map.spc
smw-12a.spc
Wild-F.spc
I'll have to go through my code and post it in a day or so.
Just an idea for the startup code... games usually have an echo buffer where the SPC stores output samples to be feed-back later to create an echo effect. Since this buffer will never by used to store code, variables or samples, it should work to place the init code here - but maybe a click will be heard on the start of the song.
I'm pretty sure Squaresoft games uses $fc00-$ff00 to store variables - as I have partially reverse engineered Secret of Mana's sound engine, and the other games uses a variant of it. Secret of Mana uses $ff00-$ffff to store repeat stacks or something, so if you alter anything here it will go crazy, fetch incorrect sound data etc... so no wonder it don't work !
The problem would be for SPC files which doesn't use, such as Super Castlevaia IV.
Not all songs use echo, or at least doesn't have it enabled in their initial state (and thus no echo buffer start address specified). Sure, one could default to $ff70 for those songs.
Anyway, let's look at ff6-307.spc (from Final Fantasy 6), which is one song that requires you to press reset for it to start playing properly on a real SNES (there's no such problem in BSNES though). Every single byte from $f9a0..$ffff contains the value $ff, which doesn't seem to me like meaningful data. It uses an echo buffer ranging from $cd00..$f4ff, so there should be no risk of the echo buffer data overwriting the initialization code before it has finished executing. Still it behaves this way. The only difference I can see before and after pressing reset is that after pressing reset my code will start executing sooner, while on the first run I first have to make the flash cart copy the ROM to its internal RAM.
You got problems with random data corruption/crashes on the spc?
This is almost certainly caused by the echo buffer, as you already suggested.
I believe I disabled echo in my music player because I didn't want to waste any precious RAM, but I did try it once.
Absolutely make sure that you initialize the echo regs in the right order and with due delay between register writes! The latter is especially important.
How much delay is "due"?
I mean, I'm verifying that the SPC echoes everything back with a cmp/bne loop.
I actually just tried moving the write to DSP reg $6c (FLG) away from the other DSP writes and down to my initialization routine. So now enabling echo is the last thing it does before it jumps to the PC init value. Still doesn't work properly.
My latest code:
http://jiggawatt.org/badc0de/spcplayer-1.1.zip
I've managed to fix the problem with some songs occasionally not starting to play until you reset the system. At least I think I've fixed it, because I've not seen it happen a single time now, while it used to happen quite frequently with certain songs in the past.
The order in which you do things do certainly seem to be important. I've also tried to make sure that the echo feature is inactive before I upload anymore data to the SPC. As a result it will now take up to 2-3 seconds for some songs to start playing, but that's better than not playing at all. I may be using both a belt and suspenders here, but when I finally got it working I didn't feel like going back and see what I could remove.
EDIT: I noticed a flaw in my latest code: The jsr to CopySPCMemoryToRam should probably come after the code that writes $20 to FLG, in order to avoid overwriting the two bytes at $100..$101 in the SPC RAM dump. It seems to work anyway, so meh..
When you power up the SPC, it often has a non-zero EDL. This means that the echo offset could become fairly large. So the first thing you want to do is set EDL to 0, and wait up to 1/4 second for the echo offset to become zero. Otherwise, even if you set EDL correctly, then enable echo in FLG, it could write way beyond what EDL would suggest because the echo offset won't have looped yet and used the new EDL.
I'm still trying to work out one last problem with my code. I think it may be due to not restoring the four I/O registers soon enough, so the SPC engine is interpreting them as commands from the game. I'm still getting occasional silence, even when echo is disabled in FLG. I was trying to run them in my SPC emulator, to trigger the problem there, but no go (I was running the upload code with a varying delay after resetting, in a loop).
Zeroing EDL wouldn't do much good in my case, since I still need to upload data to that area, so I can't have the DSP writing echo buffer data at all. That's why I disable echo in FLG before I start uploading data to the SPC or setting any other DSP regs, and then I enable echo again (if it should be enabled) right before I branch to the SPC entry point address.
Except for the occasional noise you might get at the start of a song my loader works perfectly, as you can see here:
http://www.youtube.com/watch?v=ceIEKKJ-_70
mic_ wrote:
The order in which you do things do certainly seem to be important.
So future peoples don't have to dig through the source can you list what the important order parts are?
I started adding more of your ideas to my player and it has improved significantly. Should have another cleaned up beta mappers soon.
bunnyboy wrote:
mic_ wrote:
The order in which you do things do certainly seem to be important.
So future peoples don't have to dig through the source can you list what the important order parts are?
I started adding more of your ideas to my player and it has improved significantly. Should have another cleaned up beta mappers soon.
Disable echo (FLG=$20) before sending anything else to the SPC.
Wait for EDL frames to make sure echo processing has stopped. I'm not sure if this is necessary when disabling echo altogether in FLG, but I do this anyway.
Initialize the DSP registers, except for FLG which should be initialized as late as possible.
Copy the SPC data to SPC RAM $0002..$00ef, $00f8..$00fc, $0100..$ffbf.
Copy your init routine to $ff70. Apart from initalizing the SPC registers, the init routine should init SPC RAM at $0000..$0001, IO regs $f0..$f1 and DSP register $6c. Most .SPC files contain $ff as the init value for IO reg $f0, but this seems to be a bad value to write to it. I write the value $0a.
Start execution on the SPC and as soon as the SNES side gets a $cc back from the SPC to indicate that execution is about to begin, initialize audio ports 0..3 on the SNES side with the data from $01f4..$01f7 in the .SPC file.
Great work _mic !
Hopefully it'll work with the super powerpak (that I just ordered).
Is the "big red delay" at
http://ekid.nintendev.com/snes/spctech.htm#dspedl what blargg was talking about, waiting 1/4 sec?
Most likely. It also mentions that you can wait EDL*16ms, which is what I do (or EDL*20ms since I have a PAL console).
I am probably missing something, but aren't you waiting based on the EDL value in the .spc instead of the value in the SPC700? Say the file says EDL is 1 but the chip has 100 already randomly loaded. Your wait won't be long enough?
Maybe it just isn't needed at all so it won't make a difference.
EDL can only vary from 0 to 15. So if you wait enough time so that even if EDL was 15 it cycles trough the buffer once (I'm not sure how many time it is but about 1/2 a second) you're safe.
mic_ wrote:
Zeroing EDL wouldn't do much good in my case, since I still need to upload data to that area, so I can't have the DSP writing echo buffer data at all. That's why I disable echo in FLG before I start uploading data to the SPC or setting any other DSP regs, and then I enable echo again (if it should be enabled) right before I branch to the SPC entry point address.
Doesn't matter if you zero it; if it was $0F before and the internal echo length value was based on this, the echo offset will go all the way to its maximum. So let's say it's near its maximum and you enable echo write, you get a write way past the end of the echo buffer. As Bregalad says, you must assume that when you write EDL, the echo offset is just past zero, and EDL is $0F. Anomie says 1/4 second maximum wait in his doc; I haven't bothered to verify his calculation.
I'm putting together my code. It only occasionally fails to load; maybe someone can figure it out. I think you'll find this approach much simpler (credit to Anti Resonance for his SPC2ROM code a while back when I was writing this).
Here's the SPC uploading algorithm, written in C so it could be verified correct:
spc_uploader.c
The basic algorhtm is to make copies of the DSP registers and 64K RAM from the SPC file, then upload them.
The DSP registers are patched to disable echo and a few other things, then uploaded using a small routine, rather than trying to use the bootloader.
The RAM is patched to disable the timers, and various other things, and patched with some final loader code which restores CPU registers and sets the registers to their final values before running the SPC.
Once the two patched versions are generated, you just upload the DSP one, execute it, then upload the 64K SPC data and execute it, then restore the four input ports to their final values.
Doing that search for free space to put loader code is a good idea. I might try that if the $FF70 section is found to be used lots.
I typed up a nice
Wiki page describing SPC uploading using the code I posted yesterday. I added more explanation of the various patches.
Now I'm running the code on a directory of 1200 SPC soundtracks, having it check for any that come up silent or don't start any new notes after the beginning (running on my SPC emulator of course). Should take a while.
Wiki page looks great. Couple suggestions:
-Link to .c file on your server is bad. Can it be moved to the wiki?
-Add DSP register addresses. Yes they can be looked up elsewhere but would fit well in the table.
-Explain F0/F1 a bit more. I especially found almost no mention of the test register anywhere.
-A bit confusing: "Since we write DSP registers first, this delay will be taken care of by the time it takes to load the 64K RAM." If DSP regs are loaded first, the delay isn't from loading the RAM? Again maybe it makes sense and I am just missing something.
Can you also check your spc database for how many don't have the $FF70 space free, or don't have any FF strings?
Feel free to edit the improve the Wiki page (one reason I made it such), including uploading the .c file. I'm not sure how I can add to Anomie's documentation of $F0 anf $F1. He covers both fully. I clarified the EDL description, hopefully.
Currently I have the big test log when it must use the echo buffer, and when even that isn't available and it must choose an arbitrary address. I can just write a separate scanner that goes over all the archives and checks the $FF70 space (
file-extractor to the rescue), noting how many bytes are all $FF.
Quote:
Can you also check your spc database for how many don't have the $FF70 space free, or don't have any FF strings?
I found one right away; Bishoujo Senshi Sailormoon. It does have some FF and 00 strings at other locations, but they're rather short so I'm not sure if they're padding or actual data.
EDIT: I found a few more:
Donkey Kong Country 3
Dracula X
Gokujou Parodius
Metal Morph (maybe)
StarFox
Star Ocean
Super Punchout
I tried these with my loader and none of them crash. None of them sound strange either, except maybe for the song I tried from Star Ocean (so-01.spc).
I just got a no-patch method working, where I execute the final code out of the four I/O registers (yes, it's as crazy as it sounds). The only modification to the SPC RAM is pushing three bytes (the final PSW and PC to jump to).
After you've loaded the DSP registers and 64K RAM, you tell the bootloader to start executing $00F5. The SPC bootloader code reaches this point, where it acknowledges to the S-CPU, then jumps to $00F5:
Code:
MOV $F4,A ; 4 send acknowledgement to S-CPU
MOV A,Y ; 2
MOV X,A ; 2
BNE Trans ; 2
JMP [$0000+X] ; 6 jump to $00F5
You have the S-CPU sit in a tight loop watching for acknowledgement. The moment that occurs, you write $2F $FE to the last two I/O registers. The SPC will be executing the NOP ad $00F5, then your new instruction, BRA *-2, a loop.
Now you have the SPC running a two-byte loop at $00F6. You then load a one or two-byte instruction into $00F4 (pad it with a NOP if only one byte), then write $FC to the fourth I/O register. This will change the loop to BRA *-4, so it'll execute the instruction you just wrote, over and over. Delay a bit so it has time to execute, then write $FE to the fourth I/O register again to stop executing the other instruction. Repeat for each instruction you want to execute. When you're done, execute RETI, which will restore PSW from the stack, then do RET. Currently, I execute 18 instructions (36 bytes) via this method to do the final restoration.
I tested the timing and it's very generous. I had the DRAM refresh delay (40 master clocks) occur at each timing for the S-CPU, and it doesn't bother it. I inserted delays at critical steps, and it took quite a bit to delay too much. The only critical part is getting it into the loop; when executing instructions, you just need to delay enough for it to execute at least once.
I didn't test whether the SPC reading at the same time the S-CPU is writing to an I/O register causes problems. I've read that it might OR the two values. I reasoned that it can't XOR them, as that would break the bootloader. I analyzed the above and ORing wouldn't break it. The only place the SPC would be reading a byte while the S-CPU is writing is the branch offset at $00F7. That will be either $FE or $FC, which differ only at bit 1. ORing these two yields $FE, which is fine for it to read in that case.
I'm still testing, but it's working pretty well. Sometimes songs don't play, but the same occurred for the old loader, so I'm not sure it has to do with this new method. I'll post more once I've banged on it more. I'm surprised I actually got this working.
High points for the absurd programming technique, even if it ends up fixing maybe a handful of games
OK, after about 14 hours, I've got this working well. I wrote a version that runs entirely on the SNES from an UNPATCHED SPC. You give it a buffer with the 64K SPC data, a buffer with the 128 DSP registers, and a buffer with the first 64 bytes of the SPC header (they can all be in ROM). Then it uploads to the SPC-700 using this execute-from-I/O method.
I've also got the I/O technique working perfectly to where it doesn't even execute an instruction more than once, so you can run PUSH A for example and not have it push more than one byte. This was just too interesting a technique to not attempt to make solid and reliable.
One reason I wrote this SNES version is I figured bunnyboy might be able to use it in his SNES player (I'm assuming it runs on the S-CPU, and not something custom). Will post something tomorrow.
Bonus points for originality
Are you still having the problems of songs sometimes not starting? Any significant advantage to this method compared to just patching the .SPC?
The reason why one of the songs from Star Ocean sounds a bit strange when I use my loader could be that it appears to use $FFC0..$FFFF as normal RAM. It has what appears to be actual data in that area, and that data does not match the IPL machine code sequence.
I wrote a
brief article about the experiences from writing my SPC loader, for all you peoples from the future (won't anybody think of the children?!)
mic_: You may wish to include Terranigma in your test set. You will see why skipping the writing of FFC0-FFFF, and forcing register F1 to 8x is a bad idea.
Oh, and FF70 in that games SPC driver is USED. better implement a free space search algo, then use FF70 as a LAST resort.
Write 00 to both KON and KOFF dsp registers, and load the correct KON register in the bootloader.
Ah, TCALL is evil. Won't be too hard to fix these problems though.
Here's source to the no-patching version, that runs on the SNES:
upload_spc_nopatch.zip. It came out quite short. The main benefit is that you don't have to modify anything (except the three bytes pushed on the stack). This also means you don't have to search for space to put the final code (simple enough when running on a PC, but tedious if the patching code itself has to run on the SNES). So far I haven't found any problems with this, though I have no way of automating testing on the SNES.
I've made some changes to my code so that e.g. Terranigma now works:
http://jiggawatt.org/badc0de/spcplayer-1.2.zip
The way it works now is like this:
* Turn echo off in FLG, then init the rest of the DSP regs.
* Upload a small routine at $0002 in SPC RAM which inits $00, $01, KON, KOF, sets up $f1 so that $ffc0..$ffff is normal RAM, recieves all data between $00f8..$ffff at two bytes per iteration, and finally branches to the init routine.
* The init routine is placed at some address in SPC RAM which is determined in the following way:
# If EDL is non-zero, the address is set to ESA*$100 + $200. $200 is
added to avoid pages 0 and 1 in case ESA is 0.
# If EDL is zero the entire SPC RAM dump is scanned for a free chunk
of memory (one that contains a string of only FF or 00).
# If both of these fail, $ff70 is used as a last resort.
* The init routine sets up the SPC registers, FLG, recieves all data between $0000..$00f1, and finally branches to the SPC entry point.
Does your code write 0 to EDL at the beginning, then wait at least 1/4 second before writing the new value and executing the final restoration code? I don't see any way around doing all this. For example, if you merely write EDL with the new value at the beginning and FLG with $60, later when you enable echo, the current echo position could be anything within the echo buffer. If that happens to be right at your final restoration code, you've got problems. By keeping EDL at 0 until the very end, you ensure that the echo position is 0 when your restoration code is running (your +$200 ensures that it won't overwrite the beginning once you enable echo).
At the point in my init code where I re-enable echo there are only two instructions that remain to be executed (pop psw / jmp entry_point). If it's true that it takes the DSP 240 ms to write 30 kB of echo buffer data then there's no way it could fill up $200+ bytes in the time it takes the SPC to execute those two instructions.
Now, if the DSP would start writing data at some pseudo-random point in the echo buffer when I enable the function then that could be a problem. But my understanding was that writes to ESA/EDL are "delayed" only when the echo function is active - not when ~ECEN goes from 1 to 0.
I'll do some hardware tests later.
Quote:
At the point in my init code where I re-enable echo there are only two instructions that remain to be executed (pop psw / jmp entry_point).
What about KON and $F2?
Quote:
If it's true that it takes the DSP 240 ms to write 30 kB of echo buffer data then there's no way it could fill up $200+ bytes in the time it takes the SPC to execute those two instructions.
How do you know that the echo pointer is at the beginning of the buffer when you enable echo writes?
Quote:
Now, if the DSP would start writing data at some pseudo-random point in the echo buffer when I enable the function then that could be a problem. But my understanding was that writes to ESA/EDL are "delayed" only when the echo function is active - not when ~ECEN goes from 1 to 0.
Yes, the echo pointer etc. is ALWAYS running. The ONLY thing that ECEN affects is whether the final writes occur. Remember, this is hardware, not software, so the usual "why would it waste cycles updating the echo pointer when writes are disabled?" aren't relevant. I've found that with most sound chips, it's the exact opposite: they keep everything running unless it can't be. Some operation that stops somthing will do the minimum possible to achieve the desired effect.
It's not clear exactly what you're doing, so I'll give more detail of one wrong method (as far as I can tell), with made-up values.
At the beginning, you first write $60 to FLG, to disable echo writes. You then write the EDL the SPC file uses, $01. This makes the echo buffer $800 bytes long. You then write ESA with $F0, putting the echo buffer at $F000. The echo position might currently be beyond the new EDL, but that's not a problem because writes are disabled.
Then you upload the rest of RAM, which takes at least 1/4 second, so you know that after that, the echo pointer is guaranteed to be within $F000-$F7FF. You weren't able to find any other space for your final restoration code, so you have it in the echo buffer at $F200. Your final code writes 0 to FLG, enabling echo writing, and then does a few more final things (KON, DSP address, PSW, PC).
The problem is that once you clear FLG, the echo buffer will start writing soon after, and it might happen to be pointing at the last byte of your restoration code and overwrite it before you execute it. I'm not saying your code suffers from the above, but I wanted to be sure I had described the problem fully enough.
It may be a theoretical possibility. It has never happened in practice though (and I've run probably something like 100 tests on my SNES with the different versions of my loader). This wasn't even a concern of mine when I made my latest changes. What I wanted to accomplish was to transfer the entire memory range and to avoid having a static address for my init routine, since those two things were preventing Terranigma songs from playing. While I was at it I decided to speed up the transfer loop a bit.
Of all the songs I've tested now, the only one I've noticed any problems with is the intro from Pinball Fantasies which has some missing voices. I haven't looked into what it does that makes it special, but perhaps I'll en-nerd myself in that tomorrow.
Even for an echo buffer of just $2000 bytes, you have less than a 1% possibility of it corrupting your final patch as it's executing, so you won't see it occur often. The fix is simple: set EDL to 0 initially, and then set it to the correct value just before/after restoring FLG. Not sure why you'd avoid this simple fix and have the loader fail occasionally for no obvious reason.
Wow, I was just looking over the code Anti Resonance sent me in 2004, and I came across this comment (not sure if he ever got it working, but it got me thinking outside my box):
Code:
;Code contained at 00F3-00FD:
;00F2 CF Mul YA ;9 41 ;Give the 65816 enough time to write values
;00F3 E8 ?? Mov A,#imm ;2 7 ;Load A with value in port 0
;00F5 C5 ?? ?? Mov abs,A ;5 17 ;Store A to address in ports 2 & 3
;00F8 FE F6 F7 DBNZ 0F6h,00F2 ;7 24 ;Decrease port 2 and write it back [14]
;00FB 5F 00 00 Jmp 0000h ;3 10 ;If port 2 was 01h, jump to 0000
He's got code at $F2-$FD! $F2 is DSP addr, a usable RAM location, $F3 is the value in the DSP register, also usually acting like RAM. $F4-$F7 are of course the I/O registers. $F8 and $F9 are RAM. $FA-$FC are the counter targets, and $FD is a counter output. It seems that the above wouldn't work because the counter targets always read back as zero, but still, them reliably reading back as zero, and the DSP registers being usable as RAM are great ideas. He could of course have used $F0 and $F1 as two extra NOPs (since they always read as zero), and perhaps eliminated the MUL as a delay, freeing an extra byte.
I found out yesterday why Pinball Fantasies wasn't working with my loader. My free-space searcher had a bug in it that caused it to recognize only strings containing 000000.., and never those containing FFFFFF.., so it ended up placing the init routine in the wrong place.
I also managed to cut 2 bytes from the init routine by using the (X)+ addressing mode. So after having saved those 2 bytes I decided to add the write to EDL to the routine, resulting in a net growth of 4 bytes. My init routine now weighs in at 43 bytes in total. I considered storing the 4-byte code sequence MOV $F3,#imm / RETI at $f4..$f7, which would've initialized FLG, popped the PSW and popped the entry point. It would've grown my routine by 12 bytes though, and it didn't work properly so I scrapped that idea.
The main transfer loop that recieves the bulk of the data on the SPC side has also been optimized by 1 cycle/byte.
My latest code:
http://jiggawatt.org/badc0de/spcplayer-1.3.zip
How does your loader fare with the SPC tests I posted a few days ago?
I haven't tried them. The link you posted redirects to a page named /notify/2.php on various random domains, which all contain the same set of ads.
Argh, wish someone had mentioned that to me sooner. I've fixed the link to my no-patch SPC uploader:
upload_spc_nopatch.zip
initial_in_ports: passed.
initial_regs: error code 3. I only write bits 0..2 to $f1. I see no point in writing bits 4..5 since all they do is clear $f4..$f7 and I'm setting those explicitly later anyway (which you check in initial_in_ports.spc). And what do bits 3 and 6 of $f1 even do? I've never seen them being used or documented.
I also don't set $f2..$f3 since I assume those values to be leftovers from writing the DSP register values that are found at $10100.
full_ram: error code 5. Pretty obvious since I put my init code in the echo buffer if the song has one. In this case I put my 43-byte routine at $5800.
$F0: Always reads back as 0.
$F1: Low three bits should obviously be restored. The top bit should also be restored, since the SPC may have expected to read the IPL ROM, or RAM. The port clear bits should NOT be restored, as they aren't persistent. i.e. an SPC may write with the port clear bits set, have them clear, then thousands of cycles later the SPC file is captured, after the S-CPU has written new values to the ports.
$F2: SPC may have been captured just after the code wrote $F2, and before it's about to write/read $F3. Not restoring this would screw up that access.
$F4-$F7: Obviously these must be restored, as you do.
$F8-$F9: RAM, so obviously should be restored.
$FA-$FC: not tested by this, since they can't be read back directly.
$FD-$FF: I'm not assuming an SPC uploader will restore these to the values from the SPC file. The test requires that these read as zero, since it has the timers stopped via $F1 and thus they should never increment.
full_ram: Yeah, I figured 5. That's why I had it check the other things first, so that echo buffer patching wouldn't mask any other diagnostics.
I for one found these tests to be very useful when modifying my SPC uploader, to be sure I didn't break anything important.
I feel a bit stupid for asking for a "simple" solution but I'll do it anyway.
I'm writing my code in C via the SNES SDK.
@blargg
I was happy to see some C code here, too (your spc loader). I read your wiki page about uploading a SPC file to the SNES but it's still very cryptic to me.
@mic_
Didn't know you were here, too
@both
I think I can embed ASM code in C. Would it be possible to use your software in my C code? (either as C or ASM version) And what exactly do they do? From what I understand they load a defined SPC file and upload it to the SPC-700. Maybe that's basic APU stuff (please tell me if so) but are there any play control functions like play, stop etc?
@blargg
If I used your C-spc loader, how would I have to write the remaining functions to actually hear something within my game?
@mic_
As I told you I only know a small amount of ASM. Your spc-player code is embedded into a whole "application", is there a way to know what code I actually have to include in my program to use it?
I hope I'm not asking too many questions again, all I'm looking for is the easiest way to be able to play music in my program (in C code if possible).
Quote:
I think I can embed ASM code in C. Would it be possible to use your software in my C code? (either as C or ASM version) And what exactly do they do? From what I understand they load a defined SPC file and upload it to the SPC-700. Maybe that's basic APU stuff (please tell me if so) but are there any play control functions like play, stop etc?
An .SPC file is an entire program for the APU. Once you have uploaded the .SPC file and begun executing the code in it the song should automatically start playing, because you have no way for further communication with the SPC700 unless you know how that specific game's sound driver works (that, or resetting the SNES).
Quote:
As I told you I only know a small amount of ASM. Your spc-player code is embedded into a whole "application", is there a way to know what code I actually have to include in my program to use it?
The loading code is pretty much self-contained in loadspc.asm. The only stuff you'd need from spcplay.asm is the call to LoadSPC, and the stuff at the bottom that INCBINs the .SPC file data.
It would be neat if SPC file rips were wrapped like almost all other ripped formats, where there is a standard way to get control back so you could upload a new song. It might not be that hard to write an automated program to at least determine what values must be written to the ports to cause re-entry to the IPL ROM (though some games might use a custom loader, and never re-enter the IPL ROM). These key values could be stored at some currently-unused offset in an SPC file.
mic_ wrote:
The loading code is pretty much self-contained in loadspc.asm. The only stuff you'd need from spcplay.asm is the call to LoadSPC, and the stuff at the bottom that INCBINs the .SPC file data.
I guess I can work with that. Thanks!
Currently both SPC uploaders seem to do the same for me (as an outsider). Is that so or is one better than the other?
If I understand that correctly there is no way to upload another SPC after the first is uploaded to the APU. How did the game developers do that? It has to work somehow I guess. IIRC there were games in which the music was stopped during the game, too. Would this be done by uploading an empty SPC file?
AndiNo wrote:
If I understand that correctly there is no way to upload another SPC after the first is uploaded to the APU. How did the game developers do that? It has to work somehow I guess.
Each SPC program can receive commands on the four input ports. One command might be "stop song"; another might be "start other song whose note pattern data is already loaded"; another might be "reset and prepare to load another SPC". But these commands differ from one player routine to another.
So that means you have to implement the behaviour yourself I guess... How would you go about doing this with a given SPC file? Would you need to change the SPC itself or would you just upload different data? Would I be able to see these commands in SPCs from commercial games for example?
Run the game in the debugger version of BSNES and set a breakpoint for reads from $F4 on the S-SMP bus (and possibly $F5, $F6, $F7). Then step through the code from there to see what it does with the values it reads from those ports.
Or you could run the entire .SPC (at least everything between offsets $00100 and $100FF) through an SPC700 disassembler and search for such reads in the resulting code if you think using a debugger is cumbersome. The problem with this is that the disassembler can't tell code from data so it may end up messing up the disassembly.
AndiNo wrote:
I guess I can work with that.
Well seems like I've hit a wall now. The SNES SDK uses a modified version of WLA 9.4 with which I can not create a ROM from your spc player source. I got it working only with the usual WLA 9.5 I found somewhere, however I can't use this version within the SDK because of strange errors when assembling/linking.
I somehow integrated parts of your source into my game though and tried calling the ASM "function" LoadSPC from C, although something went wrong because the game freezes when calling it. I'll have to investigate a bit further although currently I have no real idea what I'm doing
Is there a way to step through the LoadSPC function while my program executes it (maybe with the BSNES debugger or something)?
edit: The spcplay.asm was cut down to this: (sorry I couldn't get it smaller here)
Code:
.include "snes.inc"
.define SPC_FILE "so-01.spc"
.include "loadspc.asm"
; SPC-700 register values
.bank 1 slot 0
.org $0000
.incbin SPC_FILE skip $00025 read $0008
; DSP register values
.org $4000
.incbin SPC_FILE skip $10100 read $0080
; The actual 64k SPC RAM dump
.bank 2
.section "musicDataLow"
.incbin SPC_FILE skip $0100 read $8000
.ends
.bank 3
.section "musicDataHigh"
.incbin SPC_FILE skip $8100 read $8000
.ends
Am I missing something important?
Quote:
Is there a way to step through the LoadSPC function while my program executes it (maybe with the BSNES debugger or something)?
Use bsnes-debugger.exe. Open Tools->Debugger. Check the "Step S-CPU" box. Press Break. Press Step as many times as you like.
One thing that might help is if you could see the assembly that the C compiler generates from your C code. Like how it's actually calling the LoadSPC function.
Since I haven't used SNESC myself I don't know if it's possible to make it output an assembly file for you.
Nice
Thanks! I found out it hangs in an infinite loop in loadspc.asm here:
Code:
.macro waitForAudio0M
sta spcMirrorVal
-: cmp REG_APUI00
bne -
.endm
As one can see in the debug output:
Code:
0083f7 bne $83f4 [0083f4] A:01aa X:00f2 Y:0002 S:1fe0 D:0000 DB:7e NvMxdIzC V:241 H: 12
0083f4 cmp $2140 [7e2140] A:01aa X:00f2 Y:0002 S:1fe0 D:0000 DB:7e NvMxdIzC V:241 H: 34
0083f7 bne $83f4 [0083f4] A:01aa X:00f2 Y:0002 S:1fe0 D:0000 DB:7e NvMxdIzC V:241 H: 66
0083f4 cmp $2140 [7e2140] A:01aa X:00f2 Y:0002 S:1fe0 D:0000 DB:7e NvMxdIzC V:241 H: 88
...
What does this tell me and is there something I can do about it? At least it looks like the ASM code is called correctly. I found out the SNESC compiler does not allow inline ASM for the 65816 target...
Is there a way in BSNES to see the log of the running program without clicking STEP a thousand times? Other than that it's pretty helpful
And yes the compiler itself only creates ASM files so I can see my program in assembly.
edit:
I used Geigers debugger to get what I wanted. I've reduced my code to a minimum to find the error. Here's my current code:
http://pastebin.com/TcHKqG2i
And here's the log from shortly before it hangs. Can anybody see what happened before it hangs? My assembly knowledge doesn't seem to suffice
http://pastebin.com/8M4zu3DL
Quote:
Code:
0083f7 bne $83f4 [0083f4] A:01aa X:00f2 Y:0002 S:1fe0 D:0000 DB:7e NvMxdIzC V:241 H: 12
0083f4 cmp $2140 [7e2140] A:01aa X:00f2 Y:0002 S:1fe0 D:0000 DB:7e NvMxdIzC V:241 H: 34
It tells me that the SPC is not responding when the CPU is trying to send data to it. This would happen if the SPC already was executing some code other than the IPL ROM - e.g. if you've already called LoadSPC once and then tried calling it again.
Quote:
Is there a way in BSNES to see the log of the running program without clicking STEP a thousand times? Other than that it's pretty helpful
Yes. Check the "Trace S-CPU opcodes" box in the debugger window. You'll get a large file in the BSNES directory that lists all the instructions that were executed.
Thanks for your continuing help!
I've edited the above post with some more information. I've also uploaded the complete log of my program here:
http://www.file-upload.net/download-2377870/log.zip.html
It looks as if an interrupt was serviced during the SPC uploading procedure. Interrupts must be disabled while sending data to the SPC, and in fact disabling them is the first thing I do in LoadSPC. I'm not sure how that still managed to happen in your program.
Ok, the last log doesn't have that interrupt weirdness. What's happening here is that the C compiler is messing with DBR and the status register.
Remove the first 3 instructions in LoadSPC (stz/sei/sta) and replace them with this code:
Code:
php
phb
sep #$20
stz REG_NMI_TIMEN
sei
stz spcSongNr
lda #0
pha
plb
And right before the RTS-instruction at the end of the LoadSPC function, insert these instructions:
Code:
plb
plp
That weirdness could have come from the fact that I used frame advance to jump over all "normal" code.
Although I have no clue what they do your changes had some effect. The log has grown a lot but loops in another place with this code as it seems:
http://pastebin.com/qJw8iYpr
I had to cut the log as it was already over 100MB. I hope you can still spot the problem...
http://www.file-upload.net/download-2378055/trace.zip.html
That's probably just the while-loop at the end of your main().
It would be kind of embarassing if that was true, however I can't hear any music playing after the upload. So there has to be something wrong don't you think? Are there any other things I have to change, maybe set values to special registers etc that are missing from my initialization code?
edit:
When I start my program in BSNES I can hear some very faint crackling shortly after the start. The "Flag - Echo Disable" does also change from true to false in the debugger properties viewer. So at least something is happening
edit2: When calling LoadSPC from my (non-trivial) game before the while loop the game freezes. The while-loop is never entered...
Well, I think that the stuff at the end of your trace is actually the while-loop in your main(), but I can't say for sure.
Anyway, the fact that you don't hear any music could mean e.g. that you're uploading the wrong data to the SPC. My loading code relies on a LOROM configuration, with the .SPC data being located in banks 1, 2 and 3 (this could be changed by altering SPC_DATA_BANK and SPC_REG_ADDR in loadspc.asm). Take a look at your .SMC file in a hex editor and make sure that the data starting at offset 0x10000 in the .SMC matches the data starting at offset 0x100 in the .SPC file. And the data at 0xC000 in the .SMC should match the data at 0x10100 in the .SPC.
You can also use the memory editor in BSNES' debugger to look at the S-APU RAM to see if the correct data has been uploaded to it.
I just ran my minimal program without the while loop but with a return statement. The logger then constantly prints "brk #$00" and "rti" so I think it's doing nothing anymore. There has to be some other fault in my game that I need to sort out later then.
The data at 0x10000 is completely empty in the SMC. However the data from 0xC000 to 0xC080 in the SMC are the same as 0x10100 to 0x10180 in the SPC. Do the values in loadspc.asm have something to do with the definitions in the header.asm that contains the SNES header? Is there any place to read about these ROM bank things so I get a clue what I'm doing?
In the memory editor I can only select "S-APU bus" but I think that is what you meant.
Quote:
I just ran my minimal program without the while loop but with a return statement. The logger then constantly prints "brk #$00" and "rti" so I think it's doing nothing anymore. There has to be some other fault in my game that I need to sort out later then.
Which implies that it reached the return-statement and returned to some area that doesn't contain any meaningful code/data.
The defines in loadspc.asm are related to parts of the header.. like the slot size, the rom bank size, the number of rom banks, and the memory mapping type (lorom/hirom).
I've tried compiling the program with the header.asm from your spc player. I had to add
Code:
SLOT 1 $0 $2000
SLOT 2 $2000 $E000
SLOT 3 $0 $10000
to .MEMORYMAP otherwise the SDKs WLA complains about not defined slots. But there's still only zeros at address 0x10000 in the SMC file. Am I still missing something or might that be a fault of the SDKs WLA (which is a modified WLA)?
edit: WLALINK seems to always use 8 rom banks as it prints the message "OBTAIN_ROMBANKS: Using the biggest selected amount of ROM banks ( 8 )." when linking.
There should only be one slot per bank. I don't see why it should complain about slot 1-3 not being defined when they're not being used (at least not in my code).
I think I've got WLA-DX 9.4 on this computer, but I've got 9.5something on another computer. Both of them are working for me with my code. I haven't tried the version of WLA-DX that comes with SNESC.
Yehawww it works
I think after the code change you proposed it was my fault that it didn't work. The wlalink in the SDK has the additional option of discarding unnecessary data that isn't used. This option was enabled and removed the included SPC from the bank 2 and 3. I saw that already some hours ago, but I didn't realize that an error popped up while linking and I didn't notice my batch file didn't create a new SMC as the old one was still there and so I thought that was the wrong way. But disabling this option and removing some "EmptyHandler" stuff from the header.asm made the linking work. So now I have music playing in my SNES app
Thanks for your support all day long!
Now let's come back to that SPC commands while it already plays music...
Darn. I tried scanning a Super Mario World and a Chrono Trigger SPC, using a program that tried every byte value on each of the four ports, checking to see if any caused the SPC to re-run the IPL ROM. None did. So it seems that each SPC driver will need to be manually examined to determine what, if any, command can be sent to tell it to re-run the IPL ROM. Many games probably lack this, as the IPL ROM uses a slow transfer mechanism. An optimized one speeds up song changing in-game. If someone is manually looking for this, they could probably just add a command to do so if it's not there.
I'm wondering whether the PowerPak has some kind hack to reset the SPC-700, or requires pressing the reset button when changing songs.
Quote:
I'm wondering whether the PowerPak has some kind hack to reset the SPC-700, or requires pressing the reset button when changing songs.
The former. I don't know if it can reset just the SPC700 or if it resets the entire system, but it goes back to the PowerPak logo screen when you press Start, and then you can start playing another SPC.
From what I understood so far you have to program the SPC file (which is a program) to not just play a song but also listen on several ports in order to know that it has to do something if there is data on one of them. In loadspc.asm there are two sections that I think are SPC codes, namely spc700_init_code and spc700_fasl_code. So you'd have to extend them so they listen to changes on the four ports?
How would a custom loader look like? Wouldn't it use a similar way since otherwise control over the SPC700 ist lost?
blargg wrote:
Darn. I tried scanning a Super Mario World and a Chrono Trigger SPC, using a program that tried every byte value on each of the four ports, checking to see if any caused the SPC to re-run the IPL ROM. None did. So it seems that each SPC driver will need to be manually examined to determine what, if any, command can be sent to tell it to re-run the IPL ROM. Many games probably lack this, as the IPL ROM uses a slow transfer mechanism. An optimized one speeds up song changing in-game. If someone is manually looking for this, they could probably just add a command to do so if it's not there.
I'm wondering whether the PowerPak has some kind hack to reset the SPC-700, or requires pressing the reset button when changing songs.
In some cases, you may have to reverse engineer the song upload protocols for the game in question, and upload an IPL rom enabling procedure specific to that games spc driver.
In the case of super mario world, Its song uploader is 99% the same code as IPL. the 1% difference is that upon getting a 0 on port 1, with an increment of at least 2/3 on port 0, it jumps back into its own main routine. However, the trick is to overwrite some code with the fake IPL loader instead. In this case, to Address 0x1338, upload the following data bytes. 0xCD, 0x80, 0xC9, 0xF1, 0x00, 0x3F, 0xC0, 0xFF, then set port 1 to 0, and increment port 0 by 2. This regains you full control of the APU, for this driver. To start the upload procedure, write 0xFF to port 1, wait for 0xAABB on port 0.
Finally, some games, like blazeon, don 't have any new data upload procedures at all, as All of the songs needed by the game are already part of the APU ram. In that case, you have to manually patch in some code before uploading the spc, otherwise, no amount of port manipulation will get you back to IPL.
Quote:
In loadspc.asm there are two sections that I think are SPC codes, namely spc700_init_code and spc700_fasl_code. So you'd have to extend them so they listen to changes on the four ports?
Those are only used during the uploading process. spc700_fasl_code is overwritten when spc700_init_code executes, and spc700_init_code is (in most cases) overwritten once the song starts playing.