1 # Function calls in a single line. 2 # 3 # To run (on Linux): 4 # $ ./ntranslate 0*.subx apps/subx-common.subx apps/calls.subx 5 # $ mv a.elf apps/calls 6 # $ chmod +x apps/calls 7 # 8 # Example 1: 9 # $ echo '(foo %eax)' | apps/calls 10 # # . (foo %eax) # output has comments 11 # ff 6/subop/push %eax # push 12 # e8/call foo/disp32 # call 13 # 81 0/subop/add %esp 4/imm32 # undo push 14 # 15 # Example 2: 16 # $ echo '(foo Var1 *(eax + 4) "blah")' | apps/calls 17 # # . (foo Var1 *(eax + 4) "blah") 18 # 68/push "blah"/imm32 19 # ff 6/subop/push *(eax + 4) # push args in.. 20 # 68/push Var1/imm32 # ..reverse order 21 # e8/call foo/disp32 22 # 81 0/subop/add %esp 4/imm32 # undo pushes 23 # 24 # Calls always begin with '(' as the first non-whitespace on a line. 25 26 == code 27 28 Entry: # run tests if necessary, convert stdin if not 29 # . prolog 30 89/<- %ebp 4/r32/esp 31 32 # initialize heap 33 # . Heap = new-segment(Heap-size) 34 # . . push args 35 68/push Heap/imm32 36 ff 6/subop/push *Heap-size 37 # . . call 38 e8/call new-segment/disp32 39 # . . discard args 40 81 0/subop/add %esp 8/imm32 41 42 # - if argc > 1 and argv[1] == "test", then return run_tests() 43 # if (argc <= 1) goto run-main 44 81 7/subop/compare *ebp 1/imm32 45 7e/jump-if-lesser-or-equal $run-main/disp8 46 # if (!kernel-string-equal?(argv[1], "test")) goto run-main 47 # . eax = kernel-string-equal?(argv[1], "test") 48 # . . push args 49 68/push "test"/imm32 50 ff 6/subop/push *(ebp+8) 51 # . . call 52 e8/call kernel-string-equal?/disp32 53 # . . discard args 54 81 0/subop/add %esp 8/imm32 55 # . if (eax == 0) goto run-main 56 3d/compare-eax-and 0/imm32 57 74/jump-if-equal $run-main/disp8 58 # run-tests() 59 e8/call run-tests/disp32 60 # syscall(exit, *Num-test-failures) 61 8b/-> *Num-test-failures 3/r32/ebx 62 eb/jump $main:end/disp8 63 $run-main: 64 # - otherwise convert stdin 65 # convert(Stdin, Stdout) 66 # . . push args 67 68/push Stdout/imm32 68 68/push Stdin/imm32 69 # . . call 70 e8/call convert/disp32 71 # . . discard args 72 81 0/subop/add %esp 8/imm32 73 # syscall(exit, 0) 74 bb/copy-to-ebx 0/imm32 75 $main:end: 76 b8/copy-to-eax 1/imm32/exit 77 cd/syscall 0x80/imm8 78 79 convert: # in : (address buffered-file), out : (address buffered-file) -> <void> 80 # pseudocode: 81 # var line = new-stream(512, 1) 82 # var words : (address stream slice) = new-stream(16, 8) # at most function name and 15 args 83 # while true 84 # clear-stream(line) 85 # read-line-buffered(in, line) 86 # if (line->write == 0) break # end of file 87 # skip-chars-matching-whitespace(line) 88 # if line->data[line->read] != '(' 89 # write-stream-data(out, line) 90 # continue 91 # # emit comment 92 # write-buffered(out, "# . ") 93 # write-stream-data(out, line) 94 # # emit code 95 # ++line->read # skip '(' 96 # clear-stream(words) 97 # words = parse-line(line) 98 # emit-call(out, words) 99 # flush(out) 100 # 101 # . prolog 102 55/push-ebp 103 89/<- %ebp 4/r32/esp 104 # . save registers 105 # var line/ecx : (address stream byte) = stream(512) 106 81 5/subop/subtract %esp 0x200/imm32 107 68/push 0x200/imm32/length 108 68/push 0/imm32/read 109 68/push 0/imm32/write 110 89/<- %ecx 4/r32/esp 111 # var words/edx : (address stream slice) = stream(16, 8) 112 81 5/subop/subtract %esp 0x80/imm32 113 68/push 0x80/imm32/length 114 68/push 0/imm32/read 115 68/push 0/imm32/write 116 89/<- %edx 4/r32/esp 117 $convert:loop: 118 # clear-stream(line) 119 # . . push args 120 51/push-ecx 121 # . . call 122 e8/call clear-stream/disp32 123 # . . discard args 124 81 0/subop/add %esp 4/imm32 125 # read-line-buffered(in, line) 126 # . . push args 127 51/push-ecx 128 ff 6/subop/push *(ebp+8) 129 # . . call 130 e8/call read-line-buffered/disp32 131 # . . discard args 132 81 0/subop/add %esp 8/imm32 133 $convert:check0: 134 # if (line->write == 0) break 135 81 7/subop/compare *ecx 0/imm32 136 0f 84/jump-if-equal $convert:break/disp32 137 # TODO 138 e9/jump $convert:loop/disp32 139 $convert:break: 140 # flush(out) 141 # . . push args 142 ff 6/subop/push *(ebp+0xc) 143 # . . call 144 e8/call flush/disp32 145 # . . discard args 146 81 0/subop/add %esp 4/imm32 147 $convert:end: 148 # . reclaim locals 149 81 0/subop/add %esp 0x298/imm32 # 0x20c + 0x8c 150 # . restore registers 151 # . epilog 152 89/<- %esp 5/r32/ebp 153 5d/pop-to-ebp 154 c3/return 155 156 parse-line: # line : (address stream byte), words : (address stream slice) 157 # pseudocode: 158 # var word-slice : (address slice) 159 # while true 160 # word-slice = next-word-string-or-expression-without-metadata(line) 161 # if slice-empty?(word-slice) 162 # break # end of line 163 # write-int(words, word-slice->start) 164 # write-int(words, word-slice->end) 165 # 166 # . prolog 167 55/push-ebp 168 89/<- %ebp 4/r32/esp 169 # . save registers 170 $parse-line:end: 171 # . reclaim locals 172 # . restore registers 173 # . epilog 174 89/<- %esp 5/r32/ebp 175 5d/pop-to-ebp 176 c3/return 177 178 emit-call: # out : (address buffered-file), words : (address stream slice) 179 # pseudocode: 180 # if (words->write < 8) abort 181 # curr = &words->data[words->write-8] 182 # min = words->data 183 # # emit pushes 184 # while true 185 # if (curr <= min) break 186 # if *curr in '%' '*' 187 # write-buffered(out, "ff 6/subop/push ") 188 # write-slice-buffered(out, curr) 189 # write-buffered(out, "/imm32\n") 190 # else 191 # write-buffered(out, "68/push ") 192 # write-slice-buffered(out, curr) 193 # write-buffered(out, "/imm32\n") 194 # curr -= 8 195 # # emit call 196 # write-buffered(out, "e8/call ") 197 # write-slice-buffered(out, curr) 198 # write-buffered(out, "/disp32\n") 199 # # emit pops 200 # write-buffered(out, "81 0/subop/add %esp ") 201 # print-int32-buffered(out, words->write >> 1 - 4) 202 # write-buffered(out, "/imm32\n") 203 # 204 # . prolog 205 55/push-ebp 206 89/<- %ebp 4/r32/esp 207 # . save registers 208 $emit-call:end: 209 # . reclaim locals 210 # . restore registers 211 # . epilog 212 89/<- %esp 5/r32/ebp 213 5d/pop-to-ebp 214 c3/return 215 216 next-word-string-or-expression-without-metadata: # line : (address stream), word-slice : (address slice) 217 # pseudocode: 218 # skip-chars-matching(line, ' ') 219 # if line->read >= line->write # end of line 220 # out = {0, 0} 221 # return 222 # out->start = &line->data[line->read] 223 # if line->data[line->read] == '#' # comment 224 # out.end = &line->data[line->write] # skip to end of line 225 # return 226 # if line->data[line->read] == '"' # string literal 227 # skip-string(line) 228 # out.end = &line->data[line->read] # no metadata 229 # return 230 # if line->data[line->read] == '*' # expression 231 # if line->data[line->read + 1] == ' ' 232 # abort 233 # if line->data[line->read + 1] == '(' 234 # skip-until-close-paren(line) 235 # if (line->data[line->read] != ')' 236 # abort 237 # ++line->data[line->read] to skip ')' 238 # out->end = &line->data[line->read] 239 # return 240 # if line->data[line->read] == ')' 241 # ++line->read to skip ')' 242 # # make sure there's nothing else of importance 243 # if line->read >= line->write 244 # out = {0, 0} 245 # return 246 # if line->data[line->read] != ' ' 247 # abort 248 # skip-chars-matching-whitespace(line) 249 # if line->read >= line->write 250 # out = {0, 0} 251 # return 252 # if line->data[line->read] != '#' # only thing permitted after ')' is a comment 253 # abort 254 # out.end = &line->data[line->write] # skip to end of line 255 # return 256 # # default case: read a word -- but no metadata 257 # while true 258 # if line->read >= line->write 259 # break 260 # if line->data[line->read] == ' ' 261 # break 262 # if line->data[line->read] == '/' 263 # abort 264 # ++line->read 265 # out.end = &line->data[line->read] 266 # 267 # registers: 268 # ecx: often line->read 269 # eax: often line->data[line->read] 270 # 271 # . prolog 272 55/push-ebp 273 89/<- %ebp 4/r32/esp 274 # . save registers 275 50/push-eax 276 51/push-ecx 277 56/push-esi 278 57/push-edi 279 # skip-chars-matching(line, ' ') 280 # . . push args 281 68/push 0x20/imm32/space 282 ff 6/subop/push *(ebp+8) 283 # . . call 284 e8/call skip-chars-matching/disp32 285 # . . discard args 286 81 0/subop/add %esp 8/imm32 287 $next-word-string-or-expression-without-metadata:check0: 288 # if (line->read >= line->write) clear out and return 289 # . eax = line->read 290 8b/-> *(esi+4) 0/r32/eax 291 # . if (eax < line->write) goto next check 292 3b/compare *esi 0/r32/eax 293 7c/jump-if-lesser $next-word-string-or-expression-without-metadata:check-for-comment/disp8 294 # . return out = {0, 0} 295 c7 0/subop/copy *edi 0/imm32 296 c7 0/subop/copy *(edi+4) 0/imm32 297 e9/jump $next-word-string-or-expression-without-metadata:end/disp32 298 $next-word-string-or-expression-without-metadata:check-for-comment: 299 # out->start = &line->data[line->read] 300 8b/-> *(esi+4) 1/r32/ecx 301 8d/copy-address *(esi+ecx+0xc) 0/r32/eax 302 89/<- *edi 0/r32/eax 303 # if (line->data[line->read] != '#') goto next check 304 # . eax = line->data[line->read] 305 31/xor %eax 0/r32/eax 306 8a/copy-byte *(esi+ecx+0xc) 0/r32/AL 307 # . if (eax != '#') goto next check 308 3d/compare-eax-and 0x23/imm32/pound 309 75/jump-if-not-equal $next-word-string-or-expression-without-metadata:check-for-string-literal/disp8 310 $next-word-string-or-expression-without-metadata:comment: 311 # out->end = &line->data[line->write] 312 8b/-> *esi 0/r32/eax 313 8d/copy-address *(esi+eax+0xc) 0/r32/eax 314 89/<- *(edi+4) 0/r32/eax 315 # line->read = line->write # skip rest of line 316 8b/-> *esi 0/r32/eax 317 89/<- *(esi+4) 0/r32/eax 318 # return 319 eb/jump $next-word-string-or-expression-without-metadata:end/disp8 320 $next-word-string-or-expression-without-metadata:check-for-string-literal: 321 # if (line->data[line->read] != '"') goto next check 322 3d/compare-eax-and 0x22/imm32/dquote 323 75/jump-if-not-equal $next-word-string-or-expression-without-metadata:check-for-expression/disp8 324 $next-word-string-or-expression-without-metadata:string-literal: 325 # skip-string(line) 326 # . . push args 327 56/push-esi 328 # . . call 329 e8/call skip-string/disp32 330 # . . discard args 331 81 0/subop/add %esp 4/imm32 332 # out->end = &line->data[line->read] 333 8b/-> *(esi+4) 1/r32/ecx 334 8d/copy-address *(esi+ecx+0xc) 0/r32/eax 335 89/<- *(edi+4) 0/r32/eax 336 # return 337 eb/jump $next-word-string-or-expression-without-metadata:end/disp8 338 $next-word-string-or-expression-without-metadata:check-for-expression: 339 # if (line->data[line->read] != '*') goto next check 340 3d/compare-eax-and 0x2a/imm32/asterisk 341 75/jump-if-not-equal $next-word-string-or-expression-without-metadata:check-for-end-of-call/disp8 342 # if (line->data[line->read + 1] == ' ') goto error1 343 8a/copy-byte *(esi+ecx+0xd) 0/r32/AL 344 3d/compare-eax-and 0x20/imm32/space 345 74/jump-if-equal $next-word-string-or-expression-without-metadata:error1/disp8 346 # if (line->data[line->read] != '(') goto regular word 347 3d/compare-eax-and 0x28/imm32/open-paren 348 75/jump-if-not-equal $next-word-string-or-expression-without-metadata:regular-word/disp8 349 $next-word-string-or-expression-without-metadata:paren: 350 # skip-until-close-paren(line) 351 # . . push args 352 56/push-esi 353 # . . call 354 e8/call skip-until-close-paren/disp32 355 # . . discard args 356 81 0/subop/add %esp 4/imm32 357 # if (line->data[line->read] != ')') goto error2 358 # . eax = line->data[line->read] 359 8b/-> *(esi+4) 1/r32/ecx 360 8a/copy-byte *(esi+ecx+0xc) 0/r32/AL 361 # . if (eax != ')') goto error2 362 3d/compare-eax-and 0x29/imm32/close-paren 363 75/jump-if-not-equal $next-word-string-or-expression-without-metadata:error2/disp8 364 # skip ')' 365 ff 0/subop/increment *(esi+4) 366 # out->end = &line->data[line->read] 367 8b/-> *(esi+4) 1/r32/ecx 368 8d/copy-address *(esi+ecx+0xc) 0/r32/eax 369 89/<- *(edi+4) 0/r32/eax 370 # return 371 eb/jump $next-word-string-or-expression-without-metadata:end/disp8 372 $next-word-string-or-expression-without-metadata:end: 373 # . restore registers 374 5f/pop-to-edi 375 5e/pop-to-esi 376 59/pop-to-ecx 377 58/pop-to-eax 378 # . epilog 379 89/<- %esp 5/r32/ebp 380 5d/pop-to-ebp 381 c3/return 382 383 # . . vim:nowrap:textwidth=0