217 lines
6.4 KiB
Plaintext
217 lines
6.4 KiB
Plaintext
|
# 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) -> <void>
|
||
|
# 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 == '%' || *curr == '*')
|
||
|
# 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
|
||
|
|
||
|
# . . vim:nowrap:textwidth=0
|