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/log17.txt

Download raw file: devlog/log17.txt

1 Now we come to a bit of a challenge. The method of 2 starting JonesFORTH with the rest of the language 3 implementation *and* still allowing user input on 4 STDIN is quite clever: 5 6 cat jonesforth.f - | ./jonesforth 7 8 That is, have cat read the FORTH source file and then 9 whatever is supplied on STDIN (using the standard 10 '-' parameter to indicate STDIN in place of a file) 11 and pipe the lot to the jonesforth executable. 12 13 Unfortunately, it crashes my port: 14 15 eeepc:~/nasmjf$ cat jonesforth/jonesforth.f - | ./nasmjf 16 17 Segmentation fault 18 19 So we have a problem. 20 21 And this is turning out to be hard to test. 22 23 Well, it seems there are a couple routes I could take 24 here: 25 26 1. Hand-type the contents of jonesforth.f and see what 27 causes the crash. I'm okay with the effort - I plan 28 to go through that source line-by-line to understand 29 it anyway. But the problem is: how do I incrementally 30 save what I've typed so far? 31 32 2. Use divide-and-conquer to find out how much of 33 'jonesforth.f' I can run before we get a crash. That 34 would probably work - but it's going to be an annoying 35 process and I'll be working blind. 36 37 3. Figure out how to read in 'jonesforth.f' on startup 38 as part of the executable. This will allow debugging 39 in GDB to continue (no doubt I could figure out some 40 arcane way of redirecting or piping that to STDIN 41 while in a GDB session or attach to the process or 42 something...but I'm not enthusiastic about figuring 43 that out. I'd rather write code.) 44 45 As you can likely guess, I'm going with Option 3. I'm going 46 to have the nasmjf executabe read 'jonesforth.f' upon 47 startup. The plan is something like this: 48 49 A. Open the file using syscalls 50 B. Read into a fixed-size reserved buffer 51 C. Keep track of line numbers for reference? 52 D. Feed the source into the interpreter somehow 53 E. Now I can debug the crash? 54 F. When EOF reached, close the file and start accepting 55 interpreter input from STDIN again. 56 57 The only challenging part, it seems to me, is D: feeding the 58 file into the interpreter. Words like KEY are currently 59 hard-coded to read from STDIN, right? So maybe I need a global 60 switch to read from my line buffer instead? 61 62 Let's get started! 63 64 For A, I've got this near the top: 65 66 SECTION .bss 67 line_buffer: resb 1024 ; for storing source lines from file 68 69 Then 70 71 SECTION .data 72 jfsource: db "jonesforth/jonesforth.f", 0h 73 74 And then reading the file - another snippet adapted from 75 asmtutor.com! 76 77 78 Breakpoint 1, _start () at nasmjf.asm:82 79 82 cld ; Clear the "direction flag" which means the string instructions (such 80 ... 81 118 mov ecx, 0 ; read only flag for open 82 119 mov ebx, jfsource 83 120 mov eax, __NR_open 84 121 int 80h ; fd now in eax 85 124 mov edx, 1024 ; amt to read 86 125 mov ecx, line_buffer ; address of .bss reserved space 87 126 mov ebx, eax ; fd from pushed eax above 88 127 mov eax, __NR_read 89 128 int 80h 90 (gdb) x/s (int)&line_buffer 91 0x804db30 <line_buffer>: "\\ -*- text -*-\n\\\tA sometimes minimal FORTH 92 compiler and tutorial for Linux / i386 systems. -*- asm -*-\n\\\tBy Richard W.M. 93 Jones <rich@annexia.org> http://annexia.org/forth\n\\\tThis is PUBLIC DOMAIN"... 94 95 Awesome! Of course it works. It's a trivial operation, even 96 in assembly. 97 98 Anyway, now to figure out how to feed the source into the 99 interpreter. 100 101 Weeks have passed. Progress has gone at a snail's pace and 102 I've often fallen asleep with barely a single assembly 103 instruction written. 104 105 Nevertheless, I think I'm getting close. 106 107 TIME PASSES... 108 109 Okay, a month later and I've got it reading a test.f file 110 to make sure I can read in N lines of a Forth source file 111 when the interpreter starts and then hand input back to 112 the keyboard. 113 114 Here's the entirety of test.f: 115 116 1 . 117 2 . 118 3 . 119 4 . 120 5 . 121 6 . 122 7 . 123 8 . 124 125 So it's just Forth for printing the line number on each line 126 so I can see if it correctly stops after six lines. 127 128 jfsource: db "test.f" , 0h 129 %assign __lines_of_jf_to_read 6 130 131 What does it look like when it runs? Let's find out: 132 133 123456 134 135 Yay! It reads and executes six lines, then waits for user input. 136 Perfect! 137 138 This took about a month because of a ton of stupid bugs, off-by- 139 one errors, and that sort of thing. But mostly because I was 140 really tired and kept falling asleep. 141 142 Anyway, now I can read N lines of jonesforth.f and test it out 143 a bit at a time! 144 145 Next night: okay, here goes nothing: 146 147 jfsource: db "jonesforth/jonesforth.f", 0h 148 %assign __lines_of_jf_to_read 52 149 150 This reads to line 52 of jonesforth.f, which is a bunch of comments 151 and a definiton of / and MOD: 152 153 : / /MOD SWAP DROP ; 154 : MOD /MOD DROP ; 155 156 10 5 / . 157 2 158 10 5 MOD . 159 0 160 10 3 / . 161 3 162 10 3 MOD . 163 1 164 165 Wow, that actually works! So now I can start logging my 166 progress as I load and test more of the FORTH portion of 167 the JonesFORTH implementation. 168 169 Now I'm loading to line 70, which contains definitions for 170 some more handy primitives. 171 172 I'll test them out now: 173 174 3 5 . SPACE . 175 5 3 176 177 3 5 . CR . 178 5 179 3 180 181 3 NEGATE . 182 4294967293 183 184 TRUE . 185 1 186 FALSE . 187 0 188 TRUE NOT . 189 0 190 FALSE NOT . 191 1 192 193 Heh, I'll just trust at the negaative value is correct for 194 now. 195 196 So, this is great. I'll just keep working my way down 197 jonesforth.f until I've tested everything and/or reproduced 198 the crash I was experiencing before.