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

Download raw file: devlog/log23.txt

1 Well, now that I can actually run jonesforth.f with this 2 interpreter port, it "only" remains to test the rest of 3 the words defined in Forth. Then I've got some cleaning 4 up to do in the assembly. 5 6 It looks like I'm roughly halfway through testing what 7 jonesforth.f provides. So there's quite a bit left. 8 9 Next up is CASE OF...ENDOF... ENDCASE which lets you 10 do different things based on comparison with a value on 11 the stack: 12 13 JONESFORTH VERSION 1 14 20643 CELLS REMAINING 15 OK 16 17 : foo CASE 18 1 OF ." One" ENDOF 19 2 OF ." Two" ENDOF 20 3 OF ." Three" ENDOF 21 ENDCASE ; 22 23 1 foo 24 One 25 2 foo 26 Two 27 3 foo 28 Three 29 30 That is, dare I say, quite readable compared to a lot of 31 the Forth we've seen so far. 32 33 Next is CFA>, which is "the opposite of >CFA." Let's see 34 about that. I'll use >CFA to get the code word pointer 35 address from the LATEST word address, then get back to 36 the original word address with CFA> and see if it's right: 37 38 HEX 39 LATEST @ DUP . ID. 40 957ED74 foo 41 LATEST @ >CFA DUP . 42 957ED7C 43 CFA> DUP . ID. 44 957ED74 foo 45 46 Of course, we can do this all in one go as well: 47 48 LATEST @ >CFA CFA> ID. 49 foo 50 51 What's wild is that >CFA can just do a little address 52 math and skip forward to the codeword. But CFA> has to 53 search the entire dictionary for the correct word. 54 55 I feel like you should be able to scan backward to the 56 word start. But I can see how it would be hard to be 57 entirely sure it's accurate (you could use the length 58 field as a kind of "checksum" for the name field, but 59 that wouldn't be a *guarantee*). 60 61 Oooooh, the next one is bound to be a new favorite: SEE 62 decompiles a word! 63 64 SEE foo 65 : foo 1 OVER = 0BRANCH ( 20 ) DROP S" One" TELL BRANCH ( 74 ) 2 OVER = 0BRANCH 66 ( 20 ) DROP S" Two" TELL BRANCH ( 40 ) 3 OVER = 0BRANCH ( 24 ) DROP S" Three" 67 TELL BRANCH ( 8 ) DROP ; 68 69 That's awesome! I love how it prints the strings and 70 everthing. 71 72 The next thing are anonymous "no name" words. When you 73 define them with :NONAME, they leave the address of 74 their codeword on the stack. In Forth terminology, the 75 address of a codeword is an "execution token". In the 76 21st Century, we'd probably call them pointers. 77 78 Anyway, you can store an execution token like any other 79 value. And EXECUTE calls one: 80 81 :NONAME ." I have no name: " TELL CR ; 82 VALUE noname 83 noname . 84 160980384 85 ." Hello" noname EXECUTE 86 HelloI have no name: . 87 S" Hello" noname EXECUTE 88 I have no name: Hello 89 90 Next we have THROW and CATCH. I was not expecting 91 exception handling! 92 93 The THROW statement is easy to use - just give it a 94 number to throw as the exception. 95 96 But CATCH needs some very specific stuff: 97 98 : <word2> ['] <word1> CATCH IF ... THEN ; 99 100 The ['] primitive gets the execution token of a word 101 (:NONAME uses it). 102 103 : foo ." Foo! " 13 = IF ." Bah!" 5 THROW THEN ." Done." ; 104 12 foo 105 Foo! Done. 106 13 foo 107 Foo! Bah!UNCAUGHT THROW 5 108 109 : test-foo ['] foo CATCH ?DUP IF ." Foo threw " . DROP THEN ." Test done." ; 110 12 test-foo 111 Foo! Done. Test done. 112 13 test-foo 113 Foo! Bah! Foo threw 5 Test done. 114 115 i spent another night reading through the implementation 116 of THROW. It's funny how *using* CATCH and THROW is 117 inversely proportional to the complexity of those two 118 words. 119 120 This is the first time something really interesting is 121 done with the return stack. 122 123 This is also an excellent example of a language feature 124 that seems like more trouble than it's worth in a small 125 demonstration. But when you actually need a feature like 126 this in a decent-sized codebase, it will seem trivial 127 compared to the alternatives. 128 129 Having said that, reading an implementation like THROW 130 is just *nuts*. Cool how it works, though. 131 132 Next is another one that takes advantage of the return 133 stack: PRINT-STACK-TRACE. 134 135 : foo PRINT-STACK-TRACE ; 136 foo 137 foo+0 138 : bar foo ; 139 : baz bar ; 140 baz 141 foo+0 bar+0 baz+0 142 143 Beautiful. 144 145 Then JONESFORTH has support for reading the commandline 146 arguments, which is very cool. 147 148 It's easy enough to use ARGC and ARGV, but I'm getting 149 to the point where the usability of bare Linux input for 150 the Forth "REPL" is pretty painful. I must have re-typed 151 my BEGIN WHILE REPEAT loop a dozen times. 152 153 So I'm doing two things: 154 155 1. Using CAPSLOCK while typing Forth commands. 156 2. Installed "rlwrap" (Readline wrapper). 157 158 rlwrap is super awesome and easy to use. I'll be darned 159 if I can figure out why `apk search rlwrap` doesn't work 160 in Alpine Linux. It shows up as a community package when 161 I do a web search. 162 163 But whatever, that's what source is for: 164 165 $ doas apk add readline-dev 166 $ lynx github.com... 167 $ tar -xf rlwrap...tar.gz 168 $ cd rlwrap... 169 $ ./configure 170 $ make 171 $ doas make install 172 173 Now I've added this alias: 174 175 alias f='rlwrap /home/dave/nasmjf/nasmjf' 176 177 And what a relief! I can use arrow keys for editing and 178 history, etc. 179 180 eeepc:~/nasmjf$ f 181 JONESFORTH VERSION 1 182 20643 CELLS REMAINING 183 OK 184 ARGC . 185 1 186 0 ARGV TELL 187 /home/dave/nasmjf/nasmjf 188 189 Again, that's my alias 'f' providing the full path to 190 the nasmjf executable we're seeing there as the 0th 191 argument. 192 193 Now for the simple loop it took at least a dozen tries 194 to get right (often due to simple typos and some of them 195 causing nasmjf to segfault!) and now I am *so* grateful 196 that rlwrap gives me command history. 197 198 eeepc:~/nasmjf$ f foo bar 199 JONESFORTH VERSION 1 200 20643 CELLS REMAINING 201 OK 202 203 ARGC . 204 3 205 206 : PRINT-ARGS 0 BEGIN DUP ARGC < WHILE DUP DUP . ARGV TELL CR 1+ REPEAT ; 207 PRINT-ARGS 208 0 /home/dave/nasmjf/nasmjf 209 1 foo 210 2 bar 211 212 Yes! 213 214 And by the way, I have no doubt I'll get better at it, 215 but writing that loop felt way harder than it should 216 have been. The way you have to keep DUPing because 217 even comparisons eat the values on the stack... 218 219 I get it. But I'm not sure I like it. 220 221 Also, having capslock on makes my GNU screen shortcuts 222 and vim both go crazy when i forget to toggle it off 223 when i'm done typing Forth. :-( 224 225 But rlwrap is the best. I *highly* recommend it for use 226 with this or any other application that doesn't have 227 command history, etc. 228 229 Related to arguments (because Linux leaves it all in our 230 application's memory in the stack memory space, 231 evidently) are the environment variables. 232 233 JONESFORTH defines ENVIRON to get the address. Also the 234 utility STRLEN to get the length of null-terminated 235 strings, a.k.a. "C strings". 236 237 ENVIRON @ DUP STRLEN TELL 238 SHELL=/bin/bash 239 240 Unlike the result from ARGV, ENVIRON just gives us the 241 address of the first item. So I had to DUP and STRLEN to 242 get the string ready for printing with TELL. 243 244 The next item is 4 bytes after that and so on. 245 246 I guess you could write a word that gets the number of 247 environment variables available and another that gets 248 one from a particular position. 249 250 I started writing those words, but it was late and I was 251 tired. Eventually, I just wanted to see more, so I 252 printed out a big chunk of memory where args and 253 environment are stored: 254 255 0 ARGV DROP 128 DUMP 256 BFECBA8E 2E 2F 6E 61 73 6D 6A 66 0 66 6F 6F 0 62 61 72 ./nasmjf.foo.bar 257 BFECBA9E 0 53 48 45 4C 4C 3D 2F 62 69 6E 2F 62 61 73 68 .SHELL=/bin/bash 258 BFECBAAE 0 43 48 41 52 53 45 54 3D 55 54 46 2D 38 0 54 .CHARSET=UTF-8.T 259 BFECBABE 45 52 4D 43 41 50 3D 53 43 7C 73 63 72 65 65 6E ERMCAP=SC|screen 260 BFECBACE 7C 56 54 20 31 30 30 2F 41 4E 53 49 20 58 33 2E |VT 100/ANSI X3. 261 BFECBADE 36 34 20 76 69 72 74 75 61 6C 20 74 65 72 6D 69 64 virtual termi 262 BFECBAEE 6E 61 6C 3A 44 4F 3D 5C 45 5B 25 64 42 3A 4C 45 nal:DO=\E[%dB:LE 263 BFECBAFE 3D 5C 45 5B 25 64 44 3A 52 49 3D 5C 45 5B 25 64 =\E[%dD:RI=\E[%d 264 265 I'll end this particular log file with an appropriate 266 sign-off with another new word definition: 267 268 JONESFORTH VERSION 1 269 20643 CELLS REMAINING 270 OK 271 BYE