One of my goals is to be able to write platform-specific routines in native Z80 assembler instead of hand assembly or separate assembly, then inserting the bytes as DB lines in the code.
Before I show how it's accomplished, let me present the Z80 source for a couple of Forth words:
; PC! ( uc p -- ) ; output uc to IO port p ;! $CODE 3,'PC!',PCSTO EXX ; 4t POP BC ;10t POP DE ;10t LD A, E ; 4t OUT (C), A ;12t EXX ; 4t ;!DB 0C3h ;!DW NextStep ; JP NextStep ;10t ; ;54t==(10MHz)5.4usec ; PC@ ( p -- uc ) ; input uc from IO port p ;! $CODE 3,'PC@',PCAT EXX ; 4t POP BC ;10t IN A, (C) ;12t LD E, A ; 4t LD D, 0 ; 7t PUSH DE ;11t EXX ; 4t ;!DB 0C3h ;!DW NextStep ; JP NextStep ;10t ; ;62t==(10MHz)6.2usec
Now the transformed source:
; PC! ( uc p -- ) ; output uc to IO port p $CODE 3,'PC!',PCSTO DB 0D9h ;EXX ; 4t DB 0C1h ;POP BC ;10t DB 0D1h ;POP DE ;10t DB 7Bh ;LD A, E ; 4t DB 0EDh,79h ;OUT (C), A ;12t DB 0D9h ;EXX ; 4t DB 0C3h DW NextStep ; JP NextStep ;10t ; ;54t==(10MHz)5.4usec ; PC@ ( p -- uc ) ; input uc from IO port p $CODE 3,'PC@',PCAT DB 0D9h ;EXX ; 4t DB 0C1h ;POP BC ;10t DB 0EDh,78h ;IN A, (C) ;12t DB 5Fh ;LD E, A ; 4t DB 16h,00h ;LD D, 0 ; 7t DB 0D5h ;PUSH DE ;11t DB 0D9h ;EXX ; 4t DB 0C3h DW NextStep ; JP NextStep ;10t ; ;62t==(10MHz)6.2usec
The transformation is done by a Python script, lsttoinc.py. It follow several rules:
- The .z80 file should be valid asz80 assembler input
- The script works in the .lst listing output of the assembly with asz80
- Lines starting with ;! are passed through with the ;! removed and go through to JWASM
- Lines starting with ; only are passed through unchanged as comments
- If opcodes resulted from a line, they are turned into DB declarations for JWASM and output with the original line as a comment. Otherwise they are inserted as comments.
However this scheme has its limitations, witness this piece of code:
; ?RX ( -- c T | F ) ; Return input character and true, or a false if no input. ;! $CODE 3,'?RX',QRX ;!DB 21h, 14h,0FEh ; LD HL, SiobRxQout ;16t LD A, (HL) ; 7t ;!DB 21h, 12h,0FEh ; LD HL, SiobRxQin ;16t CP (HL) ; 7t JR NZ,.+9 ; 7t/12t (NZ jump=12t) ptr <>, get the char ; can't use label because JP NextStep not accounted for LD HL, 0 ;10t False flag PUSH HL ;11t ;!DB 0C3h ;!DW NextStep ; JP NextStep ;10t ==> False Timing:84t==(10MHz)8.4usec ; ; LD E, (HL) ; 7t get this ptr LOW INC HL ; 6t LD D, (HL) ; 7t get this ptr HI EX DE,HL ; 4t let HL point the char LD E, (HL) ; 7t get the char LD D, 0 ; 7t high byte =0 PUSH DE ;11t INC L ; 4t ptr+1, a 256 byte Ring queue ;!DB 22h, 12h,0FEh ; LD (SiobRxQin),HL ;16t LD HL,0xFFFF ;10t PUSH HL ;11t ;!DB 0C3h ;!DW NextStep ; JP NextStep ;10t ==> TRUE case: 100t(10MHz)10.0usec
And the transformed code:
; ?RX ( -- c T | F ) ; Return input character and true, or a false if no input. $CODE 3,'?RX',QRX DB 21h, 14h,0FEh ; LD HL, SiobRxQout ;16t DB 7Eh ;LD A, (HL) ; 7t DB 21h, 12h,0FEh ; LD HL, SiobRxQin ;16t DB 0BEh ;CP (HL) ; 7t DB 20h,07h ;JR NZ,.+9 ; 7t/12t (NZ jump=12t) ptr <>, get the char ; can't use label because JP NextStep not accounted for DB 21h,00h,00h ;LD HL, 0 ;10t False flag DB 0E5h ;PUSH HL ;11t DB 0C3h DW NextStep ; JP NextStep ;10t ==> False Timing:84t==(10MHz)8.4usec ; ; DB 5Eh ;LD E, (HL) ; 7t get this ptr LOW DB 23h ;INC HL ; 6t DB 56h ;LD D, (HL) ; 7t get this ptr HI DB 0EBh ;EX DE,HL ; 4t let HL point the char DB 5Eh ;LD E, (HL) ; 7t get the char DB 16h,00h ;LD D, 0 ; 7t high byte =0 DB 0D5h ;PUSH DE ;11t DB 2Ch ;INC L ; 4t ptr+1, a 256 byte Ring queue DB 22h, 12h,0FEh ; LD (SiobRxQin),HL ;16t DB 21h,0FFh,0FFh ;LD HL,0xFFFF ;10t DB 0E5h ;PUSH HL ;11t DB 0C3h DW NextStep ; JP NextStep ;10t ==> TRUE case: 100t(10MHz)10.0usec
The instructions involving locations like SiobRxQout are inserted as DBs. To insert them as Z80 instructions, the symbols must be known to the Z80 assembler. We could declare them in an include file. If they are also used in the JWASM code then there is duplication and you have to make sure to keep the two defines in sync. That's probably not too bad as the locations are usually decided on once.
The more serious one is where the JP NextStep is inserted as DBs. Since the Z80 assembler doesn't see these instructions, it cannot calculate jump offsets correctly, hence the JP NZ,.+9 instead of JP NZ,label. Again to fix this we should define NextLabel to the Z80 assembler. This requires building it once, looking at where NextStep is and then editing the Z80 assembler define for NextStep.
The real problem is the use of ;! lines to pass lines through verbatim. These should be kept to a minimum. They are unavoidable in the case of the Forth word headers, e.g. the
$CODE 3,'?RX',QRX
Those are ok since one should not reference code outside of the current Forth word, i.e. jump into the code for another word.
I'll probably leave it as it is for a while to think it over before making these changes.
In the long run what really needs to be done is to rewrite the code so that it assembles with asz80 only which does have a macro facility. The limitations are due to using two assemblers. But this is a larger undertaking and probably should get a new directory.
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.
Thanks I'll apply that to locate the offending assembly line.
Are you sure? yes | no
Maybe I don't yet fully understand the approach - do you plan to keep Python in the tool chain or do you plan to do a one-time transformation?
Did you check if the macro features of the ASxxxx variant bundled with SDCC (or that of the stand-alone ASxxxx) meet your needs?
Are you sure? yes | no
As I mentioned in the last paragraph above, asz80 does have a macro facility so in the long term that should be the assembler to use, but I would conclude work on this one to start a new directory for the next version. It was strange that MASM was used but that's probably because in those days that was all the author had to hand.
I tried at the beginning to port the code to asz80 but it was tough going, and I even managed to make asz80 segfault. So I decided to first restructure the code (using includes, adding Z80 coding, creating equates) to make it more comprehensible to me without breaking anything. It had the benefit of showing me which parts I should isolate as system dependent and which are pure Forth. There are some magic numbers in the comments which are peripheral ports and should be turned into equates. I will make the changes I proposed above, push the code to Github, close off this experiment and clone to a new directory for a single assembler version. But that may take a while.
Are you sure? yes | no
asxxx macros, and segfaults: I had some troubles with that, too: https://sourceforge.net/p/sdcc/bugs/2626/
Are you sure? yes | no
I'm gradually converting all the DBs in the EFZ80.ASM file to real Z80 lines and discovered a handful of discrepancies betwen the hand inserted bytes and the purported assembler code. It looks like they got out of sync at some point. I don't know if anybody has managed to use the binary unless they had the exact same hardware as the author or managed to find the right places to patch. So this little diversion has been useful in cleaning up the code.
Are you sure? yes | no
Making a 8Bit eForth stable isn't easy. Hard coded MASM defines instead of assembly code surely doesn't help. Even the eForth 2.x (i.e. STC Forth) had some really nice bugs.
Are you sure? yes | no
Thanks, that's exactly the bug I hit. I'll have to see which macro definition or invocation is triggering it. Also if a newer asxxxx version has fixed it. The SDCC bug tracker wouldn't have got the attention of the asxxxx author and the SDCC devs don't care since they don't use assembler macros. If I can't work around it I'll have to detour into finding the asxxxx bug first.
Are you sure? yes | no
Check out the patch that I posted here: https://sourceforge.net/p/sdcc/bugs/2626/#fd60
This will, at least., return the line of the macro that triggers the segfault. I tried to solve the problem but the macro code is quite complex. Avoiding certain separator characters in macros ("," and ";" I think) avoids the problem.
What's also to be avoided is macros nearby includes.
Are you sure? yes | no