C/++ for the SNES

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.
User avatar
Bregalad
Posts: 8056
Joined: Fri Nov 12, 2004 2:49 pm
Location: Divonne-les-bains, France

Re: C/++ for the SNES

Post by Bregalad »

The problem with pure ASM is that no matter how much you comment it, if you leave and come back a few months later, the code will be almost unreadable. Much like commentless Perl, and very much not like well-written C++.
I must say I disagree with this one. Assembly has a lot of problems (if it didn't, high level language would never have been invented !), however, I don't think it is unreadable at all. Of course if you do comments like that :

Code: Select all

ldx #$02  ; Load $02 into x
Then yes it's going to be totally unreadable. However if you have high level comments and never comment anything low level because it's obvious by looking at the code, then it'll be perfectly understandable.

As for people who says assembly is not a problem, again I disagree. It has the following inconvenients :
  • Slow developpement time, you have to write a lot of instructions to do something conceptually simple
  • You have to loose a lot of time deciding which register will hold what, and think again if somehow the program is better by changing this choice at a later time
  • 65xxx requires a lot of Temp variables (usually you'll need at least ~6 of them), and those are a nightmare. It's so easy to have them overwritten in a subroutine call, and this is extremely tedious to debug. I try to always comment which Temp variables I use, but this is not always up to date
  • Very error prone, I have almost never developped a function that worked as expected on the 1st try. This is true with computer science in general, but in assembly it's much more than with high level language
  • Although the CISC design of 6502 makes it relatively easy to write proper code, when you suddently run out of registers because you'd like to have that Z 3rd index registers, things will go really harsh as this means a lot of saving/loading to zeropage temp variables, loosing a lot of developpement time for something that is not conceptually complex
  • Not portable : If you want to port your project to another platform, you are forced into an entiere rewrite. Also it's much easier to convert from one HLL to another HLL than from one assembly to another assembly. Even changing the assembler itself for the same platform (e.g. switching from WLA-DX to CA65) can be almost impossible without a complete rewrite !
This doesnt' mean that assembly is not a good solution, it's just that it has it's inconvenients. As for C.
Bigger problems come from C always casting to int when a type is shorter, lack of rotate operations (compilers have to guess when those are usable), etc.
I agree. This casting to int is the #1 problem : In 6502's case that'd force the int size to 8, but it has to be mimumum 16. Therefore for the code :

Code: Select all

char a = whathever();
char b = whathever2();
char c = a+b;
This means the C standard imposes 16 bit add for the 3rd line, even if if could be done in 8-bit. OK in this particular cases it can easily be optimized, but in some cases this won't be the case.

Another problem is the assumption that all variables are on the stack. In theory it's easy to draw a function graph and to eliminate this problem, but the interupts and the existance of function pointers makes this completely impossible. I think SDCC somehow managed to fix this problem, unfortunately this compiler is much too complex for me to understand anything about it's inner working.

The combination of those 2 problems (16-bit default and variable stacking) makes it impossible for ANSI C to be ever efficient for the 65xxx. (pehaps for the 65816 it gets a little better, though). It could be possible for non-ANSI C to work, but then we'd as well invent another language.

Now there is some other HLL solutions worth mentionning :
  • ATALAN programming language - designed specifically for being efficient for 65xx. I have no idea how good it is, but looking like an interesting HLL solution
  • Virtual machines.
    Since with compiled HLLs your execution time is going to be bloated anyways, VMs does that but at least they keep the code size small. So if your execution time will be bloated, at least your ROM size won't, unlike what CC65 produces. You could even have a smaller ROM than what pure assembly would have lead to ! On 6502.org I've heard about
    • Forth (they seem to be very fond of it)
    • PLASMA
    • AcheronVM
I haven't tried any of these so I can't comment how good/bad they are. They look interesting, and should be relatively easy to port for the 65816.
psycopathicteen
Posts: 3140
Joined: Wed May 19, 2010 6:12 pm

Re: C/++ for the SNES

Post by psycopathicteen »

Can't you just use absolute addressing when you run out of dp registers?
snarfblam
Posts: 143
Joined: Fri May 13, 2011 7:36 pm

Re: C/++ for the SNES

Post by snarfblam »

Kannagi wrote: The assembler is difficult to understand, because when I read the source code, I see that the variables are little to name it, unwanted little keyword, and the code is compressed and has not Indent style.
Having much more experience with higher level languages, when I program in ASM I tend to use descriptive variable and label names and indent code to show code flow. Helps me cope.

Code: Select all

; Copy 6 blocks of $100 bytes
        ldx #$06
        *
            ; Copy $100 bytes
            ldy #$00
            *
                lda ($00),Y
                sta ($02),Y
                iny
            bne -
            
            ; Advance each pointer by $100
            inc $01
            inc $03
            
            dex
        bne --
psycopathicteen
Posts: 3140
Joined: Wed May 19, 2010 6:12 pm

Re: C/++ for the SNES

Post by psycopathicteen »

You really do have to micro-optimize the living hell out of SNES ASM.
???

You mean to tell me, you don't get the job done by relocating memory, or replacing subroutines with macros?
Sik
Posts: 1589
Joined: Thu Aug 12, 2010 3:43 am

Re: C/++ for the SNES

Post by Sik »

byuu wrote:Fun exercise for the reader: try and do muls.l #23,d0 (signed multiply) on the 65816.
Try that on the 68000 as well, it lacks that instruction (only the original 16×16→32 multiplication is present in the 68000).

But yeah, it's not like the 68000 doesn't have its fair share of weird things, just look at this (real code):

Code: Select all

    swap    d7                          ; Generate VDP command used to set
    clr.w   d7                            ; the current address
    swap    d7
    add.l   d7, d7
    add.l   d7, d7
    lsr.w   #2, d7
    or.w    #$4000, d7
    swap    d7
Or for a less common but even more unreadable case:

Code: Select all

    moveq   #0, d5                      ; Get left half of row
    move.w  (a6)+, d5
    lsl.l   #4, d5                      ; Expand it from four pixels to eight
    lsr.w   #4, d5                        ; pixels (scale horizontally)
    lsl.l   #8, d5
    lsr.w   #4, d5
    lsr.b   #4, d5
    move.l  d5, d4
    lsl.l   #4, d4
    or.l    d4, d5
    move.l  d5, (a5)+                   ; Store expanded pixels (twice so it
    move.l  d5, (a5)+                     ; scales vertically too)
The trick is to document every single operation (to a higher granurality than you'd do in C, mind you), and document what they're supposed to do. This will help you figure out what the code is doing even if the code is a total mess, since the comment is telling you what it's meant to be.
Near
Founder of higan project
Posts: 1553
Joined: Mon Mar 27, 2006 5:23 pm

Re: C/++ for the SNES

Post by Near »

> sta $211B; xba; sta $211B; lda #$17; sta $211C; lda $2134

Shoot, forgot the PPU MUL was signed. At any rate, you get the idea. The 680x0 has a lot more flexibility and registers available.

> The trick is to document every single operation (to a higher granurality than you'd do in C, mind you), and document what they're supposed to do

I do this, it doesn't help me.

But, okay. A lot of you disagree with me and that's fine. Whatever it is with my brain, coming back to ASM code I haven't touched in months is a disaster. I certainly did not want to abandon a year's worth of work on DKJM, and six month's worth on BL. If you guys can resume ASM work after a long period, then I am very impressed and jealous.
psycopathicteen
Posts: 3140
Joined: Wed May 19, 2010 6:12 pm

Re: C/++ for the SNES

Post by psycopathicteen »

Something I've been wondering about lately is how do you handle object memory slots on the 68000? With the 65816, all you have to do is move the direct page, and access the memory directly. With the 68000 you'd have to do all kinds of crazy register juggling.
Sik
Posts: 1589
Joined: Thu Aug 12, 2010 3:43 am

Re: C/++ for the SNES

Post by Sik »

You just do relative addressing (e.g. 4(a0) will be the address at register a0 + 4, you can even do stuff like 2(a0,d0.w) which would be the address at a0 + d0 + 2). Also huh, wouldn't the direct page method be wasteful on the SNES? I really doubt you need 256 bytes to store data for an object (and not aligning to a page would negate all benefits), unless I'm misunderstanding something. I know that I can get away with just 16 bytes per object generally, a few more if the game is rather complex but point stands.
psycopathicteen
Posts: 3140
Joined: Wed May 19, 2010 6:12 pm

Re: C/++ for the SNES

Post by psycopathicteen »

I know that I can get away with just 16 bytes per object generally.
Really? I'm curious in how you're able to get by with just 16 bytes.
User avatar
Bregalad
Posts: 8056
Joined: Fri Nov 12, 2004 2:49 pm
Location: Divonne-les-bains, France

Re: C/++ for the SNES

Post by Bregalad »

Fun exercise for the reader: try and do muls.l #23,d0 (signed multiply) on the 65816.
As long as there is enough bits for the result, signed and unsigned multiply are equivalent.
Sik
Posts: 1589
Joined: Thu Aug 12, 2010 3:43 am

Re: C/++ for the SNES

Post by Sik »

psycopathicteen wrote:
I know that I can get away with just 16 bytes per object generally.
Really? I'm curious in how you're able to get by with just 16 bytes.

Code: Select all

ObjType:        rs.b 1      ; 1 byte
ObjFlags:       rs.b 1      ; 1 byte
ObjX:           rs.w 1      ; 2 bytes
ObjY:           rs.w 1      ; 2 bytes
ObjSpeedX:      rs.w 1      ; 2 bytes
ObjSpeedY:      rs.w 1      ; 2 bytes
That's 10 bytes. Add some stuff that depends on the game and you'll get to a number that's around 16. The way I handle the objects means that I don't need to stick to powers of two anyway (only word alignment), so even if I end up with e.g 18 bytes it's not going to result in me having to waste space in padding.

Of course maybe you meant something else, but honestly the description is so vague I have to guess =P
Bregalad wrote:
Fun exercise for the reader: try and do muls.l #23,d0 (signed multiply) on the 65816.
As long as there is enough bits for the result, signed and unsigned multiply are equivalent.
Why does the 68000 have separate MULU and MULS then? (both always have enough bits for the result since the operation is 16-bit × 16-bit → 32-bit)
Kannagi
Posts: 100
Joined: Sun May 11, 2014 8:36 am
Location: France

Re: C/++ for the SNES

Post by Kannagi »

Sik wrote:

Code: Select all

ObjType:        rs.b 1      ; 1 byte
ObjFlags:       rs.b 1      ; 1 byte
ObjX:           rs.w 1      ; 2 bytes
ObjY:           rs.w 1      ; 2 bytes
ObjSpeedX:      rs.w 1      ; 2 bytes
ObjSpeedY:      rs.w 1      ; 2 bytes
Why put the speed on 2 bytes ? a speed of 256 or more would be huge (especially 60 Hz).
Sik
Posts: 1589
Joined: Thu Aug 12, 2010 3:43 am

Re: C/++ for the SNES

Post by Sik »

It's 8.8 fixed point actually (so 0x100 actually means 1 pixel per frame, not 256). Yes, I know the coordinates don't have a fractional part stored into them, but I have a way to fake subpixel accuracy while otherwise keeping everything pure integer.
User avatar
Bregalad
Posts: 8056
Joined: Fri Nov 12, 2004 2:49 pm
Location: Divonne-les-bains, France

Re: C/++ for the SNES

Post by Bregalad »

Why does the 68000 have separate MULU and MULS then? (both always have enough bits for the result since the operation is 16-bit × 16-bit → 32-bit)
The only difference is sign extension because the result has more bits than the products.
If it was 16x16 -> 16, or 32x32->32, both would be strictly the same.
tepples
Posts: 22708
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: C/++ for the SNES

Post by tepples »

Sik wrote:I have a way to fake subpixel accuracy while otherwise keeping everything pure integer.
Interesting. How do you go about handling less precision in displacement than in velocity? Do you add 1 if the subpixel portion of the speed is less than some pseudorandom value?
Post Reply