diff options
author | Kartik K. Agaram <vc@akkartik.com> | 2021-07-20 01:23:55 -0700 |
---|---|---|
committer | Kartik K. Agaram <vc@akkartik.com> | 2021-07-20 08:43:25 -0700 |
commit | 0b9ea19c9846445e56ce6661f788a65a51461dcf (patch) | |
tree | 2b7903fde564240f82e31cbac74910be1e00ec96 /linux/apps | |
parent | de5e1931edbfc265c4a6b0d6f50b73648361fa63 (diff) | |
download | mu-0b9ea19c9846445e56ce6661f788a65a51461dcf.tar.gz |
.
Delete the examples from Crenshaw. They're extremely rudimentary, and they were really just trial runs for the Mu toolchain.
Diffstat (limited to 'linux/apps')
-rw-r--r-- | linux/apps/crenshaw2-1.subx | 556 | ||||
-rw-r--r-- | linux/apps/crenshaw2-1b.subx | 750 |
2 files changed, 0 insertions, 1306 deletions
diff --git a/linux/apps/crenshaw2-1.subx b/linux/apps/crenshaw2-1.subx deleted file mode 100644 index 73c0f16e..00000000 --- a/linux/apps/crenshaw2-1.subx +++ /dev/null @@ -1,556 +0,0 @@ -# Port of https://github.com/akkartik/crenshaw/blob/master/tutor2.1.pas -# which corresponds to the section "single digits" in https://compilers.iecc.com/crenshaw/tutor2.txt -# except that we support hex digits. -# -# To run: -# $ bootstrap/bootstrap translate [01]*.subx apps/crenshaw2-1.subx -o crenshaw2-1 -# $ echo '3' |bootstrap/bootstrap run crenshaw2-1 -# Expected output: -# # 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' |bootstrap/bootstrap run crenshaw2-1 > z1.subx -# $ bootstrap/bootstrap translate z1.subx -o z1 -# $ bootstrap/bootstrap run z1 -# $ echo $? -# 3 -# -# Stdin must contain just a single hex digit. Other input will print an error: -# $ echo 'xyz' |bootstrap/bootstrap run crenshaw2-1 -# Error: integer expected -# -# Names in this file sometimes follow Crenshaw's original rather than my usual -# naming conventions. - -== 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, call 'compile' if not - # . prologue - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - - # initialize heap - # . Heap = new-segment(Heap-size) - # . . push args - 68/push Heap/imm32 - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Heap-size/disp32 # push *Heap-size - # . . call - e8/call new-segment/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - - # - if argc > 1 and argv[1] == "test", then return run_tests() - # if (argc <= 1) goto run-main - 81 7/subop/compare 1/mod/*+disp8 5/rm32/ebp . . . . 0/disp8 1/imm32 # compare *ebp - 7e/jump-if-<= $run-main/disp8 - # if (!kernel-string-equal?(argv[1], "test")) goto run-main - # . eax = kernel-string-equal?(argv[1], "test") - # . . push args - 68/push "test"/imm32 - ff 6/subop/push 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 run-main - 3d/compare-eax-and 0/imm32/false - 74/jump-if-= $run-main/disp8 - # run-tests() - e8/call run-tests/disp32 - # syscall(exit, *Num-test-failures) - 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/ebx Num-test-failures/disp32 # copy *Num-test-failures to ebx - eb/jump $main:end/disp8 -$run-main: - # - otherwise read a program from stdin and emit its translation to stdout - # . compile(Stdin, 1/stdout, 2/stderr, ed) - # . . push args - 68/push 0/imm32/exit-descriptor - 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: - e8/call syscall_exit/disp32 - -# the main entry point -compile: # in: (addr buffered-file), out: fd or (addr stream byte), err: fd or (addr stream byte), ed: (addr exit-descriptor) - # . 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 - # prime the pump - # . Look = get-char(in) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 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: (stream byte 7) - # Numbers can be 32 bits or 8 hex bytes long. One of them will be in 'Look', so we need space for 7 bytes. - # Sizing the stream just right buys us overflow-handling for free inside 'get-num'. - # Add 12 bytes for 'read', 'write' and 'size' fields, for a total of 19 bytes, or 0x13 in hex. - # The stack pointer is no longer aligned, so dump_stack() can be misleading past this point. - 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0x13/imm32 # subtract from esp - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # initialize the stream - # . num->size = 7 - c7 0/subop/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 - # read a digit from 'in' into 'num' - # . get-num(in, num, err, ed) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x14/disp8 . # push *(ebp+20) - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16) - 51/push-ecx/num - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 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 - # render 'num' into the following template on 'out': - # bb/copy-to-ebx _num_ - # b8/copy-to-eax 1/imm32/exit - # cd/syscall 0x80/imm8 - # - # . write(out, "bb/copy-to-ebx ") - # . . push args - 68/push "bb/copy-to-ebx "/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 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 5/rm32/ebp . . . . 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 5/rm32/ebp . . . . 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(out, "b8/copy-to-eax 1/imm32/exit\n") - # . . push args - 68/push "b8/copy-to-eax 1/imm32/exit\n"/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 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(out, "cd/syscall 0x80/imm8\n") - # . . push args - 68/push "cd/syscall 0x80/imm8\n"/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 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 -$compile:end: - # . restore registers - 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 - -# 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' (first byte) and the argument -# 'in' (rest). We leave the next byte from 'in' into 'Look' on exit. -get-num: # in: (addr buffered-file), out: (addr stream byte), err: fd or (addr stream byte), ed: (addr exit-descriptor) - # pseudocode: - # if (!digit?(Look)) expected(ed, err, "integer") - # if out->write >= out->size - # write(err, "Error: too many digits in number\n") - # stop(ed, 1) - # out->data[out->write] = LSB(Look) - # ++out->write - # Look = get-char(in) - # - # registers: - # in: esi - # out: edi - # out->write: ecx (cached copy; need to keep in sync) - # out->size: edx - # temporaries: eax, ebx - # 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.) - # - # . prologue - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # - if (digit?(Look)) expected(ed, err, "integer") - # . eax = digit?(Look) - # . . push args - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Look/disp32 . # push *Look - # . . call - e8/call digit?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . if (eax == false) - 3d/compare-eax-and 0/imm32/false - 75/jump-if-!= $get-num:main/disp8 - # . expected(ed, err, "integer") - # . . push args - 68/push "integer"/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16) - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 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: - # - otherwise read a digit - # . 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 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi - # edi = out - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 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->size - 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->size) error - 39/compare 3/mod/direct 2/rm32/edx . . . 1/r32/ecx . . # compare edx with ecx - 7d/jump-if-< $get-num:stage2/disp8 - # . error(ed, err, msg) # TODO: show full number - # . . push args - 68/push "get-num: too many digits in number"/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16) - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 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: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 -$get-num:loop-end: - # persist necessary variables from registers - 89/copy 0/mod/indirect 7/rm32/edi . . . 1/r32/ecx . . # copy ecx to *edi -$get-num:end: - # . 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 - -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->buffer) - # . . push args - 68/push $_test-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 - # . 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' for the call to 'get-num' below - # . var ed/eax: exit-descriptor - 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # subtract from esp - 89/copy 3/mod/direct 0/rm32/eax . . . 4/r32/esp . . # copy esp to eax - # . 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', msg) - # . . 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->buffer) - # . . push args - 68/push $_test-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 - # . 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' for the call to 'get-num' below - # . var ed/eax: exit-descriptor - 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # subtract from esp - 89/copy 3/mod/direct 0/rm32/eax . . . 4/r32/esp . . # copy esp to eax - # . 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* prime the pump - # 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 - -## helpers - -# write(f, "Error: "+s+" expected\n") then stop(ed, 1) -expected: # ed: (addr exit-descriptor), f: fd or (addr stream byte), s: (addr array byte) - # . prologue - 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 5/rm32/ebp . . . . 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 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16) - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 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\n") - # . . push args - 68/push " expected\n"/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 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 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) - # . . call - e8/call stop/disp32 - # should never get past this point -$expected:dead-end: - # . epilogue - 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 save it in 'Look' -get-char: # f: (addr buffered-file) - # . prologue - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 50/push-eax - # eax = read-byte-buffered(f) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) - # . . call - e8/call read-byte-buffered/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 -$get-char:end: - # . restore registers - 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 - -digit?: # c: int -> eax: boolean - # . prologue - 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 5/rm32/ebp . . . . 8/disp8 0x30/imm32 # compare *(ebp+8) - 7c/jump-if-< $digit?:end/disp8 - # if (c > '9') return false - 81 7/subop/compare 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 0x39/imm32 # compare *(ebp+8) - 7f/jump-if-> $digit?:end/disp8 - # otherwise return true - b8/copy-to-eax 1/imm32 -$digit?:end: - # . epilogue - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -== data - -Look: # (char with some extra padding) - 0/imm32 - -# . . vim:nowrap:textwidth=0 diff --git a/linux/apps/crenshaw2-1b.subx b/linux/apps/crenshaw2-1b.subx deleted file mode 100644 index 033c69c5..00000000 --- a/linux/apps/crenshaw2-1b.subx +++ /dev/null @@ -1,750 +0,0 @@ -# Port of https://github.com/akkartik/crenshaw/blob/master/tutor2.1.pas -# which corresponds to the section "single digits" in https://compilers.iecc.com/crenshaw/tutor2.txt -# except that we support hex numbers of multiple digits. -# -# To run: -# $ bootstrap/bootstrap translate [01]*.subx apps/crenshaw2-1b.subx -o crenshaw2-1b -# $ echo '1a' |bootstrap/bootstrap run crenshaw2-1b -# Expected output: -# # 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' |bootstrap/bootstrap run crenshaw2-1b > z1.subx -# $ bootstrap/bootstrap translate z1.subx -o z1 -# $ bootstrap/bootstrap run z1 -# $ echo $? -# 26 # 0x1a in decimal -# -# Stdin must contain just a single hex digit. Other input will print an error: -# $ echo 'xyz' |bootstrap/bootstrap run crenshaw2-1b -# Error: integer expected -# -# Names in this file sometimes follow Crenshaw's original rather than my usual -# naming conventions. - -== 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, call 'compile' if not - # . prologue - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - - # initialize heap - # . Heap = new-segment(Heap-size) - # . . push args - 68/push Heap/imm32 - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Heap-size/disp32 # push *Heap-size - # . . call - e8/call new-segment/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - - # - if argc > 1 and argv[1] == "test", then return run_tests() - # if (argc <= 1) goto run-main - 81 7/subop/compare 1/mod/*+disp8 5/rm32/ebp . . . . 0/disp8 1/imm32 # compare *ebp - 7e/jump-if-<= $run-main/disp8 - # if (!kernel-string-equal?(argv[1], "test")) goto run-main - # . eax = kernel-string-equal?(argv[1], "test") - # . . push args - 68/push "test"/imm32 - ff 6/subop/push 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 run-main - 3d/compare-eax-and 0/imm32/false - 74/jump-if-= $run-main/disp8 - # run-tests() - e8/call run-tests/disp32 - # syscall(exit, *Num-test-failures) - 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/ebx Num-test-failures/disp32 # copy *Num-test-failures to ebx - eb/jump $main:end/disp8 -$run-main: - # - otherwise read a program from stdin and emit its translation to stdout - # . compile(Stdin, 1/stdout, 2/stderr, 0) - # . . push args - 68/push 0/imm32/exit-descriptor - 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: - e8/call syscall_exit/disp32 - -# the main entry point -compile: # in: (addr buffered-file), out: fd or (addr stream byte), err: fd or (addr stream byte), ed: (addr exit-descriptor) - # . 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 - # prime the pump - # . Look = get-char(in) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 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: (stream byte 7) - # Numbers can be 32 bits or 8 hex bytes long. One of them will be in 'Look', so we need space for 7 bytes. - # Sizing the stream just right buys us overflow-handling for free inside 'get-num'. - # Add 12 bytes for 'read', 'write' and 'size' fields, for a total of 19 bytes, or 0x13 in hex. - # The stack pointer is no longer aligned, so dump_stack() can be misleading past this point. - 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0x13/imm32 # subtract from esp - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # initialize the stream - # . num->size = 7 - c7 0/subop/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 - # read a digit from 'in' into 'num' - # . get-num(in, num, err, ed) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x14/disp8 . # push *(ebp+20) - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16) - 51/push-ecx/num - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 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 - # render 'num' into the following template on 'out': - # bb/copy-to-ebx _num_ - # b8/copy-to-eax 1/imm32/exit - # cd/syscall 0x80/imm8 - # - # . write(out, "bb/copy-to-ebx ") - # . . push args - 68/push "bb/copy-to-ebx "/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 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 5/rm32/ebp . . . . 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 5/rm32/ebp . . . . 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(out, "b8/copy-to-eax 1/imm32/exit\n") - # . . push args - 68/push "b8/copy-to-eax 1/imm32/exit\n"/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 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(out, "cd/syscall 0x80/imm8\n") - # . . push args - 68/push "cd/syscall 0x80/imm8\n"/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 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 -$compile:end: - # . restore registers - 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 - -# 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: (addr buffered-file), out: (addr stream byte), err: fd or (addr stream byte), ed: (addr exit-descriptor) - # pseudocode: - # if (!digit?(Look)) expected(ed, err, "integer") - # do - # if out->write >= out->size - # 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 digit?(Look) - # This is complicated because I don't want to hard-code the error strategy in - # a general helper like write-byte-buffered. Maybe I should just create a - # local helper. - # - # within the loop we'll try to keep things in registers: - # in: esi - # out: edi - # out->write: ecx (cached copy; need to keep in sync) - # out->size: edx - # temporaries: eax, ebx - # 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.) - # - # . prologue - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # - if (digit?(Look)) expected(ed, err, "integer") - # . eax = digit?(Look) - # . . push args - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Look/disp32 . # push *Look - # . . call - e8/call digit?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . if (eax == false) - 3d/compare-eax-and 0/imm32/false - 75/jump-if-!= $get-num:main/disp8 - # . expected(ed, err, "integer") - # . . push args - 68/push "integer"/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16) - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 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: - # - otherwise read a digit - # . 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 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi - # edi = out - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 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->size - 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->size) error - 39/compare 3/mod/direct 2/rm32/edx . . . 1/r32/ecx . . # compare edx with ecx - 7d/jump-if-< $get-num:loop-stage2/disp8 - # . error(ed, err, msg) # TODO: show full number - # . . push args - 68/push "get-num: too many digits in number"/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16) - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 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 - # if (digit?(Look)) loop - # . eax = digit?(Look) - # . . push args - ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Look/disp32 . # push *Look - # . . call - e8/call digit?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . if (eax != false) loop - 3d/compare-eax-and 0/imm32/false - 0f 85/jump-if-!= $get-num:loop/disp32 -$get-num:loop-end: - # persist necessary variables from registers - 89/copy 0/mod/indirect 7/rm32/edi . . . 1/r32/ecx . . # copy ecx to *edi -$get-num:end: - # . 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 - -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->buffer) - # . . push args - 68/push $_test-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 - # . 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' for the call to 'get-num' below - # . var ed/eax: exit-descriptor - 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # subtract from esp - 89/copy 3/mod/direct 0/rm32/eax . . . 4/r32/esp . . # copy esp to eax - # . 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', msg) - # . . 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->buffer) - # . . push args - 68/push $_test-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 - # . 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' for the call to 'get-num' below - # . var ed/eax: exit-descriptor - 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # subtract from esp - 89/copy 3/mod/direct 0/rm32/eax . . . 4/r32/esp . . # copy esp to eax - # . 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* prime the pump - # 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->buffer) - # . . push args - 68/push $_test-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 - # . 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' for the call to 'get-num' below - # . var ed/eax: exit-descriptor - 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # subtract from esp - 89/copy 3/mod/direct 0/rm32/eax . . . 4/r32/esp . . # copy esp to eax - # . 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', msg) - # . . 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->buffer) - # . . push args - 68/push $_test-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 - # . 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' for the call to 'get-num' below - # . var ed/eax: exit-descriptor - 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # subtract from esp - 89/copy 3/mod/direct 0/rm32/eax . . . 4/r32/esp . . # copy esp to eax - # . 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', msg) - # . . 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: (addr exit-descriptor), f: fd or (addr stream byte), s: (addr array byte) - # . prologue - 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 5/rm32/ebp . . . . 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 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16) - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 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\n") - # . . push args - 68/push " expected\n"/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 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 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) - # . . call - e8/call stop/disp32 - # should never get past this point -$expected:dead-end: - # . epilogue - 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 save it in 'Look' -get-char: # f: (addr buffered-file) - # . prologue - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 50/push-eax - # eax = read-byte-buffered(f) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) - # . . call - e8/call read-byte-buffered/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 -$get-char:end: - # . restore registers - 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 - -digit?: # c: int -> eax: boolean - # . prologue - 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 5/rm32/ebp . . . . 8/disp8 0x30/imm32 # compare *(ebp+8) - 7c/jump-if-< $digit?:end/disp8 - # if (c > '9') return false - 81 7/subop/compare 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 0x39/imm32 # compare *(ebp+8) - 7f/jump-if-> $digit?:end/disp8 - # otherwise return true - b8/copy-to-eax 1/imm32 -$digit?:end: - # . epilogue - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -== data - -Look: # (char with some extra padding) - 0/imm32 - -# . . vim:nowrap:textwidth=0 |