colorful rat Ratfactor.com > Dave's Repos

meow5

A stack-based pure inlining concatenative programming language written in NASM assembly
git clone http://ratfactor.com/repos/meow5/meow5.git

meow5/log13.txt

Download raw file: log13.txt

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