News: 11 March 2016 - Forum Rules

Author Topic: Trouble with SPC upload routine (SNES)  (Read 4195 times)

JeanMarieStaub

  • Jr. Member
  • **
  • Posts: 8
    • View Profile
Trouble with SPC upload routine (SNES)
« on: August 08, 2011, 08:04:34 pm »
For a project I'm currently working on, I need to upload my own code to the SNES APU.
I created a subroutine for this:

Code: [Select]
namespace spc

define spcaddr #$0100
define spcexec #$0100

// DESIGN:
//      Base address for spc data is fixed
//      Specific offset is handed via X register (16bit)
//      First 2 bytes of spc data is length
//      -> ACL to 8bit, LDA spc_length -> TAY
//      -> Upload loop, via LDA {spc_data},X

upload:
phx
// IPL portmap outside transfer state:
// $2141   = command (transfer / run)
// $2142-3 = target address
// $2140   = trigger
rep #$20 // 16bit accumulator
sep #$10 // 8bit x/y

lda {spcaddr}
sta $2142
ldx #$01 // transfer
stx $2141
ldx #$cc
stx $2140
-; cpx $2140
bne {-}

init:
// IPL portmap inside transfer state:
// $2140 = sequence number
// $2141 = payload
// init counters
rep #$30 // 16bit registers
plx
lda $0e0000+x // get length
inx
inx // point on the beginning of data
tay // countdown
sep #$20 // 8bit accumulator
lda $0e0000+x // get first byte of data
sta $2141 // write to APU
inx
dey
lda #$00
-; sta $2140
cmp $2140
bne {-}

loop:
lda $0e0000+x // fill data byte
sta $2141 // write data...
lda $2140 // get sequence counter
inc // increment
sta $2140 // write back
-; cmp $2140 // wait for echo from IPL
bne {-}
inx // increment sequence counter...
dey
bne loop // if not, do it again

end:
rep #$20
lda {spcexec}
sta $2142 // set exec address
stz $2141 // command: run
ldx $2140
inx
inx
stx $2140 // send sequence end / execute
-; cpx $2140 // wait for last echo
bne {-}

sep #$20
rep #$10
rts
https://gist.github.com/1133063

Stepping through the SNES CPU instructions, everything seems fine, the correct data gets uploaded, the bytes written get echoed on the right registers, etc.
However, looking at the APU memory, nothing gets written at the address I specified (0x0100) and after ending my transfer it doesn't try to execute the code at the address I specified (0x0100) either.
This is the first time I'm dealing with the SNES SPC, so please bear with me and illuminate me if I misunderstood something completely.
I've mainly been following this documentation: http://wiki.superfamicom.org/snes/show/Transferring+Data+from+ROM+to+the+SNES+APU

Bregalad

  • Hero Member
  • *****
  • Posts: 2763
    • View Profile
Re: Trouble with SPC upload routine (SNES)
« Reply #1 on: August 09, 2011, 02:42:30 am »
I'm not sure what your problem is but maybe you should read this too :
http://www.romhacking.net/docs/%5B197%5Dspc700.txt
It is very accurate and explains how to transfer data to the SPC700.

JeanMarieStaub

  • Jr. Member
  • **
  • Posts: 8
    • View Profile
Re: Trouble with SPC upload routine (SNES)
« Reply #2 on: August 09, 2011, 04:39:22 am »
I've looked over Anomie's doc, but I can't see any difference between his procedure and mine/that on the wiki.
Here is the relevant part of the trace (both CPU and APU):

Code: [Select]
0ef695 phx                    A:0201 X:fe3c Y:fea9 S:027d D:0600 DB:00 NvMxdIzC V:107 H: 224
0ef696 rep #$20               A:0201 X:fe3c Y:fea9 S:027b D:0600 DB:00 NvMxdIzC V:107 H: 254
0ef698 sep #$10               A:0201 X:fe3c Y:fea9 S:027b D:0600 DB:00 NvmxdIzC V:107 H: 276
0ef69a lda #$0100             A:0201 X:003c Y:00a9 S:027b D:0600 DB:00 NvmXdIzC V:107 H: 298
0ef69d sta $2142     [002142] A:0100 X:003c Y:00a9 S:027b D:0600 DB:00 nvmXdIzC V:107 H: 322
0ef6a0 ldx #$01               A:0100 X:003c Y:00a9 S:027b D:0600 DB:00 nvmXdIzC V:107 H: 358
0ef6a2 stx $2141     [002141] A:0100 X:0001 Y:00a9 S:027b D:0600 DB:00 nvmXdIzC V:107 H: 374
0ef6a5 ldx #$cc               A:0100 X:0001 Y:00a9 S:027b D:0600 DB:00 nvmXdIzC V:107 H: 404
0ef6a7 stx $2140     [002140] A:0100 X:00cc Y:00a9 S:027b D:0600 DB:00 NvmXdIzC V:107 H: 420
0ef6aa cpx $2140     [002140] A:0100 X:00cc Y:00a9 S:027b D:0600 DB:00 NvmXdIzC V:107 H: 450
0ef6ad bne $f6aa     [0ef6aa] A:0100 X:00cc Y:00a9 S:027b D:0600 DB:00 NvmXdIzC V:107 H: 480
..13cf cmp   x,#$02           A:00 X:cc Y:4d SP:01cc YA:4d00 NvpbHizC
..13d1 bne   $13d6            A:00 X:cc Y:4d SP:01cc YA:4d00 NvpbHizC
..13d6 cmp   x,#$03           A:00 X:cc Y:4d SP:01cc YA:4d00 NvpbHizC
..13d8 beq   $13cc            A:00 X:cc Y:4d SP:01cc YA:4d00 NvpbHizC
..13da cmp   x,#$04           A:00 X:cc Y:4d SP:01cc YA:4d00 NvpbHizC
..13dc beq   $13cc            A:00 X:cc Y:4d SP:01cc YA:4d00 NvpbHizC
..13de cmp   x,#$10           A:00 X:cc Y:4d SP:01cc YA:4d00 NvpbHizC
..13e0 bcc   $13e9            A:00 X:cc Y:4d SP:01cc YA:4d00 NvpbHizC
..13e2 cmp   x,#$20           A:00 X:cc Y:4d SP:01cc YA:4d00 NvpbHizC
..13e4 bcs   $13e9            A:00 X:cc Y:4d SP:01cc YA:4d00 NvpbHizC
..13e9 cmp   x,#$80           A:00 X:cc Y:4d SP:01cc YA:4d00 NvpbHizC
..13eb bne   $13f0            A:00 X:cc Y:4d SP:01cc YA:4d00 nvpbHizC
..13f0 cmp   x,#$85           A:00 X:cc Y:4d SP:01cc YA:4d00 nvpbHizC
..13f2 bne   $13f7            A:00 X:cc Y:4d SP:01cc YA:4d00 nvpbHizC
..13f7 cmp   x,#$86           A:00 X:cc Y:4d SP:01cc YA:4d00 nvpbHizC
..13f9 bne   $13fe            A:00 X:cc Y:4d SP:01cc YA:4d00 nvpbHizC
..13fe cmp   x,#$87           A:00 X:cc Y:4d SP:01cc YA:4d00 nvpbHizC
..1400 bne   $1405            A:00 X:cc Y:4d SP:01cc YA:4d00 nvpbHizC
..1405 cmp   x,#$88           A:00 X:cc Y:4d SP:01cc YA:4d00 nvpbHizC
..1407 bne   $140c            A:00 X:cc Y:4d SP:01cc YA:4d00 nvpbHizC
..140c cmp   x,#$89           A:00 X:cc Y:4d SP:01cc YA:4d00 nvpbHizC
..140e bne   $1413            A:00 X:cc Y:4d SP:01cc YA:4d00 nvpbHizC
..1413 cmp   x,#$8a           A:00 X:cc Y:4d SP:01cc YA:4d00 nvpbHizC
..1415 bne   $141a            A:00 X:cc Y:4d SP:01cc YA:4d00 nvpbHizC
..141a cmp   x,#$8b           A:00 X:cc Y:4d SP:01cc YA:4d00 nvpbHizC
..141c bne   $1421            A:00 X:cc Y:4d SP:01cc YA:4d00 nvpbHizC
..1421 cmp   x,#$90           A:00 X:cc Y:4d SP:01cc YA:4d00 nvpbHizC
..1423 bne   $1428            A:00 X:cc Y:4d SP:01cc YA:4d00 nvpbHizC
..1428 cmp   x,#$ff           A:00 X:cc Y:4d SP:01cc YA:4d00 nvpbHizC
..142a bne   $142f            A:00 X:cc Y:4d SP:01cc YA:4d00 NvpbHizc
0ef6af rep #$30               A:0100 X:00cc Y:00a9 S:027b D:0600 DB:00 nvmXdIZC V:109 H: 628
0ef6b1 plx                    A:0100 X:00cc Y:00a9 S:027b D:0600 DB:00 nvmxdIZC V:109 H: 650
0ef6b2 lda $0e0000,x [0efe3c] A:0100 X:fe3c Y:00a9 S:027d D:0600 DB:00 NvmxdIzC V:109 H: 686
0ef6b6 inx                    A:000f X:fe3c Y:00a9 S:027d D:0600 DB:00 nvmxdIzC V:109 H: 734
0ef6b7 inx                    A:000f X:fe3d Y:00a9 S:027d D:0600 DB:00 NvmxdIzC V:109 H: 748
0ef6b8 tay                    A:000f X:fe3e Y:00a9 S:027d D:0600 DB:00 NvmxdIzC V:109 H: 762
0ef6b9 sep #$20               A:000f X:fe3e Y:000f S:027d D:0600 DB:00 nvmxdIzC V:109 H: 776
0ef6bb lda $0e0000,x [0efe3e] A:000f X:fe3e Y:000f S:027d D:0600 DB:00 nvMxdIzC V:109 H: 798
0ef6bf sta $2141     [002141] A:00e8 X:fe3e Y:000f S:027d D:0600 DB:00 NvMxdIzC V:109 H: 838
0ef6c2 inx                    A:00e8 X:fe3e Y:000f S:027d D:0600 DB:00 NvMxdIzC V:109 H: 868
0ef6c3 dey                    A:00e8 X:fe3f Y:000f S:027d D:0600 DB:00 NvMxdIzC V:109 H: 882
0ef6c4 lda #$00               A:00e8 X:fe3f Y:000e S:027d D:0600 DB:00 nvMxdIzC V:109 H: 896
0ef6c6 sta $2140     [002140] A:0000 X:fe3f Y:000e S:027d D:0600 DB:00 nvMxdIZC V:109 H: 912
0ef6c9 cmp $2140     [002140] A:0000 X:fe3f Y:000e S:027d D:0600 DB:00 nvMxdIZC V:109 H: 942
0ef6cc bne $f6c6     [0ef6c6] A:0000 X:fe3f Y:000e S:027d D:0600 DB:00 nvMxdIzc V:109 H: 972
0ef6ce lda $0e0000,x [0efe3f] A:0000 X:fe3f Y:000e S:027d D:0600 DB:00 nvMxdIZC V:110 H: 156
0ef6d2 sta $2141     [002141] A:006c X:fe3f Y:000e S:027d D:0600 DB:00 nvMxdIzC V:110 H: 196
0ef6d5 lda $2140     [002140] A:006c X:fe3f Y:000e S:027d D:0600 DB:00 nvMxdIzC V:110 H: 226
0ef6d8 inc                    A:0000 X:fe3f Y:000e S:027d D:0600 DB:00 nvMxdIZC V:110 H: 256
0ef6d9 sta $2140     [002140] A:0001 X:fe3f Y:000e S:027d D:0600 DB:00 nvMxdIzC V:110 H: 270
0ef6dc cmp $2140     [002140] A:0001 X:fe3f Y:000e S:027d D:0600 DB:00 nvMxdIzC V:110 H: 300
0ef6df bne $f6dc     [0ef6dc] A:0001 X:fe3f Y:000e S:027d D:0600 DB:00 nvMxdIzC V:110 H: 330
0ef6e1 inx                    A:0001 X:fe3f Y:000e S:027d D:0600 DB:00 nvMxdIZC V:112 H: 986
0ef6e2 dey                    A:0001 X:fe40 Y:000e S:027d D:0600 DB:00 NvMxdIzC V:112 H:1000
0ef6e3 bne $f6ce     [0ef6ce] A:0001 X:fe40 Y:000d S:027d D:0600 DB:00 nvMxdIzC V:112 H:1014
0ef6e5 rep #$20               A:000e X:fe4d Y:0000 S:027d D:0600 DB:00 nvMxdIZC V:121 H:1272
0ef6e7 lda #$0100             A:000e X:fe4d Y:0000 S:027d D:0600 DB:00 nvmxdIZC V:121 H:1294
0ef6ea sta $2142     [002142] A:0100 X:fe4d Y:0000 S:027d D:0600 DB:00 nvmxdIzC V:121 H:1318
0ef6ed stz $2141     [002141] A:0100 X:fe4d Y:0000 S:027d D:0600 DB:00 nvmxdIzC V:121 H:1354
0ef6f0 ldx $2140     [002140] A:0100 X:fe4d Y:0000 S:027d D:0600 DB:00 nvmxdIzC V:122 H:  26
0ef6f3 inx                    A:0100 X:000e Y:0000 S:027d D:0600 DB:00 nvmxdIzC V:122 H:  62
0ef6f4 inx                    A:0100 X:000f Y:0000 S:027d D:0600 DB:00 nvmxdIzC V:122 H:  76
0ef6f5 stx $2140     [002140] A:0100 X:0010 Y:0000 S:027d D:0600 DB:00 nvmxdIzC V:122 H:  90
0ef6f8 cpx $2140     [002140] A:0100 X:0010 Y:0000 S:027d D:0600 DB:00 nvmxdIzC V:122 H: 126
0ef6fb bne $f6f8     [0ef6f8] A:0100 X:0010 Y:0000 S:027d D:0600 DB:00 nvmxdIzC V:122 H: 162
0ef6fd sep #$20               A:0100 X:0010 Y:0000 S:027d D:0600 DB:00 nvmxdIZC V:122 H: 856
0ef6ff rep #$10               A:0100 X:0010 Y:0000 S:027d D:0600 DB:00 nvMxdIZC V:122 H: 878
0ef701 rts                    A:0100 X:0010 Y:0000 S:027d D:0600 DB:00 nvMxdIZC V:122 H: 900

Some more information on what I'm exactly trying to do: I have a game where I want to add a sequence before a new game starts. For this, I need to mute the music from the title screen before my sequence starts and unmute it before I JMP back to the original game code.
Currently, the sound from the titlescreen stops (not through muting, but it just stops), even though none of my code seems to be executed, but when returning to the original game code, the volume gets set to 0 (even though my code is supposed to raise it to 0x23).

Here the code I'm trying to upload:
Code: [Select]
namespace mute
// FLG register
flag:
dw $000f // length of the code
lda #$6c
sta $f2
lda #$20 // unmute, disable_echo
sta $f3
cmp $f3, #$20
bne flag

// Lower Left Output Echo Volume
left:
lda #$2c
sta $f2
lda $f3 // read current volume
cmp #$10 // already at lowest?
beq right
dec // decrement it
sta $f3 // and write it back
cmp $f3
bne left

// Lower Right Output Echo Volume
right:
lda #$3c
sta $f2
lda $f3
cmp #$10
beq loop
dec
sta $f3
cmp $f3
bne right

loop:
bra loop


namespace unmute
// FLG register
flag:
dw $000f // length of the code
lda #$6c
sta $f2
lda #$20 // unmute, disable_echo
sta $f3
cmp $f3, #$20
bne flag

// Lower Left Output Echo Volume
left:
lda #$2c
sta $f2
lda $f3 // read current volume
cmp #$23 // already at desired volume?
beq right
inc // increment it
sta $f3 // and write it back
cmp $f3
bne left

// Lower Right Output Echo Volume
right:
lda #$3c
sta $f2
lda $f3
cmp #$23
beq loop
inc
sta $f3
cmp $f3
bne right

loop:
bra loop
https://gist.github.com/1133616

Nightcrawler

  • Hero Member
  • *****
  • Posts: 5792
    • View Profile
    • Nightcrawler's Translation Corporation
Re: Trouble with SPC upload routine (SNES)
« Reply #3 on: August 09, 2011, 08:20:34 am »
Quote
I have a game where I want to add a sequence before a new game starts. For this, I need to mute the music from the title screen before my sequence starts and unmute it before I JMP back to the original game code.

I believe that is your problem. Your game has already loaded and started to run it's APU program. The IPL boot code is no longer executing and thus you wouldn't be able to transfer anything anymore via the method in that document, or your code. Use BSNES's debugger and you can see execution of APU instructions. You'll see the game is running it's own thing and thus anything you write to the APU ports is being handled by it in the whatever fashion was programmed and not doing what you expect. After execution starts of an APU program, you can do nothing more from the CPU side but write to the registers. It's up to the APU program at the other end to do anything useful with that.

If that is indeed your problem, you need to hack the game's APU code, just as you would ROM code to do what you want.
TransCorp - Over 20 years of community dedication.
Dual Orb 2, Wozz, Emerald Dragon, Tenshi No Uta, Glory of Heracles IV SFC/SNES Translations

JeanMarieStaub

  • Jr. Member
  • **
  • Posts: 8
    • View Profile
Re: Trouble with SPC upload routine (SNES)
« Reply #4 on: August 09, 2011, 08:27:21 am »
This makes a lot of sense!
I guess I'll just have to find (or squeeze in) an "empty" music track and somehow tell the sound engine to play that.

Nightcrawler

  • Hero Member
  • *****
  • Posts: 5792
    • View Profile
    • Nightcrawler's Translation Corporation
Re: Trouble with SPC upload routine (SNES)
« Reply #5 on: August 09, 2011, 08:52:49 am »
Yes. You'll have to study the existing communication scheme a little bit. Many games just send a command and song number. Sometimes the entire soundtrack is contained in the APU memory. Other times, it must transfer it from the CPU when a song switch occurs.

Depending on the game's commands, you might be able to get away with simply adding some song data at an appropriate location. It may already be able to pick it up and play it if it allows offsets or indexes to song data to load from the ROM. Or, it might be bearish and require transfer code modifications, APU code modifications, etc.

First step is to see how your game operates and come up with a plan. Fun times are ahead! :)
TransCorp - Over 20 years of community dedication.
Dual Orb 2, Wozz, Emerald Dragon, Tenshi No Uta, Glory of Heracles IV SFC/SNES Translations

JeanMarieStaub

  • Jr. Member
  • **
  • Posts: 8
    • View Profile
Re: Trouble with SPC upload routine (SNES)
« Reply #6 on: August 09, 2011, 09:28:21 am »
Fun times indeed :-)
I got it working now, all I had to do was write two single bytes at two locations in RAM and jump to a subroutine :-)