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!