diff --git a/linux/labels_baremetal b/linux/labels_baremetal new file mode 100755 index 00000000..f6fc997e Binary files /dev/null and b/linux/labels_baremetal differ diff --git a/linux/labels_baremetal.subx b/linux/labels_baremetal.subx new file mode 100644 index 00000000..fa94a5a8 --- /dev/null +++ b/linux/labels_baremetal.subx @@ -0,0 +1,2831 @@ +# Assign addresses (co-ordinates) to instructions (landmarks) in a program +# (landscape). +# Read a list of addresses and labels from a file called 'labels', +# then replace labels in stdin with their addresses. +# +# To build: +# $ bootstrap/bootstrap translate [01]*.subx subx-params.subx labels_baremetal.subx -o labels_baremetal +# +# Stdin should be a stream of bytes and some interspersed labels. Comments and +# '==' segment headers are allowed, but names are ignored. The emitted code +# will all lie in a single contiguous address range starting at address 0x7c00. +# Addresses in segment headers are optional. If provided, this program will +# insert padding in the output until the desired address is reached. +# +# $ cat x +# == code +# l1: +# aa bb l1/imm8 +# cc dd l2/disp32 +# l2: +# ee foo/imm32 +# == data 0x7c10 +# foo: +# 34 +# +# The output is the stream of bytes without segment headers or label definitions, +# and with label references replaced with numeric values/displacements. +# +# $ cat x |bootstrap/bootstrap run survey_baremetal labels +# # 0x7c00 +# aa bb nn # some computed address +# cc dd nn nn nn nn # some computed displacement +# ee nn nn nn nn # address right after this instruction +# # 0x7c0e +# 00 00 # padding +# # 0x7c10 +# 34 # data segment interleaved with code + +== 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 + + # 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 + # initialize-trace-stream(Trace-size) + # . . push args + ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-size/disp32 # push *Heap-size + # . . call + e8/call initialize-trace-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/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-labels-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-labels-main:interactive/disp8 + # run-tests() +#? e8/call test-emit-output-with-padding/disp32 + 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-labels-main:end/disp8 +$subx-labels-main:interactive: + # - otherwise convert stdin + # var labels-file/esi: (addr buffered-file) from syscall_open("labels", READ) + # . data + 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0x1000/imm32 # subtract from esp + # . size + 68/push 0x1000/imm32 + # . read + 68/push 0/imm32/read + # . write + 68/push 0/imm32/write + # . fd = syscall_open("labels", READ) + bb/copy-to-ebx Label-file/imm32 + b9/copy-to-ecx 0/imm32/read-mode + ba/copy-to-edx 0x180/imm32/permissions + e8/call syscall_open/disp32 + 50/push-eax + 89/copy 3/mod/direct 6/rm32/esi . . . 4/r32/esp . . # copy esp to esi + # subx-labels(Stdin, label-fd, Stdout) + # . . push args + 68/push Stdout/imm32 + 56/push-esi + 68/push Stdin/imm32 + # . . call + e8/call subx-labels/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # . write-stream(2/stderr, Trace-stream) +#? # . . push args +#? ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write-stream/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # syscall(exit, 0) + bb/copy-to-ebx 0/imm32 +$subx-labels-main:end: + e8/call syscall_exit/disp32 + +== data + +Label-file: + 6c/l 61/a 62/b 65/e 6c/l 73/s 00/nul + +== code + +subx-labels: # infile: (addr buffered-file), labels-file: (addr buffered-file), out: (addr buffered-file) + # pseudocode + # var labels: (stream {label-name, address} Max-labels) + # load-labels(labels-file, labels) + # var in: (stream byte Input-size) + # slurp(infile, in) + # emit-output(in, out, labels) + # + # . prologue + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 52/push-edx + 56/push-esi + # var labels/edx: (stream {label-name, address} Max-labels) + # (we get more rows than Max-labels advertises because row size is smaller than in survey_elf) + # . data + 2b/subtract 0/mod/indirect 5/rm32/.disp32 . . 4/r32/esp Max-labels/disp32 # subtract *Max-labels from esp + # . size + ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Max-labels/disp32 # push *Max-labels + # . read + 68/push 0/imm32/read + # . write + 68/push 0/imm32/write + 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx + # load-labels(labels-file, labels) + # . . push args + 52/push-edx + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) + # . . call + e8/call load-labels/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # var in/esi: (stream byte Input-size) + # . data + 2b/subtract 0/mod/indirect 5/rm32/.disp32 . . 4/r32/esp Input-size/disp32 # subtract *Input-size from esp + # . size + ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Input-size/disp32 # push *Input-size + # . read + 68/push 0/imm32/read + # . write + 68/push 0/imm32/write + 89/copy 3/mod/direct 6/rm32/esi . . . 4/r32/esp . . # copy esp to esi + # slurp(infile, in) + # . . push args + 56/push-esi + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) + # . . call + e8/call slurp/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # emit-output(in, out, labels) + # . . push args + 52/push-edx + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16) + 56/push-esi + # . . call + e8/call emit-output/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # flush(out) + # . . push args + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16) + # . . call + e8/call flush/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp +$subx-labels:end: + # . reclaim locals + 03/add 0/mod/indirect 5/rm32/.disp32 . . 4/r32/esp Max-labels/disp32 # add *Max-labels to esp + 03/add 0/mod/indirect 5/rm32/.disp32 . . 4/r32/esp Input-size/disp32 # add *Input-size to esp + # . restore registers + 5e/pop-to-esi + 5a/pop-to-edx + # . epilogue + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +load-labels: # in: (addr buffered-file), labels: (stream {label-name, address} Max-labels) + # pseudocode + # var line: (stream byte 512) + # while true + # clear-stream(line) + # read-line-buffered(in, line) + # if (line->write == 0) break # end of file + # var word-slice/ecx: (addr slice) = next-word(line) + # var dest/edi: (addr int) = get-or-insert-slice(labels, word-slice, 12 bytes/row) + # word-slice = next-word(line) + # var address/eax: int = parse-hex-int-from-slice(word-slice) + # *dest = address + # + # . prologue + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 50/push-eax + 51/push-ecx + 52/push-edx + 56/push-esi + 57/push-edi + # var line/ecx: (stream byte 512) + 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0x200/imm32 # subtract from esp + 68/push 0x200/imm32/size + 68/push 0/imm32/read + 68/push 0/imm32/write + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # var word-slice/edx: slice + 68/push 0/imm32/end + 68/push 0/imm32/start + 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx +$load-labels:loop: + # clear-stream(line) + # . . push args + 51/push-ecx + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # read-line-buffered(in, line) + # . . push args + 51/push-ecx + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) + # . . call + e8/call read-line-buffered/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # dump line {{{ +#? # . write(2/stderr, "LL: ") +#? # . . push args +#? 68/push "LL: "/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # write-stream(2/stderr, line) +#? # . . push args +#? 51/push-ecx +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write-stream/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # . write(2/stderr, "$\n") +#? # . . push args +#? 68/push "$\n"/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # . rewind-stream(line) +#? # . . push args +#? 51/push-ecx +#? # . . call +#? e8/call rewind-stream/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp +#? # }}} + # if (line->write == 0) break + 81 7/subop/compare 0/mod/indirect 1/rm32/ecx . . . . . 0/imm32 # compare *ecx + 0f 84/jump-if-= $load-labels:end/disp32 + # next-word(line, word-slice) + # . . push args + 52/push-edx + 51/push-ecx + # . . call + e8/call next-word/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # dump word-slice {{{ +#? # . write(2/stderr, "w: ") +#? # . . push args +#? 68/push "w: "/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # . write-slice-buffered(Stderr, word-slice) +#? # . . push args +#? 52/push-edx +#? 68/push Stderr/imm32 +#? # . . call +#? e8/call write-slice-buffered/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # . flush(Stderr) +#? # . . push args +#? 68/push Stderr/imm32 +#? # . . call +#? e8/call flush/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp +#? # . write(2/stderr, "$\n") +#? # . . push args +#? 68/push "$\n"/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # }}} + # var dest/edi: (addr int) = get-or-insert-slice(labels, word-slice, 12 bytes/row, Heap) + # . eax = get-or-insert-slice(labels, word-slice, 12 bytes/row, Heap) + # . . push args + 68/push Heap/imm32 + 68/push 0xc/imm32/row-size + 52/push-edx + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) + # . . call + e8/call get-or-insert-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp + # . edi = eax + 89/copy 3/mod/direct 7/rm32/edi . . . 0/r32/eax . . # copy eax to edi + # next-word(line, word-slice) + # . . push args + 52/push-edx + 51/push-ecx + # . . call + e8/call next-word/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # var address/esi: int = parse-hex-int-from-slice(word-slice) + # . . push args + 52/push-edx + # . . call + e8/call parse-hex-int-from-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . esi = eax + 89/copy 3/mod/direct 6/rm32/esi . . . 0/r32/eax . . # copy eax to esi + # *dest = address + 89/copy 0/mod/indirect 7/rm32/edi . . 6/r32/esi . . # copy esi to *edi + # + e9/jump $load-labels:loop/disp32 +$load-labels:end: + # . reclaim locals + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x214/imm32 # add to esp + # . restore registers + 5f/pop-to-edi + 5e/pop-to-esi + 5a/pop-to-edx + 59/pop-to-ecx + 58/pop-to-eax + # . epilogue + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +# global scratch space for emit-output +== data + +emit-output:datum: # slice + 0/imm32/start + 0/imm32/end + +== code + +emit-output: # in: (addr stream byte), out: (addr buffered-file), labels: (addr stream {(handle array byte), address}) + # pseudocode: + # var address-of-next-instruction = 0x7c00 + # var line: (stream byte 512) + # line-loop: + # while true + # clear-stream(line) + # read-line(in, line) + # if (line->write == 0) break # end of file + # address-of-next-instruction += num-bytes(line) + # var far-jump-or-call? = far-jump-or-call?(line) + # rewind-stream(line) + # while true + # var word-slice = next-word(line) + # if slice-empty?(word-slice) # end of line + # break + # if slice-starts-with?(word-slice, "#") # comment + # break + # if label?(word-slice) # no need for label declarations anymore + # goto line-loop # don't insert empty lines + # if slice-equal?(word-slice, "==") # no need for segment header lines + # word-slice = next-word(line) # skip segment name + # word-slice = next-word(line) + # if !slice-empty?(word-slice) + # new-address = parse-hex-int-from-slice(word-slice) + # write-buffered(out, "# " address-of-next-instruction "\n") + # while address-of-next-instruction < new-address + # write-buffered("00") + # ++address-of-next-instruction + # write-buffered(out, "# " address-of-next-instruction "\n") + # goto line-loop # don't insert empty lines + # if length(word-slice) == 2 + # write-slice-buffered(out, word-slice) + # write-buffered(out, " ") + # continue + # var datum: (addr slice) = next-token-from-slice(word-slice->start, word-slice->end, "/") + # var address: (addr int) = get-slice(labels, datum) + # if has-metadata?(word-slice, "imm8") + # emit(out, *address, 1) + # else if has-metadata?(word-slice, "imm16") + # emit(out, *address, 2) + # else if has-metadata?(word-slice, "imm32") + # emit(out, *address, 4) + # else if has-metadata?(word-slice, "disp8") + # value = *address - address-of-next-instruction + # emit(out, value, 1) + # else if has-metadata?(word-slice, "disp32") + # if far-jump-or-call? + # value = *address - address-of-next-instruction + # else + # value = *address + # emit(out, value, 4) + # else + # abort + # write-buffered(out, "\n") + # + # registers: + # line: ecx + # word-slice: edx + # address-of-next-instruction: ebx + # far-jump-or-call?: edi + # address: esi (inner loop only) + # temporaries: eax, esi (outer loop) + # + # . prologue + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 50/push-eax + 51/push-ecx + 52/push-edx + 53/push-ebx + 56/push-esi + 57/push-edi + # var line/ecx: (stream byte 512) + 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0x200/imm32 # subtract from esp + 68/push 0x200/imm32/size + 68/push 0/imm32/read + 68/push 0/imm32/write + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # var word-slice/edx: slice + 68/push 0/imm32/end + 68/push 0/imm32/start + 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx + # var address-of-next-instruction/ebx = 0x7c00 + bb/copy-to-ebx 0x7c00/imm32 +$emit-output:line-loop: + # clear-stream(line) + # . . push args + 51/push-ecx + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # read-line(in, line) + # . . push args + 51/push-ecx + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) + # . . call + e8/call read-line/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # dump line {{{ +#? # . write(2/stderr, "LL: ") +#? # . . push args +#? 68/push "LL: "/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # write-stream(2/stderr, line) +#? # . . push args +#? 51/push-ecx +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write-stream/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # . write(2/stderr, "$\n") +#? # . . push args +#? 68/push "$\n"/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # . rewind-stream(line) +#? # . . push args +#? 51/push-ecx +#? # . . call +#? e8/call rewind-stream/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp +#? # }}} +$emit-output:check-for-end-of-input: + # if (line->write == 0) break + 81 7/subop/compare 0/mod/indirect 1/rm32/ecx . . . . . 0/imm32 # compare *ecx + 0f 84/jump-if-= $emit-output:end/disp32 + # address-of-next-instruction += num-bytes(line) + # . eax = num-bytes(line) + # . . push args + 51/push-ecx + # . . call + e8/call num-bytes/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . ebx += eax + 01/add 3/mod/direct 3/rm32/ebx . . . 0/r32/eax . . # add eax to ebx + # var far-jump-or-call?/edi: boolean = far-jump-or-call?(line) + # . . push args + 51/push-ecx + # . . call + e8/call far-jump-or-call?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # rewind-stream(line) + # . . push args + 51/push-ecx + # . . call + e8/call rewind-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp +$emit-output:word-loop: + # next-word(line, word-slice) + # . . push args + 52/push-edx + 51/push-ecx + # . . call + e8/call next-word/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # dump word-slice {{{ +#? # . write(2/stderr, "w: ") +#? # . . push args +#? 68/push "w: "/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # . write-slice-buffered(Stderr, word-slice) +#? # . . push args +#? 52/push-edx +#? 68/push Stderr/imm32 +#? # . . call +#? e8/call write-slice-buffered/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # . flush(Stderr) +#? # . . push args +#? 68/push Stderr/imm32 +#? # . . call +#? e8/call flush/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp +#? # . write(2/stderr, "$\n") +#? # . . push args +#? 68/push "$\n"/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # }}} +$emit-output:check-for-end-of-line: + # if (slice-empty?(word-slice)) break + # . eax = slice-empty?(word-slice) + # . . push args + 52/push-edx + # . . call + e8/call slice-empty?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . if (eax != 0) break + 3d/compare-eax-and 0/imm32/false + 0f 85/jump-if-!= $emit-output:next-line/disp32 +$emit-output:check-for-comment: + # if (slice-starts-with?(word-slice, "#")) break + # . start/esi = word-slice->start + 8b/copy 0/mod/indirect 2/rm32/edx . . . 6/r32/esi . . # copy *edx to esi + # . c/eax = *start + 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax + 8a/copy-byte 0/mod/indirect 6/rm32/esi . . . 0/r32/AL . . # copy byte at *esi to AL + # . if (eax == '#') break + 3d/compare-eax-and 0x23/imm32/hash + 0f 84/jump-if-= $emit-output:next-line/disp32 +$emit-output:check-for-label: + # if label?(word-slice) break + # . eax = label?(word-slice) + # . . push args + 52/push-edx + # . . call + e8/call label?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . if (eax != false) break + 3d/compare-eax-and 0/imm32/false + 0f 85/jump-if-!= $emit-output:line-loop/disp32 +$emit-output:check-for-segment-header: + # if !slice-equal?(word-slice, "==") goto next check + # . eax = slice-equal?(word-slice, "==") + # . . push args + 68/push "=="/imm32 + 52/push-edx + # . . call + e8/call slice-equal?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . if (eax == false) goto next check + 3d/compare-eax-and 0/imm32/false + 0f 84/jump-if-= $emit-output:2-character/disp32 + # skip segment name + # . next-word(line, word-slice) + # . . push args + 52/push-edx + 51/push-ecx + # . . call + e8/call next-word/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # compute segment address if it exists + # . next-word(line, word-slice) + # . . push args + 52/push-edx + 51/push-ecx + # . . call + e8/call next-word/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . if slice-empty?(word-slice) goto padding-done + # . . push args + 52/push-edx + # . . call + e8/call slice-empty?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . . + 3d/compare-eax-and 0/imm32/false + 0f 85/jump-if-!= $emit-output:padding-done/disp32 + # . var new-address/eax: int = parse-hex-int-from-slice(word-slice) + # . . push args + 52/push-edx + # . . call + e8/call parse-hex-int-from-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # write-buffered(out, "# " address-of-next-instruction "\n") + # . write-buffered(out, "# ") + # . . push args + 68/push "# "/imm32 + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) + # . . call + e8/call write-buffered/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . write-int32-hex-buffered(out, address-of-next-instruction) + # . . push args + 53/push-ebx + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) + # . . call + e8/call write-int32-hex-buffered/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . write-buffered(out, "\n") + # . . push args + 68/push Newline/imm32 + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) + # . . call + e8/call write-buffered/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +$emit-output:padding-loop: + # if (address-of-next-instruction >= new-address) goto padding-loop-done + 39/compare 3/mod/direct 3/rm32/ebx . . . 0/r32/eax . . # compare ebx with eax + 73/jump-if-addr>= $emit-output:padding-loop-done/disp8 + # if (address-of-next-instruction % 8 == 0) write-buffered("\n") + 53/push-ebx + 81 4/subop/and 3/mod/direct 3/rm32/ebx . . . . . 7/imm32 # bitwise and of ebx + 81 7/subop/compare 3/mod/direct 3/rm32/ebx . . . . . 0/imm32 # compare ebx + 5b/pop-to-ebx + 75/jump-if-!= $emit-output:padding-core/disp8 + # . write-buffered(out, "\n") + # . . push args + 68/push Newline/imm32 + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) + # . . call + e8/call write-buffered/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +$emit-output:padding-core: + # write-buffered("00") + # . . push args + 68/push "00 "/imm32 + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) + # . . call + e8/call write-buffered/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # ++address-of-next-instruction + 43/increment-ebx + # loop + eb/jump $emit-output:padding-loop/disp8 +$emit-output:padding-loop-done: + # . write-buffered(out, "\n") + # . . push args + 68/push Newline/imm32 + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) + # . . call + e8/call write-buffered/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +$emit-output:padding-done: + # write-buffered(out, "# " address-of-next-instruction "\n") + # . write-buffered(out, "# ") + # . . push args + 68/push "# "/imm32 + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) + # . . call + e8/call write-buffered/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . write-int32-hex-buffered(out, address-of-next-instruction) + # . . push args + 53/push-ebx + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) + # . . call + e8/call write-int32-hex-buffered/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . write-buffered(out, "\n") + # . . push args + 68/push Newline/imm32 + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) + # . . call + e8/call write-buffered/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # + e9/jump $emit-output:line-loop/disp32 +$emit-output:2-character: + # if (size(word-slice) != 2) goto next check + # . eax = size(word-slice) + 8b/copy 1/mod/*+disp8 2/rm32/edx . . . 0/r32/eax 4/disp8 . # copy *(edx+4) to eax + 2b/subtract 0/mod/indirect 2/rm32/edx . . . 0/r32/eax . . # subtract *edx from eax + # . if (eax != 2) goto next check + 3d/compare-eax-and 2/imm32 + 75/jump-if-!= $emit-output:check-metadata/disp8 + # write-slice-buffered(out, word-slice) + # . . push args + 52/push-edx + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) + # . . call + e8/call write-slice-buffered/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # write-buffered(out, " ") + # . . push args + 68/push Space/imm32 + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) + # . . call + e8/call write-buffered/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # continue + e9/jump $emit-output:word-loop/disp32 +$emit-output:check-metadata: + # - if we get here, 'word-slice' must be a label to be looked up + # datum = next-token-from-slice(word-slice->start, word-slice->end, "/") + # . . push args + 68/push emit-output:datum/imm32 + 68/push 0x2f/imm32/slash + ff 6/subop/push 1/mod/*+disp8 2/rm32/edx . . . . 4/disp8 . # push *(edx+4) + ff 6/subop/push 0/mod/indirect 2/rm32/edx . . . . . . # push *edx + # . . call + e8/call next-token-from-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp +#? # dump datum {{{ +#? # . write(2/stderr, "datum: ") +#? # . . push args +#? 68/push "datum: "/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # . write-slice-buffered(Stderr, datum) +#? # . . push args +#? 68/push emit-output:datum/imm32 +#? 68/push Stderr/imm32 +#? # . . call +#? e8/call write-slice-buffered/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # . flush(Stderr) +#? # . . push args +#? 68/push Stderr/imm32 +#? # . . call +#? e8/call flush/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp +#? # . write(2/stderr, "$\n") +#? # . . push args +#? 68/push "$\n"/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # }}} + # address/esi: (addr int) = get-slice(labels, datum, row-size=12, "label table") + # . eax = get-slice(labels, datum, row-size=24, "label table") + # . . push args + 68/push "label table"/imm32 + 68/push 0xc/imm32/row-size + 68/push emit-output:datum/imm32 + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16) + # . . call + e8/call get-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp + # . esi = eax + 89/copy 3/mod/direct 6/rm32/esi . . . 0/r32/eax . . # copy eax to esi +$emit-output:check-imm8: + # if (!has-metadata?(word-slice, "imm8")) goto next check + # . eax = has-metadata?(edx, "imm8") + # . . push args + 68/push "imm8"/imm32 + 52/push-edx + # . . call + e8/call has-metadata?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . if (eax != false) abort + 3d/compare-eax-and 0/imm32/false + 74/jump-if-= $emit-output:check-imm16/disp8 +$emit-output:emit-imm8: + # emit-hex(out, *address, 1) + # . . push args + 68/push 1/imm32 + ff 6/subop/push 0/mod/indirect 6/rm32/esi . . . . . . # push *esi + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) + # . . call + e8/call emit-hex/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # continue + e9/jump $emit-output:word-loop/disp32 +$emit-output:check-imm16: + # if (!has-metadata?(word-slice, "imm16")) goto next check + # . eax = has-metadata?(edx, "imm16") + # . . push args + 68/push "imm16"/imm32 + 52/push-edx + # . . call + e8/call has-metadata?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . if (eax == false) goto next check + 3d/compare-eax-and 0/imm32/false + 74/jump-if-= $emit-output:check-imm32/disp8 +#? # dump *address {{{ +#? # . write(2/stderr, "*address: ") +#? # . . push args +#? 68/push "*address: "/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # . write-int32-hex-buffered(Stderr, *address) +#? # . . push args +#? ff 6/subop/push 0/mod/indirect 6/rm32/esi . . . . . . # push *esi +#? 68/push Stderr/imm32 +#? # . . call +#? e8/call write-int32-hex-buffered/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # . flush(Stderr) +#? # . . push args +#? 68/push Stderr/imm32 +#? # . . call +#? e8/call flush/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp +#? # . write(2/stderr, "$\n") +#? # . . push args +#? 68/push "$\n"/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # }}} +$emit-output:emit-imm16: + # emit-hex(out, *address, 2) + # . . push args + 68/push 2/imm32 + ff 6/subop/push 0/mod/indirect 6/rm32/esi . . . . . . # push *esi + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) + # . . call + e8/call emit-hex/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # TODO: ensure that the higher 2 bytes are zero + # continue + e9/jump $emit-output:word-loop/disp32 +$emit-output:check-imm32: + # if (!has-metadata?(word-slice, "imm32")) goto next check + # . eax = has-metadata?(edx, "imm32") + # . . push args + 68/push "imm32"/imm32 + 52/push-edx + # . . call + e8/call has-metadata?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . if (eax == false) goto next check + 3d/compare-eax-and 0/imm32/false + 74/jump-if-= $emit-output:check-disp8/disp8 +#? # dump *address {{{ +#? # . write(2/stderr, "*address: ") +#? # . . push args +#? 68/push "*address: "/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # . write-int32-hex-buffered(Stderr, *address) +#? # . . push args +#? ff 6/subop/push 0/mod/indirect 6/rm32/esi . . . . . . # push *esi +#? 68/push Stderr/imm32 +#? # . . call +#? e8/call write-int32-hex-buffered/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # . flush(Stderr) +#? # . . push args +#? 68/push Stderr/imm32 +#? # . . call +#? e8/call flush/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp +#? # . write(2/stderr, "$\n") +#? # . . push args +#? 68/push "$\n"/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # }}} +$emit-output:emit-imm32: + # emit-hex(out, *address, 4) + # . . push args + 68/push 4/imm32 + ff 6/subop/push 0/mod/indirect 6/rm32/esi . . . . . . # push *esi + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) + # . . call + e8/call emit-hex/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # continue + e9/jump $emit-output:word-loop/disp32 +$emit-output:check-disp8: + # if (!has-metadata?(word-slice, "disp8")) goto next check + # . eax = has-metadata?(edx, "disp8") + # . . push args + 68/push "disp8"/imm32 + 52/push-edx + # . . call + e8/call has-metadata?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . if (eax == false) goto next check + 3d/compare-eax-and 0/imm32/false + 74/jump-if-= $emit-output:check-disp16/disp8 +$emit-output:emit-disp8: + # emit-hex(out, *address - address-of-next-instruction, 1) + # . . push args + 68/push 1/imm32 + 8b/copy 0/mod/indirect 6/rm32/esi . . . 0/r32/eax . . # copy *esi to eax + 29/subtract 3/mod/direct 0/rm32/eax . . . 3/r32/ebx . . # subtract ebx from eax + 50/push-eax + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) + # . . call + e8/call emit-hex/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # continue + e9/jump $emit-output:word-loop/disp32 +$emit-output:check-disp16: + # if (!has-metadata?(word-slice, "disp16")) goto next check + # . eax = has-metadata?(edx, "disp16") + # . . push args + 68/push "disp16"/imm32 + 52/push-edx + # . . call + e8/call has-metadata?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . if (eax == false) goto next check + 3d/compare-eax-and 0/imm32/false + 74/jump-if-= $emit-output:check-disp32/disp8 +$emit-output:emit-disp16: + # emit-hex(out, *address - address-of-next-instruction, 2) + # . . push args + 68/push 2/imm32 + 8b/copy 0/mod/indirect 6/rm32/esi . . . 0/r32/eax . . # copy *esi to eax + 29/subtract 3/mod/direct 0/rm32/eax . . . 3/r32/ebx . . # subtract ebx from eax + 50/push-eax + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) + # . . call + e8/call emit-hex/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # continue + e9/jump $emit-output:word-loop/disp32 +$emit-output:check-disp32: + # if (!has-metadata?(word-slice, "disp32")) abort + # . eax = has-metadata?(edx, "disp32") + # . . push args + 68/push "disp32"/imm32 + 52/push-edx + # . . call + e8/call has-metadata?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . if (eax == false) abort + 3d/compare-eax-and 0/imm32/false + 0f 84/jump-if-= $emit-output:abort/disp32 +$emit-output:emit-disp32: + # var value/eax = *address + 8b/copy 0/mod/indirect 6/rm32/esi . . . 0/r32/eax . . # copy *esi to eax + # if (far-jump-or-call?) value -= address-of-next-instruction + 81 7/subop/compare 3/mod/direct 7/rm32/edi . . . . . 0/imm32/false # compare edi + 74/jump-if-= $emit-output:really-emit-disp32/disp8 + 29/subtract 3/mod/direct 0/rm32/eax . . . 3/r32/ebx . . # subtract ebx from eax +$emit-output:really-emit-disp32: + # emit-hex(out, value, 4) + # . . push args + 68/push 4/imm32 + 50/push-eax + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) + # . . call + e8/call emit-hex/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # continue + e9/jump $emit-output:word-loop/disp32 +$emit-output:next-line: + # write-buffered(out, "\n") + # . . push args + 68/push Newline/imm32 + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) + # . . call + e8/call write-buffered/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # loop + e9/jump $emit-output:line-loop/disp32 +$emit-output:end: + # . reclaim locals + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x214/imm32 # add to esp + # . restore registers + 5f/pop-to-edi + 5e/pop-to-esi + 5b/pop-to-ebx + 5a/pop-to-edx + 59/pop-to-ecx + 58/pop-to-eax + # . epilogue + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +$emit-output:abort: + # print(stderr, "missing metadata in " word-slice) + # . _write(2/stderr, "missing metadata in word ") + # . . push args + 68/push "emit-output: missing metadata in "/imm32 + 68/push 2/imm32/stderr + # . . call + e8/call _write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . write-slice-buffered(Stderr, word-slice) + # . . push args + 52/push-edx + 68/push Stderr/imm32 + # . . call + e8/call write-slice-buffered/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . flush(Stderr) + # . . push args + 68/push Stderr/imm32 + # . . call + e8/call flush/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . syscall(exit, 1) + bb/copy-to-ebx 1/imm32 + e8/call syscall_exit/disp32 + # never gets here + +test-emit-output-non-far-control-flow: + # labels turn into absolute addresses if opcodes are not far jumps or calls + # + # input: + # in: + # == code + # ab cd ef gh + # ij x/disp32 + # == data + # 00 + # 34 + # labels: + # - 'x': 0x11223344 + # + # output: + # ab cd ef gh + # ij 44 33 22 11 + # 00 + # 34 + # + # . prologue + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # setup + # . clear-stream(_test-input-stream) + # . . push args + 68/push _test-input-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . clear-stream(_test-output-stream) + # . . push args + 68/push _test-output-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . clear-stream($_test-output-buffered-file->buffer) + # . . push args + 68/push $_test-output-buffered-file->buffer/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . var labels/edx: (stream byte 8*24) + 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0xc0/imm32 # subtract from esp + 68/push 0xc0/imm32/size + 68/push 0/imm32/read + 68/push 0/imm32/write + 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx + # . var h/ebx: (handle array byte) + 68/push 0/imm32 + 68/push 0/imm32 + 89/copy 3/mod/direct 3/rm32/ebx . . . 4/r32/esp . . # copy esp to ebx + # initialize input + # . write(_test-input-stream, "== code\n") + # . . push args + 68/push "== code\n"/imm32 + 68/push _test-input-stream/imm32 + # . . call + e8/call write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . write(_test-input-stream, "ab cd ef gh\n") + # . . push args + 68/push "ab cd ef gh\n"/imm32 + 68/push _test-input-stream/imm32 + # . . call + e8/call write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . write(_test-input-stream, "ij x/disp32\n") + # . . push args + 68/push "ij x/disp32\n"/imm32 + 68/push _test-input-stream/imm32 + # . . call + e8/call write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . write(_test-input-stream, "== data\n") + # . . push args + 68/push "== data\n"/imm32 + 68/push _test-input-stream/imm32 + # . . call + e8/call write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . write(_test-input-stream, "00\n") + # . . push args + 68/push "00\n"/imm32 + 68/push _test-input-stream/imm32 + # . . call + e8/call write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . write(_test-input-stream, "34\n") + # . . push args + 68/push "34\n"/imm32 + 68/push _test-input-stream/imm32 + # . . call + e8/call write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . stream-add2(labels, "x", 0x11223344) + 68/push 0x11223344/imm32/label-address + # . . push handle for "x" + 53/push-ebx + 68/push "x"/imm32 + 68/push Heap/imm32 + e8/call copy-array/disp32 + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + ff 6/subop/push 1/mod/*+disp8 3/rm32/ebx . . . . 4/disp8 . # push *(ebx+4) + ff 6/subop/push 0/mod/indirect 3/rm32/ebx . . . . . . # push *ebx + # . . push labels + 52/push-edx + # . . call + e8/call stream-add2/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp + # component under test + # . emit-output(_test-input-stream, _test-output-buffered-file, labels) + # . . push args + 52/push-edx + 68/push _test-output-buffered-file/imm32 + 68/push _test-input-stream/imm32 + # . . call + e8/call emit-output/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # checks + # . flush(_test-output-buffered-file) + # . . push args + 68/push _test-output-buffered-file/imm32 + # . . call + e8/call flush/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp +#? # dump output {{{ +#? # . write(2/stderr, "result: ^") +#? # . . push args +#? 68/push "result: ^"/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # . write-stream(2/stderr, _test-output-stream) +#? # . . push args +#? 68/push _test-output-stream/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write-stream/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # . write(2/stderr, "$\n") +#? # . . push args +#? 68/push "$\n"/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # . rewind-stream(_test-output-stream) +#? # . . push args +#? 68/push _test-output-stream/imm32 +#? # . . call +#? e8/call rewind-stream/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp +#? # }}} + # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c00", msg) + # . . push args + 68/push "F - test-emit-output-non-far-control-flow/0"/imm32 + 68/push "# 0x00007c00"/imm32 + 68/push _test-output-stream/imm32 + # . . call + e8/call check-next-stream-line-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . check-next-stream-line-equal(_test-output-stream, "ab cd ef gh ", msg) + # . . push args + 68/push "F - test-emit-output-non-far-control-flow/1"/imm32 + 68/push "ab cd ef gh "/imm32 + 68/push _test-output-stream/imm32 + # . . call + e8/call check-next-stream-line-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . check-next-stream-line-equal(_test-output-stream, "ij 44 33 22 11 ", msg) + # . . push args + 68/push "F - test-emit-output-non-far-control-flow/2"/imm32 + 68/push "ij 44 33 22 11 "/imm32 + 68/push _test-output-stream/imm32 + # . . call + e8/call check-next-stream-line-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c09", msg) + # . . push args + 68/push "F - test-emit-output-non-far-control-flow/3"/imm32 + 68/push "# 0x00007c09"/imm32 + 68/push _test-output-stream/imm32 + # . . call + e8/call check-next-stream-line-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . check-next-stream-line-equal(_test-output-stream, "00 ", msg) + # . . push args + 68/push "F - test-emit-output-non-far-control-flow/3"/imm32 + 68/push "00 "/imm32 + 68/push _test-output-stream/imm32 + # . . call + e8/call check-next-stream-line-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . check-next-stream-line-equal(_test-output-stream, "34 ", msg) + # . . push args + 68/push "F - test-emit-output-non-far-control-flow/4"/imm32 + 68/push "34 "/imm32 + 68/push _test-output-stream/imm32 + # . . call + e8/call check-next-stream-line-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . epilogue + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-emit-output-with-padding: + # labels turn into absolute addresses if opcodes are not far jumps or calls + # + # input: + # in: + # == code + # ab cd ef gh + # == data 0x7c10 + # 34 + # + # output: + # ab cd ef gh + # # 0x7c04 + # 00 00 00 00 + # 00 00 00 00 00 00 00 00 + # # 0x7c10 + # 34 + # + # . prologue + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # setup + # . clear-stream(_test-input-stream) + # . . push args + 68/push _test-input-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . clear-stream(_test-output-stream) + # . . push args + 68/push _test-output-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . clear-stream($_test-output-buffered-file->buffer) + # . . push args + 68/push $_test-output-buffered-file->buffer/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . var labels/edx: (stream byte 8*24) + 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0xc0/imm32 # subtract from esp + 68/push 0xc0/imm32/size + 68/push 0/imm32/read + 68/push 0/imm32/write + 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx + # . var h/ebx: (handle array byte) + 68/push 0/imm32 + 68/push 0/imm32 + 89/copy 3/mod/direct 3/rm32/ebx . . . 4/r32/esp . . # copy esp to ebx + # initialize input + # . write(_test-input-stream, "== code\n") + # . . push args + 68/push "== code\n"/imm32 + 68/push _test-input-stream/imm32 + # . . call + e8/call write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . write(_test-input-stream, "ab cd ef gh\n") + # . . push args + 68/push "ab cd ef gh\n"/imm32 + 68/push _test-input-stream/imm32 + # . . call + e8/call write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . write(_test-input-stream, "== data 0x7c10\n") + # . . push args + 68/push "== data 0x7c10\n"/imm32 + 68/push _test-input-stream/imm32 + # . . call + e8/call write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . write(_test-input-stream, "34\n") + # . . push args + 68/push "34\n"/imm32 + 68/push _test-input-stream/imm32 + # . . call + e8/call write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # component under test + # . emit-output(_test-input-stream, _test-output-buffered-file, labels) + # . . push args + 52/push-edx + 68/push _test-output-buffered-file/imm32 + 68/push _test-input-stream/imm32 + # . . call + e8/call emit-output/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # checks + # . flush(_test-output-buffered-file) + # . . push args + 68/push _test-output-buffered-file/imm32 + # . . call + e8/call flush/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp +#? # dump output {{{ +#? # . write(2/stderr, "result: ^") +#? # . . push args +#? 68/push "result: ^"/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # . write-stream(2/stderr, _test-output-stream) +#? # . . push args +#? 68/push _test-output-stream/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write-stream/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # . write(2/stderr, "$\n") +#? # . . push args +#? 68/push "$\n"/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # . rewind-stream(_test-output-stream) +#? # . . push args +#? 68/push _test-output-stream/imm32 +#? # . . call +#? e8/call rewind-stream/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp +#? # }}} + # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c00", msg) + # . . push args + 68/push "F - test-emit-output-with-padding/0"/imm32 + 68/push "# 0x00007c00"/imm32 + 68/push _test-output-stream/imm32 + # . . call + e8/call check-next-stream-line-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . check-next-stream-line-equal(_test-output-stream, "ab cd ef gh ", msg) + # . . push args + 68/push "F - test-emit-output-with-padding/1"/imm32 + 68/push "ab cd ef gh "/imm32 + 68/push _test-output-stream/imm32 + # . . call + e8/call check-next-stream-line-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c04", msg) + # . . push args + 68/push "F - test-emit-output-with-padding/0"/imm32 + 68/push "# 0x00007c04"/imm32 + 68/push _test-output-stream/imm32 + # . . call + e8/call check-next-stream-line-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . check-next-stream-line-equal(_test-output-stream, "00 00 00 00 ", msg) + # . . push args + 68/push "F - test-emit-output-with-padding/2"/imm32 + 68/push "00 00 00 00 "/imm32 + 68/push _test-output-stream/imm32 + # . . call + e8/call check-next-stream-line-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . check-next-stream-line-equal(_test-output-stream, "00 00 00 00 ", msg) + # . . push args + 68/push "F - test-emit-output-with-padding/3"/imm32 + 68/push "00 00 00 00 00 00 00 00 "/imm32 + 68/push _test-output-stream/imm32 + # . . call + e8/call check-next-stream-line-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c10", msg) + # . . push args + 68/push "F - test-emit-output-with-padding/0"/imm32 + 68/push "# 0x00007c10"/imm32 + 68/push _test-output-stream/imm32 + # . . call + e8/call check-next-stream-line-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . check-next-stream-line-equal(_test-output-stream, "34 ", msg) + # . . push args + 68/push "F - test-emit-output-with-padding/4"/imm32 + 68/push "34 "/imm32 + 68/push _test-output-stream/imm32 + # . . call + e8/call check-next-stream-line-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . epilogue + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-emit-output-code-label: + # labels turn into PC-relative addresses if opcodes are far jumps or calls + # + # input: + # in: + # == code + # ab cd + # ef gh + # e8 l1/disp32 + # labels: + # - 'l1': 0x7c10 + # + # output: + # ab cd + # ef gh + # e8 07 00 00 00 # 0x7c10 - 0x7c09 = 7 + # + # . prologue + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # setup + # . clear-stream(_test-input-stream) + # . . push args + 68/push _test-input-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . clear-stream(_test-output-stream) + # . . push args + 68/push _test-output-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . clear-stream($_test-output-buffered-file->buffer) + # . . push args + 68/push $_test-output-buffered-file->buffer/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . var labels/edx: (stream byte 8*24) + 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0xc0/imm32 # subtract from esp + 68/push 0xc0/imm32/size + 68/push 0/imm32/read + 68/push 0/imm32/write + 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx + # . var h/ebx: (handle array byte) + 68/push 0/imm32 + 68/push 0/imm32 + 89/copy 3/mod/direct 3/rm32/ebx . . . 4/r32/esp . . # copy esp to ebx + # initialize input + # . write(_test-input-stream, "== code\n") + # . . push args + 68/push "== code\n"/imm32 + 68/push _test-input-stream/imm32 + # . . call + e8/call write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . write(_test-input-stream, "ab cd\n") + # . . push args + 68/push "ab cd\n"/imm32 + 68/push _test-input-stream/imm32 + # . . call + e8/call write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . write(_test-input-stream, "ef gh\n") + # . . push args + 68/push "ef gh\n"/imm32 + 68/push _test-input-stream/imm32 + # . . call + e8/call write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . write(_test-input-stream, "e8 l1/disp32\n") + # . . push args + 68/push "e8 l1/disp32\n"/imm32 + 68/push _test-input-stream/imm32 + # . . call + e8/call write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . stream-add2(labels, "l1", 0x7c10) + 68/push 0x7c10/imm32/label-address + # . . push handle for "l1" + 53/push-ebx + 68/push "l1"/imm32 + 68/push Heap/imm32 + e8/call copy-array/disp32 + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + ff 6/subop/push 1/mod/*+disp8 3/rm32/ebx . . . . 4/disp8 . # push *(ebx+4) + ff 6/subop/push 0/mod/indirect 3/rm32/ebx . . . . . . # push *ebx + # . . push labels + 52/push-edx + # . . call + e8/call stream-add2/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp + # component under test + # . emit-output(_test-input-stream, _test-output-buffered-file, labels) + # . . push args + 52/push-edx + 68/push _test-output-buffered-file/imm32 + 68/push _test-input-stream/imm32 + # . . call + e8/call emit-output/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # checks + # . flush(_test-output-buffered-file) + # . . push args + 68/push _test-output-buffered-file/imm32 + # . . call + e8/call flush/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp +#? # dump output {{{ +#? # . write(2/stderr, "result: ^") +#? # . . push args +#? 68/push "result: ^"/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # . write-stream(2/stderr, _test-output-stream) +#? # . . push args +#? 68/push _test-output-stream/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write-stream/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # . write(2/stderr, "$\n") +#? # . . push args +#? 68/push "$\n"/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # . rewind-stream(_test-output-stream) +#? # . . push args +#? 68/push _test-output-stream/imm32 +#? # . . call +#? e8/call rewind-stream/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp +#? # }}} + # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c00", msg) + # . . push args + 68/push "F - test-emit-output-code-label/0"/imm32 + 68/push "# 0x00007c00"/imm32 + 68/push _test-output-stream/imm32 + # . . call + e8/call check-next-stream-line-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . check-next-stream-line-equal(_test-output-stream, "ab cd ", msg) + # . . push args + 68/push "F - test-emit-output-code-label/1"/imm32 + 68/push "ab cd "/imm32 + 68/push _test-output-stream/imm32 + # . . call + e8/call check-next-stream-line-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . check-next-stream-line-equal(_test-output-stream, "ef gh ", msg) + # . . push args + 68/push "F - test-emit-output-code-label/2"/imm32 + 68/push "ef gh "/imm32 + 68/push _test-output-stream/imm32 + # . . call + e8/call check-next-stream-line-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . check-next-stream-line-equal(_test-output-stream, "e8 07 00 00 00 ", msg) + # . . push args + 68/push "F - test-emit-output-code-label/3"/imm32 + 68/push "e8 07 00 00 00 "/imm32 + 68/push _test-output-stream/imm32 + # . . call + e8/call check-next-stream-line-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . epilogue + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-emit-output-code-label-absolute: + # labels can also convert to absolute addresses + # + # input: + # in: + # == code + # ab cd + # ef gh + # ij l1/imm32 + # labels: + # - 'l1': 0x1056 + # + # output: + # ab cd + # ef gh + # ij 56 10 00 00 + # + # . prologue + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # setup + # . clear-stream(_test-input-stream) + # . . push args + 68/push _test-input-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . clear-stream(_test-output-stream) + # . . push args + 68/push _test-output-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . clear-stream($_test-output-buffered-file->buffer) + # . . push args + 68/push $_test-output-buffered-file->buffer/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . var labels/edx: (stream byte 8*24) + 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0xc0/imm32 # subtract from esp + 68/push 0xc0/imm32/size + 68/push 0/imm32/read + 68/push 0/imm32/write + 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx + # . var h/ebx: (handle array byte) + 68/push 0/imm32 + 68/push 0/imm32 + 89/copy 3/mod/direct 3/rm32/ebx . . . 4/r32/esp . . # copy esp to ebx + # initialize input + # . write(_test-input-stream, "== code 0x1000\n") + # . . push args + 68/push "== code\n"/imm32 + 68/push _test-input-stream/imm32 + # . . call + e8/call write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . write(_test-input-stream, "ab cd\n") + # . . push args + 68/push "ab cd\n"/imm32 + 68/push _test-input-stream/imm32 + # . . call + e8/call write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . write(_test-input-stream, "ef gh\n") + # . . push args + 68/push "ef gh\n"/imm32 + 68/push _test-input-stream/imm32 + # . . call + e8/call write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . write(_test-input-stream, "ij l1/imm32\n") + # . . push args + 68/push "ij l1/imm32\n"/imm32 + 68/push _test-input-stream/imm32 + # . . call + e8/call write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . stream-add2(labels, "l1", 0x1056) + 68/push 0x1056/imm32/label-address + # . . push handle for "l1" + 53/push-ebx + 68/push "l1"/imm32 + 68/push Heap/imm32 + e8/call copy-array/disp32 + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + ff 6/subop/push 1/mod/*+disp8 3/rm32/ebx . . . . 4/disp8 . # push *(ebx+4) + ff 6/subop/push 0/mod/indirect 3/rm32/ebx . . . . . . # push *ebx + # . . push labels + 52/push-edx + # . . call + e8/call stream-add2/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp + # component under test + # . emit-output(_test-input-stream, _test-output-buffered-file, labels) + # . . push args + 52/push-edx + 68/push _test-output-buffered-file/imm32 + 68/push _test-input-stream/imm32 + # . . call + e8/call emit-output/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # checks + # . flush(_test-output-buffered-file) + # . . push args + 68/push _test-output-buffered-file/imm32 + # . . call + e8/call flush/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp +#? # dump output {{{ +#? # . write(2/stderr, "result: ^") +#? # . . push args +#? 68/push "result: ^"/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # . write-stream(2/stderr, _test-output-stream) +#? # . . push args +#? 68/push _test-output-stream/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write-stream/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # . write(2/stderr, "$\n") +#? # . . push args +#? 68/push "$\n"/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # . rewind-stream(_test-output-stream) +#? # . . push args +#? 68/push _test-output-stream/imm32 +#? # . . call +#? e8/call rewind-stream/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp +#? # }}} + # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c00", msg) + # . . push args + 68/push "F - test-emit-output-code-label-absolute/0"/imm32 + 68/push "# 0x00007c00"/imm32 + 68/push _test-output-stream/imm32 + # . . call + e8/call check-next-stream-line-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . check-next-stream-line-equal(_test-output-stream, "ab cd ", msg) + # . . push args + 68/push "F - test-emit-output-code-label-absolute/1"/imm32 + 68/push "ab cd "/imm32 + 68/push _test-output-stream/imm32 + # . . call + e8/call check-next-stream-line-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . check-next-stream-line-equal(_test-output-stream, "ef gh ", msg) + # . . push args + 68/push "F - test-emit-output-code-label-absolute/2"/imm32 + 68/push "ef gh "/imm32 + 68/push _test-output-stream/imm32 + # . . call + e8/call check-next-stream-line-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . check-next-stream-line-equal(_test-output-stream, "ij f9 ff ff ff ", msg) + # . . push args + 68/push "F - test-emit-output-code-label-absolute/3"/imm32 + 68/push "ij 56 10 00 00 "/imm32 + 68/push _test-output-stream/imm32 + # . . call + e8/call check-next-stream-line-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . epilogue + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +# reads line to make some checks +# don't assume the read state of line after calling this function +far-jump-or-call?: # line: (addr stream byte) -> result/edi: boolean + # . prologue + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 50/push-eax + 51/push-ecx + 52/push-edx + 53/push-ebx + # ecx = line + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 8/disp8 . # copy *(ebp+8) to ecx + # var word-slice/edx: slice + 68/push 0/imm32/end + 68/push 0/imm32/start + 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx + # var datum-slice/ebx: slice + 68/push 0/imm32/end + 68/push 0/imm32/start + 89/copy 3/mod/direct 3/rm32/ebx . . . 4/r32/esp . . # copy esp to ebx + # result = false + bf/copy-to-edi 0/imm32/false +$far-jump-or-call?:check-first-word: + # next-word(line, word-slice) + # . . push args + 52/push-edx + 51/push-ecx + # . . call + e8/call next-word/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # if (slice-empty?(word-slice)) return false + # . eax = slice-empty?(word-slice) + # . . push args + 52/push-edx + # . . call + e8/call slice-empty?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . if (eax != 0) return + 3d/compare-eax-and 0/imm32/false + 0f 85/jump-if-!= $far-jump-or-call?:end/disp32 + # datum = next-token-from-slice(word-slice->start, word-slice->end, "/") + # . . push args + 53/push-ebx + 68/push 0x2f/imm32/slash + ff 6/subop/push 1/mod/*+disp8 2/rm32/edx . . . . 4/disp8 . # push *(edx+4) + ff 6/subop/push 0/mod/indirect 2/rm32/edx . . . . . . # push *edx + # . . call + e8/call next-token-from-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp + # if (datum-slice == "e8") return true + # . eax = slice-equal?(datum-slice, "e8") + # . . push args + 68/push "e8"/imm32 + 53/push-ebx + # . . call + e8/call slice-equal?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . if (eax != false) return true + 3d/compare-eax-and 0/imm32/false + 75/jump-if-!= $far-jump-or-call?:return-true/disp8 + # if (datum-slice == "e9") return true + # . eax = slice-equal?(datum-slice, "e9") + # . . push args + 68/push "e9"/imm32 + 53/push-ebx + # . . call + e8/call slice-equal?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . if (eax != false) return true + 3d/compare-eax-and 0/imm32/false + 75/jump-if-!= $far-jump-or-call?:return-true/disp8 + # if (datum-slice != "0f") return false + # . eax = slice-equal?(datum-slice, "0f") + # . . push args + 68/push "0f"/imm32 + 53/push-ebx + # . . call + e8/call slice-equal?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . if (eax == false) return + 3d/compare-eax-and 0/imm32/false + 74/jump-if-= $far-jump-or-call?:end/disp8 +$far-jump-or-call?:check-second-word: + # next-word(line, word-slice) + # . . push args + 52/push-edx + 51/push-ecx + # . . call + e8/call next-word/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # if (slice-empty?(word-slice)) return false + # . eax = slice-empty?(word-slice) + # . . push args + 52/push-edx + # . . call + e8/call slice-empty?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . if (eax != 0) return + 3d/compare-eax-and 0/imm32/false + 75/jump-if-!= $far-jump-or-call?:end/disp8 + # if datum of word-slice does not start with "8", return false + # . start/eax = word-slice->start + 8b/copy 0/mod/indirect 2/rm32/edx . . . 0/r32/eax . . # copy *edx to eax + # . c/eax = *start + 8a/copy-byte 0/mod/indirect 0/rm32/eax . . . 0/r32/AL . . # copy byte at *eax to AL + 81 4/subop/and 3/mod/direct 0/rm32/eax . . . . . 0xff/imm32 # bitwise and of eax + # . if (eax != '8') return + 3d/compare-eax-and 0x38/imm32/8 + 75/jump-if-!= $far-jump-or-call?:end/disp8 + # otherwise return true +$far-jump-or-call?:return-true: + bf/copy-to-edi 1/imm32/true +$far-jump-or-call?:end: + # . reclaim locals + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp + # . restore registers + 5b/pop-to-ebx + 5a/pop-to-edx + 59/pop-to-ecx + 58/pop-to-eax + # . epilogue + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +# - some helpers for tests + +stream-add2: # in: (addr stream byte), key: handle, val: int + # . prologue + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 50/push-eax + 51/push-ecx + 52/push-edx + 56/push-esi + # esi = in + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi + # curr/eax = &in->data[in->write] + # . eax = in->write + 8b/copy 0/mod/indirect 6/rm32/esi . . . 0/r32/eax . . # copy *esi to eax + # . eax = esi+eax+12 + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/esi 0/index/eax . 0/r32/eax 0xc/disp8 . # copy esi+eax+12 to eax + # max/edx = &in->data[in->size] + # . edx = in->size + 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 2/r32/edx 8/disp8 . # copy *(esi+8) to edx + # . edx = esi+edx+12 + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/esi 2/index/edx . 2/r32/edx 0xc/disp8 . # copy esi+edx+12 to edx + # if (curr >= max) abort + 39/compare 3/mod/direct 0/rm32/eax . . . 2/r32/edx . . # compare eax with edx + 73/jump-if-addr>= $stream-add2:abort/disp8 + # *curr = key->alloc-id + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . 1/r32/ecx 0xc/disp8 . # copy *(ebp+12) to ecx + 89/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy ecx to *eax + # curr += 4 + 05/add-to-eax 4/imm32 + # if (curr >= max) abort + 39/compare 3/mod/direct 0/rm32/eax . . . 2/r32/edx . . # compare eax with edx + 73/jump-if-addr>= $stream-add2:abort/disp8 + # *curr = key->payload + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . 1/r32/ecx 0x10/disp8 . # copy *(ebp+16) to ecx + 89/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy ecx to *eax + # curr += 4 + 05/add-to-eax 4/imm32 + # if (curr >= max) abort + 39/compare 3/mod/direct 0/rm32/eax . . . 2/r32/edx . . # compare eax with edx + 73/jump-if-addr>= $stream-add2:abort/disp8 + # *curr = val + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . 1/r32/ecx 0x14/disp8 . # copy *(ebp+20) to ecx + 89/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy ecx to *eax + # in->write += 0xc + 81 0/subop/add 0/mod/indirect 6/rm32/esi . . . . . 0xc/imm32 # add to *esi +$stream-add2:end: + # . restore registers + 5e/pop-to-esi + 5a/pop-to-edx + 59/pop-to-ecx + 58/pop-to-eax + # . epilogue + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +$stream-add2:abort: + # . _write(2/stderr, error) + # . . push args + 68/push "overflow in stream-add2\n"/imm32 + 68/push 2/imm32/stderr + # . . call + e8/call _write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . syscall(exit, 1) + bb/copy-to-ebx 1/imm32 + e8/call syscall_exit/disp32 + # never gets here + +# some variants of 'trace' that take multiple arguments in different combinations of types: +# n: int +# c: character [4-bytes, will eventually be UTF-8] +# s: (addr array byte) +# l: (addr slice) +# one gotcha: 's5' must not be empty + +trace-slsns: # s1: (addr array byte), l2: (addr slice), s3: (addr array byte), n4: int, s5: (addr array byte) + # . prologue + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # write(*Trace-stream, s1) + # . . push args + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) + ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream + # . . call + e8/call write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # write-slice(*Trace-stream, l2) + # . . push args + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) + ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream + # . . call + e8/call write-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # write(*Trace-stream, s3) + # . . push args + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16) + ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream + # . . call + e8/call write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # write-int32-hex(*Trace-stream, n4) + # . . push args + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x14/disp8 . # push *(ebp+20) + ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream + # . . call + e8/call write-int32-hex/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # trace(s5) # implicitly adds a newline and finalizes the trace line + # . . push args + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x18/disp8 . # push *(ebp+24) + # . . call + e8/call trace/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp +$trace-slsns:end: + # . epilogue + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-trace-slsns: + # . prologue + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # setup + # . *Trace-stream->write = 0 + 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/eax Trace-stream/disp32 # copy *Trace-stream to eax + c7 0/subop/copy 0/mod/direct 0/rm32/eax . . . . . 0/imm32 # clear *eax + # (eax..ecx) = "b" + b8/copy-to-eax "b"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var b/ebx: slice = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 3/rm32/ebx . . . 4/r32/esp . . # copy esp to ebx + # trace-slsls("A" b "c " 3 " e") + # . . push args + 68/push " e"/imm32 + 68/push 3/imm32 + 68/push "c "/imm32 + 53/push-ebx + 68/push "A"/imm32 + # . . call + e8/call trace-slsns/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x14/imm32 # add to esp +#? # dump *Trace-stream {{{ +#? # . write(2/stderr, "^") +#? # . . push args +#? 68/push "^"/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # . write-stream(2/stderr, *Trace-stream) +#? # . . push args +#? ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write-stream/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # . write(2/stderr, "$\n") +#? # . . push args +#? 68/push "$\n"/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # }}} + # check-trace-contains("Abc 0x00000003 e") + # . . push args + 68/push "F - test-trace-slsls"/imm32 + 68/push "Abc 0x00000003 e"/imm32 + # . . call + e8/call check-trace-contains/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . epilogue + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +num-bytes: # line: (addr stream byte) -> eax: int + # pseudocode: + # result = 0 + # while true + # var word-slice = next-word(line) + # if slice-empty?(word-slice) # end of line + # break + # if slice-starts-with?(word-slice, "#") # comment + # break + # if label?(word-slice) # no need for label declarations anymore + # break + # if slice-equal?(word-slice, "==") + # break # no need for segment header lines + # result += compute-width(word-slice) + # return result + # + # . prologue + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 51/push-ecx + 52/push-edx + 53/push-ebx + # var result/eax = 0 + 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax + # var word-slice/ecx: slice + 68/push 0/imm32/end + 68/push 0/imm32/start + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx +#? # dump line {{{ +#? # . write(2/stderr, "LL: ") +#? # . . push args +#? 68/push "LL: "/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # write-stream(2/stderr, line) +#? # . . push args +#? ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write-stream/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # . write(2/stderr, "$\n") +#? # . . push args +#? 68/push "$\n"/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # }}} + # . rewind-stream(line) + # . . push args + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) + # . . call + e8/call rewind-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp +$num-bytes:loop: + # next-word(line, word-slice) + # . . push args + 51/push-ecx + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) + # . . call + e8/call next-word/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # dump word-slice {{{ +#? # . write(2/stderr, "AA: ") +#? # . . push args +#? 68/push "AA: "/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # . clear-stream($Stderr->buffer) +#? # . . push args +#? 68/push $Stderr->buffer/imm32 +#? # . . call +#? e8/call clear-stream/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp +#? # . write-slice-buffered(Stderr, word-slice) +#? # . . push args +#? 51/push-ecx +#? 68/push Stderr/imm32 +#? # . . call +#? e8/call write-slice-buffered/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # . flush(Stderr) +#? # . . push args +#? 68/push Stderr/imm32 +#? # . . call +#? e8/call flush/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp +#? # . write(2/stderr, "$\n") +#? # . . push args +#? 68/push "$\n"/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # }}} +$num-bytes:check0: + # if (slice-empty?(word-slice)) break + # . save result + 50/push-eax + # . eax = slice-empty?(word-slice) + # . . push args + 51/push-ecx + # . . call + e8/call slice-empty?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . if (eax != false) break + 3d/compare-eax-and 0/imm32/false + # . restore result now that ZF is set + 58/pop-to-eax + 75/jump-if-!= $num-bytes:end/disp8 +$num-bytes:check-for-comment: + # if (slice-starts-with?(word-slice, "#")) break + # . start/edx = word-slice->start + 8b/copy 0/mod/indirect 1/rm32/ecx . . . 2/r32/edx . . # copy *ecx to edx + # . c/ebx = *start + 31/xor 3/mod/direct 3/rm32/ebx . . . 3/r32/ebx . . # clear ebx + 8a/copy-byte 0/mod/indirect 2/rm32/edx . . . 3/r32/BL . . # copy byte at *edx to BL + # . if (ebx == '#') break + 81 7/subop/compare 3/mod/direct 3/rm32/ebx . . . . . 0x23/imm32/hash # compare ebx + 74/jump-if-= $num-bytes:end/disp8 +$num-bytes:check-for-label: + # if (slice-ends-with?(word-slice, ":")) break + # . end/edx = word-slice->end + 8b/copy 1/mod/*+disp8 1/rm32/ecx . . . 2/r32/edx 4/disp8 . # copy *(ecx+4) to edx + # . c/ebx = *(end-1) + 31/xor 3/mod/direct 3/rm32/ebx . . . 3/r32/ebx . . # clear ebx + 8a/copy-byte 1/mod/*+disp8 2/rm32/edx . . . 3/r32/BL -1/disp8 . # copy byte at *ecx to BL + # . if (ebx == ':') break + 81 7/subop/compare 3/mod/direct 3/rm32/ebx . . . . . 0x3a/imm32/colon # compare ebx + 74/jump-if-= $num-bytes:end/disp8 +$num-bytes:check-for-segment-header: + # if (slice-equal?(word-slice, "==")) break + # . push result + 50/push-eax + # . eax = slice-equal?(word-slice, "==") + # . . push args + 68/push "=="/imm32 + 51/push-ecx + # . . call + e8/call slice-equal?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . if (eax != false) break + 3d/compare-eax-and 0/imm32/false + # . restore result now that ZF is set + 58/pop-to-eax + 75/jump-if-!= $num-bytes:end/disp8 +$num-bytes:loop-body: + # result += compute-width-of-slice(word-slice) + # . copy result to edx + 89/copy 3/mod/direct 2/rm32/edx . . . 0/r32/eax . . # copy eax to edx + # . eax = compute-width-of-slice(word-slice) + # . . push args + 51/push-ecx + # . . call + e8/call compute-width-of-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . eax += result + 01/add 3/mod/direct 0/rm32/eax . . . 2/r32/edx . . # add edx to eax + e9/jump $num-bytes:loop/disp32 +$num-bytes:end: + # . rewind-stream(line) + # . . push args + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) + # . . call + e8/call rewind-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . reclaim locals + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . restore registers + 5b/pop-to-ebx + 5a/pop-to-edx + 59/pop-to-ecx + # . epilogue + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-num-bytes-handles-empty-string: + # if a line starts with '#', return 0 + # . prologue + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # setup + # . clear-stream(_test-input-stream) + # . . push args + 68/push _test-input-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . clear-stream(_test-output-stream) + # . . push args + 68/push _test-output-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # no contents in input + # eax = num-bytes(_test-input-stream) + # . . push args + 68/push _test-input-stream/imm32 + # . . call + e8/call num-bytes/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-ints-equal(eax, 0, msg) + # . . push args + 68/push "F - test-num-bytes-handles-empty-string"/imm32 + 68/push 0/imm32/true + 50/push-eax + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . epilogue + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-num-bytes-ignores-comments: + # if a line starts with '#', return 0 + # . prologue + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # setup + # . clear-stream(_test-input-stream) + # . . push args + 68/push _test-input-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . clear-stream(_test-output-stream) + # . . push args + 68/push _test-output-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # initialize input + # . write(_test-input-stream, "# abcd") + # . . push args + 68/push "# abcd"/imm32 + 68/push _test-input-stream/imm32 + # . . call + e8/call write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # eax = num-bytes(_test-input-stream) + # . . push args + 68/push _test-input-stream/imm32 + # . . call + e8/call num-bytes/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-ints-equal(eax, 0, msg) + # . . push args + 68/push "F - test-num-bytes-ignores-comments"/imm32 + 68/push 0/imm32/true + 50/push-eax + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . epilogue + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-num-bytes-ignores-labels: + # if the first word ends with ':', return 0 + # . prologue + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # setup + # . clear-stream(_test-input-stream) + # . . push args + 68/push _test-input-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . clear-stream(_test-output-stream) + # . . push args + 68/push _test-output-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # initialize input + # . write(_test-input-stream, "ab: # cd") + # . . push args + 68/push "ab: # cd"/imm32 + 68/push _test-input-stream/imm32 + # . . call + e8/call write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # eax = num-bytes(_test-input-stream) + # . . push args + 68/push _test-input-stream/imm32 + # . . call + e8/call num-bytes/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-ints-equal(eax, 0, msg) + # . . push args + 68/push "F - test-num-bytes-ignores-labels"/imm32 + 68/push 0/imm32/true + 50/push-eax + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . epilogue + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-num-bytes-ignores-segment-headers: + # if the first word is '==', return 0 + # . prologue + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # setup + # . clear-stream(_test-input-stream) + # . . push args + 68/push _test-input-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . clear-stream(_test-output-stream) + # . . push args + 68/push _test-output-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # initialize input + # . write(_test-input-stream, "== ab cd") + # . . push args + 68/push "== ab cd"/imm32 + 68/push _test-input-stream/imm32 + # . . call + e8/call write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # eax = num-bytes(_test-input-stream) + # . . push args + 68/push _test-input-stream/imm32 + # . . call + e8/call num-bytes/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-ints-equal(eax, 0, msg) + # . . push args + 68/push "F - test-num-bytes-ignores-segment-headers"/imm32 + 68/push 0/imm32/true + 50/push-eax + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . epilogue + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-num-bytes-counts-words-by-default: + # without metadata, count words + # . prologue + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # setup + # . clear-stream(_test-input-stream) + # . . push args + 68/push _test-input-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . clear-stream(_test-output-stream) + # . . push args + 68/push _test-output-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # initialize input + # . write(_test-input-stream, "ab cd ef") + # . . push args + 68/push "ab cd ef"/imm32 + 68/push _test-input-stream/imm32 + # . . call + e8/call write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # eax = num-bytes(_test-input-stream) + # . . push args + 68/push _test-input-stream/imm32 + # . . call + e8/call num-bytes/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-ints-equal(eax, 3, msg) + # . . push args + 68/push "F - test-num-bytes-counts-words-by-default"/imm32 + 68/push 3/imm32/true + 50/push-eax + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . epilogue + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-num-bytes-ignores-trailing-comment: + # trailing comments appropriately ignored + # . prologue + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # setup + # . clear-stream(_test-input-stream) + # . . push args + 68/push _test-input-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . clear-stream(_test-output-stream) + # . . push args + 68/push _test-output-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # initialize input + # . write(_test-input-stream, "ab cd # ef") + # . . push args + 68/push "ab cd # ef"/imm32 + 68/push _test-input-stream/imm32 + # . . call + e8/call write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # eax = num-bytes(_test-input-stream) + # . . push args + 68/push _test-input-stream/imm32 + # . . call + e8/call num-bytes/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-ints-equal(eax, 2, msg) + # . . push args + 68/push "F - test-num-bytes-ignores-trailing-comment"/imm32 + 68/push 2/imm32/true + 50/push-eax + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . epilogue + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-num-bytes-handles-imm32: + # if a word has the /imm32 metadata, count it as 4 bytes + # . prologue + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # setup + # . clear-stream(_test-input-stream) + # . . push args + 68/push _test-input-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . clear-stream(_test-output-stream) + # . . push args + 68/push _test-output-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # initialize input + # . write(_test-input-stream, "ab cd/imm32 ef") + # . . push args + 68/push "ab cd/imm32 ef"/imm32 + 68/push _test-input-stream/imm32 + # . . call + e8/call write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # eax = num-bytes(_test-input-stream) + # . . push args + 68/push _test-input-stream/imm32 + # . . call + e8/call num-bytes/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-ints-equal(eax, 6, msg) + # . . push args + 68/push "F - test-num-bytes-handles-imm32"/imm32 + 68/push 6/imm32/true + 50/push-eax + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . epilogue + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +# . . vim:nowrap:textwidth=0 diff --git a/linux/survey_baremetal b/linux/survey_baremetal index 91e0c575..46de25b7 100755 Binary files a/linux/survey_baremetal and b/linux/survey_baremetal differ diff --git a/linux/survey_baremetal.subx b/linux/survey_baremetal.subx index 016757a8..d1d62906 100644 --- a/linux/survey_baremetal.subx +++ b/linux/survey_baremetal.subx @@ -1,6 +1,5 @@ # Assign addresses (co-ordinates) to instructions (landmarks) in a program # (landscape). -# Use the addresses assigned to replace labels. # # To build: # $ bootstrap/bootstrap translate [01]*.subx subx-params.subx survey_baremetal.subx -o survey_baremetal @@ -23,18 +22,12 @@ # foo: # 34 # -# The output is the stream of bytes without segment headers or label definitions, -# and with label references replaced with numeric values/displacements. +# The output is a list of labels and their computed addresses. # # $ cat x |bootstrap/bootstrap run survey_baremetal -# # 0x7c00 -# aa bb nn # some computed address -# cc dd nn nn nn nn # some computed displacement -# ee nn nn nn nn # address right after this instruction -# # 0x7c0e -# 00 00 # padding -# # 0x7c10 -# 34 # data segment interleaved with code +# 0x7c00 l1 +# 0x7c09 l2 +# 0x7c10 foo == code # instruction effective address register displacement immediate @@ -78,7 +71,6 @@ Entry: # run tests if necessary, convert stdin if not 3d/compare-eax-and 0/imm32/false 74/jump-if-= $subx-survey-main:interactive/disp8 # run-tests() -#? e8/call test-emit-output-with-padding/disp32 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 @@ -112,8 +104,7 @@ subx-survey: # infile: (addr buffered-file), out: (addr buffered-file) # slurp(infile, in) # var labels: (stream {label-name, address} Max-labels) # compute-addresses(in, labels) - # rewind-stream(in) - # emit-output(in, out, labels) + # emit-labels(out, labels) # # . prologue 55/push-ebp @@ -159,22 +150,14 @@ subx-survey: # infile: (addr buffered-file), out: (addr buffered-file) e8/call compute-addresses/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # rewind-stream(in) - # . . push args - 56/push-esi - # . . call - e8/call rewind-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # emit-output(in, out, labels) + # emit-labels(out, labels) # . . push args 52/push-edx ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) - 56/push-esi # . . call - e8/call emit-output/disp32 + e8/call emit-labels/disp32 # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # flush(out) # . . push args ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) @@ -936,78 +919,19 @@ test-compute-addresses: 5d/pop-to-ebp c3/return -# global scratch space for emit-output -== data - -emit-output:datum: # slice - 0/imm32/start - 0/imm32/end - -== code - -emit-output: # in: (addr stream byte), out: (addr buffered-file), labels: (addr stream {(handle array byte), address}) +emit-labels: # out: (addr buffered-file), labels: (addr stream {(handle array byte), address}) # pseudocode: - # var address-of-next-instruction = 0x7c00 - # var line: (stream byte 512) - # line-loop: - # while true - # clear-stream(line) - # read-line(in, line) - # if (line->write == 0) break # end of file - # address-of-next-instruction += num-bytes(line) - # var far-jump-or-call? = far-jump-or-call?(line) - # rewind-stream(line) - # while true - # var word-slice = next-word(line) - # if slice-empty?(word-slice) # end of line - # break - # if slice-starts-with?(word-slice, "#") # comment - # break - # if label?(word-slice) # no need for label declarations anymore - # goto line-loop # don't insert empty lines - # if slice-equal?(word-slice, "==") # no need for segment header lines - # word-slice = next-word(line) # skip segment name - # word-slice = next-word(line) - # if !slice-empty?(word-slice) - # new-address = parse-hex-int-from-slice(word-slice) - # write-buffered(out, "# " address-of-next-instruction "\n") - # while address-of-next-instruction < new-address - # write-buffered("00") - # ++address-of-next-instruction - # write-buffered(out, "# " address-of-next-instruction "\n") - # goto line-loop # don't insert empty lines - # if length(word-slice) == 2 - # write-slice-buffered(out, word-slice) - # write-buffered(out, " ") - # continue - # var datum: (addr slice) = next-token-from-slice(word-slice->start, word-slice->end, "/") - # var address: (addr int) = get-slice(labels, datum) - # if has-metadata?(word-slice, "imm8") - # emit(out, *address, 1) - # else if has-metadata?(word-slice, "imm16") - # emit(out, *address, 2) - # else if has-metadata?(word-slice, "imm32") - # emit(out, *address, 4) - # else if has-metadata?(word-slice, "disp8") - # value = *address - address-of-next-instruction - # emit(out, value, 1) - # else if has-metadata?(word-slice, "disp32") - # if far-jump-or-call? - # value = *address - address-of-next-instruction - # else - # value = *address - # emit(out, value, 4) - # else - # abort - # write-buffered(out, "\n") - # - # registers: - # line: ecx - # word-slice: edx - # address-of-next-instruction: ebx - # far-jump-or-call?: edi - # address: esi (inner loop only) - # temporaries: eax, esi (outer loop) + # curr = table->data + # max = &table->data[table->write] + # while curr < max + # var label: (addr array byte) = lookup(*curr) + # curr += 8 + # write-buffered(out, label) + # write-buffered(out, ' ') + # write-buffered(out, *curr) + # curr += 4 + # write(out, '\n') + # return 0 # # . prologue 55/push-ebp @@ -1016,671 +940,44 @@ emit-output: # in: (addr stream byte), out: (addr buffered-file), labels: (addr 50/push-eax 51/push-ecx 52/push-edx - 53/push-ebx 56/push-esi - 57/push-edi - # var line/ecx: (stream byte 512) - 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0x200/imm32 # subtract from esp - 68/push 0x200/imm32/size - 68/push 0/imm32/read - 68/push 0/imm32/write - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # var word-slice/edx: slice - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx - # var address-of-next-instruction/ebx = 0x7c00 - bb/copy-to-ebx 0x7c00/imm32 -$emit-output:line-loop: - # clear-stream(line) + # esi = table + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 0xc/disp8 . # copy *(ebp+12) to esi + # var curr/ecx: (addr handle array byte) = table->data + 8d/copy-address 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 0xc/disp8 . # copy esi+12 to ecx + # var max/edx: (addr byte) = &table->data[table->write] + 8b/copy 0/mod/indirect 6/rm32/esi . . . 2/r32/edx . . # copy *esi to edx + 8d/copy-address 0/mod/indirect 4/rm32/sib 1/base/ecx 2/index/edx . 2/r32/edx . . # copy ecx+edx to edx +$emit-labels:loop: + # if (curr >= max) return null + 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx + 0f 83/jump-if-addr>= $emit-labels:end/disp32 + # var label/eax: (addr array byte) = lookup(*curr) # . . push args - 51/push-ecx + ff 6/subop/push 1/mod/*+disp8 1/rm32/ecx . . . . 4/disp8 . # push *(ecx+4) + ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx # . . call - e8/call clear-stream/disp32 + e8/call lookup/disp32 # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # read-line(in, line) + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # curr += 8 + 81 0/subop/add 3/mod/direct 1/rm32/ecx . . . . . 8/imm32 # add to ecx + # write-buffered(out, label) # . . push args - 51/push-ecx + 50/push-eax ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) # . . call - e8/call read-line/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # dump line {{{ -#? # . write(2/stderr, "LL: ") -#? # . . push args -#? 68/push "LL: "/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # write-stream(2/stderr, line) -#? # . . push args -#? 51/push-ecx -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # . rewind-stream(line) -#? # . . push args -#? 51/push-ecx -#? # . . call -#? e8/call rewind-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp -#? # }}} -$emit-output:check-for-end-of-input: - # if (line->write == 0) break - 81 7/subop/compare 0/mod/indirect 1/rm32/ecx . . . . . 0/imm32 # compare *ecx - 0f 84/jump-if-= $emit-output:end/disp32 - # address-of-next-instruction += num-bytes(line) - # . eax = num-bytes(line) - # . . push args - 51/push-ecx - # . . call - e8/call num-bytes/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . ebx += eax - 01/add 3/mod/direct 3/rm32/ebx . . . 0/r32/eax . . # add eax to ebx - # var far-jump-or-call?/edi: boolean = far-jump-or-call?(line) - # . . push args - 51/push-ecx - # . . call - e8/call far-jump-or-call?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # rewind-stream(line) - # . . push args - 51/push-ecx - # . . call - e8/call rewind-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp -$emit-output:word-loop: - # next-word(line, word-slice) - # . . push args - 52/push-edx - 51/push-ecx - # . . call - e8/call next-word/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # dump word-slice {{{ -#? # . write(2/stderr, "w: ") -#? # . . push args -#? 68/push "w: "/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # . write-slice-buffered(Stderr, word-slice) -#? # . . push args -#? 52/push-edx -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call write-slice-buffered/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # . flush(Stderr) -#? # . . push args -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call flush/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # }}} -$emit-output:check-for-end-of-line: - # if (slice-empty?(word-slice)) break - # . eax = slice-empty?(word-slice) - # . . push args - 52/push-edx - # . . call - e8/call slice-empty?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . if (eax != 0) break - 3d/compare-eax-and 0/imm32/false - 0f 85/jump-if-!= $emit-output:next-line/disp32 -$emit-output:check-for-comment: - # if (slice-starts-with?(word-slice, "#")) break - # . start/esi = word-slice->start - 8b/copy 0/mod/indirect 2/rm32/edx . . . 6/r32/esi . . # copy *edx to esi - # . c/eax = *start - 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax - 8a/copy-byte 0/mod/indirect 6/rm32/esi . . . 0/r32/AL . . # copy byte at *esi to AL - # . if (eax == '#') break - 3d/compare-eax-and 0x23/imm32/hash - 0f 84/jump-if-= $emit-output:next-line/disp32 -$emit-output:check-for-label: - # if label?(word-slice) break - # . eax = label?(word-slice) - # . . push args - 52/push-edx - # . . call - e8/call label?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . if (eax != false) break - 3d/compare-eax-and 0/imm32/false - 0f 85/jump-if-!= $emit-output:line-loop/disp32 -$emit-output:check-for-segment-header: - # if !slice-equal?(word-slice, "==") goto next check - # . eax = slice-equal?(word-slice, "==") - # . . push args - 68/push "=="/imm32 - 52/push-edx - # . . call - e8/call slice-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . if (eax == false) goto next check - 3d/compare-eax-and 0/imm32/false - 0f 84/jump-if-= $emit-output:2-character/disp32 - # skip segment name - # . next-word(line, word-slice) - # . . push args - 52/push-edx - 51/push-ecx - # . . call - e8/call next-word/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # compute segment address if it exists - # . next-word(line, word-slice) - # . . push args - 52/push-edx - 51/push-ecx - # . . call - e8/call next-word/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . if slice-empty?(word-slice) goto padding-done - # . . push args - 52/push-edx - # . . call - e8/call slice-empty?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . . - 3d/compare-eax-and 0/imm32/false - 0f 85/jump-if-!= $emit-output:padding-done/disp32 - # . var new-address/eax: int = parse-hex-int-from-slice(word-slice) - # . . push args - 52/push-edx - # . . call - e8/call parse-hex-int-from-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # write-buffered(out, "# " address-of-next-instruction "\n") - # . write-buffered(out, "# ") - # . . push args - 68/push "# "/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) - # . . call e8/call write-buffered/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . write-int32-hex-buffered(out, address-of-next-instruction) - # . . push args - 53/push-ebx - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) - # . . call - e8/call write-int32-hex-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . write-buffered(out, "\n") - # . . push args - 68/push Newline/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) - # . . call - e8/call write-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -$emit-output:padding-loop: - # if (address-of-next-instruction >= new-address) goto padding-loop-done - 39/compare 3/mod/direct 3/rm32/ebx . . . 0/r32/eax . . # compare ebx with eax - 73/jump-if-addr>= $emit-output:padding-loop-done/disp8 - # if (address-of-next-instruction % 8 == 0) write-buffered("\n") - 53/push-ebx - 81 4/subop/and 3/mod/direct 3/rm32/ebx . . . . . 7/imm32 # bitwise and of ebx - 81 7/subop/compare 3/mod/direct 3/rm32/ebx . . . . . 0/imm32 # compare ebx - 5b/pop-to-ebx - 75/jump-if-!= $emit-output:padding-core/disp8 - # . write-buffered(out, "\n") - # . . push args - 68/push Newline/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) - # . . call - e8/call write-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -$emit-output:padding-core: - # write-buffered("00") - # . . push args - 68/push "00 "/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) - # . . call - e8/call write-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # ++address-of-next-instruction - 43/increment-ebx - # loop - eb/jump $emit-output:padding-loop/disp8 -$emit-output:padding-loop-done: - # . write-buffered(out, "\n") - # . . push args - 68/push Newline/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) - # . . call - e8/call write-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -$emit-output:padding-done: - # write-buffered(out, "# " address-of-next-instruction "\n") - # . write-buffered(out, "# ") - # . . push args - 68/push "# "/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) - # . . call - e8/call write-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . write-int32-hex-buffered(out, address-of-next-instruction) - # . . push args - 53/push-ebx - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) - # . . call - e8/call write-int32-hex-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . write-buffered(out, "\n") - # . . push args - 68/push Newline/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) - # . . call - e8/call write-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # - e9/jump $emit-output:line-loop/disp32 -$emit-output:2-character: - # if (size(word-slice) != 2) goto next check - # . eax = size(word-slice) - 8b/copy 1/mod/*+disp8 2/rm32/edx . . . 0/r32/eax 4/disp8 . # copy *(edx+4) to eax - 2b/subtract 0/mod/indirect 2/rm32/edx . . . 0/r32/eax . . # subtract *edx from eax - # . if (eax != 2) goto next check - 3d/compare-eax-and 2/imm32 - 75/jump-if-!= $emit-output:check-metadata/disp8 - # write-slice-buffered(out, word-slice) - # . . push args - 52/push-edx - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) - # . . call - e8/call write-slice-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # write-buffered(out, " ") + # write-buffered(out, ' ') # . . push args 68/push Space/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) # . . call e8/call write-buffered/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # continue - e9/jump $emit-output:word-loop/disp32 -$emit-output:check-metadata: - # - if we get here, 'word-slice' must be a label to be looked up - # datum = next-token-from-slice(word-slice->start, word-slice->end, "/") - # . . push args - 68/push emit-output:datum/imm32 - 68/push 0x2f/imm32/slash - ff 6/subop/push 1/mod/*+disp8 2/rm32/edx . . . . 4/disp8 . # push *(edx+4) - ff 6/subop/push 0/mod/indirect 2/rm32/edx . . . . . . # push *edx - # . . call - e8/call next-token-from-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp -#? # dump datum {{{ -#? # . write(2/stderr, "datum: ") -#? # . . push args -#? 68/push "datum: "/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # . write-slice-buffered(Stderr, datum) -#? # . . push args -#? 68/push emit-output:datum/imm32 -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call write-slice-buffered/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # . flush(Stderr) -#? # . . push args -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call flush/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # }}} - # address/esi: (addr int) = get-slice(labels, datum, row-size=12, "label table") - # . eax = get-slice(labels, datum, row-size=24, "label table") - # . . push args - 68/push "label table"/imm32 - 68/push 0xc/imm32/row-size - 68/push emit-output:datum/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16) - # . . call - e8/call get-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp - # . esi = eax - 89/copy 3/mod/direct 6/rm32/esi . . . 0/r32/eax . . # copy eax to esi -$emit-output:check-imm8: - # if (!has-metadata?(word-slice, "imm8")) goto next check - # . eax = has-metadata?(edx, "imm8") - # . . push args - 68/push "imm8"/imm32 - 52/push-edx - # . . call - e8/call has-metadata?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . if (eax != false) abort - 3d/compare-eax-and 0/imm32/false - 74/jump-if-= $emit-output:check-imm16/disp8 -$emit-output:emit-imm8: - # emit-hex(out, *address, 1) - # . . push args - 68/push 1/imm32 - ff 6/subop/push 0/mod/indirect 6/rm32/esi . . . . . . # push *esi - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) - # . . call - e8/call emit-hex/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # continue - e9/jump $emit-output:word-loop/disp32 -$emit-output:check-imm16: - # if (!has-metadata?(word-slice, "imm16")) goto next check - # . eax = has-metadata?(edx, "imm16") - # . . push args - 68/push "imm16"/imm32 - 52/push-edx - # . . call - e8/call has-metadata?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . if (eax == false) goto next check - 3d/compare-eax-and 0/imm32/false - 74/jump-if-= $emit-output:check-imm32/disp8 -#? # dump *address {{{ -#? # . write(2/stderr, "*address: ") -#? # . . push args -#? 68/push "*address: "/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # . write-int32-hex-buffered(Stderr, *address) -#? # . . push args -#? ff 6/subop/push 0/mod/indirect 6/rm32/esi . . . . . . # push *esi -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call write-int32-hex-buffered/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # . flush(Stderr) -#? # . . push args -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call flush/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # }}} -$emit-output:emit-imm16: - # emit-hex(out, *address, 2) - # . . push args - 68/push 2/imm32 - ff 6/subop/push 0/mod/indirect 6/rm32/esi . . . . . . # push *esi - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) - # . . call - e8/call emit-hex/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # TODO: ensure that the higher 2 bytes are zero - # continue - e9/jump $emit-output:word-loop/disp32 -$emit-output:check-imm32: - # if (!has-metadata?(word-slice, "imm32")) goto next check - # . eax = has-metadata?(edx, "imm32") - # . . push args - 68/push "imm32"/imm32 - 52/push-edx - # . . call - e8/call has-metadata?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . if (eax == false) goto next check - 3d/compare-eax-and 0/imm32/false - 74/jump-if-= $emit-output:check-disp8/disp8 -#? # dump *address {{{ -#? # . write(2/stderr, "*address: ") -#? # . . push args -#? 68/push "*address: "/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # . write-int32-hex-buffered(Stderr, *address) -#? # . . push args -#? ff 6/subop/push 0/mod/indirect 6/rm32/esi . . . . . . # push *esi -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call write-int32-hex-buffered/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # . flush(Stderr) -#? # . . push args -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call flush/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # }}} -$emit-output:emit-imm32: - # emit-hex(out, *address, 4) - # . . push args - 68/push 4/imm32 - ff 6/subop/push 0/mod/indirect 6/rm32/esi . . . . . . # push *esi - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) - # . . call - e8/call emit-hex/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # continue - e9/jump $emit-output:word-loop/disp32 -$emit-output:check-disp8: - # if (!has-metadata?(word-slice, "disp8")) goto next check - # . eax = has-metadata?(edx, "disp8") - # . . push args - 68/push "disp8"/imm32 - 52/push-edx - # . . call - e8/call has-metadata?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . if (eax == false) goto next check - 3d/compare-eax-and 0/imm32/false - 74/jump-if-= $emit-output:check-disp16/disp8 -$emit-output:emit-disp8: - # emit-hex(out, *address - address-of-next-instruction, 1) - # . . push args - 68/push 1/imm32 - 8b/copy 0/mod/indirect 6/rm32/esi . . . 0/r32/eax . . # copy *esi to eax - 29/subtract 3/mod/direct 0/rm32/eax . . . 3/r32/ebx . . # subtract ebx from eax - 50/push-eax - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) - # . . call - e8/call emit-hex/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # continue - e9/jump $emit-output:word-loop/disp32 -$emit-output:check-disp16: - # if (!has-metadata?(word-slice, "disp16")) goto next check - # . eax = has-metadata?(edx, "disp16") - # . . push args - 68/push "disp16"/imm32 - 52/push-edx - # . . call - e8/call has-metadata?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . if (eax == false) goto next check - 3d/compare-eax-and 0/imm32/false - 74/jump-if-= $emit-output:check-disp32/disp8 -$emit-output:emit-disp16: - # emit-hex(out, *address - address-of-next-instruction, 2) - # . . push args - 68/push 2/imm32 - 8b/copy 0/mod/indirect 6/rm32/esi . . . 0/r32/eax . . # copy *esi to eax - 29/subtract 3/mod/direct 0/rm32/eax . . . 3/r32/ebx . . # subtract ebx from eax - 50/push-eax - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) - # . . call - e8/call emit-hex/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # continue - e9/jump $emit-output:word-loop/disp32 -$emit-output:check-disp32: - # if (!has-metadata?(word-slice, "disp32")) abort - # . eax = has-metadata?(edx, "disp32") - # . . push args - 68/push "disp32"/imm32 - 52/push-edx - # . . call - e8/call has-metadata?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . if (eax == false) abort - 3d/compare-eax-and 0/imm32/false - 0f 84/jump-if-= $emit-output:abort/disp32 -$emit-output:emit-disp32: - # var value/eax = *address - 8b/copy 0/mod/indirect 6/rm32/esi . . . 0/r32/eax . . # copy *esi to eax - # if (far-jump-or-call?) value -= address-of-next-instruction - 81 7/subop/compare 3/mod/direct 7/rm32/edi . . . . . 0/imm32/false # compare edi - 74/jump-if-= $emit-output:really-emit-disp32/disp8 - 29/subtract 3/mod/direct 0/rm32/eax . . . 3/r32/ebx . . # subtract ebx from eax -$emit-output:really-emit-disp32: - # emit-hex(out, value, 4) - # . . push args - 68/push 4/imm32 - 50/push-eax - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) - # . . call - e8/call emit-hex/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # continue - e9/jump $emit-output:word-loop/disp32 -$emit-output:next-line: - # write-buffered(out, "\n") - # . . push args - 68/push Newline/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) - # . . call - e8/call write-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # loop - e9/jump $emit-output:line-loop/disp32 -$emit-output:end: - # . reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x214/imm32 # add to esp - # . restore registers - 5f/pop-to-edi - 5e/pop-to-esi - 5b/pop-to-ebx - 5a/pop-to-edx - 59/pop-to-ecx - 58/pop-to-eax - # . epilogue - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -$emit-output:abort: - # print(stderr, "missing metadata in " word-slice) - # . _write(2/stderr, "missing metadata in word ") - # . . push args - 68/push "emit-output: missing metadata in "/imm32 - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . write-slice-buffered(Stderr, word-slice) - # . . push args - 52/push-edx - 68/push Stderr/imm32 - # . . call - e8/call write-slice-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . flush(Stderr) # . . push args 68/push Stderr/imm32 @@ -1688,1014 +985,27 @@ $emit-output:abort: e8/call flush/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . syscall(exit, 1) - bb/copy-to-ebx 1/imm32 - e8/call syscall_exit/disp32 - # never gets here - -test-emit-output-non-far-control-flow: - # labels turn into absolute addresses if opcodes are not far jumps or calls - # - # input: - # in: - # == code - # ab cd ef gh - # ij x/disp32 - # == data - # 00 - # 34 - # labels: - # - 'x': 0x11223344 - # - # output: - # ab cd ef gh - # ij 44 33 22 11 - # 00 - # 34 - # - # . prologue - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # setup - # . clear-stream(_test-input-stream) + # write-int32-hex-buffered(out, *curr) # . . push args - 68/push _test-input-stream/imm32 + ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . clear-stream($_test-output-buffered-file->buffer) - # . . push args - 68/push $_test-output-buffered-file->buffer/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . var labels/edx: (stream byte 8*24) - 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0xc0/imm32 # subtract from esp - 68/push 0xc0/imm32/size - 68/push 0/imm32/read - 68/push 0/imm32/write - 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx - # . var h/ebx: (handle array byte) - 68/push 0/imm32 - 68/push 0/imm32 - 89/copy 3/mod/direct 3/rm32/ebx . . . 4/r32/esp . . # copy esp to ebx - # initialize input - # . write(_test-input-stream, "== code\n") - # . . push args - 68/push "== code\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 + e8/call write-int32-hex-buffered/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . write(_test-input-stream, "ab cd ef gh\n") - # . . push args - 68/push "ab cd ef gh\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . write(_test-input-stream, "ij x/disp32\n") - # . . push args - 68/push "ij x/disp32\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . write(_test-input-stream, "== data\n") - # . . push args - 68/push "== data\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . write(_test-input-stream, "00\n") - # . . push args - 68/push "00\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . write(_test-input-stream, "34\n") - # . . push args - 68/push "34\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . stream-add2(labels, "x", 0x11223344) - 68/push 0x11223344/imm32/label-address - # . . push handle for "x" - 53/push-ebx - 68/push "x"/imm32 - 68/push Heap/imm32 - e8/call copy-array/disp32 - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - ff 6/subop/push 1/mod/*+disp8 3/rm32/ebx . . . . 4/disp8 . # push *(ebx+4) - ff 6/subop/push 0/mod/indirect 3/rm32/ebx . . . . . . # push *ebx - # . . push labels - 52/push-edx - # . . call - e8/call stream-add2/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp - # component under test - # . emit-output(_test-input-stream, _test-output-buffered-file, labels) - # . . push args - 52/push-edx - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call emit-output/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # checks - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp -#? # dump output {{{ -#? # . write(2/stderr, "result: ^") -#? # . . push args -#? 68/push "result: ^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # . rewind-stream(_test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? # . . call -#? e8/call rewind-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp -#? # }}} - # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c00", msg) - # . . push args - 68/push "F - test-emit-output-non-far-control-flow/0"/imm32 - 68/push "# 0x00007c00"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . check-next-stream-line-equal(_test-output-stream, "ab cd ef gh ", msg) - # . . push args - 68/push "F - test-emit-output-non-far-control-flow/1"/imm32 - 68/push "ab cd ef gh "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . check-next-stream-line-equal(_test-output-stream, "ij 44 33 22 11 ", msg) - # . . push args - 68/push "F - test-emit-output-non-far-control-flow/2"/imm32 - 68/push "ij 44 33 22 11 "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c09", msg) - # . . push args - 68/push "F - test-emit-output-non-far-control-flow/3"/imm32 - 68/push "# 0x00007c09"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . check-next-stream-line-equal(_test-output-stream, "00 ", msg) - # . . push args - 68/push "F - test-emit-output-non-far-control-flow/3"/imm32 - 68/push "00 "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . check-next-stream-line-equal(_test-output-stream, "34 ", msg) - # . . push args - 68/push "F - test-emit-output-non-far-control-flow/4"/imm32 - 68/push "34 "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . epilogue - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-emit-output-with-padding: - # labels turn into absolute addresses if opcodes are not far jumps or calls - # - # input: - # in: - # == code - # ab cd ef gh - # == data 0x7c10 - # 34 - # - # output: - # ab cd ef gh - # # 0x7c04 - # 00 00 00 00 - # 00 00 00 00 00 00 00 00 - # # 0x7c10 - # 34 - # - # . prologue - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . clear-stream($_test-output-buffered-file->buffer) - # . . push args - 68/push $_test-output-buffered-file->buffer/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . var labels/edx: (stream byte 8*24) - 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0xc0/imm32 # subtract from esp - 68/push 0xc0/imm32/size - 68/push 0/imm32/read - 68/push 0/imm32/write - 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx - # . var h/ebx: (handle array byte) - 68/push 0/imm32 - 68/push 0/imm32 - 89/copy 3/mod/direct 3/rm32/ebx . . . 4/r32/esp . . # copy esp to ebx - # initialize input - # . write(_test-input-stream, "== code\n") - # . . push args - 68/push "== code\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . write(_test-input-stream, "ab cd ef gh\n") - # . . push args - 68/push "ab cd ef gh\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . write(_test-input-stream, "== data 0x7c10\n") - # . . push args - 68/push "== data 0x7c10\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . write(_test-input-stream, "34\n") - # . . push args - 68/push "34\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # component under test - # . emit-output(_test-input-stream, _test-output-buffered-file, labels) - # . . push args - 52/push-edx - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call emit-output/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # checks - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp -#? # dump output {{{ -#? # . write(2/stderr, "result: ^") -#? # . . push args -#? 68/push "result: ^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # . rewind-stream(_test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? # . . call -#? e8/call rewind-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp -#? # }}} - # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c00", msg) - # . . push args - 68/push "F - test-emit-output-with-padding/0"/imm32 - 68/push "# 0x00007c00"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . check-next-stream-line-equal(_test-output-stream, "ab cd ef gh ", msg) - # . . push args - 68/push "F - test-emit-output-with-padding/1"/imm32 - 68/push "ab cd ef gh "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c04", msg) - # . . push args - 68/push "F - test-emit-output-with-padding/0"/imm32 - 68/push "# 0x00007c04"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . check-next-stream-line-equal(_test-output-stream, "00 00 00 00 ", msg) - # . . push args - 68/push "F - test-emit-output-with-padding/2"/imm32 - 68/push "00 00 00 00 "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . check-next-stream-line-equal(_test-output-stream, "00 00 00 00 ", msg) - # . . push args - 68/push "F - test-emit-output-with-padding/3"/imm32 - 68/push "00 00 00 00 00 00 00 00 "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c10", msg) - # . . push args - 68/push "F - test-emit-output-with-padding/0"/imm32 - 68/push "# 0x00007c10"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . check-next-stream-line-equal(_test-output-stream, "34 ", msg) - # . . push args - 68/push "F - test-emit-output-with-padding/4"/imm32 - 68/push "34 "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . epilogue - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-emit-output-code-label: - # labels turn into PC-relative addresses if opcodes are far jumps or calls - # - # input: - # in: - # == code - # ab cd - # ef gh - # e8 l1/disp32 - # labels: - # - 'l1': 0x7c10 - # - # output: - # ab cd - # ef gh - # e8 07 00 00 00 # 0x7c10 - 0x7c09 = 7 - # - # . prologue - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . clear-stream($_test-output-buffered-file->buffer) - # . . push args - 68/push $_test-output-buffered-file->buffer/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . var labels/edx: (stream byte 8*24) - 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0xc0/imm32 # subtract from esp - 68/push 0xc0/imm32/size - 68/push 0/imm32/read - 68/push 0/imm32/write - 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx - # . var h/ebx: (handle array byte) - 68/push 0/imm32 - 68/push 0/imm32 - 89/copy 3/mod/direct 3/rm32/ebx . . . 4/r32/esp . . # copy esp to ebx - # initialize input - # . write(_test-input-stream, "== code\n") - # . . push args - 68/push "== code\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . write(_test-input-stream, "ab cd\n") - # . . push args - 68/push "ab cd\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . write(_test-input-stream, "ef gh\n") - # . . push args - 68/push "ef gh\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . write(_test-input-stream, "e8 l1/disp32\n") - # . . push args - 68/push "e8 l1/disp32\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . stream-add2(labels, "l1", 0x7c10) - 68/push 0x7c10/imm32/label-address - # . . push handle for "l1" - 53/push-ebx - 68/push "l1"/imm32 - 68/push Heap/imm32 - e8/call copy-array/disp32 - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - ff 6/subop/push 1/mod/*+disp8 3/rm32/ebx . . . . 4/disp8 . # push *(ebx+4) - ff 6/subop/push 0/mod/indirect 3/rm32/ebx . . . . . . # push *ebx - # . . push labels - 52/push-edx - # . . call - e8/call stream-add2/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp - # component under test - # . emit-output(_test-input-stream, _test-output-buffered-file, labels) - # . . push args - 52/push-edx - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call emit-output/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # checks - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp -#? # dump output {{{ -#? # . write(2/stderr, "result: ^") -#? # . . push args -#? 68/push "result: ^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # . rewind-stream(_test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? # . . call -#? e8/call rewind-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp -#? # }}} - # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c00", msg) - # . . push args - 68/push "F - test-emit-output-code-label/0"/imm32 - 68/push "# 0x00007c00"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . check-next-stream-line-equal(_test-output-stream, "ab cd ", msg) - # . . push args - 68/push "F - test-emit-output-code-label/1"/imm32 - 68/push "ab cd "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . check-next-stream-line-equal(_test-output-stream, "ef gh ", msg) - # . . push args - 68/push "F - test-emit-output-code-label/2"/imm32 - 68/push "ef gh "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . check-next-stream-line-equal(_test-output-stream, "e8 07 00 00 00 ", msg) - # . . push args - 68/push "F - test-emit-output-code-label/3"/imm32 - 68/push "e8 07 00 00 00 "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . epilogue - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-emit-output-code-label-absolute: - # labels can also convert to absolute addresses - # - # input: - # in: - # == code - # ab cd - # ef gh - # ij l1/imm32 - # labels: - # - 'l1': 0x1056 - # - # output: - # ab cd - # ef gh - # ij 56 10 00 00 - # - # . prologue - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . clear-stream($_test-output-buffered-file->buffer) - # . . push args - 68/push $_test-output-buffered-file->buffer/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . var labels/edx: (stream byte 8*24) - 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0xc0/imm32 # subtract from esp - 68/push 0xc0/imm32/size - 68/push 0/imm32/read - 68/push 0/imm32/write - 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx - # . var h/ebx: (handle array byte) - 68/push 0/imm32 - 68/push 0/imm32 - 89/copy 3/mod/direct 3/rm32/ebx . . . 4/r32/esp . . # copy esp to ebx - # initialize input - # . write(_test-input-stream, "== code 0x1000\n") - # . . push args - 68/push "== code\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . write(_test-input-stream, "ab cd\n") - # . . push args - 68/push "ab cd\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . write(_test-input-stream, "ef gh\n") - # . . push args - 68/push "ef gh\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . write(_test-input-stream, "ij l1/imm32\n") - # . . push args - 68/push "ij l1/imm32\n"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . stream-add2(labels, "l1", 0x1056) - 68/push 0x1056/imm32/label-address - # . . push handle for "l1" - 53/push-ebx - 68/push "l1"/imm32 - 68/push Heap/imm32 - e8/call copy-array/disp32 - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - ff 6/subop/push 1/mod/*+disp8 3/rm32/ebx . . . . 4/disp8 . # push *(ebx+4) - ff 6/subop/push 0/mod/indirect 3/rm32/ebx . . . . . . # push *ebx - # . . push labels - 52/push-edx - # . . call - e8/call stream-add2/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp - # component under test - # . emit-output(_test-input-stream, _test-output-buffered-file, labels) - # . . push args - 52/push-edx - 68/push _test-output-buffered-file/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call emit-output/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # checks - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp -#? # dump output {{{ -#? # . write(2/stderr, "result: ^") -#? # . . push args -#? 68/push "result: ^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # . rewind-stream(_test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? # . . call -#? e8/call rewind-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp -#? # }}} - # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c00", msg) - # . . push args - 68/push "F - test-emit-output-code-label-absolute/0"/imm32 - 68/push "# 0x00007c00"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . check-next-stream-line-equal(_test-output-stream, "ab cd ", msg) - # . . push args - 68/push "F - test-emit-output-code-label-absolute/1"/imm32 - 68/push "ab cd "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . check-next-stream-line-equal(_test-output-stream, "ef gh ", msg) - # . . push args - 68/push "F - test-emit-output-code-label-absolute/2"/imm32 - 68/push "ef gh "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . check-next-stream-line-equal(_test-output-stream, "ij f9 ff ff ff ", msg) - # . . push args - 68/push "F - test-emit-output-code-label-absolute/3"/imm32 - 68/push "ij 56 10 00 00 "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . epilogue - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -# reads line to make some checks -# don't assume the read state of line after calling this function -far-jump-or-call?: # line: (addr stream byte) -> result/edi: boolean - # . prologue - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 50/push-eax - 51/push-ecx - 52/push-edx - 53/push-ebx - # ecx = line - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 8/disp8 . # copy *(ebp+8) to ecx - # var word-slice/edx: slice - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx - # var datum-slice/ebx: slice - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 3/rm32/ebx . . . 4/r32/esp . . # copy esp to ebx - # result = false - bf/copy-to-edi 0/imm32/false -$far-jump-or-call?:check-first-word: - # next-word(line, word-slice) - # . . push args - 52/push-edx - 51/push-ecx - # . . call - e8/call next-word/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # if (slice-empty?(word-slice)) return false - # . eax = slice-empty?(word-slice) - # . . push args - 52/push-edx - # . . call - e8/call slice-empty?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . if (eax != 0) return - 3d/compare-eax-and 0/imm32/false - 0f 85/jump-if-!= $far-jump-or-call?:end/disp32 - # datum = next-token-from-slice(word-slice->start, word-slice->end, "/") - # . . push args - 53/push-ebx - 68/push 0x2f/imm32/slash - ff 6/subop/push 1/mod/*+disp8 2/rm32/edx . . . . 4/disp8 . # push *(edx+4) - ff 6/subop/push 0/mod/indirect 2/rm32/edx . . . . . . # push *edx - # . . call - e8/call next-token-from-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp - # if (datum-slice == "e8") return true - # . eax = slice-equal?(datum-slice, "e8") - # . . push args - 68/push "e8"/imm32 - 53/push-ebx - # . . call - e8/call slice-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . if (eax != false) return true - 3d/compare-eax-and 0/imm32/false - 75/jump-if-!= $far-jump-or-call?:return-true/disp8 - # if (datum-slice == "e9") return true - # . eax = slice-equal?(datum-slice, "e9") - # . . push args - 68/push "e9"/imm32 - 53/push-ebx - # . . call - e8/call slice-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . if (eax != false) return true - 3d/compare-eax-and 0/imm32/false - 75/jump-if-!= $far-jump-or-call?:return-true/disp8 - # if (datum-slice != "0f") return false - # . eax = slice-equal?(datum-slice, "0f") - # . . push args - 68/push "0f"/imm32 - 53/push-ebx - # . . call - e8/call slice-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . if (eax == false) return - 3d/compare-eax-and 0/imm32/false - 74/jump-if-= $far-jump-or-call?:end/disp8 -$far-jump-or-call?:check-second-word: - # next-word(line, word-slice) - # . . push args - 52/push-edx - 51/push-ecx - # . . call - e8/call next-word/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # if (slice-empty?(word-slice)) return false - # . eax = slice-empty?(word-slice) - # . . push args - 52/push-edx - # . . call - e8/call slice-empty?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . if (eax != 0) return - 3d/compare-eax-and 0/imm32/false - 75/jump-if-!= $far-jump-or-call?:end/disp8 - # if datum of word-slice does not start with "8", return false - # . start/eax = word-slice->start - 8b/copy 0/mod/indirect 2/rm32/edx . . . 0/r32/eax . . # copy *edx to eax - # . c/eax = *start - 8a/copy-byte 0/mod/indirect 0/rm32/eax . . . 0/r32/AL . . # copy byte at *eax to AL - 81 4/subop/and 3/mod/direct 0/rm32/eax . . . . . 0xff/imm32 # bitwise and of eax - # . if (eax != '8') return - 3d/compare-eax-and 0x38/imm32/8 - 75/jump-if-!= $far-jump-or-call?:end/disp8 - # otherwise return true -$far-jump-or-call?:return-true: - bf/copy-to-edi 1/imm32/true -$far-jump-or-call?:end: - # . reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp - # . restore registers - 5b/pop-to-ebx - 5a/pop-to-edx - 59/pop-to-ecx - 58/pop-to-eax - # . epilogue - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -# - some helpers for tests - -stream-add2: # in: (addr stream byte), key: handle, val: int - # . prologue - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 50/push-eax - 51/push-ecx - 52/push-edx - 56/push-esi - # esi = in - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi - # curr/eax = &in->data[in->write] - # . eax = in->write - 8b/copy 0/mod/indirect 6/rm32/esi . . . 0/r32/eax . . # copy *esi to eax - # . eax = esi+eax+12 - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/esi 0/index/eax . 0/r32/eax 0xc/disp8 . # copy esi+eax+12 to eax - # max/edx = &in->data[in->size] - # . edx = in->size - 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 2/r32/edx 8/disp8 . # copy *(esi+8) to edx - # . edx = esi+edx+12 - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/esi 2/index/edx . 2/r32/edx 0xc/disp8 . # copy esi+edx+12 to edx - # if (curr >= max) abort - 39/compare 3/mod/direct 0/rm32/eax . . . 2/r32/edx . . # compare eax with edx - 73/jump-if-addr>= $stream-add2:abort/disp8 - # *curr = key->alloc-id - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . 1/r32/ecx 0xc/disp8 . # copy *(ebp+12) to ecx - 89/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy ecx to *eax # curr += 4 - 05/add-to-eax 4/imm32 - # if (curr >= max) abort - 39/compare 3/mod/direct 0/rm32/eax . . . 2/r32/edx . . # compare eax with edx - 73/jump-if-addr>= $stream-add2:abort/disp8 - # *curr = key->payload - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . 1/r32/ecx 0x10/disp8 . # copy *(ebp+16) to ecx - 89/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy ecx to *eax - # curr += 4 - 05/add-to-eax 4/imm32 - # if (curr >= max) abort - 39/compare 3/mod/direct 0/rm32/eax . . . 2/r32/edx . . # compare eax with edx - 73/jump-if-addr>= $stream-add2:abort/disp8 - # *curr = val - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . 1/r32/ecx 0x14/disp8 . # copy *(ebp+20) to ecx - 89/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy ecx to *eax - # in->write += 0xc - 81 0/subop/add 0/mod/indirect 6/rm32/esi . . . . . 0xc/imm32 # add to *esi -$stream-add2:end: + 81 0/subop/add 3/mod/direct 1/rm32/ecx . . . . . 4/imm32 # add to ecx + # write-buffered(out, '\n') + # . . push args + 68/push Newline/imm32 + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) + # . . call + e8/call write-buffered/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # loop + e9/jump $emit-labels:loop/disp32 +$emit-labels:end: # . restore registers 5e/pop-to-esi 5a/pop-to-edx @@ -2706,19 +1016,7 @@ $stream-add2:end: 5d/pop-to-ebp c3/return -$stream-add2:abort: - # . _write(2/stderr, error) - # . . push args - 68/push "overflow in stream-add2\n"/imm32 - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . syscall(exit, 1) - bb/copy-to-ebx 1/imm32 - e8/call syscall_exit/disp32 - # never gets here +# - some helpers for tests # some variants of 'trace' that take multiple arguments in different combinations of types: # n: int @@ -2843,543 +1141,4 @@ test-trace-slsns: 5d/pop-to-ebp c3/return -num-bytes: # line: (addr stream byte) -> eax: int - # pseudocode: - # result = 0 - # while true - # var word-slice = next-word(line) - # if slice-empty?(word-slice) # end of line - # break - # if slice-starts-with?(word-slice, "#") # comment - # break - # if label?(word-slice) # no need for label declarations anymore - # break - # if slice-equal?(word-slice, "==") - # break # no need for segment header lines - # result += compute-width(word-slice) - # return result - # - # . prologue - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 51/push-ecx - 52/push-edx - 53/push-ebx - # var result/eax = 0 - 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax - # var word-slice/ecx: slice - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx -#? # dump line {{{ -#? # . write(2/stderr, "LL: ") -#? # . . push args -#? 68/push "LL: "/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # write-stream(2/stderr, line) -#? # . . push args -#? ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # }}} - # . rewind-stream(line) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) - # . . call - e8/call rewind-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp -$num-bytes:loop: - # next-word(line, word-slice) - # . . push args - 51/push-ecx - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) - # . . call - e8/call next-word/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # dump word-slice {{{ -#? # . write(2/stderr, "AA: ") -#? # . . push args -#? 68/push "AA: "/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # . clear-stream($Stderr->buffer) -#? # . . push args -#? 68/push $Stderr->buffer/imm32 -#? # . . call -#? e8/call clear-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp -#? # . write-slice-buffered(Stderr, word-slice) -#? # . . push args -#? 51/push-ecx -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call write-slice-buffered/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # . flush(Stderr) -#? # . . push args -#? 68/push Stderr/imm32 -#? # . . call -#? e8/call flush/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # }}} -$num-bytes:check0: - # if (slice-empty?(word-slice)) break - # . save result - 50/push-eax - # . eax = slice-empty?(word-slice) - # . . push args - 51/push-ecx - # . . call - e8/call slice-empty?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . if (eax != false) break - 3d/compare-eax-and 0/imm32/false - # . restore result now that ZF is set - 58/pop-to-eax - 75/jump-if-!= $num-bytes:end/disp8 -$num-bytes:check-for-comment: - # if (slice-starts-with?(word-slice, "#")) break - # . start/edx = word-slice->start - 8b/copy 0/mod/indirect 1/rm32/ecx . . . 2/r32/edx . . # copy *ecx to edx - # . c/ebx = *start - 31/xor 3/mod/direct 3/rm32/ebx . . . 3/r32/ebx . . # clear ebx - 8a/copy-byte 0/mod/indirect 2/rm32/edx . . . 3/r32/BL . . # copy byte at *edx to BL - # . if (ebx == '#') break - 81 7/subop/compare 3/mod/direct 3/rm32/ebx . . . . . 0x23/imm32/hash # compare ebx - 74/jump-if-= $num-bytes:end/disp8 -$num-bytes:check-for-label: - # if (slice-ends-with?(word-slice, ":")) break - # . end/edx = word-slice->end - 8b/copy 1/mod/*+disp8 1/rm32/ecx . . . 2/r32/edx 4/disp8 . # copy *(ecx+4) to edx - # . c/ebx = *(end-1) - 31/xor 3/mod/direct 3/rm32/ebx . . . 3/r32/ebx . . # clear ebx - 8a/copy-byte 1/mod/*+disp8 2/rm32/edx . . . 3/r32/BL -1/disp8 . # copy byte at *ecx to BL - # . if (ebx == ':') break - 81 7/subop/compare 3/mod/direct 3/rm32/ebx . . . . . 0x3a/imm32/colon # compare ebx - 74/jump-if-= $num-bytes:end/disp8 -$num-bytes:check-for-segment-header: - # if (slice-equal?(word-slice, "==")) break - # . push result - 50/push-eax - # . eax = slice-equal?(word-slice, "==") - # . . push args - 68/push "=="/imm32 - 51/push-ecx - # . . call - e8/call slice-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . if (eax != false) break - 3d/compare-eax-and 0/imm32/false - # . restore result now that ZF is set - 58/pop-to-eax - 75/jump-if-!= $num-bytes:end/disp8 -$num-bytes:loop-body: - # result += compute-width-of-slice(word-slice) - # . copy result to edx - 89/copy 3/mod/direct 2/rm32/edx . . . 0/r32/eax . . # copy eax to edx - # . eax = compute-width-of-slice(word-slice) - # . . push args - 51/push-ecx - # . . call - e8/call compute-width-of-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . eax += result - 01/add 3/mod/direct 0/rm32/eax . . . 2/r32/edx . . # add edx to eax - e9/jump $num-bytes:loop/disp32 -$num-bytes:end: - # . rewind-stream(line) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) - # . . call - e8/call rewind-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . restore registers - 5b/pop-to-ebx - 5a/pop-to-edx - 59/pop-to-ecx - # . epilogue - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-num-bytes-handles-empty-string: - # if a line starts with '#', return 0 - # . prologue - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # no contents in input - # eax = num-bytes(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call num-bytes/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # check-ints-equal(eax, 0, msg) - # . . push args - 68/push "F - test-num-bytes-handles-empty-string"/imm32 - 68/push 0/imm32/true - 50/push-eax - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . epilogue - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-num-bytes-ignores-comments: - # if a line starts with '#', return 0 - # . prologue - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # initialize input - # . write(_test-input-stream, "# abcd") - # . . push args - 68/push "# abcd"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # eax = num-bytes(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call num-bytes/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # check-ints-equal(eax, 0, msg) - # . . push args - 68/push "F - test-num-bytes-ignores-comments"/imm32 - 68/push 0/imm32/true - 50/push-eax - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . epilogue - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-num-bytes-ignores-labels: - # if the first word ends with ':', return 0 - # . prologue - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # initialize input - # . write(_test-input-stream, "ab: # cd") - # . . push args - 68/push "ab: # cd"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # eax = num-bytes(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call num-bytes/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # check-ints-equal(eax, 0, msg) - # . . push args - 68/push "F - test-num-bytes-ignores-labels"/imm32 - 68/push 0/imm32/true - 50/push-eax - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . epilogue - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-num-bytes-ignores-segment-headers: - # if the first word is '==', return 0 - # . prologue - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # initialize input - # . write(_test-input-stream, "== ab cd") - # . . push args - 68/push "== ab cd"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # eax = num-bytes(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call num-bytes/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # check-ints-equal(eax, 0, msg) - # . . push args - 68/push "F - test-num-bytes-ignores-segment-headers"/imm32 - 68/push 0/imm32/true - 50/push-eax - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . epilogue - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-num-bytes-counts-words-by-default: - # without metadata, count words - # . prologue - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # initialize input - # . write(_test-input-stream, "ab cd ef") - # . . push args - 68/push "ab cd ef"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # eax = num-bytes(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call num-bytes/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # check-ints-equal(eax, 3, msg) - # . . push args - 68/push "F - test-num-bytes-counts-words-by-default"/imm32 - 68/push 3/imm32/true - 50/push-eax - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . epilogue - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-num-bytes-ignores-trailing-comment: - # trailing comments appropriately ignored - # . prologue - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # initialize input - # . write(_test-input-stream, "ab cd # ef") - # . . push args - 68/push "ab cd # ef"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # eax = num-bytes(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call num-bytes/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # check-ints-equal(eax, 2, msg) - # . . push args - 68/push "F - test-num-bytes-ignores-trailing-comment"/imm32 - 68/push 2/imm32/true - 50/push-eax - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . epilogue - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-num-bytes-handles-imm32: - # if a word has the /imm32 metadata, count it as 4 bytes - # . prologue - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # initialize input - # . write(_test-input-stream, "ab cd/imm32 ef") - # . . push args - 68/push "ab cd/imm32 ef"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # eax = num-bytes(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call num-bytes/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # check-ints-equal(eax, 6, msg) - # . . push args - 68/push "F - test-num-bytes-handles-imm32"/imm32 - 68/push 6/imm32/true - 50/push-eax - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . epilogue - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - # . . vim:nowrap:textwidth=0 diff --git a/linux/test_apps b/linux/test_apps index 9d8e5683..1719b5fd 100755 --- a/linux/test_apps +++ b/linux/test_apps @@ -216,7 +216,7 @@ bootstrap/bootstrap translate [01]*.subx random.subx -o random # Phases of the self-hosted SubX translator. -for phase in hex survey_elf survey_baremetal pack assort dquotes tests +for phase in hex survey_elf survey_baremetal labels_baremetal pack assort dquotes tests do echo $phase bootstrap/bootstrap translate [01]*.subx subx-params.subx $phase.subx -o $phase @@ -314,7 +314,7 @@ done # Phases of the self-hosted SubX translator. -for app in hex survey_elf survey_baremetal pack assort dquotes tests +for app in hex survey_elf survey_baremetal labels_baremetal pack assort dquotes tests do echo $app ./translate_subx [01]*.subx subx-params.subx $app.subx diff --git a/linux/test_apps_emulated b/linux/test_apps_emulated index cb74c1ae..f12ada06 100755 --- a/linux/test_apps_emulated +++ b/linux/test_apps_emulated @@ -137,7 +137,7 @@ bootstrap/bootstrap translate [01]*.subx random.subx -o random # Phases of the self-hosted SubX translator. -for phase in hex survey_elf survey_baremetal pack assort dquotes tests +for phase in hex survey_elf survey_baremetal labels_baremetal pack assort dquotes tests do echo $phase bootstrap/bootstrap translate [01]*.subx subx-params.subx $phase.subx -o $phase @@ -211,7 +211,7 @@ done # Phases of the self-hosted SubX translator. -for app in hex survey_elf survey_baremetal pack assort dquotes tests +for app in hex survey_elf survey_baremetal labels_baremetal pack assort dquotes tests do echo $app ./translate_subx_emulated [01]*.subx subx-params.subx $app.subx diff --git a/translate_subx b/translate_subx index f4c461f6..70400f45 100755 --- a/translate_subx +++ b/translate_subx @@ -12,23 +12,24 @@ set -e -cat $* |linux/braces > a.braces +cat $* |linux/braces > a.braces -cat a.braces |linux/calls > a.calls +cat a.braces |linux/calls > a.calls -cat a.calls |linux/sigils > a.sigils +cat a.calls |linux/sigils > a.sigils -cat a.sigils |linux/tests > a.tests +cat a.sigils |linux/tests > a.tests # no assort since baremetal SubX doesn't have segments yet -cat a.tests |linux/dquotes > a.dquotes +cat a.tests |linux/dquotes > a.dquotes -cat a.dquotes |linux/pack > a.pack +cat a.dquotes |linux/pack > a.pack -cat a.pack |linux/survey_baremetal > a.survey +cat a.pack |linux/survey_baremetal > labels +cat a.pack |linux/labels_baremetal labels > a.survey -cat a.survey |linux/hex > a.bin +cat a.survey |linux/hex > a.bin # Create code.img containing a.bin dd if=/dev/zero of=code.img count=20160 # 512-byte sectors, so 10MB diff --git a/translate_subx_emulated b/translate_subx_emulated index 85b56436..26dd5364 100755 --- a/translate_subx_emulated +++ b/translate_subx_emulated @@ -3,27 +3,37 @@ # # This script uses emulation, so it does not require x86 or Linux. However it # is slow. +# +# A couple of gotchas: +# * Many phases here have no error-checking. Perhaps I should use a +# version of translate_subx_debug for baremetal. +# * Don't pass in numbered .subx files without translated .mu files. Our test +# harness is in test.mu, and only Mu programs can run tests in baremetal. +# +# The top level is in general not as rigorous about avoiding dependency cycles +# as the lower-level tools in linux/ set -e set -v -cat $* |linux/bootstrap/bootstrap run linux/braces > a.braces +cat $* |linux/bootstrap/bootstrap run linux/braces > a.braces -cat a.braces |linux/bootstrap/bootstrap run linux/calls > a.calls +cat a.braces |linux/bootstrap/bootstrap run linux/calls > a.calls -cat a.calls |linux/bootstrap/bootstrap run linux/sigils > a.sigils +cat a.calls |linux/bootstrap/bootstrap run linux/sigils > a.sigils -cat a.sigils |linux/bootstrap/bootstrap run linux/tests > a.tests +cat a.sigils |linux/bootstrap/bootstrap run linux/tests > a.tests # no assort since baremetal SubX doesn't have segments yet -cat a.tests |linux/bootstrap/bootstrap run linux/dquotes > a.dquotes +cat a.tests |linux/bootstrap/bootstrap run linux/dquotes > a.dquotes -cat a.dquotes |linux/bootstrap/bootstrap run linux/pack > a.pack +cat a.dquotes |linux/bootstrap/bootstrap run linux/pack > a.pack -cat a.pack |linux/bootstrap/bootstrap run linux/survey_baremetal > a.survey +cat a.pack |linux/bootstrap/bootstrap linux/survey_baremetal > labels +cat a.pack |linux/bootstrap/bootstrap linux/labels_baremetal labels > a.survey -cat a.survey |linux/bootstrap/bootstrap run linux/hex > a.bin +cat a.survey |linux/bootstrap/bootstrap run linux/hex > a.bin # Create code.img containing a.bin dd if=/dev/zero of=code.img count=20160 # 512-byte sectors, so 10MB