spca65

Discussion of hardware and software development for Super NES and Super Famicom. See the SNESdev wiki for more information.

Moderator: Moderators

Forum rules
  • For making cartridges of your Super NES games, see Reproduction.
tepples
Posts: 22708
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

spca65

Post by tepples »

I'm trying to learn SNESdev. I've loaded palettes, tile data, and nametable data into VRAM, and scrolled the screen based on the controller. The next two things to attack are sprites and sound. Eventually I want to share the music sequence interpretation code between my NES and Super NES music engines, which probably means I should write the music engine in 6502 code and use SPC700 assembly with 6502-style mnemonics. Ideally, the only code that would differ would be the instrument handling stuff. I reimplemented the 6502 instruction set with macros as a proof of concept for eventually implementing the SPC700 instruction set with macros, but I ran into a snag: ca65 offers no way to test whether a label is zero page (.importzp) or not (.import). The new .addrsize(label) function, prototyped by Movax12 in a fork of the ca65 repo, is one way to solve this.

In #nesdev, another method was suggested: add support for SPC700-in-6502-drag directly to the instruction encoder of ca65. This way we'd end up with a starting point to get away from WLA-DX and the like. But I imagine that even though this approach might work for SPC700, it might not work so well for far-less-6502-like coprocessors such as SM83 (Sharp's 8080-like CPU core in the LR35902 SoC of Game Boy and Super Game Boy) and Super FX. That's where macro packages come in.
In [url=http://forums.nesdev.com/viewtopic.php?p=121496#p121496]this post[/url], slobu wrote:Most experience newbies (ha!) start by hacking the provided examples and work there way towards original code.
Whether SPC700 is implemented as a macro package or as a modification to ca65 itself, testing it would need these deliverables:
  • A test case that exercises all instructions, with the source code and the expected binary. In the other thread I made such a test case for 6502 that has all instructions in opcode order, with the operand being a nibble-swapped version of the opcode, and I verified it by inspecting ca65's listing. For example, on 6502/65816, JMP $C4C4 would produce 4C C4 C4 in the listing's object code column. On SPC700, that line would be LSR $C4C4 instead.
  • A program that runs on a SPC700 that just plays a chord. This needs a bit of SPC700+DSP programming experience.
  • A program for the Super NES that loads this program and runs it.
User avatar
blargg
Posts: 3715
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Re: spca65

Post by blargg »

I'm experimenting with SPC-700 on ca65 and have some thoughts on the syntax:
* By default, support only the standard syntax: !abs for absolute, dp for direct-page, [!abs+X], etc.
* Allow optional support for 65xx-style addressing in addition to ! for absolute: addr for dp/absolute, depending on type, <addr for dp, abs,x for indexed, etc.
* Allow optional support for 65xx-style mnemonic aliases: jsr = call, rts = ret, etc.

This allows current SPC-700 code to assemble with minimal changes, and for an incremental transition to more familiar addressing and mnemonics.
User avatar
Movax12
Posts: 541
Joined: Sun Jan 02, 2011 11:50 am

Re: spca65

Post by Movax12 »

I don't know much about SNES or SPC-700, so I don't know if this is exactly relevant, but where do the current ca65 standards for address sizes fit? There is, of course, the lowbyte operators < or .lobyte(), but ca65 also supports forced addressing modes with z:, a: and f: preceding the operand. The former would work fine with the new .addrsize() function, but the latter would need to be scanned for in the macro code.
User avatar
blargg
Posts: 3715
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Re: spca65

Post by blargg »

SPC-700 doesn't do anything implicitly:

Code: Select all

MOV A, #imm
MOV A, (X)
MOV A, (X)+
MOV A, dp
MOV A, dp+X
MOV A, !abs
MOV A, !abs+X
MOV A, !abs+Y
MOV A, [dp+X]
MOV A, [dp]+Y
The syntax provides all the context necessary to determine the addressing mode. So no need that I can see for something like .addrsize.

Given how close it is to 6502, it's slightly tedious to code on, where one has to put ! often, and remember call instead of jsr, ret instead of rts, etc. hence the motivation for supporting 65xx variations on these things.
nocash
Posts: 1405
Joined: Fri Feb 24, 2012 12:09 pm
Contact:

Re: spca65

Post by nocash »

Maybe a bit off-topic concerning the syntax. But for a simple sound engine you can get around using SPC700 code at all. Instead you can tweak the SPC's built-in boot ROM to write samples to RAM, and to write values to the DSP chip's I/O ports (normally the boot ROM is intended for uploading custom program code, but you can also upload plain data and I/O port settings).
The SNES version at http://nocash.emubase.de/magicflr.htm is using that trick. Of course, those per-I/O-port uploads are a bit inefficient, and do take more cpu load on both processors as when running a real sound engine directly on the SPC700.
tepples
Posts: 22708
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: spca65

Post by tepples »

nocash wrote:But for a simple sound engine you can get around using SPC700 code at all.
Yeah, I saw blargg's IPL-only sound playback demo, ran it in bsnes, modified it to play a chord, and ran it on my PowerPak. I'm just looking for something to build on, toward a few things that could prove useful to developers of games that demand more CPU time than a turn-based puzzle game:
  • Efficiency, so as not to require a lot of attention from the S-CPU. There's a topic going on about Super NES games running slower than NES versions of the same game.
  • Ability to play the soundtrack in players supporting SPC700 save states (which coincidentally have the same file name suffix as Authenticode certificates) without having to lug around an entire SNES emulator.
  • Ability to write music engines in C or uc65. It's not like CPU time is at a premium given the amount of RAM it has and that it has 55% of the clock speed of an entire NES CPU.
  • Ability to write real-time synthesis engines in SPC700 assembly that output BRR.
doppelganger
Posts: 183
Joined: Tue Apr 05, 2005 7:30 pm

Re: spca65

Post by doppelganger »

For me the problem isn't writing code for a different processor, or even a different syntax...it's finding samples to play on the APU.
Be whatever the situation demands.
User avatar
blargg
Posts: 3715
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Re: spca65

Post by blargg »

I've taken up spca65 and am most of the way through. I'm still not sure what syntax to use for the bit operations, as they vary so much between references. There are several instructions that reference bit numbers.
ARM9
Posts: 57
Joined: Sun Aug 11, 2013 6:07 am

Re: spca65

Post by ARM9 »

tepples wrote:This way we'd end up with a starting point to get away from WLA-DX and the like. But I imagine that even though this approach might work for SPC700, it might not work so well for far-less-6502-like coprocessors such as GBZ80 and Super FX. That's where macro packages come in.
What's wrong with WLA? I haven't used the spc700 assembler yet but the 65816 one is pretty solid. I think the nwarp spc700 engine compiles with wla.
tepples wrote:
  • Ability to write music engines in C or uc65. It's not like CPU time is at a premium given the amount of RAM it has and that it has 55% of the clock speed of an entire NES CPU.
Do you plan on retargeting cc65 to the spc700?
doppelganger wrote:For me the problem isn't writing code for a different processor, or even a different syntax...it's finding samples to play on the APU.
I was dumping a bunch of samples from various snes games last week if you're interested.
Near
Founder of higan project
Posts: 1553
Joined: Mon Mar 27, 2006 5:23 pm

Re: spca65

Post by Near »

I don't expect anyone to actually use it, but please take a look at bass v13:

Code: Select all

http://byuu.org/files/bass_v13.tar.xz
If you go to arch/table/snes-smp.arch, it's my take on a 6502-syntax for the SPC700.

In fact, it's small enough to paste the table here. I'll post it below.

I'd be game to working with you guys to reach a consensus on the syntax (meaning changing mine.) I'd really like to avoid having multiple versions of this out there, as that would discourage its adoption.

But it really is a magnificent fix. It's pretty clear that the SPC700 is about 85% directly compatible with the 6502.

The main issues with this are: two-argument instructions (dp,imm), bit-syntax (addr.bit), new instructions (we'd usually want ora, but there's an or carry), more new instructions (mov1, etc.); the comma is particularly troublesome because 6502 uses it in place of + for addressing.

Code: Select all

nop              ;$00
brk              ;$0f
rts              ;$6f
rti              ;$7f
xcn              ;$9f
wai              ;$ef
stp              ;$ff

clp              ;$20
clc              ;$60
cli              ;$c0
clv              ;$e0
sep              ;$40
sec              ;$80
sei              ;$a0
cmc              ;$ed

php              ;$0d
pha              ;$2d
phx              ;$4d
phy              ;$6d
plp              ;$8e
pla              ;$ae
plx              ;$ce
ply              ;$ee

inx              ;$3d
iny              ;$fc
dex              ;$1d
dey              ;$dc

tax              ;$5d
tay              ;$fd
tsx              ;$9d
txa              ;$7d
txs              ;$bd
tya              ;$dd

mul              ;$cf
div              ;$9e

das              ;$be
daa              ;$df

jst *04          ;=a %0001
set *08:*03      ;=b %00010 =a
clr *08:*03      ;=b %10010 =a
bbs *08:*03=*08  ;=b %00011 =a +3c
bbc *08:*03=*08  ;=b %10011 =a +3c

jmp (*16,x)      ;$1f =a
jmp *16          ;$5f =a
jsr *16          ;$3f =a
jsp *08          ;$4f =a

bne --y=*08      ;$fe +2a
bne --*08=*08    ;$6e =a +3b
bne *08,x=*08    ;$de =a +3b
bne *08=*08      ;$2e =a +3b

bra *08          ;$2f +2a
bpl *08          ;$10 +2a
bmi *08          ;$30 +2a
bvc *08          ;$50 +2a
bvs *08          ;$70 +2a
bcc *08          ;$90 +2a
bcs *08          ;$b0 +2a
bne *08          ;$d0 +2a
beq *08          ;$f0 +2a

tsb *16          ;$0e =a
trb *16          ;$4e =a

inw *08          ;$3a =a
dew *08          ;$1a =a
adw *08          ;$7a =a
sbw *08          ;$9a =a
cpw *08          ;$5a =a
ldw *08          ;$ba =a
stw *08          ;$da =a

orc !*13:*03     ;$2a ~b ~a
orc *13:*03      ;$0a ~b ~a
and !*13:*03     ;$6a ~b ~a
and *13:*03      ;$4a ~b ~a
eor *13:*03      ;$8a ~b ~a
ldc *13:*03      ;$aa ~b ~a
stc *13:*03      ;$ca ~b ~a
not *13:*03      ;$ea ~b ~a

ora (x)          ;$06
orr (x)=(y)      ;$19
ora (*08,x)      ;$07 =a
ora (*08),y      ;$17 =a
ora #*08         ;$08 =a
orr *08=#*08     ;$18 =b =a
ora *16,x        ;$15 =a
ora *16,y        ;$16 =a
ora *08,x        ;$14 =a
orr *08=*08      ;$09 =b =a
ora *16          ;$05 =a
ora *08          ;$04 =a
//
ora.w *16,x      ;$15 ~a
ora.b *08,x      ;$14 ~a
ora.w *16        ;$05 ~a
ora.b *08        ;$04 ~a

and (x)          ;$26
and (x)=(y)      ;$39
and (*08,x)      ;$27 =a
and (*08),y      ;$37 =a
and #*08         ;$28 =a
and *08=#*08     ;$38 =b =a
and *16,x        ;$35 =a
and *16,y        ;$36 =a
and *08,x        ;$34 =a
and *08=*08      ;$29 =b =a
and *16          ;$25 =a
and *08          ;$24 =a
//
and.w *16,x      ;$35 ~a
and.b *08,x      ;$34 ~a
and.w *16        ;$25 ~a
and.b *08        ;$24 ~a

eor (x)          ;$46
eor (x)=(y)      ;$59
eor (*08,x)      ;$47 =a
eor (*08),y      ;$57 =a
eor #*08         ;$48 =a
eor *08=#*08     ;$58 =b =a
eor *16,x        ;$55 =a
eor *16,y        ;$56 =a
eor *08,x        ;$54 =a
eor *08=*08      ;$49 =b =a
eor *16          ;$45 =a
eor *08          ;$44 =a
//
eor.w *16,x      ;$55 ~a
eor.b *08,x      ;$54 ~a
eor.w *16        ;$45 ~a
eor.b *08        ;$44 ~a

cmp (x)          ;$66
cmp (x)=(y)      ;$79
cmp (*08,x)      ;$67 =a
cmp (*08),y      ;$77 =a
cmp #*08         ;$68 =a
cmp *08=#*08     ;$78 =b =a
cmp *16,x        ;$75 =a
cmp *16,y        ;$76 =a
cmp *08,x        ;$74 =a
cmp *08=*08      ;$69 =b =a
cmp *16          ;$65 =a
cmp *08          ;$64 =a
//
cmp.w *16,x      ;$75 ~a
cmp.b *08,x      ;$74 ~a
cmp.w *16        ;$65 ~a
cmp.b *08        ;$64 ~a

adc (x)          ;$86
adc (x)=(y)      ;$99
adc (*08,x)      ;$87 =a
adc (*08),y      ;$97 =a
adc #*08         ;$88 =a
adc *08=#*08     ;$98 =b =a
adc *16,x        ;$95 =a
adc *16,y        ;$96 =a
adc *08,x        ;$94 =a
adc *08=*08      ;$89 =b =a
adc *16          ;$85 =a
adc *08          ;$84 =a
//
adc.w *16,x      ;$95 ~a
adc.b *08,x      ;$94 ~a
adc.w *16        ;$85 ~a
adc.b *08        ;$84 ~a

sbc (x)          ;$a6
sbc (x)=(y)      ;$b9
sbc (*08,x)      ;$a7 =a
sbc (*08),y      ;$b7 =a
sbc #*08         ;$a8 =a
sbc *08=#*08     ;$b8 =b =a
sbc *16,x        ;$b5 =a
sbc *16,y        ;$b6 =a
sbc *08,x        ;$b4 =a
sbc *08=*08      ;$a9 =b =a
sbc *16          ;$a5 =a
sbc *08          ;$a4 =a
//
sbc.w *16,x      ;$b5 ~a
sbc.b *08,x      ;$b4 ~a
sbc.w *16        ;$a5 ~a
sbc.b *08        ;$a4 ~a

sta (x)          ;$c6
sta (x++)        ;$af
sta (*08,x)      ;$c7 =a
sta (*08),y      ;$d7 =a
str *08=#*08     ;$8f =b =a
sta *16,x        ;$d5 =a
sta *16,y        ;$d6 =a
sta *08,x        ;$d4 =a
str *08=*08      ;$fa =b =a
sta *16          ;$c5 =a
sta *08          ;$c4 =a
//
sta.w *16,x      ;$d5 ~a
sta.b *08,x      ;$d4 ~a
sta.w *16        ;$c5 ~a
sta.b *08        ;$c4 ~a

lda (x)          ;$e6
lda (x++)        ;$bf
lda (*08,x)      ;$e7 =a
lda (*08),y      ;$f7 =a
lda #*08         ;$e8 =a
lda *16,x        ;$f5 =a
lda *16,y        ;$f6 =a
lda *08,x        ;$f4 =a
lda *16          ;$e5 =a
lda *08          ;$e4 =a
//
lda.w *16,x      ;$f5 ~a
lda.b *08,x      ;$f4 ~a
lda.w *16        ;$e5 ~a
lda.b *08        ;$e4 ~a

stx *08,y        ;$d9 =a
stx *16          ;$c9 =a
stx *08          ;$d8 =a
//
stx.w *16        ;$c9 ~a
stx.b *08        ;$d8 ~a

sty *08,x        ;$db =a
sty *16          ;$cc =a
sty *08          ;$cb =a
//
sty.w *16        ;$cc ~a
sty.b *08        ;$cb ~a

ldx #*08         ;$cd =a
ldx *08,y        ;$f9 =a
ldx *16          ;$e9 =a
ldx *08          ;$f8 =a
//
ldx.w *16        ;$e9 ~a
ldx.b *08        ;$f8 ~a

ldy #*08         ;$8d =a
ldy *08,x        ;$fb =a
ldy *16          ;$ec =a
ldy *08          ;$eb =a
//
ldy.w *16        ;$ec ~a
ldy.b *08        ;$eb ~a

cpx #*08         ;$c8 =a
cpx *16          ;$1e =a
cpx *08          ;$3e =a
//
cpx.w *16        ;$1e ~a
cpx.b *08        ;$3e ~a

cpy #*08         ;$ad =a
cpy *16          ;$5e =a
cpy *08          ;$7e =a
//
cpy.w *16        ;$5e ~a
cpy.b *08        ;$7e ~a

asl              ;$1c
asl *08,x        ;$1b =a
asl *16          ;$0c =a
asl *08          ;$0b =a
//
asl.w *16        ;$0c ~a
asl.b *08        ;$0b ~a

lsr              ;$5c
lsr *08,x        ;$5b =a
lsr *16          ;$4c =a
lsr *08          ;$4b =a
//
lsr.w *16        ;$4c ~a
lsr.b *08        ;$4b ~a

rol              ;$3c
rol *08,x        ;$3b =a
rol *16          ;$2c =a
rol *08          ;$2b =a
//
rol.w *16        ;$2c ~a
rol.b *08        ;$2b ~a

ror              ;$7c
ror *08,x        ;$7b =a
ror *16          ;$6c =a
ror *08          ;$6b =a
//
ror.w *16        ;$6c ~a
ror.b *08        ;$6b ~a

inc              ;$bc
inc *08,x        ;$bb =a
inc *16          ;$ac =a
inc *08          ;$ab =a
//
inc.w *16        ;$ac ~a
inc.b *08        ;$ab ~a

dec              ;$9c
dec *08,x        ;$9b =a
dec *16          ;$8c =a
dec *08          ;$8b =a
//
dec.w *16        ;$8c ~a
dec.b *08        ;$8b ~a
User avatar
Bregalad
Posts: 8056
Joined: Fri Nov 12, 2004 2:49 pm
Location: Divonne-les-bains, France

Re: spca65

Post by Bregalad »

Just my $2, but I think the different syntax between SPC and 65xxx processors is a non-issue.
Bad programmers worries about the code, good programmers worries about data structures and algorithms.

In other words, the syntax doesn't matter. You'll probably save time rewriting the engine for SPC from scratch because anyways it doesn't have exactly the same instruction set as any 65xxx processor, even if they share a similar architecture. The ability to move direct page between zero-page and one-page is very powerful too.
In my opinion it's worth optimizing the code so that it's faster and smaller on SPC, because smaller code means more code to store music, SFX, samples and echo buffer, and faster code means you can run the sound engine faster, updating vibratos and various effects more accurately in time.

I think it's a great idea to use the same data structure to store music on NES and on the SNES (and other platforms as well). However, there is still major differences in hardware which will eventually be unavoidable to reflect in the software as well.

For instance, arpeggio makes few sense on SNES, because you have enough channels to handles real chords. There is panning on SNES, and HW volume envelope handling. Drums are much simpler (just play a drum sample, no need to do tricks), etc, etc...
tepples
Posts: 22708
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: spca65

Post by tepples »

doppelganger wrote:For me the problem isn't writing code for a different processor, or even a different syntax...it's finding samples to play on the APU.
You can extract them from MODs and the like. If you want, I can post some of my own tracked music with samples short enough to fit in SPC memory.
ARM9 wrote:
tepples wrote:This way we'd end up with a starting point to get away from WLA-DX and the like.
What's wrong with WLA? I haven't used the spc700 assembler yet but the 65816 one is pretty solid. I think the nwarp spc700 engine compiles with wla.
For one thing, needing to install two assemblers. It's already painful enough to get collaborators to install Python for image conversion tools. Or should I switch the 65816 parts of my Super NES projects and the 6502 parts of the NES projects that share the same source code to WLA? For another, WLA uses the weird syntax, and my goal in this was to use 6502 syntax so that I can share code between NES and Super NES music engines.
Do you plan on retargeting cc65 to the spc700?
SPC700 in 6502 syntax would do this implicitly.
Bregalad wrote:I think the different syntax between SPC and 65xxx processors is a non-issue.
The issue is the principle of not repeating yourself. (See articles at WikiWikiWeb, Wikipedia, and O'Reilly's 97 Things Every Programmer Should Know.) I want to write the music sequence interpretation code once and run it on both 6502 and SPC700 for the NES and Super NES versions of a single game. The only things that should differ between the two are 1. number of channels, 2. how instruments are played, and 3. how the music engine gets called.
smaller code means more code to store music, SFX, samples and echo buffer
My NES music engine, including the sound effect and instrument code that would not be shared with the Super NES version, currently takes up less than 1400 bytes.
arpeggio makes few sense on SNES, because you have enough channels to handles real chords.
True, and the (rewritten from scratch) instrument handler might treat arpeggio differently.
User avatar
Hamtaro126
Posts: 818
Joined: Thu Jan 19, 2006 5:08 pm

Re: spca65

Post by Hamtaro126 »

ARM9 wrote:What's wrong with WLA? I haven't used the spc700 assembler yet but the 65816 one is pretty solid. I think the nwarp spc700 engine compiles with wla.
WLA has many problems with it, I do not like the syntax handling, a couple of examples: Linker Errata, and Index Instructions that add numbers with + do not work in any form

Code: Select all

;example of label addition with instructions:
LDX #$00
LDA ThisData+$80,X
STA ThatData
AKA SmilyMZX/AtariHacker.
User avatar
Bregalad
Posts: 8056
Joined: Fri Nov 12, 2004 2:49 pm
Location: Divonne-les-bains, France

Re: spca65

Post by Bregalad »

Linker Errata, and Index Instructions that add numbers with + do not work in any form
Huh ? it works perfectly fine.
What's wrong with WLA?
Well, while the 65xxx ports of WLA works great, the SPC700 port is terrible.
First you have to define ROM and RAM sections despite the fact that the SPC700 only has RAM (and a small IPL ROM that you can't program anyways). (the same problem arose when I wanted to program C64 apps with WLA)

Then it will randomly generate garbage instead of the instructions you told it to generate. This can be a major pain when debugging your (correctly assembled) code is hard enough, considering the lack of step-by-step SPC700 debuggers.

However, spcas by byuu works wonders for me.
The issue is the principle of not repeating yourself. (See articles at WikiWikiWeb, Wikipedia, and O'Reilly's 97 Things Every Programmer Should Know.) I want to write the music sequence interpretation code once and run it on both 6502 and SPC700 for the NES and Super NES versions of a single game. The only things that should differ between the two are 1. number of channels, 2. how instruments are played, and 3. how the music engine gets called.
Well, as you want, but in my honest opinion while the idea of sharing parts of the sound engines is great, the idea to not rewrite a completely different version for the other system is terrible.
There is going to either be a lot more differences than this or you're going to very seriously underuse the hardware of both systems. The "dont repeat yourself" thing don't apply in this case.
However, do as you wish I'm not there to dictate you what to do !
ARM9
Posts: 57
Joined: Sun Aug 11, 2013 6:07 am

Re: spca65

Post by ARM9 »

Hamtaro126 wrote: WLA has many problems with it, I do not like the syntax handling
To each his own, I personally prefer the wla syntax over that of ca65.

Code: Select all

a couple of examples: Linker Errata, and Index Instructions that add numbers with + do not work in any form

;example of label addition with instructions:
LDX #$00
LDA ThisData+$80,X
STA ThatData
Works fine for me. :| I've pretty much used every addressing mode for the 65816 by now and I can't remember any of them not working with wla. Neither have I had much trouble with the linker. I wish I could tell you what you're doing wrong, perhaps you're using a really old version?
Bregalad wrote: Well, while the 65xxx ports of WLA works great, the SPC700 port is terrible.
First you have to define ROM and RAM sections despite the fact that the SPC700 only has RAM (and a small IPL ROM that you can't program anyways). (the same problem arose when I wanted to program C64 apps with WLA)

Then it will randomly generate garbage instead of the instructions you told it to generate. This can be a major pain when debugging your (correctly assembled) code is hard enough, considering the lack of step-by-step SPC700 debuggers.
That's unfortunate, I'm going to start programming the spc700 soon enough so I think I'll give bass v13 a shot. The only emulator I know of that has spc700 debugging is no$sns, you can switch the view in one of the menus.
Post Reply