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