I have few detail at the moment but the current z88dk version seems to encounter problems with the module functionality ('%' operator).
Code just becomes unstable and application crashes, try computing, say, 'srand()%5'.
The ZX Spectrum target shows also the following warning:
Warning at file 'crt0/l_div.asm' line 66: integer '4295038788' out of range
strange behavior of the 'module' functionality
Problem is still in, it seems to have to do with l_div / l_div_u in general, could it be connected to the new z80asm expression evaluation logics ?
XLIB l_div_u
XDEF L_DIVENTRY
.l_div_u
(...)
DEFC L_DIVENTRY = entry - l_div_u
-- -- -- --
XLIB l_div
LIB l_div_u
XREF L_DIVENTRY
IF !ARCHAIC
.l_div
(...)
call l_div_u + L_DIVENTRY ; unsigned divide but skip zero check
....this latest line is #66
XLIB l_div_u
XDEF L_DIVENTRY
.l_div_u
(...)
DEFC L_DIVENTRY = entry - l_div_u
-- -- -- --
XLIB l_div
LIB l_div_u
XREF L_DIVENTRY
IF !ARCHAIC
.l_div
(...)
call l_div_u + L_DIVENTRY ; unsigned divide but skip zero check
....this latest line is #66
Is this with the latest changes to z80asm? It looks like a z80asm bug with defc. "DEFC L_DIVENTRY = entry - l_div_u" should result in a small positive constant and " l_div_u + L_DIVENTRY" should not evaluate to a large positive number or a small negative one.
The mod operator that is in there now is incorrect -- the remainder in signed division should take the sign of the dividend and it's being given the sign of the divisor instead. I can copy the division code in the new lib over to fix that but I'll wait until Paulo can have a look.
The mod operator that is in there now is incorrect -- the remainder in signed division should take the sign of the dividend and it's being given the sign of the divisor instead. I can copy the division code in the new lib over to fix that but I'll wait until Paulo can have a look.
I have found the problem in z80asm, but the solution requires a complete rewrite of the expression evaluator.
A work around is available since the original (before I started maintaining it) assembler: the '#' operator.
The code works correctly if the '#' (transform address into constant) is used:
Explanation:
The expression (entry - l_div_u) is tagged by the expression evaluator as relocatable and stored in the object file with the correct value (4) but with the 'A'ddress tag. At link time, the value (4) is added to the base address of the module to compute a nonsense value.
To solve this, the expression evaluator needs to recognize that a subtraction of two labels of the same section in the same module is a constant and not a relocatable address, and has to be stored in the object file tagged as 'C'onstant. To be able to do this, the expression evaluator needs to build the whole parse tree of the expression, and check the address tag on each of the subtraction operands.
Today the expression evaluator only keeps a global address flag for the whole expression, and keeps the operators and operands in a stack, with no chance to check the type of each element.
A work around is available since the original (before I started maintaining it) assembler: the '#' operator.
The code works correctly if the '#' (transform address into constant) is used:
Code: Select all
DEFC L_DIVENTRY = # entry - l_div_u
The expression (entry - l_div_u) is tagged by the expression evaluator as relocatable and stored in the object file with the correct value (4) but with the 'A'ddress tag. At link time, the value (4) is added to the base address of the module to compute a nonsense value.
To solve this, the expression evaluator needs to recognize that a subtraction of two labels of the same section in the same module is a constant and not a relocatable address, and has to be stored in the object file tagged as 'C'onstant. To be able to do this, the expression evaluator needs to build the whole parse tree of the expression, and check the address tag on each of the subtraction operands.
Today the expression evaluator only keeps a global address flag for the whole expression, and keeps the operators and operands in a stack, with no chance to check the type of each element.
Thinking back to this topic, I'm wondering if it is really z80asm needing a fix.
As far as I can recall this pre-computed offset was introduced by Alvin for the 'callee' stuff.
It was a clever way to overcome to the z80asm limits which was permitting to declare (and refer to) only one single 'public' address per module.
Perhaps if we find a cleaner way to get to the same result..
As far as I can recall this pre-computed offset was introduced by Alvin for the 'callee' stuff.
It was a clever way to overcome to the z80asm limits which was permitting to declare (and refer to) only one single 'public' address per module.
Perhaps if we find a cleaner way to get to the same result..
It's the recent changes to z80asm. defc has been modified to be able to hold a relocatable value resolved at link time, like regular labels. The old defc only allowed you to define constants that needed to be resolved right away within the source file.stefano wrote:Thinking back to this topic, I'm wondering if it is really z80asm needing a fix.
As far as I can recall this pre-computed offset was introduced by Alvin for the 'callee' stuff.
The changes allow aliases to be defined:
====
PUBLIC array_size
array_size:
....
ret
====
PUBLIC vector_size
EXTERN array_size
defc vector_size = array_size
====
Now "vector_size" can be defined as an alias to array_size without using any memory. We used to have to place a jump to array_size inside vector_size, which took 3 bytes and 10 cycles.
That's the change that inadvertently broke the offset calculation.
That was the old way.. the new way is we're allowed to have multiple exported global symbols from the same source file (just declare all the entry points you want as PUBLIC). When we could only have one PUBLIC symbol, the offset trick allowed other entry points to be defined as the symbol + constant offset in source code. It only affects legacy code but there are quite a few places where the offset trick is used and Paulo has already fixed them by prefixing the defcs with '#' to force the subtractions to be classified as constants.It was a clever way to overcome to the z80asm limits which was permitting to declare (and refer to) only one single 'public' address per module.
Perhaps if we find a cleaner way to get to the same result..
So we're good, better if we abandon the old and adopt the new cleaner methods.