diff options
Diffstat (limited to '070---hex.subx')
-rw-r--r-- | 070---hex.subx | 1486 |
1 files changed, 1486 insertions, 0 deletions
diff --git a/070---hex.subx b/070---hex.subx new file mode 100644 index 00000000..9092c6dd --- /dev/null +++ b/070---hex.subx @@ -0,0 +1,1486 @@ +# Read a text file containing whitespace-separated pairs of ascii hex bytes +# from stdin, and convert them into binary bytes (octets) on stdout. Ignore +# comments between '#' and newline. +# +# To run: +# $ ./subx translate init.linux 0*.subx apps/subx-common.subx apps/hex.subx -o apps/hex +# $ echo '80 81 82 # comment' |./subx run apps/hex |xxd - +# Expected output: +# 00000000: 8081 82 +# +# Only hex bytes and comments are permitted. Outside of comments all words +# must be exactly 2 characters long and contain only characters [0-9a-f]. No +# uppercase hex. + +== code +# instruction effective address register displacement immediate +# . op subop mod rm32 base index scale r32 +# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes + +Entry: # run tests if necessary, convert stdin if not + # . prolog + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + + # initialize heap + # . Heap = new-segment(64KB) + # . . push args + 68/push Heap/imm32 + 68/push 0x10000/imm32/64KB + # . . 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-lesser-or-equal $hex: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 == 0) goto run-main + 3d/compare-eax-and 0/imm32 + 74/jump-if-equal $hex: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 $hex:end/disp8 +$hex:run-main: + # - otherwise convert stdin + # 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 + # configure ed to really exit() + # . ed->target = 0 + c7 0/subop/copy 0/mod/direct 0/rm32/eax . . . . . 0/imm32 # copy to *eax + # convert-hex(Stdin, 1/stdout, 2/stderr, ed) + # . . push args + 50/push-eax/ed + 68/push Stderr/imm32 + 68/push Stdout/imm32 + 68/push Stdin/imm32 + # . . call + e8/call convert-hex/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 +$hex:end: + b8/copy-to-eax 1/imm32/exit + cd/syscall 0x80/imm8 + +# the main entry point +convert-hex: # in : (address buffered-file), out : (address buffered-file), err : (address buffered-file), ed : (address exit-descriptor) -> <void> + # pseudocode: + # while true + # eax = convert-next-octet(in, err, ed) + # if (eax == Eof) break + # write-byte-buffered(out, AL) + # flush(out) + # + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 50/push-eax +$convert-hex:loop: + # eax = convert-next-octet(in, 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) + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) + # . . call + e8/call convert-next-octet/disp32 + # . . discard first 2 args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # if (eax == Eof) break + 3d/compare-eax-and 0xffffffff/imm32/Eof + 74/jump-if-equal $convert-hex:loop-end/disp8 + # write-byte-buffered(out, AL) + # . . push args + 50/push-eax + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) + # . . call + e8/call write-byte-buffered/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # loop + eb/jump $convert-hex:loop/disp8 +$convert-hex:loop-end: + # flush(out) + # . . push args + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) + # . . call + e8/call flush/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp +$convert-hex:end: + # . 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 + +# read bytes from 'in' until a sequence of two lowercase hex (0-9, a-f) bytes +# skip spaces and newlines +# on '#' skip bytes until newline +# raise an error and abort on all other unexpected bytes +# return in eax an _octet_ containing the binary value of the two hex characters +# return Eof on reaching end of file +convert-next-octet: # in : (address buffered-file), err : (address buffered-file), ed : (address exit-descriptor) -> byte-or-Eof/eax + # pseudocode: + # eax = scan-next-byte(in, err, ed) + # if (eax == Eof) return + # ecx = from-hex-char(eax) + # eax = scan-next-byte(in, err, ed) + # if (eax == Eof) error("partial byte found.") + # eax = from-hex-char(eax) + # eax = (ecx << 4) | eax + # return + # + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 51/push-ecx + # eax = scan-next-byte(in, err, ed) + # . . 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) + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) + # . . call + e8/call scan-next-byte/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # if (eax == Eof) return + 3d/compare-eax-and 0xffffffff/imm32/Eof + 74/jump-if-equal $convert-next-octet:end/disp8 + # eax = from-hex-char(eax) + e8/call from-hex-char/disp32 + # ecx = eax + 89/copy 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # copy eax to ecx + # eax = scan-next-byte(in, err, ed) + # . . 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) + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) + # . . call + e8/call scan-next-byte/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # if (eax == Eof) error(ed, err, "partial byte found.") + 3d/compare-eax-and 0xffffffff/imm32/Eof + 75/jump-if-not-equal $convert-next-octet:convert/disp8 + # . error-byte(ed, err, msg, '.') # reusing error-byte to avoid creating _yet_ another helper + # . . push args + 68/push 0x2e/imm32/period/dummy + 68/push "convert-next-octet: partial byte found"/imm32 + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16) + # . . call + e8/call error-byte/disp32 # never returns +$convert-next-octet:convert: + # eax = from-hex-char(eax) + e8/call from-hex-char/disp32 + # eax = (ecx << 4) | eax + # . ecx <<= 4 + c1/shift 4/subop/left 3/mod/direct 1/rm32/ecx . . . . . 4/imm8 # shift ecx left by 4 bits + # . eax |= ecx + 09/or 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # eax = bitwise OR with ecx +$convert-next-octet:end: + # . restore registers + 59/pop-to-ecx + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-convert-next-octet: + # - check that the first two bytes of the input are assembled into the resulting octet + # 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-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 + # . clear-stream(_test-error-buffered-file+4) + # . . push args + b8/copy-to-eax _test-error-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 + # initialize '_test-stream' to "abc" + # . write(_test-stream, "abc") + # . . push args + 68/push "abc"/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 'convert-next-octet' below + # . var ed/ecx : exit-descriptor + 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # subtract from esp + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # . tailor-exit-descriptor(ed, 12) + # . . push args + 68/push 0xc/imm32/nbytes-of-args-for-convert-next-octet + 51/push-ecx/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 + # eax = convert-next-octet(_test-buffered-file, _test-error-buffered-file, ed) + # . . push args + 51/push-ecx/ed + 68/push _test-error-buffered-file/imm32 + 68/push _test-buffered-file/imm32 + # . . call + e8/call convert-next-octet/disp32 + # registers except esp may be clobbered at this point + # pop args to convert-next-octet + # . . discard first 2 args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . . restore ed + 59/pop-to-ecx + # check that convert-next-octet didn't abort + # . check-ints-equal(ed->value, 0, msg) + # . . push args + 68/push "F - test-convert-next-octet: unexpected abort"/imm32 + 68/push 0/imm32 + # . . push ed->value + ff 6/subop/push 1/mod/*+disp8 1/rm32/ecx . . . . 4/disp8 . # push *(ecx+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 + # return if abort + 81 7/subop/compare 1/mod/*+disp8 1/rm32/ecx . . . . 4/disp8 0/imm32 # compare *(ecx+4) + 75/jump-if-not-equal $test-convert-next-octet:end/disp8 + # check-ints-equal(eax, 0xab, msg) + # . . push args + 68/push "F - test-convert-next-octet"/imm32 + 68/push 0xab/imm32/ab + 50/push-eax + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp +$test-convert-next-octet:end: + # . epilog + # don't restore esp from ebp; manually reclaim locals + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + 5d/pop-to-ebp + c3/return + +test-convert-next-octet-handles-Eof: + # - check that reaching end of file returns Eof + # 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-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 + # . clear-stream(_test-error-buffered-file+4) + # . . push args + b8/copy-to-eax _test-error-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 + # don't initialize '_test-stream' + # initialize exit-descriptor 'ed' for the call to 'convert-next-octet' below + # . var ed/ecx : exit-descriptor + 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # subtract from esp + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # . tailor-exit-descriptor(ed, 12) + # . . push args + 68/push 0xc/imm32/nbytes-of-args-for-convert-next-octet + 51/push-ecx/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 + # eax = convert-next-octet(_test-buffered-file, _test-error-buffered-file, ed) + # . . push args + 51/push-ecx/ed + 68/push _test-error-buffered-file/imm32 + 68/push _test-buffered-file/imm32 + # . . call + e8/call convert-next-octet/disp32 + # registers except esp may be clobbered at this point + # pop args to convert-next-octet + # . . discard first 2 args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . . restore ed + 59/pop-to-ecx + # check that convert-next-octet didn't abort + # . check-ints-equal(ed->value, 0, msg) + # . . push args + 68/push "F - test-convert-next-octet: unexpected abort"/imm32 + 68/push 0/imm32 + # . . push ed->value + ff 6/subop/push 1/mod/*+disp8 1/rm32/ecx . . . . 4/disp8 . # push *(ecx+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 + # return if abort + 81 7/subop/compare 1/mod/*+disp8 1/rm32/ecx . . . . 4/disp8 0/imm32 # compare *(ecx+4) + 75/jump-if-not-equal $test-convert-next-octet-handles-Eof:end/disp8 + # check-ints-equal(eax, Eof, msg) + # . . push args + 68/push "F - test-convert-next-octet-handles-Eof"/imm32 + 68/push 0xffffffff/imm32/Eof + 50/push-eax + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp +$test-convert-next-octet-handles-Eof:end: + # . epilog + # don't restore esp from ebp; manually reclaim locals + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + 5d/pop-to-ebp + c3/return + +test-convert-next-octet-aborts-on-single-hex-byte: + # - check that a single unaccompanied hex byte aborts + # 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-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 + # . clear-stream(_test-error-buffered-file+4) + # . . push args + b8/copy-to-eax _test-error-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 + # initialize '_test-stream' to "a" + # . write(_test-stream, "a") + # . . push args + 68/push "a"/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 'convert-next-octet' below + # . var ed/ecx : exit-descriptor + 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # subtract from esp + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # . tailor-exit-descriptor(ed, 12) + # . . push args + 68/push 0xc/imm32/nbytes-of-args-for-convert-next-octet + 51/push-ecx/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 + # eax = convert-next-octet(_test-buffered-file, _test-error-buffered-file, ed) + # . . push args + 51/push-ecx/ed + 68/push _test-error-buffered-file/imm32 + 68/push _test-buffered-file/imm32 + # . . call + e8/call convert-next-octet/disp32 + # registers except esp may be clobbered at this point + # pop args to convert-next-octet + # . . discard first 2 args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . . restore ed + 59/pop-to-ecx + # check that convert-next-octet aborted + # . check-ints-equal(ed->value, 2, msg) + # . . push args + 68/push "F - test-convert-next-octet-aborts-on-single-hex-byte: unexpected abort"/imm32 + 68/push 2/imm32 + # . . push ed->value + ff 6/subop/push 1/mod/*+disp8 1/rm32/ecx . . . . 4/disp8 . # push *(ecx+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 +$test-convert-next-octet-aborts-on-single-hex-byte:end: + # . epilog + # don't restore esp from ebp; manually reclaim locals + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + 5d/pop-to-ebp + c3/return + +# read whitespace until a hex byte, and return it +# return Eof if file ends without finding a hex byte +# on '#' skip all bytes until newline +# abort on any other byte +scan-next-byte: # in : (address buffered-file), err : (address buffered-file), ed : (address exit-descriptor) -> byte-or-Eof/eax + # pseudocode: + # while true + # eax = read-byte-buffered(in) + # if (eax == Eof) return eax + # if (is-hex-digit?(eax)) return eax + # if (eax == ' ' or '\t' or '\n') continue + # if (eax == '#') skip-until-newline(in) + # else error-byte(ed, err, "invalid byte: " eax) + # + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers +$scan-next-byte:loop: + # eax = read-byte-buffered(in) + # . . 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 + # if (eax == Eof) return eax + 3d/compare-with-eax 0xffffffff/imm32/Eof + 74/jump-if-equal $scan-next-byte:end/disp8 + # if (is-hex-digit?(eax)) return eax + # . save eax for now + 50/push-eax + # . is-hex-digit?(eax) + # . . push args + 50/push-eax + # . . call + e8/call is-hex-digit?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . compare with 'false' + 3d/compare-with-eax 0/imm32 + # . restore eax (does not affect flags) + 58/pop-to-eax + # . check whether to return + 75/jump-if-not-equal $scan-next-byte:end/disp8 +$scan-next-byte:check1: + # if (eax == ' ') continue + 3d/compare-eax-and 0x20/imm32/space + 74/jump-if-equal $scan-next-byte:loop/disp8 + # if (eax == '\t') continue + 3d/compare-eax-and 9/imm32/tab + 74/jump-if-equal $scan-next-byte:loop/disp8 + # if (eax == '\n') continue + 3d/compare-eax-and 0xa/imm32/newline + 74/jump-if-equal $scan-next-byte:loop/disp8 +$scan-next-byte:check2: + # if (eax == '#') skip-until-newline(in) + 3d/compare-with-eax 0x23/imm32 + 75/jump-if-not-equal $scan-next-byte:check3/disp8 + # . skip-until-newline(in) + # . . push args + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) + # . . call + e8/call skip-until-newline/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + eb/jump $scan-next-byte:loop/disp8 +$scan-next-byte:check3: + # otherwise error-byte(ed, err, msg, eax) + # . . push args + 50/push-eax + 68/push "scan-next-byte: invalid byte"/imm32 + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16) + # . . call + e8/call error-byte/disp32 # never returns +$scan-next-byte:end: + # . restore registers + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-scan-next-byte: + # - check that the first byte of the input is returned + # 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-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 + # . clear-stream(_test-error-buffered-file+4) + # . . push args + b8/copy-to-eax _test-error-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 + # initialize '_test-stream' to "abc" + # . write(_test-stream, "abc") + # . . push args + 68/push "abc"/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 'scan-next-byte' below + # . var ed/ecx : exit-descriptor + 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # subtract from esp + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # . tailor-exit-descriptor(ed, 12) + # . . push args + 68/push 0xc/imm32/nbytes-of-args-for-scan-next-byte + 51/push-ecx/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 + # eax = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed) + # . . push args + 51/push-ecx/ed + 68/push _test-error-buffered-file/imm32 + 68/push _test-buffered-file/imm32 + # . . call + e8/call scan-next-byte/disp32 + # registers except esp may be clobbered at this point + # pop args to scan-next-byte + # . . discard first 2 args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . . restore ed + 59/pop-to-ecx + # check that scan-next-byte didn't abort + # . check-ints-equal(ed->value, 0, msg) + # . . push args + 68/push "F - test-scan-next-byte: unexpected abort"/imm32 + 68/push 0/imm32 + # . . push ed->value + ff 6/subop/push 1/mod/*+disp8 1/rm32/ecx . . . . 4/disp8 . # push *(ecx+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 + # return if abort + 81 7/subop/compare 1/mod/*+disp8 1/rm32/ecx . . . . 4/disp8 0/imm32 # compare *(ecx+4) + 75/jump-if-not-equal $test-scan-next-byte:end/disp8 + # check-ints-equal(eax, 0x61/a, msg) + # . . push args + 68/push "F - test-scan-next-byte"/imm32 + 68/push 0x61/imm32/a + 50/push-eax + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp +$test-scan-next-byte:end: + # . epilog + # don't restore esp from ebp; manually reclaim locals + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + 5d/pop-to-ebp + c3/return + +test-scan-next-byte-skips-whitespace: + # - check that the first byte after whitespace is returned + # 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-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 + # . clear-stream(_test-error-buffered-file+4) + # . . push args + b8/copy-to-eax _test-error-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 + # initialize '_test-stream' to input with leading whitespace + # . write(_test-stream, text) + # . . push args + 68/push " abc"/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 'scan-next-byte' below + # . var ed/ecx : exit-descriptor + 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # subtract from esp + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # . tailor-exit-descriptor(ed, 12) + # . . push args + 68/push 0xc/imm32/nbytes-of-args-for-scan-next-byte + 51/push-ecx/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 + # eax = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed) + # . . push args + 51/push-ecx/ed + 68/push _test-error-buffered-file/imm32 + 68/push _test-buffered-file/imm32 + # . . call + e8/call scan-next-byte/disp32 + # registers except esp may be clobbered at this point + # pop args to scan-next-byte + # . . discard first 2 args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . . restore ed + 59/pop-to-ecx + # check that scan-next-byte didn't abort + # . check-ints-equal(ed->value, 0, msg) + # . . push args + 68/push "F - test-scan-next-byte-skips-whitespace: unexpected abort"/imm32 + 68/push 0/imm32 + # . . push ed->value + ff 6/subop/push 1/mod/*+disp8 1/rm32/ecx . . . . 4/disp8 . # push *(ecx+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 + # return if abort + 81 7/subop/compare 1/mod/*+disp8 1/rm32/ecx . . . . 4/disp8 0/imm32 # compare *(ecx+4) + 75/jump-if-not-equal $test-scan-next-byte-skips-whitespace:end/disp8 + # check-ints-equal(eax, 0x61/a, msg) + # . . push args + 68/push "F - test-scan-next-byte-skips-whitespace"/imm32 + 68/push 0x61/imm32/a + 50/push-eax + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp +$test-scan-next-byte-skips-whitespace:end: + # . epilog + # don't restore esp from ebp; manually reclaim locals + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + 5d/pop-to-ebp + c3/return + +test-scan-next-byte-skips-comment: + # - check that the first byte after a comment (and newline) is returned + # 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-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 + # . clear-stream(_test-error-buffered-file+4) + # . . push args + b8/copy-to-eax _test-error-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 + # initialize '_test-stream' to input with leading comment + # . write(_test-stream, comment) + # . . push args + 68/push "#x\n"/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 + # . write(_test-stream, real text) + # . . 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 + # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below + # . var ed/ecx : exit-descriptor + 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # subtract from esp + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # . tailor-exit-descriptor(ed, 12) + # . . push args + 68/push 0xc/imm32/nbytes-of-args-for-scan-next-byte + 51/push-ecx/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 + # eax = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed) + # . . push args + 51/push-ecx/ed + 68/push _test-error-buffered-file/imm32 + 68/push _test-buffered-file/imm32 + # . . call + e8/call scan-next-byte/disp32 + # registers except esp may be clobbered at this point + # pop args to scan-next-byte + # . . discard first 2 args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . . restore ed + 59/pop-to-ecx + # check that scan-next-byte didn't abort + # . check-ints-equal(ed->value, 0, msg) + # . . push args + 68/push "F - test-scan-next-byte-skips-comment: unexpected abort"/imm32 + 68/push 0/imm32 + # . . push ed->value + ff 6/subop/push 1/mod/*+disp8 1/rm32/ecx . . . . 4/disp8 . # push *(ecx+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 + # return if abort + 81 7/subop/compare 1/mod/*+disp8 1/rm32/ecx . . . . 4/disp8 0/imm32 # compare *(ecx+4) + 75/jump-if-not-equal $test-scan-next-byte-skips-comment:end/disp8 + # check-ints-equal(eax, 0x61/a, msg) + # . . push args + 68/push "F - test-scan-next-byte-skips-comment"/imm32 + 68/push 0x61/imm32/a + 50/push-eax + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp +$test-scan-next-byte-skips-comment:end: + # . epilog + # don't restore esp from ebp; manually reclaim locals + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + 5d/pop-to-ebp + c3/return + +test-scan-next-byte-skips-comment-and-whitespace: + # - check that the first byte after a comment and any further whitespace is returned + # 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-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 + # . clear-stream(_test-error-buffered-file+4) + # . . push args + b8/copy-to-eax _test-error-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 + # initialize '_test-stream' to input with leading comment and more whitespace after newline + # . write(_test-stream, comment) + # . . push args + 68/push "#x\n"/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 + # . write(_test-stream, real text) + # . . 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 + # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below + # . var ed/ecx : exit-descriptor + 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # subtract from esp + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # . tailor-exit-descriptor(ed, 12) + # . . push args + 68/push 0xc/imm32/nbytes-of-args-for-scan-next-byte + 51/push-ecx/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 + # eax = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed) + # . . push args + 51/push-ecx/ed + 68/push _test-error-buffered-file/imm32 + 68/push _test-buffered-file/imm32 + # . . call + e8/call scan-next-byte/disp32 + # registers except esp may be clobbered at this point + # pop args to scan-next-byte + # . . discard first 2 args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . . restore ed + 59/pop-to-ecx + # check that scan-next-byte didn't abort + # . check-ints-equal(ed->value, 0, msg) + # . . push args + 68/push "F - test-scan-next-byte-skips-comment-and-whitespace: unexpected abort"/imm32 + 68/push 0/imm32 + # . . push ed->value + ff 6/subop/push 1/mod/*+disp8 1/rm32/ecx . . . . 4/disp8 . # push *(ecx+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 + # return if abort + 81 7/subop/compare 1/mod/*+disp8 1/rm32/ecx . . . . 4/disp8 0/imm32 # compare *(ecx+4) + 75/jump-if-not-equal $test-scan-next-byte-skips-comment-and-whitespace:end/disp8 + # check-ints-equal(eax, 0x61/a, msg) + # . . push args + 68/push "F - test-scan-next-byte-skips-comment-and-whitespace"/imm32 + 68/push 0x61/imm32/a + 50/push-eax + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp +$test-scan-next-byte-skips-comment-and-whitespace:end: + # . epilog + # don't restore esp from ebp; manually reclaim locals + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + 5d/pop-to-ebp + c3/return + +test-scan-next-byte-skips-whitespace-and-comment: + # - check that the first byte after any whitespace and comments is returned + # 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-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 + # . clear-stream(_test-error-buffered-file+4) + # . . push args + b8/copy-to-eax _test-error-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 + # initialize '_test-stream' to input with leading whitespace and comment + # . write(_test-stream, comment) + # . . push args + 68/push " #x\n"/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 + # . write(_test-stream, real text) + # . . 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 + # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below + # . var ed/ecx : exit-descriptor + 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # subtract from esp + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # . tailor-exit-descriptor(ed, 12) + # . . push args + 68/push 0xc/imm32/nbytes-of-args-for-scan-next-byte + 51/push-ecx/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 + # eax = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed) + # . . push args + 51/push-ecx/ed + 68/push _test-error-buffered-file/imm32 + 68/push _test-buffered-file/imm32 + # . . call + e8/call scan-next-byte/disp32 + # registers except esp may be clobbered at this point + # pop args to scan-next-byte + # . . discard first 2 args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . . restore ed + 59/pop-to-ecx + # check that scan-next-byte didn't abort + # . check-ints-equal(ed->value, 0, msg) + # . . push args + 68/push "F - test-scan-next-byte-skips-whitespace-and-comment: unexpected abort"/imm32 + 68/push 0/imm32 + # . . push ed->value + ff 6/subop/push 1/mod/*+disp8 1/rm32/ecx . . . . 4/disp8 . # push *(ecx+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 + # return if abort + 81 7/subop/compare 1/mod/*+disp8 1/rm32/ecx . . . . 4/disp8 0/imm32 # compare *(ecx+4) + 75/jump-if-not-equal $test-scan-next-byte-skips-whitespace-and-comment:end/disp8 + # check-ints-equal(eax, 0x61/a, msg) + # . . push args + 68/push "F - test-scan-next-byte-skips-whitespace-and-comment"/imm32 + 68/push 0x61/imm32/a + 50/push-eax + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp +$test-scan-next-byte-skips-whitespace-and-comment:end: + # . epilog + # don't restore esp from ebp; manually reclaim locals + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + 5d/pop-to-ebp + c3/return + +test-scan-next-byte-reads-final-byte: + # - check that the final byte in input is returned + # 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-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 + # . clear-stream(_test-error-buffered-file+4) + # . . push args + b8/copy-to-eax _test-error-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 + # initialize '_test-stream' to input with single character + # . write(_test-stream, character) + # . . push args + 68/push "a"/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 'scan-next-byte' below + # . var ed/ecx : exit-descriptor + 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # subtract from esp + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # . tailor-exit-descriptor(ed, 12) + # . . push args + 68/push 0xc/imm32/nbytes-of-args-for-scan-next-byte + 51/push-ecx/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 + # eax = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed) + # . . push args + 51/push-ecx/ed + 68/push _test-error-buffered-file/imm32 + 68/push _test-buffered-file/imm32 + # . . call + e8/call scan-next-byte/disp32 + # registers except esp may be clobbered at this point + # pop args to scan-next-byte + # . . discard first 2 args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . . restore ed + 59/pop-to-ecx + # check that scan-next-byte didn't abort + # . check-ints-equal(ed->value, 0, msg) + # . . push args + 68/push "F - test-scan-next-byte-reads-final-byte: unexpected abort"/imm32 + 68/push 0/imm32 + # . . push ed->value + ff 6/subop/push 1/mod/*+disp8 1/rm32/ecx . . . . 4/disp8 . # push *(ecx+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 + # return if abort + 81 7/subop/compare 1/mod/*+disp8 1/rm32/ecx . . . . 4/disp8 0/imm32 # compare *(ecx+4) + 75/jump-if-not-equal $test-scan-next-byte-reads-final-byte:end/disp8 + # check-ints-equal(eax, 0x61/a, msg) + # . . push args + 68/push "F - test-scan-next-byte-reads-final-byte"/imm32 + 68/push 0x61/imm32/a + 50/push-eax + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp +$test-scan-next-byte-reads-final-byte:end: + # . epilog + # don't restore esp from ebp; manually reclaim locals + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + 5d/pop-to-ebp + c3/return + +test-scan-next-byte-handles-Eof: + # - check that the right sentinel value is returned when there's no data remaining to be read + # 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-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 + # . clear-stream(_test-error-buffered-file+4) + # . . push args + b8/copy-to-eax _test-error-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 + # leave '_test-stream' empty + # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below + # . var ed/ecx : exit-descriptor + 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # subtract from esp + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # . tailor-exit-descriptor(ed, 12) + # . . push args + 68/push 0xc/imm32/nbytes-of-args-for-scan-next-byte + 51/push-ecx/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 + # eax = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed) + # . . push args + 51/push-ecx/ed + 68/push _test-error-buffered-file/imm32 + 68/push _test-buffered-file/imm32 + # . . call + e8/call scan-next-byte/disp32 + # registers except esp may be clobbered at this point + # pop args to scan-next-byte + # . . discard first 2 args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . . restore ed + 59/pop-to-ecx + # check that scan-next-byte didn't abort + # . check-ints-equal(ed->value, 0, msg) + # . . push args + 68/push "F - test-scan-next-byte-handles-Eof: unexpected abort"/imm32 + 68/push 0/imm32 + # . . push ed->value + ff 6/subop/push 1/mod/*+disp8 1/rm32/ecx . . . . 4/disp8 . # push *(ecx+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 + # return if abort + 81 7/subop/compare 1/mod/*+disp8 1/rm32/ecx . . . . 4/disp8 0/imm32 # compare *(ecx+4) + 75/jump-if-not-equal $test-scan-next-byte-handles-Eof:end/disp8 + # check-ints-equal(eax, Eof, msg) + # . . push args + 68/push "F - test-scan-next-byte-handles-Eof"/imm32 + 68/push 0xffffffff/imm32/Eof + 50/push-eax + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp +$test-scan-next-byte-handles-Eof:end: + # . epilog + # don't restore esp from ebp; manually reclaim locals + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + 5d/pop-to-ebp + c3/return + +test-scan-next-byte-aborts-on-invalid-byte: + # - check that the a bad byte immediately aborts + # 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-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 + # . clear-stream(_test-error-buffered-file+4) + # . . push args + b8/copy-to-eax _test-error-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 + # initialize '_test-stream' to "x" + # . write(_test-stream, "x") + # . . push args + 68/push "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 'scan-next-byte' below + # . var ed/ecx : exit-descriptor + 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # subtract from esp + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # . tailor-exit-descriptor(ed, 12) + # . . push args + 68/push 0xc/imm32/nbytes-of-args-for-scan-next-byte + 51/push-ecx/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 + # eax = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed) + # . . push args + 51/push-ecx/ed + 68/push _test-error-buffered-file/imm32 + 68/push _test-buffered-file/imm32 + # . . call + e8/call scan-next-byte/disp32 + # registers except esp may be clobbered at this point + # pop args to scan-next-byte + # . . discard first 2 args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . . restore ed + 59/pop-to-ecx + # check that scan-next-byte aborted + # . check-ints-equal(ed->value, 2, msg) + # . . push args + 68/push "F - test-scan-next-byte-aborts-on-invalid-byte"/imm32 + 68/push 2/imm32 + # . . push ed->value + ff 6/subop/push 1/mod/*+disp8 1/rm32/ecx . . . . 4/disp8 . # push *(ecx+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 +$test-scan-next-byte-aborts-on-invalid-byte:end: + # . epilog + # don't restore esp from ebp; manually reclaim locals + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + 5d/pop-to-ebp + c3/return + +skip-until-newline: # in : (address buffered-file) -> <void> + # pseudocode: + # push eax + # while true + # eax = read-byte-buffered(in) + # if (eax == Eof) break + # if (eax == 0x0a) break + # pop eax + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 50/push-eax +$skip-until-newline:loop: + # . eax = read-byte-buffered(in) + # . . 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 + # . if (eax == Eof) break + 3d/compare-eax-and 0xffffffff/imm32/Eof + 74/jump-if-equal $skip-until-newline:end/disp8 + # . if (eax != 0xa/newline) loop + 3d/compare-eax-and 0xa/imm32/newline + 75/jump-if-not-equal $skip-until-newline:loop/disp8 +$skip-until-newline:end: + # . 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 + +test-skip-until-newline: + # - check that the read pointer points after the newline + # setup + # . 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 + # initialize '_test-stream' to "abc\nde" + # . write(_test-stream, "abc") + # . . push args + 68/push "abc\n"/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 + # . write(_test-stream, "de") + # . . push args + 68/push "de"/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 + # skip-until-newline(_test-buffered-file) + # . . push args + 68/push _test-buffered-file/imm32 + # . . call + e8/call skip-until-newline/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-ints-equal(_test-buffered-file->read, 4, msg) + # . . push args + 68/push "F - test-skip-until-newline"/imm32 + 68/push 4/imm32 + b8/copy-to-eax _test-buffered-file/imm32 + ff 6/subop/push 1/mod/*+disp8 0/rm32/eax . . . . 8/disp8 . # push *(eax+8) + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . end + c3/return + +# . . vim:nowrap:textwidth=0 |