Dave's nasmjf Dev Log 11
Created: 2022-07-22
This is an entry in my developer’s log series written between December 2021 and August 2022 (started project in September). I wrote these as I completed my port of the JONESFORTH assembly language Forth interpreter.
I find it hard to believe that I've discovered a
bug in JonesFORTH, but it seems to me that my fix
of simply removing FETCH from colon so that the
word header that receives the HIDDEN flag is the
one being compiled, not the previous one in the
linked list (dictionary).
!!!!!!!!!!!!!!!!!!!! Update !!!!!!!!!!!!!!!!!!!!
! In log19.txt, I realize that my variable !
! handling is wrong. Variables should leave !
! their addresses on the stack, not their !
! values! We need FETCH to get the value from !
! the address! !
!!!!!!!!!!!!!!!!!!!! Update !!!!!!!!!!!!!!!!!!!!
So unless I discover otherwise, I've changed it
from
dd LATEST, FETCH, HIDDEN ; Make the word hidden while it's being compiled.
to
dd LATEST, HIDDEN ; Make the word hidden while it's being compiled.
(gdb) break code_LATEST
Breakpoint 2 at 0x804939d: file nasmjf.asm, line 772.
(gdb) c
Continuing.
: FIVE 5 ;
Breakpoint 2, code_LATEST () at nasmjf.asm:772
772 push dword [var_%4]
27 lodsd ; NEXT: Load from memory into eax, inc esi to point to next word.
28 jmp [eax] ; Jump to whatever code we're now pointing at.
code_HIDDEN () at nasmjf.asm:636
636 pop edi ; Dictionary entry, first byte is link
637 add edi, 4 ; Move to name/flags byte.
If my fix works, then the word "FIVE" that is being compiled
will be the one we're hiding until compilation is complete:
(gdb) x/bx $edi
0x804e004: 0x04
(gdb) x/bt $edi
0x804e004: 00000100
(gdb) x/4c $edi+1
0x804e005: 70 'F' 73 'I' 86 'V' 69 'E'
Yup, looks good! And the hidden flag?
638 xor [edi], word F_HIDDEN ; Toggle the HIDDEN bit in place.
(gdb) x/bt $edi
0x804e004: 00100100
Looks good also.
Next, let's implement BRANCH!
Turns out, it's another crazy simple "hack" with
the esi register (like LIT), this time, adding an
offset to esi itself to change which word NEXT
should execute next.
(gdb) break code_BRANCH
Breakpoint 2 at 0x804904e: file nasmjf.asm, line 237.
(gdb) c
Continuing.
: FIVE 5 ;
Breakpoint 2, code_BRANCH () at nasmjf.asm:237
237 add esi, [esi] ; add the offset to the instruction pointer
The line above is the entirety of BRANCH!
So before that line executes, esi should point
to the next "word" in the definition of QUIT, which
is actually not a word at all, but an offset to
add to the current value of esi itself.
So we should see QUIT + <some offset> in esi
(gdb) info sym $esi
QUIT + 20 in section .data of /home/dave/nasmjf/nasmjf
And the value at esi should be our negative offset, -8.
(gdb) p *$esi
$2 = -8
What will -8 branch to? Here's the entire definition
of QUIT:
DEFWORD "QUIT",4,0,QUIT
dd R0 ; push R0 (addr of top of return stack)
dd RSPSTORE ; store R0 in return stack pointer (ebp)
dd INTERPRET ; interpret the next word
dd BRANCH,-8 ; and loop (indefinitely)
esi was pointing at the "double" (4 bytes) containing
the value -8. Every word linked here is also 4 bytes.
esi points to -8 (QUIT + 20)
esi-4 points to BRANCH (QUIT + 16)
esi-8 points to INTERPRET (QUIT + 12)
So BRANCH here makes a conditionless loop over INTERPRET.
(gdb) info sym $esi
QUIT + 12 in section .data of /home/dave/nasmjf/nasmjf
27 lodsd ; NEXT: Load from memory into eax, inc esi to point to next word.
28 jmp [eax] ; Jump to whatever code we're now pointing at.
code_INTERPRET () at nasmjf.asm:244
244 call _WORD ; Returns %ecx = length, %edi = pointer to word.
This is getting exciting. So we shoud be able to compile
a word and then execute it. Let's see if my "FIVE" literal
works:
(gdb) c
Continuing.
FIVE
PARSE ERROR: FIVE
Oh no! It didn't find FIVE. So something in the
compilation of ": FIVE 5 ;" isn't quite right yet.
Wait a dang second.
I've removed the FETCH so it's no longer setting the
wrong word header to hidden during compilation. But
I didn't make the same change in SEMICOLON (;) to
unhide the correct word!
dd LATEST, FETCH, HIDDEN ; Unhide word now that it's been compiled.
Removing the FETCH so we have
dd LATEST, HIDDEN ; Unhide word now that it's been compiled.
and let's see what happens.
(gdb) break code_INTERPRET
Breakpoint 2, code_INTERPRET () at nasmjf.asm:244
(gdb) c
Continuing.
: FIVE 5 ;
Breakpoint 2, code_INTERPRET () at nasmjf.asm:244
(gdb) c
Continuing.
FIVE
Breakpoint 2, code_INTERPRET () at nasmjf.asm:244
(gdb) p/x $esp
$3 = 0xbffff98c
(gdb) x/x $esp
0xbffff98c: 0x00000005
Using my compiled word FIVE put a 5 on the stack!
It works!
I think I've earned a bit of fun, so the next thing
I'll implement is EMIT, which will let me actually
display characters on the screen!