“It’s a feature, not a bug.” The Glass assembler evaluates symbols through (more or less) substitution, so what you see is what you get. If it did not do this, it would be impossible to pass indirect references, e.g. (hl) when passing a register. Unfortunately it is an ideosyncracy of the standard Z80 language that parentheses have meaning, it is a pain point. I don’t know a better way to handle this without taking away functionality or deviating from standard syntax.
The simple solution however is to not add superfluous parentheses around your equation. I myself would never do that in the first place , but it’s been reported before.
Ugh, that causes unexpected results:
NElems equ 15 Start: ; array with 3 bytes per entry ds 1 ; x ds 1 ; y ds 1 ; colour Finish: span equ (Finish-Start) ; Add more elements to complete NElems ds span*(NElems - 1) ld hl,Start ; Point HL to the 1st element ld bc,span*3 add hl,bc ; Point HL to the 4th element ld bc,span ; Point HL to the 5th element???? Nope! add hl,bc
I personally like the way MASM for x86 solved this. It distinguishes between equ
(which performs macro substitution) and =
(which performs numeric evaluation). The latter also allows redefinition of a label.
So, for example, you can do this:
Stuff = 3 rept 8 Stuff = Stuff + 5 endm mov ax,Stuff
which would be equivalent to: mov ax,43
.
But in MASM, you can't do this:
Stuff = [bx]
because [bx]
does not resolve to a numeric value.
Since you're guaranteed that =
will evaluate to a number, there's no gotcha if you use this definition of span
instead:
span = (Finish-Start)
Actually that "=" or ":=" syntax that a few assemblers have for symbols that can change value through execution gave me quite some headaches in MDL haha. MDL is lazy evaluated, and the idea that a symbol can change value half-way through execution doesn't go along well at all with lazy evaluation! So, I had to make some sort of a "hack" and mark some symbols as "eager", which are resolved right away when found. The problem is that since they have to be resolved right away, their value can only depend on symbols defined *before*.
I believe Glass is also at least partially lazy evaluated (or it seems that way), right?
Yes my eventual goal is for Glass to be a complete functional language with lazy evaluation, although it’s not there yet. Finding the right basis for functional evaluation is difficult.
In the functional paradigm re-assignable symbols are not a good fit. Where it has good use in an imperative execution flow, and can be used quite cleverly, there will be an ever cleverer functional alternative .
It would be possible to do a “light” version of redefinition where it essentially creates a new symbol which shadows the old one for code past the reassignment line, but it makes the scoping more complex, and since it would never change the value of something that was defined before it, I don’t think it’s very useful to have. (You can shadow symbols in an inner scope btw.)
But, pgimeno’s comment was not about the reassignment of course . Thanks for sharing, it’s a useful idea to consider having something intuitive to force evaluation to a number type. Maybe a type specification / conversion would do the trick (mock-up:
Stuff: equ Integer (1 + 2)
). It won’t prevent those who are not aware of it from shooting themselves in the foot though.
Actually, the above code is giving me a stack overflow error unless I add an org
other than 0. Any idea why? Here's a simplified test case:
Start: ds 1 ; x Finish: span equ Finish-Start dw span
Error:
Exception in thread "main" java.lang.StackOverflowError at java.util.HashMap.hash(HashMap.java:339) at java.util.HashMap.get(HashMap.java:557) at nl.grauw.glass.Scope.getLocalSymbolOrNull(Scope.java:94) at nl.grauw.glass.Scope.getSymbol(Scope.java:74) at nl.grauw.glass.expressions.Identifier.resolve(Identifier.java:27) at nl.grauw.glass.expressions.Passthrough.is(Passthrough.java:9) at nl.grauw.glass.expressions.BinaryOperator.is(BinaryOperator.java:25) at nl.grauw.glass.expressions.Subtract.is(Subtract.java:31) at nl.grauw.glass.expressions.Passthrough.is(Passthrough.java:9) at nl.grauw.glass.expressions.Group.is(Group.java:27)
with the latest 5 lines repeating until the stack overflows. Replacing Finish:
with Finish equ $
fixes it.
If you know you want an integer, you can always do this, which is how I've solved a similar ambiguity in Pasmo:
Stuff equ +(1+2)
Labels associate with the next statement. So span and finish are both set to the equ, which makes it recursive. Your fix is the correct one.
As for the mutable labels, yes, that's a separate topic. I think they make things easier to the programmer. Having to work around their lack by outsmarting the assembler isn't what such a tool is supposed to do, which is to simplify your task, and not to get in your way.
Pasmo supports mutable labels with the defl directive. I've used them to allocate two regions of BSS space, because Pasmo does not support BSS (nor segments at all). See the hidata, lodata, word, byte macros here:
https://notabug.org/pgimeno/vdptest/src/master/vdptest.asm
Example usage:
https://notabug.org/pgimeno/vdptest/src/master/test-vram-tim...
(That's also an example of why Pasmo's way of handling the org directive is useful too)
Labels associate with the next statement. So span and finish are both set to the equ, which makes it recursive. Your fix is the correct one.
Oh, I see. I'm too used to the colon being basically a shortcut for equ $
(which is also an x86 MASM trait).
Interesting, never looked at it that way. I always found the “optional” colon confusing. My assembler background is WBASS, then Gen80, then Compass. Never used M80, although I have read the manual as part of my research.
I made the change to associate all labels with the following statement last year. The real goal of that change was so that I did not have to put mnemonics on the same line as the labels to be able to access their inner scope.
I forgot exactly why I decided to associate all prior unbound labels rather than only the preceding one (and apparently didn’t document it in the commit). There must’ve been some reason, maybe a technical one. I think I probably wanted to get rid of lines with an “empty” statement.
Yeah, I also understood the colon in the same way as pgimeno! I always thought if there is a colon, the thing on the left is a label. So, it takes the value "$" by default, unless you specify a different one with equ.
Just out of curiosity (I just want to understand the inner workings a bit better ). Imagine that you have this:
mylabel: include "auxiliar.asm" endofrom:
What would mylabel and endofrom resolve to? would mylabel depend on the first line of "auxiliar.asm" or would it take "$"? Also, how about endofrom? if there is nothing after, would it take "$"?
Edit: Finally, you mentioned you find optional colons confusing. So, are you suggesting colons should NOT be present, or that they should be mandatory? I find them quite useful to disambiguate the syntax in some corner cases. So, I lean on the "they should be mandatory" side
mylabel: include "auxiliar.asm" endofrom:
What would mylabel and endofrom resolve to? would mylabel depend on the first line of "auxiliar.asm" or would it take "$"?
It is associated with the include statement itself.
Also, how about endofrom? if there is nothing after, would it take "$"?
It associates with that END statement the assembler inserts at the end of each file, which you encountered earlier.
You may debate whether it is a good choice, but at least it is consistent .
Edit: Finally, you mentioned you find optional colons confusing. So, are you suggesting colons should NOT be present, or that they should be mandatory? I find them quite useful to disambiguate the syntax in some corner cases. So, I lean on the "they should be mandatory" side
If you ask me colons should be mandatory. Without colon the only way to distinguish labels from statements is indentation. Because Glass does support labels without colon, as a result statements can not be at the start of the line, which some people dislike but there’s not much I can do about it without making colons mandatory for labels. It also makes the syntax less regular.