diff options
author | Kartik Agaram <vc@akkartik.com> | 2018-11-24 23:20:54 -0800 |
---|---|---|
committer | Kartik Agaram <vc@akkartik.com> | 2018-11-24 23:26:14 -0800 |
commit | 33fdc60b168a04ca26b4924ecde2adb7c8059a13 (patch) | |
tree | 22238a4ef7b598a51aed1426ca89db00e6ab43f5 /subx | |
parent | 31ff94214bb9ae2f60b10bb9fa5ea468b141f752 (diff) | |
download | mu-33fdc60b168a04ca26b4924ecde2adb7c8059a13.tar.gz |
4775
Start with an exactly corresponding version to Crenshaw 2-1: single-digit numbers. The only change: we assume the number is in hex. The next version now supports multi-digit hex numbers.
Diffstat (limited to 'subx')
-rwxr-xr-x | subx/apps/crenshaw2-1 | bin | 7048 -> 6489 bytes | |||
-rw-r--r-- | subx/apps/crenshaw2-1.subx | 224 | ||||
-rwxr-xr-x | subx/apps/crenshaw2-1b | bin | 0 -> 7048 bytes | |||
-rw-r--r-- | subx/apps/crenshaw2-1b.subx | 836 | ||||
-rwxr-xr-x | subx/test_apps | 10 |
5 files changed, 857 insertions, 213 deletions
diff --git a/subx/apps/crenshaw2-1 b/subx/apps/crenshaw2-1 index 1396ede0..16829e6a 100755 --- a/subx/apps/crenshaw2-1 +++ b/subx/apps/crenshaw2-1 Binary files differdiff --git a/subx/apps/crenshaw2-1.subx b/subx/apps/crenshaw2-1.subx index 3abf7365..62e2cb89 100644 --- a/subx/apps/crenshaw2-1.subx +++ b/subx/apps/crenshaw2-1.subx @@ -186,23 +186,18 @@ compile: # in : fd or (address stream), out : fd or (address stream), err : fd 5d/pop-to-EBP c3/return -# Read a sequence of digits into 'out'. Abort if there are none, or if there -# are too many to fit in 'out'. -# Input comes from the global variable 'Look' (first byte) and the argument -# 'in' (rest). +# Read a single digit into 'out'. Abort if there are none, or if there is no space in 'out'. +# Input comes from the global variable 'Look', and we leave the next byte from +# 'in' into it on exit. get-num: # in : (address buffered-file), out : (address stream), err : fd or (address stream), ed : (address exit-descriptor) -> <void> # pseudocode: # if !is-digit?(Look) expected(ed, err, "integer") - # do - # if out.write >= out.length - # write(err, "Error: too many digits in number\n") - # stop(ed, 1) - # out.data[out.write] = LSB(Look) - # ++out.write - # Look = get-char(in) - # while is-digit?(Look) - # This is complicated because I don't want to hard-code the error strategy in - # a general helper like write-byte. Maybe I should just create a local helper. + # if out.write >= out.length + # write(err, "Error: too many digits in number\n") + # stop(ed, 1) + # out.data[out.write] = LSB(Look) + # ++out.write + # Look = get-char(in) # # within the loop we'll try to keep things in registers: # ESI : in @@ -254,10 +249,9 @@ $get-num:main: 8b/copy 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # copy *EDI to ECX # EDX = out->length 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 2/r32/EDX 8/disp8 . # copy *(EDI+8) to EDX -$get-num:loop: # if out->write >= out->length error 3b/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare EDX with ECX - 7d/jump-if-lesser $get-num:loop-stage2/disp8 + 7d/jump-if-lesser $get-num:stage2/disp8 # error(ed, err, "get-num: too many digits in number") # TODO: show full number # push args 68/push "get-num: too many digits in number"/imm32 @@ -267,7 +261,7 @@ $get-num:loop: e8/call error/disp32 # never returns # discard args 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -$get-num:loop-stage2: +$get-num:stage2: # out->data[out->write] = LSB(Look) 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 1/index/ECX . 3/r32/EBX 0xc/disp8 . # copy EDI+ECX+12 to EBX 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Look/disp32 . # copy *Look to EAX @@ -281,16 +275,6 @@ $get-num:loop-stage2: e8/call get-char/disp32 # discard args 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # EAX = is-digit?(Look) - # push args - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Look/disp32 . # push *Look - # call - e8/call is-digit?/disp32 - # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - # if EAX loop - 3d/compare-EAX 0/imm32 - 0f 85/jump-if-not-equal $get-num:loop/disp16 $get-num:loop-end: # persist necessary variables from registers 89/copy 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # copy ECX to *EDI @@ -486,192 +470,6 @@ test-get-num-aborts-on-non-digit-in-Look: 5d/pop-to-EBP c3/return -test-get-num-reads-multiple-digits: - ## check that get-num returns all initial digits until it encounters a non-digit - # This test uses exit-descriptors. Use EBP for setting up local variables. - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - ## clear all streams - # clear-stream(_test-stream) - # push args - 68/push _test-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-buffered-file+4) - # push args - b8/copy-to-EAX _test-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # 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-error-stream) - # push args - 68/push _test-error-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 'in' - # write(_test-stream, "3456 x") - # push args - 68/push "3456"/imm32 - 68/push _test-stream/imm32 - # call - e8/call write/disp32 - # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - ## initialize exit-descriptor 'ed' - # allocate on stack - # var ed/EAX : (address exit-descriptor) - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX . . # copy ESP to EAX - # size the exit-descriptor for the call to get-num below - # tailor-exit-descriptor(ed, 16) - # push args - 68/push 0x10/imm32/nbytes-of-args-for-get-num - 50/push-EAX/ed - # call - e8/call tailor-exit-descriptor/disp32 - # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - ## prime the pump - # get-char(_test-buffered-file) - # push args - 68/push _test-buffered-file/imm32 - # call - e8/call get-char/disp32 - # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - ## get-num(in, out, err, ed) - # push args - 50/push-EAX/ed - 68/push _test-error-stream/imm32 - 68/push _test-output-stream/imm32 - 68/push _test-buffered-file/imm32 - # call - e8/call get-num/disp32 - ## registers except ESP may be clobbered at this point - # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - # check-ints-equal(*_test-output-stream.data, '3456') - # push args - 68/push "F - test-get-num-reads-multiple-digits"/imm32 - 68/push 0x36353433/imm32 - b8/copy-to-EAX _test-output-stream/imm32 - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) - # call - e8/call check-ints-equal/disp32 - # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 5d/pop-to-EBP - c3/return - -test-get-num-reads-multiple-digits-followed-by-nondigit: - ## check that get-num returns all initial digits until it encounters a non-digit - # This test uses exit-descriptors. Use EBP for setting up local variables. - 55/push-EBP - 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - ## clear all streams - # clear-stream(_test-stream) - # push args - 68/push _test-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-buffered-file+4) - # push args - b8/copy-to-EAX _test-buffered-file/imm32 - 05/add-to-EAX 4/imm32 - 50/push-EAX - # 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-error-stream) - # push args - 68/push _test-error-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 'in' - # write(_test-stream, "3456 x") - # push args - 68/push "3456 x"/imm32 - 68/push _test-stream/imm32 - # call - e8/call write/disp32 - # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - ## initialize exit-descriptor 'ed' - # allocate on stack - # var ed/EAX : (address exit-descriptor) - 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP - 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX . . # copy ESP to EAX - # size the exit-descriptor for the call to get-num below - # tailor-exit-descriptor(ed, 16) - # push args - 68/push 0x10/imm32/nbytes-of-args-for-get-num - 50/push-EAX/ed - # call - e8/call tailor-exit-descriptor/disp32 - # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - ## prime the pump - # get-char(_test-buffered-file) - # push args - 68/push _test-buffered-file/imm32 - # call - e8/call get-char/disp32 - # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - ## get-num(in, out, err, ed) - # push args - 50/push-EAX/ed - 68/push _test-error-stream/imm32 - 68/push _test-output-stream/imm32 - 68/push _test-buffered-file/imm32 - # call - e8/call get-num/disp32 - ## registers except ESP may be clobbered at this point - # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - # check-ints-equal(*_test-output-stream.data, '3456') - # push args - 68/push "F - test-get-num-reads-multiple-digits-followed-by-nondigit"/imm32 - 68/push 0x36353433/imm32 - b8/copy-to-EAX _test-output-stream/imm32 - ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) - # call - e8/call check-ints-equal/disp32 - # discard args - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - # reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 5d/pop-to-EBP - c3/return - ## helpers # write(f, "Error: "+s+" expected\n") then stop(ed, 1) diff --git a/subx/apps/crenshaw2-1b b/subx/apps/crenshaw2-1b new file mode 100755 index 00000000..1396ede0 --- /dev/null +++ b/subx/apps/crenshaw2-1b Binary files differdiff --git a/subx/apps/crenshaw2-1b.subx b/subx/apps/crenshaw2-1b.subx new file mode 100644 index 00000000..ef6996ee --- /dev/null +++ b/subx/apps/crenshaw2-1b.subx @@ -0,0 +1,836 @@ +## Port of https://github.com/akkartik/crenshaw/blob/master/tutor2.1.pas +# Corresponds to the section "single digits" in https://compilers.iecc.com/crenshaw/tutor2.txt +# except that we support numbers of multiple digits. +# +# To run (from the subx directory): +# $ ./subx translate *.subx apps/crenshaw2-1.subx -o crenshaw2-1 +# $ echo '1a' |./subx run apps/crenshaw2-1 +# Expected output (not working yet): +# # syscall(exit, 1a) +# bb/copy-to-EBX 3/imm32 +# b8/copy-to-EAX 1/imm32/exit +# cd/syscall 0x80/imm8 +# +# To run the generated output: +# $ echo '1a' |./subx run apps/crenshaw2-1 > z1.subx +# $ ./subx translate z1.subx -o z1 +# $ ./subx run z1 +# $ echo $? +# 26 # 0x1a in decimal +# +# Stdin must contain just a single hex digit. Other input will print an error: +# $ echo 'xyz' |./subx run apps/crenshaw2-1 +# Error: integer expected +# +# Names in this file sometimes follow Crenshaw's original rather than my usual +# naming conventions. + +== code +# instruction effective address operand 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 + +# main: run tests if necessary, call 'compile' if not + # prolog + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # if (argc > 1) + 81 7/subop/compare 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0/disp8 1/imm32 # compare *EBP + 7e/jump-if-lesser-or-equal $run-main/disp8 + # and if (argv[1] == "test") + # push args + 68/push "test"/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/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 + # check result + 3d/compare-EAX 1/imm32 + 75/jump-if-not-equal $run-main/disp8 + # then return run-tests() + e8/call run-tests/disp32 +#? e8/call test-get-num-reads-multiple-digits/disp32 + 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX + eb/jump $main:end/disp8 +$run-main: + # allocate space for an exit-descriptor + # var ed/EAX : (address exit-descriptor) + 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP + 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX . . # copy ESP to EAX + # clear ed->target (so we really exit) + c7/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX + # compile(Stdin, 1/stdout, 2/stderr, ed) + # push args + 50/push-EAX/ed + 68/push 2/imm32/stderr + 68/push 1/imm32/stdout + 68/push Stdin/imm32 + # call + e8/call compile/disp32 + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP + # syscall(exit, 0) + bb/copy-to-EBX 0/imm32 +$main:end: + b8/copy-to-EAX 1/imm32/exit + cd/syscall 0x80/imm8 + +# the main entry point +compile: # in : fd or (address stream), out : fd or (address stream), err : fd or (address stream), ed : (address exit-descriptor) -> <void> + # prolog + 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 + # Look = get-char(in) + # push args + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 . # push *(EBP+8) + # call + e8/call get-char/disp32 + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # var num/ECX : (address stream) on the stack + # Numbers can be 32 bits or 8 hex bytes long. One of them will be in 'Look', so we need space for 7 bytes. + # We won't add more, so that we can get overflow-handling for free. + # Add 12 bytes for 'read', 'write' and 'length' fields, for a total of 19 bytes, or 0x13 in hex. + 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0x13/imm32 # subtract from ESP + 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 1/r32/ECX . . # copy ESP to ECX + # num->length = 7 + c7/copy 1/mod/*+disp8 1/rm32/ECX . . . . 8/disp8 7/imm32 # copy to *(ECX+8) + # clear-stream(num) + # 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 + # get-num(in, num, err, ed) + # push args + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x14/disp8 . # push *(EBP+20) + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x10/disp8 . # push *(EBP+16) + 51/push-ECX/num + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 . # push *(EBP+8) + # call + e8/call get-num/disp32 + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP + # EAX = write(_test-stream, "Ab") + # push args + 68/push "Ab"/imm32 + 68/push _test-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 = write(out, "bb/copy-to-EBX ") + # push args + 68/push "bb/copy-to-EBX "/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + # call + e8/call write/disp32 + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # write-stream(out, num) + # push args + 51/push-ECX/num + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + # call + e8/call write-stream/disp32 + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # write(out, Newline) + # push args + 68/push Newline/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + # call + e8/call write/disp32 + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # EAX = write(out, "b8/copy-to-EAX 1/imm32/exit") + # push args + 68/push "b8/copy-to-EAX 1/imm32/exit"/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + # call + e8/call write/disp32 + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # EAX = write(out, Newline) + # push args + 68/push Newline/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + # call + e8/call write/disp32 + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # EAX = write(out, "cd/syscall 0x80/imm8") + # push args + 68/push "cd/syscall 0x80/imm8"/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + # call + e8/call write/disp32 + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # EAX = write(out, Newline) + # push args + 68/push Newline/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + # call + e8/call write/disp32 + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # restore registers + 59/pop-to-ECX + 58/pop-to-EAX + # epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return + +# Read a sequence of digits into 'out'. Abort if there are none, or if there is +# no space in 'out'. +# Input comes from the global variable 'Look' (first byte) and the argument +# 'in' (rest). We leave the next byte from 'in' into 'Look' on exit. +get-num: # in : (address buffered-file), out : (address stream), err : fd or (address stream), ed : (address exit-descriptor) -> <void> + # pseudocode: + # if !is-digit?(Look) expected(ed, err, "integer") + # do + # if out.write >= out.length + # write(err, "Error: too many digits in number\n") + # stop(ed, 1) + # out.data[out.write] = LSB(Look) + # ++out.write + # Look = get-char(in) + # while is-digit?(Look) + # This is complicated because I don't want to hard-code the error strategy in + # a general helper like write-byte. Maybe I should just create a local helper. + # + # within the loop we'll try to keep things in registers: + # ESI : in + # EDI : out + # EAX : temp + # ECX : out->write + # EDX : out->length + # EBX : temp2 + # We can't allocate Look to a register because it gets written implicitly in + # get-char in each iteration of the loop. (Thereby demonstrating that it's + # not the right interface for us. But we'll keep it just to follow Crenshaw.) + # + # prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # EAX = is-digit?(Look) + # push args + ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Look/disp32 . # push *Look + # call + e8/call is-digit?/disp32 + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # if EAX == 0 error + 3d/compare-EAX 0/imm32 + 75/jump-if-not-equal $get-num:main/disp8 + # expected(ed, err, "integer") + # push args + 68/push "integer"/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x10/disp8 . # push *(EBP+16) + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x14/disp8 . # push *(EBP+20) + # call + e8/call expected/disp32 # never returns + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +$get-num:main: + # save registers + 50/push-EAX + 51/push-ECX + 52/push-EDX + 53/push-EBX + 56/push-ESI + 57/push-EDI + # read necessary variables to registers + # ESI = in + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI + # EDI = out + 8b/copy 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI + # ECX = out->write + 8b/copy 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # copy *EDI to ECX + # EDX = out->length + 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 2/r32/EDX 8/disp8 . # copy *(EDI+8) to EDX +$get-num:loop: + # if out->write >= out->length error + 3b/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare EDX with ECX + 7d/jump-if-lesser $get-num:loop-stage2/disp8 + # error(ed, err, "get-num: too many digits in number") # TODO: show full number + # push args + 68/push "get-num: too many digits in number"/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x10/disp8 . # push *(EBP+16) + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x14/disp8 . # push *(EBP+20) + # call + e8/call error/disp32 # never returns + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +$get-num:loop-stage2: + # out->data[out->write] = LSB(Look) + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 1/index/ECX . 3/r32/EBX 0xc/disp8 . # copy EDI+ECX+12 to EBX + 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Look/disp32 . # copy *Look to EAX + 88/copy-byte 0/mod/indirect 3/rm32/EBX . . . 0/r32/AL . . # copy byte at AL to *EBX + # ++out->write + 41/increment-ECX + # Look = get-char(in) + # push args + 56/push-ESI + # call + e8/call get-char/disp32 + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # EAX = is-digit?(Look) + # push args + ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Look/disp32 . # push *Look + # call + e8/call is-digit?/disp32 + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # if EAX loop + 3d/compare-EAX 0/imm32 + 0f 85/jump-if-not-equal $get-num:loop/disp16 +$get-num:loop-end: + # persist necessary variables from registers + 89/copy 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # copy ECX to *EDI + # 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 + # epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return + +test-get-num-reads-single-digit: + ## check that get-num returns first character if it's a digit + # This test uses exit-descriptors. Use EBP for setting up local variables. + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + ## clear all streams + # clear-stream(_test-stream) + # push args + 68/push _test-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-buffered-file+4) + # push args + b8/copy-to-EAX _test-buffered-file/imm32 + 05/add-to-EAX 4/imm32 + 50/push-EAX + # 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-error-stream) + # push args + 68/push _test-error-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 'in' + # write(_test-stream, "3") + # push args + 68/push "3"/imm32 + 68/push _test-stream/imm32 + # call + e8/call write/disp32 + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + ## initialize exit-descriptor 'ed' + # allocate on stack + # var ed/EAX : (address exit-descriptor) + 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP + 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX . . # copy ESP to EAX + # size the exit-descriptor for the call to get-num below + # tailor-exit-descriptor(ed, 16) + # push args + 68/push 0x10/imm32/nbytes-of-args-for-get-num + 50/push-EAX/ed + # call + e8/call tailor-exit-descriptor/disp32 + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + ## prime the pump + # get-char(_test-buffered-file) + # push args + 68/push _test-buffered-file/imm32 + # call + e8/call get-char/disp32 + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + ## get-num(in, out, err, ed) + # push args + 50/push-EAX/ed + 68/push _test-error-stream/imm32 + 68/push _test-output-stream/imm32 + 68/push _test-buffered-file/imm32 + # call + e8/call get-num/disp32 + ## registers except ESP may be clobbered at this point + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP + # check-ints-equal(*_test-output-stream.data, '3') + # push args + 68/push "F - test-get-num-reads-single-digit"/imm32 + 68/push 0x33/imm32 + b8/copy-to-EAX _test-output-stream/imm32 + ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) + # call + e8/call check-ints-equal/disp32 + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # reclaim locals + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 5d/pop-to-EBP + c3/return + +test-get-num-aborts-on-non-digit-in-Look: + ## check that get-num returns first character if it's a digit + # This test uses exit-descriptors. Use EBP for setting up local variables. + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + ## clear all streams + # clear-stream(_test-stream) + # push args + 68/push _test-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-buffered-file+4) + # push args + b8/copy-to-EAX _test-buffered-file/imm32 + 05/add-to-EAX 4/imm32 + 50/push-EAX + # 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-error-stream) + # push args + 68/push _test-error-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 'in' + # write(_test-stream, "3") + # push args + 68/push "3"/imm32 + 68/push _test-stream/imm32 + # call + e8/call write/disp32 + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + ## initialize exit-descriptor 'ed' + # allocate on stack + # var ed/EAX : (address exit-descriptor) + 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP + 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX . . # copy ESP to EAX + # size the exit-descriptor for the call to get-num below + # tailor-exit-descriptor(ed, 16) + # push args + 68/push 0x10/imm32/nbytes-of-args-for-get-num + 50/push-EAX/ed + # call + e8/call tailor-exit-descriptor/disp32 + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + ## don't initialize Look + ## get-num(in, out, err, ed) + # push args + 50/push-EAX/ed + 68/push _test-error-stream/imm32 + 68/push _test-output-stream/imm32 + 68/push _test-buffered-file/imm32 + # call + e8/call get-num/disp32 + ## registers except ESP may be clobbered at this point + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP + ## check that get-num tried to call exit(1) + # check-ints-equal(ed->value, 2, msg) # i.e. stop was called with value 1 + # push args + 68/push "F - test-get-num-aborts-on-non-digit-in-Look"/imm32 + 68/push 2/imm32 + # push ed->value + ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) + # call + e8/call check-ints-equal/disp32 + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # reclaim locals + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 5d/pop-to-EBP + c3/return + +test-get-num-reads-multiple-digits: + ## check that get-num returns all initial digits until it encounters a non-digit + # This test uses exit-descriptors. Use EBP for setting up local variables. + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + ## clear all streams + # clear-stream(_test-stream) + # push args + 68/push _test-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-buffered-file+4) + # push args + b8/copy-to-EAX _test-buffered-file/imm32 + 05/add-to-EAX 4/imm32 + 50/push-EAX + # 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-error-stream) + # push args + 68/push _test-error-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 'in' + # write(_test-stream, "3456 x") + # push args + 68/push "3456"/imm32 + 68/push _test-stream/imm32 + # call + e8/call write/disp32 + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + ## initialize exit-descriptor 'ed' + # allocate on stack + # var ed/EAX : (address exit-descriptor) + 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP + 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX . . # copy ESP to EAX + # size the exit-descriptor for the call to get-num below + # tailor-exit-descriptor(ed, 16) + # push args + 68/push 0x10/imm32/nbytes-of-args-for-get-num + 50/push-EAX/ed + # call + e8/call tailor-exit-descriptor/disp32 + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + ## prime the pump + # get-char(_test-buffered-file) + # push args + 68/push _test-buffered-file/imm32 + # call + e8/call get-char/disp32 + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + ## get-num(in, out, err, ed) + # push args + 50/push-EAX/ed + 68/push _test-error-stream/imm32 + 68/push _test-output-stream/imm32 + 68/push _test-buffered-file/imm32 + # call + e8/call get-num/disp32 + ## registers except ESP may be clobbered at this point + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP + # check-ints-equal(*_test-output-stream.data, '3456') + # push args + 68/push "F - test-get-num-reads-multiple-digits"/imm32 + 68/push 0x36353433/imm32 + b8/copy-to-EAX _test-output-stream/imm32 + ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) + # call + e8/call check-ints-equal/disp32 + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # reclaim locals + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 5d/pop-to-EBP + c3/return + +test-get-num-reads-multiple-digits-followed-by-nondigit: + ## check that get-num returns all initial digits until it encounters a non-digit + # This test uses exit-descriptors. Use EBP for setting up local variables. + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + ## clear all streams + # clear-stream(_test-stream) + # push args + 68/push _test-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-buffered-file+4) + # push args + b8/copy-to-EAX _test-buffered-file/imm32 + 05/add-to-EAX 4/imm32 + 50/push-EAX + # 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-error-stream) + # push args + 68/push _test-error-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 'in' + # write(_test-stream, "3456 x") + # push args + 68/push "3456 x"/imm32 + 68/push _test-stream/imm32 + # call + e8/call write/disp32 + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + ## initialize exit-descriptor 'ed' + # allocate on stack + # var ed/EAX : (address exit-descriptor) + 81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # subtract from ESP + 8d/copy-address 0/mod/indirect 4/rm32/sib 4/base/ESP 4/index/none . 0/r32/EAX . . # copy ESP to EAX + # size the exit-descriptor for the call to get-num below + # tailor-exit-descriptor(ed, 16) + # push args + 68/push 0x10/imm32/nbytes-of-args-for-get-num + 50/push-EAX/ed + # call + e8/call tailor-exit-descriptor/disp32 + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + ## prime the pump + # get-char(_test-buffered-file) + # push args + 68/push _test-buffered-file/imm32 + # call + e8/call get-char/disp32 + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + ## get-num(in, out, err, ed) + # push args + 50/push-EAX/ed + 68/push _test-error-stream/imm32 + 68/push _test-output-stream/imm32 + 68/push _test-buffered-file/imm32 + # call + e8/call get-num/disp32 + ## registers except ESP may be clobbered at this point + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP + # check-ints-equal(*_test-output-stream.data, '3456') + # push args + 68/push "F - test-get-num-reads-multiple-digits-followed-by-nondigit"/imm32 + 68/push 0x36353433/imm32 + b8/copy-to-EAX _test-output-stream/imm32 + ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) + # call + e8/call check-ints-equal/disp32 + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # reclaim locals + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 5d/pop-to-EBP + c3/return + +## helpers + +# write(f, "Error: "+s+" expected\n") then stop(ed, 1) +expected: # ed : (address exit-descriptor), f : fd or (address stream), s : (address array byte) -> <void> + # prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # write(f, "Error: ") + # push args + 68/push "Error: "/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + # call + e8/call write/disp32 + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # write(f, s) + # push args + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x10/disp8 . # push *(EBP+16) + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + # call + e8/call write/disp32 + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # write(f, " expected") + # push args + 68/push " expected"/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + # call + e8/call write/disp32 + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # write(f, Newline) + # push args + 68/push Newline/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + # call + e8/call write/disp32 + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # stop(ed, 1) + # push args + 68/push 1/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 . # push *(EBP+8) + # call + e8/call stop/disp32 + ## should never get past this point + # epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return + +# write(f, "Error: "+s+"\n") then stop(ed, 1) +error: # ed : (address exit-descriptor), f : fd or (address stream), s : (address array byte) -> <void> + # prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # write(f, "Error: ") + # push args + 68/push "Error: "/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + # call + e8/call write/disp32 + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # write(f, s) + # push args + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x10/disp8 . # push *(EBP+16) + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + # call + e8/call write/disp32 + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # write(f, Newline) + # push args + 68/push Newline/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0xc/disp8 . # push *(EBP+12) + # call + e8/call write/disp32 + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # stop(ed, 1) + # push args + 68/push 1/imm32 + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 8/disp8 . # push *(EBP+8) + # call + e8/call stop/disp32 + ## should never get past this point + # epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return + +# read a byte from 'f', and store it in 'Look' +get-char: # f : (address buffered-file) -> <void> + # prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # save registers + 50/push-EAX + # read-byte(f) + # push args + ff 6/subop/push 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/disp8 . # push *(EBP+8) + # call + e8/call read-byte/disp32 + # discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # save EAX to Look + 89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Look/disp32 . # copy EAX to *Look + # restore registers + 58/pop-to-EAX + # epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return + +is-digit?: # c : int -> bool/EAX + # prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # EAX = false + b8/copy-to-EAX 0/imm32 + # if c < '0' return false + 81 7/subop/compare 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/disp8 0x30/imm32 # compare *(EBP+8) + 7c/jump-if-lesser $is-digit?:end/disp8 + # if c > '9' return false + 81 7/subop/compare 1/mod/*+disp8 4/rm32/sib 5/base/EBP 4/index/none . . 0x8/disp8 0x39/imm32 # compare *(EBP+8) + 7f/jump-if-greater $is-digit?:end/disp8 + # otherwise return true + b8/copy-to-EAX 1/imm32 +$is-digit?:end: + # epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return + +== data + +Look: + 00 00 00 00 + +_test-output-stream: + # current write index + 00 00 00 00 + # current read index + 00 00 00 00 + # length (= 8) + 08 00 00 00 + # data + 00 00 00 00 00 00 00 00 # 8 bytes + +_test-error-stream: + # current write index + 00 00 00 00 + # current read index + 00 00 00 00 + # length (= 8) + 08 00 00 00 + # data + 00 00 00 00 00 00 00 00 # 8 bytes + +# vim:nowrap:textwidth=0 diff --git a/subx/test_apps b/subx/test_apps index 8c97c521..2bb7adec 100755 --- a/subx/test_apps +++ b/subx/test_apps @@ -150,4 +150,14 @@ test `uname` = 'Linux' && { echo } +echo crenshaw2-1b +CFLAGS=-g ./subx translate *.subx apps/crenshaw2-1b.subx -o apps/crenshaw2-1b +[ "$1" != record ] && git diff --quiet apps/crenshaw2-1b +CFLAGS=-g ./subx run apps/crenshaw2-1b test +echo +test `uname` = 'Linux' && { + apps/crenshaw2-1b test + echo +} + exit 0 |