Page 1 of 1

ca65 C-style macro problems

Posted: Wed Apr 05, 2017 6:41 pm
by nicklausw

Code: Select all

.define sprite(sprnum, sprattr) ($700+(sprnum*4)+sprattr)
The above does its job, but it seems ca65 doesn't expect it to be a function when used like this:

Code: Select all

sta sprite(0, tile)
(tile is defined as 1). Unexpected trailing garbage characters.

The best workaround I've found is to add a "0+", as such.

Code: Select all

sta 0+sprite(0, tile)
This isn't preferable, since it's kinda ugly. Are there any better ways around this?

Re: ca65 C-style macro problems

Posted: Wed Apr 05, 2017 6:44 pm
by tepples
The macro appears to expand as follows:

Code: Select all

sta ($700+(0*4)+tile)
But because the parentheses are outermost, ca65 thinks it's indirect addressing mode, which sta doesn't have.

Re: ca65 C-style macro problems

Posted: Wed Apr 05, 2017 6:50 pm
by rainwarrior
In my version of ca65 this gives "illegal addressing mode" which suggests to me that the problem is the parentheses () which suggest "STA (indirect)" which is an illegal addressing mode. Try your .define without the enclosing parentheses?

Edit: Hmm, didn't even get a ninja notification from tepples' post. ;)

Re: ca65 C-style macro problems

Posted: Wed Apr 05, 2017 7:08 pm
by rainwarrior
It's normal C macro practice to use the prophylactic () around your macro results, but it has this particular consequence in 6502 assembly where the meaning of () has this one contextual overload.

I've hit this particular problem myself a few times when using ca65 defines, making me wish there was some sort of "safe" expression enclosing operator that could wrap them up, like .expr() or something. Though, your 0+ solution is interesting; what if you put that directly in the .define instead of everywhere it's used?

Code: Select all

.define sprite(sprnum, sprattr) 0+($700+(sprnum*4)+sprattr)
My actual practice is just to be very sparing with my use of .define in ca65, I think, preferring .macro in most cases (which has slightly fewer caveats). I find there's a lot of counter-intuitive things about its macro system if you're already used to C style macros, because it's not really a text substitution at all, but more like substituting a symbolic tokenized version.

Also, the command line -D define behaves differently from the .define control command, which is another thing to watch out for. (I forget exactly how at the moment. :S)

Re: ca65 C-style macro problems

Posted: Wed Apr 05, 2017 9:37 pm
by RT-55J
I know DASM allows you to use square brackets as an equivalent to parentheses (e.g. [[blah+blah]*blah] ). Perhaps ca65 is similarly permissive.

Re: ca65 C-style macro problems

Posted: Thu Apr 06, 2017 7:57 am
by tepples
In ca65, square brackets mean indirect long, a 65816-exclusive addressing mode.

Putting the 0+ workaround in the macro could mess up operator precedence, as addition is fairly low in ca65's precedence chart.

Code: Select all

; With a macro like this
.define sprite(sprnum, sprattr) 0+($700+(sprnum*4)+sprattr)
; Something like this
.word 0x8000 | sprite(0, tile)
; would expand to this
.word 0x8000 | 0+($700+(0*4)+tile)
; But because bitwise OR operator | binds more tightly than the
; addition operator +, it automatically parenthesizes thus
.word (0x8000 | 0)+($700+(0*4)+tile)
Instead, you can wrap with a unary prefix operator, as these tend to have the tightest precedence. One might try using +, the unary numeric no-op, but because + is also the numeric addition operator, it can hide syntax errors.

Code: Select all

; With a macro like this
.define sprite(sprnum, sprattr) +($700+(sprnum*4)+sprattr)
; Something like this will unexpectedly assemble correctly
.word 0x8000 sprite(0, tile)
; because it expands to this
.word 0x8000 +($700+(0*4)+tile)
An invertible unary prefix operator that doesn't do double duty as an infix operator is ~, the ones' complement operator. Doubling it on an integer is a no-op unless there's something broken that I hadn't considered. So try this:

Code: Select all

; With a macro like this
.define sprite(sprnum, sprattr) ~~($700+(sprnum*4)+sprattr)
; Your instruction
sta sprite(0, tile)
; expands to this
sta ~~($700+(0*4)+tile)

Re: ca65 C-style macro problems

Posted: Thu Apr 06, 2017 8:27 am
by nicklausw
Removing the parentheses fixed the problem, but I will consider ~~(using this) for other macros that may be mixed into formulas themselves.

Thanks all!

Re: ca65 C-style macro problems

Posted: Thu Apr 06, 2017 8:29 am
by thefox
As tepples suggested, I have been using ~~ to disambiguate these cases, although it's unfortunate that it's needed.

Re: ca65 C-style macro problems

Posted: Thu Apr 06, 2017 9:04 am
by tepples
Unfortunate but understandable. I guess MOS didn't anticipate that the equivalent of the C preprocessor would eventually be used on assembly language source code for its CPU. The first edition of The C Programming Language by K&R wouldn't even come out until 1978, three years after the 1975 introduction of the 6501 and 6502.

Re: ca65 C-style macro problems

Posted: Thu Apr 06, 2017 5:55 pm
by rainwarrior
Apparently there is a .feature extension that selects [] for indirection instead of (). Maybe not useful on an existing project, but if you're starting from scratch (or perhaps switching from NESASM) you could just use square brackets for indirection instead and not have the conflict.

Code: Select all

.feature bracket_as_indirect
jmp [$FFFC]