ca65 C-style macro problems

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems. See the NESdev wiki for more information.

Moderator: Moderators

Post Reply
User avatar
nicklausw
Posts: 376
Joined: Sat Jan 03, 2015 5:58 pm
Location: ...
Contact:

ca65 C-style macro problems

Post 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?
tepples
Posts: 22705
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: ca65 C-style macro problems

Post 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.
User avatar
rainwarrior
Posts: 8731
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: ca65 C-style macro problems

Post 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. ;)
User avatar
rainwarrior
Posts: 8731
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: ca65 C-style macro problems

Post 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)
RT-55J
Posts: 47
Joined: Fri Aug 21, 2009 3:16 pm

Re: ca65 C-style macro problems

Post 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.
tepples
Posts: 22705
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: ca65 C-style macro problems

Post 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)
User avatar
nicklausw
Posts: 376
Joined: Sat Jan 03, 2015 5:58 pm
Location: ...
Contact:

Re: ca65 C-style macro problems

Post 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!
User avatar
thefox
Posts: 3134
Joined: Mon Jan 03, 2005 10:36 am
Location: 🇫🇮
Contact:

Re: ca65 C-style macro problems

Post by thefox »

As tepples suggested, I have been using ~~ to disambiguate these cases, although it's unfortunate that it's needed.
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi
tepples
Posts: 22705
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: ca65 C-style macro problems

Post 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.
User avatar
rainwarrior
Posts: 8731
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: ca65 C-style macro problems

Post 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]
Post Reply