1 I find it hard to believe that I've discovered a
2 bug in JonesFORTH, but it seems to me that my fix
3 of simply removing FETCH from colon so that the
4 word header that receives the HIDDEN flag is the
5 one being compiled, not the previous one in the
6 linked list (dictionary).
7
8 !!!!!!!!!!!!!!!!!!!! Update !!!!!!!!!!!!!!!!!!!!
9 ! In log19.txt, I realize that my variable !
10 ! handling is wrong. Variables should leave !
11 ! their addresses on the stack, not their !
12 ! values! We need FETCH to get the value from !
13 ! the address! !
14 !!!!!!!!!!!!!!!!!!!! Update !!!!!!!!!!!!!!!!!!!!
15
16
17 So unless I discover otherwise, I've changed it
18 from
19
20
21 dd LATEST, FETCH, HIDDEN ; Make the word hidden while it's being compiled.
22
23 to
24
25 dd LATEST, HIDDEN ; Make the word hidden while it's being compiled.
26
27 (gdb) break code_LATEST
28 Breakpoint 2 at 0x804939d: file nasmjf.asm, line 772.
29 (gdb) c
30 Continuing.
31 : FIVE 5 ;
32
33 Breakpoint 2, code_LATEST () at nasmjf.asm:772
34 772 push dword [var_%4]
35 27 lodsd ; NEXT: Load from memory into eax, inc esi to point to next word.
36 28 jmp [eax] ; Jump to whatever code we're now pointing at.
37 code_HIDDEN () at nasmjf.asm:636
38 636 pop edi ; Dictionary entry, first byte is link
39 637 add edi, 4 ; Move to name/flags byte.
40
41 If my fix works, then the word "FIVE" that is being compiled
42 will be the one we're hiding until compilation is complete:
43
44 (gdb) x/bx $edi
45 0x804e004: 0x04
46 (gdb) x/bt $edi
47 0x804e004: 00000100
48 (gdb) x/4c $edi+1
49 0x804e005: 70 'F' 73 'I' 86 'V' 69 'E'
50
51 Yup, looks good! And the hidden flag?
52
53 638 xor [edi], word F_HIDDEN ; Toggle the HIDDEN bit in place.
54 (gdb) x/bt $edi
55 0x804e004: 00100100
56
57 Looks good also.
58
59 Next, let's implement BRANCH!
60 Turns out, it's another crazy simple "hack" with
61 the esi register (like LIT), this time, adding an
62 offset to esi itself to change which word NEXT
63 should execute next.
64
65 (gdb) break code_BRANCH
66 Breakpoint 2 at 0x804904e: file nasmjf.asm, line 237.
67 (gdb) c
68 Continuing.
69 : FIVE 5 ;
70
71 Breakpoint 2, code_BRANCH () at nasmjf.asm:237
72 237 add esi, [esi] ; add the offset to the instruction pointer
73
74 The line above is the entirety of BRANCH!
75 So before that line executes, esi should point
76 to the next "word" in the definition of QUIT, which
77 is actually not a word at all, but an offset to
78 add to the current value of esi itself.
79
80 So we should see QUIT + <some offset> in esi
81
82 (gdb) info sym $esi
83 QUIT + 20 in section .data of /home/dave/nasmjf/nasmjf
84
85 And the value at esi should be our negative offset, -8.
86
87 (gdb) p *$esi
88 $2 = -8
89
90 What will -8 branch to? Here's the entire definition
91 of QUIT:
92
93 DEFWORD "QUIT",4,0,QUIT
94 dd R0 ; push R0 (addr of top of return stack)
95 dd RSPSTORE ; store R0 in return stack pointer (ebp)
96 dd INTERPRET ; interpret the next word
97 dd BRANCH,-8 ; and loop (indefinitely)
98
99 esi was pointing at the "double" (4 bytes) containing
100 the value -8. Every word linked here is also 4 bytes.
101
102 esi points to -8 (QUIT + 20)
103 esi-4 points to BRANCH (QUIT + 16)
104 esi-8 points to INTERPRET (QUIT + 12)
105
106 So BRANCH here makes a conditionless loop over INTERPRET.
107
108 (gdb) info sym $esi
109 QUIT + 12 in section .data of /home/dave/nasmjf/nasmjf
110 27 lodsd ; NEXT: Load from memory into eax, inc esi to point to next word.
111 28 jmp [eax] ; Jump to whatever code we're now pointing at.
112 code_INTERPRET () at nasmjf.asm:244
113 244 call _WORD ; Returns %ecx = length, %edi = pointer to word.
114
115 This is getting exciting. So we shoud be able to compile
116 a word and then execute it. Let's see if my "FIVE" literal
117 works:
118
119 (gdb) c
120 Continuing.
121 FIVE
122 PARSE ERROR: FIVE
123
124 Oh no! It didn't find FIVE. So something in the
125 compilation of ": FIVE 5 ;" isn't quite right yet.
126
127 Wait a dang second.
128
129 I've removed the FETCH so it's no longer setting the
130 wrong word header to hidden during compilation. But
131 I didn't make the same change in SEMICOLON (;) to
132 unhide the correct word!
133
134 dd LATEST, FETCH, HIDDEN ; Unhide word now that it's been compiled.
135
136 Removing the FETCH so we have
137
138 dd LATEST, HIDDEN ; Unhide word now that it's been compiled.
139
140 and let's see what happens.
141
142 (gdb) break code_INTERPRET
143 Breakpoint 2, code_INTERPRET () at nasmjf.asm:244
144 (gdb) c
145 Continuing.
146 : FIVE 5 ;
147
148 Breakpoint 2, code_INTERPRET () at nasmjf.asm:244
149 (gdb) c
150 Continuing.
151 FIVE
152
153 Breakpoint 2, code_INTERPRET () at nasmjf.asm:244
154 (gdb) p/x $esp
155 $3 = 0xbffff98c
156 (gdb) x/x $esp
157 0xbffff98c: 0x00000005
158
159 Using my compiled word FIVE put a 5 on the stack!
160 It works!
161
162 I think I've earned a bit of fun, so the next thing
163 I'll implement is EMIT, which will let me actually
164 display characters on the screen!