1 Alright, continuing from the resolution I made at the
2 end of Dev Log 12, here's the TODO plan for making
3 strings work in exported ELF executables. (Spoiler: it's
4 Moore-style strings, the dang guy was right again!!!)
5
6 [ ] Strings go right into the compiled function (not
7 a separate data area)
8 [ ] Instruction to push string address on the stack
9 [ ] Instruction to skip over the string itself
10
11 I'm still not happy about this "jump" logic being needed
12 because it feels like one of those assembly language
13 "tricks". But I don't see any way out of it, either.
14
15 First, I gotta see if I can jump over a string.
16
17 ; WIP:
18 ; jump over string instruction
19 mov byte [edx], 0xEB ; i386 opcode for JMP rel8
20 mov byte [edx+1], 4 ; jump this many bytes
21 mov byte [edx+2], 0xDE
22 mov byte [edx+3], 0xAD
23 mov byte [edx+4], 0xBE
24 mov byte [edx+5], 0xEF
25 add edx, 6 ; update "here"
26
27 What I've got here is a "string" of data 0xDEADBEEF that
28 I want to jump over. I put that code in my QUOTE word
29 and kept re-running a little test script until I got the
30 bugs worked out:
31
32 stringtest.sh
33 ------------------------------------------
34 #!/usr/bin/bash
35
36 set -e
37
38 ./build.sh
39
40 echo ': f "foo" ;' \
41 '"f" find inspect' \
42 '"f" find dump-word' \
43 'f say' | ./meow5
44 ------------------------------------------
45
46 The output once it stopped crashing:
47
48 f: 11 bytes IMMEDIATE COMPILE
49 eb 4 de ad be ef 68 98 c1 4 8
50 foo
51
52 You can see the "eb 4" opcode and operand to jump over
53 the next four bytes. And the next four bytes are the
54 deadbeef data. The next part, starting with 68, is the
55 existing QUOTE functionality: it's the opcode to push
56 the address of the string onto the stack.
57
58 Okay, that's a nice little test. Now I need to actually
59 store the string here in the compiled function.
60
61 A couple nights later...
62
63 So I went up a couple wrong alleys, even on the
64 jump-over-string issue.
65
66 For one thing, i386 doesn't have an opcode to directly
67 get the instruction pointer (if you can believe it).
68
69 So a common trick is to use `call` to do a relative jump
70 to the next address *and* push the next address on the
71 stack.
72
73 But then I got to thinking: well, if `call` is going to
74 jump anyway, I can use that to get the current address
75 of the string at runtime *AND* jump over it at the same
76 time!
77
78 And sure enough, that works perfectly.
79
80 I made a stand-alone program to test the concept and
81 also to demo it with a nice little complete "hello
82 world":
83
84 https://ratfactor.com/cards/asm-data-in-text
85
86 Then I "ported" that concept to Meow5, embedding the
87 opcode for the call and the distance of the relative
88 "jump" (the call does the jumping) after the string has
89 been copied into the compiled word's machine code and
90 the length has been determined.
91
92 I had plenty of trial and error and I had to restore my
93 ELF writing word "make_elf". But it didn't take that
94 long.
95
96 Now I can finally make the canonical test program and
97 the namesake...but there's a funny catch.
98
99 I make a meow word:
100
101 $ ./meow5
102 : meow "Meow.\n" print ;
103
104 It runs in the interpreter:
105
106 meow
107 Meow.
108
109 Then I make a meow3 word:
110
111 : meow3 meow meow meow exit ;
112
113 It contains "exit" so the executable I export will exit
114 properly and not segfault!
115
116 But I can't run it now or the interpreter will exit!
117
118 Now I'll export that word:
119
120 make_elf meow3
121 Wrote to "meow3".
122
123 And let's see what happens when I run it in the
124 interpreter:
125
126 meow3
127 Meow.
128 Meow.
129 Meow.
130 $
131
132 Yup, prints three meows and exits!
133
134 Now, how about that executable?
135
136 $ ls -l meow3
137 -rwxr-xr-x 1 dave users 227 Nov 9 19:23 meow3
138
139 Two hundred and twenty-seven bytes. Not bad. :-)
140
141 (A "hello world!" is just 144 bytes!)
142
143 Aaaaaaand now for the moment of truth:
144
145 $ ./meow3
146 Meow.
147 Meow.
148 Meow.
149
150 Yeah!!!
151
152 Okay, I said there was a catch earlier. It's this: why
153 didn't I call the word "meow5" and have it print 5
154 meows?
155
156 The answer is funny: it's because the exported program
157 is given the same name as the word that is exported...so
158 exporting a "meow5" program in the same directory as
159 "meow5"...well, you can see the problem.
160
161 Obviously I can solve this by running it in another
162 directory or something.
163
164 One of the things I realized earlier was that I don't
165 have some really basic words like "dup" (when I wanted
166 to use them!). So I'll probably add some of those.
167
168 And some words can't be compiled (like "say", which has
169 an absolute reference to a newline character in the
170 running interpreter, which will be a segfault in an
171 exported executable.
172
173 So I'm going to document and test stuff and play around
174 with it.
175
176 But this is it for the log. I consider this a success.
177
178 The End. 2023-11-09
179
180 Then:
181
182 Ha ha, nope! I couldn't leave it alone! Yeah, this
183 works, but I think I'm really close to having an actual
184 programming language if I just add some branching...
185
186 See you in log14.txt LOL