# Read a text file of SubX instructions from stdin, and convert it into a list # of whitespace-separated ascii hex bytes on stdout. Label definitions and # uses are left untouched. # # To run: # $ bootstrap/bootstrap translate [01]*.subx subx-params.subx pack.subx -o pack # $ echo '05/add-to-eax 0x20/imm32' |bootstrap/bootstrap run pack # Expected output: # 05 20 00 00 00 # 05/add-to-eax 0x20/imm32 # The original instruction gets included as a comment at the end of each # converted line. # # There's zero error-checking. For now we assume the input program is valid. # We'll continue to rely on the C++ version for error messages. == code # instruction effective address register displacement immediate # . op subop mod rm32 base index scale r32 # . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes Entry: # run tests if necessary, convert stdin if not # . prologue 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp # initialize heap # . Heap = new-segment(Heap-size) # . . push args 68/push Heap/imm32 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Heap-size/disp32 # push *Heap-size # . . call e8/call new-segment/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # - if argc > 1 and argv[1] == "test", then return run_tests() # if (argc <= 1) goto interactive 81 7/subop/compare 1/mod/*+disp8 5/rm32/ebp . . . . 0/disp8 1/imm32 # compare *ebp 7e/jump-if-<= $subx-pack-main:interactive/disp8 # if (!kernel-string-equal?(argv[1], "test")) goto interactive # . eax = kernel-string-equal?(argv[1], "test") # . . push args 68/push "test"/imm32 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) # . . call e8/call kernel-string-equal?/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . if (eax == false) goto interactive 3d/compare-eax-and 0/imm32/false 74/jump-if-= $subx-pack-main:interactive/disp8 # run-tests() e8/call run-tests/disp32 # syscall(exit, *Num-test-failures) 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/ebx Num-test-failures/disp32 # copy *Num-test-failures to ebx eb/jump $subx-pack-main:end/disp8 $subx-pack-main:interactive: # - otherwise convert stdin # subx-pack(Stdin, Stdout) # . . push args 68/push Stdout/imm32 68/push Stdin/imm32 # . . call e8/call subx-pack/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x8/imm32 # add to esp # syscall(exit, 0) bb/copy-to-ebx 0/imm32 $subx-pack-main:end: e8/call syscall_exit/disp32 # - big picture # We'll operate on each line/instruction in isolation. That way we only need to # allocate memory for converting a single instruction. # # To pack an entire file, convert every segment in it # To convert a code segment, convert every instruction (line) until the next segment header # To convert a non-data segment, convert every word until the next segment header # # primary state: line # stream of 512 bytes; abort if it ever overflows # conceptual hierarchy within a line: # line = words separated by ' ', maybe followed by comment starting with '#' # word = datum until '/', then 0 or more metadata separated by '/' # # we won't bother saving the internal structure of lines; reparsing should be cheap using three primitives: # next-token(stream, delim char) -> slice (start, end pointers) # next-token-from-slice(start, end, delim char) -> slice # slice-equal?(slice, string) subx-pack: # in: (addr buffered-file), out: (addr buffered-file) # pseudocode: # var line: (stream byte 512) # var in-code? = false # while true # clear-stream(line) # read-line-buffered(in, line) # if (line->write == 0) break # end of file # var word-slice = next-word(line) # if slice-empty?(word-slice) # whitespace #
c{0: 0 (((default-space space-address)) <- ((new)) ((space literal)) ((2 literal))) -- nil
c{0: 1 (((1 integer) (raw)) <- ((copy)) ((23 literal))) -- nil
c{1: 0 ✓ (((default-space space-address)) <- ((new)) ((space literal)) ((2 literal)))
c{1: 1 ✓ (((1 integer) (raw)) <- ((copy)) ((23 literal)))
cn0: convert-names in main
cn0: (((default-space space-address)) <- ((new)) ((space literal)) ((2 literal))) nil nil
cn0: checking arg ((space literal))
cn0: checking arg ((2 literal))
cn0: checking oarg ((default-space space-address))
maybe-add: ((default-space space-address))
cn0: (((1 integer) (raw)) <- ((copy)) ((23 literal))) nil nil
cn0: checking arg ((23 literal))
cn0: checking oarg ((1 integer) (raw))
maybe-add: ((1 integer) (raw))
cn1: (((default-space space-address)) <- ((new)) ((space literal)) ((2 literal)))
cn1: (((1 integer) (raw)) <- ((copy)) ((23 literal)))
schedule: main
run: main 0: (((default-space space-address)) <- ((new)) ((space literal)) ((2 literal)))
run: main 0: 1000 => ((default-space space-address))
run: main 1: (((1 integer) (raw)) <- ((copy)) ((23 literal)))
run: main 1: 23 => ((1 integer) (raw))
mem: ((1 integer) (raw)): 1 <= 23
schedule: done with routine nil