123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450 |
- From PARADIS@htu.tu-graz.ac.at Fri Mar 25 08:41:08 1994
- The Bloody SPC-700
- ------------------
- A try to stumble into the inner secret of a nasty chip.
- By Antitrack exclusively for the FAMIDEV development group.
- Chapter 1:
- ----------
- FACTS
- * The SPC 700 is a very stupid sound chip with about the worst
- handling
- that you have seen in your lifetime.
- * This chip is a co processor. He has a quite large instruction set
- (contrary to the Amiga's COPPER, who has a very small one) and 64KB
- RAM
- memory, of which you can use atleast 32KB. (or so)
- * All program and data that is supposed to be run by this chip must
- be'
- moved to the SPC's own ram with a small loop that pokes each byte of
- your SPC assembler program and (e.g. sample-)data into four memory
- locations : $2140 - $2143. They are your only chance to communicate
- with
- the SPC.
- * These four memory locations have different meanings for read and
- write;
- if you read (LDA) $2140, you get the data from memory loc. 00f4 (or
- so)
- of the sound chip.
- * On power-on, the SPC 700 jumps (much like the main processor) to a
- very
- small ROM area that resides from $ffc0 to $ffff inside the SPC.
- (This chip REALLY follows the black box principle, eh...) This
- program
- at $ffc0 is waiting to get the data in the right format on his
- input ports
- at $00f4/5/6/7 , which are $2140/1/2/3 from the 65c816's (e.g.
- your's )
- point of view.
- * Your main program will therefore have to follow the SPC's
- conditions and
- poke all the program and data for the SPC into 2140/1/2/3 in a
- special
- order.
- * When transmission is completed, you will also have transmitted the
- start
- address of your SPC code, and the SPC will start to execute your
- program
- there.
- --------------------QUESTIONS.
- Q: How do I move my program and data to the SPC then, what format do
- I have
- to use?
- A: First, your SPC data/code has to be moved from ROM to the extra
- RAM at
- e.g. $7f0000 . Dont ask me why it has to be in RAM, probably it doesnt
- but all the existing routines that send data to the SPC do something
- like
- that.
- Your data/code has to be in groups which I will call "chunks". A
- valid chunk
- looks like that:
- first word: number of bytes to transmit to SPC -+
- sec. word : start address where to move data to the SPC | one chunk
- byte 4-???? : your data/code -+
- You can have as many chunks as you want to , but the last chunk must
- be like
- that:
- first word : $0000
- second word: Start address of your code.
- Q: So if you are right, this means: After I transmitted all my code
- and
- data, and my own SPC code takes over the control, I might encounter
- problems
- if my SPC program has to communicate with the outer world (the
- 65c816).
- What if the main program wants to change sounds? What if a background
- melody
- shall always play on two voices, and extra two voices will be used for
- sound effects whenever the player sprite e.g. picks up an object?
- A: That is sure a point. Your own code will have to look at memory
- locations
- $00f4/00f5/00f6/00f7 , because they are the only accessible from
- outside
- at $2140/1/2/3. The easiest way would be: As soon as any of $f4-$f7
- change,
- jump into the Boot ROM at $ffc0 (?) so the SPC is executing his
- receive
- routine again. Then you *probably* can send another SPC chunk with new
- sound and code to the SPC....
- Q: This only helps if a complete new tune is to be played, this
- doesnt help
- if a melody using two voices shall still remain....
- A: Thats true. The best approach is to send own command bytes to the
- SPC and
- your SPC code has to check out $f4-$f7 constantly and react to it.....
- A command byte like $00 could mean: sound off,
- $01 : play tune 1
- .
- .
- .
- $0f : play tune $0f
- $10 : play jingle (fx) 01
- .
- .
- .
- $ff : jump to $ffc0 (??) the receive
- ROM routine
- Q: is there another approach?
- A: Yes there is. As you probably know, all important addresses of the
- SPC 700 reside inside its own RAM's zeropage:
- Address / register / usage
- 0000 Volume left
- 0001 Volume right
- 0002 Pitch low
- 0003 Pitch high (The total 14 bits of pitch
- height)
- 0004 SRCN Designates source number from 0-
- 255
- 0005 ADSR 1
- 0006 ADSR 2
- 0007 GAIN Envelope can be freely designated by
- your code
- 0008 ENVX Present val of envelope with DSP
- rewrites
- 0009 VALX Present wave height val
- (and so on...)
- Your approach would be to move only sample data there, and/or (lots
- of) very
- small chunks of data with a target address in the zeropage, and a
- starting
- address of e.g. $ffc0. The small chunks would access zeropage
- addresses e.g.
- for the volume etc and thus result in tones; if this is done every
- frame
- you might end up with a music player quite similar to the C64 styled
- ones.
- Q: So anyway, in what format exactly do I have to move data to the
- SPC?
- A: I have the following source code for you, but let me explain it a
- bit
- BEFORE you start to dig into it.
- I've already mentioned the general "chunk" format. The loop does the
- following:
- - move ram destination address to $2142/3 (akku: 16 bit)
- - move either #$00 or #$01 into 2141, this depends if you have more
- than $0100
- bytes of data for the SPC;
- - first time (first chunk you transmit): move constant #$cc into 2140
- - loop: poke each byte that you want to be transmitted into 2140
- (word)
- the higher 7-15 bits of your accu-word contain the number of bytes
- already
- moved (e.g. 00 on the start)
- - cmp $2140 with this number of bytes already moved (lower 8 bits of
- this
- number only!) and wait if its not equal.
- - until the loop is over.
- - for the next chunk header this is repeated, but not #$cc is moved
- into
- 2140 but "nn" (lobyte of number of bytes moved) +3 or +6 if it was
- 00 when
- +3 was used.
- EXAMPLE:
- move #$0400 to 2142 /word access
- move #$01 to 2141
- move #$cc to 2140
- move "gg00" to 2140 where "gg" is the first real code/data
- byte for
- the SPC
- wait till 2140 is #$00
- move hh01 to 2140 where "hh" is the second byte of code or
- data for SPC
- wait till 2140 is #$01
- move ii02 to 2140 where "ii" is the 3rd byte of data for the
- SPC....
- wait till 2140 is #$02
- lets say "ii" was the last byte. Now we add #$04 (3+carry) to
- #$02
- (#$02 being the number-1 of how many bytes we moved to the
- SPC), we
- will push it onto the stack), now :
- fetch the next header , poke target RAM address into $2142
- (word)
- poke 00 or 01 into 2141 depending of how many bytes to send,
- poke #$06 into 2140 (06 : number of bytes sent from last chunk-
- 1 + 3 )
- I think I got this scheme pretty much right this time. Now, is PLEASE
- someone
- going to donate their home-brewed SPC dis/assemblers to me? Oh pretty
- please,
- I hate silent SNES's ! :)
- Source code follows, reassembled from a PAN/Baseline demo "xmas wish
- 92/93":
- ----------------------------------------------------------------------
- ------
- ; entry to the code starts here
- SEP #$30 ; x y a set to 8 bit length
- LDA #$FF ; ff into audio0w (write)
- STA $2140
- REP #$10 ; x,y: 16 bit length
- LDX #$7FFF
- l0DB5B LDA $018000,X ; move rom music data to ram at $7f0000
- STA $7F0000,X
- LDA $028000,X ; move rom music data to ram at $7f0000
- STA $7F8000,X
- DEX
- BPL l0DB5B
- LDA #$80 ; screen on , probably not important at all
- STA $2100
- LDA #$00 ; 00fd/00fe/00ff point to the data that is
- now
- STA $00FD ; in ram at $7f0000
- LDA #$00
- STA $00FE
- LDA #$7F
- STA $00FF
- STZ $4200 ; disable nmi and timer h/v count
- SEI ; disable irq
- JSR l0DBCD ; unknown sub routine, labeled "RESTART"
- by PAN/ATX
- SEP #$30 ; all regs 8 bit
- l0DB8B LDA $2140 ; wait for reply from sound chip ?
- BNE l0DB8B
- LDA #$E0 ; audio3w ?
- STA $2143
- LDA #$FF ; send data to sound chip ?
- STA $2142 ; $ffe0 this could be an address within the
- ; sound chip ROM between $ffc0 and $ffff
- in the
- ; ROM mask.......
- LDA #$01 ; send data to sound chip ?
- STA $2141
- LDA #$01 ; send data to sound chip ?
- STA $2140
- l0DBA4 LDA $2140 ; wait for reply from sound chip ?
- CMP #$01 ; what a fuck of a protocol .... :(
- BNE l0DBA4
- l0DBAB LDA $2140 ; wait again for reply from soundchip ?
- CMP #$55
- BNE l0DBAB
- LDA $0207 ; aha ... move $0207 to sound chip ?
- STA $2141 ; probably sound number selector
- LDA #$07
- STA $2140 ; send data to sound chip
- l0DBBD LDA $2140 ; wait until sound chip accepted data?
- CMP #$07
- BNE l0DBBD
- l0DBC4 LDA $2140 ; wait for reply ?
- CMP #$55
- BNE l0DBC4
- CLI
- RTS
- l0DBCD PHP ; labeled "RESTART" by pan/ATX
- JSR l0DBD8 ;
- PLP
- LDA #$00 ; 00 into audio0w
- STA $2140
- RTS
- l0DBD8 PHP
- REP #$30 ; a,x,y 16 bit regs
- LDY #$0000 ; needed first time at lda [$fd],y :
- pointer to ram
- LDA #$BBAA
- l0DBE1 CMP $2140 ; wait for sound chip $2140/2141 ?
- BNE l0DBE1
- SEP #$20 ; akku 8 bit
- LDA #$CC
- BRA l0DC12 ; oh well, another mystery :-)
- ; jump here if overflow is set e.g. if more than $0100 data to move
- l0DBEC LDA [$FD],Y ; get data from ram pointer
- INY ; the accumulator is about to get "xx00"
- where
- XBA ; /"xx" is the byte from [fd],y (first
- data byte)
- LDA #$00 ; /and resides into bit 15-7 of accu,
- and 00 is
- BRA l0DBFF ; /#$00 (8bit number of bytes already
- sent)
- l0DBF4 XBA ; accu is now "nn??" ?? is old data from
- last loop
- LDA [$FD],Y ; accu is now "nnxx" with xx the newest
- data byte
- INY ; /for
- the SPC!
- XBA ; accu is now "xxnn"
- l0DBF9 CMP $2140 ; wait for sound chip to reply with "nn" !!
- BNE l0DBF9
- INC A ; increment number of bytes that were
- sent...
- ; accu is now "xxnn" with newest val for
- nn:=nn+1
- l0DBFF REP #$20 ; akku 16 bit
- STA $2140 ; poke "xxnn" to soundchip. xx is actual
- data,
- SEP #$20 ; akku 8 bit ! nn is the 8-bit cutted
- number of bytes
- DEX ! which were already sent!!
- BNE l0DBF4 ; as many times as xreg says...
- l0DC09 CMP $2140 ; byte "nn" will be replied from the SPC if
- data
- BNE l0DC09 ; received correctly!
- l0DC0E ADC #$03 ; compare accu with #$fb ADC WILL ADD #$04
- COZ
- ; CARRY IS ALWAYS SET AFTER THE CMP!!!
- ATTENTION!
- BEQ l0DC0E ; if accu was $fb then accu := $03 . (what
- for?)
- l0DC12 PHA ; push value accu+$04 to stack (or
- beginning: #$cc)
- REP #$20 ; accu = 16 bit
- LDA [$FD],Y ; get ram data 2 bytes
- INY ; point to next word
- INY
- TAX ; x:=a : number of bytes to transmit
- LDA [$FD],Y ; get ram data
- INY
- INY
- STA $2142 ; audio2w : possibly the dest. area in the
- spc700
- SEP #$20 ; accu 8 bit
- CPX #$0100 ; set carry if first ram data was >= 0100
- lda #$00 ;
- ROL ;
- STA $2141 ; if ram data >= 0100, poke "1" into reg 1
- otherw 0
- ADC #$7F ; SET OVERFLOW FLAG IF X>=$0100 !!!! (nice
- trick!)
- PLA
- STA $2140 ; $cc in the first case , nn+4 on all later
- cases
- l0DC32 CMP $2140 ; wait for snd chip reply
- BNE l0DC32
- BVS l0DBEC ; if there were more than $0100 data for the
- spc's RAM
- ; move them where they R supposed to belong
- to!
- PLP
- RTS
- PLA
- STA $2140 ; same shit, never been jumped into
- l0DC3F CMP $2140
- BNE l0DC3F
- BVS l0DBF9
- PLP
- RTS
- ; also lets look at 7f0000: the first few bytes at 7f0000 are:
- 7f0000: b7 0e 00 04 20 cd cf bd e8 00 5d af c8 f0 d0 fb 5d d5 00 01
- d5 00 02
- b7 0e should be number of bytes to transmit, 0400 the destination
- inside the
- spc....
- at this point I really need an SPC dis/assembler..... :(((
- Okay well my first source was incompetent, sure thing. But I think I
- could
- solve a lot of questions meanwhile.
|