1     We'll pick up right where we left off with a convenience
     
2     word to be used with VALUEs that works just like += in
     
3     "C-like" languages:
     
4 
     5         +TO adds to the value
     
6 
     7 50 VALUE foo foo . 50
     
8 100 TO foo foo . 100
     
9 10 +TO foo foo . 110
    
10 
    11     And now some exciting introspection words:
    
12 
    13         ID.          prints name of word at address
    
14         ?HIDDEN      returns truthy value if word hidden
    
15         ?IMMEDATE    returns truthy value if word immediate
    
16 
    17 LATEST @ ID.
    
18 foo
    
19 LATEST @ DUP DUP ID. SPACE ?IMMEDIATE . ?HIDDEN .
    
20 foo 0 0
    
21 LATEST @ @ DUP DUP ID. SPACE ?IMMEDIATE . ?HIDDEN .
    
22 CFA> 0 0
    
23 LATEST @ @ @ DUP DUP ID. SPACE ?IMMEDIATE . ?HIDDEN .
    
24 ENDCASE 128 0
    
25 
    26     And
    
27 
    28         WORDS        prints all non-hidden words
    
29 
    30 WORDS
    
31 foo CFA> ENDCASE ENDOF OF CASE DUMP FORGET WORDS ?IMMEDIATE ?HIDDEN ID. +TO TO VALUE VARIABLE CELLS
    
32 ALLOT CONSTANT ." S" C, ALIGN ALIGNED DEPTH WITHIN ? U. . .R U.R UWIDTH .S U. HEX DECIMAL SPACES PIC
    
33 K TUCK NIP ( UNLESS REPEAT WHILE AGAIN UNTIL BEGIN ELSE THEN IF RECURSE [COMPILE] '.' '-' '0' 'A' '"
    
34 ' ')' '(' ';' ':' LITERAL NOT FALSE TRUE NEGATE SPACE CR BL '\n' MOD / LATEST CEND CSTART BASE S0 HE
    
35 RE STATE O_NONBLOCK O_APPEND O_TRUNC O_EXCL O_CREAT O_RDWR O_WRONLY O_RDONLY SYS_BRK SYS_CREAT SYS_W
    
36 RITE SYS_READ SYS_CLOSE SYS_OPEN SYS_EXIT F_LENMASK F_HIDDEN F_IMMED DOCOL R0 VERSION SYSCALL0 SYSCA
    
37 LL1 SYSCALL2 SYSCALL3 EXECUTE CHAR HIDE IMMEDIATE DSP! DSP@ RDROP RSP! RSP@ R> >R CMOVE C@C! C@ C! -
    
38 ! +! @ ! INVERT XOR OR AND 0>= 0<= 0> 0< 0<> 0= >= <= > < <> = /MOD * - + 4- 4+ 1- 1+ ?DUP 2SWAP 2DU
    
39 P 2DROP -ROT ROT OVER DUP SWAP DROP PRINTWORD . EMIT ; : EXIT HIDDEN ] [ , CREATE FIND LIT NUMBER >D
    
40 FA >CFA KEY WORD INTERPRET TELL LITSTRING 0BRANCH BRANCH ' gtfo QUIT
    
41 
    42     The next one is pretty crazy. You would think that this
    
43     would just hide or "forget" the word CR. But it doesn't,
    
44     it forgets CR and *everything after* it!
    
45 
    46 FORGET CR
    
47 
    48     So now WORDS should only show up to BL:
    
49 
    50 WORDS
    
51 PARSE ERROR: WORDS
    
52 
    53     Huh? Oh, ha ha ha. Right.
    
54 
    55     Time to reload.
    
56     Let's see that again, but maybe not "forget" quite so
    
57     much:
    
58 
    59 LATEST @ ID.
    
60 CFA>
    
61 : foo ." Hello" ;
    
62 foo
    
63 
    64 Program received signal SIGSEGV, Segmentation fault.
    
65 code_LITSTRING () at nasmjf.asm:28
    
66 28          jmp [eax] ; Jump to whatever code we're now pointing at.
    
67 
    68     Argh! I can hardly believe this is the first time I've
    
69     tried compiling a word that contains a print string
    
70     statement.  But I guess so. So my LITSTRING assembly
    
71     port of the original must contain a bug?
    
72 
    73     Two nights later: debugging this with GDB *sucks*
    
74     because LITSTRING is written in assembly, but the
    
75     higher-level S" and ." are written in Forth, so it's
    
76     really hard to break right when I want to. On top of
    
77     that, trying to examine Forth constructs with GDB feels
    
78     like trying to eat soup with a fork. I know it's a
    
79     programmable fork and I could probably make it an
    
80     awesome soup fork, but I'd rather work on my soup
    
81     instead of my utensils.
    
82 
    83     Hmmm... you know what? The next word I was going to test
    
84     after FORGET is DUMP, which is supposed to give a hex
    
85     dump of memory.
    
86 
    87     Maybe I can use it to help debug LITSTRING and friends?
    
88 
    89     First, let's see how it works:
    
90 
    91         DUMP ( addr len -- )
    
92 
    93 	DUMP is used to dump out the contents of memory,
    
94         in the 'traditional' hexdump format.
    
95 
    96     So we give it an address of memory and a number of bytes
    
97     to print. I wonder it will look like if I create a
    
98     simple word and display it?
    
99 
   100     This one has some repetition so maybe I can see the
   
101     pattern:
   
102 
   103 : foobar 5 . 5 . ;
   
104 foobar
   
105 5 5
   
106 
   107     And let's dump 64 bytes of memory starting at the
   
108     laatest definition:
   
109 
   110 LATEST @ 64 DUMP
   
111  804EF20 C4 EE  4  8  6 66 6F 6F 62 61 72  0 5A 90  4  8 .....foobar.Z...
   
112  804EF30 38 A1  4  8  5  0  0  0 2C E7  4  8 38 A1  4  8 8.......,...8...
   
113  804EF40  5  0  0  0 2C E7  4  8 9C A1  4  8  0  0  0  0 ....,...........
   
114  804EF50  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 ................
   
115 
   116     Gasp! Oh my goodness! It's beautiful. So handy. And DUMP
   
117     is less than 50 lines of Forth? 
   
118 
   119     Okay, my bitter annoyance is melting away.
   
120 
   121     I can totally read my word.
   
122 
   123     The little-endian data is annoying, but
   
124 
   125             C4 EE  4  8
   
126 
   127     is 804EEC4, so that'll be the link to the previous word.    
   
128 
   129     I really wish these were grouped by 4-byte chunks and
   
130     put into big-endian order...and then run ID. on each
   
131     recognizable address (since I know they're all
   
132     0804xxxx)...
   
133 
   134     Okay, after midnight now. 
   
135 
   136     I figured it out after verifying every single statement
   
137     in the compile-mode execution of S" and wouldn't you
   
138     know it was the very last statement that was wrecking
   
139     everything?
   
140 
   141     At the very end, it calls ALIGN to get the HERE pointer
   
142     to the next 4-byte boundary (after writing an address,
   
143     length, and string to memory).
   
144 
   145     ALIGN calls ALIGNED which uses "3 INVERT" to create a
   
146     mask to zero out the last two bits of an address.
   
147     Well, it turns out my INVERT word definition was doing
   
148     this:
   
149 
   150         not word [esp]
   
151 
   152     which gives you a 16 bit number. Instead, I needed this:
   
153 
   154         not dword [esp]
   
155 
   156     to operate on the full 32 bit number.
   
157 
   158     So let me take another moment here to rant for a second:
   
159     x86 terminoloy blows! A "word" should be the natural
   
160     address size on the architecture. But in x86 land,
   
161     "word" is stuck at 16 bits! So 32 bits is a dword and 64
   
162     bits is a qword and so on. Argh! This is not the first
   
163     time I've been bitten by this garbage.
   
164 
   165                 ************************
   
166                 *** "word" is a lie! ***
   
167                 ************************
   
168 
   169     This would *definitely* explain all the segfaults since
   
170     ALIGN was masking off half of HERE and setting it to an
   
171     invalid address!
   
172 
   173     Do I dare get my hopes up?
   
174 
   175     Yeah, why not...
   
176 
   177 JONESFORTH VERSION 1
   
178 20643 CELLS REMAINING
   
179 OK
   
180 
   181     Yes! It works!
   
182 
   183 : foo ." Hello World!!!" ;
   
184 foo
   
185 Hello World!!!
   
186 
   187     Hello World, indeed.
   
188 
   189     As far as I know, my NASM interpreter port is complete
   
190     now. I'll continue with the word testing. And I have a
   
191     lot of cleanup to do. But this is a great night for
   
192     nasmjf!
   
193 
   194     I'll start the next log with CASE statement testing