1     Welcome back! In the last log, I got string literals
     
2     working with the 'quote' word.
     
3 
     4     One thing lead to another and I ended up solving a lot
     
5     of different problems so that now I can define words
     
6     not just from "primitives" (machine code words written
     
7     in assembler), but also words composed from *other*
     
8     non-primitive words.
     
9 
    10     So yeah, you can only make programs that print strings,
    
11     but they are real programs.
    
12 
    13     Okay, so now the fun TODOs!
    
14 
    15         [ ] Pretty-print meta info about word!
    
16         [ ] Loop through dictionary, list all names
    
17         [ ] Loop through dictionary, pretty-print all
    
18 
    19     (And I'm not being sarcastic or something - I'm really
    
20     looking forward to these.)
    
21 
    22     First, I need general number handling! I need to parse
    
23     numbers from input and I need to be able to print
    
24     numbers as strings.
    
25 
    26         [ ] New word: str2num (ascii to integer)
    
27         [ ] New word: num2str (integer to ascii)
    
28 
    29     I'd also like to have the ability to intuitively append
    
30     strings. Ideally:
    
31 
    32         "There are " 5 " chickens."
    
33         "Your name is '" username "'?"
    
34 
    35     (I also don't have variables yet, so the username one is
    
36     even more hypothetical. But you get the idea.)
    
37 
    38     My new 'str2num' word seems to work. I realized I could
    
39     use the exit status of the epplication since it is
    
40     already popping a number from the stack (which I'd
    
41     completely forgotten about, ha ha).
    
42 
    43     I updated my `mr` (meow5 run) alias to print the exit
    
44     status:
    
45 
    46         alias mr=`./build.sh run ; echo "Exit status: $?"'
    
47 
    48     The current parse/print number radix is set in a new
    
49     "variable" currently set to decimal on startup:
    
50 
    51         mov dword [radix], 10
    
52 
    53     And created these words to manage the radix:
    
54 
    55         radix (takes number from stack)
    
56         dec  - sets radix to 10
    
57         hex  - sets radix to 16
    
58         oct  - sets radix to 8
    
59         bin  - sets radix to 2
    
60 
    61     Now I'll try str2num out with a decimal number:
    
62 
    63         42 exit
    
64 
    65 $ mr
    
66 Exit status: 42
    
67 
    68     And I'll mess with it by giving it a hex number without
    
69     updating the radix to hex:
    
70 
    71         ff exit
    
72 
    73 $ mr
    
74 Could not find word "ff" while looking in IMMEDIATE mode.
    
75 Exit status: 1
    
76 
    77     Now I'll compact the results a bit to not take up too
    
78     much room here:
    
79 
    80 hex ff exit         <-- hexadecimal
    
81 Exit status: 255
    
82 
    83 
    84 oct 100 exit        <-- octal
    
85 Exit status: 64
    
86 
    87 bin 101010 exit     <-- binary
    
88 Exit status: 42         (uh...seems right)
    
89 bin 11111111 exit
    
90 Exit status: 255        (yup, that's definitely right)
    
91 
    92     Alright! Now this is getting fun. It'll be even better
    
93     when it's taking input from STDIN to be fully
    
94     interactive.
    
95 
    96     Number printing is interesting. So I could just have it
    
97     print numbers when requested, like Forth's DOT (.) word.
    
98     But to print some mixed strings and numbers would look
    
99     like this:
   
100 
   101         "You own " print 16 printnum " snakes." print
   
102 
   103     And like I wrote above in this log, I'd rather have
   
104     strings and numbers automatically appended like this:
   
105 
   106         "You own " 16 " snakes." print
   
107 
   108     But I'm not sure if that's a good idea or not because
   
109     I'm going to either have to further complicate my
   
110     interpreter (it's already doing look-ahead for quotes so
   
111     I don't have to have a space like in Forth: " foo") or
   
112     some up with something else.
   
113 
   114     One thing I've thought about is having interpolated
   
115     strings. As long as I have some unambiguous symbol, I
   
116     could have it pop numbers off the stack and append them
   
117     to the string as I go:
   
118 
   119         16 "You own $ snakes." print
   
120 
   121     I planed to have escapes anyway, so \$ for a literal '$'
   
122     is no problem.
   
123 
   124     Okay, I like that.
   
125 
   126     So I need to write num2str now and then add number
   
127     interpolation to my 'quote' word. This is gonna be cool!
   
128 
   129     Next night: Wrote num2str. I've got a division error.
   
130     Here's how I'm testing:
   
131 
   132         ; test num2str
   
133         push 42           ; num
   
134         push dword [free] ; store here
   
135         CALLWORD num2str
   
136         pop eax        ; chars written
   
137 
   138     First, let's double-check the input:
   
139 
   140 (gdb) break num2str
   
141 528	    pop ebp ; address of string destination
   
142 num2str () at meow5.asm:529
   
143 529	    pop eax ; number
   
144 num2str () at meow5.asm:530
   
145 530	    mov ecx, 0 ; counter of digit characters
   
146 531	    mov ebx, [var_radix]
   
147 532	    mov edx, 0
   
148 (gdb) p/a $ebp
   
149 $1 = 0x804b114 <data_area>  <-- addr for string storage
   
150 (gdb) p $eax
   
151 $2 = 42                     <-- num to convert
   
152 (gdb) p $ebx
   
153 $3 = 10                     <-- radix
   
154 
   155     That's all good:
   
156 
   157         string storage addr: 0x804b114
   
158              num to convert: 42
   
159                       radix: 10
   
160 
   161     Now we'll start converting. I divide the number by the
   
162     radix. The remainder is the digit that was less than the
   
163     radix (so 0-9 for dec, 0-f for hex, etc.). The result of
   
164     the division is the rest of the number.
   
165 
   166 (gdb) s
   
167 num2str.divide_next () at meow5.asm:534
   
168 534	    idiv ebx     ; eax / ebx = eax, remainder in edx
   
169 (gdb) p $eax
   
170 $6 = 4                      <-- quotient
   
171 (gdb) p $ebx
   
172 $7 = 10                     <-- radix (double-checking)
   
173 (gdb) p $edx
   
174 $8 = 2                      <-- remainder (digit to convert)
   
175 
   176     Yup, that looks good. We've got the first digit (working
   
177     lowest to highest) to convert to a character (2) and the
   
178     rest of the number with the first digit gone (4).
   
179 
   180     I convert to the character equivalent and store that on
   
181     the stack temporarily. This is because I convert
   
182     starting from the least significant digit, but we want
   
183     the string to have the number in the expected order,
   
184     starting with the greatest significant digit.
   
185 
   186 (gdb) s
   
187 535	    cmp edx, 9   ; digit bigger than 9? (radix allows a-z)
   
188 536	    jg .toalpha  ; yes, convert to 'a'-'z'
   
189 537	    add edx, '0' ; no, convert to '0'-'9'
   
190 538	    jmp .store_char
   
191 num2str.store_char () at meow5.asm:542
   
192 542	    push edx ; put on stack (pop later to reverse order)
   
193 543	    inc ecx
   
194 544	    cmp eax, 0        ; are we done converting?
   
195 545	    jne .divide_next  ; no, loop
   
196 
   197     Now it's on to the next division:
   
198 
   199 Program received signal SIGFPE, Arithmetic exception.
   
200 num2str.divide_next () at meow5.asm:534
   
201 534	    idiv ebx     ; eax / ebx = eax, remainder in edx
   
202 
   203     There it is. Well, I'm not dividing by 0, so that's
   
204     good. But then I remember idiv is signed division, and
   
205     I'm not doing signed integers just yet. Maybe that's it?
   
206 
   207     Ah, a quick look at a reference:
   
208 
   209         "32-bit division with IDIV requires EAX be
   
210         sign-extended into EDX."
   
211 
   212     Yeah, I don't want to do signed division anyway because
   
213     I don't have the ability to parse or print negative
   
214     numbers (yet?) so I should really be using DIV anyway:
   
215 
   216 Program received signal SIGFPE, Arithmetic exception.
   
217 
   218     Nope! I know this isn't a divide-by-zero problem...
   
219 
   220     Oh, I needed to read a bit more:
   
221 
   222         "DIV Always divides the 64 bits value accross
   
223         EDX:EAX by a value."
   
224 
   225     Oh, right, I need to clear EDX to 0 in my division loop
   
226     because it currently holds the character to convert. It
   
227     makes total sense that the answer is overflowing. I was
   
228     dividing by a huge 64-bit number the second time around!
   
229 
   230     So I just need to clear edx before my division:
   
231 
   232 533	    mov edx, 0   ; div actually divides edx:eax / ebx!
   
233 534	    div ebx      ; eax / ebx = eax, remainder in edx
   
234 535	    cmp edx, 9   ; digit bigger than 9? (radix allows a-z)
   
235 (gdb) p $eax
   
236 $1 = 0
   
237 (gdb) p $edx
   
238 $2 = 4
   
239 
   240     That's got it! The final digit (remainder after
   
241     division) is 4 and there's nothing left to divide after
   
242     that (quotient is 0).
   
243 
   244     Once it sees the rest of the number is now 0, I store
   
245     the string at the address provided and return the number
   
246     of characters written (digits in radix) on the stack:
   
247 
   248 554	    push eax  ; return num chars written
   
249 end_num2str () at meow5.asm:149
   
250 149	    mov eax, [return_addr] ; RETURN
   
251 (gdb) p $eax
   
252 $1 = 2
   
253 (gdb) x/s $ebp
   
254 0x804b114 <data_area>:	"42"
   
255 
   256     Correct! Two characters were written, and the string is
   
257     "42", my value in decimal!
   
258 
   259     Since I can now, I'll make the test print real nice and
   
260     use the exit value to show the number of digits written
   
261     (to make sure it's correct):
   
262 
   263         ; test num2str
   
264         CALLWORD decimal
   
265         push dword 42     ; num
   
266         push dword [free] ; store here
   
267         CALLWORD num2str
   
268         PRINTSTR "Answer: "
   
269         push dword [free] ; print from here
   
270         CALLWORD print
   
271         CALLWORD newline
   
272         CALLWORD exit     ; stack still has chars written
   
273 
   274 $ mr
   
275 Answer: 42
   
276 Exit status: 2
   
277 
   278     Correct. Let's see some others:
   
279 
   280         CALLWORD oct
   
281         push dword 64
   
282 
   283 Answer: 100
   
284 Exit status: 3
   
285 
   286         CALLWORD hex
   
287         push dword 3735928559
   
288 
   289 Answer: deadbeef
   
290 Exit status: 8
   
291 
   292         CALLWORD bin
   
293         push dword 7
   
294 
   295 Answer: 111
   
296 Exit status: 3
   
297 
   298         CALLWORD bin
   
299         push dword 257
   
300 
   301 Answer: 100000001
   
302 Exit status: 9
   
303 
   304     Looks good! Now I'm kinda wishing the interpreter was
   
305     interactive because that would have been way more fun
   
306     than changing the assembly test for each run. But
   
307     that'll be coming up soon enough.
   
308 
   309     After sleeping on it, I'm really excited about the idea
   
310     of string interpolation using '$' as a placeholder. So
   
311     I'll make that the next todo:
   
312 
   313         [ ] Add "$" placeholders so the 'quote' word so it
   
314             can interpolate numbers from the stack into the
   
315             string.
   
316 
   317     I suspect the hard part will be remembering to push the
   
318     stack values in the opposite order that they appear in
   
319     the string...so we'll see what that's like in practice.
   
320 
   321     Okay, I've got it working, but only just barely. It
   
322     feels incredibly fragile. In particular, the interpreter
   
323     has become way too complicated for my taste. Juggling
   
324     registers willy-nilly and using the stack when I run out
   
325     of places to put things is just sloppy.
   
326 
   327     It does work. Cleaned up example:
   
328 
   329         42 "The answer is $." print
   
330         42 hex "The answer is 0x$ in hex." print
   
331         42 bin "The answer is $ in computer." print
   
332         42 oct "The answer is $ in octal." print
   
333 
   334     Output:
   
335 
   336 The answer is 42.
   
337 The answer is 0x2a in hex.
   
338 The answer is 101010 in computer.
   
339 The answer is 52 in octal.
   
340 
   341     Which is really cool. But I need to get my mess under
   
342     control.
   
343 
   344     For one thing, I've been using the registers very
   
345     inconsistantly because I don't have any plan regarding
   
346     where to put stuff.
   
347 
   348     This article has helped a ton:
   
349 
   350         https://www.swansontec.com/sregisters.html
   
351 
   352     I'm not sure I'm going to re-write EVERYTHING to
   
353     follow these guidelines, but new stuff definitely will
   
354     and I'm going to immediately switch the 'quote' word to
   
355     use the esi and edi registers!
   
356 
   357     Using esi and edi in the 'quote' word helped make the
   
358     usage much clearer and simplified some of the address
   
359     manipulation considerably (the source and destination
   
360     pointers aren't always moving in sync because escapes
   
361     and placeholders in the source string may expand to a
   
362     different number of characters in the destination
   
363     string.
   
364 
   365     Another thing that makes the interpreter so fragile is
   
366     the way I have to juggle registers and the stack around
   
367     to check for a valid numeric literal when an input token
   
368     doesn't match any of the exisitng words.
   
369 
   370     So what I'm thinking is that just like quote, I'll test
   
371     for tokens that start with a number and handle them
   
372     _before_ trying to find them in the dictionary.
   
373 
   374     Consequently, I won't be able to start any word or
   
375     variable names with a number. (Like Forth allows.)
   
376     In exchange, it should simplify the interpreter a fair
   
377     amount in some messy areas.
   
378 
   379         [ ] Break out 'number' into separate word that does
   
380             pre-check like 'quote'.
   
381 
   382     This will also give it a nice symetry with the way I'm
   
383     handling string literals (my first big departure from
   
384     pur Forth syntax).
   
385 
   386     Three sleepy nights later: Well, it works...but I've now
   
387     exposed another problem.
   
388 
   389     This is what the top of my interpreter loop looks like:
   
390 
   391         get_next_token:
   
392             CALLWORD eat_spaces ; skip whitespace
   
393             CALLWORD quote      ; handle any string literals
   
394             CALLWORD number     ; handle any number literals
   
395             CALLWORD get_token
   
396 
   397     It's nice and neat and readable. The problem is that it
   
398     doesn't work unless you have an optional quote followed
   
399     by an optional number followed by a non-string,
   
400     non-quote token.
   
401 
   402     Two strings in a row or a number followed by a string or
   
403     various other combos won't work.
   
404 
   405     Here's what needs to happen:
   
406 
   407         interpreter loop:
   
408             eat spaces
   
409             if quote
   
410                 make string literal
   
411                 restart interpreter loop
   
412             if number
   
413                 make number literal
   
414                 restart interpreter loop
   
415             if token
   
416                 find/execute/compile
   
417                 restart interpreter loop
   
418             else
   
419                 error
   
420 
   421     So as much as I liked having 'quote' and 'number' handle
   
422     all aspects of those respective inputs, it's just making
   
423     everying too interdependant. I need to break them up
   
424     into even smaller words and give more control back to
   
425     the interpreter where it belongs.
   
426 
   427     So more refactoring awaits me before I can do the fun
   
428     stuff. Ah well, this is where the "slow and steady"
   
429     approach wins - I know I'll eventually push through this
   
430     kinda boring housekeeping stuff.
   
431 
   432     Next night: Progress made on that refactor. Then there's
   
433     a segfault. So that's what I'll work on tomorrow night.
   
434 
   435     Next night: Looks like the segfault is happening in old
   
436     code (specifically inline, but because of wrong
   
437     calculations in semicolon ";"). Since the old code won't
   
438     just start failing for no reason, this has to be due to
   
439     bad input from all the changes I've made in the outer
   
440     interpreter loop.
   
441 
   442     Hard to track down with GDB with breakpoints and watch
   
443     statements. So I added some print debugging and sure
   
444     enough:
   
445 
   446 COLON here: 0804a290
   
447 inlining print
   
448 SEMICOLON there: 0804b2e6
   
449 
   450     That's the problem. 'colon' is trying to save the
   
451     machine code start address for 'meow' on the stack and
   
452     'semicolon' should be getting that same value, but it's
   
453     not. It's while executing this, by the way:
   
454 
   455         : meow "Meow." print ;
   
456 
   457     The botched semicolon tail writing is why trying to use
   
458     'meow' in 'meow5' fails - the calculations are wildly
   
459     wrong (it thinks the machine code length is -4144).
   
460 
   461     So I need to figure out where I'm leaving something on
   
462     the stack. But how?
   
463 
   464     Oh, I've got an idea, That wrong value from the stack
   
465     looks like an address. I'll fire up GDB and see what's at
   
466     that address. Maybe that will help.
   
467 
   468 (gdb) x 0x0804b2e6
   
469 0x804b2e6:	0x776f654d
   
470 
   471     Hmmm. Looks like a string?
   
472 
   473 (gdb) x/s 0x0804b2e6
   
474 0x804b2e6:	"Meow."
   
475 
   476     Aha! Interesting. It's the address from the string
   
477     literal before the print. What's that doing there? In
   
478     compile mode, 'quote' should be compiling that address
   
479     into the new word's machine code memory, not on the
   
480     stack.
   
481 
   482     Oh, right, I knew that would come back to haunt me. At
   
483     some point, my run/compile logic got lost (could be a
   
484     dozen commits ago by now). I'm currently compiling the
   
485     instruction to push the string's address *and* I'm also
   
486     actually pushing the string's address every single time.
   
487     So this has actually been broken for a while and my
   
488     refactoring has (almost?) nothing to do with it.
   
489 
   490     This is why I need a set of proper regression tests
   
491     ASAP. Well, I'll get there soon. Gotta be able to take
   
492     input before I can do that!
   
493 
   494     Anyway, time to add (back) some conditional jumps in
   
495     'quote'!
   
496 
   497 The answer is 42.
   
498 The answer is 0x2a in hex.
   
499 The answer is 101010 in computer.
   
500 The answer is 52 in octal.
   
501 Meow.Meow.Meow.Meow.Meow.
   
502 
   503     Much better.
   
504 
   505     And I was reminded that I had been in the middle of
   
506     adding escape sequences to string literals! Let's try
   
507     them out:
   
508 
   509         42 "I paid \$$ for beans\\cheese.\nOkay?\n" print
   
510 
   511 $ mr
   
512 I paid $42 for beans\cheese.
   
513 Okay?
   
514 
   515     Wow, that was super satisfying.
   
516 
   517     Okay, I think *now* I'm finally ready to make those
   
518     introspective word-printing words!
   
519 
   520     Next night: Uh, so I didn't think far enough ahead. I
   
521     made some really nice string making and printing
   
522     facilities, but I can't use them easily from assembly.
   
523     Nor can I define something as complex as a "dictionary"
   
524     printing word (to use the Forth term for all the defined
   
525     words.)
   
526 
   527     Hmmm... this is a quandary.
   
528 
   529     Okay, it wasn't so bad. I just needed to write an
   
530     additional word 'printnum' (with macro PRINTNUM_CODE) to
   
531     help it out.
   
532 
   533     After that, this is all it takes to write an 'inspect'
   
534     that takes a word tail address and prints the word name
   
535     and bytes of machine code (from the tail metadata):
   
536 
   537         DEFWORD inspect
   
538             pop esi ; get tail addr
   
539             lea eax, [esi + T_NAME]
   
540             push eax
   
541             PRINT_CODE 
   
542             PRINTSTR ": "
   
543             mov eax, [esi + T_CODE_LEN]
   
544             push esi ; preserve tail addr
   
545             ; param 1: num to be stringified
   
546             push eax
   
547             PRINTNUM_CODE
   
548             PRINTSTR " bytes "
   
549             NEWLINE_CODE
   
550         ENDWORD inspect, 'inspect', (IMMEDIATE)
   
551 
   552 
   553     Here it is printing the word 'find':
   
554 
   555         "find" find inspect
   
556 
   557 find: 58 bytes
   
558 
   559     Neat!
   
560 
   561     Oh, and I guess I can show my new 'printnum' in
   
562     standalone action:
   
563 
   564         bin 11101111 hex printnum
   
565 
   566 ef
   
567 
   568     Nice.
   
569 
   570   +--------------------------------------------------------+
   
571   |                                                        |
   
572   |  Just so it's clear I'm not trying to trick anybody,   |
   
573   |  the interpreter input is still hard-coded into        |
   
574   |  memory, so that calling line really looks like this   |
   
575   |  in assembly:                                          |
   
576   |                                                        |
   
577   |      db 'bin 11101111 hex printnum '                   |
   
578   |                                                        |
   
579   |  I'm just trying to make the log readable, so I leave  |
   
580   |  off the 'db' and quotes.                              |
   
581   |                                                        |
   
582   |  I'll take input on STDIN very soon (next!).           |
   
583   |                                                        |
   
584   +--------------------------------------------------------+
   
585 
   586     And now I can find out how big a "meow" printing
   
587     function is in machine code (minus the "Meow." string
   
588     itself, because that's stored elsewhere. And also the
   
589     combined five meows:
   
590 
   591         "meow" find inspect
   
592         "meow5" find inspect
   
593 
   594 meow: 38 bytes
   
595 meow5: 190 bytes
   
596 
   597     There we go. So even repeating the entire printing
   
598     routine five times is still under 200 bytes of machine
   
599     code. When's the last time you shed a tear over 200
   
600     bytes?
   
601 
   602     Okay, now I just need a loop to call inspect for every
   
603     defined word and I'm all set.
   
604 
   605     I'm not sure I want to use the term "dictionary" from
   
606     Forth (or "word", for that matter). Hmm, I guess I can
   
607     avoid the issue by just calling it 'inspect_all'...
   
608 
   609     Almost there on inspect_all.  It seems something has
   
610     left the token str addr on the stack and it's getting in
   
611     the way of popping the link addr and ruining everything.
   
612     At least, that's what it looks like.
   
613 
   614     Next night: So what I woke up thinking in the morning is
   
615     that if I'm not maintaining my stack correctly between
   
616     words, there are three ways I could debug this:
   
617 
   618     1. Painfully, with GDB stepping through...a lot.
   
619 
   620     2. Write a DEBUG_STACK macro that non-destructively
   
621        prints the values on the stack from anywhere within
   
622        the assembly.
   
623 
   624     3. Write a script (perhaps in awk?) that checks the
   
625        push/pop balance by following all of the called or
   
626        inlined code and incrementing a counter for each push
   
627        and decrementing for each pop.
   
628 
   629     Obviously I'm trying to avoid Option 1.
   
630 
   631     Option 3 sounds neat, but I'm not sure how to *display*
   
632     that information so that it helps me track down the
   
633     problem - some words push more than they pop and
   
634     vice-versa. It'd still be a lot of manual checking.
   
635 
   636     Option 2 seems generally useful anyway, so I'm going to
   
637     give that a shot and hope that peppering it about the
   
638     program will help me figure out what's going on.
   
639 
   640     Hmmm...well, printing the stack at the Meow5 language
   
641     level is no problem, but printing it at the assembly
   
642     level is a big problem. I've got a chicken-and-egg
   
643     problem where I need to preserve the registers it uses
   
644     AND preserve the stack.
   
645 
   646     I could preserve the registers in a purpose-built bit of
   
647     memory.
   
648 
   649     Well, I think I did that right. Here's the whole ugly
   
650     thing:
   
651 
   652         section .bss
   
653             ds_eax: resb 4
   
654             ds_ebx: resb 4
   
655             ds_ecx: resb 4
   
656             ds_edx: resb 4
   
657             ds_esi: resb 4
   
658             ds_edi: resb 4
   
659         section .text
   
660         %macro DEBUG_STACK 0
   
661             mov [ds_eax], eax
   
662             mov [ds_ebx], ebx
   
663             mov [ds_ecx], ecx
   
664             mov [ds_edx], edx
   
665             mov [ds_esi], esi
   
666             mov [ds_edi], edi
   
667             PRINTSTR "Stack: "
   
668             PRINTSTACK_CODE
   
669             NEWLINE_CODE
   
670             mov eax, [ds_eax]
   
671             mov ebx, [ds_ebx]
   
672             mov ecx, [ds_ecx]
   
673             mov edx, [ds_edx]
   
674             mov esi, [ds_esi]
   
675             mov edi, [ds_edi]
   
676         %endmacro
   
677 
   678     But talk about chicken-and-egg problem. Or is it more
   
679     of a "who watches the watchmen?" thing? Or maybe there's
   
680     an even better expression for it, but my problem is that
   
681     I think I'm acutally calling whatever is doing bad stack
   
682     things WHILE trying to print the stack... :-(
   
683 
   684     Okay, it's worse than even that. I don't know how I've
   
685     gotten away with it this long, but I've been using a
   
686     bunch of words mid-assembly to debug stuff...and a lot
   
687     of them aren't safe for that (like 'newline') because
   
688     they don't preserve the registers. 
   
689 
   690     So it's possible that by trying to observe what's going
   
691     on for the 'inspect_all' feature, I've been ruining it!
   
692 
   693     I have to remember that most of my words are only safe
   
694     if I'm treating them as if they're being called from the
   
695     language - transfering values on the stack and not
   
696     giving a hoot what happens to the registers in between!
   
697 
   698     Okay, I think I got it. No idea how this got here, but
   
699     I had an extra pop for no reason I could understand in
   
700     'printnum'! I have no idea how it got there, but that
   
701     would definitely mess things up:
   
702 
   703         pop esi ; get preserved <-----??????
   
704 
   705     I think the lesson is to test all new words more
   
706     thoroughly before moving on!
   
707 
   708     Okay, can we _finally_ see the output of 'inspect_all'?
   
709 
   710 $ mr
   
711 Meow. Meow. Meow. Meow. Meow. 
   
712 meow5: 190 bytes IMMEDIATE COMPILE 
   
713 meow: 38 bytes IMMEDIATE COMPILE 
   
714 inspect_all: 359 bytes IMMEDIATE 
   
715 inspect: 340 bytes IMMEDIATE 
   
716 ps: 177 bytes IMMEDIATE COMPILE 
   
717 printmode: 100 bytes IMMEDIATE COMPILE 
   
718 printnum: 116 bytes IMMEDIATE COMPILE 
   
719 number: 306 bytes IMMEDIATE COMPILE 
   
720 decimal: 10 bytes IMMEDIATE COMPILE 
   
721 bin: 10 bytes IMMEDIATE COMPILE 
   
722 oct: 10 bytes IMMEDIATE COMPILE 
   
723 hex: 10 bytes IMMEDIATE COMPILE 
   
724 radix: 6 bytes IMMEDIATE COMPILE 
   
725 str2num: 95 bytes IMMEDIATE COMPILE 
   
726 quote: 247 bytes IMMEDIATE COMPILE 
   
727 num2str: 58 bytes IMMEDIATE COMPILE 
   
728 ;: 150 bytes COMPILE RUNCOMP 
   
729 return: 7 bytes IMMEDIATE 
   
730 :: 137 bytes IMMEDIATE 
   
731 copystr: 18 bytes IMMEDIATE COMPILE 
   
732 get_token: 55 bytes IMMEDIATE 
   
733 eat_spaces: 38 bytes IMMEDIATE COMPILE 
   
734 find: 58 bytes IMMEDIATE 
   
735 is_runcomp: 5 bytes IMMEDIATE COMPILE 
   
736 get_flags: 7 bytes IMMEDIATE COMPILE 
   
737 inline: 25 bytes IMMEDIATE 
   
738 print: 33 bytes IMMEDIATE COMPILE 
   
739 newline: 26 bytes IMMEDIATE COMPILE 
   
740 strlen: 16 bytes IMMEDIATE COMPILE 
   
741 exit: 8 bytes IMMEDIATE COMPILE 
   
742 Goodbye.
   
743 Exit status: 0
   
744 
   745     Yay! At last! Every word in the system with the number
   
746     of bytes of machine code and mode(s).
   
747 
   748     And with the help of the shell, we can sort by size.
   
749 
   750 $ ./meow5 |  awk '
   
751         /^.*: [0-9]+/ {t=t+$2; print $2, $1}
   
752         END{print "Total bytes:", t}
   
753   ' | sort -n
   
754 
   755 5 is_runcomp:
   
756 6 radix:
   
757 7 get_flags:
   
758 7 return:
   
759 8 exit:
   
760 10 bin:
   
761 10 decimal:
   
762 10 hex:
   
763 10 oct:
   
764 16 strlen:
   
765 18 copystr:
   
766 25 inline:
   
767 26 newline:
   
768 33 print:
   
769 38 eat_spaces:
   
770 38 meow:
   
771 55 get_token:
   
772 58 find:
   
773 58 num2str:
   
774 95 str2num:
   
775 100 printmode:
   
776 116 printnum:
   
777 137 ::
   
778 150 ;:
   
779 177 ps:
   
780 190 meow5:
   
781 247 quote:
   
782 306 number:
   
783 340 inspect:
   
784 359 inspect_all:
   
785 
   786 Total bytes: 2655
   
787 
   788     (Command line formatted for semi-readability and total
   
789     bytes separated out for clarity.)
   
790 
   791     So the smallest word is 'is_runcomp', which tests for
   
792     the RUNCOMP flag in a mode and pushes the result.
   
793 
   794     The largest word is not a surprise to me, 'inspect_all',
   
795     which contains 'inspect' and puts a loop around it. In
   
796     turn, 'inspect' includes a bunch of string and number
   
797     printing code.
   
798 
   799     So let's see how I did:
   
800 
   801         [x] Pretty-print meta info about word!
   
802         [ ] Loop through dictionary, list all names
   
803         [x] Loop through dictionary, pretty-print all
   
804         [x] New word: str2num (ascii to integer)
   
805         [x] New word: num2str (integer to ascii)
   
806         [x] Add "$" placeholders so the 'quote' word so it
   
807             can interpolate numbers from the stack into the
   
808             string.
   
809         [x] Break out 'number' into separate word that does
   
810             pre-check like 'quote'.
   
811 
   812     I forgot about a loop that just prints all the word
   
813     names. And 'number' works a little differently now than
   
814     I'd envisioned. But I'm gonna call this part done for
   
815     now and close out this log file.