C/++ for the SNES

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
C/++ for the SNES
by on (#136459)
I have found a SNES sdk for the C language; however, does this console have the oomph to run C-language games? Are there any coders who use C to develop for the SNES? Is it a good idea, in your evaluation?
Re: C/++ for the SNES
by on (#136461)
Koei's sim games are believed to have been written in C. There are even a few NES homebrew games written in C (Alter Ego, Zooming Secretary, MineShaft, Chase).
Re: C/++ for the SNES
by on (#136465)
There is a C compiler for the Apple IIGS (65816) called ORCA/C. WDC also has their own (never used it). The topic has come up a couple times on the cc65 mailing list (from the look of it) and it's been rejected multiple times due to the large undertaking required. Hint: it's not easy and you probably will not end up with very optimal code.

Whether or not either of these could be used for SNES development is completely unknown (by me) at this time. And regardless of the possibility, I would suggest sticking with assembly language on classic consoles.

Even though this topic is about the SNES, Shiru from the forum here did a write-up on doing C on the NES (6502). Again: painful.

Just stick to assembly. It's really not that hard.
Re: C/++ for the SNES
by on (#136472)
You can do it of course, as the 65xx is turing complete. But you cannot do it well. And on a 2.68MHz processor with an instruction throughput around 0.4MIPS ... you had better not try.

The 65xx is just an awful design for C code. However, I'm not convinced that the only alternative is to go right to pure assembler. I think it just needs a language somewhere between C and ASM that plays on its strengths. But to date, my attempts to design such a language have failed.

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++.
Re: C/++ for the SNES
by on (#136475)
byuu wrote:
You can do it of course, as the 65xx is turing complete. But you cannot do it well. And on a 2.68MHz processor with an instruction throughput around 0.4MIPS ... you had better not try.

The 65xx is just an awful design for C code. However, I'm not convinced that the only alternative is to go right to pure assembler. I think it just needs a language somewhere between C and ASM that plays on its strengths. But to date, my attempts to design such a language have failed.

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++.


Isn't C just a step up from assembly? I don't think there is a better bridge between the two. Sorry if that sounds uneducated. I'm an electrical engineer that only dabbles in ASM and C so I'm not that well educated on the matter.
Re: C/++ for the SNES
by on (#136477)
Fusion916 wrote:
Isn't C just a step up from assembly?
Yes, but no... C really relies on passing parameters and allocating local variables on the stack. The 6502's stack is really only for subroutine calls, although you can stuff other things into it. And it's only 256 bytes total, so you can't allocate many things into it anyway.

This lack of a low-overhead way of using dynamic allocations of memory, coupled with the comparatively small amount of RAM provided by the NES, means that a lot of things we take for granted with more-modern languages on more-modern hardware are awkward on the NES, and only a little better on the C64.
Re: C/++ for the SNES
by on (#136481)
byuu wrote:
You can do it of course, as the 65xx is turing complete. But you cannot do it well. And on a 2.68MHz processor with an instruction throughput around 0.4MIPS ... you had better not try.

0.4MIPS wait what? That puts it in a much worse light than the 68000 on the Mega Drive, especially when you consider how much the 68000 can do compared with the 65816 given the same amount of instructions. I thought that MIPS-wise they were closer to each other?

byuu wrote:
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 disagree, I still can read my old 68000 asm code just fine, it's perfectly doable. Agreed that you probably need to do more commenting (since asm opcodes tend to lack context), but it's doable.

Fusion916 wrote:
Isn't C just a step up from assembly?

No, not even remotely close. C makes a lot of abstractions. It also tries to be as portable as possible so a lot of things end up being unoptimal, especially for 8-bit processors (C was designed for Unix which originally ran on much more powerful processors, so it wasn't anywhere as bad for those).
Re: C/++ for the SNES
by on (#136482)
C isn't much at all like hand-optimized assembly language on the older 1 MHz 8 bit processors. Stack setup and tear-down is horrifically expensive in terms of clock ticks on them.

In my opinion, C has to assume a lot about variable access and making sure their value can be read later, even if it never does. C memory pointers have to be a certain overly huge size... For example, even if an 8-bit register using a single special indexed mode instruction could theoretically be used to access an array element in the first 256 bytes of memory, the compiler maker has to write in a more general purpose, less efficient block of indexing code to handle the worst case, just because the address value COULD be that big (even if it isn't).

Hand-written assembly destroys or overwrites things merely for the sake of convenience and speed and less instructions. The author knows that variable "X" isn't going to be used anywhere else in the procedure, and thus adds and shifts it as needed right there in a register, and suddenly it's morphed into another value, instead of C's stack-based read-modify-write necessity after every elemental operation. It needs to do this because again the C programmers MIGHT (not will, MIGHT) put an assignment operator somewhere in a long string of math operations. C can go X = X + ++Y + 1 + Z++; Therefore again it has to be more stack oriented and ultimately inefficient for the sake of the language.

Coming from the other angle Gigahertz level CPU's are so starved of instructions that it doesn't typically matter if a procedure is less optimized and takes up say 10 more clocks than it needs to. Getting the result out of the chip into the memory or registers happens at a much slower rate than it can compute individual instructions at. This is the opposite problem on 8-bitters, that any little trick to gain speed has a huge effect visually and on possible game complexity.

Now this is my total opinion, that C programmers are lazy, and like to write in the least amount of symbols, because to them it looks simpler. The IF statements without a comparison operator, the abuse of FOR loops with multiple counters... what may seem simpler visually might not match with what number instructions these poor little 8 bitters need to execute to deal with these assumptions. So it's like you need to be aware of the assembly instructions anyway lest you write something terribly slow.

But, here's my ultimate opinion: it would be stupid nowadays to write an entire large game completely in assembly language. Use C for the logical decisions and its ability for handling call complexity. The core graphics and memory copying routines would have to be in assembly. If you know the processor's assembly instructions and know C, and have a compiler that allows you to mix the two freely, then you're unstoppable.
Re: C/++ for the SNES
by on (#136495)
Note that practically everything you said is caught by a modern compiler and optimized out... They analyze when variables are used and will ditch them as soon as they aren't needed anymore, they don't bother retaining intermediate values if there's a faster way to get to the same result, in some extreme cases they may completely rewrite the code (GCC seems to like doing this a lot with loops, to the point that it may outright eliminate some variables and replace them with decrementing loop counters even if the original check didn't have counters at all).

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.
Re: C/++ for the SNES
by on (#136496)
I think you can go pretty far with C on a platform like this. You can go a lot farther if you also know assembly, and can optimize the parts that need it.

The incredible optimizing abilities of modern compilers aren't terribly relevant to this, though, most compilers for these targets are pretty poor in that respect.

Try to use global variables instead of the stack. Profile your code, and when things get too slow find the routines taking the most time and rewrite them in assembly. C code will be really bloated compared to assembly, but this is more of a problem on the NES than it is SNES, since space is much more precious on the NES.
Re: C/++ for the SNES
by on (#136498)
it is very possible to make a game in assembly for the SNES, I advise against C.
The assembler is not unreadable, I can very well understand the assembly code of another person.

I don't understand this fear of the assembly.
Re: C/++ for the SNES
by on (#136499)
Why do you think it's fear?

Writing, iterating on, and debugging assembly code takes longer than equivalent C code. This is just a natural fact.

If your development time is limited (and it is; you are going to die someday), it is reasonable to concern yourself with the amount of time you will spend developing the code. This will be a factor in whether or not you can finish your project. There are probably thousands of posts about unfinished projects on this board; some of them would be finished if they could have been done faster.

Honestly, I've seen more reluctance to use C around here than I've seen reluctance to use assembly.
Re: C/++ for the SNES
by on (#136503)
> 0.4MIPS wait what?

Technically, it depends on a lot of factors. When I say 0.4MIPS, I'm usually rolling operations together like "clc; adc", "sec; sbc", and so forth.

But the chip clock runs at 21477272hz. Given normal memory speeds (8 clocks per cycle), instructions are anywhere from 2 - 8 cycles long. The more cycles, the more useful.

In the absolute best case, you could execute at 1.342MIPS. But, good luck making a game with only:
Code:
clc, cld, cli, clv, sec, sed, sei,
tax, tay, txa, txy, tya, tyx,
tcd, tcs, tdc, tsc, tsx, txs,
inc, inx, iny, dec, dex, dey,
asl, lsr, rol, ror, nop, xce,

(and you don't want to know why I just happen to have that list handy :P)

Even if it were possible, you would spend way more instructions working around the horrible limitations to do the same thing anyway.

So I tend to think of 6 cycles as being the average for doing something useful (clc + long-adc, jsl subroutine, stuff that eg the 680x0 can easily do (and then some) in one instruction.) That gets you to 0.447MIPS.

If you want to be more optimistic, you could say 4 cycles is more reasonable, which only brings you to 0.671MIPS. FastROM can also help a bit, but is heavily crippled by not being usable on RAM.

Regardless, said performance is absolutely dreadful. You really do have to micro-optimize the living hell out of SNES ASM. Especially if you ever try something as reckless as "drawing proportional fonts." It can take entire frames to render a proportional font screen.

> I disagree, I still can read my old 68000 asm code just fine, it's perfectly doable.

You're comparing apples and carburetors. 680x0 is very readable compared to 65816.

Code:
mulu.l #23,d0  //(unsigned) multiply d0 by 23


vs

Code:
sta $00; asl; sta $02; asl; sta $04; asl; asl; clc; adc $04; clc; adc $02; clc; adc $00  //(unsigned) multiply by 23


Fun exercise for the reader: try and do muls.l #23,d0 (signed multiply) on the 65816.

> I don't understand this fear of the assembly.

You haven't written enough.

I tried to make a fan translation for Dai Kaijuu Monogatari. I hacked 95% of the game, reprogrammed all of the text engines and window displays entirely. It was about 200 - 300KB of pure 65816 ASM, commented as best I could.

I sat around for a year asking for anyone to please help me translate the script. I went back to look at the code and realized ... it was only maybe 5% better than looking at a raw disassembly.

Why? Because all math was done on the accumulator (and optimized like my mul 23 above that renders it unreadable), speed optimizations resulted in nasty tricks with the stack and register sizes (no common calling convention or assumptions on A/X sizes inside called functions), branching was abused heavily for performance (the bad kind of goto), memory was all hard-coded addresses, and there was no grander structure beyond basic functions.

This isn't a problem at all for writing 65816. But when you come back to it after months of inactivity, you can't look at the code and intuitively understand all of the speed hacks and accumulator math at first glance. You basically have to study and relearn each function you wrote in order to be able to use them again.

With C++, all of my expressions are in pure math, I can work with all the variables I want at one time, I can build classes with member functions, inherit from base classes and obtain polymorphism transparently, and allocate memory dynamically on the heap.

Absolute world of difference. I know 65816 very well, and I've reverse engineered and programmed massive blocks of code in it. There are many as good as me, but quite possibly no one better at it. But I'm not going to pretend it's easy to write large-scale 65816 applications because of that.
Re: C/++ for the SNES
by on (#136506)
byuu wrote:
In the absolute best case, you could execute at 1.342MIPS.

...well, or 1.79 MIPS in FastROM, as long as you never touch RAM...

Also, I think immediate 8-bit loads only take two cycles. Unfortunately there are no store instructions that fast...

Is it the whole instruction that slows down when slow memory is involved (doesn't seem plausible), or just the actual read/write? Or some other way of handling it? I mean, if you wrote sta $7E1234 in FastROM, shouldn't it take 32 master cycles rather than 40?

I seem to recall hearing about a manual that breaks down instructions by cycle, and I'm pretty sure it wasn't the WDC one. Do you know what I'm talking about?

byuu wrote:
Fun exercise for the reader: try and do muls.l #23,d0 (signed multiply) on the 65816.

Code:
sta $211B; xba; sta $211B; lda #$17; sta $211C; lda $2134

Boom; done.

...I'll get my coat.
Re: C/++ for the SNES
by on (#136507)
I coded a little shmup in 2 days , I don't think I would have been C significantly faster.
A video game for me what is the longest it's algorithm.
There are very good macro assembly, which allows to code quickly.

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.
Re: C/++ for the SNES
by on (#136509)
Quote:
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:
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.
Quote:
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:
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.
Re: C/++ for the SNES
by on (#136520)
Can't you just use absolute addressing when you run out of dp registers?
Re: C/++ for the SNES
by on (#136521)
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:
; 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 --
Re: C/++ for the SNES
by on (#136527)
Quote:
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?
Re: C/++ for the SNES
by on (#136530)
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:
    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:
    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.
Re: C/++ for the SNES
by on (#136575)
> 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.
Re: C/++ for the SNES
by on (#136587)
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.
Re: C/++ for the SNES
by on (#136589)
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.
Re: C/++ for the SNES
by on (#136592)
Quote:
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.
Re: C/++ for the SNES
by on (#136595)
Quote:
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.
Re: C/++ for the SNES
by on (#136596)
psycopathicteen wrote:
Quote:
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:
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:
Quote:
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)
Re: C/++ for the SNES
by on (#136598)
Sik wrote:
Code:
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).
Re: C/++ for the SNES
by on (#136599)
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.
Re: C/++ for the SNES
by on (#136608)
Quote:
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.
Re: C/++ for the SNES
by on (#136611)
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?
Re: C/++ for the SNES
by on (#136626)
First I have an 8-bit counter that increments once every frame. Then I take that counter and reverse its bits (i.e. bit 0 becomes 7, bit 1 becomes 6, etc.), this only needs to be done once in the frame since the value is reused for everything. When dealing with assembly you can try abusing carry (rotate left in one register, rotate right in the other, repeat for every bit).

After that, whenever I want to apply momentum, I take the speed value, add that reversed counter value to it, and then shift left 8. The result of that is then added to the coordinate as usual:
Code:
; d0 = Position
; d1 = Speed
; d2 = Counter (zero extended)

    add.w   d2, d1   ; speed += subpixel
    asr.w   #8, d1   ; speed >>= 8
    add.w   d1, d0   ; position += speed

(don't save the modified speed value, of course!)

This same method should be easily doable on the SNES, and I'd assume on the NES as well, even. Here's an example of how well it works. Note that if you want to cheap out, you can get away with less bits, e.g. you could just take four bits from the counter and leave the rest cleared (this gives you steps of 1/16th pixels, which most of the time is more than enough), just make sure that the bits are at the top of the byte and not the bottom.

The two biggest advantages are that it means you need less memory and that everything is measured in pixels (which can help reduce errors). The downside is the sightly added complexity (although for many things you don't even need subpixel momentum anyway, so remember that).

EDIT: added comments.

EDIT 2: coming to think on it, yeah it's pretty much what tepples said, except that instead of being a pseudorandom value it's the most perfect pattern that gets the job done instead (and the addition of 1 comes as a side effect of the calculation, rather than being an explicit branch).
Re: C/++ for the SNES
by on (#136628)
This "dithering" of velocity kind of reminds me of Heisenberg's uncertainty principle. Each game object has a precise momentum, but not a precise position.
Re: C/++ for the SNES
by on (#136660)
Sik: What is the advantage to dithering the subpixel part?
Re: C/++ for the SNES
by on (#136661)
It's more even. For example, when the subpixel part is 1/2, the offset is 0,1,0,1,0,1... when it's 1/4 the offset is 0,0,0,1,0,0,0,1,0,0,0,1... you get the idea. Using a (pseudo-)random number doesn't guarantee that the movement will be even like that.

Unless you mean compared to just storing it in the coordinates, in which case well, the advantage is saving memory and keeping everything (non-speed) in pixel units.
Re: C/++ for the SNES
by on (#136911)
Back on subject, asm code does get complicated really fast. I try to keep things simple, but then there's always that extra feature I need to add to my routine, and I end up butchering my elegant looking code for it.
Re: C/++ for the SNES
by on (#136914)
Honestly I think that's more of an issue of the 65816 having so few registers =S
Re: C/++ for the SNES
by on (#136915)
Yes, I was referring to 65816 asm specifically.
Re: C/++ for the SNES
by on (#136918)
In my experience, most people who complain about lack of registers on 65xxx platforms come from other architectures where they've already been spoiled (i.e. tainted) via more registers and opcodes which are the equivalent of a bidet. I'm going off historic experiences here, but most of the time it was 68K or x86 people trying to do 65xxx. (Thought as someone who went from 65xxx to x86, I can't tell you how much I hated the fact that mov doesn't set status flags like on the 65xxx. I developed a hatred for test as a result).

But this is why I tell people the 65xxx is a great "starting" processor/platform -- you have limited resources, you learn to develop true KISS and minimal-resource-use mentalities and thought processes.

You then can move to 68K or x86 where all of your secret lusts and desires have been answered *cough cough*.

Buncha whiners. ;-) Like I've said in the past, about the only thing I'd like on the 65816 is native multiplication/division opcodes (and don't refer me to the SNES MMIO registers that rely on a small math IC). Maybe some additional addressing modes, but I'd be fine without that.
Re: C/++ for the SNES
by on (#136920)
Nobody tell them about the PowerPC.
Re: C/++ for the SNES
by on (#136921)
rainwarrior, I'm sending you a bill for having my LCD cleaned of 7-Up. Thanks a lot. :P
Re: C/++ for the SNES
by on (#136925)
Don't forget about processors where the's only an accumulator and nothing else and every operation ends up involving memory instead =P (i.e. literally just a single register, ignoring program counter and such, assuming you can even access those anyway)

The obvious answer to this stuff on 65xx anyway ends up being "use zero page" (and they'll argue that it's actually better because you get 256 bytes, way more than what you get with registers on other CPUs). Well, except for the part that they're also usually used to hold program state as well... but yeah. I think 65816 stores zero page internally anyway.
Re: C/++ for the SNES
by on (#136928)
the low register is not really a problem, you have to work with a temporary memory.

Koitsu:
why multiplication / division? most are rather slow on the 68k he made roughly 170 cycles, I think it is better to work with bit shifts
Re: C/++ for the SNES
by on (#136929)
Sik wrote:
The obvious answer to this stuff on 65xx anyway ends up being "use zero page" (and they'll argue that it's actually better because you get 256 bytes, way more than what you get with registers on other CPUs). Well, except for the part that they're also usually used to hold program state as well

My typical practice on NES has been to use $0000-$000F for those things where I'd use a register on other architectures and $0010-$00FF for program state.

Quote:
but yeah. I think 65816 stores zero page internally anyway.

Nope. Zero page is just as external on 65816 as it is on 6502. It can just be relocated within $000000-$00FFFF, making it useful as a "frame pointer" for a stack in a C style language (like x86 BP). And some memory controllers may have special fast memory that's mapped to some area of bank $00.
Re: C/++ for the SNES
by on (#136933)
Hm, I wonder who was saying around some time ago that the 65816 stored its zero page internally (or is there some other 65xx variant that does?).
Re: C/++ for the SNES
by on (#136937)
I reserve a set of ZP values for pseudo register names (A0-A7, R0-R7, D0-D7); they're 16bit wide. I have equates for accessing the high and low bytes directly (A0.l, A0.h, etc). I have a set of code compact macros to go along with this for non speed essential code, or for prototyping clean looking code (ADD.word #$1234,<D0 or something like ADD.w.byte #$34,<D0 for slightly optimized macros).

But yeah for optimized stuff, you pay the price: convoluted/complex code in trade off for read ability/etc. But I don't mind.

koitsu: I went from x86, to z80, to GBz80, before doing 65x and others. I never really had a problem transitioning. I was surprised by how different the ISA and approach was by comparison at first, but it didn't bother me. X86 actually annoyed me the most. Even though it was my first processor, I couldn't help but feel that it was primitive in design; the register set felt really limited and I didn't even have anything to compare it to (never coded for another processor at the time). Going to the 68k, I couldn't help feel that coders growing up on that processor were just spoiled - hah.
Re: C/++ for the SNES
by on (#136941)
Most programmers write code like this when transitioning from 68k to 65xx:

Code:

lda $00      ;; add $01 to $00
clc
adc $01
sta $00

lda $00      ;; add $02 to $00
clc
adc $02
sta $00

lda $1000    ;; move $1000 to the $03 so it can run faster
sta $03

lda $03      ;; add $00 to $03
clc
adc $00
sta $03

lda $03
sta $1000    ;; move $03 back to $1000
Re: C/++ for the SNES
by on (#136942)
To teach a smug 68000 weenie how to make a 6502 scream, you might have to show him a few peephole optimizations one at a time.

Remove loads after stores:
Code:
lda $00
clc
adc $01
sta $00      ;; remove lda $00 after store
clc
adc $02
sta $00
lda $1000    ;; move $1000 to the $03 so it can run faster
sta $03      ;; remove lda $03 after store
clc
adc $00
sta $03
sta $1000


Remove stores whose value is provably unused:
Code:
lda $00
clc
adc $01
clc          ;; remove unused sta $00
adc $02
sta $00
lda $1000
clc
adc $00
sta $03
sta $1000


Addition of this type is commutative:
Code:
lda $00
clc
adc $01
clc
adc $02
sta $00
lda $00      ;; group accesses to same address
clc
adc $1000
sta $03
sta $1000


Which allows removing another load after store:
Code:
lda $00
clc
adc $01
clc
adc $02
sta $00      ;; remove lda $00 after store
clc
adc $1000
sta $03
sta $1000


Thus this section of code is provably equivalent yet small enough for repeating unused store analysis with $00 and $03 in the rest of the snippet. If it turns out they're not needed, you end up with perfectly idiomatic 6502 assembly:
Code:
lda $00
clc
adc $01
clc
adc $02      ;; remove unused sta $00
clc
adc $1000    ;; remove unused sta $03
sta $1000


You already know all this, but the process illustrated in this example might help someone else adapt to the 6502. Each step might produce its own "Oh!" moment.

(And now this is used as an example on wiki.superfamicom.org.)
Re: C/++ for the SNES
by on (#136944)
If you want to make a 68000 programmer suffer, go with Z80 instead. Not too few registers to result in a mentality change, but not enough registers to be comfortable. Enjoy watching how heavily that stack gets hammered.
Re: C/++ for the SNES
by on (#136954)
Those are pretty much the kinds of "hardcore optimizations" that games like Space Megaforce use.