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!