Macros to assert current register size in ca65

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
Macros to assert current register size in ca65
by on (#124791)
The ca65 Users Guide lists a pseudo-variable .CPU for use in assertions about the CPU type. But it doesn't list anything for the state of a given CPU type, namely whether a 65816 is in 8-bit or 16-bit mode for the accumulator or index registers. So I wrote some macros to test these. They're useful if you're using smart mode (which tracks 65816 REP and SEP instructions to know which instruction size to generate for immediate operands. It requires the linker script to define a segment called "DEVNULL" that is not written to the output file.

Am I doing it wrong?
Code:
.macro assert_regbits_816 ldr, expected_size
.pushseg
.segment "DEVNULL"
.local Code
.proc Code
  ldr #0
.endproc
.popseg
.assert .sizeof(Code) = expected_size, error, .sprintf("expected %d-bit mode for %s but got %d-bit", 8 * (expected_size - 1), .string(ldr), 8 * (.sizeof(Code) - 1))
.endmacro

.macro assert_a16
  assert_regbits_816 lda, 3
.endmacro
.macro assert_i16
  assert_regbits_816 ldx, 3
.endmacro
.macro assert_a8
  assert_regbits_816 lda, 2
.endmacro
.macro assert_i8
  assert_regbits_816 ldx, 2
.endmacro

; Test cases ;;;;;;;;;;;;;;;;;;;

.out "The following should all succeed"
.p02
assert_a8
assert_i8
.p816
.a16
assert_a16
.a8
assert_a8
.i16
assert_i16
.i8
assert_i8

.out "Six errors should follow"
.a16
assert_a8
.i16
assert_i8
.a8
assert_a16
.i8
assert_i16
.p02
assert_i16
assert_a16
Re: Macros to assert current register size in ca65
by on (#124799)
Seems like a good way to test it. I'll be probably using this trick if/when I get around to doing some more SNES coding.
Re: Macros to assert current register size in ca65
by on (#124806)
I'm only imagining a use for this in macros. If you want to be sure the assembler is configured for a certain register size for a routine, just set it with .A8, .A16, etc. before the routine's code. If you want to be sure that the register is that size at run-time, an assemble-time macro can't help you; you ned REP and SEP.
Re: Macros to assert current register size in ca65
by on (#124807)
I have absolutely no idea what all of that pseudocode is about (nor do I have any idea what ldr does -- there is no such opcode on 65816), nor what it solves. What's the issue?

The ca65 assembler already seems to have brains for knowing what size your registers are at operation-time (by following sep/rep bits as you said). If you want to change the behaviour or override it, then what blargg said is spot on. This is how 65816 assemblers have behaved/worked for years (my own experience being with Merlin 16, ORCA/M, and x816). You use the assemblers' pseudo-ops (ex. .a16) to accomplish such overrides, all with the hope that the programmer knows what they're doing. (Such overrides almost always justify a small comment explaining why it's being done)

(Footnote: not in this thread, but the other -- people whining/crying over the dynamic register sizes on the 65816 need to get over it already, such complaints (at least with me) fall on deaf ears. It isn't ever a problem when writing/creating new code, only when disassembling or reverse-engineering existing binary data. And I'd take the pains of that over the nonsense of most present-day architectures any day of the week.)
Re: Macros to assert current register size in ca65
by on (#124809)
In the macro, ldr is a macro argument whose value is lda or ldx.

I just wonder what was in that Psygnosis assembler.
Re: Macros to assert current register size in ca65
by on (#124810)
I can understand the motivation for this. The processor has modes, and if you change them at all, you may be in the wrong one when some code is executing. To make it worse, there are two copies of this mode, in addition to the one you expect: the one the assembler is in, and the one the processor is in while the code is executing. These macros at least allow you to ensure the assembler is in the same mode you want, and presumably the programmer has the processor in the same mode as the assembler. The objection to changing the mode is that it has overhead and might require masking things if you're widening any registers.

The above has led me to laying out a "standard" mode to run everything in, and only stray from it where the benefits outweigh the cost. This has some cost in slightly slower/larger code than optimizing register size everywhere and handling all the variations between routines in some globally-optimal way (which would be ripe for hard-to-find bugs).
Re: Macros to assert current register size in ca65
by on (#124851)
This thread is confusing. Many directives & variables & macros, and it isn't quite clear to me what tepples was asking for.

The .p02 .p816 directives seem to be selecting 6502 or 65816 mode.
The .a8 .a16 .i8 .i16 directives seem to be selecting 8bit/16bit modes.
I don't know if they do just tell the assembler which mode to use, or if they do additionally produce code (eg. REP/SEP).

But I think tepples already knows that, and he wasn't asking what they are doing, or how to switch between 8bit/16bit modes - but rather how to detect if the assembler is in 8bit mode or 16bit mode.

I can't imagine what tepples meant by "useful if you're using smart mode". But that type of detection might be useful here or there. Like in a macro that shall generate REP/SEP opcodes only if needed (ie. only if the assembler state (and implied: the assumed cpu state) isn't already in the desired mode).

tepples wrote:
Am I doing it wrong?
...
.out "Six errors should follow"

If you were getting that six errors (?) then you are probably doing it right.

tepples wrote:
I just wonder what was in that Psygnosis assembler.

What Psygnosis assembler? Didn't you refer to something called "ca65"? Or is ca65 a compiler that interacts with the psygnosis assembler?

The term "assert" (and the purpose ".assert " directive) are also confusing me. Maybe my english just too bad to understand the subject :-)
Re: Macros to assert current register size in ca65
by on (#124852)
nocash wrote:
tepples wrote:
I just wonder what was in that Psygnosis assembler.

What Psygnosis assembler? Didn't you refer to something called "ca65"? Or is ca65 a compiler that interacts with the psygnosis assembler?

Psygnosis had a SNES development kit (Psy-Q) with an assembler called ASM658. In another thread somebody mentioned that it had some automation related to checking that the register sizes are set correctly.
Re: Macros to assert current register size in ca65
by on (#124853)
As I understand it (I haven't read tepples' code closely), these are macros to assert that the assembler's current A or X/Y size setting is a particular value. I noticed that the code generates an instruction that generates 2 or 3 bytes of code depending on the mode (and in a dummy segment so it doesn't add anything to the ROM), and has an .assert, which agree. It also has some tests where a concrete failure is an abstract pass. In another thread another assembler is mentioned and I think the question is how/what it did regarding this issue of verifying the assembler mode.

Smart mode is probably where the assembler attempts to adjust its mode to match the code, e.g. it watches for REP/SEP. In this mode, one might not realize the register size changed and thus the assertion is a good way to express and verify the programmer's expectation in the source code.

tepples' approach (perhaps all one can do within ca65's limitations) I don't think allows for anything other than an assertion/error, as the size of generated code can't feed back to deciding whether to generate some code (probably because this could easily lead to feedback loops where the assembler/linker would have to keep re-running).
Re: Macros to assert current register size in ca65
by on (#124860)
nocash wrote:
But that type of detection might be useful here or there. Like in a macro that shall generate REP/SEP opcodes only if needed (ie. only if the assembler state (and implied: the assumed cpu state) isn't already in the desired mode).

I'd rather have it throw an error than trying to write code for me in that case, not only because you'd also need to have the macro restore the processor state at the end with an additional rep and/or sep.
Re: Macros to assert current register size in ca65
by on (#124868)
I just had a realization that was delayed this long because of the premature-optimization disease. My troubled mind sees the register size mode bits and yells "Oh my god, I have to optimize the size at all times or I'm not getting every last ounce out of the processor!" But I just had a flash of insight: these mode bits are something you decide globally based on your overall design. Are you porting lots of 6502 code? Then you probably use emulation mode. Is size critical, and are you dealing with mostly small arrays? 8-bit everything is probably best. Larger arrays, size not critical? 16-bit X/Y. Compiled C code? Asm doing lots of large integer manipulation? Obviously 16-bit A. Basically it's a way to set the overall processor characteristics. Of course you might temporarily change them where it has a significant benefit, but it's not something you change constantly and try to accommodate as if it were just another ever-changing flag.

I know I said that I do the above already, but it wasn't due to the above realization, mostly defeat and acceptance of terrible, dreadful sub-optimality.