mu/apps/calls.subx

217 lines
6.4 KiB
Plaintext
Raw Normal View History

# 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