1     Third time's the charm. First time, I might have...
     
2     ...fallen asleep while stepping through the program.
     
3     The second time, I got to the end to discover that
     
4     I'd either missed a line of instruction or accidentally
     
5     deleted it. Either way, I was missing the crucial
     
6     instruction to add the parsed digit to the total
     
7     in the NUMBER word.
     
8 
     9     And in between each of those attempts were sleepy
    
10     nights when I just booted up, read some source,
    
11     poked around in the debugger, and then logged off
    
12     again.
    
13 
    14     These things ebb and flow.
    
15 
    16     But if you just keep coming back to them day after
    
17     day, they DO get completed. I'm increasingly able
    
18     to make use of this obvious truth.
    
19 
    20     Anyway, parsing literal numbers in the interpreter.
    
21 
    22     Note the '42' below is me typing at the Forth
    
23     prompt (via STDIN):
    
24 
    25 Reading symbols from nasmjf...
    
26 (gdb) break _NUMBER
    
27 Breakpoint 3 at 0x80491ac: file nasmjf.asm, line 407.
    
28 (gdb) c
    
29 Continuing.
    
30 42
    
31 
    32 Breakpoint 3, _NUMBER () at nasmjf.asm:407
    
33 407         xor eax,eax
    
34 408         xor ebx,ebx
    
35 410         test ecx,ecx            ; trying to parse a zero-length string is an error, but returns
    
36 411         jz .return
    
37 
    38     GDB prints the _next_ line of source to be executed
    
39     after a step. So below, I'm checking the BASE (radix)
    
40     for number parsing right after line 413 executes.
    
41 
    42     As we can see, base 10 is the default.
    
43 
    44 413         mov edx, [var_BASE]    ; get BASE (in dl)
    
45 416         mov bl,[edi]            ; bl = first character in string
    
46 (gdb) p $dl
    
47 $1 = 10
    
48 417         inc edi
    
49 
    50     And now let's examine the first character of input from
    
51     the "42" I typed. Yup, it's a '4'.
    
52 
    53 (gdb) p/c $bl
    
54 $2 = 52 '4'
    
55 
    56     Now we convert the ASCII char '4' to the actual
    
57     value 4 using the base.
    
58 
    59 418         push eax                ; push 0 on stack
    
60 _NUMBER () at nasmjf.asm:419
    
61 419         cmp bl,'-'              ; negative number?
    
62 420         jnz .convert_char
    
63 _NUMBER.convert_char () at nasmjf.asm:435
    
64 435         sub bl,'0'              ; < '0'?
    
65 436         jb .negate
    
66 437         cmp bl,10        ; <= '9'?
    
67 438         jb .compare_base
    
68 _NUMBER.compare_base () at nasmjf.asm:444
    
69 444             cmp bl,dl               ; >= BASE?
    
70 445         jge .negate
    
71 
    72     This line is the most crucial of all. It's where we
    
73     add the current digit's value to the total. This is
    
74     the line I was missing. :-O
    
75 
    76 448         add eax,ebx
    
77 (gdb) p $ebx
    
78 $3 = 4
    
79 449         dec ecx
    
80 450         jnz 1b
    
81 
    82     Each new digit means the previous sum is another place
    
83     value higher. So we mutiply our accumulated value by
    
84     the base.
    
85 
    86 _NUMBER.next_char () at nasmjf.asm:430
    
87 430         imul eax,edx           ; eax *= BASE
    
88 431         mov bl,[edi]           ; bl = next character in string
    
89 (gdb) p $eax
    
90 $4 = 40
    
91 
    92     Then the whole thing repeats for the '2' character.
    
93 
    94 432         inc edi
    
95 (gdb) p/c $bl
    
96 $5 = 50 '2'
    
97 _NUMBER.convert_char () at nasmjf.asm:435
    
98 435         sub bl,'0'              ; < '0'?
    
99 436         jb .negate
   
100 437         cmp bl,10        ; <= '9'?
   
101 438         jb .compare_base
   
102 _NUMBER.compare_base () at nasmjf.asm:444
   
103 444             cmp bl,dl               ; >= BASE?
   
104 445         jge .negate
   
105 448         add eax,ebx
   
106 449         dec ecx
   
107 
   108     Do we have the correct total? Yes! 42 is correct.
   
109     NUMBER returns with the value in eax.
   
110 
   111 (gdb) p $eax
   
112 $6 = 42
   
113 (gdb) s
   
114 450         jnz 1b
   
115 _NUMBER.negate () at nasmjf.asm:453
   
116 453         pop ebx
   
117 _NUMBER.negate () at nasmjf.asm:454
   
118 454         test ebx,ebx
   
119 455         jz .return
   
120 _NUMBER.return () at nasmjf.asm:459
   
121 459         ret
   
122 
   123     Back in INTERPRET now, we check to see if NUMBER was
   
124     successful. And it was (ecx is zero).
   
125 
   126     We are currently in "immediate mode" (STATE is zero), which
   
127     means that the word is executed as soon as it's entered.
   
128     However, you can see that the address of the LIT word is being
   
129     saved in eax so that if we were in "compiling mode", the
   
130     interpreter would have it available.
   
131 
   132 code_INTERPRET.try_literal () at nasmjf.asm:232
   
133 232         test ecx,ecx
   
134 233         jnz .parse_error
   
135 234         mov ebx,eax
   
136 235         mov eax,LIT             ; The word is now LIT
   
137 code_INTERPRET.check_state () at nasmjf.asm:238
   
138 238         mov edx,[var_STATE]
   
139 239         test edx,edx
   
140 240         jz .execute             ; Jump if executing.
   
141 code_INTERPRET.execute () at nasmjf.asm:253
   
142 253         mov ecx,[interpret_is_lit] ; Literal?
   
143 254         test ecx,ecx               ; Literal?
   
144 255         jnz .do_literal
   
145 
   146     And how do we immediately "execute" the provided
   
147     numerical literal value in Forth? By pushing it on
   
148     the stack!
   
149 
   150 code_INTERPRET.do_literal () at nasmjf.asm:262
   
151 262         push ebx
   
152 
   153     So that works. :-)