Page 1 of 2

SNES register sizing schemes (65816 M and X)

Posted: Sat Aug 29, 2015 6:28 am
by furrykef
I'd heard that what a lot of 65816 programmers do is they usually keep the M flag 8-bit and the X flag 16-bit. I found I rarely need 16-bit indexing and started to ponder whether this might be the wrong way around, so last night I rewrote my current projects to default to 16-bit M and 8-bit X. (This actually went pretty quickly.) And... I'm not convinced that this method is any better or worse. It seems the hardware is screwy enough that both schemes are about equally bad.

Let's have a look at the implications of both schemes...

8-bit M, 16-bit X:
  • May have to toggle the size of M more often, which can be bug-prone
  • Instructions such as TAX, TAY will be 16-bit moves while X is 16-bit, and it's easy (especially for 6502 coders) to forget to clear the upper byte first
16-bit M, 8-bit X:
  • Encourages the use of 16-bit vars where 8-bit vars will do (slightly larger and slower code)
  • Can't use STZ for 8-bit values
  • Sometimes I find myself deliberately loading 8-bit variables into my 16-bit A, knowing that I will soon TAX or TAY and chop off the upper byte. I'm not sure this is good practice. I do use Hungarian notation for 65816 ASM code, so it's pretty obvious when I'm doing this.
So my impression is the first scheme results in slightly smaller and faster code, but it has slightly more opportunities for gotchas that could add hours of debugging time. Of course, bits of code where every cycle counts can still be written to use whatever scheme is appropriate, so I don't think the performance hit is a significant concern.

Thoughts?

Re: SNES register sizing schemes (65816 M and X)

Posted: Sat Aug 29, 2015 9:13 am
by psycopathicteen
I do almost everything with 16-bit accumulators and index. During vblank I mainly deal with writing to hardware regs that are mixed sizes, so I use mixed sized accumulator and index.

Re: SNES register sizing schemes (65816 M and X)

Posted: Sat Aug 29, 2015 9:22 am
by Bregalad
I am no 65c816 coder so I don't know much, but what is the argument against having 16-bit accumulator *and* index registers most of the time ? If this is a 16-bit CPU, normally most variables and registers should always be 16-bit, exept in rare cases where a smaller size is required, such as arrays with lots of data, which must be 8-bit, or communication with hardware registers. In those cases you could either do AND/OR trickery or shortly turn a register into a 8-bit time before returning to normal, depending on which one is more efficient.

Re: SNES register sizing schemes (65816 M and X)

Posted: Sat Aug 29, 2015 9:39 am
by psycopathicteen
such as arrays with lots of data
Which can still be 16 bit, as long as you're not running out of memory.

Re: SNES register sizing schemes (65816 M and X)

Posted: Sat Aug 29, 2015 10:49 am
by Khaz
I use both ways. Generally speaking I default to keeping both at 16-bit unless I have to do otherwise. When I need an 8-bit register, which I choose depends on which register size I need two of and whether I will be doing any adc/sbc or other A-only instructions. I'd say I use both equally as much and just keep very rigorous track of my register widths.

This is coming from the perspective of someone who has done very little optimizing so far so take it with a grain of salt.

Re: SNES register sizing schemes (65816 M and X)

Posted: Sat Aug 29, 2015 12:16 pm
by psycopathicteen
Not only do I program almost everything in 16-bit mode, but I also do almost everything in bank $80 too.

Re: SNES register sizing schemes (65816 M and X)

Posted: Sat Aug 29, 2015 1:22 pm
by 93143
So far I've mostly just been coding graphics tests. I find it's useful to be able to quickly stz to an 8-bit PPU register, so I use 8-bit A and 16-bit X/Y. This is also useful for DMA subroutines, since you can pass two 16-bit numbers and an 8-bit number (actually, I think this is probably why I started off with this combination, even though I don't use subroutines any more). And it strikes me that it could be useful to have some indexing capability without losing the ability to load and store both data sizes. There are exceptions, of course - for instance, writing to (especially zeroing) pairs of adjacent PPU registers in a time-critical H-IRQ while deliberately not using X or Y so as to save on stack operations... bit of an edge case, perhaps...

In the few more game-like bits of code I've done, I find a 16-bit accumulator to be a good idea, since you want to be able to do math with larger numbers.

When I coded an overlay renderer for my title screen mockup, I ended up doing the bitwise logic section with 8-bit A and the actual data combination with 16-bit A. X/Y stayed 16-bit, because I needed to be able to index within the source and destination graphics buffers. (It seems to me that this could be a more general result; large arrays of object parameters, for instance, could easily exceed 256 bytes. On the other hand, the OP says this sort of thing doesn't happen much, and people wrote games on the NES somehow...)

Basically, if I want something to execute quickly, I try to optimize the register size for maximum speed. It may be helpful that I don't seem to care how long this game takes to write didn't start on 6502; 65816 is the first assembly language I learned, so the variable register sizes are a fundamental part of my understanding rather than an annoying hack I keep forgetting about.

That said, I don't believe I've ever used 8-bit X/Y, though that may change as I start writing more game engine code...
psycopathicteen wrote:I also do almost everything in bank $80 too.
Ah, FastROM. I do that too. It's a bit of a pain getting my ancient copy of WLA DX to actually land on the correct bank, but once it's done it's done...

Re: SNES register sizing schemes (65816 M and X)

Posted: Sat Aug 29, 2015 4:27 pm
by Near
I always keep 16-bit index registers, and I toggle freely and constantly between 8-bit and 16-bit accumulator.

I am very resentful that we didn't get a pipeline fetcher and a 16-bit prefix byte so that you could use both modes without messing with rep/sep, and constantly have to use php/plp (or have a rigid ABI)

Re: SNES register sizing schemes (65816 M and X)

Posted: Sat Aug 29, 2015 5:42 pm
by psycopathicteen
93143 wrote:
psycopathicteen wrote:I also do almost everything in bank $80 too.
Ah, FastROM. I do that too. It's a bit of a pain getting my ancient copy of WLA DX to actually land on the correct bank, but once it's done it's done...
Not only that, but I rarely touch the bank register at all.

Re: SNES register sizing schemes (65816 M and X)

Posted: Sat Aug 29, 2015 6:48 pm
by 93143
You mean the program bank register? I don't either, but I assumed it was because my programs are all small, so there's no point putting anything but data (and sometimes not even that) anywhere outside the first bank. But I code without subroutines, for speed, and I think I prefer to generate multiple specialized routines rather than generalize a single routine at the expense of speed, so I fully expect to use more than one bank for code in the finished game (if it ever gets finished...). The official GSU map seems to have 16 Mb of LoROM from $80-$BF and 32 Mb of HiROM from $C0-$FF, so I think that's probably a reasonable way to divide up code and bulk data...

I have used the data bank register, most notably in that overlay renderer. It took up a sizeable chunk of WRAM, and using 24-bit addressing for the whole thing was just not going to fly...

Re: SNES register sizing schemes (65816 M and X)

Posted: Sat Aug 29, 2015 7:16 pm
by UnDisbeliever
My code uses the following three schemes

8 bit A, 16 bit Index: Used for Setup, PPU setup, register manipulation, text routines.
16 bit A, 16 bit Index: Used for physics, tilemap manipulation, pointer manipulation, entity/object manipulation code.
16 bit A, 8 bit Index: Used in the occasional VBlank routine.

I tend to use the method that seams faster in my mind.

I also ensure that a routine exits in the same register sizes as it entered on.

byuu wrote:I am very resentful that we didn't get a pipeline fetcher and a 16-bit prefix byte so that you could use both modes without messing with rep/sep, and constantly have to use php/plp (or have a rigid ABI)
Ditto, 20% of my crashes are due to me exiting a routine/loop/if with the wrong A/X/Y state.

I've gotten better, it was ~60% at the beginning of the year.

Re: SNES register sizing schemes (65816 M and X)

Posted: Sat Aug 29, 2015 7:30 pm
by psycopathicteen
Whenever I have a crash, it's always because of some crazy chain reaction.

Re: SNES register sizing schemes (65816 M and X)

Posted: Sun Aug 30, 2015 10:25 am
by Ramsis
byuu wrote:I always keep 16-bit index registers, and I toggle freely and constantly between 8-bit and 16-bit accumulator.
Ditto. :)

Re: SNES register sizing schemes (65816 M and X)

Posted: Mon Aug 31, 2015 1:51 am
by Bregalad
93143 wrote:I find it's useful to be able to quickly stz to an 8-bit PPU register, so I use 8-bit A and 16-bit X/Y. This is also useful for DMA subroutines, since you can pass two 16-bit numbers and an 8-bit number (actually, I think this is probably why I started off with this combination, even though I don't use subroutines any more). And it strikes me that it could be useful to have some indexing capability without losing the ability to load and store both data sizes. There are exceptions, of course - for instance, writing to (especially zeroing) pairs of adjacent PPU registers in a time-critical H-IRQ while deliberately not using X or Y so as to save on stack operations... bit of an edge case, perhaps...
All you're reffereing to is really rare cases, such as "writing to a 8-bit PPU register" or "time-critical H-IRQ". Writing to PPU registers will only be 0.1% of a game's code, and a time-critical H-IRQ won't be needed by 99% of games. So really all you're prooving is that only in some particular cases there's a real advantage to using 8-bit registers. For tech-demoes of course this would be more significant, but nevertheless for game logic I don't see why you wouldn't want to have all registers to be 16-bit.

The only reason to have 8-bit accumulator and 16-bit index is if you're not wanting to change that ever, and that you consider that it is important to have a 8-bit register to write to the hardware reigsters more easily.

Re: SNES register sizing schemes (65816 M and X)

Posted: Mon Aug 31, 2015 2:21 am
by 93143
I do not pretend that my experience is in any way representative. In fact I explicitly noted that I hadn't written much game logic yet. If I'd had more comprehensive experience, my post would have been a lot shorter.

But I think you may be exaggerating the rarity of the need for an 8-bit register. It's encouraging to me that both byuu and Ramsis seem to handle this essentially the same way I do.
Bregalad wrote:The only reason to have 8-bit accumulator and 16-bit index is if you're not wanting to change that ever
This makes no sense to me.