colorful rat Ratfactor.com > Dave's Repos

nasmjf

A NASM assembler port of JONESFORTH
git clone http://ratfactor.com/repos/nasmjf/nasmjf.git

nasmjf/devlog/log05.txt

Download raw file: devlog/log05.txt

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. :-)