# Function calls in a single line. # # To run (on Linux): # $ ./ntranslate 0*.subx apps/subx-common.subx apps/calls.subx # $ mv a.elf apps/calls # $ chmod +x apps/calls # # Example 1: # $ echo '(foo %eax)' | apps/calls # # . (foo %eax) # output has comments # ff 6/subop/push %eax # push # e8/call foo/disp32 # call # 81 0/subop/add %esp 4/imm32 # undo push # # Example 2: # $ echo '(foo Var1 *(eax + 4) "blah")' | apps/calls # # . (foo Var1 *(eax + 4) "blah") # 68/push "blah"/imm32 # ff 6/subop/push *(eax + 4) # push args in.. # 68/push Var1/imm32 # ..reverse order # e8/call foo/disp32 # 81 0/subop/add %esp 4/imm32 # undo pushes # # Calls always begin with '(' as the first non-whitespace on a line. == code Entry: # run tests if necessary, convert stdin if not # . prolog 89/<- %ebp 4/r32/esp # initialize heap # . Heap = new-segment(Heap-size) # . . push args 68/push Heap/imm32 ff 6/subop/push *Heap-size # . . call e8/call new-segment/disp32 # . . discard args 81 0/subop/add %esp 8/imm32 # - if argc > 1 and argv[1] == "test", then return run_tests() # if (argc <= 1) goto run-main 81 7/subop/compare *ebp 1/imm32 7e/jump-if-lesser-or-equal $run-main/disp8 # if (!kernel-string-equal?(argv[1], "test")) goto run-main # . eax = kernel-string-equal?(argv[1], "test") # . . push args 68/push "test"/imm32 ff 6/subop/push *(ebp+8) # . . call e8/call kernel-string-equal?/disp32 # . . discard args 81 0/subop/add %esp 8/imm32 # . if (eax == 0) goto run-main 3d/compare-eax-and 0/imm32 74/jump-if-equal $run-main/disp8 # run-tests() e8/call run-tests/disp32 # syscall(exit, *Num-test-failures) 8b/-> *Num-test-failures 3/r32/ebx eb/jump $main:end/disp8 $run-main: # - otherwise convert stdin # convert(Stdin, Stdout) # . . push args 68/push Stdout/imm32 68/push Stdin/imm32 # . . call e8/call convert/disp32 # . . discard args 81 0/subop/add %esp 8/imm32 # syscall(exit, 0) bb/copy-to-ebx 0/imm32 $main:end: b8/copy-to-eax 1/imm32/exit cd/syscall 0x80/imm8 convert: # in : (address buffered-file), out : (address buffered-file) -> # pseudocode: # var line = new-stream(512, 1) # var words : (address stream slice) = new-stream(16, 8) # at most function name and 15 args # while true # clear-stream(line) # read-line-buffered(in, line) # if (line->write == 0) break # end of file # skip-chars-matching-whitespace(line) # if line->data[line->read] != '(' # write-stream-data(out, line) # continue # # emit comment # write-buffered(out, "# . ") # write-stream-data(out, line) # # emit code # ++line->read # skip '(' # clear-stream(words) # words = parse-line(line) # emit-call(out, words) # flush(out) # # . prolog 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers # var line/ecx : (address stream byte) = stream(512) 81 5/subop/subtract %esp 0x200/imm32 68/push 0x200/imm32/length 68/push 0/imm32/read 68/push 0/imm32/write 89/<- %ecx 4/r32/esp # var words/edx : (address stream slice) = stream(16, 8) 81 5/subop/subtract %esp 0x80/imm32 68/push 0x80/imm32/length 68/push 0/imm32/read 68/push 0/imm32/write 89/<- %edx 4/r32/esp $convert:loop: # clear-stream(line) # . . push args 51/push-ecx # . . call e8/call clear-stream/disp32 # . . discard args 81 0/subop/add %esp 4/imm32 # read-line-buffered(in, line) # . . push args 51/push-ecx ff 6/subop/push *(ebp+8) # . . call e8/call read-line-buffered/disp32 # . . discard args 81 0/subop/add %esp 8/imm32 $convert:check0: # if (line->write == 0) break 81 7/subop/compare *ecx 0/imm32 0f 84/jump-if-equal $convert:break/disp32 # TODO e9/jump $convert:loop/disp32 $convert:break: # flush(out) # . . push args ff 6/subop/push *(ebp+0xc) # . . call e8/call flush/disp32 # . . discard args 81 0/subop/add %esp 4/imm32 $convert:end: # . reclaim locals 81 0/subop/add %esp 0x298/imm32 # 0x20c + 0x8c # . restore registers # . epilog 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return parse-line: # line : (address stream byte), words : (address stream slice) # pseudocode: # var word-slice : (address slice) # while true # word-slice = next-word-string-or-expression-without-metadata(line) # if slice-empty?(word-slice) # break # end of line # write-int(words, word-slice->start) # write-int(words, word-slice->end) # # . prolog 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers $parse-line:end: # . reclaim locals # . restore registers # . epilog 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return emit-call: # out : (address buffered-file), words : (address stream slice) # pseudocode: # if (words->write < 8) abort # curr = &words->data[words->write-8] # min = words->data # # emit pushes # while true # if (curr <= min) break # if *curr in '%' '*' # write-buffered(out, "ff 6/subop/push ") # write-slice-buffered(out, curr) # write-buffered(out, "/imm32\n") # else # write-buffered(out, "68/push ") # write-slice-buffered(out, curr) # write-buffered(out, "/imm32\n") # curr -= 8 # # emit call # write-buffered(out, "e8/call ") # write-slice-buffered(out, curr) # write-buffered(out, "/disp32\n") # # emit pops # write-buffered(out, "81 0/subop/add %esp ") # print-int32-buffered(out, words->write >> 1 - 4) # write-buffered(out, "/imm32\n") # # . prolog 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers $emit-call:end: # . reclaim locals # . restore registers # . epilog 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return next-word-string-or-expression-without-metadata: # line : (address stream), word-slice : (address slice) # pseudocode: # skip-chars-matching(line, ' ') # if line->read >= line->write # end of line # out = {0, 0} # return # out->start = &line->data[line->read] # if line->data[line->read] == '#' # comment # out.end = &line->data[line->write] # skip to end of line # return # if line->data[line->read] == '"' # string literal # skip-string(line) # out.end = &line->data[line->read] # no metadata # return # if line->data[line->read] == '*' # expression # if line->data[line->read + 1] == ' ' # abort # if line->data[line->read + 1] == '(' # skip-until-close-paren(line) # if (line->data[line->read] != ')' # abort # ++line->data[line->read] to skip ')' # out->end = &line->data[line->read] # return # if line->data[line->read] == ')' # ++line->read to skip ')' # # make sure there's nothing else of importance # if line->read >= line->write # out = {0, 0} # return # if line->data[line->read] != ' ' # abort # skip-chars-matching-whitespace(line) # if line->read >= line->write # out = {0, 0} # return # if line->data[line->read] != '#' # only thing permitted after ')' is a comment # abort # out.end = &line->data[line->write] # skip to end of line # return # # default case: read a word -- but no metadata # while true # if line->read >= line->write # break # if line->data[line->read] == ' ' # break # if line->data[line->read] == '/' # abort # ++line->read # out.end = &line->data[line->read] # # registers: # ecx: often line->read # eax: often line->data[line->read] # # . prolog 55/push-ebp 89/<- %ebp 4/r32/esp # . save registers 50/push-eax 51/push-ecx 56/push-esi 57/push-edi # skip-chars-matching(line, ' ') # . . push args 68/push 0x20/imm32/space ff 6/subop/push *(ebp+8) # . . call e8/call skip-chars-matching/disp32 # . . discard args 81 0/subop/add %esp 8/imm32 $next-word-string-or-expression-without-metadata:check0: # if (line->read >= line->write) clear out and return # . eax = line->read 8b/-> *(esi+4) 0/r32/eax # . if (eax < line->write) goto next check 3b/compare *esi 0/r32/eax 7c/jump-if-lesser $next-word-string-or-expression-without-metadata:check-for-comment/disp8 # . return out = {0, 0} c7 0/subop/copy *edi 0/imm32 c7 0/subop/copy *(edi+4) 0/imm32 e9/jump $next-word-string-or-expression-without-metadata:end/disp32 $next-word-string-or-expression-without-metadata:check-for-comment: # out->start = &line->data[line->read] 8b/-> *(esi+4) 1/r32/ecx 8d/copy-address *(esi+ecx+0xc) 0/r32/eax 89/<- *edi 0/r32/eax # if (line->data[line->read] != '#') goto next check # . eax = line->data[line->read] 31/xor %eax 0/r32/eax 8a/copy-byte *(esi+ecx+0xc) 0/r32/AL # . if (eax != '#') goto next check 3d/compare-eax-and 0x23/imm32/pound 75/jump-if-not-equal $next-word-string-or-expression-without-metadata:check-for-string-literal/disp8 $next-word-string-or-expression-without-metadata:comment: # out->end = &line->data[line->write] 8b/-> *esi 0/r32/eax 8d/copy-address *(esi+eax+0xc) 0/r32/eax 89/<- *(edi+4) 0/r32/eax # line->read = line->write # skip rest of line 8b/-> *esi 0/r32/eax 89/<- *(esi+4) 0/r32/eax # return eb/jump $next-word-string-or-expression-without-metadata:end/disp8 $next-word-string-or-expression-without-metadata:check-for-string-literal: # if (line->data[line->read] != '"') goto next check 3d/compare-eax-and 0x22/imm32/dquote 75/jump-if-not-equal $next-word-string-or-expression-without-metadata:check-for-expression/disp8 $next-word-string-or-expression-without-metadata:string-literal: # skip-string(line) # . . push args 56/push-esi # . . call e8/call skip-string/disp32 # . . discard args 81 0/subop/add %esp 4/imm32 # out->end = &line->data[line->read] 8b/-> *(esi+4) 1/r32/ecx 8d/copy-address *(esi+ecx+0xc) 0/r32/eax 89/<- *(edi+4) 0/r32/eax # return eb/jump $next-word-string-or-expression-without-metadata:end/disp8 $next-word-string-or-expression-without-metadata:check-for-expression: # if (line->data[line->read] != '*') goto next check 3d/compare-eax-and 0x2a/imm32/asterisk 75/jump-if-not-equal $next-word-string-or-expression-without-metadata:check-for-end-of-call/disp8 # if (line->data[line->read + 1] == ' ') goto error1 8a/copy-byte *(esi+ecx+0xd) 0/r32/AL 3d/compare-eax-and 0x20/imm32/space 74/jump-if-equal $next-word-string-or-expression-without-metadata:error1/disp8 # if (line->data[line->read] != '(') goto regular word 3d/compare-eax-and 0x28/imm32/open-paren 75/jump-if-not-equal $next-word-string-or-expression-without-metadata:regular-word/disp8 $next-word-string-or-expression-without-metadata:paren: # skip-until-close-paren(line) # . . push args 56/push-esi # . . call e8/call skip-until-close-paren/disp32 # . . discard args 81 0/subop/add %esp 4/imm32 # if (line->data[line->read] != ')') goto error2 # . eax = line->data[line->read] 8b/-> *(esi+4) 1/r32/ecx 8a/copy-byte *(esi+ecx+0xc) 0/r32/AL # . if (eax != ')') goto error2 3d/compare-eax-and 0x29/imm32/close-paren 75/jump-if-not-equal $next-word-string-or-expression-without-metadata:error2/disp8 # skip ')' ff 0/subop/increment *(esi+4) # out->end = &line->data[line->read] 8b/-> *(esi+4) 1/r32/ecx 8d/copy-address *(esi+ecx+0xc) 0/r32/eax 89/<- *(edi+4) 0/r32/eax # return eb/jump $next-word-string-or-expression-without-metadata:end/disp8 $next-word-string-or-expression-without-metadata:end: # . restore registers 5f/pop-to-edi 5e/pop-to-esi 59/pop-to-ecx 58/pop-to-eax # . epilog 89/<- %esp 5/r32/ebp 5d/pop-to-ebp c3/return # . . vim:nowrap:textwidth=0