## 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 # # To run (from the subx directory): # $ ./subx translate *.subx apps/crenshaw2-1.subx -o crenshaw2-1 # $ echo '3' |./subx run apps/crenshaw2-1 # Expected output (not working yet): # # syscall(exit, 3) # bb/copy-to-EBX 3/imm32 # b8/copy-to-EAX 1/imm32/exit # cd/syscall 0x80/imm8 # # To run the generated output: # $ echo '3' |./subx run apps/crenshaw2-1 > z1.subx # $ ./subx translate z1.subx -o z1 # $ ./subx run z1 # $ echo $? # 3 # # 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: # 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 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 # expected(ed, 2/stderr, "integer") # push args 68/push "integer"/imm32 68/push 2/imm32/stderr 50/push-EAX # call e8/call expected/disp32 # discard args 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/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 # 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). get-num: # in : (address buffered-file), out : (address stream), err : fd or (address stream), ed : (address exit-descriptor) -> # pseudocode: # if !is-digit?(Look) expected(ed, err, "integer") # do # if Look > 0xff # write(err, "Error: tried to write more than one byte\n") # stop(ed, 1) # 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 Look > 0xff error 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Look/disp32 . # copy *Look to EAX 3d/compare-EAX 0xff/imm32 7e/jump-if-lesser-or-equal $get-num:loop-stage2/disp8 # error(ed, err, "get-num: tried to write too large a value into a single byte") # TODO: show value being printed # push args 68/push "get-num: tried to write too large a value into a single byte"/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: # 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-stage3/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-stage3: # 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 75/jump-if-not-equal $get-num:loop/disp8 $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) -> # 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) -> # 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) -> # 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