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

Download raw file: devlog/log20.txt

1 So log19.txt had a major breakthrough with my 2 understanding of Forth variables. And it solved a 3 previous mystery of the need for FETCH after the 4 variable LATEST. 5 6 Unfortunately, I'm still crashing with a segfault and 7 getting PARSE ERROR on some jonesforth.f source. 8 9 So I'll set my lines to read to just before that error 10 and continue reading and testing the Forth source: 11 12 %assign __lines_of_jf_to_read 704 13 14 After RECURSE comes IF ... THEN statements! Oooh, 15 exiciting stuff. With this, I can *finally* write 16 programs with branching and logic! 17 18 So the format is: 19 20 <condition on stack> IF true-statements THEN 21 22 Oh, and the Jones implementation of these only works in 23 compiled words. These are crazy enough as it is (THEN 24 supplies the address to branch to in a way that reminds 25 me of INTERCAL's "come from" statement. Ha ha.) but 26 apparently you can make some that work in immediate mode 27 too. 28 29 The gymnastics in the definition are amazing. And I can 30 finally see 0BRANCH used properly. No wonder I wasn't 31 having much luck on my own with that. 32 33 Okay, let's see if it works: 34 35 : foo IF 1 . THEN 2 . ; 36 1 foo 37 1 2 38 0 foo 39 2 40 41 That worked. Oh, and Forth is one of those languages 42 which use numeric 1 for true, 0 for false, 43 44 Now an ELSE: 45 46 : bar IF 1 . ELSE 2. THEN 3 . ; 47 1 foo 48 1 3 49 0 foo 50 2 3 51 52 Excellent. 53 54 Next night: This is what I'm *most* excited about: 55 looping! 56 57 BEGIN <put condition on stack> 58 WHILE 59 <still true, do this> 60 REPEAT 61 62 Here goes nothing: 63 64 : foo BEGIN DUP 0> WHILE DUP . 1- REPEAT ; 65 10 foo 66 10 9 8 7 6 5 4 3 2 1 67 68 (By the way, I had a bit of a challenge remembering how 69 to use 0> and 1- because they _look_ like, "zero is 70 greater than..." and, "backwards negative one." But the 71 Forth way to read them is: "push 0 onto the stack and 72 check if first item is greater than that," and, "push 1 73 onto the stack and subtract.") 74 75 Ooh, and I wonder if I can use IF to make a RECURSEive 76 word that actually works correctly? 77 78 : foo DUP . DUP 0> IF 1- RECURSE THEN ; 79 5 foo 80 5 4 3 2 1 0 81 82 Yes! Now I'm getting somewhere. 83 84 UNLESS is just NOT IF...but it uses that wild [COMPILE] 85 word to include the immediate IF word as part of its 86 definition. Usage is simple. And I just remembered that 87 we have TRUE and FALSE defined, so I can use those 88 instead of 1 and 0 (take that, Perl!): 89 90 : foo UNLESS 'A' EMIT ELSE 'A' 1+ EMIT THEN ; 91 TRUE foo 92 B 93 FALSE foo 94 A 95 96 And yeah, I just realized I can get 'B' by incrementing 97 'A'. It seems my mind is in top form tonight, ha ha. 98 99 Next is comments with ( parens ). And since '(' is an 100 immediate word, it can be programmed to be smart and 101 handle nested parethesis correcty: 102 103 : foo ( this is foo ) 42 . ; 104 ( now some foo ) foo 105 42 106 ( can we nest 107 as much ( as we want? 108 ( of course! ) 109 ) ) 110 foo 111 42 112 113 Amazing. Not too many languages let you change the 114 comment syntax mid-stream. :-) 115 116 Now that we have comments, Jones demonstrates some stack 117 effect comments (which are a standard documentation 118 format for words in Forth): 119 120 121 : NIP ( x y -- y ) SWAP DROP ; 122 : TUCK ( x y -- y x y ) SWAP OVER ; 123 : PICK ( x_u ... x_1 x_0 u -- x_u ... x_1 x_0 x_u ) 124 1+ ( add one because of 'u' on the stack ) 125 4 * ( multiply by the word size ) 126 DSP@ + ( add to the stack pointer ) 127 @ ( and fetch ) 128 ; 129 130 Let's try them out. NIP shows that it takes two items 131 from the stack and returns the first: 132 133 1 2 3 NIP . . 134 3 1 135 136 Tuck takes two items and puts a copy of the first after 137 the second: 138 139 1 2 3 TUCK . . . . 140 3 2 3 1 141 142 The notation for PICK looks insane until you stare at it 143 for a while. We get the element in the 'u'th position on 144 the stack where 'u' is on top of the stack. 145 146 50 40 30 20 10 4 PICK . . . . . . 147 50 10 20 30 40 50 148 149 Clever notation and did you see the definition above for 150 PICK? It had not occurred to me to fetch items on the 151 stack via the stack pointer! 152 153 Makes me want to try: 154 155 1 2 3 4 156 DSP@ @ . 157 4 158 DSP@ 4+ @ . 159 3 160 DSP@ 8 + @ . 161 2 162 163 Of course that works. You can do anything in Forth. 164 165 Which reminds me: long ago, I remember hearing about 166 this legendary programming language called Forth where 167 you could even redefine _numbers_ to have different 168 meanings. 169 170 I can't believe it's taken me this long to try it: 171 172 : 4 12 ; 173 4 1 + . 174 13 175 176 LOL. Of course. But, wow. That's really something to 177 see, isn't it? 178 179 Then a word that takes advantage of the ability to loop: 180 SPACES. 181 182 0 SPACES 183 20 SPACES 184 20 SPACES 185 186 And then HEX and DECIMAL, which simply set BASE: 187 188 15 HEX . 189 F 190 FF00 DECIMAL . 191 65280 192 193 Next up is number printing. If you've been reading this 194 far, you know that I added '.' DOT (prints number from 195 the top of the stack) in assembly as a debugging tool. 196 The one defined in jonesforth.f will, of course 197 "overwrite" mine. 198 199 Before '.' itself, Jones defines these: 200 201 U.R ( u width -- ) 202 U. ( u -- ) 203 .R ( n width -- ) 204 205 Where U stands for "unsigned" number and R indicates 206 that the word will print the number padded to a width. 207 Let's try it out. Here's normal unsigned number 208 printing: 209 210 50 U. CR 5000 U. CR 211 50 212 5000 213 214 Now with a width of 10 characters: 215 216 50 10 U.R CR 500 10 U.R CR 5000000 10 U.R CR 217 50 218 500 219 5000000 220 221 Compare signed and unsigned printing: 222 223 -50 U. 224 4294967246 225 -50 10 .R 226 -50 227 228 And now one that I'm very excited about: '.S' which 229 prints everything on the stack without removing anything 230 on the stack. 231 232 1 2 3 4 5 .S 233 5 4 3 2 1 1 3215633123 0 3215633132 3215633148 3215633162 3215634118 3215634127 234 3215634178 3215634191 3215634203 3215634213 3215634256 3215634264 3215634275 3215634288 235 ...6649441 791559519 1836278126 771778154 1935765039 6711917 0 Segmentation fault 236 237 Oh no! Let's take a look at the definition: 238 239 : .S ( -- ) 240 DSP@ ( get current stack pointer ) 241 BEGIN 242 DUP S0 @ < 243 WHILE 244 DUP @ U. ( print the stack element ) 245 SPACE 246 4+ ( move up ) 247 REPEAT 248 DROP 249 ; 250 251 I'd say my S0 variable isn't set correctly. 252 253 A couple nights later: Yes, that's exactly what was 254 wrong. But it took me a while to figure out why. 255 256 Long story, but in my DEFVAR macro, if I try to set the 257 labels by "name" macro parameter like JonesForth does, 258 NASM won't like it: 259 260 var_"S0": 261 262 (Since a quote isn't a legal label character, it just 263 sees the first part, 'var_' and complains about 264 redefining 'var_' over and over.) 265 266 And maybe I figured that out before, because I was 267 setting them to the unquoted "label" macro parameter 268 instead: 269 270 var_SZ 271 272 Which is great, except where we initially store the 273 stack pointer in S0, it was by name (as in JonesForth). 274 275 The fix was easy. Just change: 276 277 278 mov [var_S0], esp ; save stack pointer 279 280 to: 281 282 mov [var_SZ], esp ; save stack pointer 283 284 In case you're wondering why it didn't just crash 285 before, since the macro wasn't defining a 'var_S0' 286 label, I had apparently made one. Maybe I meant it as a 287 temporary measure. I don't know, but it certainly was 288 confusing to piece together later. 289 290 With the fix in, let's test DSP@ and S0. They should 291 start off the same: 292 293 HEX 294 DSP@ U. 295 BFFFF990 296 S0 @ U. 297 BFFFF990 298 299 Then let's put something on the stack: 300 301 1 302 DSP@ U. 303 BFFFF98C 304 305 And another thing: 306 307 2 308 DSP@ U. 309 BFFFF988 310 311 Then pop the stack: 312 313 . 314 2 315 DSP@ U. 316 BFFFF98C 317 318 Again, and we should be back where we started: 319 320 . 321 1 322 DSP@ U. 323 BFFFF990 324 325 Hooray! Now .S should work, right? 326 327 1 2 3 328 .S 329 3 2 1 330 .S 331 3 2 1 332 333 Excellent! It non-destructively prints the stack! 334 335 (Now that I've made another fix to my port, I wonder if 336 _now_ I can run jonesforth.f without errors? Darn it, 337 nope. Same parse error on the the same spot as before. 338 Oh well, I'll keep going until I hit that spot...) 339 340 Next word is UWIDTH. It prints the width of a printed 341 number: 342 343 5 UWIDTH . 344 1 345 42 UWIDTH . 346 2 347 777 UWIDTH . 348 3 349 350 That works, but it's no surprise since U.R and .R depend 351 on it. 352 353 Oh, and I thought this was fun. To add a space to U., 354 Jones redefines it by calling the previous definition 355 and adding a space: 356 357 ( The real U., note the trailing space. ) 358 : U. U. SPACE ; 359 360 I'll close out this log with another tiny one: 361 362 363 ( ? fetches the integer at an address and prints it. ) 364 : ? ( addr -- ) @ . ; 365 366 Which will be easy to test: 367 368 555 DSP@ ? 369 555 370 371 Looks like there's a real mix of stuff coming up next. 372 See you in the next log!