Closed aaaaaa123456789 closed 4 years ago
Compare .const()
in ca65 which is used thus:
.if .const(a + 3)
.out "a + 3 is constant"
.else
.out "a + 3 is not constant"
.endif
Can't such sanity checks be added with assertions? (#292)
I thought about adding such a ISCONST
function, but I didn't see any use for it that wouldn't overlap with link-time asserts (which I do plan to add as soon as handling of changing the .o file format is decided on in #470).
Also, removing such checks if they can't be done at assembly time doesn't sound like something you'd want, right?
I understand ca65 .const
as answering "Would this expression be a valid argument to rept
or if
?" (Ignore for a moment negative numbers being invalid for rept
.) Thus if this expression can be evaluated now, return 1. Otherwise, such as if it would need to be deferred until link time, return 0.
That is also exactly how I understand it, what I'm asking about is what contexts this would be useful in, assuming link-time assertions are supported.
Link-time assertions could solve the exact example I posted above, but anything more complex would be impossible. I've been writing lots of very complex macros lately, with multiple calculations inside, and I've come to the point where having different behaviors if an expression is compile-time constant and if it isn't actually makes sense.
Really? Do you have an example? That sounds like a bug more than anything else
Might as well. Context: splitting a long array of structs (perhaps too long to fit in a bank) into chunks and creating a table of chunks. The table contains four-byte entries containing count (byte), bank (byte), address (halfword).
This is my current code for the macro that creates an entry: https://gist.github.com/aaaaaa123456789/751d3d5590c5a13d789a25b7269bce6f
You will notice there's a lot of code for dealing with the case of having more than 255 entries. Now, all of that code requires being able to calculate the actual entry count at compile time. If I ever used a non-constant count, it would make perfect sense to just assert that the count fits in a byte and forgo all the impossible calculations. (I'd probably have to restructure the macro a bit to take the actual count as an argument and not the new max index, but that's beside the point.) Doing what you can and covering most common cases is better than doing nothing; there's no reason why I'd have to write a separate macro for non-constant data to get what's essentially the same behavior.
In ca65, I've occasionally implemented the instruction set of a different processor connected to the Super NES using a macro pack.
Some of these ISAs have shorter encodings when constant values are in a specific range. For example, to determine whether the ld
to ldh
optimization is valid, I'd have to say something like .if .const(addr) && (addr) > $100
.
Some possible ISAs that one might want to implement atop RGBDS:
Question: do we want ISCONST(symbol)
, or ISCONST(expression)
? There is a major difference, which would be that EQUS
are not expanded in the former, but are in the latter.
Definitely expression. When you get an argument in a macro it's impossible to know whether you're getting an expression (which might be const depending on what symbols/expressions are used) or not.
Okay then. Note that this means I cannot special-case ISCONST(symbol)
to not expand EQUS
, as this requires the decision to be taken before lexing any tokens inside the expression, but it can only be taken after lexing at least one.
Why would not expanding EQUS
be a good thing?
For the same reason EQUS
are not expanded within DEF()
or PURGE
.
Sometimes you want to add sanity checks to macros. Something like this:
But those checks require the macro argument to be a compile-time constant. If the macro can be used with expressions that are calculated by the linker, this is not possible.
The current implementation requires removing the check. If an expression can be tested for const-ness, the checks can be kept as long as the expression is constant. This can be a function or even a new conditional statement: after all, if
(\1)
is a constant expression, so is(\1) < 4
, so evaluating some conditional statement only if the expression is constant will do the trick.