1 Now we come to a bit of a challenge. The method of
2 starting JonesFORTH with the rest of the language
3 implementation *and* still allowing user input on
4 STDIN is quite clever:
5
6 cat jonesforth.f - | ./jonesforth
7
8 That is, have cat read the FORTH source file and then
9 whatever is supplied on STDIN (using the standard
10 '-' parameter to indicate STDIN in place of a file)
11 and pipe the lot to the jonesforth executable.
12
13 Unfortunately, it crashes my port:
14
15 eeepc:~/nasmjf$ cat jonesforth/jonesforth.f - | ./nasmjf
16
17 Segmentation fault
18
19 So we have a problem.
20
21 And this is turning out to be hard to test.
22
23 Well, it seems there are a couple routes I could take
24 here:
25
26 1. Hand-type the contents of jonesforth.f and see what
27 causes the crash. I'm okay with the effort - I plan
28 to go through that source line-by-line to understand
29 it anyway. But the problem is: how do I incrementally
30 save what I've typed so far?
31
32 2. Use divide-and-conquer to find out how much of
33 'jonesforth.f' I can run before we get a crash. That
34 would probably work - but it's going to be an annoying
35 process and I'll be working blind.
36
37 3. Figure out how to read in 'jonesforth.f' on startup
38 as part of the executable. This will allow debugging
39 in GDB to continue (no doubt I could figure out some
40 arcane way of redirecting or piping that to STDIN
41 while in a GDB session or attach to the process or
42 something...but I'm not enthusiastic about figuring
43 that out. I'd rather write code.)
44
45 As you can likely guess, I'm going with Option 3. I'm going
46 to have the nasmjf executabe read 'jonesforth.f' upon
47 startup. The plan is something like this:
48
49 A. Open the file using syscalls
50 B. Read into a fixed-size reserved buffer
51 C. Keep track of line numbers for reference?
52 D. Feed the source into the interpreter somehow
53 E. Now I can debug the crash?
54 F. When EOF reached, close the file and start accepting
55 interpreter input from STDIN again.
56
57 The only challenging part, it seems to me, is D: feeding the
58 file into the interpreter. Words like KEY are currently
59 hard-coded to read from STDIN, right? So maybe I need a global
60 switch to read from my line buffer instead?
61
62 Let's get started!
63
64 For A, I've got this near the top:
65
66 SECTION .bss
67 line_buffer: resb 1024 ; for storing source lines from file
68
69 Then
70
71 SECTION .data
72 jfsource: db "jonesforth/jonesforth.f", 0h
73
74 And then reading the file - another snippet adapted from
75 asmtutor.com!
76
77
78 Breakpoint 1, _start () at nasmjf.asm:82
79 82 cld ; Clear the "direction flag" which means the string instructions (such
80 ...
81 118 mov ecx, 0 ; read only flag for open
82 119 mov ebx, jfsource
83 120 mov eax, __NR_open
84 121 int 80h ; fd now in eax
85 124 mov edx, 1024 ; amt to read
86 125 mov ecx, line_buffer ; address of .bss reserved space
87 126 mov ebx, eax ; fd from pushed eax above
88 127 mov eax, __NR_read
89 128 int 80h
90 (gdb) x/s (int)&line_buffer
91 0x804db30 <line_buffer>: "\\ -*- text -*-\n\\\tA sometimes minimal FORTH
92 compiler and tutorial for Linux / i386 systems. -*- asm -*-\n\\\tBy Richard W.M.
93 Jones <rich@annexia.org> http://annexia.org/forth\n\\\tThis is PUBLIC DOMAIN"...
94
95 Awesome! Of course it works. It's a trivial operation, even
96 in assembly.
97
98 Anyway, now to figure out how to feed the source into the
99 interpreter.
100
101 Weeks have passed. Progress has gone at a snail's pace and
102 I've often fallen asleep with barely a single assembly
103 instruction written.
104
105 Nevertheless, I think I'm getting close.
106
107 TIME PASSES...
108
109 Okay, a month later and I've got it reading a test.f file
110 to make sure I can read in N lines of a Forth source file
111 when the interpreter starts and then hand input back to
112 the keyboard.
113
114 Here's the entirety of test.f:
115
116 1 .
117 2 .
118 3 .
119 4 .
120 5 .
121 6 .
122 7 .
123 8 .
124
125 So it's just Forth for printing the line number on each line
126 so I can see if it correctly stops after six lines.
127
128 jfsource: db "test.f" , 0h
129 %assign __lines_of_jf_to_read 6
130
131 What does it look like when it runs? Let's find out:
132
133 123456
134
135 Yay! It reads and executes six lines, then waits for user input.
136 Perfect!
137
138 This took about a month because of a ton of stupid bugs, off-by-
139 one errors, and that sort of thing. But mostly because I was
140 really tired and kept falling asleep.
141
142 Anyway, now I can read N lines of jonesforth.f and test it out
143 a bit at a time!
144
145 Next night: okay, here goes nothing:
146
147 jfsource: db "jonesforth/jonesforth.f", 0h
148 %assign __lines_of_jf_to_read 52
149
150 This reads to line 52 of jonesforth.f, which is a bunch of comments
151 and a definiton of / and MOD:
152
153 : / /MOD SWAP DROP ;
154 : MOD /MOD DROP ;
155
156 10 5 / .
157 2
158 10 5 MOD .
159 0
160 10 3 / .
161 3
162 10 3 MOD .
163 1
164
165 Wow, that actually works! So now I can start logging my
166 progress as I load and test more of the FORTH portion of
167 the JonesFORTH implementation.
168
169 Now I'm loading to line 70, which contains definitions for
170 some more handy primitives.
171
172 I'll test them out now:
173
174 3 5 . SPACE .
175 5 3
176
177 3 5 . CR .
178 5
179 3
180
181 3 NEGATE .
182 4294967293
183
184 TRUE .
185 1
186 FALSE .
187 0
188 TRUE NOT .
189 0
190 FALSE NOT .
191 1
192
193 Heh, I'll just trust at the negaative value is correct for
194 now.
195
196 So, this is great. I'll just keep working my way down
197 jonesforth.f until I've tested everything and/or reproduced
198 the crash I was experiencing before.