From fd91f7f61bfa84cbc24590d5394d75891cc1cfcc Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Thu, 19 Sep 2019 21:01:43 -0700 Subject: 5675 - move helpers from subx-common into layers This undoes 5672 in favor of a new plan: Layers 000 - 099 are for running without syntax sugar. We use them for building syntax-sugar passes. Layers 100 and up are for running with all syntax sugar. The layers are arranged in approximate order so more phases rely on earlier layers than later ones. I plan to not use intermediate syntax sugar (just sigils without calls, or sigils and calls without braces) anywhere except in the specific passes implementing them. --- 069allocate.subx | 2 + 070---hex.subx | 1486 -------------------------------- 070new-stream.subx | 118 +++ 071---subx-params.subx | 16 - 071read-line.subx | 391 +++++++++ 072slice.subx | 1173 +++++++++++++++++++++++++ 073next-token.subx | 1923 +++++++++++++++++++++++++++++++++++++++++ 074write-stream-data.subx | 116 +++ 075print-int-decimal.subx | 307 +++++++ 076next-word.subx | 252 ++++++ 077subx-words.subx | 631 ++++++++++++++ 078emit-hex.subx | 249 ++++++ 079emit.subx | 484 +++++++++++ 080new-stream.subx | 118 --- 080zero-out.subx | 84 ++ 081read-line.subx | 391 --------- 081table.subx | 1696 ++++++++++++++++++++++++++++++++++++ 082slice.subx | 1173 ------------------------- 082slurp.subx | 161 ++++ 083next-token.subx | 1923 ----------------------------------------- 083subx-widths.subx | 238 ++++++ 084emit-hex-array.subx | 142 +++ 084print-int-decimal.subx | 307 ------- 085array-equal.subx | 603 ------------- 086zero-out.subx | 84 -- 087slurp.subx | 161 ---- 088table.subx | 1696 ------------------------------------ 089write-int.subx | 119 --- 092write-int.subx | 119 +++ 093array-equal.subx | 603 +++++++++++++ Readme.md | 12 +- apps/assort | Bin 44323 -> 40203 bytes apps/assort.subx | 52 +- apps/braces | Bin 43717 -> 39565 bytes apps/braces.subx | 2 +- apps/calls | Bin 49078 -> 44926 bytes apps/calls.subx | 2 +- apps/crenshaw2-1 | Bin 38083 -> 39614 bytes apps/crenshaw2-1b | Bin 38642 -> 40173 bytes apps/dquotes | Bin 49512 -> 45445 bytes apps/dquotes.subx | 110 +-- apps/factorial | Bin 37095 -> 38626 bytes apps/handle | Bin 37988 -> 39519 bytes apps/hex | Bin 19496 -> 42569 bytes apps/hex.subx | 1486 ++++++++++++++++++++++++++++++++ apps/mulisp.subx | 2 +- apps/pack | Bin 56994 -> 52864 bytes apps/pack.subx | 112 +-- apps/sigils | Bin 57016 -> 52864 bytes apps/sigils.subx | 2 +- apps/subx-common.subx | 2084 --------------------------------------------- apps/subx-params.subx | 15 + apps/survey | Bin 53596 -> 49460 bytes apps/survey.subx | 40 +- apps/tests | Bin 43140 -> 38988 bytes apps/tests.subx | 44 +- build | 50 -- run_one_test | 2 +- test_apps | 108 +-- test_layers | 4 +- 60 files changed, 10405 insertions(+), 10488 deletions(-) delete mode 100644 070---hex.subx create mode 100644 070new-stream.subx delete mode 100644 071---subx-params.subx create mode 100644 071read-line.subx create mode 100644 072slice.subx create mode 100644 073next-token.subx create mode 100644 074write-stream-data.subx create mode 100644 075print-int-decimal.subx create mode 100644 076next-word.subx create mode 100644 077subx-words.subx create mode 100644 078emit-hex.subx create mode 100644 079emit.subx delete mode 100644 080new-stream.subx create mode 100644 080zero-out.subx delete mode 100644 081read-line.subx create mode 100644 081table.subx delete mode 100644 082slice.subx create mode 100644 082slurp.subx delete mode 100644 083next-token.subx create mode 100644 083subx-widths.subx create mode 100644 084emit-hex-array.subx delete mode 100644 084print-int-decimal.subx delete mode 100644 085array-equal.subx delete mode 100644 086zero-out.subx delete mode 100644 087slurp.subx delete mode 100644 088table.subx delete mode 100644 089write-int.subx create mode 100644 092write-int.subx create mode 100644 093array-equal.subx create mode 100644 apps/hex.subx delete mode 100644 apps/subx-common.subx create mode 100644 apps/subx-params.subx diff --git a/069allocate.subx b/069allocate.subx index af3a7885..5e7a170d 100644 --- a/069allocate.subx +++ b/069allocate.subx @@ -28,6 +28,8 @@ Heap: # a reasonable default Heap-size: 0x200000/imm32/2MB +#? # TODO: reclaim space allocated in tests. +#? 0x2000000/imm32/32MB == code # instruction effective address register displacement immediate diff --git a/070---hex.subx b/070---hex.subx deleted file mode 100644 index ffe72d06..00000000 --- a/070---hex.subx +++ /dev/null @@ -1,1486 +0,0 @@ -# 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(Heap-size) - # . . push args - 68/push Heap/imm32 - 68/push Heap-size/imm32 - # . . 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) -> - # 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) -> - # 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 diff --git a/070new-stream.subx b/070new-stream.subx new file mode 100644 index 00000000..c3f204d2 --- /dev/null +++ b/070new-stream.subx @@ -0,0 +1,118 @@ +# Helper to allocate a stream on the heap. + +== 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 + +new-stream: # ad : (address allocation-descriptor), length : int, elemsize : int -> address/eax + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 52/push-edx + # n = elemsize * length + 12 (for read, write and length) + # . eax = elemsize + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 0x10/disp8 . # copy *(ebp+16) to eax + # . eax *= length + 31/xor 3/mod/direct 2/rm32/edx . . . 2/r32/edx . . # clear edx + f7 4/subop/multiply 1/mod/*+disp8 5/rm32/ebp . . 0xc/disp8 . # multiply *(ebp+12) into eax + # . if overflow abort + 81 7/subop/compare 3/mod/direct 2/rm32/edx . . . . . 0/imm32 # compare edx + 75/jump-if-not-equal $new-stream:abort/disp8 + # . edx = elemsize*length + 89/copy 3/mod/direct 2/rm32/edx . . . 0/r32/eax . . # copy eax to edx + # . eax += 12 + 05/add-to-eax 0xc/imm32 + # allocate(ad, n) + # . . push args + 50/push-eax + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) + # . . call + e8/call allocate/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # eax->length = elemsize*length + 89/copy 1/mod/*+disp8 0/rm32/eax . . . 2/r32/edx 8/disp8 . # copy edx to *(eax+8) + # clear-stream(eax) + # . . push args + 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 +$new-stream:end: + # . restore registers + 5a/pop-to-edx + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +$new-stream:abort: + # . _write(2/stderr, error) + # . . push args + 68/push "new-stream: size too large\n"/imm32 + 68/push 2/imm32/stderr + # . . call + e8/call _write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . syscall(exit, 1) + bb/copy-to-ebx 1/imm32 + b8/copy-to-eax 1/imm32/exit + cd/syscall 0x80/imm8 + # never gets here + +test-new-stream: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # var heap/ecx : (address allocation-descriptor) = {0, 0} + 68/push 0/imm32/limit + 68/push 0/imm32/curr + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # heap = new-segment(512) + # . . push args + 51/push-ecx + 68/push 0x200/imm32 + # . . call + e8/call new-segment/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # var start/edx = ad->curr + 8b/copy 0/mod/indirect 1/rm32/ecx . . . 2/r32/edx . . # copy *ecx to edx + # eax = new-stream(heap, 3, 2) + # . . push args + 68/push 2/imm32 + 68/push 3/imm32 + 51/push-ecx + # . . call + e8/call new-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # check-ints-equal(eax, edx, msg) + # . . push args + 68/push "F - test-new-stream: returns current pointer of allocation descriptor"/imm32 + 52/push-edx + 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 + # check-ints-equal(eax->length, 6, msg) + # . . push args + 68/push "F - test-new-stream: sets length correctly"/imm32 + 68/push 6/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 + # the rest is delegated to clear-stream() so we won't bother checking it + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +# . . vim:nowrap:textwidth=0 diff --git a/071---subx-params.subx b/071---subx-params.subx deleted file mode 100644 index aefb6639..00000000 --- a/071---subx-params.subx +++ /dev/null @@ -1,16 +0,0 @@ -# Normally we introduce names in the layers that need them, but we'll make an -# exception to colocate various knobs for translating SubX programs using SubX. - -== data - -# largest segment that can be translated -Segment-size: - 0x80000/imm32/512KB - -# maximum size of input textual stream (spanning all segments) -Input-size: - 0x100000/imm32/1MB - -# number of labels we can translate to addresses -Max-labels: - 0x10000/imm32/4K-labels/64KB diff --git a/071read-line.subx b/071read-line.subx new file mode 100644 index 00000000..bce42750 --- /dev/null +++ b/071read-line.subx @@ -0,0 +1,391 @@ +== 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 + +# read bytes from 'f' until (and including) a newline and store them into 's' +# 's' fails to grow if and only if no data found +# just abort if 's' is too small +read-line-buffered: # f : (address buffered-file), s : (address stream byte) -> + # pseudocode: + # while true + # if (s->write >= s->length) abort + # if (f->read >= f->write) populate stream from file + # if (f->write == 0) break + # AL = f->data[f->read] + # s->data[s->write] = AL + # ++f->read + # ++s->write + # if (AL == '\n') break + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 50/push-eax + 51/push-ecx + 52/push-edx + 56/push-esi + 57/push-edi + # esi = f + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi + # ecx = f->read + 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 8/disp8 . # copy *(esi+8) to ecx + # edi = s + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 7/r32/edi 0xc/disp8 . # copy *(ebp+12) to edi + # edx = s->write + 8b/copy 0/mod/indirect 7/rm32/edi . . . 2/r32/edx . . # copy *edi to edx +$read-line-buffered:loop: + # if (s->write >= s->length) abort + 3b/compare 1/mod/*+disp8 7/rm32/edi . . . 2/r32/edx 8/disp8 . # compare edx with *(edi+8) + 7d/jump-if-greater-or-equal $read-line-buffered:abort/disp8 + # if (f->read >= f->write) populate stream from file + 3b/compare 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # compare ecx with *(esi+4) + 7c/jump-if-lesser $read-line-buffered:from-stream/disp8 + # . clear-stream(stream = f+4) + # . . push args + 8d/copy-address 1/mod/*+disp8 6/rm32/esi . . . 0/r32/eax 4/disp8 . # copy esi+4 to eax + 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 + # . f->read must now be 0; update its cache at ecx + 31/xor 3/mod/direct 1/rm32/ecx . . . 1/r32/ecx . . # clear ecx + # . eax = read(f->fd, stream = f+4) + # . . push args + 50/push-eax + ff 6/subop/push 0/mod/indirect 6/rm32/esi . . . . . . # push *esi + # . . call + e8/call read/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # if (f->write == 0) break + # since f->read was initially 0, eax is the same as f->write + # . if (eax == 0) return true + 3d/compare-eax-and 0/imm32 + 74/jump-if-equal $read-line-buffered:end/disp8 +$read-line-buffered:from-stream: + # AL = f->data[f->read] + 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax + 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/esi 1/index/ecx . 0/r32/AL 0x10/disp8 . # copy byte at *(esi+ecx+16) to AL + # s->data[s->write] = AL + 88/copy-byte 1/mod/*+disp8 4/rm32/sib 7/base/edi 2/index/edx . 0/r32/AL 0xc/disp8 . # copy AL to *(edi+edx+12) + # ++f->read + 41/increment-ecx + # ++s->write + 42/increment-edx + # if (AL == '\n') return + 3d/compare-eax-and 0xa/imm32 + 75/jump-if-not-equal $read-line-buffered:loop/disp8 +$read-line-buffered:end: + # save f->read + 89/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 8/disp8 . # copy ecx to *(esi+8) + # save s->write + 89/copy 0/mod/indirect 7/rm32/edi . . . 2/r32/edx . . # copy edx to *edi + # . restore registers + 5f/pop-to-edi + 5e/pop-to-esi + 5a/pop-to-edx + 59/pop-to-ecx + 58/pop-to-eax + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +$read-line-buffered:abort: + # . _write(2/stderr, error) + # . . push args + 68/push "read-line-buffered: line too long\n"/imm32 + 68/push 2/imm32/stderr + # . . call + e8/call _write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . syscall(exit, 1) + bb/copy-to-ebx 1/imm32 + b8/copy-to-eax 1/imm32/exit + cd/syscall 0x80/imm8 + # never gets here + +test-read-line-buffered: + # - check that read-line-buffered stops at a 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 + # . clear-stream(_test-tmp-stream) + # . . push args + 68/push _test-tmp-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 + # write(_test-stream, "ab\ncd") + # . . push args + 68/push "ab\ncd"/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 + # read a line from _test-stream (buffered by _test-buffered-file) into _test-tmp-stream + # . eax = read-line-buffered(_test-buffered-file, _test-tmp-stream) + # . . push args + 68/push _test-tmp-stream/imm32 + 68/push _test-buffered-file/imm32 + # . . call + e8/call read-line-buffered/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-next-stream-line-equal(_test-tmp-stream, "ab", msg) + # . . push args + 68/push "F - test-read-line-buffered"/imm32 + 68/push "ab"/imm32 + 68/push _test-tmp-stream/imm32 + # . . call + e8/call check-next-stream-line-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # end + c3/return + +test-read-line-buffered-reads-final-line-until-Eof: + # 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 + # . clear-stream(_test-tmp-stream) + # . . push args + 68/push _test-tmp-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 + # write(_test-stream, "cd") + # . . push args + 68/push "cd"/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 + # read a line from _test-stream (buffered by _test-buffered-file) into _test-tmp-stream + # . eax = read-line-buffered(_test-buffered-file, _test-tmp-stream) + # . . push args + 68/push _test-tmp-stream/imm32 + 68/push _test-buffered-file/imm32 + # . . call + e8/call read-line-buffered/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-stream-equal(_test-tmp-stream, "cd", msg) + # . . push args + 68/push "F - test-read-line-buffered-reads-final-line-until-Eof"/imm32 + 68/push "cd"/imm32 + 68/push _test-tmp-stream/imm32 + # . . call + e8/call check-stream-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # end + c3/return + +# read bytes from 'f' until (and including) a newline and store them into 's' +# 's' fails to grow if and only if no data found +# just abort if 's' is too small +read-line: # f : (address stream), s : (address stream byte) -> + # pseudocode: + # while true + # if (s->write >= s->length) abort + # if (f->read >= f->write) break + # AL = f->data[f->read] + # s->data[s->write] = AL + # ++f->read + # ++s->write + # if (AL == '\n') break + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 50/push-eax + 51/push-ecx + 52/push-edx + 56/push-esi + 57/push-edi + # esi = f + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi + # ecx = f->read + 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy *(esi+4) to ecx + # edi = s + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 7/r32/edi 0xc/disp8 . # copy *(ebp+12) to edi + # edx = s->write + 8b/copy 0/mod/indirect 7/rm32/edi . . . 2/r32/edx . . # copy *edi to edx +$read-line:loop: + # if (s->write >= s->length) abort + 3b/compare 1/mod/*+disp8 7/rm32/edi . . . 2/r32/edx 8/disp8 . # compare edx with *(edi+8) + 0f 8d/jump-if-greater-or-equal $read-line:abort/disp32 + # if (f->read >= f->write) break + 3b/compare 0/mod/indirect 6/rm32/esi . . . 1/r32/ecx . . # compare ecx with *esi + 7d/jump-if-greater-or-equal $read-line:end/disp8 + # AL = f->data[f->read] + 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax + 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/esi 1/index/ecx . 0/r32/AL 0xc/disp8 . # copy byte at *(esi+ecx+12) to AL + # s->data[s->write] = AL + 88/copy-byte 1/mod/*+disp8 4/rm32/sib 7/base/edi 2/index/edx . 0/r32/AL 0xc/disp8 . # copy AL to *(edi+edx+12) + # ++f->read + 41/increment-ecx + # ++s->write + 42/increment-edx + # if (AL == '\n') return + 3d/compare-eax-and 0xa/imm32 + 0f 85/jump-if-not-equal $read-line:loop/disp32 +$read-line:end: + # save f->read + 89/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy ecx to *(esi+4) + # save s->write + 89/copy 0/mod/indirect 7/rm32/edi . . . 2/r32/edx . . # copy edx to *edi + # . restore registers + 5f/pop-to-edi + 5e/pop-to-esi + 5a/pop-to-edx + 59/pop-to-ecx + 58/pop-to-eax + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +$read-line:abort: + # . _write(2/stderr, error) + # . . push args + 68/push "read-line: line too long\n"/imm32 + 68/push 2/imm32/stderr + # . . call + e8/call _write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . syscall(exit, 1) + bb/copy-to-ebx 1/imm32 + b8/copy-to-eax 1/imm32/exit + cd/syscall 0x80/imm8 + # never gets here + +test-read-line: + # - check that read-line stops at a 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-tmp-stream) + # . . push args + 68/push _test-tmp-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 + # write(_test-stream, "ab\ncd") + # . . push args + 68/push "ab\ncd"/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 + # read a line from _test-stream into _test-tmp-stream + # . eax = read-line(_test-stream, _test-tmp-stream) + # . . push args + 68/push _test-tmp-stream/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call read-line/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-next-stream-line-equal(_test-tmp-stream, "ab", msg) + # . . push args + 68/push "F - test-read-line"/imm32 + 68/push "ab"/imm32 + 68/push _test-tmp-stream/imm32 + # . . call + e8/call check-next-stream-line-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # end + c3/return + +test-read-line-reads-final-line-until-Eof: + # 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-tmp-stream) + # . . push args + 68/push _test-tmp-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 + # write(_test-stream, "cd") + # . . push args + 68/push "cd"/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 + # read a line from _test-stream into _test-tmp-stream + # . eax = read-line(_test-stream, _test-tmp-stream) + # . . push args + 68/push _test-tmp-stream/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call read-line/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-stream-equal(_test-tmp-stream, "cd", msg) + # . . push args + 68/push "F - test-read-line-reads-final-line-until-Eof"/imm32 + 68/push "cd"/imm32 + 68/push _test-tmp-stream/imm32 + # . . call + e8/call check-stream-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 diff --git a/072slice.subx b/072slice.subx new file mode 100644 index 00000000..09f45e18 --- /dev/null +++ b/072slice.subx @@ -0,0 +1,1173 @@ +# new data structure: a slice is an open interval of addresses [start, end) +# that includes 'start' but not 'end' + +== 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 + +slice-empty?: # s : (address slice) -> eax : boolean + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 51/push-ecx + # ecx = s + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 8/disp8 . # copy *(ebp+8) to ecx + # if (s->start == s->end) return true + # . eax = s->start + 8b/copy 0/mod/indirect 1/rm32/ecx . . . 0/r32/eax . . # copy *ecx to eax + # . compare eax and s->end + 39/compare 1/mod/*+disp8 1/rm32/ecx . . . 0/r32/eax 4/disp8 . # compare eax and *(ecx+4) + b8/copy-to-eax 1/imm32/true + 74/jump-if-equal $slice-empty?:end/disp8 + b8/copy-to-eax 0/imm32/false +$slice-empty?: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-slice-empty-true: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # var slice/ecx = {34, 34} + 68/push 34/imm32/end + 68/push 34/imm32/start + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # slice-empty?(slice) + # . . push args + 51/push-ecx + # . . call + e8/call slice-empty?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-ints-equal(eax, 1, msg) + # . . push args + 68/push "F - test-slice-empty-true"/imm32 + 68/push 1/imm32 + 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 + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-slice-empty-false: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # var slice/ecx = {34, 23} + 68/push 23/imm32/end + 68/push 34/imm32/start + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # slice-empty?(slice) + # . . push args + 51/push-ecx + # . . call + e8/call slice-empty?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-ints-equal(eax, 0, msg) + # . . push args + 68/push "F - test-slice-empty-false"/imm32 + 68/push 0/imm32 + 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 + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +slice-equal?: # s : (address slice), p : (address string) -> eax : boolean + # pseudocode: + # if (p == 0) return (s == 0) + # currs = s->start + # maxs = s->end + # if (maxs - currs != p->length) return false + # currp = p->data + # while currs < maxs + # if (*currs != *currp) return false + # ++currs + # ++currp + # return true + # + # registers: + # currs: edx + # maxs: esi + # currp: ebx + # *currs: eax + # *currp: ecx + # + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 51/push-ecx + 52/push-edx + 53/push-ebx + 56/push-esi + # esi = s + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi + # currs/edx = s->start + 8b/copy 0/mod/indirect 6/rm32/esi . . . 2/r32/edx . . # copy *esi to edx + # maxs/esi = s->end + 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 6/r32/esi 4/disp8 . # copy *(esi+4) to esi + # eax = maxs - currs + 89/copy 3/mod/direct 0/rm32/eax . . . 6/r32/esi . . # copy esi to eax + 29/subtract 3/mod/direct 0/rm32/eax . . . 2/r32/edx . . # subtract edx from eax + # ebx = p + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 3/r32/ebx 0xc/disp8 . # copy *(ebp+12) to ebx + # if (p != 0) goto next check + 81 7/subop/compare 3/mod/direct 3/rm32/ebx . . . . . 0/imm32 # compare ebx + 75/jump-if-not-equal $slice-equal?:nonnull-string/disp8 +$slice-equal?:null-string: + # return s->start == s->end + 3d/compare-eax-and 0/imm32 + 74/jump-if-equal $slice-equal?:true/disp8 + eb/jump $slice-equal?:false/disp8 +$slice-equal?:nonnull-string: + # if (eax != p->length) return false + 39/compare 0/mod/indirect 3/rm32/ebx . . . 0/r32/eax . . # compare *ebx and eax + 75/jump-if-not-equal $slice-equal?:false/disp8 + # currp/ebx = p->data + 81 0/subop/add 3/mod/direct 3/rm32/ebx . . . . . 4/imm32 # add to ebx + # eax = ecx = 0 + 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax + 31/xor 3/mod/direct 1/rm32/ecx . . . 1/r32/ecx . . # clear ecx +$slice-equal?:loop: + # if (currs >= maxs) return true + 39/compare 3/mod/direct 2/rm32/edx . . . 6/r32/esi . . # compare edx with esi + 73/jump-if-greater-or-equal-unsigned $slice-equal?:true/disp8 + # AL = *currp + 8a/copy-byte 0/mod/indirect 3/rm32/ebx . . . 0/r32/AL . . # copy byte at *ebx to AL + # CL = *currs + 8a/copy-byte 0/mod/indirect 2/rm32/edx . . . 1/r32/CL . . # copy byte at *edx to CL + # if (eax != ecx) return false + 39/compare 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # compare eax and ecx + 75/jump-if-not-equal $slice-equal?:false/disp8 + # ++currp + 43/increment-ebx + # ++currs + 42/increment-edx + eb/jump $slice-equal?:loop/disp8 +$slice-equal?:false: + b8/copy-to-eax 0/imm32 + eb/jump $slice-equal?:end/disp8 +$slice-equal?:true: + b8/copy-to-eax 1/imm32 +$slice-equal?:end: + # . restore registers + 5e/pop-to-esi + 5b/pop-to-ebx + 5a/pop-to-edx + 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-slice-equal: + # - slice-equal?(slice("Abc"), "Abc") == 1 + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # (eax..ecx) = "Abc" + b8/copy-to-eax "Abc"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var slice/ecx = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # eax = slice-equal?(ecx, "Abc") + # . . push args + 68/push "Abc"/imm32 + 51/push-ecx + # . . call + e8/call slice-equal?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-ints-equal(eax, 1, msg) + # . . push args + 68/push "F - test-slice-equal"/imm32 + 68/push 1/imm32 + 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 + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-slice-equal-false: + # - slice-equal?(slice("bcd"), "Abc") == 0 + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # (eax..ecx) = "bcd" + b8/copy-to-eax "bcd"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var slice/ecx = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # eax = slice-equal?(ecx, "Abc") + # . . push args + 68/push "Abc"/imm32 + 51/push-ecx + # . . call + e8/call slice-equal?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-ints-equal(eax, 0, msg) + # . . push args + 68/push "F - test-slice-equal-false"/imm32 + 68/push 0/imm32 + 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 + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-slice-equal-too-long: + # - slice-equal?(slice("Abcd"), "Abc") == 0 + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # (eax..ecx) = "Abcd" + b8/copy-to-eax "Abcd"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var slice/ecx = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # eax = slice-equal?(ecx, "Abc") + # . . push args + 68/push "Abc"/imm32 + 51/push-ecx + # . . call + e8/call slice-equal?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-ints-equal(eax, 0, msg) + # . . push args + 68/push "F - test-slice-equal-too-long"/imm32 + 68/push 0/imm32 + 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 + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-slice-equal-too-short: + # - slice-equal?(slice("A"), "Abc") == 0 + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # (eax..ecx) = "A" + b8/copy-to-eax "A"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var slice/ecx = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # eax = slice-equal?(ecx, "Abc") + # . . push args + 68/push "Abc"/imm32 + 51/push-ecx + # . . call + e8/call slice-equal?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-ints-equal(eax, 0, msg) + # . . push args + 68/push "F - test-slice-equal-too-short"/imm32 + 68/push 0/imm32 + 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 + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-slice-equal-empty: + # - slice-equal?(slice(""), "Abc") == 0 + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # var slice/ecx + 68/push 0/imm32/end + 68/push 0/imm32/start + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # eax = slice-equal?(ecx, "Abc") + # . . push args + 68/push "Abc"/imm32 + 51/push-ecx + # . . call + e8/call slice-equal?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-ints-equal(eax, 0, msg) + # . . push args + 68/push "F - test-slice-equal-empty"/imm32 + 68/push 0/imm32 + 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 + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-slice-equal-with-empty: + # - slice-equal?(slice("Ab"), "") == 0 + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # (eax..ecx) = "Ab" + b8/copy-to-eax "Ab"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var slice/ecx = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # eax = slice-equal?(ecx, "") + # . . push args + 68/push ""/imm32 + 51/push-ecx + # . . call + e8/call slice-equal?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-ints-equal(eax, 0, msg) + # . . push args + 68/push "F - test-slice-equal-with-empty"/imm32 + 68/push 0/imm32 + 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 + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-slice-equal-empty-with-empty: + # - slice-equal?(slice(""), "") == 1 + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # var slice/ecx + 68/push 0/imm32/end + 68/push 0/imm32/start + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # eax = slice-equal?(ecx, "") + # . . push args + 68/push ""/imm32 + 51/push-ecx + # . . call + e8/call slice-equal?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-ints-equal(eax, 1, msg) + # . . push args + 68/push "F - test-slice-equal-empty-with-empty"/imm32 + 68/push 1/imm32 + 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 + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-slice-equal-with-null: + # - slice-equal?(slice("Ab"), null) == 0 + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # (eax..ecx) = "Ab" + b8/copy-to-eax "Ab"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var slice/ecx = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # eax = slice-equal?(ecx, 0) + # . . push args + 68/push 0/imm32 + 51/push-ecx + # . . call + e8/call slice-equal?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-ints-equal(eax, 0, msg) + # . . push args + 68/push "F - test-slice-equal-with-null"/imm32 + 68/push 0/imm32 + 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 + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +slice-starts-with?: # s : (address slice), head : (address string) -> eax : boolean + # pseudocode + # lenh = head->length + # if (lenh > s->end - s->start) return false + # i = 0 + # currs = s->start + # currp = head->data + # while i < lenh + # if (*currs != *currh) return false + # ++i + # ++currs + # ++currh + # return true + # + # registers: + # currs: esi + # currh: edi + # *currs: eax + # *currh: ebx + # i: ecx + # lenh: edx + # + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 51/push-ecx + 52/push-edx + 53/push-ebx + 56/push-esi + 57/push-edi + # esi = s + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi + # ecx = s->end - s->start + 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy *(esi+4) to ecx + 2b/subtract 0/mod/indirect 6/rm32/esi . . . 1/r32/ecx . . # subtract *esi from ecx + # edi = head + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 7/r32/edi 0xc/disp8 . # copy *(ebp+12) to edi + # lenh/edx = head->length + 8b/copy 0/mod/indirect 7/rm32/edi . . . 2/r32/edx . . # copy *edi to edx + # if (lenh > s->end - s->start) return false + 39/compare 3/mod/direct 2/rm32/edx . . . 1/r32/ecx . . # compare edx with ecx + 7f/jump-if-greater $slice-starts-with?:false/disp8 + # currs/esi = s->start + 8b/subtract 0/mod/indirect 6/rm32/esi . . . 6/r32/esi . . # copy *esi to esi + # currh/edi = head->data + 81 0/subop/add 3/mod/direct 7/rm32/edi . . . . . 4/imm32 # add to edi + # i/ecx = 0 + 31/xor 3/mod/direct 1/rm32/ecx . . . 1/r32/ecx . . # clear ecx + # eax = ebx = 0 + 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax + 31/xor 3/mod/direct 3/rm32/ebx . . . 3/r32/ebx . . # clear ebx +$slice-starts-with?:loop: + # if (i >= lenh) return true + 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx + 7d/jump-if-greater-or-equal $slice-starts-with?:true/disp8 + # AL = *currs + 8a/copy-byte 0/mod/indirect 6/rm32/esi . . . 0/r32/AL . . # copy byte at *esi to AL + # BL = *currh + 8a/copy-byte 0/mod/indirect 7/rm32/edi . . . 3/r32/BL . . # copy byte at *edi to BL + # if (*currs != *currh) return false + 39/compare 3/mod/direct 0/rm32/eax . . . 3/r32/ebx . . # compare eax and ebx + 75/jump-if-not-equal $slice-starts-with?:false/disp8 + # ++i + 41/increment-ecx + # ++currs + 46/increment-esi + # ++currh + 47/increment-edi + eb/jump $slice-starts-with?:loop/disp8 +$slice-starts-with?:true: + b8/copy-to-eax 1/imm32 + eb/jump $slice-starts-with?:end/disp8 +$slice-starts-with?:false: + b8/copy-to-eax 0/imm32 +$slice-starts-with?:end: + # . restore registers + 5f/pop-to-edi + 5e/pop-to-esi + 5b/pop-to-ebx + 5a/pop-to-edx + 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-slice-starts-with-single-character: + # - slice-starts-with?(slice("Abc"), "A") == 1 + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # (eax..ecx) = "Abc" + b8/copy-to-eax "Abc"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var slice/ecx = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # eax = slice-starts-with?(ecx, "A") + # . . push args + 68/push "A"/imm32 + 51/push-ecx + # . . call + e8/call slice-starts-with?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-ints-equal(eax, 1, msg) + # . . push args + 68/push "F - test-slice-starts-with-single-character"/imm32 + 68/push 1/imm32 + 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 + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-slice-starts-with-empty-string: + # - slice-starts-with?(slice("Abc"), "") == 1 + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # (eax..ecx) = "Abc" + b8/copy-to-eax "Abc"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var slice/ecx = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # eax = slice-starts-with?(ecx, "") + # . . push args + 68/push ""/imm32 + 51/push-ecx + # . . call + e8/call slice-starts-with?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-ints-equal(eax, 1, msg) + # . . push args + 68/push "F - test-slice-starts-with-empty-string"/imm32 + 68/push 1/imm32 + 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 + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-slice-starts-with-multiple-characters: + # - slice-starts-with?(slice("Abc"), "Ab") == 1 + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # (eax..ecx) = "Abc" + b8/copy-to-eax "Abc"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var slice/ecx = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # eax = slice-starts-with?(ecx, "Ab") + # . . push args + 68/push "Ab"/imm32 + 51/push-ecx + # . . call + e8/call slice-starts-with?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-ints-equal(eax, 1, msg) + # . . push args + 68/push "F - test-slice-starts-with-multiple-characters"/imm32 + 68/push 1/imm32 + 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 + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-slice-starts-with-entire-string: + # - slice-starts-with?(slice("Abc"), "Abc") == 1 + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # (eax..ecx) = "Abc" + b8/copy-to-eax "Abc"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var slice/ecx = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # eax = slice-starts-with?(ecx, "Abc") + # . . push args + 68/push "Abc"/imm32 + 51/push-ecx + # . . call + e8/call slice-starts-with?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-ints-equal(eax, 1, msg) + # . . push args + 68/push "F - test-slice-starts-with-entire-string"/imm32 + 68/push 1/imm32 + 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 + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-slice-starts-with-fails: + # - slice-starts-with?(slice("Abc"), "Abd") == 1 + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # (eax..ecx) = "Abc" + b8/copy-to-eax "Abc"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var slice/ecx = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # eax = slice-starts-with?(ecx, "Abd") + # . . push args + 68/push "Abd"/imm32 + 51/push-ecx + # . . call + e8/call slice-starts-with?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-ints-equal(eax, 0, msg) + # . . push args + 68/push "F - test-slice-starts-with-fails"/imm32 + 68/push 0/imm32 + 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 + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-slice-starts-with-fails-2: + # - slice-starts-with?(slice("Abc"), "Ac") == 1 + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # (eax..ecx) = "Abc" + b8/copy-to-eax "Abc"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var slice/ecx = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # eax = slice-starts-with?(ecx, "Ac") + # . . push args + 68/push "Ac"/imm32 + 51/push-ecx + # . . call + e8/call slice-starts-with?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-ints-equal(eax, 0, msg) + # . . push args + 68/push "F - test-slice-starts-with-fails-2"/imm32 + 68/push 0/imm32 + 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 + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +# write a slice to a stream +# abort if the stream doesn't have enough space +write-slice: # out : (address stream), s : (address slice) + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 50/push-eax + 51/push-ecx + 52/push-edx + 53/push-ebx + 56/push-esi + 57/push-edi + # esi = s + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 0xc/disp8 . # copy *(ebp+12) to esi + # curr/ecx = s->start + 8b/copy 0/mod/indirect 6/rm32/esi . . . 1/r32/ecx . . # copy *esi to ecx + # max/esi = s->end + 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 6/r32/esi 4/disp8 . # copy *(esi+4) to esi + # edi = out + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . 7/r32/edi 8/disp8 . # copy *(ebp+8) to edi + # edx = out->length + 8b/copy 1/mod/*+disp8 7/rm32/edi . . . 2/r32/edx 8/disp8 . # copy *(edi+8) to edx + # ebx = out->write + 8b/copy 0/mod/indirect 7/rm32/edi . . . 3/r32/ebx . . # copy *edi to ebx +$write-slice:loop: + # if (curr >= max) break + 39/compare 3/mod/direct 1/rm32/ecx . . . 6/r32/esi . . # compare ecx with esi + 73/jump-if-greater-or-equal-unsigned $write-slice:loop-end/disp8 + # if (out->write >= out->length) abort + 39/compare 3/mod/direct 3/rm32/ebx . . . 2/r32/edx . . # compare ebx with edx + 7d/jump-if-greater-or-equal $write-slice:abort/disp8 + # out->data[out->write] = *in + # . AL = *in + 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax + 8a/copy-byte 0/mod/indirect 1/rm32/ecx . . . 0/r32/AL . . # copy byte at *ecx to AL + # . out->data[out->write] = AL + 88/copy-byte 1/mod/*+disp8 4/rm32/sib 7/base/edi 3/index/ebx . 0/r32/AL 0xc/disp8 . # copy AL to *(edi+ebx+12) + # ++out->write + 43/increment-ebx + # ++in + 41/increment-ecx + eb/jump $write-slice:loop/disp8 +$write-slice:loop-end: + # persist out->write + 89/copy 0/mod/indirect 7/rm32/edi . . . 3/r32/ebx . . # copy ebx to *edi +$write-slice: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 + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +$write-slice:abort: + # . _write(2/stderr, error) + # . . push args + 68/push "write-slice: out of space"/imm32 + 68/push 2/imm32/stderr + # . . call + e8/call _write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . syscall(exit, 1) + bb/copy-to-ebx 1/imm32 + b8/copy-to-eax 1/imm32/exit + cd/syscall 0x80/imm8 + # never gets here + +test-write-slice: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # 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 + # (eax..ecx) = "Abc" + b8/copy-to-eax "Abc"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var slice/ecx = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # write-slice(_test-stream, slice) + # . . push args + 51/push-ecx + 68/push _test-stream/imm32 + # . . call + e8/call write-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-stream-equal(_test-stream, "Abc", msg) + # . . push args + 68/push "F - test-write-slice"/imm32 + 68/push "Abc"/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call check-stream-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +# write a slice to a buffered-file +write-slice-buffered: # out : (address buffered-file), s : (address slice) + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 50/push-eax + 51/push-ecx + 52/push-edx + 53/push-ebx + 56/push-esi + 57/push-edi + # esi = s + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 0xc/disp8 . # copy *(ebp+12) to esi + # curr/ecx = s->start + 8b/copy 0/mod/indirect 6/rm32/esi . . . 1/r32/ecx . . # copy *esi to ecx + # max/esi = s->end + 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 6/r32/esi 4/disp8 . # copy *(esi+4) to esi + # edi = out + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . 7/r32/edi 8/disp8 . # copy *(ebp+8) to edi + # edx = out->length + 8b/copy 1/mod/*+disp8 7/rm32/edi . . . 2/r32/edx 0xc/disp8 . # copy *(edi+12) to edx + # ebx = out->write + 8b/copy 1/mod/*+disp8 7/rm32/edi . . . 3/r32/ebx 4/disp8 . # copy *(edi+4) to ebx +$write-slice-buffered:loop: + # if (curr >= max) break + 39/compare 3/mod/direct 1/rm32/ecx . . . 6/r32/esi . . # compare ecx with esi + 73/jump-if-greater-or-equal-unsigned $write-slice-buffered:loop-end/disp8 + # if (out->write >= out->length) flush and clear out's stream + 39/compare 3/mod/direct 3/rm32/ebx . . . 2/r32/edx . . # compare ebx with edx + 7c/jump-if-lesser $write-slice-buffered:to-stream/disp8 + # . persist out->write + 89/copy 1/mod/*+disp8 7/rm32/edi . . . 3/r32/ebx 4/disp8 . # copy ebx to *(edi+4) + # . flush(out) + # . . push args + 57/push-edi + # . . call + e8/call flush/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . clear-stream(stream = out+4) + # . . push args + 8d/copy-address 1/mod/*+disp8 7/rm32/edi . . . 0/r32/eax 4/disp8 . # copy edi+4 to eax + 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 + # . out->write must now be 0; update its cache at ebx + 31/xor 3/mod/direct 3/rm32/ebx . . . 3/r32/ebx . . # clear ebx +$write-slice-buffered:to-stream: + # out->data[out->write] = *in + # . AL = *in + 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax + 8a/copy-byte 0/mod/indirect 1/rm32/ecx . . . 0/r32/AL . . # copy byte at *ecx to AL + # . out->data[out->write] = AL + 88/copy-byte 1/mod/*+disp8 4/rm32/sib 7/base/edi 3/index/ebx . 0/r32/AL 0x10/disp8 . # copy AL to *(edi+ebx+16) + # ++out->write + 43/increment-ebx + # ++in + 41/increment-ecx + eb/jump $write-slice-buffered:loop/disp8 +$write-slice-buffered:loop-end: + # persist necessary variables from registers + 89/copy 1/mod/*+disp8 7/rm32/edi . . . 3/r32/ebx 4/disp8 . # copy ebx to *(edi+4) +$write-slice-buffered: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 + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-write-slice-buffered: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # 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 + # (eax..ecx) = "Abc" + b8/copy-to-eax "Abc"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var slice/ecx = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # write-slice-buffered(_test-buffered-file, slice) + # . . push args + 51/push-ecx + 68/push _test-buffered-file/imm32 + # . . call + e8/call write-slice-buffered/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # flush(_test-buffered-file) + # . . push args + 68/push _test-buffered-file/imm32 + # . . call + e8/call flush/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-stream-equal(_test-stream, "Abc", msg) + # . . push args + 68/push "F - test-write-slice-buffered"/imm32 + 68/push "Abc"/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call check-stream-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +# copy a slice into a new (dynamically allocated) string +slice-to-string: # ad : (address allocation-descriptor), in : (address slice) -> out/eax : (address array) + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 51/push-ecx + 52/push-edx + 53/push-ebx + 56/push-esi + # esi = in + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 0xc/disp8 . # copy *(ebp+12) to esi + # curr/edx = in->start + 8b/copy 0/mod/indirect 6/rm32/esi . . . 2/r32/edx . . # copy *esi to edx + # max/ebx = in->end + 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 3/r32/ebx 4/disp8 . # copy *(esi+4) to ebx + # size/ecx = max - curr + 4 # total size of output string (including the initial length) + 89/copy 3/mod/direct 1/rm32/ecx . . . 3/r32/ebx . . # copy ebx to ecx + 29/subtract 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # subtract edx from ecx + 81 0/subop/add 3/mod/direct 1/rm32/ecx . . . . . 4/imm32 # add to ecx + # out/eax = allocate(ad, size) + # . . push args + 51/push-ecx + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) + # . . call + e8/call allocate/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # if (eax == 0) abort + 3d/compare-eax-and 0/imm32 + 74/jump-if-equal $slice-to-string:abort/disp8 + # *out = size-4 + 89/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy ecx to *eax + 81 5/subop/subtract 0/mod/indirect 0/rm32/eax . . . . . 4/imm32 # subtract 4 from *eax + # save out + 50/push-eax + # eax = _append-4(eax+4, eax+size, curr, max) # clobbering ecx + # . . push args + 53/push-ebx + 52/push-edx + # . . push eax+size (clobbering ecx) + 01/add 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # add eax to ecx + 51/push-ecx + # . . push eax+4 (clobbering eax) + 81 0/subop/add 3/mod/direct 0/rm32/eax . . . . . 4/imm32 # add to eax + 50/push-eax + # . . call + e8/call _append-4/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp + # restore out (assumes _append-4 can't error) + 58/pop-to-eax +$slice-to-string:end: + # . restore registers + 5e/pop-to-esi + 5b/pop-to-ebx + 5a/pop-to-edx + 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 + +$slice-to-string:abort: + # . _write(2/stderr, error) + # . . push args + 68/push "slice-to-string: out of space\n"/imm32 + 68/push 2/imm32/stderr + # . . call + e8/call _write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . syscall(exit, 1) + bb/copy-to-ebx 1/imm32 + b8/copy-to-eax 1/imm32/exit + cd/syscall 0x80/imm8 + # never gets here + +test-slice-to-string: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # var heap/edx : (address allocation-descriptor) = {0, 0} + 68/push 0/imm32/limit + 68/push 0/imm32/curr + 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx + # heap = new-segment(512) + # . . push args + 52/push-edx + 68/push 0x200/imm32 + # . . call + e8/call new-segment/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # (eax..ecx) = "Abc" + b8/copy-to-eax "Abc"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var slice/ecx = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # eax = slice-to-string(heap, slice) + # . . push args + 51/push-ecx + 52/push-edx + # . . call + e8/call slice-to-string/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # dump word-slice {{{ +#? # . write(2/stderr, "AA: ") +#? # . . push args +#? 68/push "AA: "/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # . write(2/stderr, eax) +#? # . . push args +#? 50/push-eax +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # . write(2/stderr, "$\n") +#? # . . push args +#? 68/push "$\n"/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # }}} + # eax = string-equal?(eax, "Abc") + # . . push args + 68/push "Abc"/imm32 + 50/push-eax + # . . call + e8/call string-equal?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-ints-equal(eax, 1, msg) + # . . push args + 68/push "F - test-slice-to-string"/imm32 + 68/push 1/imm32/true + 50/push-eax + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +# . . vim:nowrap:textwidth=0 diff --git a/073next-token.subx b/073next-token.subx new file mode 100644 index 00000000..73a658c1 --- /dev/null +++ b/073next-token.subx @@ -0,0 +1,1923 @@ +# Some tokenization primitives. + +== 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 + +# extract the next run of characters that are different from a given 'delimiter' (skipping multiple delimiters if necessary) +# on reaching end of file, return an empty interval +next-token: # in : (address stream), delimiter : byte, out : (address slice) + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 50/push-eax + 51/push-ecx + 56/push-esi + 57/push-edi + # 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 0x10/disp8 . # copy *(ebp+16) to edi + # skip-chars-matching(in, delimiter) + # . . push args + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) + 56/push-esi + # . . call + e8/call skip-chars-matching/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # out->start = &in->data[in->read] + 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy *(esi+4) to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/esi 1/index/ecx . 0/r32/eax 0xc/disp8 . # copy esi+ecx+12 to eax + 89/copy 0/mod/indirect 7/rm32/edi . . . 0/r32/eax . . # copy eax to *edi + # skip-chars-not-matching(in, delimiter) + # . . push args + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) + 56/push-esi + # . . call + e8/call skip-chars-not-matching/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # out->end = &in->data[in->read] + 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy *(esi+4) to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/esi 1/index/ecx . 0/r32/eax 0xc/disp8 . # copy esi+ecx+12 to eax + 89/copy 1/mod/*+disp8 7/rm32/edi . . . 0/r32/eax 4/disp8 . # copy eax to *(edi+4) + # . restore registers + 5f/pop-to-edi + 5e/pop-to-esi + 59/pop-to-ecx + 58/pop-to-eax + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-next-token: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # 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 + # var slice/ecx = {0, 0} + 68/push 0/imm32/end + 68/push 0/imm32/start + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # write(_test-stream, " ab") + # . . push args + 68/push " ab"/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # next-token(_test-stream, 0x20/space, slice) + # . . push args + 51/push-ecx + 68/push 0x20/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call next-token/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # check-ints-equal(slice->start - _test-stream->data, 2, msg) + # . check-ints-equal(slice->start - _test-stream, 14, msg) + # . . push args + 68/push "F - test-next-token: start"/imm32 + 68/push 0xe/imm32 + # . . push slice->start - _test-stream + 8b/copy 0/mod/indirect 1/rm32/ecx . . . 0/r32/eax . . # copy *ecx to eax + 81 5/subop/subtract 3/mod/direct 0/rm32/eax . . . . . _test-stream/imm32 # subtract from eax + 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 + # check-ints-equal(slice->end - _test-stream->data, 4, msg) + # . check-ints-equal(slice->end - _test-stream, 16, msg) + # . . push args + 68/push "F - test-next-token: end"/imm32 + 68/push 0x10/imm32 + # . . push slice->end - _test-stream + 8b/copy 1/mod/*+disp8 1/rm32/ecx . . . 0/r32/eax 4/disp8 . # copy *(ecx+4) to eax + 81 5/subop/subtract 3/mod/direct 0/rm32/eax . . . . . _test-stream/imm32 # subtract from eax + 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 + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-next-token-Eof: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # 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 + # var slice/ecx = {0, 0} + 68/push 0/imm32/end + 68/push 0/imm32/start + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # write nothing to _test-stream + # next-token(_test-stream, 0x20/space, slice) + # . . push args + 51/push-ecx + 68/push 0x20/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call next-token/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # check-ints-equal(slice->end, slice->start, msg) + # . . push args + 68/push "F - test-next-token-Eof"/imm32 + ff 6/subop/push 1/mod/*+disp8 1/rm32/ecx . . . . 4/disp8 . # push *(ecx+4) + ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +# extract the next run of characters that are different from a given 'delimiter' (skipping multiple delimiters if necessary) +# on reaching end of file, return an empty interval +next-token-from-slice: # start : (address byte), end : (address byte), delimiter : byte, out : (address slice) -> + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 50/push-eax + 51/push-ecx + 52/push-edx + 57/push-edi + # ecx = end + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 0xc/disp8 . # copy *(ebp+12) to ecx + # edx = delimiter + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 2/r32/edx 0x10/disp8 . # copy *(ebp+16) to edx + # edi = out + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 7/r32/edi 0x14/disp8 . # copy *(ebp+20) to edi + # eax = skip-chars-matching-in-slice(start, end, delimiter) + # . . push args + 52/push-edx + 51/push-ecx + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) + # . . call + e8/call skip-chars-matching-in-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # out->start = eax + 89/copy 0/mod/indirect 7/rm32/edi . . . 0/r32/eax . . # copy eax to *edi + # eax = skip-chars-not-matching-in-slice(eax, end, delimiter) + # . . push args + 52/push-edx + 51/push-ecx + 50/push-eax + # . . call + e8/call skip-chars-not-matching-in-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # out->end = eax + 89/copy 1/mod/*+disp8 7/rm32/edi . . . 0/r32/eax 4/disp8 . # copy eax to *(edi+4) + # . restore registers + 5f/pop-to-edi + 5a/pop-to-edx + 59/pop-to-ecx + 58/pop-to-eax + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-next-token-from-slice: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # (eax..ecx) = " ab" + b8/copy-to-eax " ab"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var out/edi : (address slice) = {0, 0} + 68/push 0/imm32/end + 68/push 0/imm32/start + 89/copy 3/mod/direct 7/rm32/edi . . . 4/r32/esp . . # copy esp to edi + # next-token-from-slice(eax, ecx, 0x20/space, out) + # . . push args + 57/push-edi + 68/push 0x20/imm32 + 51/push-ecx + 50/push-eax + # . . call + e8/call next-token-from-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp + # out->start should be at the 'a' + # . check-ints-equal(out->start - in->start, 2, msg) + # . . push args + 68/push "F - test-next-token-from-slice: start"/imm32 + 68/push 2/imm32 + # . . push out->start - in->start + 8b/copy 0/mod/indirect 7/rm32/edi . . . 1/r32/ecx . . # copy *edi to ecx + 2b/subtract 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # subtract eax from ecx + 51/push-ecx + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # out->end should be after the 'b' + # check-ints-equal(out->end - in->start, 4, msg) + # . . push args + 68/push "F - test-next-token-from-slice: end"/imm32 + 68/push 4/imm32 + # . . push out->end - in->start + 8b/copy 1/mod/*+disp8 7/rm32/edi . . . 1/r32/ecx 4/disp8 . # copy *(edi+4) to ecx + 2b/subtract 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # subtract eax from ecx + 51/push-ecx + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-next-token-from-slice-Eof: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # var out/edi : (address slice) = {0, 0} + 68/push 0/imm32/end + 68/push 0/imm32/start + 89/copy 3/mod/direct 7/rm32/edi . . . 4/r32/esp . . # copy esp to edi + # next-token-from-slice(0, 0, 0x20/space, out) + # . . push args + 57/push-edi + 68/push 0x20/imm32 + 68/push 0/imm32 + 68/push 0/imm32 + # . . call + e8/call next-token-from-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp + # out should be empty + # . check-ints-equal(out->end - out->start, 0, msg) + # . . push args + 68/push "F - test-next-token-from-slice-Eof"/imm32 + 68/push 0/imm32 + # . . push out->start - in->start + 8b/copy 1/mod/*+disp8 7/rm32/edi . . . 1/r32/ecx 4/disp8 . # copy *(edi+4) to ecx + 2b/subtract 0/mod/indirect 7/rm32/edi . . . 1/r32/ecx . . # subtract *edi from ecx + 51/push-ecx + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-next-token-from-slice-nothing: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # (eax..ecx) = " " + b8/copy-to-eax " "/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var out/edi : (address slice) = {0, 0} + 68/push 0/imm32/end + 68/push 0/imm32/start + 89/copy 3/mod/direct 7/rm32/edi . . . 4/r32/esp . . # copy esp to edi + # next-token-from-slice(in, 0x20/space, out) + # . . push args + 57/push-edi + 68/push 0x20/imm32 + 51/push-ecx + 50/push-eax + # . . call + e8/call next-token-from-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp + # out should be empty + # . check-ints-equal(out->end - out->start, 0, msg) + # . . push args + 68/push "F - test-next-token-from-slice-Eof"/imm32 + 68/push 0/imm32 + # . . push out->start - in->start + 8b/copy 1/mod/*+disp8 7/rm32/edi . . . 1/r32/ecx 4/disp8 . # copy *(edi+4) to ecx + 2b/subtract 0/mod/indirect 7/rm32/edi . . . 1/r32/ecx . . # subtract *edi from ecx + 51/push-ecx + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +skip-chars-matching: # in : (address stream), delimiter : byte + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 50/push-eax + 51/push-ecx + 52/push-edx + 53/push-ebx + 56/push-esi + # esi = in + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi + # ecx = in->read + 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy *(esi+4) to ecx + # ebx = in->write + 8b/copy 0/mod/indirect 6/rm32/esi . . . 3/r32/ebx . . # copy *esi to ebx + # edx = delimiter + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 2/r32/edx 0xc/disp8 . # copy *(ebp+12) to edx +$skip-chars-matching:loop: + # if (in->read >= in->write) break + 39/compare 3/mod/direct 1/rm32/ecx . . . 3/r32/ebx . . # compare ecx with ebx + 7d/jump-if-greater-or-equal $skip-chars-matching:end/disp8 + # eax = in->data[in->read] + 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax + 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/esi 1/index/ecx . 0/r32/AL 0xc/disp8 . # copy byte at *(esi+ecx+12) to AL + # if (eax != delimiter) break + 39/compare 3/mod/direct 0/rm32/eax . . . 2/r32/edx . . # compare eax and edx + 75/jump-if-not-equal $skip-chars-matching:end/disp8 + # ++in->read + 41/increment-ecx + eb/jump $skip-chars-matching:loop/disp8 +$skip-chars-matching:end: + # persist in->read + 89/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy ecx to *(esi+4) + # . restore registers + 5e/pop-to-esi + 5b/pop-to-ebx + 5a/pop-to-edx + 59/pop-to-ecx + 58/pop-to-eax + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-skip-chars-matching: + # 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 + # write(_test-stream, " ab") + # . . push args + 68/push " ab"/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # skip-chars-matching(_test-stream, 0x20/space) + # . . push args + 68/push 0x20/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call skip-chars-matching/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-ints-equal(_test-stream->read, 2, msg) + # . . push args + 68/push "F - test-skip-chars-matching"/imm32 + 68/push 2/imm32 + # . . push *_test-stream->read + b8/copy-to-eax _test-stream/imm32 + 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 + # end + c3/return + +test-skip-chars-matching-none: + # 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 + # write(_test-stream, "ab") + # . . push args + 68/push "ab"/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # skip-chars-matching(_test-stream, 0x20/space) + # . . push args + 68/push 0x20/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call skip-chars-matching/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-ints-equal(_test-stream->read, 0, msg) + # . . push args + 68/push "F - test-skip-chars-matching-none"/imm32 + 68/push 0/imm32 + # . . push *_test-stream->read + b8/copy-to-eax _test-stream/imm32 + 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 + # end + c3/return + +skip-chars-matching-whitespace: # in : (address stream) + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 50/push-eax + 51/push-ecx + 53/push-ebx + 56/push-esi + # esi = in + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi + # ecx = in->read + 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy *(esi+4) to ecx + # ebx = in->write + 8b/copy 0/mod/indirect 6/rm32/esi . . . 3/r32/ebx . . # copy *esi to ebx +$skip-chars-matching-whitespace:loop: + # if (in->read >= in->write) break + 39/compare 3/mod/direct 1/rm32/ecx . . . 3/r32/ebx . . # compare ecx with ebx + 7d/jump-if-greater-or-equal $skip-chars-matching-whitespace:end/disp8 + # eax = in->data[in->read] + 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax + 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/esi 1/index/ecx . 0/r32/AL 0xc/disp8 . # copy byte at *(esi+ecx+12) to AL + # if (eax == ' ') goto body + 3d/compare-eax-and 0x20/imm32/space + 74/jump-if-equal $skip-chars-matching-whitespace:body/disp8 + # if (eax == '\n') goto body + 3d/compare-eax-and 0x0a/imm32/newline + 74/jump-if-equal $skip-chars-matching-whitespace:body/disp8 + # if (eax == '\t') goto body + 3d/compare-eax-and 0x09/imm32/tab + 74/jump-if-equal $skip-chars-matching-whitespace:body/disp8 + # if (eax != '\r') break + 3d/compare-eax-and 0x0d/imm32/cr + 75/jump-if-not-equal $skip-chars-matching-whitespace:end/disp8 +$skip-chars-matching-whitespace:body: + # ++in->read + 41/increment-ecx + eb/jump $skip-chars-matching-whitespace:loop/disp8 +$skip-chars-matching-whitespace:end: + # persist in->read + 89/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy ecx to *(esi+4) + # . restore registers + 5e/pop-to-esi + 5b/pop-to-ebx + 59/pop-to-ecx + 58/pop-to-eax + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-skip-chars-matching-whitespace: + # 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 + # write(_test-stream, " \nab") + # . . push args + 68/push " \nab"/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-chars-matching-whitespace(_test-stream) + # . . push args + 68/push _test-stream/imm32 + # . . call + e8/call skip-chars-matching-whitespace/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-ints-equal(_test-stream->read, 2, msg) + # . . push args + 68/push "F - test-skip-chars-matching-whitespace"/imm32 + 68/push 2/imm32 + # . . push *_test-stream->read + b8/copy-to-eax _test-stream/imm32 + 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 + # end + c3/return + +# minor fork of 'skip-chars-matching' +skip-chars-not-matching: # in : (address stream), delimiter : byte + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 50/push-eax + 51/push-ecx + 52/push-edx + 53/push-ebx + 56/push-esi + # esi = in + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi + # ecx = in->read + 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy *(esi+4) to ecx + # ebx = in->write + 8b/copy 0/mod/indirect 6/rm32/esi . . . 3/r32/ebx . . # copy *esi to ebx + # edx = delimiter + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 2/r32/edx 0xc/disp8 . # copy *(ebp+12) to edx +$skip-chars-not-matching:loop: + # if (in->read >= in->write) break + 39/compare 3/mod/direct 1/rm32/ecx . . . 3/r32/ebx . . # compare ecx with ebx + 7d/jump-if-greater-or-equal $skip-chars-not-matching:end/disp8 + # eax = in->data[in->read] + 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax + 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/esi 1/index/ecx . 0/r32/AL 0xc/disp8 . # copy byte at *(esi+ecx+12) to AL + # if (eax == delimiter) break + 39/compare 3/mod/direct 0/rm32/eax . . . 2/r32/edx . . # compare eax and edx + 74/jump-if-equal $skip-chars-not-matching:end/disp8 + # ++in->read + 41/increment-ecx + eb/jump $skip-chars-not-matching:loop/disp8 +$skip-chars-not-matching:end: + # persist in->read + 89/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy ecx to *(esi+4) + # . restore registers + 5e/pop-to-esi + 5b/pop-to-ebx + 5a/pop-to-edx + 59/pop-to-ecx + 58/pop-to-eax + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-skip-chars-not-matching: + # 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 + # write(_test-stream, "ab ") + # . . push args + 68/push "ab "/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # skip-chars-not-matching(_test-stream, 0x20/space) + # . . push args + 68/push 0x20/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call skip-chars-not-matching/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-ints-equal(_test-stream->read, 2, msg) + # . . push args + 68/push "F - test-skip-chars-not-matching"/imm32 + 68/push 2/imm32 + # . . push *_test-stream->read + b8/copy-to-eax _test-stream/imm32 + 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 + # end + c3/return + +test-skip-chars-not-matching-none: + # 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 + # write(_test-stream, " ab") + # . . push args + 68/push " ab"/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # skip-chars-not-matching(_test-stream, 0x20/space) + # . . push args + 68/push 0x20/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call skip-chars-not-matching/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-ints-equal(_test-stream->read, 0, msg) + # . . push args + 68/push "F - test-skip-chars-not-matching-none"/imm32 + 68/push 0/imm32 + # . . push *_test-stream->read + b8/copy-to-eax _test-stream/imm32 + 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 + # end + c3/return + +test-skip-chars-not-matching-all: + # 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 + # write(_test-stream, "ab") + # . . push args + 68/push "ab"/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # skip-chars-not-matching(_test-stream, 0x20/space) + # . . push args + 68/push 0x20/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call skip-chars-not-matching/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-ints-equal(_test-stream->read, 2, msg) + # . . push args + 68/push "F - test-skip-chars-not-matching-all"/imm32 + 68/push 2/imm32 + # . . push *_test-stream->read + b8/copy-to-eax _test-stream/imm32 + 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 + # end + c3/return + +skip-chars-not-matching-whitespace: # in : (address stream) + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 50/push-eax + 51/push-ecx + 53/push-ebx + 56/push-esi + # esi = in + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi + # ecx = in->read + 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy *(esi+4) to ecx + # ebx = in->write + 8b/copy 0/mod/indirect 6/rm32/esi . . . 3/r32/ebx . . # copy *esi to ebx +$skip-chars-not-matching-whitespace:loop: + # if (in->read >= in->write) break + 39/compare 3/mod/direct 1/rm32/ecx . . . 3/r32/ebx . . # compare ecx with ebx + 7d/jump-if-greater-or-equal $skip-chars-not-matching-whitespace:end/disp8 + # eax = in->data[in->read] + 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax + 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/esi 1/index/ecx . 0/r32/AL 0xc/disp8 . # copy byte at *(esi+ecx+12) to AL + # if (eax == ' ') break + 3d/compare-eax-and 0x20/imm32/space + 74/jump-if-equal $skip-chars-not-matching-whitespace:end/disp8 + # if (eax == '\n') break + 3d/compare-eax-and 0x0a/imm32/newline + 74/jump-if-equal $skip-chars-not-matching-whitespace:end/disp8 + # if (eax == '\t') break + 3d/compare-eax-and 0x09/imm32/tab + 74/jump-if-equal $skip-chars-not-matching-whitespace:end/disp8 + # if (eax == '\r') break + 3d/compare-eax-and 0x0d/imm32/cr + 74/jump-if-equal $skip-chars-not-matching-whitespace:end/disp8 + # ++in->read + 41/increment-ecx + eb/jump $skip-chars-not-matching-whitespace:loop/disp8 +$skip-chars-not-matching-whitespace:end: + # persist in->read + 89/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy ecx to *(esi+4) + # . restore registers + 5e/pop-to-esi + 5b/pop-to-ebx + 59/pop-to-ecx + 58/pop-to-eax + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-skip-chars-not-matching-whitespace: + # 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 + # write(_test-stream, "ab\n") + # . . push args + 68/push "ab\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 + # skip-chars-not-matching-whitespace(_test-stream) + # . . push args + 68/push _test-stream/imm32 + # . . call + e8/call skip-chars-not-matching-whitespace/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-ints-equal(_test-stream->read, 2, msg) + # . . push args + 68/push "F - test-skip-chars-not-matching-whitespace"/imm32 + 68/push 2/imm32 + # . . push *_test-stream->read + b8/copy-to-eax _test-stream/imm32 + 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 + # end + c3/return + +skip-chars-matching-in-slice: # curr : (address byte), end : (address byte), delimiter : byte -> curr/eax + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 51/push-ecx + 52/push-edx + 53/push-ebx + # eax = curr + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 8/disp8 . # copy *(ebp+8) to eax + # ecx = end + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 0xc/disp8 . # copy *(ebp+12) to ecx + # edx = delimiter + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 2/r32/edx 0x10/disp8 . # copy *(ebp+16) to edx + # ebx = 0 + 31/xor 3/mod/direct 3/rm32/ebx . . . 3/r32/ebx . . # clear ebx +$skip-chars-matching-in-slice:loop: + # if (curr >= end) break + 39/compare 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # compare eax with ecx + 73/jump-if-greater-or-equal-unsigned $skip-chars-matching-in-slice:end/disp8 + # if (*curr != delimiter) break + 8a/copy-byte 0/mod/indirect 0/rm32/eax . . . 3/r32/BL . . # copy byte at *eax to BL + 39/compare 3/mod/direct 3/rm32/ebx . . . 2/r32/edx . . # compare ebx and edx + 75/jump-if-not-equal $skip-chars-matching-in-slice:end/disp8 + # ++curr + 40/increment-eax + eb/jump $skip-chars-matching-in-slice:loop/disp8 +$skip-chars-matching-in-slice:end: + # . restore registers + 5b/pop-to-ebx + 5a/pop-to-edx + 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-skip-chars-matching-in-slice: + # (eax..ecx) = " ab" + b8/copy-to-eax " ab"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # eax = skip-chars-matching-in-slice(eax, ecx, 0x20/space) + # . . push args + 68/push 0x20/imm32/space + 51/push-ecx + 50/push-eax + # . . call + e8/call skip-chars-matching-in-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # check-ints-equal(ecx-eax, 2, msg) + # . . push args + 68/push "F - test-skip-chars-matching-in-slice"/imm32 + 68/push 2/imm32 + # . . push ecx-eax + 29/subtract 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # subtract eax from ecx + 51/push-ecx + # . . 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 + +test-skip-chars-matching-in-slice-none: + # (eax..ecx) = "ab" + b8/copy-to-eax "ab"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # eax = skip-chars-matching-in-slice(eax, ecx, 0x20/space) + # . . push args + 68/push 0x20/imm32/space + 51/push-ecx + 50/push-eax + # . . call + e8/call skip-chars-matching-in-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # check-ints-equal(ecx-eax, 2, msg) + # . . push args + 68/push "F - test-skip-chars-matching-in-slice-none"/imm32 + 68/push 2/imm32 + # . . push ecx-eax + 29/subtract 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # subtract eax from ecx + 51/push-ecx + # . . 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 + +skip-chars-matching-whitespace-in-slice: # in : (address stream) + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 51/push-ecx + 53/push-ebx + # eax = curr + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 8/disp8 . # copy *(ebp+8) to eax + # ecx = end + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 0xc/disp8 . # copy *(ebp+12) to ecx + # ebx = 0 + 31/xor 3/mod/direct 3/rm32/ebx . . . 3/r32/ebx . . # clear ebx +$skip-chars-matching-whitespace-in-slice:loop: + # if (curr >= end) break + 39/compare 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # compare eax with ecx + 0f 83/jump-if-greater-or-equal-unsigned $skip-chars-matching-in-slice:end/disp32 + # ebx = *curr + 8a/copy-byte 0/mod/indirect 0/rm32/eax . . . 3/r32/BL . . # copy byte at *eax to BL + # if (*curr == ' ') goto body + 81 7/subop/compare 3/mod/direct 3/rm32/ebx . . . . . 0x20/imm32/space # compare ebx + 74/jump-if-equal $skip-chars-matching-whitespace-in-slice:body/disp8 + # if (*curr == '\n') goto body + 81 7/subop/compare 3/mod/direct 3/rm32/ebx . . . . . 0x0a/imm32/newline # compare ebx + 74/jump-if-equal $skip-chars-matching-whitespace-in-slice:body/disp8 + # if (*curr == '\t') goto body + 81 7/subop/compare 3/mod/direct 3/rm32/ebx . . . . . 0x09/imm32/tab # compare ebx + 74/jump-if-equal $skip-chars-matching-whitespace-in-slice:body/disp8 + # if (*curr != '\r') break + 81 7/subop/compare 3/mod/direct 3/rm32/ebx . . . . . 0x0d/imm32/cr # compare ebx + 75/jump-if-not-equal $skip-chars-matching-whitespace-in-slice:end/disp8 +$skip-chars-matching-whitespace-in-slice:body: + # ++curr + 40/increment-eax + eb/jump $skip-chars-matching-whitespace-in-slice:loop/disp8 +$skip-chars-matching-whitespace-in-slice:end: + # . restore registers + 5b/pop-to-ebx + 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-skip-chars-matching-whitespace-in-slice: + # (eax..ecx) = " \nab" + b8/copy-to-eax " \nab"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # eax = skip-chars-matching-whitespace-in-slice(eax, ecx) + # . . push args + 51/push-ecx + 50/push-eax + # . . call + e8/call skip-chars-matching-whitespace-in-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-ints-equal(ecx-eax, 2, msg) + # . . push args + 68/push "F - test-skip-chars-matching-whitespace-in-slice"/imm32 + 68/push 2/imm32 + # . . push ecx-eax + 29/subtract 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # subtract eax from ecx + 51/push-ecx + # . . 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 + +# minor fork of 'skip-chars-matching-in-slice' +skip-chars-not-matching-in-slice: # curr : (address byte), end : (address byte), delimiter : byte -> curr/eax + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 51/push-ecx + 52/push-edx + 53/push-ebx + # eax = curr + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 8/disp8 . # copy *(ebp+8) to eax + # ecx = end + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 0xc/disp8 . # copy *(ebp+12) to ecx + # edx = delimiter + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 2/r32/edx 0x10/disp8 . # copy *(ebp+16) to edx + # ebx = 0 + 31/xor 3/mod/direct 3/rm32/ebx . . . 3/r32/ebx . . # clear ebx +$skip-chars-not-matching-in-slice:loop: + # if (curr >= end) break + 39/compare 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # compare eax with ecx + 73/jump-if-greater-or-equal-unsigned $skip-chars-not-matching-in-slice:end/disp8 + # if (*curr == delimiter) break + 8a/copy-byte 0/mod/indirect 0/rm32/eax . . . 3/r32/BL . . # copy byte at *eax to BL + 39/compare 3/mod/direct 3/rm32/ebx . . . 2/r32/edx . . # compare ebx and edx + 74/jump-if-equal $skip-chars-not-matching-in-slice:end/disp8 + # ++curr + 40/increment-eax + eb/jump $skip-chars-not-matching-in-slice:loop/disp8 +$skip-chars-not-matching-in-slice:end: + # . restore registers + 5b/pop-to-ebx + 5a/pop-to-edx + 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-skip-chars-not-matching-in-slice: + # (eax..ecx) = "ab " + b8/copy-to-eax "ab "/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # eax = skip-chars-not-matching-in-slice(eax, ecx, 0x20/space) + # . . push args + 68/push 0x20/imm32/space + 51/push-ecx + 50/push-eax + # . . call + e8/call skip-chars-not-matching-in-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # check-ints-equal(ecx-eax, 1, msg) + # . . push args + 68/push "F - test-skip-chars-not-matching-in-slice"/imm32 + 68/push 1/imm32 + # . . push ecx-eax + 29/subtract 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # subtract eax from ecx + 51/push-ecx + # . . 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 + +test-skip-chars-not-matching-in-slice-none: + # (eax..ecx) = " ab" + b8/copy-to-eax " ab"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # eax = skip-chars-not-matching-in-slice(eax, ecx, 0x20/space) + # . . push args + 68/push 0x20/imm32/space + 51/push-ecx + 50/push-eax + # . . call + e8/call skip-chars-not-matching-in-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # check-ints-equal(ecx-eax, 3, msg) + # . . push args + 68/push "F - test-skip-chars-not-matching-in-slice-none"/imm32 + 68/push 3/imm32 + # . . push ecx-eax + 29/subtract 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # subtract eax from ecx + 51/push-ecx + # . . 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 + +test-skip-chars-not-matching-in-slice-all: + # (eax..ecx) = "ab" + b8/copy-to-eax "ab"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # eax = skip-chars-not-matching-in-slice(eax, ecx, 0x20/space) + # . . push args + 68/push 0x20/imm32/space + 51/push-ecx + 50/push-eax + # . . call + e8/call skip-chars-not-matching-in-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # check-ints-equal(ecx-eax, 0, msg) + # . . push args + 68/push "F - test-skip-chars-not-matching-in-slice-all"/imm32 + 68/push 0/imm32 + # . . push ecx-eax + 29/subtract 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # subtract eax from ecx + 51/push-ecx + # . . 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 + +skip-chars-not-matching-whitespace-in-slice: # in : (address stream) + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 51/push-ecx + 53/push-ebx + # eax = curr + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 8/disp8 . # copy *(ebp+8) to eax + # ecx = end + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 0xc/disp8 . # copy *(ebp+12) to ecx + # ebx = 0 + 31/xor 3/mod/direct 3/rm32/ebx . . . 3/r32/ebx . . # clear ebx +$skip-chars-not-matching-whitespace-in-slice:loop: + # if (curr >= end) break + 39/compare 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # compare eax with ecx + 0f 83/jump-if-greater-or-equal-unsigned $skip-chars-not-matching-in-slice:end/disp32 + # ebx = *curr + 8a/copy-byte 0/mod/indirect 0/rm32/eax . . . 3/r32/BL . . # copy byte at *eax to BL + # if (*curr == ' ') break + 81 7/subop/compare 3/mod/direct 3/rm32/ebx . . . . . 0x20/imm32/space # compare ebx + 74/jump-if-equal $skip-chars-not-matching-whitespace-in-slice:end/disp8 + # if (*curr == '\n') break + 81 7/subop/compare 3/mod/direct 3/rm32/ebx . . . . . 0x0a/imm32/newline # compare ebx + 74/jump-if-equal $skip-chars-not-matching-whitespace-in-slice:end/disp8 + # if (*curr == '\t') break + 81 7/subop/compare 3/mod/direct 3/rm32/ebx . . . . . 0x09/imm32/tab # compare ebx + 74/jump-if-equal $skip-chars-not-matching-whitespace-in-slice:end/disp8 + # if (*curr == '\r') break + 81 7/subop/compare 3/mod/direct 3/rm32/ebx . . . . . 0x0d/imm32/cr # compare ebx + 74/jump-if-equal $skip-chars-not-matching-whitespace-in-slice:end/disp8 + # ++curr + 40/increment-eax + eb/jump $skip-chars-not-matching-whitespace-in-slice:loop/disp8 +$skip-chars-not-matching-whitespace-in-slice:end: + # . restore registers + 5b/pop-to-ebx + 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-skip-chars-not-matching-whitespace-in-slice: + # (eax..ecx) = "ab\n" + b8/copy-to-eax "ab\n"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # eax = skip-chars-not-matching-whitespace-in-slice(eax, ecx) + # . . push args + 51/push-ecx + 50/push-eax + # . . call + e8/call skip-chars-not-matching-whitespace-in-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-ints-equal(ecx-eax, 1, msg) + # . . push args + 68/push "F - test-skip-chars-not-matching-whitespace-in-slice"/imm32 + 68/push 1/imm32 + # . . push ecx-eax + 29/subtract 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # subtract eax from ecx + 51/push-ecx + # . . 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 + +# update line->read to end of string literal surrounded by double quotes +# line->read must start out at a double-quote +skip-string: # line : (address stream) + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 50/push-eax + 51/push-ecx + 52/push-edx + # ecx = line + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . 1/r32/ecx 8/disp8 . # copy *(ebp+8) to ecx + # eax = skip-string-in-slice(&line->data[line->read], &line->data[line->write]) + # . . push &line->data[line->write] + 8b/copy 1/mod/*+disp8 1/rm32/ecx . . 2/r32/edx 8/disp8 . # copy *(ecx+8) to edx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 1/base/ecx 2/index/edx . 2/r32/edx 0xc/disp8 . # copy ecx+edx+12 to edx + 52/push-edx + # . . push &line->data[line->read] + 8b/copy 1/mod/*+disp8 1/rm32/ecx . . 2/r32/edx 4/disp8 . # copy *(ecx+4) to edx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 1/base/ecx 2/index/edx . 2/r32/edx 0xc/disp8 . # copy ecx+edx+12 to edx + 52/push-edx + # . . call + e8/call skip-string-in-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # line->read = eax - line->data + 29/subtract 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # subtract ecx from eax + 2d/subtract-from-eax 0xc/imm32 + 89/copy 1/mod/*+disp8 1/rm32/ecx . . 0/r32/eax 4/disp8 . # copy eax to *(ecx+4) +$skip-string:end: + # . restore registers + 5a/pop-to-edx + 59/pop-to-ecx + 58/pop-to-eax + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-skip-string: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # setup + # . clear-stream(_test-input-stream) + # . . push args + 68/push _test-input-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . write(_test-input-stream, "\"abc\" def") + # . indices: 0123 45 + # . . push args + 68/push "\"abc\" def"/imm32 + 68/push _test-input-stream/imm32 + # . . call + e8/call write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # precondition: line->read == 0 + # . . push args + 68/push "F - test-skip-string/precondition"/imm32 + 68/push 0/imm32 + b8/copy-to-eax _test-input-stream/imm32 + 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 + # skip-string(_test-input-stream) + # . . push args + 68/push _test-input-stream/imm32 + # . . call + e8/call skip-string/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-ints-equal(line->read, 5, msg) + # . . push args + 68/push "F - test-skip-string"/imm32 + 68/push 5/imm32 + b8/copy-to-eax _test-input-stream/imm32 + 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 + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-skip-string-ignores-spaces: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # setup + # . clear-stream(_test-input-stream) + # . . push args + 68/push _test-input-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . write(_test-input-stream, "\"a b\"/yz") + # . indices: 0123 45 + # . . push args + 68/push "\"a b\"/yz"/imm32 + 68/push _test-input-stream/imm32 + # . . call + e8/call write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # precondition: line->read == 0 + # . . push args + 68/push "F - test-skip-string-ignores-spaces/precondition"/imm32 + 68/push 0/imm32 + b8/copy-to-eax _test-input-stream/imm32 + 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 + # skip-string(_test-input-stream) + # . . push args + 68/push _test-input-stream/imm32 + # . . call + e8/call skip-string/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-ints-equal(line->read, 5, msg) + # . . push args + 68/push "F - test-skip-string-ignores-spaces"/imm32 + 68/push 5/imm32 + b8/copy-to-eax _test-input-stream/imm32 + 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 + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-skip-string-ignores-escapes: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # setup + # . clear-stream(_test-input-stream) + # . . push args + 68/push _test-input-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . write(_test-input-stream, "\"a\\\"b\"/yz") + # . indices: 01 2 34 56 + # . . push args + 68/push "\"a\\\"b\"/yz"/imm32 + 68/push _test-input-stream/imm32 + # . . call + e8/call write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # precondition: line->read == 0 + # . . push args + 68/push "F - test-skip-string-ignores-escapes/precondition"/imm32 + 68/push 0/imm32 + b8/copy-to-eax _test-input-stream/imm32 + 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 + # skip-string(_test-input-stream) + # . . push args + 68/push _test-input-stream/imm32 + # . . call + e8/call skip-string/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-ints-equal(line->read, 6, msg) + # . . push args + 68/push "F - test-skip-string-ignores-escapes"/imm32 + 68/push 6/imm32 + b8/copy-to-eax _test-input-stream/imm32 + 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 + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-skip-string-works-from-mid-stream: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # setup + # . clear-stream(_test-input-stream) + # . . push args + 68/push _test-input-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . write(_test-input-stream, "0 \"a\\\"b\"/yz") + # . indices: 01 2 34 56 + # . . push args + 68/push "0 \"a\\\"b\"/yz"/imm32 + 68/push _test-input-stream/imm32 + # . . call + e8/call write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # precondition: line->read == 2 + b8/copy-to-eax _test-input-stream/imm32 + c7 0/subop/copy 1/mod/*+disp8 0/rm32/eax . . . . 4/disp8 2/imm32 # copy to *(eax+4) + # skip-string(_test-input-stream) + # . . push args + 68/push _test-input-stream/imm32 + # . . call + e8/call skip-string/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-ints-equal(line->read, 8, msg) + # . . push args + 68/push "F - test-skip-string-works-from-mid-stream"/imm32 + 68/push 8/imm32 + b8/copy-to-eax _test-input-stream/imm32 + 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 + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +skip-string-in-slice: # curr : (address byte), end : (address byte) -> new_curr/eax + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 51/push-ecx + 52/push-edx + 53/push-ebx + # ecx = curr + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . 1/r32/ecx 8/disp8 . # copy *(ebp+8) to ecx + # edx = end + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . 2/r32/edx 0xc/disp8 . # copy *(ebp+12) to edx + # eax = 0 + 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax + # skip initial dquote + 41/increment-ecx +$skip-string-in-slice:loop: + # if (curr >= end) return curr + 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx + 73/jump-if-greater-unsigned-or-equal $skip-string-in-slice:return-curr/disp8 + # AL = *curr + 8a/copy-byte 0/mod/indirect 1/rm32/ecx . . . 0/r32/AL . . # copy byte at *ecx to AL +$skip-string-in-slice:dquote: + # if (eax == '"') break + 3d/compare-eax-and 0x22/imm32/double-quote + 74/jump-if-equal $skip-string-in-slice:break/disp8 +$skip-string-in-slice:check-for-escape: + # if (eax == '\') escape next char + 3d/compare-eax-and 0x5c/imm32/backslash + 75/jump-if-not-equal $skip-string-in-slice:continue/disp8 +$skip-string-in-slice:escape: + 41/increment-ecx +$skip-string-in-slice:continue: + # ++curr + 41/increment-ecx + eb/jump $skip-string-in-slice:loop/disp8 +$skip-string-in-slice:break: + # skip final dquote + 41/increment-ecx +$skip-string-in-slice:return-curr: + # return curr + 89/copy 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # copy ecx to eax +$skip-string-in-slice:end: + # . restore registers + 5b/pop-to-ebx + 5a/pop-to-edx + 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-skip-string-in-slice: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # setup: (eax..ecx) = "\"abc\" def" + b8/copy-to-eax "\"abc\" def"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # eax = skip-string-in-slice(eax, ecx) + # . . push args + 51/push-ecx + 50/push-eax + # . . call + e8/call skip-string-in-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-ints-equal(ecx-eax, 4, msg) # number of chars remaining after the string literal + # . . push args + 68/push "F - test-skip-string-in-slice"/imm32 + 68/push 4/imm32 + # . . push ecx-eax + 29/subtract 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # subtract eax from ecx + 51/push-ecx + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-skip-string-in-slice-ignores-spaces: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # setup: (eax..ecx) = "\"a b\"/yz" + b8/copy-to-eax "\"a b\"/yz"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # eax = skip-string-in-slice(eax, ecx) + # . . push args + 51/push-ecx + 50/push-eax + # . . call + e8/call skip-string-in-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-ints-equal(ecx-eax, 3, msg) # number of chars remaining after the string literal + # . . push args + 68/push "F - test-skip-string-in-slice-ignores-spaces"/imm32 + 68/push 3/imm32 + # . . push ecx-eax + 29/subtract 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # subtract eax from ecx + 51/push-ecx + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-skip-string-in-slice-ignores-escapes: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # setup: (eax..ecx) = "\"a\\\"b\"/yz" + b8/copy-to-eax "\"a\\\"b\"/yz"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # eax = skip-string-in-slice(eax, ecx) + # . . push args + 51/push-ecx + 50/push-eax + # . . call + e8/call skip-string-in-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-ints-equal(ecx-eax, 3, msg) # number of chars remaining after the string literal + # . . push args + 68/push "F - test-skip-string-in-slice-ignores-escapes"/imm32 + 68/push 3/imm32 + # . . push ecx-eax + 29/subtract 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # subtract eax from ecx + 51/push-ecx + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-skip-string-in-slice-stops-at-end: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # setup: (eax..ecx) = "\"abc" # unbalanced dquote + b8/copy-to-eax "\"abc"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # eax = skip-string-in-slice(eax, ecx) + # . . push args + 51/push-ecx + 50/push-eax + # . . call + e8/call skip-string-in-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-ints-equal(ecx-eax, 0, msg) # skipped to end of slice + # . . push args + 68/push "F - test-skip-string-in-slice-stops-at-end"/imm32 + 68/push 0/imm32 + # . . push ecx-eax + 29/subtract 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # subtract eax from ecx + 51/push-ecx + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +# update line->read to ')' +# line->read ends at ')' +skip-until-close-paren: # line : (address stream) + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 50/push-eax + 51/push-ecx + 52/push-edx + # ecx = line + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . 1/r32/ecx 8/disp8 . # copy *(ebp+8) to ecx + # eax = skip-until-close-paren-in-slice(&line->data[line->read], &line->data[line->write]) + # . . push &line->data[line->write] + 8b/copy 1/mod/*+disp8 1/rm32/ecx . . 2/r32/edx 8/disp8 . # copy *(ecx+8) to edx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 1/base/ecx 2/index/edx . 2/r32/edx 0xc/disp8 . # copy ecx+edx+12 to edx + 52/push-edx + # . . push &line->data[line->read] + 8b/copy 1/mod/*+disp8 1/rm32/ecx . . 2/r32/edx 4/disp8 . # copy *(ecx+4) to edx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 1/base/ecx 2/index/edx . 2/r32/edx 0xc/disp8 . # copy ecx+edx+12 to edx + 52/push-edx + # . . call + e8/call skip-until-close-paren-in-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # line->read = eax - line->data + 29/subtract 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # subtract ecx from eax + 2d/subtract-from-eax 0xc/imm32 + 89/copy 1/mod/*+disp8 1/rm32/ecx . . 0/r32/eax 4/disp8 . # copy eax to *(ecx+4) +$skip-until-close-paren:end: + # . restore registers + 5a/pop-to-edx + 59/pop-to-ecx + 58/pop-to-eax + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-skip-until-close-paren: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # setup + # . clear-stream(_test-input-stream) + # . . push args + 68/push _test-input-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . write(_test-input-stream, "*(abc) def") + # . indices: 0123 45 + # . . push args + 68/push "*(abc) def"/imm32 + 68/push _test-input-stream/imm32 + # . . call + e8/call write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # precondition: line->read == 0 + # . . push args + 68/push "F - test-skip-until-close-paren/precondition"/imm32 + 68/push 0/imm32 + b8/copy-to-eax _test-input-stream/imm32 + 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 + # skip-until-close-paren(_test-input-stream) + # . . push args + 68/push _test-input-stream/imm32 + # . . call + e8/call skip-until-close-paren/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-ints-equal(line->read, 5, msg) + # . . push args + 68/push "F - test-skip-until-close-paren"/imm32 + 68/push 5/imm32 + b8/copy-to-eax _test-input-stream/imm32 + 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 + # . 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-close-paren-ignores-spaces: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # setup + # . clear-stream(_test-input-stream) + # . . push args + 68/push _test-input-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . write(_test-input-stream, "*(a b)/yz") + # . . push args + 68/push "*(a b)/yz"/imm32 + 68/push _test-input-stream/imm32 + # . . call + e8/call write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # precondition: line->read == 0 + # . . push args + 68/push "F - test-skip-until-close-paren-ignores-spaces/precondition"/imm32 + 68/push 0/imm32 + b8/copy-to-eax _test-input-stream/imm32 + 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 + # skip-until-close-paren(_test-input-stream) + # . . push args + 68/push _test-input-stream/imm32 + # . . call + e8/call skip-until-close-paren/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-ints-equal(line->read, 5, msg) + # . . push args + 68/push "F - test-skip-until-close-paren-ignores-spaces"/imm32 + 68/push 5/imm32 + b8/copy-to-eax _test-input-stream/imm32 + 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 + # . 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-close-paren-works-from-mid-stream: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # setup + # . clear-stream(_test-input-stream) + # . . push args + 68/push _test-input-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . write(_test-input-stream, "0 *(a b)/yz") + # . . push args + 68/push "0 *(a b)/yz"/imm32 + 68/push _test-input-stream/imm32 + # . . call + e8/call write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # precondition: _test-input-stream->read == 2 + b8/copy-to-eax _test-input-stream/imm32 + c7 0/subop/copy 1/mod/*+disp8 0/rm32/eax . . . . 4/disp8 2/imm32 # copy to *(eax+4) + # skip-until-close-paren(_test-input-stream) + # . . push args + 68/push _test-input-stream/imm32 + # . . call + e8/call skip-until-close-paren/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-ints-equal(_test-input-stream->read, 7, msg) + # . . push args + 68/push "F - test-skip-until-close-paren-works-from-mid-stream"/imm32 + 68/push 7/imm32 + b8/copy-to-eax _test-input-stream/imm32 + 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 + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +skip-until-close-paren-in-slice: # curr : (address byte), end : (address byte) -> new_curr/eax + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 51/push-ecx + 52/push-edx + # ecx = curr + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . 1/r32/ecx 8/disp8 . # copy *(ebp+8) to ecx + # edx = end + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . 2/r32/edx 0xc/disp8 . # copy *(ebp+12) to edx + # eax = 0 + 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax + # skip initial dquote + 41/increment-ecx +$skip-until-close-paren-in-slice:loop: + # if (curr >= end) break + 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx + 73/jump-if-greater-unsigned-or-equal $skip-until-close-paren-in-slice:break/disp8 + # AL = *curr + 8a/copy-byte 0/mod/indirect 1/rm32/ecx . . . 0/r32/AL . . # copy byte at *ecx to AL +$skip-until-close-paren-in-slice:check-close: + # if (eax == ')') break + 3d/compare-eax-and 0x29/imm32/close-paren + 74/jump-if-equal $skip-until-close-paren-in-slice:break/disp8 + # ++curr + 41/increment-ecx + eb/jump $skip-until-close-paren-in-slice:loop/disp8 +$skip-until-close-paren-in-slice:break: + # return curr + 89/copy 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # copy ecx to eax +$skip-until-close-paren-in-slice:end: + # . restore registers + 5a/pop-to-edx + 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-skip-until-close-paren-in-slice: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # setup: (eax..ecx) = "*(abc) def" + b8/copy-to-eax "*(abc) def"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # eax = skip-until-close-paren-in-slice(eax, ecx) + # . . push args + 51/push-ecx + 50/push-eax + # . . call + e8/call skip-until-close-paren-in-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-ints-equal(ecx-eax, 5, msg) # eax is at the ')' + # . . push args + 68/push "F - test-skip-until-close-paren-in-slice"/imm32 + 68/push 5/imm32 + # . . push ecx-eax + 29/subtract 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # subtract eax from ecx + 51/push-ecx + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . 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-close-paren-in-slice-ignores-spaces: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # setup: (eax..ecx) = "*(a b)/yz" + b8/copy-to-eax "*(a b)/yz"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # eax = skip-until-close-paren-in-slice(eax, ecx) + # . . push args + 51/push-ecx + 50/push-eax + # . . call + e8/call skip-until-close-paren-in-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-ints-equal(ecx-eax, 4, msg) # eax is at the ')' + # . . push args + 68/push "F - test-skip-until-close-paren-in-slice-ignores-spaces"/imm32 + 68/push 4/imm32 + # . . push ecx-eax + 29/subtract 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # subtract eax from ecx + 51/push-ecx + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . 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-close-paren-in-slice-stops-at-end: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # setup: (eax..ecx) = "*(abc" # unbalanced dquote + b8/copy-to-eax "*(abc"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # eax = skip-until-close-paren-in-slice(eax, ecx) + # . . push args + 51/push-ecx + 50/push-eax + # . . call + e8/call skip-until-close-paren-in-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-ints-equal(ecx-eax, 0, msg) # skipped to end of slice + # . . push args + 68/push "F - test-skip-until-close-paren-in-slice-stops-at-end"/imm32 + 68/push 0/imm32 + # . . push ecx-eax + 29/subtract 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # subtract eax from ecx + 51/push-ecx + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +# . . vim:nowrap:textwidth=0 diff --git a/074write-stream-data.subx b/074write-stream-data.subx new file mode 100644 index 00000000..d5d901b4 --- /dev/null +++ b/074write-stream-data.subx @@ -0,0 +1,116 @@ +== 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 + +# write an entire stream's contents to a buffered-file +# ways to do this: +# - construct a 'maximal slice' and pass it to write-slice-buffered +# - flush the buffered-file and pass the stream directly to its fd (disabling buffering) +# we'll go with the first way for now +write-stream-data: # f : (address buffered-file), s : (address stream) -> + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 50/push-eax + 51/push-ecx + 56/push-esi + # esi = s + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 0xc/disp8 . # copy *(ebp+12) to esi + # var slice/ecx = {s->data, s->data + s->write} + # . push s->data + s->write + 8b/copy 0/mod/indirect 6/rm32/esi . . . 0/r32/eax . . # copy *esi to eax + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/esi 0/index/eax . 0/r32/eax 0xc/disp8 . # copy esi+eax+12 to eax + 50/push-eax + # . push s->data + 8d/copy-address 1/mod/*+disp8 6/rm32/esi . . . 0/r32/eax 0xc/disp8 . # copy esi+12 to eax + 50/push-eax + # . ecx = esp + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # write-slice-buffered(f, slice) + # . . push args + 51/push-ecx + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) + # . . call + e8/call write-slice-buffered/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +$write-stream-data:end: + # . restore locals + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . restore registers + 5e/pop-to-esi + 59/pop-to-ecx + 58/pop-to-eax + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-write-stream-data: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # setup + # . clear-stream(_test-output-stream) + # . . push args + 68/push _test-output-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . clear-stream(_test-output-buffered-file+4) + # . . push args + b8/copy-to-eax _test-output-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-input-stream) + # . . push args + 68/push _test-input-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # initialize input + # . write(_test-input-stream, "abcd") + # . . push args + 68/push "abcd"/imm32 + 68/push _test-input-stream/imm32 + # . . call + e8/call write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # write-stream-data(_test-output-buffered-file, _test-input-stream) + # . . push args + 68/push _test-input-stream/imm32 + 68/push _test-output-buffered-file/imm32 + # . . call + e8/call write-stream-data/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check that the write happened as expected + # . flush(_test-output-buffered-file) + # . . push args + 68/push _test-output-buffered-file/imm32 + # . . call + e8/call flush/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . check-stream-equal(_test-output-stream, "abcd", msg) + # . . push args + 68/push "F - test-write-stream-data"/imm32 + 68/push "abcd"/imm32 + 68/push _test-output-stream/imm32 + # . . call + e8/call check-stream-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return diff --git a/075print-int-decimal.subx b/075print-int-decimal.subx new file mode 100644 index 00000000..f7898fe9 --- /dev/null +++ b/075print-int-decimal.subx @@ -0,0 +1,307 @@ +# Helper to print an int32 in decimal. + +== 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 + +print-int32-decimal: # out : (address stream), n : int32 + # works by generating characters from lowest to highest and pushing them + # to the stack, before popping them one by one into the stream + # + # pseudocode: + # push sentinel + # eax = abs(n) + # while true + # sign-extend eax into edx + # eax, edx = eax/10, eax%10 + # edx += '0' + # push edx + # if (eax == 0) break + # if n < 0 + # push '-' + # w = out->write + # curr = &out->data[out->write] + # max = &out->data[out->length] + # while true + # pop into eax + # if (eax == sentinel) break + # if (curr >= max) abort + # *curr = AL + # ++curr + # ++w + # out->write = w + # (based on K&R itoa: https://en.wikibooks.org/wiki/C_Programming/stdlib.h/itoa) + # (this pseudocode contains registers because operations like division + # require specific registers in x86) + # + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 50/push-eax + 51/push-ecx + 52/push-edx + 53/push-ebx + 57/push-edi + # ten/ecx = 10 + b9/copy-to-ecx 0xa/imm32 + # push sentinel + 68/push 0/imm32/sentinel + # eax = abs(n) + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 0xc/disp8 . # copy *(ebp+12) to eax + 3d/compare-eax-with 0/imm32 + 7d/jump-if-greater-or-equal $print-int32-decimal:read-loop/disp8 +$print-int32-decimal:negative: + f7 3/subop/negate 3/mod/direct 0/rm32/eax . . . . . . # negate eax +$print-int32-decimal:read-loop: + # eax, edx = eax / 10, eax % 10 + 99/sign-extend-eax-into-edx + f7 7/subop/idiv 3/mod/direct 1/rm32/ecx . . . . . . # divide edx:eax by ecx, storing quotient in eax and remainder in edx + # edx += '0' + 81 0/subop/add 3/mod/direct 2/rm32/edx . . . . . 0x30/imm32 # add to edx + # push edx + 52/push-edx + # if (eax == 0) break + 3d/compare-eax-and 0/imm32 + 7f/jump-if-greater $print-int32-decimal:read-loop/disp8 +$print-int32-decimal:read-break: + # if (n < 0) push('-') + 81 7/subop/compare 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 0/imm32 # compare *(ebp+12) + 7d/jump-if-greater-or-equal $print-int32-decimal:write/disp8 +$print-int32-decimal:push-negative: + 68/push 0x2d/imm32/- +$print-int32-decimal:write: + # edi = out + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 7/r32/edi 8/disp8 . # copy *(ebp+8) to edi + # w/edx = out->write + 8b/copy 0/mod/indirect 7/rm32/edi . . . 2/r32/edx . . # copy *edi to edx + # curr/ecx = &out->data[out->write] + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/edi 2/index/edx . 1/r32/ecx 0xc/disp8 . # copy ebx+edx+12 to ecx + # max/ebx = &out->data[out->length] + 8b/copy 1/mod/*+disp8 7/rm32/edi . . . 3/r32/ebx 8/disp8 . # copy *(edi+8) to ebx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/edi 3/index/ebx . 3/r32/ebx 0xc/disp8 . # copy edi+ebx+12 to ebx +$print-int32-decimal:write-loop: + # pop into eax + 58/pop-to-eax + # if (eax == sentinel) break + 3d/compare-eax-and 0/imm32/sentinel + 74/jump-if-equal $print-int32-decimal:write-break/disp8 + # if (curr >= max) abort + 39/compare 3/mod/direct 1/rm32/ecx . . . 3/r32/ebx . . # compare ecx with ebx + 73/jump-if-greater-or-equal-unsigned $print-int32-decimal:abort/disp8 +$print-int32-decimal:write-char: + # *curr = AL + 88/copy-byte 0/mod/indirect 1/rm32/ecx . . . 0/r32/AL . . # copy AL to byte at *ecx + # ++curr + 41/increment-ecx + # ++w + 42/increment-edx + eb/jump $print-int32-decimal:write-loop/disp8 +$print-int32-decimal:write-break: + # out->write = w + 89/copy 0/mod/indirect 7/rm32/edi . . . 2/r32/edx . . # copy edx to *edi +$print-int32-decimal:end: + # . restore registers + 5f/pop-to-edi + 5b/pop-to-ebx + 5a/pop-to-edx + 59/pop-to-ecx + 58/pop-to-eax + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +$print-int32-decimal:abort: + # . _write(2/stderr, error) + # . . push args + 68/push "print-int32-decimal: out of space\n"/imm32 + 68/push 2/imm32/stderr + # . . call + e8/call _write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . syscall(exit, 1) + bb/copy-to-ebx 1/imm32 + b8/copy-to-eax 1/imm32/exit + cd/syscall 0x80/imm8 + # never gets here + +test-print-int32-decimal: + # - check that a single-digit number converts correctly + # 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 + # print-int32-decimal(_test-stream, 9) + # . . push args + 68/push 9/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call print-int32-decimal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-stream-equal(_test-stream, "9", msg) + # . . push args + 68/push "F - test-print-int32-decimal"/imm32 + 68/push "9"/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call check-stream-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . end + c3/return + +test-print-int32-decimal-zero: + # - check that 0 converts correctly + # 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 + # print-int32-decimal(_test-stream, 0) + # . . push args + 68/push 0/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call print-int32-decimal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-stream-equal(_test-stream, "0", msg) + # . . push args + 68/push "F - test-print-int32-decimal-zero"/imm32 + 68/push "0"/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call check-stream-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . end + c3/return + +test-print-int32-decimal-multiple-digits: + # - check that a multi-digit number converts correctly + # 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 + # print-int32-decimal(_test-stream, 10) + # . . push args + 68/push 0xa/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call print-int32-decimal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-stream-equal(_test-stream, "10", msg) + # . . push args + 68/push "F - test-print-int32-decimal-multiple-digits"/imm32 + 68/push "10"/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call check-stream-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . end + c3/return + +test-print-int32-decimal-negative: + # - check that a negative single-digit number converts correctly + # 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 + # print-int32-decimal(_test-stream, -9) + # . . push args + 68/push -9/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call print-int32-decimal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # dump _test-stream {{{ +#? # . write(2/stderr, "^") +#? # . . push args +#? 68/push "^"/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # . write-stream(2/stderr, _test-stream) +#? # . . push args +#? 68/push _test-stream/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write-stream/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # . write(2/stderr, "$\n") +#? # . . push args +#? 68/push "$\n"/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # }}} + # check-stream-equal(_test-stream, "-9", msg) + # . . push args + 68/push "F - test-print-int32-decimal-negative"/imm32 + 68/push "-9"/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call check-stream-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . end + c3/return + +test-print-int32-decimal-negative-multiple-digits: + # - check that a multi-digit number converts correctly + # 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 + # print-int32-decimal(_test-stream, -10) + # . . push args + 68/push -0xa/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call print-int32-decimal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-stream-equal(_test-stream, "-10", msg) + # . . push args + 68/push "F - test-print-int32-decimal-negative-multiple-digits"/imm32 + 68/push "-10"/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call check-stream-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 diff --git a/076next-word.subx b/076next-word.subx new file mode 100644 index 00000000..efd99a64 --- /dev/null +++ b/076next-word.subx @@ -0,0 +1,252 @@ +# Tokenize by whitespace. + +== 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 + +# (re)compute the bounds of the next word in the line +# return empty string on reaching end of file +next-word: # line : (address stream byte), out : (address slice) + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 50/push-eax + 51/push-ecx + 56/push-esi + 57/push-edi + # esi = line + 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 + # skip-chars-matching(line, ' ') + # . . push args + 68/push 0x20/imm32/space + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) + # . . call + e8/call skip-chars-matching/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +$next-word:check0: + # if (line->read >= line->write) clear out and return + # . eax = line->read + 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 0/r32/eax 4/disp8 . # copy *(esi+4) to eax + # . if (eax < line->write) goto next check + 3b/compare 0/mod/indirect 6/rm32/esi . . . 0/r32/eax . . # compare eax with *esi + 7c/jump-if-lesser $next-word:check-for-comment/disp8 + # . return out = {0, 0} + c7 0/subop/copy 0/mod/direct 7/rm32/edi . . . . . 0/imm32 # copy to *edi + c7 0/subop/copy 1/mod/*+disp8 7/rm32/edi . . . . 4/disp8 0/imm32 # copy to *(edi+4) + eb/jump $next-word:end/disp8 +$next-word:check-for-comment: + # out->start = &line->data[line->read] + 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy *(esi+4) to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/esi 1/index/ecx . 0/r32/eax 0xc/disp8 . # copy esi+ecx+12 to eax + 89/copy 0/mod/indirect 7/rm32/edi . . . 0/r32/eax . . # copy eax to *edi + # if (line->data[line->read] == '#') out->end = &line->data[line->write]), skip rest of stream and return + # . eax = line->data[line->read] + 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax + 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/esi 1/index/ecx . 0/r32/AL 0xc/disp8 . # copy byte at *(esi+ecx+12) to AL + # . compare + 3d/compare-eax-and 0x23/imm32/pound + 75/jump-if-not-equal $next-word:regular-word/disp8 +$next-word:comment: + # . out->end = &line->data[line->write] + 8b/copy 0/mod/indirect 6/rm32/esi . . . 0/r32/eax . . # copy *esi to eax + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/esi 0/index/eax . 0/r32/eax 0xc/disp8 . # copy esi+eax+12 to eax + 89/copy 1/mod/*+disp8 7/rm32/edi . . . 0/r32/eax 4/disp8 . # copy eax to *(edi+4) + # . line->read = line->write + 89/copy 1/mod/*+disp8 6/rm32/esi . . . 0/r32/eax 4/disp8 . # copy eax to *(esi+4) + # . return + eb/jump $next-word:end/disp8 +$next-word:regular-word: + # otherwise skip-chars-not-matching-whitespace(line) # including trailing newline + # . . push args + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) + # . . call + e8/call skip-chars-not-matching-whitespace/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # out->end = &line->data[line->read] + 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy *(esi+4) to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/esi 1/index/ecx . 0/r32/eax 0xc/disp8 . # copy esi+ecx+12 to eax + 89/copy 1/mod/*+disp8 7/rm32/edi . . . 0/r32/eax 4/disp8 . # copy eax to *(edi+4) +$next-word:end: + # . restore registers + 5f/pop-to-edi + 5e/pop-to-esi + 59/pop-to-ecx + 58/pop-to-eax + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-next-word: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # 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 + # var slice/ecx = {0, 0} + 68/push 0/imm32/end + 68/push 0/imm32/start + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # write(_test-stream, " ab") + # . . push args + 68/push " ab"/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # next-word(_test-stream, slice) + # . . push args + 51/push-ecx + 68/push _test-stream/imm32 + # . . call + e8/call next-word/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-ints-equal(slice->start - _test-stream->data, 2, msg) + # . check-ints-equal(slice->start - _test-stream, 14, msg) + # . . push args + 68/push "F - test-next-word: start"/imm32 + 68/push 0xe/imm32 + # . . push slice->start - _test-stream + 8b/copy 0/mod/indirect 1/rm32/ecx . . . 0/r32/eax . . # copy *ecx to eax + 81 5/subop/subtract 3/mod/direct 0/rm32/eax . . . . . _test-stream/imm32 # subtract from eax + 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 + # check-ints-equal(slice->end - _test-stream->data, 4, msg) + # . check-ints-equal(slice->end - _test-stream, 16, msg) + # . . push args + 68/push "F - test-next-word: end"/imm32 + 68/push 0x10/imm32 + # . . push slice->end - _test-stream + 8b/copy 1/mod/*+disp8 1/rm32/ecx . . . 0/r32/eax 4/disp8 . # copy *(ecx+4) to eax + 81 5/subop/subtract 3/mod/direct 0/rm32/eax . . . . . _test-stream/imm32 # subtract from eax + 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 + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-next-word-returns-whole-comment: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # 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 + # var slice/ecx = {0, 0} + 68/push 0/imm32/end + 68/push 0/imm32/start + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # 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 + # next-word(_test-stream, slice) + # . . push args + 51/push-ecx + 68/push _test-stream/imm32 + # . . call + e8/call next-word/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-ints-equal(slice->start - _test-stream->data, 2, msg) + # . check-ints-equal(slice->start - _test-stream, 14, msg) + # . . push args + 68/push "F - test-next-word-returns-whole-comment: start"/imm32 + 68/push 0xe/imm32 + # . . push slice->start - _test-stream + 8b/copy 0/mod/indirect 1/rm32/ecx . . . 0/r32/eax . . # copy *ecx to eax + 81 5/subop/subtract 3/mod/direct 0/rm32/eax . . . . . _test-stream/imm32 # subtract from eax + 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 + # check-ints-equal(slice->end - _test-stream->data, 5, msg) + # . check-ints-equal(slice->end - _test-stream, 17, msg) + # . . push args + 68/push "F - test-next-word-returns-whole-comment: end"/imm32 + 68/push 0x11/imm32 + # . . push slice->end - _test-stream + 8b/copy 1/mod/*+disp8 1/rm32/ecx . . . 0/r32/eax 4/disp8 . # copy *(ecx+4) to eax + 81 5/subop/subtract 3/mod/direct 0/rm32/eax . . . . . _test-stream/imm32 # subtract from eax + 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 + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-next-word-returns-empty-string-on-eof: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # 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 + # var slice/ecx = {0, 0} + 68/push 0/imm32/end + 68/push 0/imm32/start + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # write nothing to _test-stream + # next-word(_test-stream, slice) + # . . push args + 51/push-ecx + 68/push _test-stream/imm32 + # . . call + e8/call next-word/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-ints-equal(slice->end - slice->start, 0, msg) + # . . push args + 68/push "F - test-next-word-returns-empty-string-on-eof"/imm32 + 68/push 0/imm32 + # . . push slice->end - slice->start + 8b/copy 1/mod/*+disp8 1/rm32/ecx . . . 0/r32/eax 4/disp8 . # copy *(ecx+4) to eax + 2b/subtract 0/mod/indirect 1/rm32/ecx . . . 0/r32/eax . . # subtract *ecx from eax + 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 + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return diff --git a/077subx-words.subx b/077subx-words.subx new file mode 100644 index 00000000..5bdf196a --- /dev/null +++ b/077subx-words.subx @@ -0,0 +1,631 @@ +# Helpers for parsing SubX words, with their rules for hex, labels and metadata. + +== 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 + +has-metadata?: # word : (address slice), s : (address string) -> eax : boolean + # pseudocode: + # var twig : &slice = next-token-from-slice(word->start, word->end, '/') # skip name + # curr = twig->end + # while true + # twig = next-token-from-slice(curr, word->end, '/') + # if (twig.empty()) break + # if (slice-equal?(twig, s)) return true + # curr = twig->end + # return false + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 51/push-ecx + 52/push-edx + 56/push-esi + 57/push-edi + # esi = word + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi + # edx = word->end + 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 2/r32/edx 4/disp8 . # copy *(esi+4) to edx + # var twig/edi : (address slice) = {0, 0} + 68/push 0/imm32/end + 68/push 0/imm32/start + 89/copy 3/mod/direct 7/rm32/edi . . . 4/r32/esp . . # copy esp to edi + # next-token-from-slice(word->start, word->end, '/', twig) + # . . push args + 57/push-edi + 68/push 0x2f/imm32/slash + 52/push-edx + ff 6/subop/push 0/mod/indirect 6/rm32/esi . . . . . . # push *esi + # . . call + e8/call next-token-from-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp + # curr/ecx = twig->end + 8b/copy 1/mod/*+disp8 7/rm32/edi . . . 1/r32/ecx 4/disp8 . # copy *(edi+4) to ecx +$has-metadata?:loop: + # next-token-from-slice(curr, word->end, '/', twig) + # . . push args + 57/push-edi + 68/push 0x2f/imm32/slash + 52/push-edx + 51/push-ecx + # . . call + e8/call next-token-from-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp + # if (slice-empty?(twig)) return false + # . eax = slice-empty?(twig) + # . . push args + 57/push-edi + # . . call + e8/call slice-empty?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . if (eax != 0) return false + 3d/compare-eax-and 0/imm32 + 75/jump-if-not-equal $has-metadata?:false/disp8 + # if (slice-equal?(twig, s)) return true + # . eax = slice-equal?(twig, s) + # . . push args + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) + 57/push-edi + # . . call + e8/call slice-equal?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . if (eax != 0) return true + 3d/compare-eax-and 0/imm32 + 75/jump-if-not-equal $has-metadata?:true/disp8 + # curr = twig->end + 8b/copy 1/mod/*+disp8 7/rm32/edi . . . 1/r32/ecx 4/disp8 . # copy *(edi+4) to ecx + eb/jump $has-metadata?:loop/disp8 +$has-metadata?:true: + b8/copy-to-eax 1/imm32/true + eb/jump $has-metadata?:end/disp8 +$has-metadata?:false: + b8/copy-to-eax 0/imm32/false +$has-metadata?:end: + # . reclaim locals + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . restore registers + 5f/pop-to-edi + 5e/pop-to-esi + 5a/pop-to-edx + 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-has-metadata-true: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # (eax..ecx) = "ab/imm32" + b8/copy-to-eax "ab/imm32"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var in/esi : (address slice) = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 6/rm32/esi . . . 4/r32/esp . . # copy esp to esi + # eax = has-metadata?(esi, "imm32") + # . . push args + 68/push "imm32"/imm32 + 56/push-esi + # . . call + e8/call has-metadata?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-ints-equal(eax, 1, msg) + # . . push args + 68/push "F - test-has-metadata-true"/imm32 + 68/push 1/imm32/true + 50/push-eax + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-has-metadata-false: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # (eax..ecx) = "ab/c" + b8/copy-to-eax "ab/c"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var in/esi : (address slice) = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 6/rm32/esi . . . 4/r32/esp . . # copy esp to esi + # eax = has-metadata?(esi, "d") + # . . push args + 68/push "d"/imm32 + 56/push-esi + # . . call + e8/call has-metadata?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-ints-equal(eax, 0, msg) + # . . push args + 68/push "F - test-has-metadata-false"/imm32 + 68/push 0/imm32/false + 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 + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-has-metadata-ignore-name: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # (eax..ecx) = "a/b" + b8/copy-to-eax "a/b"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var in/esi : (address slice) = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 6/rm32/esi . . . 4/r32/esp . . # copy esp to esi + # eax = has-metadata?(esi, "a") + # . . push args + 68/push "a"/imm32 + 56/push-esi + # . . call + e8/call has-metadata?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-ints-equal(eax, 0, msg) + # . . push args + 68/push "F - test-has-metadata-ignore-name"/imm32 + 68/push 0/imm32/false + 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 + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-has-metadata-multiple-true: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # (eax..ecx) = "a/b/c" + b8/copy-to-eax "a/b/c"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var in/esi : (address slice) = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 6/rm32/esi . . . 4/r32/esp . . # copy esp to esi + # eax = has-metadata?(esi, "c") + # . . push args + 68/push "c"/imm32 + 56/push-esi + # . . call + e8/call has-metadata?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-ints-equal(eax, 1, msg) + # . . push args + 68/push "F - test-has-metadata-multiple-true"/imm32 + 68/push 1/imm32/true + 50/push-eax + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-has-metadata-multiple-false: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # (eax..ecx) = "a/b/c" + b8/copy-to-eax "a/b/c"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var in/esi : (address slice) = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 6/rm32/esi . . . 4/r32/esp . . # copy esp to esi + # eax = has-metadata?(esi, "d") + # . . push args + 68/push "d"/imm32 + 56/push-esi + # . . call + e8/call has-metadata?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-ints-equal(eax, 0, msg) + # . . push args + 68/push "F - test-has-metadata-multiple-false"/imm32 + 68/push 0/imm32/false + 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 + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +# conditions for 'valid' names that are not at risk of looking like hex numbers +# keep in sync with the rules in labels.cc +#: - if it starts with a digit, it's treated as a number. If it can't be +#: parsed as hex it will raise an error. +#: - if it starts with '-' it's treated as a number. +#: - if it starts with '0x' it's treated as a number. (redundant) +#: - if it's two characters long, it can't be a name. Either it's a hex +#: byte, or it raises an error. +is-valid-name?: # in : (address slice) -> eax : boolean + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 51/push-ecx + 56/push-esi + # esi = in + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi + # start/ecx = in->start + 8b/copy 0/mod/indirect 6/rm32/esi . . . 1/r32/ecx . . # copy *esi to ecx + # end/eax = in->end + 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 0/r32/eax 4/disp8 . # copy *(esi+4) to eax +$is-valid-name?:check0: + # if (start >= end) return false + 39/compare 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # compare ecx with eax + 73/jump-if-greater-or-equal-unsigned $is-valid-name?:false/disp8 +$is-valid-name?:check1: + # eax -= ecx + 29/subtract 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # subtract ecx from eax + # if (eax == 2) return false + 3d/compare-eax-and 2/imm32 + 74/jump-if-equal $is-valid-name?:false/disp8 +$is-valid-name?:check2: + # c/eax = *ecx + 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax + 8a/copy-byte 0/mod/indirect 1/rm32/ecx . . . 0/r32/AL . . # copy byte at *ecx to AL + # if (c == "-") return false + 3d/compare-eax-and 2d/imm32/- + 74/jump-if-equal $is-valid-name?:false/disp8 +$is-valid-name?:check3a: + # if (c < "0") return true + 3d/compare-eax-with 30/imm32/0 + 7c/jump-if-lesser $is-valid-name?:true/disp8 +$is-valid-name?:check3b: + # if (c > "9") return true + 3d/compare-eax-with 39/imm32/9 + 7f/jump-if-greater $is-valid-name?:true/disp8 +$is-valid-name?:false: + # return false + b8/copy-to-eax 0/imm32/false + eb/jump $is-valid-name?:end/disp8 +$is-valid-name?:true: + # return true + b8/copy-to-eax 1/imm32/true +$is-valid-name?:end: + # . restore registers + 5e/pop-to-esi + 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-is-valid-name-digit-prefix: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # (eax..ecx) = "34" + b8/copy-to-eax "34"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var slice/ecx = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # eax = is-valid-name?(slice) + # . . push args + 51/push-ecx + # . . call + e8/call is-valid-name?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-ints-equal(eax, 0, msg) + # . . push args + 68/push "F - test-is-valid-name-digit-prefix"/imm32 + 68/push 0/imm32/false + 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 + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-is-valid-name-negative-prefix: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # (eax..ecx) = "-0x34" + b8/copy-to-eax "-0x34"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var slice/ecx = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # eax = is-valid-name?(slice) + # . . push args + 51/push-ecx + # . . call + e8/call is-valid-name?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-ints-equal(eax, 0, msg) + # . . push args + 68/push "F - test-is-valid-name-negative-prefix"/imm32 + 68/push 0/imm32/false + 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 + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-is-valid-name-0x-prefix: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # (eax..ecx) = "0x34" + b8/copy-to-eax "0x34"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var slice/ecx = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # eax = is-valid-name?(slice) + # . . push args + 51/push-ecx + # . . call + e8/call is-valid-name?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-ints-equal(eax, 0, msg) + # . . push args + 68/push "F - test-is-valid-name-0x-prefix"/imm32 + 68/push 0/imm32/false + 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 + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-is-valid-name-starts-with-pre-digit: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # (eax..ecx) = "/03" + b8/copy-to-eax "/03"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var slice/ecx = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # eax = is-valid-name?(slice) + # . . push args + 51/push-ecx + # . . call + e8/call is-valid-name?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-ints-equal(eax, 1, msg) + # . . push args + 68/push "F - test-is-valid-name-starts-with-pre-digit"/imm32 + 68/push 1/imm32/true + 50/push-eax + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-is-valid-name-starts-with-post-digit: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # (eax..ecx) = "q34" + b8/copy-to-eax "q34"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var slice/ecx = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # eax = is-valid-name?(slice) + # . . push args + 51/push-ecx + # . . call + e8/call is-valid-name?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-ints-equal(eax, 1, msg) + # . . push args + 68/push "F - test-is-valid-name-starts-with-post-digit"/imm32 + 68/push 1/imm32/true + 50/push-eax + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-is-valid-name-starts-with-digit: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # (eax..ecx) = "0x34" + b8/copy-to-eax "0x34"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var slice/ecx = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # eax = is-valid-name?(slice) + # . . push args + 51/push-ecx + # . . call + e8/call is-valid-name?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-ints-equal(eax, 0, msg) + # . . push args + 68/push "F - test-is-valid-name-starts-with-digit"/imm32 + 68/push 0/imm32/false + 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 + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +is-label?: # word : (address slice) -> eax : boolean + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 51/push-ecx + # ecx = word + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 8/disp8 . # copy *(ebp+8) to ecx + # ecx = word->end + 8b/copy 1/mod/*+disp8 1/rm32/ecx . . . 1/r32/ecx 4/disp8 . # copy *(ecx+4) to ecx + # return *(word->end - 1) == ':' + # . eax = 0 + 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax + # . eax = *((char *) word->end - 1) + 8a/copy-byte 1/mod/*+disp8 1/rm32/ecx . . . 0/r32/AL -1/disp8 . # copy byte at *(ecx-1) to AL + # . return (eax == ':') + 3d/compare-eax-and 0x3a/imm32/colon + b8/copy-to-eax 1/imm32/true + 74/jump-if-equal $is-label?:end/disp8 + b8/copy-to-eax 0/imm32/false +$is-label?: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-is-label?: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp +$test-is-label?:true: + # (eax..ecx) = "AAA:" + b8/copy-to-eax "AAA:"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var slice/ecx = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # is-label?(slice/ecx) + # . . push args + 51/push-ecx + # . . call + e8/call is-label?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-ints-equal(eax, 1, msg) + # . . push args + 68/push "F - test-is-label?:true"/imm32 + 68/push 1/imm32 + 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-is-label?:false: + # (eax..ecx) = "AAA" + b8/copy-to-eax "AAA"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var slice/ecx = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # is-label?(slice/ecx) + # . . push args + 51/push-ecx + # . . call + e8/call is-label?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-ints-equal(eax, 0, msg) + # . . push args + 68/push "F - test-is-label?:false"/imm32 + 68/push 0/imm32 + 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 + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +# . . vim:nowrap:textwidth=0 diff --git a/078emit-hex.subx b/078emit-hex.subx new file mode 100644 index 00000000..73912ea0 --- /dev/null +++ b/078emit-hex.subx @@ -0,0 +1,249 @@ +== 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 + +# print 'n' in hex in 'width' bytes in lower-endian order, with a space after every byte +emit-hex: # out : (address buffered-file), n : int, width : int -> + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 50/push-eax + 51/push-ecx + 52/push-edx + 53/push-ebx + 57/push-edi + # edi = out + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 7/r32/edi 8/disp8 . # copy *(ebp+8) to edi + # ebx = n + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 3/r32/ebx 0xc/disp8 . # copy *(ebp+12) to ebx + # edx = width + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 2/r32/edx 0x10/disp8 . # copy *(ebp+16) to edx + # var curr/ecx = 0 + 31/xor 3/mod/direct 1/rm32/ecx . . . 1/r32/ecx . . # clear ecx +$emit-hex:loop: + # if (curr >= width) break + 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx + 7d/jump-if-greater-or-equal $emit-hex:end/disp8 + # print-byte-buffered(out, ebx) + # . . push args + 53/push-ebx + 57/push-edi + # . . call + e8/call print-byte-buffered/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # write-byte-buffered(out, ' ') + # . . push args + 68/push 0x20/imm32/space + 57/push-edi + # . . call + e8/call write-byte-buffered/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # ebx = ebx >> 8 + c1/shift 5/subop/logic-right 3/mod/direct 3/rm32/ebx . . . . . 8/imm8 # shift ebx right by 8 bits, while padding zeroes +$emit-hex:continue: + # ++curr + 41/increment-ecx + eb/jump $emit-hex:loop/disp8 +$emit-hex:end: + # . restore registers + 5f/pop-to-edi + 5b/pop-to-ebx + 5a/pop-to-edx + 59/pop-to-ecx + 58/pop-to-eax + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-emit-hex-single-byte: + # setup + # . clear-stream(_test-output-stream) + # . . push args + 68/push _test-output-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . clear-stream(_test-output-buffered-file+4) + # . . push args + b8/copy-to-eax _test-output-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 + # emit-hex(_test-output-buffered-file, 0xab, 1) + # . . push args + 68/push 1/imm32 + 68/push 0xab/imm32 + 68/push _test-output-buffered-file/imm32 + # . . call + e8/call emit-hex/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # flush(_test-output-buffered-file) + # . . push args + 68/push _test-output-buffered-file/imm32 + # . . call + e8/call flush/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-ints-equal(*_test-output-stream->data, 'ab ', msg) + # . . push args + 68/push "F - test-emit-hex-single-byte"/imm32 + 68/push 0x206261/imm32 + # . . push *_test-output-stream->data + 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 + # . end + c3/return + +test-emit-hex-multiple-byte: + # setup + # . clear-stream(_test-output-stream) + # . . push args + 68/push _test-output-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . clear-stream(_test-output-buffered-file+4) + # . . push args + b8/copy-to-eax _test-output-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 + # emit-hex(_test-output-buffered-file, 0x1234, 2) + # . . push args + 68/push 2/imm32 + 68/push 0x1234/imm32 + 68/push _test-output-buffered-file/imm32 + # . . call + e8/call emit-hex/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # flush(_test-output-buffered-file) + # . . push args + 68/push _test-output-buffered-file/imm32 + # . . call + e8/call flush/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-stream-equal(_test-output-stream, "34 12 ", msg) + # . . push args + 68/push "F - test-emit-hex-multiple-byte/1"/imm32 + 68/push "34 12 "/imm32 + 68/push _test-output-stream/imm32 + # . . call + e8/call check-stream-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . end + c3/return + +test-emit-hex-zero-pad: + # setup + # . clear-stream(_test-output-stream) + # . . push args + 68/push _test-output-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . clear-stream(_test-output-buffered-file+4) + # . . push args + b8/copy-to-eax _test-output-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 + # emit-hex(_test-output-buffered-file, 0xab, 2) + # . . push args + 68/push 2/imm32 + 68/push 0xab/imm32 + 68/push _test-output-buffered-file/imm32 + # . . call + e8/call emit-hex/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # flush(_test-output-buffered-file) + # . . push args + 68/push _test-output-buffered-file/imm32 + # . . call + e8/call flush/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check(_test-output-stream->data == 'ab 00 ') + # . . push args + 68/push "F - test-emit-hex-zero-pad/1"/imm32 + 68/push "ab 00 "/imm32 + 68/push _test-output-stream/imm32 + # . . call + e8/call check-stream-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . end + c3/return + +test-emit-hex-negative: + # setup + # . clear-stream(_test-output-stream) + # . . push args + 68/push _test-output-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . clear-stream(_test-output-buffered-file+4) + # . . push args + b8/copy-to-eax _test-output-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 + # emit-hex(_test-output-buffered-file, -1, 2) + # . . push args + 68/push 2/imm32 + 68/push -1/imm32 + 68/push _test-output-buffered-file/imm32 + # . . call + e8/call emit-hex/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # flush(_test-output-buffered-file) + # . . push args + 68/push _test-output-buffered-file/imm32 + # . . call + e8/call flush/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-stream-equal(_test-output-stream == "ff ff ") + # . . push args + 68/push "F - test-emit-hex-negative/1"/imm32 + 68/push "ff ff "/imm32 + 68/push _test-output-stream/imm32 + # . . call + e8/call check-stream-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 diff --git a/079emit.subx b/079emit.subx new file mode 100644 index 00000000..b0c06de6 --- /dev/null +++ b/079emit.subx @@ -0,0 +1,484 @@ +== 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 + +# If datum of 'word' is not a valid name, it must be a hex int. Parse and print +# it in 'width' bytes of hex, least significant first. +# Otherwise just print the entire word including metadata. +# Always print a trailing space. +emit: # out : (address buffered-file), word : (address slice), width : int -> + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 50/push-eax + 56/push-esi + 57/push-edi + # esi = word + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 0xc/disp8 . # copy *(ebp+12) to esi + # var name/edi : (address slice) = {0, 0} + 68/push 0/imm32/end + 68/push 0/imm32/start + 89/copy 3/mod/direct 7/rm32/edi . . . 4/r32/esp . . # copy esp to edi + # datum = next-token-from-slice(word->start, word->end, '/') + # . . push args + 57/push-edi + 68/push 0x2f/imm32/slash + ff 6/subop/push 1/mod/*+disp8 6/rm32/esi . . . . 4/disp8 . # push *(esi+4) + ff 6/subop/push 0/mod/indirect 6/rm32/esi . . . . . . # push *esi + # . . call + e8/call next-token-from-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp + # if (is-valid-name?(datum)) write-slice-buffered(out, word) and return + # . eax = is-valid-name?(name) + # . . push args + 57/push-edi + # . . call + e8/call is-valid-name?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . if (eax != 0) + 3d/compare-eax-and 0/imm32 + 74/jump-if-equal $emit:hex-int/disp8 +$emit:name: + # . write-slice-buffered(out, word) + # . . push args + 56/push-esi + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) + # . . call + e8/call write-slice-buffered/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . write-buffered(out, " ") + # . . push args + 68/push Space/imm32 + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) + # . . call + e8/call write-buffered/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . return + eb/jump $emit:end/disp8 + # otherwise emit-hex(out, parse-hex-int(datum), width) + # (Weird shit can happen here if the datum of 'word' isn't either a valid + # name or a hex number, but we're only going to be passing in real legal + # programs. We just want to make sure that valid names aren't treated as + # (valid) hex numbers.) +$emit:hex-int: + # . value/eax = parse-hex-int(datum) + # . . push args + 57/push-edi + # . . call + e8/call parse-hex-int/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . emit-hex(out, value, width) + # . . push args + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16) + 50/push-eax + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) + # . . call + e8/call emit-hex/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp +$emit:end: + # . reclaim locals + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . restore registers + 5f/pop-to-edi + 5e/pop-to-esi + 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-emit-number: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # setup + # . clear-stream(_test-output-stream) + # . . push args + 68/push _test-output-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . clear-stream(_test-output-buffered-file+4) + # . . push args + b8/copy-to-eax _test-output-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 + # (eax..ecx) = "30" + b8/copy-to-eax "30"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var slice/ecx = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # emit(_test-output-buffered-file, slice, 1) + # . . push args + 68/push 1/imm32 + 51/push-ecx + 68/push _test-output-buffered-file/imm32 + # . . call + e8/call emit/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # flush(_test-output-buffered-file) + # . . push args + 68/push _test-output-buffered-file/imm32 + # . . call + e8/call flush/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-stream-equal(_test-output-stream, "30 ", msg) + # . . push args + 68/push "F - test-emit-number/1"/imm32 + 68/push "30 "/imm32 + 68/push _test-output-stream/imm32 + # . . call + e8/call check-stream-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-emit-negative-number: + # test support for sign-extending negative numbers + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # setup + # . clear-stream(_test-output-stream) + # . . push args + 68/push _test-output-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . clear-stream(_test-output-buffered-file+4) + # . . push args + b8/copy-to-eax _test-output-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 + # (eax..ecx) = "-2" + b8/copy-to-eax "-2"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var slice/ecx = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # emit(_test-output-buffered-file, slice, 2) + # . . push args + 68/push 2/imm32 + 51/push-ecx + 68/push _test-output-buffered-file/imm32 + # . . call + e8/call emit/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # flush(_test-output-buffered-file) + # . . push args + 68/push _test-output-buffered-file/imm32 + # . . call + e8/call flush/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-stream-equal(_test-output-stream, "fe ff ", msg) + # . . push args + 68/push "F - test-emit-number/1"/imm32 + 68/push "fe ff "/imm32 + 68/push _test-output-stream/imm32 + # . . call + e8/call check-stream-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-emit-number-with-metadata: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # setup + # . clear-stream(_test-output-stream) + # . . push args + 68/push _test-output-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . clear-stream(_test-output-buffered-file+4) + # . . push args + b8/copy-to-eax _test-output-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 + # (eax..ecx) = "-2/foo" + b8/copy-to-eax "-2/foo"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var slice/ecx = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # emit(_test-output-buffered-file, slice, 2) + # . . push args + 68/push 2/imm32 + 51/push-ecx + 68/push _test-output-buffered-file/imm32 + # . . call + e8/call emit/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # flush(_test-output-buffered-file) + # . . push args + 68/push _test-output-buffered-file/imm32 + # . . call + e8/call flush/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # the '/foo' will have no impact on the output + # check-stream-equal(_test-output-stream, "fe ff ", msg) + # . . push args + 68/push "F - test-emit-number-with-metadata"/imm32 + 68/push "fe ff "/imm32 + 68/push _test-output-stream/imm32 + # . . call + e8/call check-stream-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-emit-non-number: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # setup + # . clear-stream(_test-output-stream) + # . . push args + 68/push _test-output-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . clear-stream(_test-output-buffered-file+4) + # . . push args + b8/copy-to-eax _test-output-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 + # (eax..ecx) = "xyz" + b8/copy-to-eax "xyz"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var slice/ecx = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # emit(_test-output-buffered-file, slice, 2) + # . . push args + 68/push 2/imm32 + 51/push-ecx + 68/push _test-output-buffered-file/imm32 + # . . call + e8/call emit/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # flush(_test-output-buffered-file) + # . . push args + 68/push _test-output-buffered-file/imm32 + # . . call + e8/call flush/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-stream-equal(_test-output-stream, "xyz", msg) + # . . push args + 68/push "F - test-emit-non-number"/imm32 + 68/push "xyz "/imm32 + 68/push _test-output-stream/imm32 + # . . call + e8/call check-stream-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-emit-non-number-with-metadata: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # setup + # . clear-stream(_test-output-stream) + # . . push args + 68/push _test-output-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . clear-stream(_test-output-buffered-file+4) + # . . push args + b8/copy-to-eax _test-output-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 + # (eax..ecx) = "xyz/" + b8/copy-to-eax "xyz/"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var slice/ecx = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # emit(_test-output-buffered-file, slice, 2) + # . . push args + 68/push 2/imm32 + 51/push-ecx + 68/push _test-output-buffered-file/imm32 + # . . call + e8/call emit/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # flush(_test-output-buffered-file) + # . . push args + 68/push _test-output-buffered-file/imm32 + # . . call + e8/call flush/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-stream-equal(_test-output-stream, "xyz/", msg) + # . . push args + 68/push "F - test-emit-non-number-with-metadata"/imm32 + 68/push "xyz/ "/imm32 + 68/push _test-output-stream/imm32 + # . . call + e8/call check-stream-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-emit-non-number-with-all-hex-digits-and-metadata: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # setup + # . clear-stream(_test-output-stream) + # . . push args + 68/push _test-output-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . clear-stream(_test-output-buffered-file+4) + # . . push args + b8/copy-to-eax _test-output-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 + # (eax..ecx) = "abcd/xyz" + b8/copy-to-eax "abcd/xyz"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + 05/add-to-eax 4/imm32 + # var slice/ecx = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # emit(_test-output-buffered-file, slice, 2) + # . . push args + 68/push 2/imm32 + 51/push-ecx + 68/push _test-output-buffered-file/imm32 + # . . call + e8/call emit/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # flush(_test-output-buffered-file) + # . . push args + 68/push _test-output-buffered-file/imm32 + # . . call + e8/call flush/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp +#? # dump output {{{ +#? # . write(2/stderr, "^") +#? # . . push args +#? 68/push "^"/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # . write-stream(2/stderr, _test-output-stream) +#? # . . push args +#? 68/push _test-output-stream/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write-stream/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # . write(2/stderr, "$\n") +#? # . . push args +#? 68/push "$\n"/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # }}} + # check-stream-equal(_test-output-stream, "abcd/xyz") + # . . push args + 68/push "F - test-emit-non-number-with-all-hex-digits"/imm32 + 68/push "abcd/xyz "/imm32 + 68/push _test-output-stream/imm32 + # . . call + e8/call check-stream-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return diff --git a/080new-stream.subx b/080new-stream.subx deleted file mode 100644 index c3f204d2..00000000 --- a/080new-stream.subx +++ /dev/null @@ -1,118 +0,0 @@ -# Helper to allocate a stream on the heap. - -== 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 - -new-stream: # ad : (address allocation-descriptor), length : int, elemsize : int -> address/eax - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 52/push-edx - # n = elemsize * length + 12 (for read, write and length) - # . eax = elemsize - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 0x10/disp8 . # copy *(ebp+16) to eax - # . eax *= length - 31/xor 3/mod/direct 2/rm32/edx . . . 2/r32/edx . . # clear edx - f7 4/subop/multiply 1/mod/*+disp8 5/rm32/ebp . . 0xc/disp8 . # multiply *(ebp+12) into eax - # . if overflow abort - 81 7/subop/compare 3/mod/direct 2/rm32/edx . . . . . 0/imm32 # compare edx - 75/jump-if-not-equal $new-stream:abort/disp8 - # . edx = elemsize*length - 89/copy 3/mod/direct 2/rm32/edx . . . 0/r32/eax . . # copy eax to edx - # . eax += 12 - 05/add-to-eax 0xc/imm32 - # allocate(ad, n) - # . . push args - 50/push-eax - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) - # . . call - e8/call allocate/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # eax->length = elemsize*length - 89/copy 1/mod/*+disp8 0/rm32/eax . . . 2/r32/edx 8/disp8 . # copy edx to *(eax+8) - # clear-stream(eax) - # . . push args - 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 -$new-stream:end: - # . restore registers - 5a/pop-to-edx - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -$new-stream:abort: - # . _write(2/stderr, error) - # . . push args - 68/push "new-stream: size too large\n"/imm32 - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . syscall(exit, 1) - bb/copy-to-ebx 1/imm32 - b8/copy-to-eax 1/imm32/exit - cd/syscall 0x80/imm8 - # never gets here - -test-new-stream: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # var heap/ecx : (address allocation-descriptor) = {0, 0} - 68/push 0/imm32/limit - 68/push 0/imm32/curr - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # heap = new-segment(512) - # . . push args - 51/push-ecx - 68/push 0x200/imm32 - # . . call - e8/call new-segment/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # var start/edx = ad->curr - 8b/copy 0/mod/indirect 1/rm32/ecx . . . 2/r32/edx . . # copy *ecx to edx - # eax = new-stream(heap, 3, 2) - # . . push args - 68/push 2/imm32 - 68/push 3/imm32 - 51/push-ecx - # . . call - e8/call new-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # check-ints-equal(eax, edx, msg) - # . . push args - 68/push "F - test-new-stream: returns current pointer of allocation descriptor"/imm32 - 52/push-edx - 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 - # check-ints-equal(eax->length, 6, msg) - # . . push args - 68/push "F - test-new-stream: sets length correctly"/imm32 - 68/push 6/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 - # the rest is delegated to clear-stream() so we won't bother checking it - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -# . . vim:nowrap:textwidth=0 diff --git a/080zero-out.subx b/080zero-out.subx new file mode 100644 index 00000000..1a4c73d1 --- /dev/null +++ b/080zero-out.subx @@ -0,0 +1,84 @@ +# Fill a region of memory with zeroes. + +== 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 + +zero-out: # start : address, len : int + # pseudocode: + # curr/esi = start + # i/ecx = 0 + # while true + # if (i >= len) break + # *curr = 0 + # ++curr + # ++i + # + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 50/push-eax + 51/push-ecx + 52/push-edx + 56/push-esi + # curr/esi = start + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi + # i/ecx = 0 + 31/xor 3/mod/direct 1/rm32/ecx . . . 1/r32/ecx . . # clear ecx + # edx = len + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 2/r32/edx 0xc/disp8 . # copy *(ebp+12) to edx +$zero-out:loop: + # if (i >= len) break + 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx + 7d/jump-if-greater-or-equal $zero-out:end/disp8 + # *curr = 0 + c6 0/subop/copy 0/mod/direct 6/rm32/esi . . . . . 0/imm8 # copy byte to *esi + # ++curr + 46/increment-esi + # ++i + 41/increment-ecx + eb/jump $zero-out:loop/disp8 +$zero-out:end: + # . restore registers + 5e/pop-to-esi + 5a/pop-to-edx + 59/pop-to-ecx + 58/pop-to-eax + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-zero-out: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # region/ecx = 34, 35, 36, 37 + 68/push 0x37363534/imm32 + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # zero-out(ecx, 3) + # . . push args + 68/push 3/imm32/len + 51/push-ecx + # . . call + e8/call zero-out/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # first 3 bytes cleared, fourth left alone + # . check-ints-equal(*ecx, 0x37000000, msg) + # . . push args + 68/push "F - test-zero-out"/imm32 + 68/push 0x37000000/imm32 + ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +# . . vim:nowrap:textwidth=0 diff --git a/081read-line.subx b/081read-line.subx deleted file mode 100644 index bce42750..00000000 --- a/081read-line.subx +++ /dev/null @@ -1,391 +0,0 @@ -== 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 - -# read bytes from 'f' until (and including) a newline and store them into 's' -# 's' fails to grow if and only if no data found -# just abort if 's' is too small -read-line-buffered: # f : (address buffered-file), s : (address stream byte) -> - # pseudocode: - # while true - # if (s->write >= s->length) abort - # if (f->read >= f->write) populate stream from file - # if (f->write == 0) break - # AL = f->data[f->read] - # s->data[s->write] = AL - # ++f->read - # ++s->write - # if (AL == '\n') break - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 50/push-eax - 51/push-ecx - 52/push-edx - 56/push-esi - 57/push-edi - # esi = f - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi - # ecx = f->read - 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 8/disp8 . # copy *(esi+8) to ecx - # edi = s - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 7/r32/edi 0xc/disp8 . # copy *(ebp+12) to edi - # edx = s->write - 8b/copy 0/mod/indirect 7/rm32/edi . . . 2/r32/edx . . # copy *edi to edx -$read-line-buffered:loop: - # if (s->write >= s->length) abort - 3b/compare 1/mod/*+disp8 7/rm32/edi . . . 2/r32/edx 8/disp8 . # compare edx with *(edi+8) - 7d/jump-if-greater-or-equal $read-line-buffered:abort/disp8 - # if (f->read >= f->write) populate stream from file - 3b/compare 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # compare ecx with *(esi+4) - 7c/jump-if-lesser $read-line-buffered:from-stream/disp8 - # . clear-stream(stream = f+4) - # . . push args - 8d/copy-address 1/mod/*+disp8 6/rm32/esi . . . 0/r32/eax 4/disp8 . # copy esi+4 to eax - 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 - # . f->read must now be 0; update its cache at ecx - 31/xor 3/mod/direct 1/rm32/ecx . . . 1/r32/ecx . . # clear ecx - # . eax = read(f->fd, stream = f+4) - # . . push args - 50/push-eax - ff 6/subop/push 0/mod/indirect 6/rm32/esi . . . . . . # push *esi - # . . call - e8/call read/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # if (f->write == 0) break - # since f->read was initially 0, eax is the same as f->write - # . if (eax == 0) return true - 3d/compare-eax-and 0/imm32 - 74/jump-if-equal $read-line-buffered:end/disp8 -$read-line-buffered:from-stream: - # AL = f->data[f->read] - 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax - 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/esi 1/index/ecx . 0/r32/AL 0x10/disp8 . # copy byte at *(esi+ecx+16) to AL - # s->data[s->write] = AL - 88/copy-byte 1/mod/*+disp8 4/rm32/sib 7/base/edi 2/index/edx . 0/r32/AL 0xc/disp8 . # copy AL to *(edi+edx+12) - # ++f->read - 41/increment-ecx - # ++s->write - 42/increment-edx - # if (AL == '\n') return - 3d/compare-eax-and 0xa/imm32 - 75/jump-if-not-equal $read-line-buffered:loop/disp8 -$read-line-buffered:end: - # save f->read - 89/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 8/disp8 . # copy ecx to *(esi+8) - # save s->write - 89/copy 0/mod/indirect 7/rm32/edi . . . 2/r32/edx . . # copy edx to *edi - # . restore registers - 5f/pop-to-edi - 5e/pop-to-esi - 5a/pop-to-edx - 59/pop-to-ecx - 58/pop-to-eax - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -$read-line-buffered:abort: - # . _write(2/stderr, error) - # . . push args - 68/push "read-line-buffered: line too long\n"/imm32 - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . syscall(exit, 1) - bb/copy-to-ebx 1/imm32 - b8/copy-to-eax 1/imm32/exit - cd/syscall 0x80/imm8 - # never gets here - -test-read-line-buffered: - # - check that read-line-buffered stops at a 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 - # . clear-stream(_test-tmp-stream) - # . . push args - 68/push _test-tmp-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 - # write(_test-stream, "ab\ncd") - # . . push args - 68/push "ab\ncd"/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 - # read a line from _test-stream (buffered by _test-buffered-file) into _test-tmp-stream - # . eax = read-line-buffered(_test-buffered-file, _test-tmp-stream) - # . . push args - 68/push _test-tmp-stream/imm32 - 68/push _test-buffered-file/imm32 - # . . call - e8/call read-line-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-next-stream-line-equal(_test-tmp-stream, "ab", msg) - # . . push args - 68/push "F - test-read-line-buffered"/imm32 - 68/push "ab"/imm32 - 68/push _test-tmp-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # end - c3/return - -test-read-line-buffered-reads-final-line-until-Eof: - # 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 - # . clear-stream(_test-tmp-stream) - # . . push args - 68/push _test-tmp-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 - # write(_test-stream, "cd") - # . . push args - 68/push "cd"/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 - # read a line from _test-stream (buffered by _test-buffered-file) into _test-tmp-stream - # . eax = read-line-buffered(_test-buffered-file, _test-tmp-stream) - # . . push args - 68/push _test-tmp-stream/imm32 - 68/push _test-buffered-file/imm32 - # . . call - e8/call read-line-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-stream-equal(_test-tmp-stream, "cd", msg) - # . . push args - 68/push "F - test-read-line-buffered-reads-final-line-until-Eof"/imm32 - 68/push "cd"/imm32 - 68/push _test-tmp-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # end - c3/return - -# read bytes from 'f' until (and including) a newline and store them into 's' -# 's' fails to grow if and only if no data found -# just abort if 's' is too small -read-line: # f : (address stream), s : (address stream byte) -> - # pseudocode: - # while true - # if (s->write >= s->length) abort - # if (f->read >= f->write) break - # AL = f->data[f->read] - # s->data[s->write] = AL - # ++f->read - # ++s->write - # if (AL == '\n') break - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 50/push-eax - 51/push-ecx - 52/push-edx - 56/push-esi - 57/push-edi - # esi = f - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi - # ecx = f->read - 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy *(esi+4) to ecx - # edi = s - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 7/r32/edi 0xc/disp8 . # copy *(ebp+12) to edi - # edx = s->write - 8b/copy 0/mod/indirect 7/rm32/edi . . . 2/r32/edx . . # copy *edi to edx -$read-line:loop: - # if (s->write >= s->length) abort - 3b/compare 1/mod/*+disp8 7/rm32/edi . . . 2/r32/edx 8/disp8 . # compare edx with *(edi+8) - 0f 8d/jump-if-greater-or-equal $read-line:abort/disp32 - # if (f->read >= f->write) break - 3b/compare 0/mod/indirect 6/rm32/esi . . . 1/r32/ecx . . # compare ecx with *esi - 7d/jump-if-greater-or-equal $read-line:end/disp8 - # AL = f->data[f->read] - 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax - 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/esi 1/index/ecx . 0/r32/AL 0xc/disp8 . # copy byte at *(esi+ecx+12) to AL - # s->data[s->write] = AL - 88/copy-byte 1/mod/*+disp8 4/rm32/sib 7/base/edi 2/index/edx . 0/r32/AL 0xc/disp8 . # copy AL to *(edi+edx+12) - # ++f->read - 41/increment-ecx - # ++s->write - 42/increment-edx - # if (AL == '\n') return - 3d/compare-eax-and 0xa/imm32 - 0f 85/jump-if-not-equal $read-line:loop/disp32 -$read-line:end: - # save f->read - 89/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy ecx to *(esi+4) - # save s->write - 89/copy 0/mod/indirect 7/rm32/edi . . . 2/r32/edx . . # copy edx to *edi - # . restore registers - 5f/pop-to-edi - 5e/pop-to-esi - 5a/pop-to-edx - 59/pop-to-ecx - 58/pop-to-eax - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -$read-line:abort: - # . _write(2/stderr, error) - # . . push args - 68/push "read-line: line too long\n"/imm32 - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . syscall(exit, 1) - bb/copy-to-ebx 1/imm32 - b8/copy-to-eax 1/imm32/exit - cd/syscall 0x80/imm8 - # never gets here - -test-read-line: - # - check that read-line stops at a 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-tmp-stream) - # . . push args - 68/push _test-tmp-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 - # write(_test-stream, "ab\ncd") - # . . push args - 68/push "ab\ncd"/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 - # read a line from _test-stream into _test-tmp-stream - # . eax = read-line(_test-stream, _test-tmp-stream) - # . . push args - 68/push _test-tmp-stream/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call read-line/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-next-stream-line-equal(_test-tmp-stream, "ab", msg) - # . . push args - 68/push "F - test-read-line"/imm32 - 68/push "ab"/imm32 - 68/push _test-tmp-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # end - c3/return - -test-read-line-reads-final-line-until-Eof: - # 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-tmp-stream) - # . . push args - 68/push _test-tmp-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 - # write(_test-stream, "cd") - # . . push args - 68/push "cd"/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 - # read a line from _test-stream into _test-tmp-stream - # . eax = read-line(_test-stream, _test-tmp-stream) - # . . push args - 68/push _test-tmp-stream/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call read-line/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-stream-equal(_test-tmp-stream, "cd", msg) - # . . push args - 68/push "F - test-read-line-reads-final-line-until-Eof"/imm32 - 68/push "cd"/imm32 - 68/push _test-tmp-stream/imm32 - # . . call - e8/call check-stream-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 diff --git a/081table.subx b/081table.subx new file mode 100644 index 00000000..602cd872 --- /dev/null +++ b/081table.subx @@ -0,0 +1,1696 @@ +# A table is a stream of (key, value) rows. +# +# Each row consists of a 4-byte key (address to a string) and a variable-size +# value. +# +# Accessing the table performs a linear scan for a key string, and always +# requires passing in the row size. +# +# Table primitives have the form (stream, , row-size, ...) -> address/eax +# +# The following table shows available options for : +# if not found: | arg=string arg=slice +# ------------------------+--------------------------------------------------- +# abort | get get-slice +# insert key | get-or-insert leaky-get-or-insert-slice +# stop | get-or-stop get-slice-or-stop +# return null | maybe-get maybe-get-slice +# Some variants may take extra args. + +== 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 + +# if no row is found, abort +get: # table : (address stream {string, _}), key : (address string), row-size : int, abort-message-prefix : (address string) -> eax : (address _) + # pseudocode: + # curr = table->data + # max = &table->data[table->write] + # while curr < max + # if string-equal?(key, *curr) + # return curr+4 + # curr += row-size + # abort + # + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 51/push-ecx + 52/push-edx + 56/push-esi + # esi = table + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi + # curr/ecx = table->data + 8d/copy-address 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 0xc/disp8 . # copy esi+12 to ecx + # max/edx = table->data + table->write + 8b/copy 0/mod/indirect 6/rm32/esi . . . 2/r32/edx . . # copy *esi to edx + 8d/copy-address 0/mod/indirect 4/rm32/sib 1/base/ecx 2/index/edx . 2/r32/edx . . # copy ecx+edx to edx +$get:search-loop: + # if (curr >= max) abort + 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx + 73/jump-if-greater-or-equal-unsigned $get:abort/disp8 + # if (string-equal?(key, *curr)) return curr+4 + # . eax = string-equal?(key, *curr) + # . . push args + ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) + # . . call + e8/call string-equal?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . if (eax != 0) return eax = curr+4 + 3d/compare-eax-and 0/imm32 + 74/jump-if-equal $get:mismatch/disp8 + 8d/copy-address 1/mod/*+disp8 1/rm32/ecx . . . 0/r32/eax 4/disp8 . # copy ecx+4 to eax + eb/jump $get:end/disp8 +$get:mismatch: + # curr += row-size + 03/add 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 0x10/disp8 . # add *(ebp+16) to ecx + # loop + eb/jump $get:search-loop/disp8 +$get:end: + # . restore registers + 5e/pop-to-esi + 5a/pop-to-edx + 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 + +$get:abort: + # . _write(2/stderr, abort-message-prefix) + # . . push args + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x14/disp8 . # push *(ebp+20) + 68/push 2/imm32/stderr + # . . call + e8/call _write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . _write(2/stderr, error) + # . . push args + 68/push ": get: key not found: "/imm32 + 68/push 2/imm32/stderr + # . . call + e8/call _write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . _write(2/stderr, key) + # . . push args + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) + 68/push 2/imm32/stderr + # . . call + e8/call _write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . _write(2/stderr, "\n") + # . . push args + 68/push Newline/imm32 + 68/push 2/imm32/stderr + # . . call + e8/call _write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . syscall(exit, 1) + bb/copy-to-ebx 1/imm32 + b8/copy-to-eax 1/imm32/exit + cd/syscall 0x80/imm8 + # never gets here + +test-get: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # - setup: create a table with a couple of keys + # var table/ecx : (address stream {string, number}) = stream(2 rows * 8 bytes) + 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # subtract from esp + 68/push 0x10/imm32/length + 68/push 0/imm32/read + 68/push 0/imm32/write + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # insert(table, "code", 8 bytes per row) + # . . push args + 68/push 8/imm32/row-size + 68/push "code"/imm32 + 51/push-ecx + # . . call + e8/call get-or-insert/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # insert(table, "data", 8 bytes per row) + # . . push args + 68/push 8/imm32/row-size + 68/push "data"/imm32 + 51/push-ecx + # . . call + e8/call get-or-insert/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp +$test-get:check1: + # eax = get(table, "code", 8 bytes per row) + # . . push args + 68/push 8/imm32/row-size + 68/push "code"/imm32 + 51/push-ecx + # . . call + e8/call get/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # check-ints-equal(eax - table->data, 4, msg) + # . check-ints-equal(eax - table, 16, msg) + # . . push args + 68/push "F - test-get/0"/imm32 + 68/push 0x10/imm32 + 29/subtract 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # subtract ecx from eax + 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-get:check2: + # eax = get(table, "data", 8 bytes per row) + # . . push args + 68/push 8/imm32/row-size + 68/push "data"/imm32 + 51/push-ecx + # . . call + e8/call get/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # check-ints-equal(eax - table->data, 12, msg) + # . check-ints-equal(eax - table, 24, msg) + # . . push args + 68/push "F - test-get/1"/imm32 + 68/push 0x18/imm32 + 29/subtract 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # subtract ecx from eax + 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-get:end: + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +# if no row is found, abort +get-slice: # table : (address stream {string, _}), key : (address slice), row-size : int, abort-message-prefix : (address string) -> eax : (address _) + # pseudocode: + # curr = table->data + # max = &table->data[table->write] + # while curr < max + # if slice-equal?(key, *curr) + # return curr+4 + # curr += row-size + # abort + # + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 51/push-ecx + 52/push-edx + 56/push-esi + # esi = table + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi + # curr/ecx = table->data + 8d/copy-address 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 0xc/disp8 . # copy esi+12 to ecx + # max/edx = table->data + table->write + 8b/copy 0/mod/indirect 6/rm32/esi . . . 2/r32/edx . . # copy *esi to edx + 8d/copy-address 0/mod/indirect 4/rm32/sib 1/base/ecx 2/index/edx . 2/r32/edx . . # copy ecx+edx to edx +$get-slice:search-loop: + # if (curr >= max) abort + 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx + 73/jump-if-greater-or-equal-unsigned $get-slice:abort/disp8 + # if (slice-equal?(key, *curr)) return curr+4 + # . eax = slice-equal?(key, *curr) + # . . push args + ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) + # . . call + e8/call slice-equal?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . if (eax != 0) return eax = curr+4 + 3d/compare-eax-and 0/imm32 + 74/jump-if-equal $get-slice:mismatch/disp8 + 8d/copy-address 1/mod/*+disp8 1/rm32/ecx . . . 0/r32/eax 4/disp8 . # copy ecx+4 to eax + eb/jump $get-slice:end/disp8 +$get-slice:mismatch: + # curr += row-size + 03/add 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 0x10/disp8 . # add *(ebp+16) to ecx + # loop + eb/jump $get-slice:search-loop/disp8 +$get-slice:end: + # . restore registers + 5e/pop-to-esi + 5a/pop-to-edx + 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 + +$get-slice:abort: + # . _write(2/stderr, abort-message-prefix) + # . . push args + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x14/disp8 . # push *(ebp+20) + 68/push 2/imm32/stderr + # . . call + e8/call _write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . _write(2/stderr, error) + # . . push args + 68/push ": get-slice: key not found: "/imm32 + 68/push 2/imm32/stderr + # . . call + e8/call _write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . write-slice-buffered(Stderr, key) + # . . push args + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) + 68/push Stderr/imm32 + # . . call + e8/call write-slice-buffered/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . flush(Stderr) + # . . push args + 68/push Stderr/imm32 + # . . call + e8/call flush/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . _write(2/stderr, "\n") + # . . push args + 68/push Newline/imm32 + 68/push 2/imm32/stderr + # . . call + e8/call _write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . syscall(exit, 1) + bb/copy-to-ebx 1/imm32 + b8/copy-to-eax 1/imm32/exit + cd/syscall 0x80/imm8 + # never gets here + +test-get-slice: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # - setup: create a table with a couple of keys + # var table/ecx : (address stream {string, number}) = stream(2 rows * 8 bytes) + 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # subtract from esp + 68/push 0x10/imm32/length + 68/push 0/imm32/read + 68/push 0/imm32/write + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # insert(table, "code", 8 bytes per row) + # . . push args + 68/push 8/imm32/row-size + 68/push "code"/imm32 + 51/push-ecx + # . . call + e8/call get-or-insert/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # insert(table, "data", 8 bytes per row) + # . . push args + 68/push 8/imm32/row-size + 68/push "data"/imm32 + 51/push-ecx + # . . call + e8/call get-or-insert/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp +$test-get-slice:check1: + # (eax..edx) = "code" + b8/copy-to-eax "code"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 2/r32/edx . . # copy *eax to edx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 2/index/edx . 2/r32/edx 4/disp8 . # copy eax+edx+4 to edx + 05/add-to-eax 4/imm32 + # var slice/edx = {eax, edx} + 52/push-edx + 50/push-eax + 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx + # eax = get-slice(table, "code", 8 bytes per row) + # . . push args + 68/push 8/imm32/row-size + 52/push-edx + 51/push-ecx + # . . call + e8/call get-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # check-ints-equal(eax - table->data, 4, msg) # first row's value slot returned + # . check-ints-equal(eax - table, 16, msg) + # . . push args + 68/push "F - test-get-slice/0"/imm32 + 68/push 0x10/imm32 + 29/subtract 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # subtract ecx from eax + 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-get-slice:check2: + # (eax..edx) = "data" + b8/copy-to-eax "data"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 2/r32/edx . . # copy *eax to edx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 2/index/edx . 2/r32/edx 4/disp8 . # copy eax+edx+4 to edx + 05/add-to-eax 4/imm32 + # var slice/edx = {eax, edx} + 52/push-edx + 50/push-eax + 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx + # eax = get-slice(table, "data" slice, 8 bytes per row) + # . . push args + 68/push 8/imm32/row-size + 52/push-edx + 51/push-ecx + # . . call + e8/call get-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # check-ints-equal(eax - table->data, 12, msg) + # . check-ints-equal(eax - table, 24, msg) + # . . push args + 68/push "F - test-get-slice/1"/imm32 + 68/push 0x18/imm32 + 29/subtract 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # subtract ecx from eax + 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-get-slice:end: + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +# if no row is found, save 'key' to the next available row +# if there are no rows free, abort +# return the address of the value +# Beware: assume keys are immutable; they're inserted by reference +# TODO: pass in an allocation descriptor +get-or-insert: # table : (address stream {string, _}), key : (address string), row-size : int -> eax : (address _) + # pseudocode: + # curr = table->data + # max = &table->data[table->write] + # while curr < max + # if string-equal?(key, *curr) + # return curr+4 + # curr += row-size + # if table->write >= table->length + # abort + # zero-out(max, row-size) + # *max = key + # table->write += row-size + # return max+4 + # + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 51/push-ecx + 52/push-edx + 56/push-esi + # esi = table + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi + # curr/ecx = table->data + 8d/copy-address 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 0xc/disp8 . # copy esi+12 to ecx + # max/edx = table->data + table->write + 8b/copy 0/mod/indirect 6/rm32/esi . . . 2/r32/edx . . # copy *esi to edx + 8d/copy-address 0/mod/indirect 4/rm32/sib 1/base/ecx 2/index/edx . 2/r32/edx . . # copy ecx+edx to edx +$get-or-insert:search-loop: + # if (curr >= max) break + 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx + 73/jump-if-greater-or-equal-unsigned $get-or-insert:not-found/disp8 + # if (string-equal?(key, *curr)) return curr+4 + # . eax = string-equal?(key, *curr) + # . . push args + ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) + # . . call + e8/call string-equal?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . if (eax != 0) return eax = curr+4 + 3d/compare-eax-and 0/imm32 + 74/jump-if-equal $get-or-insert:mismatch/disp8 + 8d/copy-address 1/mod/*+disp8 1/rm32/ecx . . . 0/r32/eax 4/disp8 . # copy ecx+4 to eax + eb/jump $get-or-insert:end/disp8 +$get-or-insert:mismatch: + # curr += row-size + 03/add 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 0x10/disp8 . # add *(ebp+16) to ecx + # loop + eb/jump $get-or-insert:search-loop/disp8 +$get-or-insert:not-found: + # result/eax = 0 + 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax + # if (table->write >= table->length) abort + 8b/copy 0/mod/indirect 6/rm32/esi . . . 1/r32/ecx . . # copy *esi to ecx + 3b/compare 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 8/disp8 . # compare ecx with *(esi+8) + 73/jump-if-greater-or-equal-unsigned $get-or-insert:abort/disp8 + # zero-out(max, row-size) + # . . push args + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16) + 52/push-edx + # . . call + e8/call zero-out/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # *max = key + # . eax = key + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 0xc/disp8 . # copy *(ebp+12) to eax + # . *max = eax + 89/copy 0/mod/indirect 2/rm32/edx . . . 0/r32/eax . . # copy eax to *edx + # table->write += row-size + # . eax = row-size + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 0x10/disp8 . # copy *(ebp+16) to eax + # . table->write += eax + 01/add 0/mod/indirect 6/rm32/esi . . . 0/r32/eax . . # add eax to *esi + # return max+4 + # . eax = max + 89/copy 3/mod/direct 0/rm32/eax . . . 2/r32/edx . . # copy edx to eax + # . eax += 4 + 05/add-to-eax 4/imm32 +$get-or-insert:end: + # . restore registers + 5e/pop-to-esi + 5a/pop-to-edx + 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 + +$get-or-insert:abort: + # . _write(2/stderr, error) + # . . push args + 68/push "get-or-insert: table is full\n"/imm32 + 68/push 2/imm32/stderr + # . . call + e8/call _write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . syscall(exit, 1) + bb/copy-to-ebx 1/imm32 + b8/copy-to-eax 1/imm32/exit + cd/syscall 0x80/imm8 + # never gets here + +test-get-or-insert: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # var table/ecx : (address stream {string, number}) = stream(2 rows * 8 bytes) + 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # subtract from esp + 68/push 0x10/imm32/length + 68/push 0/imm32/read + 68/push 0/imm32/write + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx +$test-get-or-insert:first-call: + # - start with an empty table, insert one key, verify that it was inserted + # eax = get-or-insert(table, "code", 8 bytes per row) + # . . push args + 68/push 8/imm32/row-size + 68/push "code"/imm32 + 51/push-ecx + # . . call + e8/call get-or-insert/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # check-ints-equal(eax - table->data, 4, msg) # first row's value slot returned + # . check-ints-equal(eax - table, 16, msg) + # . . push args + 68/push "F - test-get-or-insert/0"/imm32 + 68/push 0x10/imm32 + 29/subtract 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # subtract ecx from eax + 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-get-or-insert:check2: + # check-ints-equal(table->write, row-size = 8, msg) + # . . push args + 68/push "F - test-get-or-insert/1"/imm32 + 68/push 8/imm32/row-size + ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # check-string-equal(*table->data, "code", msg) + # . . push args + 68/push "F - test-get-or-insert/2"/imm32 + 68/push "code"/imm32 + ff 6/subop/push 1/mod/*+disp8 1/rm32/ecx . . . . 0xc/disp8 . # push *(ecx+12) + # . . call + e8/call check-string-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp +$test-get-or-insert:second-call: + # - insert the same key again, verify that it was reused + # eax = get-or-insert(table, "code", 8 bytes per row) + # . . push args + 68/push 8/imm32/row-size + 68/push "code"/imm32 + 51/push-ecx + # . . call + e8/call get-or-insert/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # check-ints-equal(eax - table->data, 4, msg) + # . check-ints-equal(eax - table, 16, msg) + # . . push args + 68/push "F - test-get-or-insert/3"/imm32 + 68/push 0x10/imm32 + 29/subtract 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # subtract ecx from eax + 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 + # no new row inserted + # . check-ints-equal(table->write, row-size = 8, msg) + # . . push args + 68/push "F - test-get-or-insert/4"/imm32 + 68/push 8/imm32/row-size + ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # check-string-equal(*table->data, "code", msg) + # . . push args + 68/push "F - test-get-or-insert/5"/imm32 + 68/push "code"/imm32 + ff 6/subop/push 1/mod/*+disp8 1/rm32/ecx . . . . 0xc/disp8 . # push *(ecx+12) + # . . call + e8/call check-string-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp +$test-get-or-insert:third-call: + # - insert a new key, verify that it was inserted + # eax = get-or-insert(table, "data", 8 bytes per row) + # . . push args + 68/push 8/imm32/row-size + 68/push "data"/imm32 + 51/push-ecx + # . . call + e8/call get-or-insert/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # table gets a new row + # check-ints-equal(eax - table->data, 12, msg) # second row's value slot returned + # . check-ints-equal(eax - table, 24, msg) + # . . push args + 68/push "F - test-get-or-insert/6"/imm32 + 68/push 0x18/imm32 + 29/subtract 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # subtract ecx from eax + 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 + # check-ints-equal(table->write, 2 rows = 16, msg) + # . . push args + 68/push "F - test-get-or-insert/7"/imm32 + 68/push 0x10/imm32/two-rows + ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # check-string-equal(*table->data+8, "data", msg) + # check-string-equal(*(table+20), "data", msg) + # . . push args + 68/push "F - test-get-or-insert/8"/imm32 + 68/push "data"/imm32 + ff 6/subop/push 1/mod/*+disp8 1/rm32/ecx . . . . 0x14/disp8 . # push *(ecx+20) + # . . call + e8/call check-string-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp +$test-get-or-insert:end: + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +# if no row is found, save 'key' in the next available row +# if there are no rows free, abort +# WARNING: leaks memory +# TODO: pass in an allocation descriptor +leaky-get-or-insert-slice: # table : (address stream {string, _}), key : (address slice), row-size : int -> eax : (address _) + # pseudocode: + # curr = table->data + # max = &table->data[table->write] + # while curr < max + # if slice-equal?(key, *curr) + # return curr+4 + # curr += row-size + # if table->write >= table->length + # abort + # zero-out(max, row-size) + # *max = slice-to-string(Heap, key) + # table->write += row-size + # return max+4 + # + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 51/push-ecx + 52/push-edx + 56/push-esi + # esi = table + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi + # curr/ecx = table->data + 8d/copy-address 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 0xc/disp8 . # copy esi+12 to ecx + # max/edx = table->data + table->write + 8b/copy 0/mod/indirect 6/rm32/esi . . . 2/r32/edx . . # copy *esi to edx + 8d/copy-address 0/mod/indirect 4/rm32/sib 1/base/ecx 2/index/edx . 2/r32/edx . . # copy ecx+edx to edx +$leaky-get-or-insert-slice:search-loop: + # if (curr >= max) break + 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx + 73/jump-if-greater-or-equal-unsigned $leaky-get-or-insert-slice:not-found/disp8 + # if (slice-equal?(key, *curr)) return curr+4 + # . eax = slice-equal?(key, *curr) + # . . push args + ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) + # . . call + e8/call slice-equal?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . if (eax != 0) return eax = curr+4 + 3d/compare-eax-and 0/imm32 + 74/jump-if-equal $leaky-get-or-insert-slice:mismatch/disp8 + 8d/copy-address 1/mod/*+disp8 1/rm32/ecx . . . 0/r32/eax 4/disp8 . # copy ecx+4 to eax + eb/jump $leaky-get-or-insert-slice:end/disp8 +$leaky-get-or-insert-slice:mismatch: + # curr += row-size + 03/add 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 0x10/disp8 . # add *(ebp+16) to ecx + # loop + eb/jump $leaky-get-or-insert-slice:search-loop/disp8 +$leaky-get-or-insert-slice:not-found: + # result/eax = 0 + 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax + # if (table->write >= table->length) abort + 8b/copy 0/mod/indirect 6/rm32/esi . . . 1/r32/ecx . . # copy *esi to ecx + 3b/compare 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 8/disp8 . # compare ecx with *(esi+8) + 7d/jump-if-greater-or-equal $leaky-get-or-insert-slice:abort/disp8 + # zero-out(max, row-size) + # . . push args + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16) + 52/push-edx + # . . call + e8/call zero-out/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # *max = slice-to-string(Heap, key) + # . eax = slice-to-string(Heap, key) + # . . push args + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) + 68/push Heap/imm32 + # . . call + e8/call slice-to-string/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . *max = eax + 89/copy 0/mod/indirect 2/rm32/edx . . . 0/r32/eax . . # copy eax to *edx + # table->write += row-size + # . eax = row-size + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 0x10/disp8 . # copy *(ebp+16) to eax + # . table->write += eax + 01/add 0/mod/indirect 6/rm32/esi . . . 0/r32/eax . . # add eax to *esi + # return max+4 + # . eax = max + 89/copy 3/mod/direct 0/rm32/eax . . . 2/r32/edx . . # copy edx to eax + # . eax += 4 + 05/add-to-eax 4/imm32 +$leaky-get-or-insert-slice:end: + # . restore registers + 5e/pop-to-esi + 5a/pop-to-edx + 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 + +$leaky-get-or-insert-slice:abort: + # . _write(2/stderr, error) + # . . push args + 68/push "leaky-get-or-insert-slice: table is full\n"/imm32 + 68/push 2/imm32/stderr + # . . call + e8/call _write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . syscall(exit, 1) + bb/copy-to-ebx 1/imm32 + b8/copy-to-eax 1/imm32/exit + cd/syscall 0x80/imm8 + # never gets here + +test-leaky-get-or-insert-slice: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # var table/ecx : (address stream {string, number}) = stream(2 rows * 8 bytes) + 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # subtract from esp + 68/push 0x10/imm32/length + 68/push 0/imm32/read + 68/push 0/imm32/write + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # (eax..edx) = "code" + b8/copy-to-eax "code"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 2/r32/edx . . # copy *eax to edx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 2/index/edx . 2/r32/edx 4/disp8 . # copy eax+edx+4 to edx + 05/add-to-eax 4/imm32 + # var slice/edx = {eax, edx} + 52/push-edx + 50/push-eax + 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx +$test-leaky-get-or-insert-slice:first-call: + # - start with an empty table, insert one key, verify that it was inserted + # eax = leaky-get-or-insert-slice(table, "code" slice, 8 bytes per row) + # . . push args + 68/push 8/imm32/row-size + 52/push-edx + 51/push-ecx + # . . call + e8/call leaky-get-or-insert-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # check-ints-equal(eax - table->data, 4, msg) # first row's value slot returned + # . check-ints-equal(eax - table, 16, msg) + # . . push args + 68/push "F - test-leaky-get-or-insert-slice/0"/imm32 + 68/push 0x10/imm32 + 29/subtract 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # subtract ecx from eax + 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-leaky-get-or-insert-slice:check2: + # check-ints-equal(table->write, row-size = 8, msg) + # . . push args + 68/push "F - test-leaky-get-or-insert-slice/1"/imm32 + 68/push 8/imm32/row-size + ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # check-string-equal(*table->data, "code", msg) + # . . push args + 68/push "F - test-leaky-get-or-insert-slice/2"/imm32 + 68/push "code"/imm32 + ff 6/subop/push 1/mod/*+disp8 1/rm32/ecx . . . . 0xc/disp8 . # push *(ecx+12) + # . . call + e8/call check-string-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp +$test-leaky-get-or-insert-slice:second-call: + # - insert the same key again, verify that it was reused + # eax = leaky-get-or-insert-slice(table, "code" slice, 8 bytes per row) + # . . push args + 68/push 8/imm32/row-size + 52/push-edx + 51/push-ecx + # . . call + e8/call leaky-get-or-insert-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # check-ints-equal(eax - table->data, 4, msg) + # . check-ints-equal(eax - table, 16, msg) + # . . push args + 68/push "F - test-leaky-get-or-insert-slice/3"/imm32 + 68/push 0x10/imm32 + 29/subtract 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # subtract ecx from eax + 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 + # no new row inserted + # . check-ints-equal(table->write, row-size = 8, msg) + # . . push args + 68/push "F - test-leaky-get-or-insert-slice/4"/imm32 + 68/push 8/imm32/row-size + ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # check-string-equal(*table->data, "code", msg) + # . . push args + 68/push "F - test-leaky-get-or-insert-slice/5"/imm32 + 68/push "code"/imm32 + ff 6/subop/push 1/mod/*+disp8 1/rm32/ecx . . . . 0xc/disp8 . # push *(ecx+12) + # . . call + e8/call check-string-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp +$test-leaky-get-or-insert-slice:third-call: + # - insert a new key, verify that it was inserted + # (eax..edx) = "data" + b8/copy-to-eax "data"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 2/r32/edx . . # copy *eax to edx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 2/index/edx . 2/r32/edx 4/disp8 . # copy eax+edx+4 to edx + 05/add-to-eax 4/imm32 + # var slice/edx = {eax, edx} + 52/push-edx + 50/push-eax + 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx + # eax = leaky-get-or-insert-slice(table, "data" slice, 8 bytes per row) + # . . push args + 68/push 8/imm32/row-size + 52/push-edx + 51/push-ecx + # . . call + e8/call leaky-get-or-insert-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # table gets a new row + # check-ints-equal(eax - table->data, 12, msg) # second row's value slot returned + # . check-ints-equal(eax - table, 24, msg) + # . . push args + 68/push "F - test-leaky-get-or-insert-slice/6"/imm32 + 68/push 0x18/imm32 + 29/subtract 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # subtract ecx from eax + 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 + # check-ints-equal(table->write, 2 rows = 16, msg) + # . . push args + 68/push "F - test-leaky-get-or-insert-slice/7"/imm32 + 68/push 0x10/imm32/two-rows + ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # check-string-equal(*table->data+8, "data", msg) + # check-string-equal(*(table+20), "data", msg) + # . . push args + 68/push "F - test-leaky-get-or-insert-slice/8"/imm32 + 68/push "data"/imm32 + ff 6/subop/push 1/mod/*+disp8 1/rm32/ecx . . . . 0x14/disp8 . # push *(ecx+20) + # . . call + e8/call check-string-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp +$test-leaky-get-or-insert-slice:end: + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +# if no row is found, stop(ed) +get-or-stop: # table : (address stream {string, _}), key : (address string), row-size : int, + # abort-message-prefix : (address string), err : (address buffered-file), ed : (address exit-descriptor) + # -> eax : (address _) + # pseudocode: + # curr = table->data + # max = &table->data[table->write] + # while curr < max + # if string-equal?(key, *curr) + # return curr+4 + # curr += row-size + # write-buffered(err, msg) + # stop(ed) + # + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 51/push-ecx + 52/push-edx + 56/push-esi + # esi = table + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi + # curr/ecx = table->data + 8d/copy-address 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 0xc/disp8 . # copy esi+12 to ecx + # max/edx = table->data + table->write + 8b/copy 0/mod/indirect 6/rm32/esi . . . 2/r32/edx . . # copy *esi to edx + 8d/copy-address 0/mod/indirect 4/rm32/sib 1/base/ecx 2/index/edx . 2/r32/edx . . # copy ecx+edx to edx +$get-or-stop:search-loop: + # if (curr >= max) stop(ed) + 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx + 73/jump-if-greater-or-equal-unsigned $get-or-stop:stop/disp8 + # if (string-equal?(key, *curr)) return curr+4 + # . eax = string-equal?(key, *curr) + # . . push args + ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) + # . . call + e8/call string-equal?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . if (eax != 0) return eax = curr+4 + 3d/compare-eax-and 0/imm32 + 74/jump-if-equal $get-or-stop:mismatch/disp8 + 8d/copy-address 1/mod/*+disp8 1/rm32/ecx . . . 0/r32/eax 4/disp8 . # copy ecx+4 to eax + eb/jump $get-or-stop:end/disp8 +$get-or-stop:mismatch: + # curr += row-size + 03/add 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 0x10/disp8 . # add *(ebp+16) to ecx + # loop + eb/jump $get-or-stop:search-loop/disp8 +$get-or-stop:end: + # . restore registers + 5e/pop-to-esi + 5a/pop-to-edx + 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 + +$get-or-stop:stop: + # . write-buffered(err, abort-message-prefix) + # . . 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 . . . . 0x18/disp8 . # push *(ebp+24) + # . . call + e8/call write-buffered/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . write-buffered(err, error) + # . . push args + 68/push ": get-or-stop: key not found: "/imm32 + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x18/disp8 . # push *(ebp+24) + # . . call + e8/call write-buffered/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . write-buffered(err, key) + # . . push args + 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 . . . . 0x18/disp8 . # push *(ebp+24) + # . . call + e8/call write-buffered/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . write-buffered(err, "\n") + # . . push args + 68/push Newline/imm32 + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x18/disp8 . # push *(ebp+24) + # . . call + e8/call write-buffered/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 . . . . 0x1c/disp8 . # push *(ebp+28) + # . . call + e8/call stop/disp32 + # never gets here +$get-or-stop:terminus: + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # syscall(exit, 1) + b8/copy-to-eax 1/imm32/exit + cd/syscall 0x80/imm8 + +test-get-or-stop: + # This test uses exit-descriptors. Use ebp for setting up local variables. + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # setup + # . 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 + # var table/ecx : (address stream {string, number}) = stream(2 rows * 8 bytes) + 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # subtract from esp + 68/push 0x10/imm32/length + 68/push 0/imm32/read + 68/push 0/imm32/write + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # var ed/edx : (address exit-descriptor) + 68/push 0/imm32 + 68/push 0/imm32 + 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx + # size 'ed' for the calls to 'get-or-stop' + # . tailor-exit-descriptor(ed, 24) + # . . push args + 68/push 0x18/imm32/nbytes-of-args-for-get-or-stop + 52/push-edx + # . . call + e8/call tailor-exit-descriptor/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # insert(table, "code", 8 bytes per row) + # . . push args + 68/push 8/imm32/row-size + 68/push "code"/imm32 + 51/push-ecx + # . . call + e8/call get-or-insert/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp +$test-get-or-stop:success: + # eax = get-or-stop(table, "code", row-size=8, msg, _test-error-buffered-file, ed) + # . . push args + 52/push-edx/ed + 68/push _test-error-buffered-file/imm32 + 68/push "foo"/imm32/abort-prefix + 68/push 8/imm32/row-size + 68/push "code"/imm32 + 51/push-ecx + # . . call + e8/call get-or-stop/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x18/imm32 # add to esp +$test-get-or-stop:success-assertion: + # check-ints-equal(eax - table->data, 4, msg) + # . check-ints-equal(eax - table, 16, msg) + # . . push args + 68/push "F - test-get-or-stop/0"/imm32 + 68/push 0x10/imm32 + 29/subtract 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # subtract ecx from eax + 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-get-or-stop:failure: + # eax = get-or-stop(table, "data", row-size=8, msg, _test-error-buffered-file, ed) + # . . push args + 52/push-edx/ed + 68/push _test-error-buffered-file/imm32 + 68/push "foo"/imm32/abort-prefix + 68/push 8/imm32/row-size + 68/push "data"/imm32 + 51/push-ecx + # . . call + e8/call get-or-stop/disp32 + # registers except esp may be clobbered at this point + # restore register args, discard others + 59/pop-to-ecx + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp + 5a/pop-to-edx +$test-get-or-stop:failure-assertion: + # check that get-or-stop tried to call stop(1) + # . check-ints-equal(ed->value, 2, msg) + # . . push args + 68/push "F - test-get-or-stop/1"/imm32 + 68/push 2/imm32 + # . . push ed->value + ff 6/subop/push 1/mod/*+disp8 2/rm32/edx . . . . 4/disp8 . # push *(edx+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-get-or-stop:end: + # . epilog + # don't restore esp from ebp; manually reclaim locals + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x24/imm32 # add to esp + 5d/pop-to-ebp + c3/return + +# if no row is found, stop(ed) +get-slice-or-stop: # table : (address stream {string, _}), key : (address slice), row-size : int, + # abort-message-prefix : (address string), err : (address buffered-file), ed : (address exit-descriptor) + # -> eax : (address _) + # pseudocode: + # curr = table->data + # max = &table->data[table->write] + # while curr < max + # if slice-equal?(key, *curr) + # return curr+4 + # curr += row-size + # write-buffered(err, msg) + # stop(ed) + # + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 51/push-ecx + 52/push-edx + 56/push-esi + # esi = table + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi + # curr/ecx = table->data + 8d/copy-address 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 0xc/disp8 . # copy esi+12 to ecx + # max/edx = table->data + table->write + 8b/copy 0/mod/indirect 6/rm32/esi . . . 2/r32/edx . . # copy *esi to edx + 8d/copy-address 0/mod/indirect 4/rm32/sib 1/base/ecx 2/index/edx . 2/r32/edx . . # copy ecx+edx to edx +$get-slice-or-stop:search-loop: + # if (curr >= max) stop(ed) + 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx + 73/jump-if-greater-or-equal-unsigned $get-slice-or-stop:stop/disp8 + # if (slice-equal?(key, *curr)) return curr+4 + # . eax = slice-equal?(key, *curr) + # . . push args + ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) + # . . call + e8/call slice-equal?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . if (eax != 0) return eax = curr+4 + 3d/compare-eax-and 0/imm32 + 74/jump-if-equal $get-slice-or-stop:mismatch/disp8 + 8d/copy-address 1/mod/*+disp8 1/rm32/ecx . . . 0/r32/eax 4/disp8 . # copy ecx+4 to eax + eb/jump $get-slice-or-stop:end/disp8 +$get-slice-or-stop:mismatch: + # curr += row-size + 03/add 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 0x10/disp8 . # add *(ebp+16) to ecx + # loop + eb/jump $get-slice-or-stop:search-loop/disp8 +$get-slice-or-stop:end: + # . restore registers + 5e/pop-to-esi + 5a/pop-to-edx + 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 + +$get-slice-or-stop:stop: + # . write-buffered(err, abort-message-prefix) + # . . 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 . . . . 0x18/disp8 . # push *(ebp+24) + # . . call + e8/call write-buffered/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . write-buffered(err, error) + # . . push args + 68/push ": get-slice-or-stop: key not found: "/imm32 + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x18/disp8 . # push *(ebp+24) + # . . call + e8/call write-buffered/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . write-slice-buffered(err, key) + # . . push args + 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 . . . . 0x18/disp8 . # push *(ebp+24) + # . . call + e8/call write-slice-buffered/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . write-buffered(err, "\n") + # . . push args + 68/push Newline/imm32 + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x18/disp8 . # push *(ebp+24) + # . . call + e8/call write-buffered/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 . . . . 0x1c/disp8 . # push *(ebp+28) + # . . call + e8/call stop/disp32 + # never gets here +$get-slice-or-stop:terminus: + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # syscall(exit, 1) + b8/copy-to-eax 1/imm32/exit + cd/syscall 0x80/imm8 + +test-get-slice-or-stop: + # This test uses exit-descriptors. Use ebp for setting up local variables. + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # setup + # . 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 + # var table/ecx : (address stream {string, number}) = stream(2 rows * 8 bytes) + 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # subtract from esp + 68/push 0x10/imm32/length + 68/push 0/imm32/read + 68/push 0/imm32/write + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # var ed/edx : (address exit-descriptor) + 68/push 0/imm32 + 68/push 0/imm32 + 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx + # var slice/ebx = "code" + # . (eax..ebx) = "code" + b8/copy-to-eax "code"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 3/r32/ebx . . # copy *eax to ebx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 3/index/ebx . 3/r32/ebx 4/disp8 . # copy eax+ebx+4 to ebx + 05/add-to-eax 4/imm32 + # . ebx = {eax, ebx} + 53/push-ebx + 50/push-eax + 89/copy 3/mod/direct 3/rm32/ebx . . . 4/r32/esp . . # copy esp to ebx + # size 'ed' for the calls to 'get-or-stop' (define no locals past this point) + # . tailor-exit-descriptor(ed, 24) + # . . push args + 68/push 0x18/imm32/nbytes-of-args-for-get-or-stop + 52/push-edx + # . . call + e8/call tailor-exit-descriptor/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # insert(table, "code", 8 bytes per row) + # . . push args + 68/push 8/imm32/row-size + 68/push "code"/imm32 + 51/push-ecx + # . . call + e8/call get-or-insert/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp +$test-get-slice-or-stop:success: + # eax = get-slice-or-stop(table, slice, row-size=8, msg, _test-error-buffered-file, ed) + # . . push args + 52/push-edx/ed + 68/push _test-error-buffered-file/imm32 + 68/push "foo"/imm32/abort-prefix + 68/push 8/imm32/row-size + 53/push-ebx/slice + 51/push-ecx + # . . call + e8/call get-slice-or-stop/disp32 + # registers except esp may be clobbered at this point + # restore register args, discard others + 59/pop-to-ecx + 5b/pop-to-ebx + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + 5a/pop-to-edx +$test-get-slice-or-stop:success-assertion: + # check-ints-equal(eax - table->data, 4, msg) # first row's value slot returned + # . check-ints-equal(eax - table, 16, msg) + # . . push args + 68/push "F - test-get-slice-or-stop/0"/imm32 + 68/push 0x10/imm32 + 29/subtract 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # subtract ecx from eax + 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-get-slice-or-stop:failure: + # slice = "segment2" + # . *ebx = "segment2"->data + b8/copy-to-eax "segment2"/imm32 + 05/add-to-eax 4/imm32 + 89/copy 0/mod/indirect 3/rm32/ebx . . . 0/r32/eax . . # copy eax to *ebx + # . *(ebx+4) = "segment2"->data + len("segment2") + 05/add-to-eax 8/imm32/strlen + 89/copy 1/mod/*+disp8 3/rm32/ebx . . . 0/r32/eax 4/disp8 . # copy eax to *(ebx+4) + # eax = get-slice-or-stop(table, slice, row-size=8, msg, _test-error-buffered-file, ed) + # . . push args + 52/push-edx/ed + 68/push _test-error-buffered-file/imm32 + 68/push "foo"/imm32/abort-prefix + 68/push 8/imm32/row-size + 53/push-ebx/slice + 51/push-ecx + # . . call + e8/call get-slice-or-stop/disp32 + # registers except esp may be clobbered at this point + # restore register args, discard others + 59/pop-to-ecx + 5b/pop-to-ebx + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + 5a/pop-to-edx +$test-get-slice-or-stop:failure-assertion: + # check that get-or-stop tried to call stop(1) + # . check-ints-equal(ed->value, 2, msg) + # . . push args + 68/push "F - test-get-or-stop/1"/imm32 + 68/push 2/imm32 + # . . push ed->value + ff 6/subop/push 1/mod/*+disp8 2/rm32/edx . . . . 4/disp8 . # push *(edx+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-get-slice-or-stop:end: + # . epilog + # don't restore esp from ebp; manually reclaim locals + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x2c/imm32 # add to esp + 5d/pop-to-ebp + c3/return + +# if no row is found, return null (0) +maybe-get: # table : (address stream {string, _}), key : (address string), row-size : int -> eax : (address _) + # pseudocode: + # curr = table->data + # max = &table->data[table->write] + # while curr < max + # if string-equal?(key, *curr) + # return curr+4 + # curr += row-size + # return 0 + # + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 51/push-ecx + 52/push-edx + 56/push-esi + # esi = table + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi + # curr/ecx = table->data + 8d/copy-address 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 0xc/disp8 . # copy esi+12 to ecx + # max/edx = table->data + table->write + 8b/copy 0/mod/indirect 6/rm32/esi . . . 2/r32/edx . . # copy *esi to edx + 8d/copy-address 0/mod/indirect 4/rm32/sib 1/base/ecx 2/index/edx . 2/r32/edx . . # copy ecx+edx to edx +$maybe-get:search-loop: + # if (curr >= max) return null + 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx + 73/jump-if-greater-or-equal-unsigned $maybe-get:null/disp8 + # if (string-equal?(key, *curr)) return curr+4 + # . eax = string-equal?(key, *curr) + # . . push args + ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) + # . . call + e8/call string-equal?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . if (eax != 0) return eax = curr+4 + 3d/compare-eax-and 0/imm32 + 74/jump-if-equal $maybe-get:mismatch/disp8 + 8d/copy-address 1/mod/*+disp8 1/rm32/ecx . . . 0/r32/eax 4/disp8 . # copy ecx+4 to eax + eb/jump $maybe-get:end/disp8 +$maybe-get:mismatch: + # curr += row-size + 03/add 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 0x10/disp8 . # add *(ebp+16) to ecx + # loop + eb/jump $maybe-get:search-loop/disp8 +$maybe-get:null: + b8/copy-to-eax 0/imm32 +$maybe-get:end: + # . restore registers + 5e/pop-to-esi + 5a/pop-to-edx + 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-maybe-get: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # - setup: create a table with one row + # var table/ecx : (address stream {string, number}) = stream(2 rows * 8 bytes) + 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # subtract from esp + 68/push 0x10/imm32/length + 68/push 0/imm32/read + 68/push 0/imm32/write + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # eax = get-or-insert(table, "code", 8 bytes per row) + # . . push args + 68/push 8/imm32/row-size + 68/push "code"/imm32 + 51/push-ecx + # . . call + e8/call get-or-insert/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp +$test-maybe-get:success: + # - check for the same key, verify that it was reused + # eax = maybe-get(table, "code", 8 bytes per row) + # . . push args + 68/push 8/imm32/row-size + 68/push "code"/imm32 + 51/push-ecx + # . . call + e8/call maybe-get/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # check-ints-equal(eax - table->data, 4, msg) + # . check-ints-equal(eax - table, 16, msg) + # . . push args + 68/push "F - test-maybe-get/0"/imm32 + 68/push 0x10/imm32 + 29/subtract 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # subtract ecx from eax + 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 + # no new row inserted + # . check-ints-equal(table->write, row-size = 8, msg) + # . . push args + 68/push "F - test-maybe-get/1"/imm32 + 68/push 8/imm32/row-size + ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # check-string-equal(*table->data, "code", msg) + # . . push args + 68/push "F - test-maybe-get/2"/imm32 + 68/push "code"/imm32 + ff 6/subop/push 1/mod/*+disp8 1/rm32/ecx . . . . 0xc/disp8 . # push *(ecx+12) + # . . call + e8/call check-string-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp +$test-maybe-get:failure: + # - search for a new key + # eax = maybe-get(table, "data", 8 bytes per row) + # . . push args + 68/push 8/imm32/row-size + 68/push "data"/imm32 + 51/push-ecx + # . . call + e8/call maybe-get/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # check-ints-equal(eax, 0, msg) + # . . push args + 68/push "F - test-maybe-get/3"/imm32 + 68/push 0/imm32 + 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-maybe-get:end: + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +# if no row is found, return null (0) +maybe-get-slice: # table : (address stream {string, _}), key : (address slice), row-size : int -> eax : (address _) + # pseudocode: + # curr = table->data + # max = &table->data[table->write] + # while curr < max + # if slice-equal?(key, *curr) + # return curr+4 + # curr += row-size + # return 0 + # + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 51/push-ecx + 52/push-edx + 56/push-esi + # esi = table + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi + # curr/ecx = table->data + 8d/copy-address 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 0xc/disp8 . # copy esi+12 to ecx + # max/edx = table->data + table->write + 8b/copy 0/mod/indirect 6/rm32/esi . . . 2/r32/edx . . # copy *esi to edx + 8d/copy-address 0/mod/indirect 4/rm32/sib 1/base/ecx 2/index/edx . 2/r32/edx . . # copy ecx+edx to edx +$maybe-get-slice:search-loop: + # if (curr >= max) return null + 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx + 73/jump-if-greater-or-equal-unsigned $maybe-get-slice:null/disp8 + # if (slice-equal?(key, *curr)) return curr+4 + # . eax = slice-equal?(key, *curr) + # . . push args + ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) + # . . call + e8/call slice-equal?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . if (eax != 0) return eax = curr+4 + 3d/compare-eax-and 0/imm32 + 74/jump-if-equal $maybe-get-slice:mismatch/disp8 + 8d/copy-address 1/mod/*+disp8 1/rm32/ecx . . . 0/r32/eax 4/disp8 . # copy ecx+4 to eax + eb/jump $maybe-get-slice:end/disp8 +$maybe-get-slice:mismatch: + # curr += row-size + 03/add 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 0x10/disp8 . # add *(ebp+16) to ecx + # loop + eb/jump $maybe-get-slice:search-loop/disp8 +$maybe-get-slice:null: + b8/copy-to-eax 0/imm32 +$maybe-get-slice:end: + # . restore registers + 5e/pop-to-esi + 5a/pop-to-edx + 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-maybe-get-slice: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # - setup: create a table with one row + # var table/ecx : (address stream {string, number}) = stream(2 rows * 8 bytes) + 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # subtract from esp + 68/push 0x10/imm32/length + 68/push 0/imm32/read + 68/push 0/imm32/write + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # insert(table, "code", 8 bytes per row) + # . . push args + 68/push 8/imm32/row-size + 68/push "code"/imm32 + 51/push-ecx + # . . call + e8/call get-or-insert/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp +$test-maybe-get-slice:success: + # - check for the same key, verify that it was reused + # (eax..edx) = "code" + b8/copy-to-eax "code"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 2/r32/edx . . # copy *eax to edx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 2/index/edx . 2/r32/edx 4/disp8 . # copy eax+edx+4 to edx + 05/add-to-eax 4/imm32 + # var slice/edx = {eax, edx} + 52/push-edx + 50/push-eax + 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx + # eax = maybe-get-slice(table, "code" slice, 8 bytes per row) + # . . push args + 68/push 8/imm32/row-size + 52/push-edx + 51/push-ecx + # . . call + e8/call maybe-get-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # check-ints-equal(eax - table->data, 4, msg) + # . check-ints-equal(eax - table, 16, msg) + # . . push args + 68/push "F - test-maybe-get-slice/0"/imm32 + 68/push 0x10/imm32 + 29/subtract 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # subtract ecx from eax + 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 + # no new row inserted + # . check-ints-equal(table->write, row-size = 8, msg) + # . . push args + 68/push "F - test-maybe-get-slice/1"/imm32 + 68/push 8/imm32/row-size + ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # check-string-equal(*table->data, "code", msg) + # . . push args + 68/push "F - test-maybe-get-slice/2"/imm32 + 68/push "code"/imm32 + ff 6/subop/push 1/mod/*+disp8 1/rm32/ecx . . . . 0xc/disp8 . # push *(ecx+12) + # . . call + e8/call check-string-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp +$test-maybe-get-slice:failure: + # - search for a new key + # (eax..edx) = "data" + b8/copy-to-eax "data"/imm32 + 8b/copy 0/mod/indirect 0/rm32/eax . . . 2/r32/edx . . # copy *eax to edx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 2/index/edx . 2/r32/edx 4/disp8 . # copy eax+edx+4 to edx + 05/add-to-eax 4/imm32 + # var slice/edx = {eax, edx} + 52/push-edx + 50/push-eax + 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx + # eax = maybe-get-slice(table, "data" slice, 8 bytes per row) + # . . push args + 68/push 8/imm32/row-size + 52/push-edx + 51/push-ecx + # . . call + e8/call maybe-get-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # check-ints-equal(eax, 0, msg) + # . . push args + 68/push "F - test-maybe-get-slice/3"/imm32 + 68/push 0/imm32 + 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-maybe-get-slice:end: + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +# . . vim:nowrap:textwidth=0 diff --git a/082slice.subx b/082slice.subx deleted file mode 100644 index 09f45e18..00000000 --- a/082slice.subx +++ /dev/null @@ -1,1173 +0,0 @@ -# new data structure: a slice is an open interval of addresses [start, end) -# that includes 'start' but not 'end' - -== 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 - -slice-empty?: # s : (address slice) -> eax : boolean - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 51/push-ecx - # ecx = s - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 8/disp8 . # copy *(ebp+8) to ecx - # if (s->start == s->end) return true - # . eax = s->start - 8b/copy 0/mod/indirect 1/rm32/ecx . . . 0/r32/eax . . # copy *ecx to eax - # . compare eax and s->end - 39/compare 1/mod/*+disp8 1/rm32/ecx . . . 0/r32/eax 4/disp8 . # compare eax and *(ecx+4) - b8/copy-to-eax 1/imm32/true - 74/jump-if-equal $slice-empty?:end/disp8 - b8/copy-to-eax 0/imm32/false -$slice-empty?: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-slice-empty-true: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # var slice/ecx = {34, 34} - 68/push 34/imm32/end - 68/push 34/imm32/start - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # slice-empty?(slice) - # . . push args - 51/push-ecx - # . . call - e8/call slice-empty?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # check-ints-equal(eax, 1, msg) - # . . push args - 68/push "F - test-slice-empty-true"/imm32 - 68/push 1/imm32 - 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 - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-slice-empty-false: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # var slice/ecx = {34, 23} - 68/push 23/imm32/end - 68/push 34/imm32/start - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # slice-empty?(slice) - # . . push args - 51/push-ecx - # . . call - e8/call slice-empty?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # check-ints-equal(eax, 0, msg) - # . . push args - 68/push "F - test-slice-empty-false"/imm32 - 68/push 0/imm32 - 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 - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -slice-equal?: # s : (address slice), p : (address string) -> eax : boolean - # pseudocode: - # if (p == 0) return (s == 0) - # currs = s->start - # maxs = s->end - # if (maxs - currs != p->length) return false - # currp = p->data - # while currs < maxs - # if (*currs != *currp) return false - # ++currs - # ++currp - # return true - # - # registers: - # currs: edx - # maxs: esi - # currp: ebx - # *currs: eax - # *currp: ecx - # - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 51/push-ecx - 52/push-edx - 53/push-ebx - 56/push-esi - # esi = s - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi - # currs/edx = s->start - 8b/copy 0/mod/indirect 6/rm32/esi . . . 2/r32/edx . . # copy *esi to edx - # maxs/esi = s->end - 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 6/r32/esi 4/disp8 . # copy *(esi+4) to esi - # eax = maxs - currs - 89/copy 3/mod/direct 0/rm32/eax . . . 6/r32/esi . . # copy esi to eax - 29/subtract 3/mod/direct 0/rm32/eax . . . 2/r32/edx . . # subtract edx from eax - # ebx = p - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 3/r32/ebx 0xc/disp8 . # copy *(ebp+12) to ebx - # if (p != 0) goto next check - 81 7/subop/compare 3/mod/direct 3/rm32/ebx . . . . . 0/imm32 # compare ebx - 75/jump-if-not-equal $slice-equal?:nonnull-string/disp8 -$slice-equal?:null-string: - # return s->start == s->end - 3d/compare-eax-and 0/imm32 - 74/jump-if-equal $slice-equal?:true/disp8 - eb/jump $slice-equal?:false/disp8 -$slice-equal?:nonnull-string: - # if (eax != p->length) return false - 39/compare 0/mod/indirect 3/rm32/ebx . . . 0/r32/eax . . # compare *ebx and eax - 75/jump-if-not-equal $slice-equal?:false/disp8 - # currp/ebx = p->data - 81 0/subop/add 3/mod/direct 3/rm32/ebx . . . . . 4/imm32 # add to ebx - # eax = ecx = 0 - 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax - 31/xor 3/mod/direct 1/rm32/ecx . . . 1/r32/ecx . . # clear ecx -$slice-equal?:loop: - # if (currs >= maxs) return true - 39/compare 3/mod/direct 2/rm32/edx . . . 6/r32/esi . . # compare edx with esi - 73/jump-if-greater-or-equal-unsigned $slice-equal?:true/disp8 - # AL = *currp - 8a/copy-byte 0/mod/indirect 3/rm32/ebx . . . 0/r32/AL . . # copy byte at *ebx to AL - # CL = *currs - 8a/copy-byte 0/mod/indirect 2/rm32/edx . . . 1/r32/CL . . # copy byte at *edx to CL - # if (eax != ecx) return false - 39/compare 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # compare eax and ecx - 75/jump-if-not-equal $slice-equal?:false/disp8 - # ++currp - 43/increment-ebx - # ++currs - 42/increment-edx - eb/jump $slice-equal?:loop/disp8 -$slice-equal?:false: - b8/copy-to-eax 0/imm32 - eb/jump $slice-equal?:end/disp8 -$slice-equal?:true: - b8/copy-to-eax 1/imm32 -$slice-equal?:end: - # . restore registers - 5e/pop-to-esi - 5b/pop-to-ebx - 5a/pop-to-edx - 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-slice-equal: - # - slice-equal?(slice("Abc"), "Abc") == 1 - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # (eax..ecx) = "Abc" - b8/copy-to-eax "Abc"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # var slice/ecx = {eax, ecx} - 51/push-ecx - 50/push-eax - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # eax = slice-equal?(ecx, "Abc") - # . . push args - 68/push "Abc"/imm32 - 51/push-ecx - # . . call - e8/call slice-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-ints-equal(eax, 1, msg) - # . . push args - 68/push "F - test-slice-equal"/imm32 - 68/push 1/imm32 - 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 - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-slice-equal-false: - # - slice-equal?(slice("bcd"), "Abc") == 0 - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # (eax..ecx) = "bcd" - b8/copy-to-eax "bcd"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # var slice/ecx = {eax, ecx} - 51/push-ecx - 50/push-eax - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # eax = slice-equal?(ecx, "Abc") - # . . push args - 68/push "Abc"/imm32 - 51/push-ecx - # . . call - e8/call slice-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-ints-equal(eax, 0, msg) - # . . push args - 68/push "F - test-slice-equal-false"/imm32 - 68/push 0/imm32 - 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 - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-slice-equal-too-long: - # - slice-equal?(slice("Abcd"), "Abc") == 0 - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # (eax..ecx) = "Abcd" - b8/copy-to-eax "Abcd"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # var slice/ecx = {eax, ecx} - 51/push-ecx - 50/push-eax - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # eax = slice-equal?(ecx, "Abc") - # . . push args - 68/push "Abc"/imm32 - 51/push-ecx - # . . call - e8/call slice-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-ints-equal(eax, 0, msg) - # . . push args - 68/push "F - test-slice-equal-too-long"/imm32 - 68/push 0/imm32 - 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 - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-slice-equal-too-short: - # - slice-equal?(slice("A"), "Abc") == 0 - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # (eax..ecx) = "A" - b8/copy-to-eax "A"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # var slice/ecx = {eax, ecx} - 51/push-ecx - 50/push-eax - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # eax = slice-equal?(ecx, "Abc") - # . . push args - 68/push "Abc"/imm32 - 51/push-ecx - # . . call - e8/call slice-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-ints-equal(eax, 0, msg) - # . . push args - 68/push "F - test-slice-equal-too-short"/imm32 - 68/push 0/imm32 - 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 - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-slice-equal-empty: - # - slice-equal?(slice(""), "Abc") == 0 - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # var slice/ecx - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # eax = slice-equal?(ecx, "Abc") - # . . push args - 68/push "Abc"/imm32 - 51/push-ecx - # . . call - e8/call slice-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-ints-equal(eax, 0, msg) - # . . push args - 68/push "F - test-slice-equal-empty"/imm32 - 68/push 0/imm32 - 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 - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-slice-equal-with-empty: - # - slice-equal?(slice("Ab"), "") == 0 - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # (eax..ecx) = "Ab" - b8/copy-to-eax "Ab"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # var slice/ecx = {eax, ecx} - 51/push-ecx - 50/push-eax - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # eax = slice-equal?(ecx, "") - # . . push args - 68/push ""/imm32 - 51/push-ecx - # . . call - e8/call slice-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-ints-equal(eax, 0, msg) - # . . push args - 68/push "F - test-slice-equal-with-empty"/imm32 - 68/push 0/imm32 - 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 - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-slice-equal-empty-with-empty: - # - slice-equal?(slice(""), "") == 1 - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # var slice/ecx - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # eax = slice-equal?(ecx, "") - # . . push args - 68/push ""/imm32 - 51/push-ecx - # . . call - e8/call slice-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-ints-equal(eax, 1, msg) - # . . push args - 68/push "F - test-slice-equal-empty-with-empty"/imm32 - 68/push 1/imm32 - 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 - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-slice-equal-with-null: - # - slice-equal?(slice("Ab"), null) == 0 - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # (eax..ecx) = "Ab" - b8/copy-to-eax "Ab"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # var slice/ecx = {eax, ecx} - 51/push-ecx - 50/push-eax - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # eax = slice-equal?(ecx, 0) - # . . push args - 68/push 0/imm32 - 51/push-ecx - # . . call - e8/call slice-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-ints-equal(eax, 0, msg) - # . . push args - 68/push "F - test-slice-equal-with-null"/imm32 - 68/push 0/imm32 - 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 - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -slice-starts-with?: # s : (address slice), head : (address string) -> eax : boolean - # pseudocode - # lenh = head->length - # if (lenh > s->end - s->start) return false - # i = 0 - # currs = s->start - # currp = head->data - # while i < lenh - # if (*currs != *currh) return false - # ++i - # ++currs - # ++currh - # return true - # - # registers: - # currs: esi - # currh: edi - # *currs: eax - # *currh: ebx - # i: ecx - # lenh: edx - # - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 51/push-ecx - 52/push-edx - 53/push-ebx - 56/push-esi - 57/push-edi - # esi = s - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi - # ecx = s->end - s->start - 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy *(esi+4) to ecx - 2b/subtract 0/mod/indirect 6/rm32/esi . . . 1/r32/ecx . . # subtract *esi from ecx - # edi = head - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 7/r32/edi 0xc/disp8 . # copy *(ebp+12) to edi - # lenh/edx = head->length - 8b/copy 0/mod/indirect 7/rm32/edi . . . 2/r32/edx . . # copy *edi to edx - # if (lenh > s->end - s->start) return false - 39/compare 3/mod/direct 2/rm32/edx . . . 1/r32/ecx . . # compare edx with ecx - 7f/jump-if-greater $slice-starts-with?:false/disp8 - # currs/esi = s->start - 8b/subtract 0/mod/indirect 6/rm32/esi . . . 6/r32/esi . . # copy *esi to esi - # currh/edi = head->data - 81 0/subop/add 3/mod/direct 7/rm32/edi . . . . . 4/imm32 # add to edi - # i/ecx = 0 - 31/xor 3/mod/direct 1/rm32/ecx . . . 1/r32/ecx . . # clear ecx - # eax = ebx = 0 - 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax - 31/xor 3/mod/direct 3/rm32/ebx . . . 3/r32/ebx . . # clear ebx -$slice-starts-with?:loop: - # if (i >= lenh) return true - 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx - 7d/jump-if-greater-or-equal $slice-starts-with?:true/disp8 - # AL = *currs - 8a/copy-byte 0/mod/indirect 6/rm32/esi . . . 0/r32/AL . . # copy byte at *esi to AL - # BL = *currh - 8a/copy-byte 0/mod/indirect 7/rm32/edi . . . 3/r32/BL . . # copy byte at *edi to BL - # if (*currs != *currh) return false - 39/compare 3/mod/direct 0/rm32/eax . . . 3/r32/ebx . . # compare eax and ebx - 75/jump-if-not-equal $slice-starts-with?:false/disp8 - # ++i - 41/increment-ecx - # ++currs - 46/increment-esi - # ++currh - 47/increment-edi - eb/jump $slice-starts-with?:loop/disp8 -$slice-starts-with?:true: - b8/copy-to-eax 1/imm32 - eb/jump $slice-starts-with?:end/disp8 -$slice-starts-with?:false: - b8/copy-to-eax 0/imm32 -$slice-starts-with?:end: - # . restore registers - 5f/pop-to-edi - 5e/pop-to-esi - 5b/pop-to-ebx - 5a/pop-to-edx - 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-slice-starts-with-single-character: - # - slice-starts-with?(slice("Abc"), "A") == 1 - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # (eax..ecx) = "Abc" - b8/copy-to-eax "Abc"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # var slice/ecx = {eax, ecx} - 51/push-ecx - 50/push-eax - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # eax = slice-starts-with?(ecx, "A") - # . . push args - 68/push "A"/imm32 - 51/push-ecx - # . . call - e8/call slice-starts-with?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-ints-equal(eax, 1, msg) - # . . push args - 68/push "F - test-slice-starts-with-single-character"/imm32 - 68/push 1/imm32 - 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 - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-slice-starts-with-empty-string: - # - slice-starts-with?(slice("Abc"), "") == 1 - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # (eax..ecx) = "Abc" - b8/copy-to-eax "Abc"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # var slice/ecx = {eax, ecx} - 51/push-ecx - 50/push-eax - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # eax = slice-starts-with?(ecx, "") - # . . push args - 68/push ""/imm32 - 51/push-ecx - # . . call - e8/call slice-starts-with?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-ints-equal(eax, 1, msg) - # . . push args - 68/push "F - test-slice-starts-with-empty-string"/imm32 - 68/push 1/imm32 - 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 - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-slice-starts-with-multiple-characters: - # - slice-starts-with?(slice("Abc"), "Ab") == 1 - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # (eax..ecx) = "Abc" - b8/copy-to-eax "Abc"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # var slice/ecx = {eax, ecx} - 51/push-ecx - 50/push-eax - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # eax = slice-starts-with?(ecx, "Ab") - # . . push args - 68/push "Ab"/imm32 - 51/push-ecx - # . . call - e8/call slice-starts-with?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-ints-equal(eax, 1, msg) - # . . push args - 68/push "F - test-slice-starts-with-multiple-characters"/imm32 - 68/push 1/imm32 - 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 - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-slice-starts-with-entire-string: - # - slice-starts-with?(slice("Abc"), "Abc") == 1 - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # (eax..ecx) = "Abc" - b8/copy-to-eax "Abc"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # var slice/ecx = {eax, ecx} - 51/push-ecx - 50/push-eax - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # eax = slice-starts-with?(ecx, "Abc") - # . . push args - 68/push "Abc"/imm32 - 51/push-ecx - # . . call - e8/call slice-starts-with?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-ints-equal(eax, 1, msg) - # . . push args - 68/push "F - test-slice-starts-with-entire-string"/imm32 - 68/push 1/imm32 - 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 - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-slice-starts-with-fails: - # - slice-starts-with?(slice("Abc"), "Abd") == 1 - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # (eax..ecx) = "Abc" - b8/copy-to-eax "Abc"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # var slice/ecx = {eax, ecx} - 51/push-ecx - 50/push-eax - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # eax = slice-starts-with?(ecx, "Abd") - # . . push args - 68/push "Abd"/imm32 - 51/push-ecx - # . . call - e8/call slice-starts-with?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-ints-equal(eax, 0, msg) - # . . push args - 68/push "F - test-slice-starts-with-fails"/imm32 - 68/push 0/imm32 - 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 - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-slice-starts-with-fails-2: - # - slice-starts-with?(slice("Abc"), "Ac") == 1 - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # (eax..ecx) = "Abc" - b8/copy-to-eax "Abc"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # var slice/ecx = {eax, ecx} - 51/push-ecx - 50/push-eax - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # eax = slice-starts-with?(ecx, "Ac") - # . . push args - 68/push "Ac"/imm32 - 51/push-ecx - # . . call - e8/call slice-starts-with?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-ints-equal(eax, 0, msg) - # . . push args - 68/push "F - test-slice-starts-with-fails-2"/imm32 - 68/push 0/imm32 - 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 - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -# write a slice to a stream -# abort if the stream doesn't have enough space -write-slice: # out : (address stream), s : (address slice) - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 50/push-eax - 51/push-ecx - 52/push-edx - 53/push-ebx - 56/push-esi - 57/push-edi - # esi = s - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 0xc/disp8 . # copy *(ebp+12) to esi - # curr/ecx = s->start - 8b/copy 0/mod/indirect 6/rm32/esi . . . 1/r32/ecx . . # copy *esi to ecx - # max/esi = s->end - 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 6/r32/esi 4/disp8 . # copy *(esi+4) to esi - # edi = out - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . 7/r32/edi 8/disp8 . # copy *(ebp+8) to edi - # edx = out->length - 8b/copy 1/mod/*+disp8 7/rm32/edi . . . 2/r32/edx 8/disp8 . # copy *(edi+8) to edx - # ebx = out->write - 8b/copy 0/mod/indirect 7/rm32/edi . . . 3/r32/ebx . . # copy *edi to ebx -$write-slice:loop: - # if (curr >= max) break - 39/compare 3/mod/direct 1/rm32/ecx . . . 6/r32/esi . . # compare ecx with esi - 73/jump-if-greater-or-equal-unsigned $write-slice:loop-end/disp8 - # if (out->write >= out->length) abort - 39/compare 3/mod/direct 3/rm32/ebx . . . 2/r32/edx . . # compare ebx with edx - 7d/jump-if-greater-or-equal $write-slice:abort/disp8 - # out->data[out->write] = *in - # . AL = *in - 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax - 8a/copy-byte 0/mod/indirect 1/rm32/ecx . . . 0/r32/AL . . # copy byte at *ecx to AL - # . out->data[out->write] = AL - 88/copy-byte 1/mod/*+disp8 4/rm32/sib 7/base/edi 3/index/ebx . 0/r32/AL 0xc/disp8 . # copy AL to *(edi+ebx+12) - # ++out->write - 43/increment-ebx - # ++in - 41/increment-ecx - eb/jump $write-slice:loop/disp8 -$write-slice:loop-end: - # persist out->write - 89/copy 0/mod/indirect 7/rm32/edi . . . 3/r32/ebx . . # copy ebx to *edi -$write-slice: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 - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -$write-slice:abort: - # . _write(2/stderr, error) - # . . push args - 68/push "write-slice: out of space"/imm32 - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . syscall(exit, 1) - bb/copy-to-ebx 1/imm32 - b8/copy-to-eax 1/imm32/exit - cd/syscall 0x80/imm8 - # never gets here - -test-write-slice: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # 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 - # (eax..ecx) = "Abc" - b8/copy-to-eax "Abc"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # var slice/ecx = {eax, ecx} - 51/push-ecx - 50/push-eax - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # write-slice(_test-stream, slice) - # . . push args - 51/push-ecx - 68/push _test-stream/imm32 - # . . call - e8/call write-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-stream-equal(_test-stream, "Abc", msg) - # . . push args - 68/push "F - test-write-slice"/imm32 - 68/push "Abc"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -# write a slice to a buffered-file -write-slice-buffered: # out : (address buffered-file), s : (address slice) - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 50/push-eax - 51/push-ecx - 52/push-edx - 53/push-ebx - 56/push-esi - 57/push-edi - # esi = s - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 0xc/disp8 . # copy *(ebp+12) to esi - # curr/ecx = s->start - 8b/copy 0/mod/indirect 6/rm32/esi . . . 1/r32/ecx . . # copy *esi to ecx - # max/esi = s->end - 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 6/r32/esi 4/disp8 . # copy *(esi+4) to esi - # edi = out - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . 7/r32/edi 8/disp8 . # copy *(ebp+8) to edi - # edx = out->length - 8b/copy 1/mod/*+disp8 7/rm32/edi . . . 2/r32/edx 0xc/disp8 . # copy *(edi+12) to edx - # ebx = out->write - 8b/copy 1/mod/*+disp8 7/rm32/edi . . . 3/r32/ebx 4/disp8 . # copy *(edi+4) to ebx -$write-slice-buffered:loop: - # if (curr >= max) break - 39/compare 3/mod/direct 1/rm32/ecx . . . 6/r32/esi . . # compare ecx with esi - 73/jump-if-greater-or-equal-unsigned $write-slice-buffered:loop-end/disp8 - # if (out->write >= out->length) flush and clear out's stream - 39/compare 3/mod/direct 3/rm32/ebx . . . 2/r32/edx . . # compare ebx with edx - 7c/jump-if-lesser $write-slice-buffered:to-stream/disp8 - # . persist out->write - 89/copy 1/mod/*+disp8 7/rm32/edi . . . 3/r32/ebx 4/disp8 . # copy ebx to *(edi+4) - # . flush(out) - # . . push args - 57/push-edi - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . clear-stream(stream = out+4) - # . . push args - 8d/copy-address 1/mod/*+disp8 7/rm32/edi . . . 0/r32/eax 4/disp8 . # copy edi+4 to eax - 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 - # . out->write must now be 0; update its cache at ebx - 31/xor 3/mod/direct 3/rm32/ebx . . . 3/r32/ebx . . # clear ebx -$write-slice-buffered:to-stream: - # out->data[out->write] = *in - # . AL = *in - 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax - 8a/copy-byte 0/mod/indirect 1/rm32/ecx . . . 0/r32/AL . . # copy byte at *ecx to AL - # . out->data[out->write] = AL - 88/copy-byte 1/mod/*+disp8 4/rm32/sib 7/base/edi 3/index/ebx . 0/r32/AL 0x10/disp8 . # copy AL to *(edi+ebx+16) - # ++out->write - 43/increment-ebx - # ++in - 41/increment-ecx - eb/jump $write-slice-buffered:loop/disp8 -$write-slice-buffered:loop-end: - # persist necessary variables from registers - 89/copy 1/mod/*+disp8 7/rm32/edi . . . 3/r32/ebx 4/disp8 . # copy ebx to *(edi+4) -$write-slice-buffered: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 - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-write-slice-buffered: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # 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 - # (eax..ecx) = "Abc" - b8/copy-to-eax "Abc"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # var slice/ecx = {eax, ecx} - 51/push-ecx - 50/push-eax - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # write-slice-buffered(_test-buffered-file, slice) - # . . push args - 51/push-ecx - 68/push _test-buffered-file/imm32 - # . . call - e8/call write-slice-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # flush(_test-buffered-file) - # . . push args - 68/push _test-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # check-stream-equal(_test-stream, "Abc", msg) - # . . push args - 68/push "F - test-write-slice-buffered"/imm32 - 68/push "Abc"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -# copy a slice into a new (dynamically allocated) string -slice-to-string: # ad : (address allocation-descriptor), in : (address slice) -> out/eax : (address array) - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 51/push-ecx - 52/push-edx - 53/push-ebx - 56/push-esi - # esi = in - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 0xc/disp8 . # copy *(ebp+12) to esi - # curr/edx = in->start - 8b/copy 0/mod/indirect 6/rm32/esi . . . 2/r32/edx . . # copy *esi to edx - # max/ebx = in->end - 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 3/r32/ebx 4/disp8 . # copy *(esi+4) to ebx - # size/ecx = max - curr + 4 # total size of output string (including the initial length) - 89/copy 3/mod/direct 1/rm32/ecx . . . 3/r32/ebx . . # copy ebx to ecx - 29/subtract 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # subtract edx from ecx - 81 0/subop/add 3/mod/direct 1/rm32/ecx . . . . . 4/imm32 # add to ecx - # out/eax = allocate(ad, size) - # . . push args - 51/push-ecx - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) - # . . call - e8/call allocate/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # if (eax == 0) abort - 3d/compare-eax-and 0/imm32 - 74/jump-if-equal $slice-to-string:abort/disp8 - # *out = size-4 - 89/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy ecx to *eax - 81 5/subop/subtract 0/mod/indirect 0/rm32/eax . . . . . 4/imm32 # subtract 4 from *eax - # save out - 50/push-eax - # eax = _append-4(eax+4, eax+size, curr, max) # clobbering ecx - # . . push args - 53/push-ebx - 52/push-edx - # . . push eax+size (clobbering ecx) - 01/add 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # add eax to ecx - 51/push-ecx - # . . push eax+4 (clobbering eax) - 81 0/subop/add 3/mod/direct 0/rm32/eax . . . . . 4/imm32 # add to eax - 50/push-eax - # . . call - e8/call _append-4/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp - # restore out (assumes _append-4 can't error) - 58/pop-to-eax -$slice-to-string:end: - # . restore registers - 5e/pop-to-esi - 5b/pop-to-ebx - 5a/pop-to-edx - 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 - -$slice-to-string:abort: - # . _write(2/stderr, error) - # . . push args - 68/push "slice-to-string: out of space\n"/imm32 - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . syscall(exit, 1) - bb/copy-to-ebx 1/imm32 - b8/copy-to-eax 1/imm32/exit - cd/syscall 0x80/imm8 - # never gets here - -test-slice-to-string: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # var heap/edx : (address allocation-descriptor) = {0, 0} - 68/push 0/imm32/limit - 68/push 0/imm32/curr - 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx - # heap = new-segment(512) - # . . push args - 52/push-edx - 68/push 0x200/imm32 - # . . call - e8/call new-segment/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # (eax..ecx) = "Abc" - b8/copy-to-eax "Abc"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # var slice/ecx = {eax, ecx} - 51/push-ecx - 50/push-eax - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # eax = slice-to-string(heap, slice) - # . . push args - 51/push-ecx - 52/push-edx - # . . call - e8/call slice-to-string/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # dump word-slice {{{ -#? # . write(2/stderr, "AA: ") -#? # . . push args -#? 68/push "AA: "/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # . write(2/stderr, eax) -#? # . . push args -#? 50/push-eax -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # }}} - # eax = string-equal?(eax, "Abc") - # . . push args - 68/push "Abc"/imm32 - 50/push-eax - # . . call - e8/call string-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-ints-equal(eax, 1, msg) - # . . push args - 68/push "F - test-slice-to-string"/imm32 - 68/push 1/imm32/true - 50/push-eax - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -# . . vim:nowrap:textwidth=0 diff --git a/082slurp.subx b/082slurp.subx new file mode 100644 index 00000000..36a95d48 --- /dev/null +++ b/082slurp.subx @@ -0,0 +1,161 @@ +== 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 + +# read all bytes from 'f' and store them into 's' +# abort if 's' is too small +slurp: # f : (address buffered-file), s : (address stream byte) -> + # pseudocode: + # while true + # if (s->write >= s->length) abort + # if (f->read >= f->write) populate stream from file + # if (f->write == 0) break + # AL = f->data[f->read] + # s->data[s->write] = AL + # ++f->read + # ++s->write + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 50/push-eax + 51/push-ecx + 52/push-edx + 56/push-esi + 57/push-edi + # esi = f + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi + # ecx = f->read + 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 8/disp8 . # copy *(esi+8) to ecx + # edi = s + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 7/r32/edi 0xc/disp8 . # copy *(ebp+12) to edi + # edx = s->write + 8b/copy 0/mod/indirect 7/rm32/edi . . . 2/r32/edx . . # copy *edi to edx +$slurp:loop: + # if (s->write >= s->length) abort + 3b/compare 1/mod/*+disp8 7/rm32/edi . . . 2/r32/edx 8/disp8 . # compare edx with *(edi+8) + 7d/jump-if-greater-or-equal $slurp:abort/disp8 + # if (f->read >= f->write) populate stream from file + 3b/compare 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # compare ecx with *(esi+4) + 7c/jump-if-lesser $slurp:from-stream/disp8 + # . clear-stream(stream = f+4) + # . . push args + 8d/copy-address 1/mod/*+disp8 6/rm32/esi . . . 0/r32/eax 4/disp8 . # copy esi+4 to eax + 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 + # . f->read must now be 0; update its cache at ecx + 31/xor 3/mod/direct 1/rm32/ecx . . . 1/r32/ecx . . # clear ecx + # . eax = read(f->fd, stream = f+4) + # . . push args + 50/push-eax + ff 6/subop/push 0/mod/indirect 6/rm32/esi . . . . . . # push *esi + # . . call + e8/call read/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # if (f->write == 0) break + # since f->read was initially 0, eax is the same as f->write + # . if (eax == 0) return true + 3d/compare-eax-and 0/imm32 + 74/jump-if-equal $slurp:end/disp8 +$slurp:from-stream: + # AL = f->data[f->read] + 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax + 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/esi 1/index/ecx . 0/r32/AL 0x10/disp8 . # copy byte at *(esi+ecx+16) to AL + # s->data[s->write] = AL + 88/copy-byte 1/mod/*+disp8 4/rm32/sib 7/base/edi 2/index/edx . 0/r32/AL 0xc/disp8 . # copy AL to *(edi+edx+12) + # ++f->read + 41/increment-ecx + # ++s->write + 42/increment-edx + eb/jump $slurp:loop/disp8 +$slurp:end: + # save f->read + 89/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 8/disp8 . # copy ecx to *(esi+8) + # save s->write + 89/copy 0/mod/indirect 7/rm32/edi . . . 2/r32/edx . . # copy edx to *edi + # . restore registers + 5f/pop-to-edi + 5e/pop-to-esi + 5a/pop-to-edx + 59/pop-to-ecx + 58/pop-to-eax + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +$slurp:abort: + # . _write(2/stderr, error) + # . . push args + 68/push "slurp: destination too small\n"/imm32 + 68/push 2/imm32/stderr + # . . call + e8/call _write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . syscall(exit, 1) + bb/copy-to-ebx 1/imm32 + b8/copy-to-eax 1/imm32/exit + cd/syscall 0x80/imm8 + # never gets here + +test-slurp: + # 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 + # . clear-stream(_test-tmp-stream) + # . . push args + 68/push _test-tmp-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 + # write(_test-stream, "ab\ncd") + # . . push args + 68/push "ab\ncd"/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 + # read a line from _test-stream (buffered by _test-buffered-file) into _test-tmp-stream + # . eax = slurp(_test-buffered-file, _test-tmp-stream) + # . . push args + 68/push _test-tmp-stream/imm32 + 68/push _test-buffered-file/imm32 + # . . call + e8/call slurp/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-stream-equal(_test-tmp-stream, "ab\ncd", msg) + # . . push args + 68/push "F - test-slurp"/imm32 + 68/push "ab\ncd"/imm32 + 68/push _test-tmp-stream/imm32 + # . . call + e8/call check-stream-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 diff --git a/083next-token.subx b/083next-token.subx deleted file mode 100644 index 73a658c1..00000000 --- a/083next-token.subx +++ /dev/null @@ -1,1923 +0,0 @@ -# Some tokenization primitives. - -== 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 - -# extract the next run of characters that are different from a given 'delimiter' (skipping multiple delimiters if necessary) -# on reaching end of file, return an empty interval -next-token: # in : (address stream), delimiter : byte, out : (address slice) - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 50/push-eax - 51/push-ecx - 56/push-esi - 57/push-edi - # 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 0x10/disp8 . # copy *(ebp+16) to edi - # skip-chars-matching(in, delimiter) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) - 56/push-esi - # . . call - e8/call skip-chars-matching/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # out->start = &in->data[in->read] - 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy *(esi+4) to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/esi 1/index/ecx . 0/r32/eax 0xc/disp8 . # copy esi+ecx+12 to eax - 89/copy 0/mod/indirect 7/rm32/edi . . . 0/r32/eax . . # copy eax to *edi - # skip-chars-not-matching(in, delimiter) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) - 56/push-esi - # . . call - e8/call skip-chars-not-matching/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # out->end = &in->data[in->read] - 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy *(esi+4) to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/esi 1/index/ecx . 0/r32/eax 0xc/disp8 . # copy esi+ecx+12 to eax - 89/copy 1/mod/*+disp8 7/rm32/edi . . . 0/r32/eax 4/disp8 . # copy eax to *(edi+4) - # . restore registers - 5f/pop-to-edi - 5e/pop-to-esi - 59/pop-to-ecx - 58/pop-to-eax - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-next-token: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # 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 - # var slice/ecx = {0, 0} - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # write(_test-stream, " ab") - # . . push args - 68/push " ab"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # next-token(_test-stream, 0x20/space, slice) - # . . push args - 51/push-ecx - 68/push 0x20/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call next-token/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # check-ints-equal(slice->start - _test-stream->data, 2, msg) - # . check-ints-equal(slice->start - _test-stream, 14, msg) - # . . push args - 68/push "F - test-next-token: start"/imm32 - 68/push 0xe/imm32 - # . . push slice->start - _test-stream - 8b/copy 0/mod/indirect 1/rm32/ecx . . . 0/r32/eax . . # copy *ecx to eax - 81 5/subop/subtract 3/mod/direct 0/rm32/eax . . . . . _test-stream/imm32 # subtract from eax - 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 - # check-ints-equal(slice->end - _test-stream->data, 4, msg) - # . check-ints-equal(slice->end - _test-stream, 16, msg) - # . . push args - 68/push "F - test-next-token: end"/imm32 - 68/push 0x10/imm32 - # . . push slice->end - _test-stream - 8b/copy 1/mod/*+disp8 1/rm32/ecx . . . 0/r32/eax 4/disp8 . # copy *(ecx+4) to eax - 81 5/subop/subtract 3/mod/direct 0/rm32/eax . . . . . _test-stream/imm32 # subtract from eax - 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 - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-next-token-Eof: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # 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 - # var slice/ecx = {0, 0} - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # write nothing to _test-stream - # next-token(_test-stream, 0x20/space, slice) - # . . push args - 51/push-ecx - 68/push 0x20/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call next-token/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # check-ints-equal(slice->end, slice->start, msg) - # . . push args - 68/push "F - test-next-token-Eof"/imm32 - ff 6/subop/push 1/mod/*+disp8 1/rm32/ecx . . . . 4/disp8 . # push *(ecx+4) - ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -# extract the next run of characters that are different from a given 'delimiter' (skipping multiple delimiters if necessary) -# on reaching end of file, return an empty interval -next-token-from-slice: # start : (address byte), end : (address byte), delimiter : byte, out : (address slice) -> - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 50/push-eax - 51/push-ecx - 52/push-edx - 57/push-edi - # ecx = end - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 0xc/disp8 . # copy *(ebp+12) to ecx - # edx = delimiter - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 2/r32/edx 0x10/disp8 . # copy *(ebp+16) to edx - # edi = out - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 7/r32/edi 0x14/disp8 . # copy *(ebp+20) to edi - # eax = skip-chars-matching-in-slice(start, end, delimiter) - # . . push args - 52/push-edx - 51/push-ecx - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) - # . . call - e8/call skip-chars-matching-in-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # out->start = eax - 89/copy 0/mod/indirect 7/rm32/edi . . . 0/r32/eax . . # copy eax to *edi - # eax = skip-chars-not-matching-in-slice(eax, end, delimiter) - # . . push args - 52/push-edx - 51/push-ecx - 50/push-eax - # . . call - e8/call skip-chars-not-matching-in-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # out->end = eax - 89/copy 1/mod/*+disp8 7/rm32/edi . . . 0/r32/eax 4/disp8 . # copy eax to *(edi+4) - # . restore registers - 5f/pop-to-edi - 5a/pop-to-edx - 59/pop-to-ecx - 58/pop-to-eax - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-next-token-from-slice: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # (eax..ecx) = " ab" - b8/copy-to-eax " ab"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # var out/edi : (address slice) = {0, 0} - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 7/rm32/edi . . . 4/r32/esp . . # copy esp to edi - # next-token-from-slice(eax, ecx, 0x20/space, out) - # . . push args - 57/push-edi - 68/push 0x20/imm32 - 51/push-ecx - 50/push-eax - # . . call - e8/call next-token-from-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp - # out->start should be at the 'a' - # . check-ints-equal(out->start - in->start, 2, msg) - # . . push args - 68/push "F - test-next-token-from-slice: start"/imm32 - 68/push 2/imm32 - # . . push out->start - in->start - 8b/copy 0/mod/indirect 7/rm32/edi . . . 1/r32/ecx . . # copy *edi to ecx - 2b/subtract 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # subtract eax from ecx - 51/push-ecx - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # out->end should be after the 'b' - # check-ints-equal(out->end - in->start, 4, msg) - # . . push args - 68/push "F - test-next-token-from-slice: end"/imm32 - 68/push 4/imm32 - # . . push out->end - in->start - 8b/copy 1/mod/*+disp8 7/rm32/edi . . . 1/r32/ecx 4/disp8 . # copy *(edi+4) to ecx - 2b/subtract 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # subtract eax from ecx - 51/push-ecx - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-next-token-from-slice-Eof: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # var out/edi : (address slice) = {0, 0} - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 7/rm32/edi . . . 4/r32/esp . . # copy esp to edi - # next-token-from-slice(0, 0, 0x20/space, out) - # . . push args - 57/push-edi - 68/push 0x20/imm32 - 68/push 0/imm32 - 68/push 0/imm32 - # . . call - e8/call next-token-from-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp - # out should be empty - # . check-ints-equal(out->end - out->start, 0, msg) - # . . push args - 68/push "F - test-next-token-from-slice-Eof"/imm32 - 68/push 0/imm32 - # . . push out->start - in->start - 8b/copy 1/mod/*+disp8 7/rm32/edi . . . 1/r32/ecx 4/disp8 . # copy *(edi+4) to ecx - 2b/subtract 0/mod/indirect 7/rm32/edi . . . 1/r32/ecx . . # subtract *edi from ecx - 51/push-ecx - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-next-token-from-slice-nothing: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # (eax..ecx) = " " - b8/copy-to-eax " "/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # var out/edi : (address slice) = {0, 0} - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 7/rm32/edi . . . 4/r32/esp . . # copy esp to edi - # next-token-from-slice(in, 0x20/space, out) - # . . push args - 57/push-edi - 68/push 0x20/imm32 - 51/push-ecx - 50/push-eax - # . . call - e8/call next-token-from-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp - # out should be empty - # . check-ints-equal(out->end - out->start, 0, msg) - # . . push args - 68/push "F - test-next-token-from-slice-Eof"/imm32 - 68/push 0/imm32 - # . . push out->start - in->start - 8b/copy 1/mod/*+disp8 7/rm32/edi . . . 1/r32/ecx 4/disp8 . # copy *(edi+4) to ecx - 2b/subtract 0/mod/indirect 7/rm32/edi . . . 1/r32/ecx . . # subtract *edi from ecx - 51/push-ecx - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -skip-chars-matching: # in : (address stream), delimiter : byte - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 50/push-eax - 51/push-ecx - 52/push-edx - 53/push-ebx - 56/push-esi - # esi = in - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi - # ecx = in->read - 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy *(esi+4) to ecx - # ebx = in->write - 8b/copy 0/mod/indirect 6/rm32/esi . . . 3/r32/ebx . . # copy *esi to ebx - # edx = delimiter - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 2/r32/edx 0xc/disp8 . # copy *(ebp+12) to edx -$skip-chars-matching:loop: - # if (in->read >= in->write) break - 39/compare 3/mod/direct 1/rm32/ecx . . . 3/r32/ebx . . # compare ecx with ebx - 7d/jump-if-greater-or-equal $skip-chars-matching:end/disp8 - # eax = in->data[in->read] - 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax - 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/esi 1/index/ecx . 0/r32/AL 0xc/disp8 . # copy byte at *(esi+ecx+12) to AL - # if (eax != delimiter) break - 39/compare 3/mod/direct 0/rm32/eax . . . 2/r32/edx . . # compare eax and edx - 75/jump-if-not-equal $skip-chars-matching:end/disp8 - # ++in->read - 41/increment-ecx - eb/jump $skip-chars-matching:loop/disp8 -$skip-chars-matching:end: - # persist in->read - 89/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy ecx to *(esi+4) - # . restore registers - 5e/pop-to-esi - 5b/pop-to-ebx - 5a/pop-to-edx - 59/pop-to-ecx - 58/pop-to-eax - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-skip-chars-matching: - # 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 - # write(_test-stream, " ab") - # . . push args - 68/push " ab"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # skip-chars-matching(_test-stream, 0x20/space) - # . . push args - 68/push 0x20/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call skip-chars-matching/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-ints-equal(_test-stream->read, 2, msg) - # . . push args - 68/push "F - test-skip-chars-matching"/imm32 - 68/push 2/imm32 - # . . push *_test-stream->read - b8/copy-to-eax _test-stream/imm32 - 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 - # end - c3/return - -test-skip-chars-matching-none: - # 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 - # write(_test-stream, "ab") - # . . push args - 68/push "ab"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # skip-chars-matching(_test-stream, 0x20/space) - # . . push args - 68/push 0x20/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call skip-chars-matching/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-ints-equal(_test-stream->read, 0, msg) - # . . push args - 68/push "F - test-skip-chars-matching-none"/imm32 - 68/push 0/imm32 - # . . push *_test-stream->read - b8/copy-to-eax _test-stream/imm32 - 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 - # end - c3/return - -skip-chars-matching-whitespace: # in : (address stream) - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 50/push-eax - 51/push-ecx - 53/push-ebx - 56/push-esi - # esi = in - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi - # ecx = in->read - 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy *(esi+4) to ecx - # ebx = in->write - 8b/copy 0/mod/indirect 6/rm32/esi . . . 3/r32/ebx . . # copy *esi to ebx -$skip-chars-matching-whitespace:loop: - # if (in->read >= in->write) break - 39/compare 3/mod/direct 1/rm32/ecx . . . 3/r32/ebx . . # compare ecx with ebx - 7d/jump-if-greater-or-equal $skip-chars-matching-whitespace:end/disp8 - # eax = in->data[in->read] - 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax - 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/esi 1/index/ecx . 0/r32/AL 0xc/disp8 . # copy byte at *(esi+ecx+12) to AL - # if (eax == ' ') goto body - 3d/compare-eax-and 0x20/imm32/space - 74/jump-if-equal $skip-chars-matching-whitespace:body/disp8 - # if (eax == '\n') goto body - 3d/compare-eax-and 0x0a/imm32/newline - 74/jump-if-equal $skip-chars-matching-whitespace:body/disp8 - # if (eax == '\t') goto body - 3d/compare-eax-and 0x09/imm32/tab - 74/jump-if-equal $skip-chars-matching-whitespace:body/disp8 - # if (eax != '\r') break - 3d/compare-eax-and 0x0d/imm32/cr - 75/jump-if-not-equal $skip-chars-matching-whitespace:end/disp8 -$skip-chars-matching-whitespace:body: - # ++in->read - 41/increment-ecx - eb/jump $skip-chars-matching-whitespace:loop/disp8 -$skip-chars-matching-whitespace:end: - # persist in->read - 89/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy ecx to *(esi+4) - # . restore registers - 5e/pop-to-esi - 5b/pop-to-ebx - 59/pop-to-ecx - 58/pop-to-eax - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-skip-chars-matching-whitespace: - # 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 - # write(_test-stream, " \nab") - # . . push args - 68/push " \nab"/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-chars-matching-whitespace(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call skip-chars-matching-whitespace/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # check-ints-equal(_test-stream->read, 2, msg) - # . . push args - 68/push "F - test-skip-chars-matching-whitespace"/imm32 - 68/push 2/imm32 - # . . push *_test-stream->read - b8/copy-to-eax _test-stream/imm32 - 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 - # end - c3/return - -# minor fork of 'skip-chars-matching' -skip-chars-not-matching: # in : (address stream), delimiter : byte - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 50/push-eax - 51/push-ecx - 52/push-edx - 53/push-ebx - 56/push-esi - # esi = in - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi - # ecx = in->read - 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy *(esi+4) to ecx - # ebx = in->write - 8b/copy 0/mod/indirect 6/rm32/esi . . . 3/r32/ebx . . # copy *esi to ebx - # edx = delimiter - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 2/r32/edx 0xc/disp8 . # copy *(ebp+12) to edx -$skip-chars-not-matching:loop: - # if (in->read >= in->write) break - 39/compare 3/mod/direct 1/rm32/ecx . . . 3/r32/ebx . . # compare ecx with ebx - 7d/jump-if-greater-or-equal $skip-chars-not-matching:end/disp8 - # eax = in->data[in->read] - 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax - 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/esi 1/index/ecx . 0/r32/AL 0xc/disp8 . # copy byte at *(esi+ecx+12) to AL - # if (eax == delimiter) break - 39/compare 3/mod/direct 0/rm32/eax . . . 2/r32/edx . . # compare eax and edx - 74/jump-if-equal $skip-chars-not-matching:end/disp8 - # ++in->read - 41/increment-ecx - eb/jump $skip-chars-not-matching:loop/disp8 -$skip-chars-not-matching:end: - # persist in->read - 89/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy ecx to *(esi+4) - # . restore registers - 5e/pop-to-esi - 5b/pop-to-ebx - 5a/pop-to-edx - 59/pop-to-ecx - 58/pop-to-eax - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-skip-chars-not-matching: - # 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 - # write(_test-stream, "ab ") - # . . push args - 68/push "ab "/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # skip-chars-not-matching(_test-stream, 0x20/space) - # . . push args - 68/push 0x20/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call skip-chars-not-matching/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-ints-equal(_test-stream->read, 2, msg) - # . . push args - 68/push "F - test-skip-chars-not-matching"/imm32 - 68/push 2/imm32 - # . . push *_test-stream->read - b8/copy-to-eax _test-stream/imm32 - 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 - # end - c3/return - -test-skip-chars-not-matching-none: - # 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 - # write(_test-stream, " ab") - # . . push args - 68/push " ab"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # skip-chars-not-matching(_test-stream, 0x20/space) - # . . push args - 68/push 0x20/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call skip-chars-not-matching/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-ints-equal(_test-stream->read, 0, msg) - # . . push args - 68/push "F - test-skip-chars-not-matching-none"/imm32 - 68/push 0/imm32 - # . . push *_test-stream->read - b8/copy-to-eax _test-stream/imm32 - 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 - # end - c3/return - -test-skip-chars-not-matching-all: - # 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 - # write(_test-stream, "ab") - # . . push args - 68/push "ab"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # skip-chars-not-matching(_test-stream, 0x20/space) - # . . push args - 68/push 0x20/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call skip-chars-not-matching/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-ints-equal(_test-stream->read, 2, msg) - # . . push args - 68/push "F - test-skip-chars-not-matching-all"/imm32 - 68/push 2/imm32 - # . . push *_test-stream->read - b8/copy-to-eax _test-stream/imm32 - 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 - # end - c3/return - -skip-chars-not-matching-whitespace: # in : (address stream) - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 50/push-eax - 51/push-ecx - 53/push-ebx - 56/push-esi - # esi = in - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi - # ecx = in->read - 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy *(esi+4) to ecx - # ebx = in->write - 8b/copy 0/mod/indirect 6/rm32/esi . . . 3/r32/ebx . . # copy *esi to ebx -$skip-chars-not-matching-whitespace:loop: - # if (in->read >= in->write) break - 39/compare 3/mod/direct 1/rm32/ecx . . . 3/r32/ebx . . # compare ecx with ebx - 7d/jump-if-greater-or-equal $skip-chars-not-matching-whitespace:end/disp8 - # eax = in->data[in->read] - 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax - 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/esi 1/index/ecx . 0/r32/AL 0xc/disp8 . # copy byte at *(esi+ecx+12) to AL - # if (eax == ' ') break - 3d/compare-eax-and 0x20/imm32/space - 74/jump-if-equal $skip-chars-not-matching-whitespace:end/disp8 - # if (eax == '\n') break - 3d/compare-eax-and 0x0a/imm32/newline - 74/jump-if-equal $skip-chars-not-matching-whitespace:end/disp8 - # if (eax == '\t') break - 3d/compare-eax-and 0x09/imm32/tab - 74/jump-if-equal $skip-chars-not-matching-whitespace:end/disp8 - # if (eax == '\r') break - 3d/compare-eax-and 0x0d/imm32/cr - 74/jump-if-equal $skip-chars-not-matching-whitespace:end/disp8 - # ++in->read - 41/increment-ecx - eb/jump $skip-chars-not-matching-whitespace:loop/disp8 -$skip-chars-not-matching-whitespace:end: - # persist in->read - 89/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy ecx to *(esi+4) - # . restore registers - 5e/pop-to-esi - 5b/pop-to-ebx - 59/pop-to-ecx - 58/pop-to-eax - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-skip-chars-not-matching-whitespace: - # 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 - # write(_test-stream, "ab\n") - # . . push args - 68/push "ab\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 - # skip-chars-not-matching-whitespace(_test-stream) - # . . push args - 68/push _test-stream/imm32 - # . . call - e8/call skip-chars-not-matching-whitespace/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # check-ints-equal(_test-stream->read, 2, msg) - # . . push args - 68/push "F - test-skip-chars-not-matching-whitespace"/imm32 - 68/push 2/imm32 - # . . push *_test-stream->read - b8/copy-to-eax _test-stream/imm32 - 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 - # end - c3/return - -skip-chars-matching-in-slice: # curr : (address byte), end : (address byte), delimiter : byte -> curr/eax - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 51/push-ecx - 52/push-edx - 53/push-ebx - # eax = curr - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 8/disp8 . # copy *(ebp+8) to eax - # ecx = end - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 0xc/disp8 . # copy *(ebp+12) to ecx - # edx = delimiter - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 2/r32/edx 0x10/disp8 . # copy *(ebp+16) to edx - # ebx = 0 - 31/xor 3/mod/direct 3/rm32/ebx . . . 3/r32/ebx . . # clear ebx -$skip-chars-matching-in-slice:loop: - # if (curr >= end) break - 39/compare 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # compare eax with ecx - 73/jump-if-greater-or-equal-unsigned $skip-chars-matching-in-slice:end/disp8 - # if (*curr != delimiter) break - 8a/copy-byte 0/mod/indirect 0/rm32/eax . . . 3/r32/BL . . # copy byte at *eax to BL - 39/compare 3/mod/direct 3/rm32/ebx . . . 2/r32/edx . . # compare ebx and edx - 75/jump-if-not-equal $skip-chars-matching-in-slice:end/disp8 - # ++curr - 40/increment-eax - eb/jump $skip-chars-matching-in-slice:loop/disp8 -$skip-chars-matching-in-slice:end: - # . restore registers - 5b/pop-to-ebx - 5a/pop-to-edx - 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-skip-chars-matching-in-slice: - # (eax..ecx) = " ab" - b8/copy-to-eax " ab"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # eax = skip-chars-matching-in-slice(eax, ecx, 0x20/space) - # . . push args - 68/push 0x20/imm32/space - 51/push-ecx - 50/push-eax - # . . call - e8/call skip-chars-matching-in-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # check-ints-equal(ecx-eax, 2, msg) - # . . push args - 68/push "F - test-skip-chars-matching-in-slice"/imm32 - 68/push 2/imm32 - # . . push ecx-eax - 29/subtract 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # subtract eax from ecx - 51/push-ecx - # . . 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 - -test-skip-chars-matching-in-slice-none: - # (eax..ecx) = "ab" - b8/copy-to-eax "ab"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # eax = skip-chars-matching-in-slice(eax, ecx, 0x20/space) - # . . push args - 68/push 0x20/imm32/space - 51/push-ecx - 50/push-eax - # . . call - e8/call skip-chars-matching-in-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # check-ints-equal(ecx-eax, 2, msg) - # . . push args - 68/push "F - test-skip-chars-matching-in-slice-none"/imm32 - 68/push 2/imm32 - # . . push ecx-eax - 29/subtract 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # subtract eax from ecx - 51/push-ecx - # . . 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 - -skip-chars-matching-whitespace-in-slice: # in : (address stream) - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 51/push-ecx - 53/push-ebx - # eax = curr - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 8/disp8 . # copy *(ebp+8) to eax - # ecx = end - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 0xc/disp8 . # copy *(ebp+12) to ecx - # ebx = 0 - 31/xor 3/mod/direct 3/rm32/ebx . . . 3/r32/ebx . . # clear ebx -$skip-chars-matching-whitespace-in-slice:loop: - # if (curr >= end) break - 39/compare 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # compare eax with ecx - 0f 83/jump-if-greater-or-equal-unsigned $skip-chars-matching-in-slice:end/disp32 - # ebx = *curr - 8a/copy-byte 0/mod/indirect 0/rm32/eax . . . 3/r32/BL . . # copy byte at *eax to BL - # if (*curr == ' ') goto body - 81 7/subop/compare 3/mod/direct 3/rm32/ebx . . . . . 0x20/imm32/space # compare ebx - 74/jump-if-equal $skip-chars-matching-whitespace-in-slice:body/disp8 - # if (*curr == '\n') goto body - 81 7/subop/compare 3/mod/direct 3/rm32/ebx . . . . . 0x0a/imm32/newline # compare ebx - 74/jump-if-equal $skip-chars-matching-whitespace-in-slice:body/disp8 - # if (*curr == '\t') goto body - 81 7/subop/compare 3/mod/direct 3/rm32/ebx . . . . . 0x09/imm32/tab # compare ebx - 74/jump-if-equal $skip-chars-matching-whitespace-in-slice:body/disp8 - # if (*curr != '\r') break - 81 7/subop/compare 3/mod/direct 3/rm32/ebx . . . . . 0x0d/imm32/cr # compare ebx - 75/jump-if-not-equal $skip-chars-matching-whitespace-in-slice:end/disp8 -$skip-chars-matching-whitespace-in-slice:body: - # ++curr - 40/increment-eax - eb/jump $skip-chars-matching-whitespace-in-slice:loop/disp8 -$skip-chars-matching-whitespace-in-slice:end: - # . restore registers - 5b/pop-to-ebx - 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-skip-chars-matching-whitespace-in-slice: - # (eax..ecx) = " \nab" - b8/copy-to-eax " \nab"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # eax = skip-chars-matching-whitespace-in-slice(eax, ecx) - # . . push args - 51/push-ecx - 50/push-eax - # . . call - e8/call skip-chars-matching-whitespace-in-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-ints-equal(ecx-eax, 2, msg) - # . . push args - 68/push "F - test-skip-chars-matching-whitespace-in-slice"/imm32 - 68/push 2/imm32 - # . . push ecx-eax - 29/subtract 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # subtract eax from ecx - 51/push-ecx - # . . 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 - -# minor fork of 'skip-chars-matching-in-slice' -skip-chars-not-matching-in-slice: # curr : (address byte), end : (address byte), delimiter : byte -> curr/eax - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 51/push-ecx - 52/push-edx - 53/push-ebx - # eax = curr - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 8/disp8 . # copy *(ebp+8) to eax - # ecx = end - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 0xc/disp8 . # copy *(ebp+12) to ecx - # edx = delimiter - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 2/r32/edx 0x10/disp8 . # copy *(ebp+16) to edx - # ebx = 0 - 31/xor 3/mod/direct 3/rm32/ebx . . . 3/r32/ebx . . # clear ebx -$skip-chars-not-matching-in-slice:loop: - # if (curr >= end) break - 39/compare 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # compare eax with ecx - 73/jump-if-greater-or-equal-unsigned $skip-chars-not-matching-in-slice:end/disp8 - # if (*curr == delimiter) break - 8a/copy-byte 0/mod/indirect 0/rm32/eax . . . 3/r32/BL . . # copy byte at *eax to BL - 39/compare 3/mod/direct 3/rm32/ebx . . . 2/r32/edx . . # compare ebx and edx - 74/jump-if-equal $skip-chars-not-matching-in-slice:end/disp8 - # ++curr - 40/increment-eax - eb/jump $skip-chars-not-matching-in-slice:loop/disp8 -$skip-chars-not-matching-in-slice:end: - # . restore registers - 5b/pop-to-ebx - 5a/pop-to-edx - 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-skip-chars-not-matching-in-slice: - # (eax..ecx) = "ab " - b8/copy-to-eax "ab "/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # eax = skip-chars-not-matching-in-slice(eax, ecx, 0x20/space) - # . . push args - 68/push 0x20/imm32/space - 51/push-ecx - 50/push-eax - # . . call - e8/call skip-chars-not-matching-in-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # check-ints-equal(ecx-eax, 1, msg) - # . . push args - 68/push "F - test-skip-chars-not-matching-in-slice"/imm32 - 68/push 1/imm32 - # . . push ecx-eax - 29/subtract 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # subtract eax from ecx - 51/push-ecx - # . . 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 - -test-skip-chars-not-matching-in-slice-none: - # (eax..ecx) = " ab" - b8/copy-to-eax " ab"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # eax = skip-chars-not-matching-in-slice(eax, ecx, 0x20/space) - # . . push args - 68/push 0x20/imm32/space - 51/push-ecx - 50/push-eax - # . . call - e8/call skip-chars-not-matching-in-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # check-ints-equal(ecx-eax, 3, msg) - # . . push args - 68/push "F - test-skip-chars-not-matching-in-slice-none"/imm32 - 68/push 3/imm32 - # . . push ecx-eax - 29/subtract 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # subtract eax from ecx - 51/push-ecx - # . . 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 - -test-skip-chars-not-matching-in-slice-all: - # (eax..ecx) = "ab" - b8/copy-to-eax "ab"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # eax = skip-chars-not-matching-in-slice(eax, ecx, 0x20/space) - # . . push args - 68/push 0x20/imm32/space - 51/push-ecx - 50/push-eax - # . . call - e8/call skip-chars-not-matching-in-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # check-ints-equal(ecx-eax, 0, msg) - # . . push args - 68/push "F - test-skip-chars-not-matching-in-slice-all"/imm32 - 68/push 0/imm32 - # . . push ecx-eax - 29/subtract 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # subtract eax from ecx - 51/push-ecx - # . . 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 - -skip-chars-not-matching-whitespace-in-slice: # in : (address stream) - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 51/push-ecx - 53/push-ebx - # eax = curr - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 8/disp8 . # copy *(ebp+8) to eax - # ecx = end - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 0xc/disp8 . # copy *(ebp+12) to ecx - # ebx = 0 - 31/xor 3/mod/direct 3/rm32/ebx . . . 3/r32/ebx . . # clear ebx -$skip-chars-not-matching-whitespace-in-slice:loop: - # if (curr >= end) break - 39/compare 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # compare eax with ecx - 0f 83/jump-if-greater-or-equal-unsigned $skip-chars-not-matching-in-slice:end/disp32 - # ebx = *curr - 8a/copy-byte 0/mod/indirect 0/rm32/eax . . . 3/r32/BL . . # copy byte at *eax to BL - # if (*curr == ' ') break - 81 7/subop/compare 3/mod/direct 3/rm32/ebx . . . . . 0x20/imm32/space # compare ebx - 74/jump-if-equal $skip-chars-not-matching-whitespace-in-slice:end/disp8 - # if (*curr == '\n') break - 81 7/subop/compare 3/mod/direct 3/rm32/ebx . . . . . 0x0a/imm32/newline # compare ebx - 74/jump-if-equal $skip-chars-not-matching-whitespace-in-slice:end/disp8 - # if (*curr == '\t') break - 81 7/subop/compare 3/mod/direct 3/rm32/ebx . . . . . 0x09/imm32/tab # compare ebx - 74/jump-if-equal $skip-chars-not-matching-whitespace-in-slice:end/disp8 - # if (*curr == '\r') break - 81 7/subop/compare 3/mod/direct 3/rm32/ebx . . . . . 0x0d/imm32/cr # compare ebx - 74/jump-if-equal $skip-chars-not-matching-whitespace-in-slice:end/disp8 - # ++curr - 40/increment-eax - eb/jump $skip-chars-not-matching-whitespace-in-slice:loop/disp8 -$skip-chars-not-matching-whitespace-in-slice:end: - # . restore registers - 5b/pop-to-ebx - 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-skip-chars-not-matching-whitespace-in-slice: - # (eax..ecx) = "ab\n" - b8/copy-to-eax "ab\n"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # eax = skip-chars-not-matching-whitespace-in-slice(eax, ecx) - # . . push args - 51/push-ecx - 50/push-eax - # . . call - e8/call skip-chars-not-matching-whitespace-in-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-ints-equal(ecx-eax, 1, msg) - # . . push args - 68/push "F - test-skip-chars-not-matching-whitespace-in-slice"/imm32 - 68/push 1/imm32 - # . . push ecx-eax - 29/subtract 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # subtract eax from ecx - 51/push-ecx - # . . 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 - -# update line->read to end of string literal surrounded by double quotes -# line->read must start out at a double-quote -skip-string: # line : (address stream) - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 50/push-eax - 51/push-ecx - 52/push-edx - # ecx = line - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . 1/r32/ecx 8/disp8 . # copy *(ebp+8) to ecx - # eax = skip-string-in-slice(&line->data[line->read], &line->data[line->write]) - # . . push &line->data[line->write] - 8b/copy 1/mod/*+disp8 1/rm32/ecx . . 2/r32/edx 8/disp8 . # copy *(ecx+8) to edx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 1/base/ecx 2/index/edx . 2/r32/edx 0xc/disp8 . # copy ecx+edx+12 to edx - 52/push-edx - # . . push &line->data[line->read] - 8b/copy 1/mod/*+disp8 1/rm32/ecx . . 2/r32/edx 4/disp8 . # copy *(ecx+4) to edx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 1/base/ecx 2/index/edx . 2/r32/edx 0xc/disp8 . # copy ecx+edx+12 to edx - 52/push-edx - # . . call - e8/call skip-string-in-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # line->read = eax - line->data - 29/subtract 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # subtract ecx from eax - 2d/subtract-from-eax 0xc/imm32 - 89/copy 1/mod/*+disp8 1/rm32/ecx . . 0/r32/eax 4/disp8 . # copy eax to *(ecx+4) -$skip-string:end: - # . restore registers - 5a/pop-to-edx - 59/pop-to-ecx - 58/pop-to-eax - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-skip-string: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . write(_test-input-stream, "\"abc\" def") - # . indices: 0123 45 - # . . push args - 68/push "\"abc\" def"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # precondition: line->read == 0 - # . . push args - 68/push "F - test-skip-string/precondition"/imm32 - 68/push 0/imm32 - b8/copy-to-eax _test-input-stream/imm32 - 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 - # skip-string(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call skip-string/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # check-ints-equal(line->read, 5, msg) - # . . push args - 68/push "F - test-skip-string"/imm32 - 68/push 5/imm32 - b8/copy-to-eax _test-input-stream/imm32 - 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 - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-skip-string-ignores-spaces: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . write(_test-input-stream, "\"a b\"/yz") - # . indices: 0123 45 - # . . push args - 68/push "\"a b\"/yz"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # precondition: line->read == 0 - # . . push args - 68/push "F - test-skip-string-ignores-spaces/precondition"/imm32 - 68/push 0/imm32 - b8/copy-to-eax _test-input-stream/imm32 - 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 - # skip-string(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call skip-string/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # check-ints-equal(line->read, 5, msg) - # . . push args - 68/push "F - test-skip-string-ignores-spaces"/imm32 - 68/push 5/imm32 - b8/copy-to-eax _test-input-stream/imm32 - 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 - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-skip-string-ignores-escapes: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . write(_test-input-stream, "\"a\\\"b\"/yz") - # . indices: 01 2 34 56 - # . . push args - 68/push "\"a\\\"b\"/yz"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # precondition: line->read == 0 - # . . push args - 68/push "F - test-skip-string-ignores-escapes/precondition"/imm32 - 68/push 0/imm32 - b8/copy-to-eax _test-input-stream/imm32 - 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 - # skip-string(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call skip-string/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # check-ints-equal(line->read, 6, msg) - # . . push args - 68/push "F - test-skip-string-ignores-escapes"/imm32 - 68/push 6/imm32 - b8/copy-to-eax _test-input-stream/imm32 - 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 - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-skip-string-works-from-mid-stream: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . write(_test-input-stream, "0 \"a\\\"b\"/yz") - # . indices: 01 2 34 56 - # . . push args - 68/push "0 \"a\\\"b\"/yz"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # precondition: line->read == 2 - b8/copy-to-eax _test-input-stream/imm32 - c7 0/subop/copy 1/mod/*+disp8 0/rm32/eax . . . . 4/disp8 2/imm32 # copy to *(eax+4) - # skip-string(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call skip-string/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # check-ints-equal(line->read, 8, msg) - # . . push args - 68/push "F - test-skip-string-works-from-mid-stream"/imm32 - 68/push 8/imm32 - b8/copy-to-eax _test-input-stream/imm32 - 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 - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -skip-string-in-slice: # curr : (address byte), end : (address byte) -> new_curr/eax - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 51/push-ecx - 52/push-edx - 53/push-ebx - # ecx = curr - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . 1/r32/ecx 8/disp8 . # copy *(ebp+8) to ecx - # edx = end - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . 2/r32/edx 0xc/disp8 . # copy *(ebp+12) to edx - # eax = 0 - 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax - # skip initial dquote - 41/increment-ecx -$skip-string-in-slice:loop: - # if (curr >= end) return curr - 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx - 73/jump-if-greater-unsigned-or-equal $skip-string-in-slice:return-curr/disp8 - # AL = *curr - 8a/copy-byte 0/mod/indirect 1/rm32/ecx . . . 0/r32/AL . . # copy byte at *ecx to AL -$skip-string-in-slice:dquote: - # if (eax == '"') break - 3d/compare-eax-and 0x22/imm32/double-quote - 74/jump-if-equal $skip-string-in-slice:break/disp8 -$skip-string-in-slice:check-for-escape: - # if (eax == '\') escape next char - 3d/compare-eax-and 0x5c/imm32/backslash - 75/jump-if-not-equal $skip-string-in-slice:continue/disp8 -$skip-string-in-slice:escape: - 41/increment-ecx -$skip-string-in-slice:continue: - # ++curr - 41/increment-ecx - eb/jump $skip-string-in-slice:loop/disp8 -$skip-string-in-slice:break: - # skip final dquote - 41/increment-ecx -$skip-string-in-slice:return-curr: - # return curr - 89/copy 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # copy ecx to eax -$skip-string-in-slice:end: - # . restore registers - 5b/pop-to-ebx - 5a/pop-to-edx - 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-skip-string-in-slice: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # setup: (eax..ecx) = "\"abc\" def" - b8/copy-to-eax "\"abc\" def"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # eax = skip-string-in-slice(eax, ecx) - # . . push args - 51/push-ecx - 50/push-eax - # . . call - e8/call skip-string-in-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-ints-equal(ecx-eax, 4, msg) # number of chars remaining after the string literal - # . . push args - 68/push "F - test-skip-string-in-slice"/imm32 - 68/push 4/imm32 - # . . push ecx-eax - 29/subtract 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # subtract eax from ecx - 51/push-ecx - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-skip-string-in-slice-ignores-spaces: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # setup: (eax..ecx) = "\"a b\"/yz" - b8/copy-to-eax "\"a b\"/yz"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # eax = skip-string-in-slice(eax, ecx) - # . . push args - 51/push-ecx - 50/push-eax - # . . call - e8/call skip-string-in-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-ints-equal(ecx-eax, 3, msg) # number of chars remaining after the string literal - # . . push args - 68/push "F - test-skip-string-in-slice-ignores-spaces"/imm32 - 68/push 3/imm32 - # . . push ecx-eax - 29/subtract 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # subtract eax from ecx - 51/push-ecx - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-skip-string-in-slice-ignores-escapes: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # setup: (eax..ecx) = "\"a\\\"b\"/yz" - b8/copy-to-eax "\"a\\\"b\"/yz"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # eax = skip-string-in-slice(eax, ecx) - # . . push args - 51/push-ecx - 50/push-eax - # . . call - e8/call skip-string-in-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-ints-equal(ecx-eax, 3, msg) # number of chars remaining after the string literal - # . . push args - 68/push "F - test-skip-string-in-slice-ignores-escapes"/imm32 - 68/push 3/imm32 - # . . push ecx-eax - 29/subtract 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # subtract eax from ecx - 51/push-ecx - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-skip-string-in-slice-stops-at-end: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # setup: (eax..ecx) = "\"abc" # unbalanced dquote - b8/copy-to-eax "\"abc"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # eax = skip-string-in-slice(eax, ecx) - # . . push args - 51/push-ecx - 50/push-eax - # . . call - e8/call skip-string-in-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-ints-equal(ecx-eax, 0, msg) # skipped to end of slice - # . . push args - 68/push "F - test-skip-string-in-slice-stops-at-end"/imm32 - 68/push 0/imm32 - # . . push ecx-eax - 29/subtract 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # subtract eax from ecx - 51/push-ecx - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -# update line->read to ')' -# line->read ends at ')' -skip-until-close-paren: # line : (address stream) - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 50/push-eax - 51/push-ecx - 52/push-edx - # ecx = line - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . 1/r32/ecx 8/disp8 . # copy *(ebp+8) to ecx - # eax = skip-until-close-paren-in-slice(&line->data[line->read], &line->data[line->write]) - # . . push &line->data[line->write] - 8b/copy 1/mod/*+disp8 1/rm32/ecx . . 2/r32/edx 8/disp8 . # copy *(ecx+8) to edx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 1/base/ecx 2/index/edx . 2/r32/edx 0xc/disp8 . # copy ecx+edx+12 to edx - 52/push-edx - # . . push &line->data[line->read] - 8b/copy 1/mod/*+disp8 1/rm32/ecx . . 2/r32/edx 4/disp8 . # copy *(ecx+4) to edx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 1/base/ecx 2/index/edx . 2/r32/edx 0xc/disp8 . # copy ecx+edx+12 to edx - 52/push-edx - # . . call - e8/call skip-until-close-paren-in-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # line->read = eax - line->data - 29/subtract 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # subtract ecx from eax - 2d/subtract-from-eax 0xc/imm32 - 89/copy 1/mod/*+disp8 1/rm32/ecx . . 0/r32/eax 4/disp8 . # copy eax to *(ecx+4) -$skip-until-close-paren:end: - # . restore registers - 5a/pop-to-edx - 59/pop-to-ecx - 58/pop-to-eax - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-skip-until-close-paren: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . write(_test-input-stream, "*(abc) def") - # . indices: 0123 45 - # . . push args - 68/push "*(abc) def"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # precondition: line->read == 0 - # . . push args - 68/push "F - test-skip-until-close-paren/precondition"/imm32 - 68/push 0/imm32 - b8/copy-to-eax _test-input-stream/imm32 - 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 - # skip-until-close-paren(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call skip-until-close-paren/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # check-ints-equal(line->read, 5, msg) - # . . push args - 68/push "F - test-skip-until-close-paren"/imm32 - 68/push 5/imm32 - b8/copy-to-eax _test-input-stream/imm32 - 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 - # . 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-close-paren-ignores-spaces: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . write(_test-input-stream, "*(a b)/yz") - # . . push args - 68/push "*(a b)/yz"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # precondition: line->read == 0 - # . . push args - 68/push "F - test-skip-until-close-paren-ignores-spaces/precondition"/imm32 - 68/push 0/imm32 - b8/copy-to-eax _test-input-stream/imm32 - 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 - # skip-until-close-paren(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call skip-until-close-paren/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # check-ints-equal(line->read, 5, msg) - # . . push args - 68/push "F - test-skip-until-close-paren-ignores-spaces"/imm32 - 68/push 5/imm32 - b8/copy-to-eax _test-input-stream/imm32 - 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 - # . 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-close-paren-works-from-mid-stream: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # setup - # . clear-stream(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . write(_test-input-stream, "0 *(a b)/yz") - # . . push args - 68/push "0 *(a b)/yz"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # precondition: _test-input-stream->read == 2 - b8/copy-to-eax _test-input-stream/imm32 - c7 0/subop/copy 1/mod/*+disp8 0/rm32/eax . . . . 4/disp8 2/imm32 # copy to *(eax+4) - # skip-until-close-paren(_test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call skip-until-close-paren/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # check-ints-equal(_test-input-stream->read, 7, msg) - # . . push args - 68/push "F - test-skip-until-close-paren-works-from-mid-stream"/imm32 - 68/push 7/imm32 - b8/copy-to-eax _test-input-stream/imm32 - 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 - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -skip-until-close-paren-in-slice: # curr : (address byte), end : (address byte) -> new_curr/eax - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 51/push-ecx - 52/push-edx - # ecx = curr - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . 1/r32/ecx 8/disp8 . # copy *(ebp+8) to ecx - # edx = end - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . 2/r32/edx 0xc/disp8 . # copy *(ebp+12) to edx - # eax = 0 - 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax - # skip initial dquote - 41/increment-ecx -$skip-until-close-paren-in-slice:loop: - # if (curr >= end) break - 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx - 73/jump-if-greater-unsigned-or-equal $skip-until-close-paren-in-slice:break/disp8 - # AL = *curr - 8a/copy-byte 0/mod/indirect 1/rm32/ecx . . . 0/r32/AL . . # copy byte at *ecx to AL -$skip-until-close-paren-in-slice:check-close: - # if (eax == ')') break - 3d/compare-eax-and 0x29/imm32/close-paren - 74/jump-if-equal $skip-until-close-paren-in-slice:break/disp8 - # ++curr - 41/increment-ecx - eb/jump $skip-until-close-paren-in-slice:loop/disp8 -$skip-until-close-paren-in-slice:break: - # return curr - 89/copy 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # copy ecx to eax -$skip-until-close-paren-in-slice:end: - # . restore registers - 5a/pop-to-edx - 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-skip-until-close-paren-in-slice: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # setup: (eax..ecx) = "*(abc) def" - b8/copy-to-eax "*(abc) def"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # eax = skip-until-close-paren-in-slice(eax, ecx) - # . . push args - 51/push-ecx - 50/push-eax - # . . call - e8/call skip-until-close-paren-in-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-ints-equal(ecx-eax, 5, msg) # eax is at the ')' - # . . push args - 68/push "F - test-skip-until-close-paren-in-slice"/imm32 - 68/push 5/imm32 - # . . push ecx-eax - 29/subtract 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # subtract eax from ecx - 51/push-ecx - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . 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-close-paren-in-slice-ignores-spaces: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # setup: (eax..ecx) = "*(a b)/yz" - b8/copy-to-eax "*(a b)/yz"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # eax = skip-until-close-paren-in-slice(eax, ecx) - # . . push args - 51/push-ecx - 50/push-eax - # . . call - e8/call skip-until-close-paren-in-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-ints-equal(ecx-eax, 4, msg) # eax is at the ')' - # . . push args - 68/push "F - test-skip-until-close-paren-in-slice-ignores-spaces"/imm32 - 68/push 4/imm32 - # . . push ecx-eax - 29/subtract 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # subtract eax from ecx - 51/push-ecx - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . 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-close-paren-in-slice-stops-at-end: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # setup: (eax..ecx) = "*(abc" # unbalanced dquote - b8/copy-to-eax "*(abc"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # eax = skip-until-close-paren-in-slice(eax, ecx) - # . . push args - 51/push-ecx - 50/push-eax - # . . call - e8/call skip-until-close-paren-in-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-ints-equal(ecx-eax, 0, msg) # skipped to end of slice - # . . push args - 68/push "F - test-skip-until-close-paren-in-slice-stops-at-end"/imm32 - 68/push 0/imm32 - # . . push ecx-eax - 29/subtract 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # subtract eax from ecx - 51/push-ecx - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -# . . vim:nowrap:textwidth=0 diff --git a/083subx-widths.subx b/083subx-widths.subx new file mode 100644 index 00000000..18fe9833 --- /dev/null +++ b/083subx-widths.subx @@ -0,0 +1,238 @@ +== 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 + +compute-width: # word : (address array byte) -> eax : int + # . 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 = word + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 8/disp8 . # copy *(ebp+8) to ecx + # ecx = word + word->length + 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx + 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx + # eax = word->data + 05/add-to-eax 4/imm32 + # var in/ecx : (address slice) = {eax, ecx} + 51/push-ecx + 50/push-eax + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # return compute-width-of-slice(ecx) + # . . push args + 51/push-ecx + # . . call + e8/call compute-width-of-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp +$compute-width:end: + # . reclaim locals + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . 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 + +compute-width-of-slice: # s : (address slice) -> eax : int + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 51/push-ecx + # ecx = s + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 8/disp8 . # copy *(ebp+8) to ecx + # if (has-metadata?(word, "imm32")) return 4 + # . eax = has-metadata?(word, "imm32") + # . . push args + 68/push "imm32"/imm32 + 51/push-ecx + # . . call + e8/call has-metadata?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . if (eax != 0) return 4 + 3d/compare-eax-and 0/imm32 + b8/copy-to-eax 4/imm32 # ZF is set, so we can overwrite eax now + 75/jump-if-not-equal $compute-width-of-slice:end/disp8 + # if (has-metadata?(word, "disp32")) return 4 + # . eax = has-metadata?(word, "disp32") + # . . push args + 68/push "disp32"/imm32 + 51/push-ecx + # . . call + e8/call has-metadata?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . if (eax != 0) return 4 + 3d/compare-eax-and 0/imm32 + b8/copy-to-eax 4/imm32 # ZF is set, so we can overwrite eax now + 75/jump-if-not-equal $compute-width-of-slice:end/disp8 + # if (has-metadata?(word, "imm16")) return 2 + # . eax = has-metadata?(word, "imm16") + # . . push args + 68/push "imm16"/imm32 + 51/push-ecx + # . . call + e8/call has-metadata?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . if (eax != 0) return 2 + 3d/compare-eax-and 0/imm32 + b8/copy-to-eax 2/imm32 # ZF is set, so we can overwrite eax now + 75/jump-if-not-equal $compute-width-of-slice:end/disp8 + # if (has-metadata?(word, "disp16")) return 2 + # . eax = has-metadata?(word, "disp16") + # . . push args + 68/push "disp16"/imm32 + 51/push-ecx + # . . call + e8/call has-metadata?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . if (eax != 0) return 2 + 3d/compare-eax-and 0/imm32 + b8/copy-to-eax 2/imm32 # ZF is set, so we can overwrite eax now + 75/jump-if-not-equal $compute-width-of-slice:end/disp8 + # otherwise return 1 + b8/copy-to-eax 1/imm32 +$compute-width-of-slice: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-compute-width: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp +$test-compute-width:imm8: + # eax = compute-width("0x2/imm8") + # . . push args + 68/push "0x2/imm8"/imm32 + # . . call + e8/call compute-width/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-ints-equal(eax, 1, msg) + # . . push args + 68/push "F - test-compute-width: 0x2/imm8"/imm32 + 50/push-eax + 68/push 1/imm32 + # . . 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-compute-width:imm16: + # eax = compute-width("4/imm16") + # . . push args + 68/push "4/imm16"/imm32 + # . . call + e8/call compute-width/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-ints-equal(eax, 2, msg) + # . . push args + 68/push "F - test-compute-width: 4/imm16"/imm32 + 50/push-eax + 68/push 2/imm32 + # . . 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-compute-width:imm32: + # eax = compute-width("4/imm32") + # . . push args + 68/push "4/imm32"/imm32 + # . . call + e8/call compute-width/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-ints-equal(eax, 4, msg) + # . . push args + 68/push "F - test-compute-width: 4/imm32"/imm32 + 50/push-eax + 68/push 4/imm32 + # . . 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-compute-width:disp8: + # eax = compute-width("foo/disp8") + # . . push args + 68/push "foo/disp8"/imm32 + # . . call + e8/call compute-width/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-ints-equal(eax, 1, msg) + # . . push args + 68/push "F - test-compute-width: foo/disp8"/imm32 + 50/push-eax + 68/push 1/imm32 + # . . 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-compute-width:disp16: + # eax = compute-width("foo/disp16") + # . . push args + 68/push "foo/disp16"/imm32 + # . . call + e8/call compute-width/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-ints-equal(eax, 2, msg) + # . . push args + 68/push "F - test-compute-width: foo/disp16"/imm32 + 50/push-eax + 68/push 2/imm32 + # . . 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-compute-width:disp32: + # eax = compute-width("foo/disp32") + # . . push args + 68/push "foo/disp32"/imm32 + # . . call + e8/call compute-width/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-ints-equal(eax, 4, msg) + # . . push args + 68/push "F - test-compute-width: foo/disp32"/imm32 + 50/push-eax + 68/push 4/imm32 + # . . 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-compute-width:no-metadata: + # eax = compute-width("45") + # . . push args + 68/push "45"/imm32 + # . . call + e8/call compute-width/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # check-ints-equal(eax, 1, msg) + # . . push args + 68/push "F - test-compute-width: 45 (no metadata)"/imm32 + 50/push-eax + 68/push 1/imm32 + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +# . . vim:nowrap:textwidth=0 diff --git a/084emit-hex-array.subx b/084emit-hex-array.subx new file mode 100644 index 00000000..eb79d07f --- /dev/null +++ b/084emit-hex-array.subx @@ -0,0 +1,142 @@ +== 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 + +# print 'arr' in hex with a space after every byte +emit-hex-array: # out : (address buffered-file), arr : (address array byte) -> + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 50/push-eax + 51/push-ecx + 52/push-edx + 57/push-edi + # edi = out + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 7/r32/edi 8/disp8 . # copy *(ebp+8) to edi + # edx = arr # <== 0xbdffffe4 + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 2/r32/edx 0xc/disp8 . # copy *(ebp+12) to edx + # curr/ecx = arr->data + 8d/copy-address 1/mod/*+disp8 2/rm32/edx . . . 1/r32/ecx 4/disp8 . # copy edx+4 to ecx + # max/edx = arr->data + arr->length + 8b/copy 0/mod/indirect 2/rm32/edx . . . 2/r32/edx . . # copy *edx to edx + 01/add 3/mod/direct 2/rm32/edx . . . 1/r32/ecx . . # add ecx to edx + # eax = 0 + 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax +$emit-hex-array:loop: + # if (curr >= width) break + 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx + 73/jump-if-greater-or-equal-unsigned $emit-hex-array:end/disp8 + # emit-hex(out, *curr, width=1) + # . . push args + 68/push 1/imm32/width + 8a/copy-byte 0/mod/indirect 1/rm32/ecx . . . 0/r32/AL . . # copy byte at *ecx to AL + 50/push-eax + 57/push-edi + # . . call + e8/call emit-hex/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # ++curr + 41/increment-ecx + eb/jump $emit-hex-array:loop/disp8 +$emit-hex-array:end: + # . restore registers + 5f/pop-to-edi + 5a/pop-to-edx + 59/pop-to-ecx + 58/pop-to-eax + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-emit-hex-array: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # setup + # . clear-stream(_test-output-stream) + # . . push args + 68/push _test-output-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # . clear-stream(_test-output-buffered-file+4) + # . . push args + b8/copy-to-eax _test-output-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 + # var arr/ecx (address array byte) = [01, 02, 03] + 68/push 0x00030201/imm32 # bytes 01 02 03 + 68/push 3/imm32/length + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # emit-hex-array(_test-output-buffered-file, arr) + # . . push args + 51/push-ecx + 68/push _test-output-buffered-file/imm32 + # . . call + e8/call emit-hex-array/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . flush(_test-output-buffered-file) + # . . push args + 68/push _test-output-buffered-file/imm32 + # . . call + e8/call flush/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp +#? # dump output {{{ +#? # . write(2/stderr, "result: ^") +#? # . . push args +#? 68/push "result: ^"/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # . write-stream(2/stderr, _test-output-stream) +#? # . . push args +#? 68/push _test-output-stream/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write-stream/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # . write(2/stderr, "$\n") +#? # . . push args +#? 68/push "$\n"/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +#? # . rewind-stream(_test-output-stream) +#? # . . push args +#? 68/push _test-output-stream/imm32 +#? # . . call +#? e8/call rewind-stream/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp +#? # }}} + # check-next-stream-line-equal(_test-output-stream, "01 02 03 ", msg) + # . . push args + 68/push "F - test-emit-hex-array"/imm32 + 68/push "01 02 03 "/imm32 + 68/push _test-output-stream/imm32 + # . . call + e8/call check-next-stream-line-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +# . . vim:nowrap:textwidth=0 diff --git a/084print-int-decimal.subx b/084print-int-decimal.subx deleted file mode 100644 index f7898fe9..00000000 --- a/084print-int-decimal.subx +++ /dev/null @@ -1,307 +0,0 @@ -# Helper to print an int32 in decimal. - -== 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 - -print-int32-decimal: # out : (address stream), n : int32 - # works by generating characters from lowest to highest and pushing them - # to the stack, before popping them one by one into the stream - # - # pseudocode: - # push sentinel - # eax = abs(n) - # while true - # sign-extend eax into edx - # eax, edx = eax/10, eax%10 - # edx += '0' - # push edx - # if (eax == 0) break - # if n < 0 - # push '-' - # w = out->write - # curr = &out->data[out->write] - # max = &out->data[out->length] - # while true - # pop into eax - # if (eax == sentinel) break - # if (curr >= max) abort - # *curr = AL - # ++curr - # ++w - # out->write = w - # (based on K&R itoa: https://en.wikibooks.org/wiki/C_Programming/stdlib.h/itoa) - # (this pseudocode contains registers because operations like division - # require specific registers in x86) - # - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 50/push-eax - 51/push-ecx - 52/push-edx - 53/push-ebx - 57/push-edi - # ten/ecx = 10 - b9/copy-to-ecx 0xa/imm32 - # push sentinel - 68/push 0/imm32/sentinel - # eax = abs(n) - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 0xc/disp8 . # copy *(ebp+12) to eax - 3d/compare-eax-with 0/imm32 - 7d/jump-if-greater-or-equal $print-int32-decimal:read-loop/disp8 -$print-int32-decimal:negative: - f7 3/subop/negate 3/mod/direct 0/rm32/eax . . . . . . # negate eax -$print-int32-decimal:read-loop: - # eax, edx = eax / 10, eax % 10 - 99/sign-extend-eax-into-edx - f7 7/subop/idiv 3/mod/direct 1/rm32/ecx . . . . . . # divide edx:eax by ecx, storing quotient in eax and remainder in edx - # edx += '0' - 81 0/subop/add 3/mod/direct 2/rm32/edx . . . . . 0x30/imm32 # add to edx - # push edx - 52/push-edx - # if (eax == 0) break - 3d/compare-eax-and 0/imm32 - 7f/jump-if-greater $print-int32-decimal:read-loop/disp8 -$print-int32-decimal:read-break: - # if (n < 0) push('-') - 81 7/subop/compare 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 0/imm32 # compare *(ebp+12) - 7d/jump-if-greater-or-equal $print-int32-decimal:write/disp8 -$print-int32-decimal:push-negative: - 68/push 0x2d/imm32/- -$print-int32-decimal:write: - # edi = out - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 7/r32/edi 8/disp8 . # copy *(ebp+8) to edi - # w/edx = out->write - 8b/copy 0/mod/indirect 7/rm32/edi . . . 2/r32/edx . . # copy *edi to edx - # curr/ecx = &out->data[out->write] - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/edi 2/index/edx . 1/r32/ecx 0xc/disp8 . # copy ebx+edx+12 to ecx - # max/ebx = &out->data[out->length] - 8b/copy 1/mod/*+disp8 7/rm32/edi . . . 3/r32/ebx 8/disp8 . # copy *(edi+8) to ebx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/edi 3/index/ebx . 3/r32/ebx 0xc/disp8 . # copy edi+ebx+12 to ebx -$print-int32-decimal:write-loop: - # pop into eax - 58/pop-to-eax - # if (eax == sentinel) break - 3d/compare-eax-and 0/imm32/sentinel - 74/jump-if-equal $print-int32-decimal:write-break/disp8 - # if (curr >= max) abort - 39/compare 3/mod/direct 1/rm32/ecx . . . 3/r32/ebx . . # compare ecx with ebx - 73/jump-if-greater-or-equal-unsigned $print-int32-decimal:abort/disp8 -$print-int32-decimal:write-char: - # *curr = AL - 88/copy-byte 0/mod/indirect 1/rm32/ecx . . . 0/r32/AL . . # copy AL to byte at *ecx - # ++curr - 41/increment-ecx - # ++w - 42/increment-edx - eb/jump $print-int32-decimal:write-loop/disp8 -$print-int32-decimal:write-break: - # out->write = w - 89/copy 0/mod/indirect 7/rm32/edi . . . 2/r32/edx . . # copy edx to *edi -$print-int32-decimal:end: - # . restore registers - 5f/pop-to-edi - 5b/pop-to-ebx - 5a/pop-to-edx - 59/pop-to-ecx - 58/pop-to-eax - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -$print-int32-decimal:abort: - # . _write(2/stderr, error) - # . . push args - 68/push "print-int32-decimal: out of space\n"/imm32 - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . syscall(exit, 1) - bb/copy-to-ebx 1/imm32 - b8/copy-to-eax 1/imm32/exit - cd/syscall 0x80/imm8 - # never gets here - -test-print-int32-decimal: - # - check that a single-digit number converts correctly - # 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 - # print-int32-decimal(_test-stream, 9) - # . . push args - 68/push 9/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call print-int32-decimal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-stream-equal(_test-stream, "9", msg) - # . . push args - 68/push "F - test-print-int32-decimal"/imm32 - 68/push "9"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . end - c3/return - -test-print-int32-decimal-zero: - # - check that 0 converts correctly - # 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 - # print-int32-decimal(_test-stream, 0) - # . . push args - 68/push 0/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call print-int32-decimal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-stream-equal(_test-stream, "0", msg) - # . . push args - 68/push "F - test-print-int32-decimal-zero"/imm32 - 68/push "0"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . end - c3/return - -test-print-int32-decimal-multiple-digits: - # - check that a multi-digit number converts correctly - # 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 - # print-int32-decimal(_test-stream, 10) - # . . push args - 68/push 0xa/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call print-int32-decimal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-stream-equal(_test-stream, "10", msg) - # . . push args - 68/push "F - test-print-int32-decimal-multiple-digits"/imm32 - 68/push "10"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . end - c3/return - -test-print-int32-decimal-negative: - # - check that a negative single-digit number converts correctly - # 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 - # print-int32-decimal(_test-stream, -9) - # . . push args - 68/push -9/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call print-int32-decimal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # dump _test-stream {{{ -#? # . write(2/stderr, "^") -#? # . . push args -#? 68/push "^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # . write-stream(2/stderr, _test-stream) -#? # . . push args -#? 68/push _test-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # }}} - # check-stream-equal(_test-stream, "-9", msg) - # . . push args - 68/push "F - test-print-int32-decimal-negative"/imm32 - 68/push "-9"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . end - c3/return - -test-print-int32-decimal-negative-multiple-digits: - # - check that a multi-digit number converts correctly - # 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 - # print-int32-decimal(_test-stream, -10) - # . . push args - 68/push -0xa/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call print-int32-decimal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-stream-equal(_test-stream, "-10", msg) - # . . push args - 68/push "F - test-print-int32-decimal-negative-multiple-digits"/imm32 - 68/push "-10"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call check-stream-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 diff --git a/085array-equal.subx b/085array-equal.subx deleted file mode 100644 index d6dec878..00000000 --- a/085array-equal.subx +++ /dev/null @@ -1,603 +0,0 @@ -# Comparing arrays of numbers. - -== 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 - -array-equal?: # a : (address array int), b : (address array int) -> eax : boolean - # pseudocode: - # lena = a->length - # if (lena != b->length) return false - # i = 0 - # curra = a->data - # currb = b->data - # while i < lena - # i1 = *curra - # i2 = *currb - # if (c1 != c2) return false - # i+=4, curra+=4, currb+=4 - # return true - # - # registers: - # i: ecx - # lena: edx - # curra: esi - # currb: edi - # i1: eax - # i2: ebx - # - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 51/push-ecx - 52/push-edx - 53/push-ebx - 56/push-esi - 57/push-edi - # esi = a - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi - # edi = b - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 7/r32/edi 0xc/disp8 . # copy *(ebp+12) to edi - # lena/edx = a->length - 8b/copy 0/mod/indirect 6/rm32/esi . . . 2/r32/edx . . # copy *esi to edx -$array-equal?:lengths: - # if (lena != b->length) return false - 39/compare 0/mod/indirect 7/rm32/edi . . . 2/r32/edx . . # compare *edi and edx - 75/jump-if-not-equal $array-equal?:false/disp8 - # curra/esi = a->data - 81 0/subop/add 3/mod/direct 6/rm32/esi . . . . . 4/imm32 # add to esi - # currb/edi = b->data - 81 0/subop/add 3/mod/direct 7/rm32/edi . . . . . 4/imm32 # add to edi - # i/ecx = i1/eax = i2/ebx = 0 - 31/xor 3/mod/direct 1/rm32/ecx . . . 1/r32/ecx . . # clear ecx -$array-equal?:loop: - # if (i >= lena) return true - 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx - 7d/jump-if-greater-or-equal $array-equal?:true/disp8 - # i1 = *curra - 8b/copy 0/mod/indirect 6/rm32/esi . . . 0/r32/eax . . # copy *esi to eax - # i2 = *currb - 8b/copy 0/mod/indirect 7/rm32/edi . . . 3/r32/ebx . . # copy *edi to ebx - # if (i1 != i2) return false - 39/compare 3/mod/direct 0/rm32/eax . . . 3/r32/ebx . . # compare eax and ebx - 75/jump-if-not-equal $array-equal?:false/disp8 - # i += 4 - 81 0/subop/add 3/mod/direct 1/rm32/ecx . . . . . 4/imm32 # add to ecx - # currs += 4 - 81 0/subop/add 3/mod/direct 6/rm32/esi . . . . . 4/imm32 # add to esi - # currb += 4 - 81 0/subop/add 3/mod/direct 7/rm32/edi . . . . . 4/imm32 # add to edi - eb/jump $array-equal?:loop/disp8 -$array-equal?:true: - b8/copy-to-eax 1/imm32 - eb/jump $array-equal?:end/disp8 -$array-equal?:false: - b8/copy-to-eax 0/imm32 -$array-equal?:end: - # . restore registers - 5f/pop-to-edi - 5e/pop-to-esi - 5b/pop-to-ebx - 5a/pop-to-edx - 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-compare-empty-with-empty-array: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # var ecx = [] - 68/push 0/imm32/size - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # var edx = [] - 68/push 0/imm32/size - 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx - # eax = array-equal?(ecx, edx) - # . . push args - 52/push-edx - 51/push-ecx - # . . call - e8/call array-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-ints-equal(eax, 1, msg) - # . . push args - 68/push "F - test-compare-empty-with-empty-array"/imm32 - 68/push 1/imm32/true - 50/push-eax - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-compare-empty-with-non-empty-array: # also checks length-mismatch code path - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # var ecx = [1] - 68/push 1/imm32 - 68/push 4/imm32/size - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # var edx = [] - 68/push 0/imm32/size - 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx - # eax = array-equal?(ecx, edx) - # . . push args - 52/push-edx - 51/push-ecx - # . . call - e8/call array-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-ints-equal(eax, 0, msg) - # . . push args - 68/push "F - test-compare-empty-with-non-empty-array"/imm32 - 68/push 0/imm32/false - 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 - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-compare-equal-arrays: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # var ecx = [1, 2, 3] - 68/push 3/imm32 - 68/push 2/imm32 - 68/push 1/imm32 - 68/push 0xc/imm32/size - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # var edx = [1, 2, 3] - 68/push 3/imm32 - 68/push 2/imm32 - 68/push 1/imm32 - 68/push 0xc/imm32/size - 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx - # eax = array-equal?(ecx, edx) - # . . push args - 52/push-edx - 51/push-ecx - # . . call - e8/call array-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-ints-equal(eax, 1, msg) - # . . push args - 68/push "F - test-compare-equal-arrays"/imm32 - 68/push 1/imm32/true - 50/push-eax - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-compare-inequal-arrays-equal-lengths: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # var ecx = [1, 4, 3] - 68/push 3/imm32 - 68/push 4/imm32 - 68/push 1/imm32 - 68/push 0xc/imm32/size - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # var edx = [1, 2, 3] - 68/push 3/imm32 - 68/push 2/imm32 - 68/push 1/imm32 - 68/push 0xc/imm32/size - 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx - # eax = array-equal?(ecx, edx) - # . . push args - 52/push-edx - 51/push-ecx - # . . call - e8/call array-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-ints-equal(eax, 0, msg) - # . . push args - 68/push "F - test-compare-inequal-arrays-equal-lengths"/imm32 - 68/push 0/imm32/false - 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 - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -parse-array-of-ints: # ad : (address allocation-descriptor), s : (address string) -> result/eax : (address array int) - # pseudocode - # end = s->data + s->length - # curr = s->data - # size = 0 - # while true - # if (curr >= end) break - # curr = skip-chars-matching-in-slice(curr, end, ' ') - # if (curr >= end) break - # curr = skip-chars-not-matching-in-slice(curr, end, ' ') - # ++size - # result = allocate(ad, (size+1)*4) - # result->size = (size+1)*4 - # var slice = {s->data, 0} - # out = result->data - # while true - # if (slice->start >= end) break - # slice->start = skip-chars-matching-in-slice(slice->start, end, ' ') - # if (slice->start >= end) break - # slice->end = skip-chars-not-matching-in-slice(slice->start, end, ' ') - # *out = parse-hex-int(slice) - # out += 4 - # slice->start = slice->end - # return result - # - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 51/push-ecx - 52/push-edx - 53/push-ebx - 56/push-esi - 57/push-edi - # esi = s - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 0xc/disp8 . # copy *(ebp+12) to esi - # curr/ecx = s->data - 8d/copy-address 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy esi+4 to ecx - # end/edx = s->data + s->length - # . edx = s->length - 8b/copy 0/mod/indirect 6/rm32/esi . . . 2/r32/edx . . # copy *esi to edx - # . edx += curr - 01/add 3/mod/direct 2/rm32/edx . . . 1/r32/ecx . . # add ecx to edx - # size/ebx = 0 - 31/xor 3/mod/direct 3/rm32/ebx . . . 3/r32/ebx . . # clear ebx -$parse-array-of-ints:loop1: - # if (curr >= end) break - 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx - 73/jump-if-greater-or-equal-unsigned $parse-array-of-ints:break1/disp8 - # curr = skip-chars-matching-in-slice(curr, end, ' ') - # . eax = skip-chars-matching-in-slice(curr, end, ' ') - # . . push args - 68/push 0x20/imm32/space - 52/push-edx - 51/push-ecx - # . . call - e8/call skip-chars-matching-in-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . ecx = eax - 89/copy 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # copy eax to ecx - # if (curr >= end) break - 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx - 73/jump-if-greater-or-equal-unsigned $parse-array-of-ints:break1/disp8 - # curr = skip-chars-not-matching-in-slice(curr, end, ' ') - # . eax = skip-chars-not-matching-in-slice(curr, end, ' ') - # . . push args - 68/push 0x20/imm32/space - 52/push-edx - 51/push-ecx - # . . call - e8/call skip-chars-not-matching-in-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . ecx = eax - 89/copy 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # copy eax to ecx - # size += 4 - 81 0/subop/add 3/mod/direct 3/rm32/ebx . . . . . 4/imm32 # add to ebx - eb/jump $parse-array-of-ints:loop1/disp8 -$parse-array-of-ints:break1: - # result/edi = allocate(ad, size+4) - # . eax = allocate(ad, size+4) - # . . push args - 89/copy 3/mod/direct 0/rm32/eax . . . 3/r32/ebx . . # copy ebx to eax - 05/add-to-eax 4/imm32 - 50/push-eax - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) - # . . call - e8/call allocate/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . edi = eax - 89/copy 3/mod/direct 7/rm32/edi . . . 0/r32/eax . . # copy eax to edi - # result->size = size - 89/copy 0/mod/indirect 0/rm32/eax . . . 3/r32/ebx . . # copy ebx to *eax -$parse-array-of-ints:pass2: - # var slice/ecx = {s->data, 0} - # . push 0 - 68/push 0/imm32/end - # . push s->data - 8d/copy-address 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy esi+4 to ecx - 51/push-ecx - # . bookmark - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # out/ebx = result->data - 8d/copy-address 1/mod/*+disp8 0/rm32/eax . . . 3/r32/ebx 4/disp8 . # copy eax+4 to ebx -$parse-array-of-ints:loop2: - # if (slice->start >= end) break - 39/compare 0/mod/indirect 1/rm32/ecx . . . 2/r32/edx . . # compare *ecx with edx - 73/jump-if-greater-or-equal-unsigned $parse-array-of-ints:end/disp8 - # slice->start = skip-chars-matching-in-slice(slice->start, end, ' ') - # . eax = skip-chars-matching-in-slice(slice->start, end, ' ') - # . . push args - 68/push 0x20/imm32/space - 52/push-edx - ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx - # . . call - e8/call skip-chars-matching-in-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . slice->start = eax - 89/copy 0/mod/indirect 1/rm32/ecx . . . 0/r32/eax . . # copy eax to *ecx - # if (slice->start >= end) break - 39/compare 0/mod/indirect 1/rm32/ecx . . . 2/r32/edx . . # compare *ecx with edx - 73/jump-if-greater-or-equal-unsigned $parse-array-of-ints:end/disp8 - # slice->end = skip-chars-not-matching-in-slice(slice->start, end, ' ') - # . eax = skip-chars-not-matching-in-slice(curr, end, ' ') - # . . push args - 68/push 0x20/imm32/space - 52/push-edx - 50/push-eax - # . . call - e8/call skip-chars-not-matching-in-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . slice->end = eax - 89/copy 1/mod/direct 1/rm32/ecx . . . 0/r32/eax 4/disp8 . # copy eax to *(ecx+4) - # *out = parse-hex-int(slice) - # . eax = parse-hex-int(slice) - # . . push args - 51/push-ecx - # . . call - e8/call parse-hex-int/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # *out = eax - 89/copy 0/mod/indirect 3/rm32/ebx . . . 0/r32/eax . . # copy eax to *ebx - # out += 4 - 81 0/subop/add 3/mod/direct 3/rm32/ebx . . . . . 4/imm32 # add to ebx - # slice->start = slice->end - 8b/copy 1/mod/direct 1/rm32/ecx . . . 0/r32/eax 4/disp8 . # copy *(ecx+4) to eax - 89/copy 0/mod/indirect 1/rm32/ecx . . . 0/r32/eax . . # copy eax to *ecx - 81 0/subop/add 3/mod/direct 1/rm32/ecx . . . . . 4/imm32 # add to ecx - eb/jump $parse-array-of-ints:loop2/disp8 -$parse-array-of-ints:end: - # return edi - 89/copy 3/mod/direct 0/rm32/eax . . . 7/r32/edi . . # copy edi to eax - # . reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . restore registers - 5f/pop-to-edi - 5e/pop-to-esi - 5b/pop-to-ebx - 5a/pop-to-edx - 59/pop-to-ecx - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-parse-array-of-ints: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # var ecx = [1, 2, 3] - 68/push 3/imm32 - 68/push 2/imm32 - 68/push 1/imm32 - 68/push 0xc/imm32/size - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # eax = parse-array-of-ints(Heap, "1 2 3") - # . . push args - 68/push "1 2 3"/imm32 - 68/push Heap/imm32 - # . . call - e8/call parse-array-of-ints/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # eax = array-equal?(ecx, eax) - # . . push args - 50/push-eax - 51/push-ecx - # . . call - e8/call array-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-ints-equal(eax, 1, msg) - # . . push args - 68/push "F - test-parse-array-of-ints"/imm32 - 68/push 1/imm32/true - 50/push-eax - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-parse-array-of-ints-empty: - # - empty string = empty array - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # eax = parse-array-of-ints(Heap, "") - # . . push args - 68/push ""/imm32 - 68/push Heap/imm32 - # . . call - e8/call parse-array-of-ints/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-ints-equal(*eax, 0, msg) - # . . push args - 68/push "F - test-parse-array-of-ints-empty"/imm32 - 68/push 0/imm32/size - ff 6/subop/push 0/mod/indirect 0/rm32/eax . . . . . . # 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 - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-parse-array-of-ints-just-whitespace: - # - just whitespace = empty array - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # eax = parse-array-of-ints(Heap, " ") - # . . push args - 68/push Space/imm32 - 68/push Heap/imm32 - # . . call - e8/call parse-array-of-ints/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-ints-equal(*eax, 0, msg) - # . . push args - 68/push "F - test-parse-array-of-ints-empty"/imm32 - 68/push 0/imm32/size - ff 6/subop/push 0/mod/indirect 0/rm32/eax . . . . . . # 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 - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-parse-array-of-ints-extra-whitespace: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # var ecx = [1, 2, 3] - 68/push 3/imm32 - 68/push 2/imm32 - 68/push 1/imm32 - 68/push 0xc/imm32/size - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # eax = parse-array-of-ints(Heap, " 1 2 3 ") - # . . push args - 68/push " 1 2 3 "/imm32 - 68/push Heap/imm32 - # . . call - e8/call parse-array-of-ints/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # eax = array-equal?(ecx, eax) - # . . push args - 50/push-eax - 51/push-ecx - # . . call - e8/call array-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-ints-equal(eax, 1, msg) - # . . push args - 68/push "F - test-parse-array-of-ints-extra-whitespace"/imm32 - 68/push 1/imm32/true - 50/push-eax - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -# helper for later tests -# compare an array with a string representation of an array literal -check-array-equal: # a : (address array int), expected : (address string), msg : (address string) - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 50/push-eax - # var b/ecx = parse-array-of-ints(Heap, expected) - # . eax = parse-array-of-ints(Heap, expected) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) - 68/push Heap/imm32 - # . . call - e8/call parse-array-of-ints/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . b = eax - 89/copy 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # copy eax to ecx - # eax = array-equal?(a, b) - # . . push args - 51/push-ecx - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) - # . . call - e8/call array-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-ints-equal(eax, 1, msg) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16) - 68/push 1/imm32 - 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 -$check-array-equal: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-check-array-equal: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # var ecx = [1, 2, 3] - 68/push 3/imm32 - 68/push 2/imm32 - 68/push 1/imm32 - 68/push 0xc/imm32/size - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # check-array-equal(ecx, "1 2 3", "msg") - # . . push args - 68/push "F - test-check-array-equal"/imm32 - 68/push "1 2 3"/imm32 - 51/push-ecx - # . . call - e8/call check-array-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -# . . vim:nowrap:textwidth=0 diff --git a/086zero-out.subx b/086zero-out.subx deleted file mode 100644 index 1a4c73d1..00000000 --- a/086zero-out.subx +++ /dev/null @@ -1,84 +0,0 @@ -# Fill a region of memory with zeroes. - -== 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 - -zero-out: # start : address, len : int - # pseudocode: - # curr/esi = start - # i/ecx = 0 - # while true - # if (i >= len) break - # *curr = 0 - # ++curr - # ++i - # - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 50/push-eax - 51/push-ecx - 52/push-edx - 56/push-esi - # curr/esi = start - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi - # i/ecx = 0 - 31/xor 3/mod/direct 1/rm32/ecx . . . 1/r32/ecx . . # clear ecx - # edx = len - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 2/r32/edx 0xc/disp8 . # copy *(ebp+12) to edx -$zero-out:loop: - # if (i >= len) break - 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx - 7d/jump-if-greater-or-equal $zero-out:end/disp8 - # *curr = 0 - c6 0/subop/copy 0/mod/direct 6/rm32/esi . . . . . 0/imm8 # copy byte to *esi - # ++curr - 46/increment-esi - # ++i - 41/increment-ecx - eb/jump $zero-out:loop/disp8 -$zero-out:end: - # . restore registers - 5e/pop-to-esi - 5a/pop-to-edx - 59/pop-to-ecx - 58/pop-to-eax - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-zero-out: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # region/ecx = 34, 35, 36, 37 - 68/push 0x37363534/imm32 - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # zero-out(ecx, 3) - # . . push args - 68/push 3/imm32/len - 51/push-ecx - # . . call - e8/call zero-out/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # first 3 bytes cleared, fourth left alone - # . check-ints-equal(*ecx, 0x37000000, msg) - # . . push args - 68/push "F - test-zero-out"/imm32 - 68/push 0x37000000/imm32 - ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -# . . vim:nowrap:textwidth=0 diff --git a/087slurp.subx b/087slurp.subx deleted file mode 100644 index 36a95d48..00000000 --- a/087slurp.subx +++ /dev/null @@ -1,161 +0,0 @@ -== 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 - -# read all bytes from 'f' and store them into 's' -# abort if 's' is too small -slurp: # f : (address buffered-file), s : (address stream byte) -> - # pseudocode: - # while true - # if (s->write >= s->length) abort - # if (f->read >= f->write) populate stream from file - # if (f->write == 0) break - # AL = f->data[f->read] - # s->data[s->write] = AL - # ++f->read - # ++s->write - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 50/push-eax - 51/push-ecx - 52/push-edx - 56/push-esi - 57/push-edi - # esi = f - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi - # ecx = f->read - 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 8/disp8 . # copy *(esi+8) to ecx - # edi = s - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 7/r32/edi 0xc/disp8 . # copy *(ebp+12) to edi - # edx = s->write - 8b/copy 0/mod/indirect 7/rm32/edi . . . 2/r32/edx . . # copy *edi to edx -$slurp:loop: - # if (s->write >= s->length) abort - 3b/compare 1/mod/*+disp8 7/rm32/edi . . . 2/r32/edx 8/disp8 . # compare edx with *(edi+8) - 7d/jump-if-greater-or-equal $slurp:abort/disp8 - # if (f->read >= f->write) populate stream from file - 3b/compare 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # compare ecx with *(esi+4) - 7c/jump-if-lesser $slurp:from-stream/disp8 - # . clear-stream(stream = f+4) - # . . push args - 8d/copy-address 1/mod/*+disp8 6/rm32/esi . . . 0/r32/eax 4/disp8 . # copy esi+4 to eax - 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 - # . f->read must now be 0; update its cache at ecx - 31/xor 3/mod/direct 1/rm32/ecx . . . 1/r32/ecx . . # clear ecx - # . eax = read(f->fd, stream = f+4) - # . . push args - 50/push-eax - ff 6/subop/push 0/mod/indirect 6/rm32/esi . . . . . . # push *esi - # . . call - e8/call read/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # if (f->write == 0) break - # since f->read was initially 0, eax is the same as f->write - # . if (eax == 0) return true - 3d/compare-eax-and 0/imm32 - 74/jump-if-equal $slurp:end/disp8 -$slurp:from-stream: - # AL = f->data[f->read] - 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax - 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/esi 1/index/ecx . 0/r32/AL 0x10/disp8 . # copy byte at *(esi+ecx+16) to AL - # s->data[s->write] = AL - 88/copy-byte 1/mod/*+disp8 4/rm32/sib 7/base/edi 2/index/edx . 0/r32/AL 0xc/disp8 . # copy AL to *(edi+edx+12) - # ++f->read - 41/increment-ecx - # ++s->write - 42/increment-edx - eb/jump $slurp:loop/disp8 -$slurp:end: - # save f->read - 89/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 8/disp8 . # copy ecx to *(esi+8) - # save s->write - 89/copy 0/mod/indirect 7/rm32/edi . . . 2/r32/edx . . # copy edx to *edi - # . restore registers - 5f/pop-to-edi - 5e/pop-to-esi - 5a/pop-to-edx - 59/pop-to-ecx - 58/pop-to-eax - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -$slurp:abort: - # . _write(2/stderr, error) - # . . push args - 68/push "slurp: destination too small\n"/imm32 - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . syscall(exit, 1) - bb/copy-to-ebx 1/imm32 - b8/copy-to-eax 1/imm32/exit - cd/syscall 0x80/imm8 - # never gets here - -test-slurp: - # 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 - # . clear-stream(_test-tmp-stream) - # . . push args - 68/push _test-tmp-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 - # write(_test-stream, "ab\ncd") - # . . push args - 68/push "ab\ncd"/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 - # read a line from _test-stream (buffered by _test-buffered-file) into _test-tmp-stream - # . eax = slurp(_test-buffered-file, _test-tmp-stream) - # . . push args - 68/push _test-tmp-stream/imm32 - 68/push _test-buffered-file/imm32 - # . . call - e8/call slurp/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-stream-equal(_test-tmp-stream, "ab\ncd", msg) - # . . push args - 68/push "F - test-slurp"/imm32 - 68/push "ab\ncd"/imm32 - 68/push _test-tmp-stream/imm32 - # . . call - e8/call check-stream-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 diff --git a/088table.subx b/088table.subx deleted file mode 100644 index 602cd872..00000000 --- a/088table.subx +++ /dev/null @@ -1,1696 +0,0 @@ -# A table is a stream of (key, value) rows. -# -# Each row consists of a 4-byte key (address to a string) and a variable-size -# value. -# -# Accessing the table performs a linear scan for a key string, and always -# requires passing in the row size. -# -# Table primitives have the form (stream, , row-size, ...) -> address/eax -# -# The following table shows available options for : -# if not found: | arg=string arg=slice -# ------------------------+--------------------------------------------------- -# abort | get get-slice -# insert key | get-or-insert leaky-get-or-insert-slice -# stop | get-or-stop get-slice-or-stop -# return null | maybe-get maybe-get-slice -# Some variants may take extra args. - -== 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 - -# if no row is found, abort -get: # table : (address stream {string, _}), key : (address string), row-size : int, abort-message-prefix : (address string) -> eax : (address _) - # pseudocode: - # curr = table->data - # max = &table->data[table->write] - # while curr < max - # if string-equal?(key, *curr) - # return curr+4 - # curr += row-size - # abort - # - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 51/push-ecx - 52/push-edx - 56/push-esi - # esi = table - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi - # curr/ecx = table->data - 8d/copy-address 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 0xc/disp8 . # copy esi+12 to ecx - # max/edx = table->data + table->write - 8b/copy 0/mod/indirect 6/rm32/esi . . . 2/r32/edx . . # copy *esi to edx - 8d/copy-address 0/mod/indirect 4/rm32/sib 1/base/ecx 2/index/edx . 2/r32/edx . . # copy ecx+edx to edx -$get:search-loop: - # if (curr >= max) abort - 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx - 73/jump-if-greater-or-equal-unsigned $get:abort/disp8 - # if (string-equal?(key, *curr)) return curr+4 - # . eax = string-equal?(key, *curr) - # . . push args - ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) - # . . call - e8/call string-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . if (eax != 0) return eax = curr+4 - 3d/compare-eax-and 0/imm32 - 74/jump-if-equal $get:mismatch/disp8 - 8d/copy-address 1/mod/*+disp8 1/rm32/ecx . . . 0/r32/eax 4/disp8 . # copy ecx+4 to eax - eb/jump $get:end/disp8 -$get:mismatch: - # curr += row-size - 03/add 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 0x10/disp8 . # add *(ebp+16) to ecx - # loop - eb/jump $get:search-loop/disp8 -$get:end: - # . restore registers - 5e/pop-to-esi - 5a/pop-to-edx - 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 - -$get:abort: - # . _write(2/stderr, abort-message-prefix) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x14/disp8 . # push *(ebp+20) - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . _write(2/stderr, error) - # . . push args - 68/push ": get: key not found: "/imm32 - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . _write(2/stderr, key) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . _write(2/stderr, "\n") - # . . push args - 68/push Newline/imm32 - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . syscall(exit, 1) - bb/copy-to-ebx 1/imm32 - b8/copy-to-eax 1/imm32/exit - cd/syscall 0x80/imm8 - # never gets here - -test-get: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # - setup: create a table with a couple of keys - # var table/ecx : (address stream {string, number}) = stream(2 rows * 8 bytes) - 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # subtract from esp - 68/push 0x10/imm32/length - 68/push 0/imm32/read - 68/push 0/imm32/write - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # insert(table, "code", 8 bytes per row) - # . . push args - 68/push 8/imm32/row-size - 68/push "code"/imm32 - 51/push-ecx - # . . call - e8/call get-or-insert/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # insert(table, "data", 8 bytes per row) - # . . push args - 68/push 8/imm32/row-size - 68/push "data"/imm32 - 51/push-ecx - # . . call - e8/call get-or-insert/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp -$test-get:check1: - # eax = get(table, "code", 8 bytes per row) - # . . push args - 68/push 8/imm32/row-size - 68/push "code"/imm32 - 51/push-ecx - # . . call - e8/call get/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # check-ints-equal(eax - table->data, 4, msg) - # . check-ints-equal(eax - table, 16, msg) - # . . push args - 68/push "F - test-get/0"/imm32 - 68/push 0x10/imm32 - 29/subtract 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # subtract ecx from eax - 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-get:check2: - # eax = get(table, "data", 8 bytes per row) - # . . push args - 68/push 8/imm32/row-size - 68/push "data"/imm32 - 51/push-ecx - # . . call - e8/call get/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # check-ints-equal(eax - table->data, 12, msg) - # . check-ints-equal(eax - table, 24, msg) - # . . push args - 68/push "F - test-get/1"/imm32 - 68/push 0x18/imm32 - 29/subtract 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # subtract ecx from eax - 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-get:end: - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -# if no row is found, abort -get-slice: # table : (address stream {string, _}), key : (address slice), row-size : int, abort-message-prefix : (address string) -> eax : (address _) - # pseudocode: - # curr = table->data - # max = &table->data[table->write] - # while curr < max - # if slice-equal?(key, *curr) - # return curr+4 - # curr += row-size - # abort - # - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 51/push-ecx - 52/push-edx - 56/push-esi - # esi = table - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi - # curr/ecx = table->data - 8d/copy-address 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 0xc/disp8 . # copy esi+12 to ecx - # max/edx = table->data + table->write - 8b/copy 0/mod/indirect 6/rm32/esi . . . 2/r32/edx . . # copy *esi to edx - 8d/copy-address 0/mod/indirect 4/rm32/sib 1/base/ecx 2/index/edx . 2/r32/edx . . # copy ecx+edx to edx -$get-slice:search-loop: - # if (curr >= max) abort - 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx - 73/jump-if-greater-or-equal-unsigned $get-slice:abort/disp8 - # if (slice-equal?(key, *curr)) return curr+4 - # . eax = slice-equal?(key, *curr) - # . . push args - ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) - # . . call - e8/call slice-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . if (eax != 0) return eax = curr+4 - 3d/compare-eax-and 0/imm32 - 74/jump-if-equal $get-slice:mismatch/disp8 - 8d/copy-address 1/mod/*+disp8 1/rm32/ecx . . . 0/r32/eax 4/disp8 . # copy ecx+4 to eax - eb/jump $get-slice:end/disp8 -$get-slice:mismatch: - # curr += row-size - 03/add 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 0x10/disp8 . # add *(ebp+16) to ecx - # loop - eb/jump $get-slice:search-loop/disp8 -$get-slice:end: - # . restore registers - 5e/pop-to-esi - 5a/pop-to-edx - 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 - -$get-slice:abort: - # . _write(2/stderr, abort-message-prefix) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x14/disp8 . # push *(ebp+20) - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . _write(2/stderr, error) - # . . push args - 68/push ": get-slice: key not found: "/imm32 - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . write-slice-buffered(Stderr, key) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) - 68/push Stderr/imm32 - # . . call - e8/call write-slice-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . flush(Stderr) - # . . push args - 68/push Stderr/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . _write(2/stderr, "\n") - # . . push args - 68/push Newline/imm32 - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . syscall(exit, 1) - bb/copy-to-ebx 1/imm32 - b8/copy-to-eax 1/imm32/exit - cd/syscall 0x80/imm8 - # never gets here - -test-get-slice: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # - setup: create a table with a couple of keys - # var table/ecx : (address stream {string, number}) = stream(2 rows * 8 bytes) - 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # subtract from esp - 68/push 0x10/imm32/length - 68/push 0/imm32/read - 68/push 0/imm32/write - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # insert(table, "code", 8 bytes per row) - # . . push args - 68/push 8/imm32/row-size - 68/push "code"/imm32 - 51/push-ecx - # . . call - e8/call get-or-insert/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # insert(table, "data", 8 bytes per row) - # . . push args - 68/push 8/imm32/row-size - 68/push "data"/imm32 - 51/push-ecx - # . . call - e8/call get-or-insert/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp -$test-get-slice:check1: - # (eax..edx) = "code" - b8/copy-to-eax "code"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 2/r32/edx . . # copy *eax to edx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 2/index/edx . 2/r32/edx 4/disp8 . # copy eax+edx+4 to edx - 05/add-to-eax 4/imm32 - # var slice/edx = {eax, edx} - 52/push-edx - 50/push-eax - 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx - # eax = get-slice(table, "code", 8 bytes per row) - # . . push args - 68/push 8/imm32/row-size - 52/push-edx - 51/push-ecx - # . . call - e8/call get-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # check-ints-equal(eax - table->data, 4, msg) # first row's value slot returned - # . check-ints-equal(eax - table, 16, msg) - # . . push args - 68/push "F - test-get-slice/0"/imm32 - 68/push 0x10/imm32 - 29/subtract 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # subtract ecx from eax - 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-get-slice:check2: - # (eax..edx) = "data" - b8/copy-to-eax "data"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 2/r32/edx . . # copy *eax to edx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 2/index/edx . 2/r32/edx 4/disp8 . # copy eax+edx+4 to edx - 05/add-to-eax 4/imm32 - # var slice/edx = {eax, edx} - 52/push-edx - 50/push-eax - 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx - # eax = get-slice(table, "data" slice, 8 bytes per row) - # . . push args - 68/push 8/imm32/row-size - 52/push-edx - 51/push-ecx - # . . call - e8/call get-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # check-ints-equal(eax - table->data, 12, msg) - # . check-ints-equal(eax - table, 24, msg) - # . . push args - 68/push "F - test-get-slice/1"/imm32 - 68/push 0x18/imm32 - 29/subtract 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # subtract ecx from eax - 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-get-slice:end: - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -# if no row is found, save 'key' to the next available row -# if there are no rows free, abort -# return the address of the value -# Beware: assume keys are immutable; they're inserted by reference -# TODO: pass in an allocation descriptor -get-or-insert: # table : (address stream {string, _}), key : (address string), row-size : int -> eax : (address _) - # pseudocode: - # curr = table->data - # max = &table->data[table->write] - # while curr < max - # if string-equal?(key, *curr) - # return curr+4 - # curr += row-size - # if table->write >= table->length - # abort - # zero-out(max, row-size) - # *max = key - # table->write += row-size - # return max+4 - # - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 51/push-ecx - 52/push-edx - 56/push-esi - # esi = table - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi - # curr/ecx = table->data - 8d/copy-address 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 0xc/disp8 . # copy esi+12 to ecx - # max/edx = table->data + table->write - 8b/copy 0/mod/indirect 6/rm32/esi . . . 2/r32/edx . . # copy *esi to edx - 8d/copy-address 0/mod/indirect 4/rm32/sib 1/base/ecx 2/index/edx . 2/r32/edx . . # copy ecx+edx to edx -$get-or-insert:search-loop: - # if (curr >= max) break - 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx - 73/jump-if-greater-or-equal-unsigned $get-or-insert:not-found/disp8 - # if (string-equal?(key, *curr)) return curr+4 - # . eax = string-equal?(key, *curr) - # . . push args - ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) - # . . call - e8/call string-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . if (eax != 0) return eax = curr+4 - 3d/compare-eax-and 0/imm32 - 74/jump-if-equal $get-or-insert:mismatch/disp8 - 8d/copy-address 1/mod/*+disp8 1/rm32/ecx . . . 0/r32/eax 4/disp8 . # copy ecx+4 to eax - eb/jump $get-or-insert:end/disp8 -$get-or-insert:mismatch: - # curr += row-size - 03/add 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 0x10/disp8 . # add *(ebp+16) to ecx - # loop - eb/jump $get-or-insert:search-loop/disp8 -$get-or-insert:not-found: - # result/eax = 0 - 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax - # if (table->write >= table->length) abort - 8b/copy 0/mod/indirect 6/rm32/esi . . . 1/r32/ecx . . # copy *esi to ecx - 3b/compare 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 8/disp8 . # compare ecx with *(esi+8) - 73/jump-if-greater-or-equal-unsigned $get-or-insert:abort/disp8 - # zero-out(max, row-size) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16) - 52/push-edx - # . . call - e8/call zero-out/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # *max = key - # . eax = key - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 0xc/disp8 . # copy *(ebp+12) to eax - # . *max = eax - 89/copy 0/mod/indirect 2/rm32/edx . . . 0/r32/eax . . # copy eax to *edx - # table->write += row-size - # . eax = row-size - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 0x10/disp8 . # copy *(ebp+16) to eax - # . table->write += eax - 01/add 0/mod/indirect 6/rm32/esi . . . 0/r32/eax . . # add eax to *esi - # return max+4 - # . eax = max - 89/copy 3/mod/direct 0/rm32/eax . . . 2/r32/edx . . # copy edx to eax - # . eax += 4 - 05/add-to-eax 4/imm32 -$get-or-insert:end: - # . restore registers - 5e/pop-to-esi - 5a/pop-to-edx - 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 - -$get-or-insert:abort: - # . _write(2/stderr, error) - # . . push args - 68/push "get-or-insert: table is full\n"/imm32 - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . syscall(exit, 1) - bb/copy-to-ebx 1/imm32 - b8/copy-to-eax 1/imm32/exit - cd/syscall 0x80/imm8 - # never gets here - -test-get-or-insert: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # var table/ecx : (address stream {string, number}) = stream(2 rows * 8 bytes) - 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # subtract from esp - 68/push 0x10/imm32/length - 68/push 0/imm32/read - 68/push 0/imm32/write - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx -$test-get-or-insert:first-call: - # - start with an empty table, insert one key, verify that it was inserted - # eax = get-or-insert(table, "code", 8 bytes per row) - # . . push args - 68/push 8/imm32/row-size - 68/push "code"/imm32 - 51/push-ecx - # . . call - e8/call get-or-insert/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # check-ints-equal(eax - table->data, 4, msg) # first row's value slot returned - # . check-ints-equal(eax - table, 16, msg) - # . . push args - 68/push "F - test-get-or-insert/0"/imm32 - 68/push 0x10/imm32 - 29/subtract 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # subtract ecx from eax - 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-get-or-insert:check2: - # check-ints-equal(table->write, row-size = 8, msg) - # . . push args - 68/push "F - test-get-or-insert/1"/imm32 - 68/push 8/imm32/row-size - ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # check-string-equal(*table->data, "code", msg) - # . . push args - 68/push "F - test-get-or-insert/2"/imm32 - 68/push "code"/imm32 - ff 6/subop/push 1/mod/*+disp8 1/rm32/ecx . . . . 0xc/disp8 . # push *(ecx+12) - # . . call - e8/call check-string-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp -$test-get-or-insert:second-call: - # - insert the same key again, verify that it was reused - # eax = get-or-insert(table, "code", 8 bytes per row) - # . . push args - 68/push 8/imm32/row-size - 68/push "code"/imm32 - 51/push-ecx - # . . call - e8/call get-or-insert/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # check-ints-equal(eax - table->data, 4, msg) - # . check-ints-equal(eax - table, 16, msg) - # . . push args - 68/push "F - test-get-or-insert/3"/imm32 - 68/push 0x10/imm32 - 29/subtract 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # subtract ecx from eax - 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 - # no new row inserted - # . check-ints-equal(table->write, row-size = 8, msg) - # . . push args - 68/push "F - test-get-or-insert/4"/imm32 - 68/push 8/imm32/row-size - ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # check-string-equal(*table->data, "code", msg) - # . . push args - 68/push "F - test-get-or-insert/5"/imm32 - 68/push "code"/imm32 - ff 6/subop/push 1/mod/*+disp8 1/rm32/ecx . . . . 0xc/disp8 . # push *(ecx+12) - # . . call - e8/call check-string-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp -$test-get-or-insert:third-call: - # - insert a new key, verify that it was inserted - # eax = get-or-insert(table, "data", 8 bytes per row) - # . . push args - 68/push 8/imm32/row-size - 68/push "data"/imm32 - 51/push-ecx - # . . call - e8/call get-or-insert/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # table gets a new row - # check-ints-equal(eax - table->data, 12, msg) # second row's value slot returned - # . check-ints-equal(eax - table, 24, msg) - # . . push args - 68/push "F - test-get-or-insert/6"/imm32 - 68/push 0x18/imm32 - 29/subtract 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # subtract ecx from eax - 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 - # check-ints-equal(table->write, 2 rows = 16, msg) - # . . push args - 68/push "F - test-get-or-insert/7"/imm32 - 68/push 0x10/imm32/two-rows - ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # check-string-equal(*table->data+8, "data", msg) - # check-string-equal(*(table+20), "data", msg) - # . . push args - 68/push "F - test-get-or-insert/8"/imm32 - 68/push "data"/imm32 - ff 6/subop/push 1/mod/*+disp8 1/rm32/ecx . . . . 0x14/disp8 . # push *(ecx+20) - # . . call - e8/call check-string-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp -$test-get-or-insert:end: - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -# if no row is found, save 'key' in the next available row -# if there are no rows free, abort -# WARNING: leaks memory -# TODO: pass in an allocation descriptor -leaky-get-or-insert-slice: # table : (address stream {string, _}), key : (address slice), row-size : int -> eax : (address _) - # pseudocode: - # curr = table->data - # max = &table->data[table->write] - # while curr < max - # if slice-equal?(key, *curr) - # return curr+4 - # curr += row-size - # if table->write >= table->length - # abort - # zero-out(max, row-size) - # *max = slice-to-string(Heap, key) - # table->write += row-size - # return max+4 - # - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 51/push-ecx - 52/push-edx - 56/push-esi - # esi = table - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi - # curr/ecx = table->data - 8d/copy-address 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 0xc/disp8 . # copy esi+12 to ecx - # max/edx = table->data + table->write - 8b/copy 0/mod/indirect 6/rm32/esi . . . 2/r32/edx . . # copy *esi to edx - 8d/copy-address 0/mod/indirect 4/rm32/sib 1/base/ecx 2/index/edx . 2/r32/edx . . # copy ecx+edx to edx -$leaky-get-or-insert-slice:search-loop: - # if (curr >= max) break - 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx - 73/jump-if-greater-or-equal-unsigned $leaky-get-or-insert-slice:not-found/disp8 - # if (slice-equal?(key, *curr)) return curr+4 - # . eax = slice-equal?(key, *curr) - # . . push args - ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) - # . . call - e8/call slice-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . if (eax != 0) return eax = curr+4 - 3d/compare-eax-and 0/imm32 - 74/jump-if-equal $leaky-get-or-insert-slice:mismatch/disp8 - 8d/copy-address 1/mod/*+disp8 1/rm32/ecx . . . 0/r32/eax 4/disp8 . # copy ecx+4 to eax - eb/jump $leaky-get-or-insert-slice:end/disp8 -$leaky-get-or-insert-slice:mismatch: - # curr += row-size - 03/add 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 0x10/disp8 . # add *(ebp+16) to ecx - # loop - eb/jump $leaky-get-or-insert-slice:search-loop/disp8 -$leaky-get-or-insert-slice:not-found: - # result/eax = 0 - 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax - # if (table->write >= table->length) abort - 8b/copy 0/mod/indirect 6/rm32/esi . . . 1/r32/ecx . . # copy *esi to ecx - 3b/compare 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 8/disp8 . # compare ecx with *(esi+8) - 7d/jump-if-greater-or-equal $leaky-get-or-insert-slice:abort/disp8 - # zero-out(max, row-size) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16) - 52/push-edx - # . . call - e8/call zero-out/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # *max = slice-to-string(Heap, key) - # . eax = slice-to-string(Heap, key) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) - 68/push Heap/imm32 - # . . call - e8/call slice-to-string/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . *max = eax - 89/copy 0/mod/indirect 2/rm32/edx . . . 0/r32/eax . . # copy eax to *edx - # table->write += row-size - # . eax = row-size - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 0x10/disp8 . # copy *(ebp+16) to eax - # . table->write += eax - 01/add 0/mod/indirect 6/rm32/esi . . . 0/r32/eax . . # add eax to *esi - # return max+4 - # . eax = max - 89/copy 3/mod/direct 0/rm32/eax . . . 2/r32/edx . . # copy edx to eax - # . eax += 4 - 05/add-to-eax 4/imm32 -$leaky-get-or-insert-slice:end: - # . restore registers - 5e/pop-to-esi - 5a/pop-to-edx - 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 - -$leaky-get-or-insert-slice:abort: - # . _write(2/stderr, error) - # . . push args - 68/push "leaky-get-or-insert-slice: table is full\n"/imm32 - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . syscall(exit, 1) - bb/copy-to-ebx 1/imm32 - b8/copy-to-eax 1/imm32/exit - cd/syscall 0x80/imm8 - # never gets here - -test-leaky-get-or-insert-slice: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # var table/ecx : (address stream {string, number}) = stream(2 rows * 8 bytes) - 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # subtract from esp - 68/push 0x10/imm32/length - 68/push 0/imm32/read - 68/push 0/imm32/write - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # (eax..edx) = "code" - b8/copy-to-eax "code"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 2/r32/edx . . # copy *eax to edx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 2/index/edx . 2/r32/edx 4/disp8 . # copy eax+edx+4 to edx - 05/add-to-eax 4/imm32 - # var slice/edx = {eax, edx} - 52/push-edx - 50/push-eax - 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx -$test-leaky-get-or-insert-slice:first-call: - # - start with an empty table, insert one key, verify that it was inserted - # eax = leaky-get-or-insert-slice(table, "code" slice, 8 bytes per row) - # . . push args - 68/push 8/imm32/row-size - 52/push-edx - 51/push-ecx - # . . call - e8/call leaky-get-or-insert-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # check-ints-equal(eax - table->data, 4, msg) # first row's value slot returned - # . check-ints-equal(eax - table, 16, msg) - # . . push args - 68/push "F - test-leaky-get-or-insert-slice/0"/imm32 - 68/push 0x10/imm32 - 29/subtract 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # subtract ecx from eax - 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-leaky-get-or-insert-slice:check2: - # check-ints-equal(table->write, row-size = 8, msg) - # . . push args - 68/push "F - test-leaky-get-or-insert-slice/1"/imm32 - 68/push 8/imm32/row-size - ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # check-string-equal(*table->data, "code", msg) - # . . push args - 68/push "F - test-leaky-get-or-insert-slice/2"/imm32 - 68/push "code"/imm32 - ff 6/subop/push 1/mod/*+disp8 1/rm32/ecx . . . . 0xc/disp8 . # push *(ecx+12) - # . . call - e8/call check-string-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp -$test-leaky-get-or-insert-slice:second-call: - # - insert the same key again, verify that it was reused - # eax = leaky-get-or-insert-slice(table, "code" slice, 8 bytes per row) - # . . push args - 68/push 8/imm32/row-size - 52/push-edx - 51/push-ecx - # . . call - e8/call leaky-get-or-insert-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # check-ints-equal(eax - table->data, 4, msg) - # . check-ints-equal(eax - table, 16, msg) - # . . push args - 68/push "F - test-leaky-get-or-insert-slice/3"/imm32 - 68/push 0x10/imm32 - 29/subtract 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # subtract ecx from eax - 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 - # no new row inserted - # . check-ints-equal(table->write, row-size = 8, msg) - # . . push args - 68/push "F - test-leaky-get-or-insert-slice/4"/imm32 - 68/push 8/imm32/row-size - ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # check-string-equal(*table->data, "code", msg) - # . . push args - 68/push "F - test-leaky-get-or-insert-slice/5"/imm32 - 68/push "code"/imm32 - ff 6/subop/push 1/mod/*+disp8 1/rm32/ecx . . . . 0xc/disp8 . # push *(ecx+12) - # . . call - e8/call check-string-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp -$test-leaky-get-or-insert-slice:third-call: - # - insert a new key, verify that it was inserted - # (eax..edx) = "data" - b8/copy-to-eax "data"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 2/r32/edx . . # copy *eax to edx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 2/index/edx . 2/r32/edx 4/disp8 . # copy eax+edx+4 to edx - 05/add-to-eax 4/imm32 - # var slice/edx = {eax, edx} - 52/push-edx - 50/push-eax - 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx - # eax = leaky-get-or-insert-slice(table, "data" slice, 8 bytes per row) - # . . push args - 68/push 8/imm32/row-size - 52/push-edx - 51/push-ecx - # . . call - e8/call leaky-get-or-insert-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # table gets a new row - # check-ints-equal(eax - table->data, 12, msg) # second row's value slot returned - # . check-ints-equal(eax - table, 24, msg) - # . . push args - 68/push "F - test-leaky-get-or-insert-slice/6"/imm32 - 68/push 0x18/imm32 - 29/subtract 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # subtract ecx from eax - 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 - # check-ints-equal(table->write, 2 rows = 16, msg) - # . . push args - 68/push "F - test-leaky-get-or-insert-slice/7"/imm32 - 68/push 0x10/imm32/two-rows - ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # check-string-equal(*table->data+8, "data", msg) - # check-string-equal(*(table+20), "data", msg) - # . . push args - 68/push "F - test-leaky-get-or-insert-slice/8"/imm32 - 68/push "data"/imm32 - ff 6/subop/push 1/mod/*+disp8 1/rm32/ecx . . . . 0x14/disp8 . # push *(ecx+20) - # . . call - e8/call check-string-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp -$test-leaky-get-or-insert-slice:end: - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -# if no row is found, stop(ed) -get-or-stop: # table : (address stream {string, _}), key : (address string), row-size : int, - # abort-message-prefix : (address string), err : (address buffered-file), ed : (address exit-descriptor) - # -> eax : (address _) - # pseudocode: - # curr = table->data - # max = &table->data[table->write] - # while curr < max - # if string-equal?(key, *curr) - # return curr+4 - # curr += row-size - # write-buffered(err, msg) - # stop(ed) - # - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 51/push-ecx - 52/push-edx - 56/push-esi - # esi = table - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi - # curr/ecx = table->data - 8d/copy-address 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 0xc/disp8 . # copy esi+12 to ecx - # max/edx = table->data + table->write - 8b/copy 0/mod/indirect 6/rm32/esi . . . 2/r32/edx . . # copy *esi to edx - 8d/copy-address 0/mod/indirect 4/rm32/sib 1/base/ecx 2/index/edx . 2/r32/edx . . # copy ecx+edx to edx -$get-or-stop:search-loop: - # if (curr >= max) stop(ed) - 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx - 73/jump-if-greater-or-equal-unsigned $get-or-stop:stop/disp8 - # if (string-equal?(key, *curr)) return curr+4 - # . eax = string-equal?(key, *curr) - # . . push args - ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) - # . . call - e8/call string-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . if (eax != 0) return eax = curr+4 - 3d/compare-eax-and 0/imm32 - 74/jump-if-equal $get-or-stop:mismatch/disp8 - 8d/copy-address 1/mod/*+disp8 1/rm32/ecx . . . 0/r32/eax 4/disp8 . # copy ecx+4 to eax - eb/jump $get-or-stop:end/disp8 -$get-or-stop:mismatch: - # curr += row-size - 03/add 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 0x10/disp8 . # add *(ebp+16) to ecx - # loop - eb/jump $get-or-stop:search-loop/disp8 -$get-or-stop:end: - # . restore registers - 5e/pop-to-esi - 5a/pop-to-edx - 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 - -$get-or-stop:stop: - # . write-buffered(err, abort-message-prefix) - # . . 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 . . . . 0x18/disp8 . # push *(ebp+24) - # . . call - e8/call write-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . write-buffered(err, error) - # . . push args - 68/push ": get-or-stop: key not found: "/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x18/disp8 . # push *(ebp+24) - # . . call - e8/call write-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . write-buffered(err, key) - # . . push args - 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 . . . . 0x18/disp8 . # push *(ebp+24) - # . . call - e8/call write-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . write-buffered(err, "\n") - # . . push args - 68/push Newline/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x18/disp8 . # push *(ebp+24) - # . . call - e8/call write-buffered/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 . . . . 0x1c/disp8 . # push *(ebp+28) - # . . call - e8/call stop/disp32 - # never gets here -$get-or-stop:terminus: - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # syscall(exit, 1) - b8/copy-to-eax 1/imm32/exit - cd/syscall 0x80/imm8 - -test-get-or-stop: - # This test uses exit-descriptors. Use ebp for setting up local variables. - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # setup - # . 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 - # var table/ecx : (address stream {string, number}) = stream(2 rows * 8 bytes) - 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # subtract from esp - 68/push 0x10/imm32/length - 68/push 0/imm32/read - 68/push 0/imm32/write - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # var ed/edx : (address exit-descriptor) - 68/push 0/imm32 - 68/push 0/imm32 - 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx - # size 'ed' for the calls to 'get-or-stop' - # . tailor-exit-descriptor(ed, 24) - # . . push args - 68/push 0x18/imm32/nbytes-of-args-for-get-or-stop - 52/push-edx - # . . call - e8/call tailor-exit-descriptor/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # insert(table, "code", 8 bytes per row) - # . . push args - 68/push 8/imm32/row-size - 68/push "code"/imm32 - 51/push-ecx - # . . call - e8/call get-or-insert/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp -$test-get-or-stop:success: - # eax = get-or-stop(table, "code", row-size=8, msg, _test-error-buffered-file, ed) - # . . push args - 52/push-edx/ed - 68/push _test-error-buffered-file/imm32 - 68/push "foo"/imm32/abort-prefix - 68/push 8/imm32/row-size - 68/push "code"/imm32 - 51/push-ecx - # . . call - e8/call get-or-stop/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x18/imm32 # add to esp -$test-get-or-stop:success-assertion: - # check-ints-equal(eax - table->data, 4, msg) - # . check-ints-equal(eax - table, 16, msg) - # . . push args - 68/push "F - test-get-or-stop/0"/imm32 - 68/push 0x10/imm32 - 29/subtract 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # subtract ecx from eax - 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-get-or-stop:failure: - # eax = get-or-stop(table, "data", row-size=8, msg, _test-error-buffered-file, ed) - # . . push args - 52/push-edx/ed - 68/push _test-error-buffered-file/imm32 - 68/push "foo"/imm32/abort-prefix - 68/push 8/imm32/row-size - 68/push "data"/imm32 - 51/push-ecx - # . . call - e8/call get-or-stop/disp32 - # registers except esp may be clobbered at this point - # restore register args, discard others - 59/pop-to-ecx - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp - 5a/pop-to-edx -$test-get-or-stop:failure-assertion: - # check that get-or-stop tried to call stop(1) - # . check-ints-equal(ed->value, 2, msg) - # . . push args - 68/push "F - test-get-or-stop/1"/imm32 - 68/push 2/imm32 - # . . push ed->value - ff 6/subop/push 1/mod/*+disp8 2/rm32/edx . . . . 4/disp8 . # push *(edx+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-get-or-stop:end: - # . epilog - # don't restore esp from ebp; manually reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x24/imm32 # add to esp - 5d/pop-to-ebp - c3/return - -# if no row is found, stop(ed) -get-slice-or-stop: # table : (address stream {string, _}), key : (address slice), row-size : int, - # abort-message-prefix : (address string), err : (address buffered-file), ed : (address exit-descriptor) - # -> eax : (address _) - # pseudocode: - # curr = table->data - # max = &table->data[table->write] - # while curr < max - # if slice-equal?(key, *curr) - # return curr+4 - # curr += row-size - # write-buffered(err, msg) - # stop(ed) - # - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 51/push-ecx - 52/push-edx - 56/push-esi - # esi = table - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi - # curr/ecx = table->data - 8d/copy-address 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 0xc/disp8 . # copy esi+12 to ecx - # max/edx = table->data + table->write - 8b/copy 0/mod/indirect 6/rm32/esi . . . 2/r32/edx . . # copy *esi to edx - 8d/copy-address 0/mod/indirect 4/rm32/sib 1/base/ecx 2/index/edx . 2/r32/edx . . # copy ecx+edx to edx -$get-slice-or-stop:search-loop: - # if (curr >= max) stop(ed) - 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx - 73/jump-if-greater-or-equal-unsigned $get-slice-or-stop:stop/disp8 - # if (slice-equal?(key, *curr)) return curr+4 - # . eax = slice-equal?(key, *curr) - # . . push args - ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) - # . . call - e8/call slice-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . if (eax != 0) return eax = curr+4 - 3d/compare-eax-and 0/imm32 - 74/jump-if-equal $get-slice-or-stop:mismatch/disp8 - 8d/copy-address 1/mod/*+disp8 1/rm32/ecx . . . 0/r32/eax 4/disp8 . # copy ecx+4 to eax - eb/jump $get-slice-or-stop:end/disp8 -$get-slice-or-stop:mismatch: - # curr += row-size - 03/add 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 0x10/disp8 . # add *(ebp+16) to ecx - # loop - eb/jump $get-slice-or-stop:search-loop/disp8 -$get-slice-or-stop:end: - # . restore registers - 5e/pop-to-esi - 5a/pop-to-edx - 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 - -$get-slice-or-stop:stop: - # . write-buffered(err, abort-message-prefix) - # . . 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 . . . . 0x18/disp8 . # push *(ebp+24) - # . . call - e8/call write-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . write-buffered(err, error) - # . . push args - 68/push ": get-slice-or-stop: key not found: "/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x18/disp8 . # push *(ebp+24) - # . . call - e8/call write-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . write-slice-buffered(err, key) - # . . push args - 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 . . . . 0x18/disp8 . # push *(ebp+24) - # . . call - e8/call write-slice-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . write-buffered(err, "\n") - # . . push args - 68/push Newline/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x18/disp8 . # push *(ebp+24) - # . . call - e8/call write-buffered/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 . . . . 0x1c/disp8 . # push *(ebp+28) - # . . call - e8/call stop/disp32 - # never gets here -$get-slice-or-stop:terminus: - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # syscall(exit, 1) - b8/copy-to-eax 1/imm32/exit - cd/syscall 0x80/imm8 - -test-get-slice-or-stop: - # This test uses exit-descriptors. Use ebp for setting up local variables. - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # setup - # . 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 - # var table/ecx : (address stream {string, number}) = stream(2 rows * 8 bytes) - 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # subtract from esp - 68/push 0x10/imm32/length - 68/push 0/imm32/read - 68/push 0/imm32/write - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # var ed/edx : (address exit-descriptor) - 68/push 0/imm32 - 68/push 0/imm32 - 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx - # var slice/ebx = "code" - # . (eax..ebx) = "code" - b8/copy-to-eax "code"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 3/r32/ebx . . # copy *eax to ebx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 3/index/ebx . 3/r32/ebx 4/disp8 . # copy eax+ebx+4 to ebx - 05/add-to-eax 4/imm32 - # . ebx = {eax, ebx} - 53/push-ebx - 50/push-eax - 89/copy 3/mod/direct 3/rm32/ebx . . . 4/r32/esp . . # copy esp to ebx - # size 'ed' for the calls to 'get-or-stop' (define no locals past this point) - # . tailor-exit-descriptor(ed, 24) - # . . push args - 68/push 0x18/imm32/nbytes-of-args-for-get-or-stop - 52/push-edx - # . . call - e8/call tailor-exit-descriptor/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # insert(table, "code", 8 bytes per row) - # . . push args - 68/push 8/imm32/row-size - 68/push "code"/imm32 - 51/push-ecx - # . . call - e8/call get-or-insert/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp -$test-get-slice-or-stop:success: - # eax = get-slice-or-stop(table, slice, row-size=8, msg, _test-error-buffered-file, ed) - # . . push args - 52/push-edx/ed - 68/push _test-error-buffered-file/imm32 - 68/push "foo"/imm32/abort-prefix - 68/push 8/imm32/row-size - 53/push-ebx/slice - 51/push-ecx - # . . call - e8/call get-slice-or-stop/disp32 - # registers except esp may be clobbered at this point - # restore register args, discard others - 59/pop-to-ecx - 5b/pop-to-ebx - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - 5a/pop-to-edx -$test-get-slice-or-stop:success-assertion: - # check-ints-equal(eax - table->data, 4, msg) # first row's value slot returned - # . check-ints-equal(eax - table, 16, msg) - # . . push args - 68/push "F - test-get-slice-or-stop/0"/imm32 - 68/push 0x10/imm32 - 29/subtract 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # subtract ecx from eax - 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-get-slice-or-stop:failure: - # slice = "segment2" - # . *ebx = "segment2"->data - b8/copy-to-eax "segment2"/imm32 - 05/add-to-eax 4/imm32 - 89/copy 0/mod/indirect 3/rm32/ebx . . . 0/r32/eax . . # copy eax to *ebx - # . *(ebx+4) = "segment2"->data + len("segment2") - 05/add-to-eax 8/imm32/strlen - 89/copy 1/mod/*+disp8 3/rm32/ebx . . . 0/r32/eax 4/disp8 . # copy eax to *(ebx+4) - # eax = get-slice-or-stop(table, slice, row-size=8, msg, _test-error-buffered-file, ed) - # . . push args - 52/push-edx/ed - 68/push _test-error-buffered-file/imm32 - 68/push "foo"/imm32/abort-prefix - 68/push 8/imm32/row-size - 53/push-ebx/slice - 51/push-ecx - # . . call - e8/call get-slice-or-stop/disp32 - # registers except esp may be clobbered at this point - # restore register args, discard others - 59/pop-to-ecx - 5b/pop-to-ebx - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - 5a/pop-to-edx -$test-get-slice-or-stop:failure-assertion: - # check that get-or-stop tried to call stop(1) - # . check-ints-equal(ed->value, 2, msg) - # . . push args - 68/push "F - test-get-or-stop/1"/imm32 - 68/push 2/imm32 - # . . push ed->value - ff 6/subop/push 1/mod/*+disp8 2/rm32/edx . . . . 4/disp8 . # push *(edx+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-get-slice-or-stop:end: - # . epilog - # don't restore esp from ebp; manually reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x2c/imm32 # add to esp - 5d/pop-to-ebp - c3/return - -# if no row is found, return null (0) -maybe-get: # table : (address stream {string, _}), key : (address string), row-size : int -> eax : (address _) - # pseudocode: - # curr = table->data - # max = &table->data[table->write] - # while curr < max - # if string-equal?(key, *curr) - # return curr+4 - # curr += row-size - # return 0 - # - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 51/push-ecx - 52/push-edx - 56/push-esi - # esi = table - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi - # curr/ecx = table->data - 8d/copy-address 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 0xc/disp8 . # copy esi+12 to ecx - # max/edx = table->data + table->write - 8b/copy 0/mod/indirect 6/rm32/esi . . . 2/r32/edx . . # copy *esi to edx - 8d/copy-address 0/mod/indirect 4/rm32/sib 1/base/ecx 2/index/edx . 2/r32/edx . . # copy ecx+edx to edx -$maybe-get:search-loop: - # if (curr >= max) return null - 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx - 73/jump-if-greater-or-equal-unsigned $maybe-get:null/disp8 - # if (string-equal?(key, *curr)) return curr+4 - # . eax = string-equal?(key, *curr) - # . . push args - ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) - # . . call - e8/call string-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . if (eax != 0) return eax = curr+4 - 3d/compare-eax-and 0/imm32 - 74/jump-if-equal $maybe-get:mismatch/disp8 - 8d/copy-address 1/mod/*+disp8 1/rm32/ecx . . . 0/r32/eax 4/disp8 . # copy ecx+4 to eax - eb/jump $maybe-get:end/disp8 -$maybe-get:mismatch: - # curr += row-size - 03/add 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 0x10/disp8 . # add *(ebp+16) to ecx - # loop - eb/jump $maybe-get:search-loop/disp8 -$maybe-get:null: - b8/copy-to-eax 0/imm32 -$maybe-get:end: - # . restore registers - 5e/pop-to-esi - 5a/pop-to-edx - 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-maybe-get: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # - setup: create a table with one row - # var table/ecx : (address stream {string, number}) = stream(2 rows * 8 bytes) - 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # subtract from esp - 68/push 0x10/imm32/length - 68/push 0/imm32/read - 68/push 0/imm32/write - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # eax = get-or-insert(table, "code", 8 bytes per row) - # . . push args - 68/push 8/imm32/row-size - 68/push "code"/imm32 - 51/push-ecx - # . . call - e8/call get-or-insert/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp -$test-maybe-get:success: - # - check for the same key, verify that it was reused - # eax = maybe-get(table, "code", 8 bytes per row) - # . . push args - 68/push 8/imm32/row-size - 68/push "code"/imm32 - 51/push-ecx - # . . call - e8/call maybe-get/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # check-ints-equal(eax - table->data, 4, msg) - # . check-ints-equal(eax - table, 16, msg) - # . . push args - 68/push "F - test-maybe-get/0"/imm32 - 68/push 0x10/imm32 - 29/subtract 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # subtract ecx from eax - 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 - # no new row inserted - # . check-ints-equal(table->write, row-size = 8, msg) - # . . push args - 68/push "F - test-maybe-get/1"/imm32 - 68/push 8/imm32/row-size - ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # check-string-equal(*table->data, "code", msg) - # . . push args - 68/push "F - test-maybe-get/2"/imm32 - 68/push "code"/imm32 - ff 6/subop/push 1/mod/*+disp8 1/rm32/ecx . . . . 0xc/disp8 . # push *(ecx+12) - # . . call - e8/call check-string-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp -$test-maybe-get:failure: - # - search for a new key - # eax = maybe-get(table, "data", 8 bytes per row) - # . . push args - 68/push 8/imm32/row-size - 68/push "data"/imm32 - 51/push-ecx - # . . call - e8/call maybe-get/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # check-ints-equal(eax, 0, msg) - # . . push args - 68/push "F - test-maybe-get/3"/imm32 - 68/push 0/imm32 - 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-maybe-get:end: - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -# if no row is found, return null (0) -maybe-get-slice: # table : (address stream {string, _}), key : (address slice), row-size : int -> eax : (address _) - # pseudocode: - # curr = table->data - # max = &table->data[table->write] - # while curr < max - # if slice-equal?(key, *curr) - # return curr+4 - # curr += row-size - # return 0 - # - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 51/push-ecx - 52/push-edx - 56/push-esi - # esi = table - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi - # curr/ecx = table->data - 8d/copy-address 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 0xc/disp8 . # copy esi+12 to ecx - # max/edx = table->data + table->write - 8b/copy 0/mod/indirect 6/rm32/esi . . . 2/r32/edx . . # copy *esi to edx - 8d/copy-address 0/mod/indirect 4/rm32/sib 1/base/ecx 2/index/edx . 2/r32/edx . . # copy ecx+edx to edx -$maybe-get-slice:search-loop: - # if (curr >= max) return null - 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx - 73/jump-if-greater-or-equal-unsigned $maybe-get-slice:null/disp8 - # if (slice-equal?(key, *curr)) return curr+4 - # . eax = slice-equal?(key, *curr) - # . . push args - ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) - # . . call - e8/call slice-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . if (eax != 0) return eax = curr+4 - 3d/compare-eax-and 0/imm32 - 74/jump-if-equal $maybe-get-slice:mismatch/disp8 - 8d/copy-address 1/mod/*+disp8 1/rm32/ecx . . . 0/r32/eax 4/disp8 . # copy ecx+4 to eax - eb/jump $maybe-get-slice:end/disp8 -$maybe-get-slice:mismatch: - # curr += row-size - 03/add 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 0x10/disp8 . # add *(ebp+16) to ecx - # loop - eb/jump $maybe-get-slice:search-loop/disp8 -$maybe-get-slice:null: - b8/copy-to-eax 0/imm32 -$maybe-get-slice:end: - # . restore registers - 5e/pop-to-esi - 5a/pop-to-edx - 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-maybe-get-slice: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # - setup: create a table with one row - # var table/ecx : (address stream {string, number}) = stream(2 rows * 8 bytes) - 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # subtract from esp - 68/push 0x10/imm32/length - 68/push 0/imm32/read - 68/push 0/imm32/write - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # insert(table, "code", 8 bytes per row) - # . . push args - 68/push 8/imm32/row-size - 68/push "code"/imm32 - 51/push-ecx - # . . call - e8/call get-or-insert/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp -$test-maybe-get-slice:success: - # - check for the same key, verify that it was reused - # (eax..edx) = "code" - b8/copy-to-eax "code"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 2/r32/edx . . # copy *eax to edx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 2/index/edx . 2/r32/edx 4/disp8 . # copy eax+edx+4 to edx - 05/add-to-eax 4/imm32 - # var slice/edx = {eax, edx} - 52/push-edx - 50/push-eax - 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx - # eax = maybe-get-slice(table, "code" slice, 8 bytes per row) - # . . push args - 68/push 8/imm32/row-size - 52/push-edx - 51/push-ecx - # . . call - e8/call maybe-get-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # check-ints-equal(eax - table->data, 4, msg) - # . check-ints-equal(eax - table, 16, msg) - # . . push args - 68/push "F - test-maybe-get-slice/0"/imm32 - 68/push 0x10/imm32 - 29/subtract 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # subtract ecx from eax - 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 - # no new row inserted - # . check-ints-equal(table->write, row-size = 8, msg) - # . . push args - 68/push "F - test-maybe-get-slice/1"/imm32 - 68/push 8/imm32/row-size - ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # check-string-equal(*table->data, "code", msg) - # . . push args - 68/push "F - test-maybe-get-slice/2"/imm32 - 68/push "code"/imm32 - ff 6/subop/push 1/mod/*+disp8 1/rm32/ecx . . . . 0xc/disp8 . # push *(ecx+12) - # . . call - e8/call check-string-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp -$test-maybe-get-slice:failure: - # - search for a new key - # (eax..edx) = "data" - b8/copy-to-eax "data"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 2/r32/edx . . # copy *eax to edx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 2/index/edx . 2/r32/edx 4/disp8 . # copy eax+edx+4 to edx - 05/add-to-eax 4/imm32 - # var slice/edx = {eax, edx} - 52/push-edx - 50/push-eax - 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx - # eax = maybe-get-slice(table, "data" slice, 8 bytes per row) - # . . push args - 68/push 8/imm32/row-size - 52/push-edx - 51/push-ecx - # . . call - e8/call maybe-get-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # check-ints-equal(eax, 0, msg) - # . . push args - 68/push "F - test-maybe-get-slice/3"/imm32 - 68/push 0/imm32 - 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-maybe-get-slice:end: - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -# . . vim:nowrap:textwidth=0 diff --git a/089write-int.subx b/089write-int.subx deleted file mode 100644 index 29ba8d2a..00000000 --- a/089write-int.subx +++ /dev/null @@ -1,119 +0,0 @@ -# write-int: add a single int byte to a stream - -== code - -write-int: # out : (address stream), n : int -> - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 50/push-eax - 51/push-ecx - 57/push-edi - # edi = out - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 7/r32/edi 8/disp8 . # copy *(ebp+8) to edi - # ecx = out->write - 8b/copy 0/mod/indirect 7/rm32/edi . . . 1/r32/ecx . . # copy *edi to ecx - # if (out->write >= out->length) abort - 3b/compare 1/mod/*+disp8 7/rm32/edi . . . 1/r32/ecx 8/disp8 . # compare ecx with *(edi+8) - 7d/jump-if-greater-or-equal $write-int:abort/disp8 -$write-int:to-stream: - # out->data[out->write] = n - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 0xc/disp8 . # copy *(ebp+12) to eax - 89/copy 1/mod/*+disp8 4/rm32/sib 7/base/edi 1/index/ecx . 0/r32/eax 0xc/disp8 . # copy eax to *(edi+ecx+12) - # out->write += 4 - 81 0/subop/add 0/mod/indirect 7/rm32/edi . . . . . 4/imm32 # add to *edi -$write-int:end: - # . restore registers - 5f/pop-to-edi - 59/pop-to-ecx - 58/pop-to-eax - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -$write-int:abort: - # . _write(2/stderr, error) - # . . push args - 68/push "write-int: out of space\n"/imm32 - 68/push 2/imm32/stderr - # . . call - e8/call _write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . syscall(exit, 1) - bb/copy-to-ebx 1/imm32 - b8/copy-to-eax 1/imm32/exit - cd/syscall 0x80/imm8 - # never gets here - -test-write-int-single: - # - check that write-int writes to first int of 'stream' - # 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 - # write-int(_test-stream, "abcd") - # . . push args - 68/push 0x64636261/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write-int/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-stream-equal(_test-stream, "abcd", msg) - # . . push args - 68/push "F - test-write-int-single"/imm32 - 68/push "abcd"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . end - c3/return - -test-write-byte-buffered-multiple: - # - check that write-int correctly appends multiple writes - # 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 - # write-int(_test-stream, "abcd") - # . . push args - 68/push 0x64636261/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write-int/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # write-int(_test-stream, "efgh") - # . . push args - 68/push 0x68676665/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write-int/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-stream-equal(_test-stream, "abcdefgh", msg) - # . . push args - 68/push "F - test-write-byte-buffered-multiple"/imm32 - 68/push "abcdefgh"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call check-stream-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 diff --git a/092write-int.subx b/092write-int.subx new file mode 100644 index 00000000..29ba8d2a --- /dev/null +++ b/092write-int.subx @@ -0,0 +1,119 @@ +# write-int: add a single int byte to a stream + +== code + +write-int: # out : (address stream), n : int -> + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 50/push-eax + 51/push-ecx + 57/push-edi + # edi = out + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 7/r32/edi 8/disp8 . # copy *(ebp+8) to edi + # ecx = out->write + 8b/copy 0/mod/indirect 7/rm32/edi . . . 1/r32/ecx . . # copy *edi to ecx + # if (out->write >= out->length) abort + 3b/compare 1/mod/*+disp8 7/rm32/edi . . . 1/r32/ecx 8/disp8 . # compare ecx with *(edi+8) + 7d/jump-if-greater-or-equal $write-int:abort/disp8 +$write-int:to-stream: + # out->data[out->write] = n + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 0xc/disp8 . # copy *(ebp+12) to eax + 89/copy 1/mod/*+disp8 4/rm32/sib 7/base/edi 1/index/ecx . 0/r32/eax 0xc/disp8 . # copy eax to *(edi+ecx+12) + # out->write += 4 + 81 0/subop/add 0/mod/indirect 7/rm32/edi . . . . . 4/imm32 # add to *edi +$write-int:end: + # . restore registers + 5f/pop-to-edi + 59/pop-to-ecx + 58/pop-to-eax + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +$write-int:abort: + # . _write(2/stderr, error) + # . . push args + 68/push "write-int: out of space\n"/imm32 + 68/push 2/imm32/stderr + # . . call + e8/call _write/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . syscall(exit, 1) + bb/copy-to-ebx 1/imm32 + b8/copy-to-eax 1/imm32/exit + cd/syscall 0x80/imm8 + # never gets here + +test-write-int-single: + # - check that write-int writes to first int of 'stream' + # 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 + # write-int(_test-stream, "abcd") + # . . push args + 68/push 0x64636261/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call write-int/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-stream-equal(_test-stream, "abcd", msg) + # . . push args + 68/push "F - test-write-int-single"/imm32 + 68/push "abcd"/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call check-stream-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . end + c3/return + +test-write-byte-buffered-multiple: + # - check that write-int correctly appends multiple writes + # 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 + # write-int(_test-stream, "abcd") + # . . push args + 68/push 0x64636261/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call write-int/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # write-int(_test-stream, "efgh") + # . . push args + 68/push 0x68676665/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call write-int/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-stream-equal(_test-stream, "abcdefgh", msg) + # . . push args + 68/push "F - test-write-byte-buffered-multiple"/imm32 + 68/push "abcdefgh"/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call check-stream-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 diff --git a/093array-equal.subx b/093array-equal.subx new file mode 100644 index 00000000..d6dec878 --- /dev/null +++ b/093array-equal.subx @@ -0,0 +1,603 @@ +# Comparing arrays of numbers. + +== 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 + +array-equal?: # a : (address array int), b : (address array int) -> eax : boolean + # pseudocode: + # lena = a->length + # if (lena != b->length) return false + # i = 0 + # curra = a->data + # currb = b->data + # while i < lena + # i1 = *curra + # i2 = *currb + # if (c1 != c2) return false + # i+=4, curra+=4, currb+=4 + # return true + # + # registers: + # i: ecx + # lena: edx + # curra: esi + # currb: edi + # i1: eax + # i2: ebx + # + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 51/push-ecx + 52/push-edx + 53/push-ebx + 56/push-esi + 57/push-edi + # esi = a + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi + # edi = b + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 7/r32/edi 0xc/disp8 . # copy *(ebp+12) to edi + # lena/edx = a->length + 8b/copy 0/mod/indirect 6/rm32/esi . . . 2/r32/edx . . # copy *esi to edx +$array-equal?:lengths: + # if (lena != b->length) return false + 39/compare 0/mod/indirect 7/rm32/edi . . . 2/r32/edx . . # compare *edi and edx + 75/jump-if-not-equal $array-equal?:false/disp8 + # curra/esi = a->data + 81 0/subop/add 3/mod/direct 6/rm32/esi . . . . . 4/imm32 # add to esi + # currb/edi = b->data + 81 0/subop/add 3/mod/direct 7/rm32/edi . . . . . 4/imm32 # add to edi + # i/ecx = i1/eax = i2/ebx = 0 + 31/xor 3/mod/direct 1/rm32/ecx . . . 1/r32/ecx . . # clear ecx +$array-equal?:loop: + # if (i >= lena) return true + 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx + 7d/jump-if-greater-or-equal $array-equal?:true/disp8 + # i1 = *curra + 8b/copy 0/mod/indirect 6/rm32/esi . . . 0/r32/eax . . # copy *esi to eax + # i2 = *currb + 8b/copy 0/mod/indirect 7/rm32/edi . . . 3/r32/ebx . . # copy *edi to ebx + # if (i1 != i2) return false + 39/compare 3/mod/direct 0/rm32/eax . . . 3/r32/ebx . . # compare eax and ebx + 75/jump-if-not-equal $array-equal?:false/disp8 + # i += 4 + 81 0/subop/add 3/mod/direct 1/rm32/ecx . . . . . 4/imm32 # add to ecx + # currs += 4 + 81 0/subop/add 3/mod/direct 6/rm32/esi . . . . . 4/imm32 # add to esi + # currb += 4 + 81 0/subop/add 3/mod/direct 7/rm32/edi . . . . . 4/imm32 # add to edi + eb/jump $array-equal?:loop/disp8 +$array-equal?:true: + b8/copy-to-eax 1/imm32 + eb/jump $array-equal?:end/disp8 +$array-equal?:false: + b8/copy-to-eax 0/imm32 +$array-equal?:end: + # . restore registers + 5f/pop-to-edi + 5e/pop-to-esi + 5b/pop-to-ebx + 5a/pop-to-edx + 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-compare-empty-with-empty-array: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # var ecx = [] + 68/push 0/imm32/size + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # var edx = [] + 68/push 0/imm32/size + 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx + # eax = array-equal?(ecx, edx) + # . . push args + 52/push-edx + 51/push-ecx + # . . call + e8/call array-equal?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-ints-equal(eax, 1, msg) + # . . push args + 68/push "F - test-compare-empty-with-empty-array"/imm32 + 68/push 1/imm32/true + 50/push-eax + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-compare-empty-with-non-empty-array: # also checks length-mismatch code path + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # var ecx = [1] + 68/push 1/imm32 + 68/push 4/imm32/size + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # var edx = [] + 68/push 0/imm32/size + 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx + # eax = array-equal?(ecx, edx) + # . . push args + 52/push-edx + 51/push-ecx + # . . call + e8/call array-equal?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-ints-equal(eax, 0, msg) + # . . push args + 68/push "F - test-compare-empty-with-non-empty-array"/imm32 + 68/push 0/imm32/false + 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 + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-compare-equal-arrays: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # var ecx = [1, 2, 3] + 68/push 3/imm32 + 68/push 2/imm32 + 68/push 1/imm32 + 68/push 0xc/imm32/size + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # var edx = [1, 2, 3] + 68/push 3/imm32 + 68/push 2/imm32 + 68/push 1/imm32 + 68/push 0xc/imm32/size + 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx + # eax = array-equal?(ecx, edx) + # . . push args + 52/push-edx + 51/push-ecx + # . . call + e8/call array-equal?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-ints-equal(eax, 1, msg) + # . . push args + 68/push "F - test-compare-equal-arrays"/imm32 + 68/push 1/imm32/true + 50/push-eax + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-compare-inequal-arrays-equal-lengths: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # var ecx = [1, 4, 3] + 68/push 3/imm32 + 68/push 4/imm32 + 68/push 1/imm32 + 68/push 0xc/imm32/size + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # var edx = [1, 2, 3] + 68/push 3/imm32 + 68/push 2/imm32 + 68/push 1/imm32 + 68/push 0xc/imm32/size + 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx + # eax = array-equal?(ecx, edx) + # . . push args + 52/push-edx + 51/push-ecx + # . . call + e8/call array-equal?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-ints-equal(eax, 0, msg) + # . . push args + 68/push "F - test-compare-inequal-arrays-equal-lengths"/imm32 + 68/push 0/imm32/false + 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 + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +parse-array-of-ints: # ad : (address allocation-descriptor), s : (address string) -> result/eax : (address array int) + # pseudocode + # end = s->data + s->length + # curr = s->data + # size = 0 + # while true + # if (curr >= end) break + # curr = skip-chars-matching-in-slice(curr, end, ' ') + # if (curr >= end) break + # curr = skip-chars-not-matching-in-slice(curr, end, ' ') + # ++size + # result = allocate(ad, (size+1)*4) + # result->size = (size+1)*4 + # var slice = {s->data, 0} + # out = result->data + # while true + # if (slice->start >= end) break + # slice->start = skip-chars-matching-in-slice(slice->start, end, ' ') + # if (slice->start >= end) break + # slice->end = skip-chars-not-matching-in-slice(slice->start, end, ' ') + # *out = parse-hex-int(slice) + # out += 4 + # slice->start = slice->end + # return result + # + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 51/push-ecx + 52/push-edx + 53/push-ebx + 56/push-esi + 57/push-edi + # esi = s + 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 0xc/disp8 . # copy *(ebp+12) to esi + # curr/ecx = s->data + 8d/copy-address 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy esi+4 to ecx + # end/edx = s->data + s->length + # . edx = s->length + 8b/copy 0/mod/indirect 6/rm32/esi . . . 2/r32/edx . . # copy *esi to edx + # . edx += curr + 01/add 3/mod/direct 2/rm32/edx . . . 1/r32/ecx . . # add ecx to edx + # size/ebx = 0 + 31/xor 3/mod/direct 3/rm32/ebx . . . 3/r32/ebx . . # clear ebx +$parse-array-of-ints:loop1: + # if (curr >= end) break + 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx + 73/jump-if-greater-or-equal-unsigned $parse-array-of-ints:break1/disp8 + # curr = skip-chars-matching-in-slice(curr, end, ' ') + # . eax = skip-chars-matching-in-slice(curr, end, ' ') + # . . push args + 68/push 0x20/imm32/space + 52/push-edx + 51/push-ecx + # . . call + e8/call skip-chars-matching-in-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . ecx = eax + 89/copy 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # copy eax to ecx + # if (curr >= end) break + 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx + 73/jump-if-greater-or-equal-unsigned $parse-array-of-ints:break1/disp8 + # curr = skip-chars-not-matching-in-slice(curr, end, ' ') + # . eax = skip-chars-not-matching-in-slice(curr, end, ' ') + # . . push args + 68/push 0x20/imm32/space + 52/push-edx + 51/push-ecx + # . . call + e8/call skip-chars-not-matching-in-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . ecx = eax + 89/copy 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # copy eax to ecx + # size += 4 + 81 0/subop/add 3/mod/direct 3/rm32/ebx . . . . . 4/imm32 # add to ebx + eb/jump $parse-array-of-ints:loop1/disp8 +$parse-array-of-ints:break1: + # result/edi = allocate(ad, size+4) + # . eax = allocate(ad, size+4) + # . . push args + 89/copy 3/mod/direct 0/rm32/eax . . . 3/r32/ebx . . # copy ebx to eax + 05/add-to-eax 4/imm32 + 50/push-eax + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) + # . . call + e8/call allocate/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . edi = eax + 89/copy 3/mod/direct 7/rm32/edi . . . 0/r32/eax . . # copy eax to edi + # result->size = size + 89/copy 0/mod/indirect 0/rm32/eax . . . 3/r32/ebx . . # copy ebx to *eax +$parse-array-of-ints:pass2: + # var slice/ecx = {s->data, 0} + # . push 0 + 68/push 0/imm32/end + # . push s->data + 8d/copy-address 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy esi+4 to ecx + 51/push-ecx + # . bookmark + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # out/ebx = result->data + 8d/copy-address 1/mod/*+disp8 0/rm32/eax . . . 3/r32/ebx 4/disp8 . # copy eax+4 to ebx +$parse-array-of-ints:loop2: + # if (slice->start >= end) break + 39/compare 0/mod/indirect 1/rm32/ecx . . . 2/r32/edx . . # compare *ecx with edx + 73/jump-if-greater-or-equal-unsigned $parse-array-of-ints:end/disp8 + # slice->start = skip-chars-matching-in-slice(slice->start, end, ' ') + # . eax = skip-chars-matching-in-slice(slice->start, end, ' ') + # . . push args + 68/push 0x20/imm32/space + 52/push-edx + ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx + # . . call + e8/call skip-chars-matching-in-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . slice->start = eax + 89/copy 0/mod/indirect 1/rm32/ecx . . . 0/r32/eax . . # copy eax to *ecx + # if (slice->start >= end) break + 39/compare 0/mod/indirect 1/rm32/ecx . . . 2/r32/edx . . # compare *ecx with edx + 73/jump-if-greater-or-equal-unsigned $parse-array-of-ints:end/disp8 + # slice->end = skip-chars-not-matching-in-slice(slice->start, end, ' ') + # . eax = skip-chars-not-matching-in-slice(curr, end, ' ') + # . . push args + 68/push 0x20/imm32/space + 52/push-edx + 50/push-eax + # . . call + e8/call skip-chars-not-matching-in-slice/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . slice->end = eax + 89/copy 1/mod/direct 1/rm32/ecx . . . 0/r32/eax 4/disp8 . # copy eax to *(ecx+4) + # *out = parse-hex-int(slice) + # . eax = parse-hex-int(slice) + # . . push args + 51/push-ecx + # . . call + e8/call parse-hex-int/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp + # *out = eax + 89/copy 0/mod/indirect 3/rm32/ebx . . . 0/r32/eax . . # copy eax to *ebx + # out += 4 + 81 0/subop/add 3/mod/direct 3/rm32/ebx . . . . . 4/imm32 # add to ebx + # slice->start = slice->end + 8b/copy 1/mod/direct 1/rm32/ecx . . . 0/r32/eax 4/disp8 . # copy *(ecx+4) to eax + 89/copy 0/mod/indirect 1/rm32/ecx . . . 0/r32/eax . . # copy eax to *ecx + 81 0/subop/add 3/mod/direct 1/rm32/ecx . . . . . 4/imm32 # add to ecx + eb/jump $parse-array-of-ints:loop2/disp8 +$parse-array-of-ints:end: + # return edi + 89/copy 3/mod/direct 0/rm32/eax . . . 7/r32/edi . . # copy edi to eax + # . reclaim locals + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . restore registers + 5f/pop-to-edi + 5e/pop-to-esi + 5b/pop-to-ebx + 5a/pop-to-edx + 59/pop-to-ecx + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-parse-array-of-ints: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # var ecx = [1, 2, 3] + 68/push 3/imm32 + 68/push 2/imm32 + 68/push 1/imm32 + 68/push 0xc/imm32/size + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # eax = parse-array-of-ints(Heap, "1 2 3") + # . . push args + 68/push "1 2 3"/imm32 + 68/push Heap/imm32 + # . . call + e8/call parse-array-of-ints/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # eax = array-equal?(ecx, eax) + # . . push args + 50/push-eax + 51/push-ecx + # . . call + e8/call array-equal?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-ints-equal(eax, 1, msg) + # . . push args + 68/push "F - test-parse-array-of-ints"/imm32 + 68/push 1/imm32/true + 50/push-eax + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-parse-array-of-ints-empty: + # - empty string = empty array + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # eax = parse-array-of-ints(Heap, "") + # . . push args + 68/push ""/imm32 + 68/push Heap/imm32 + # . . call + e8/call parse-array-of-ints/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-ints-equal(*eax, 0, msg) + # . . push args + 68/push "F - test-parse-array-of-ints-empty"/imm32 + 68/push 0/imm32/size + ff 6/subop/push 0/mod/indirect 0/rm32/eax . . . . . . # 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 + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-parse-array-of-ints-just-whitespace: + # - just whitespace = empty array + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # eax = parse-array-of-ints(Heap, " ") + # . . push args + 68/push Space/imm32 + 68/push Heap/imm32 + # . . call + e8/call parse-array-of-ints/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-ints-equal(*eax, 0, msg) + # . . push args + 68/push "F - test-parse-array-of-ints-empty"/imm32 + 68/push 0/imm32/size + ff 6/subop/push 0/mod/indirect 0/rm32/eax . . . . . . # 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 + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +test-parse-array-of-ints-extra-whitespace: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # var ecx = [1, 2, 3] + 68/push 3/imm32 + 68/push 2/imm32 + 68/push 1/imm32 + 68/push 0xc/imm32/size + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # eax = parse-array-of-ints(Heap, " 1 2 3 ") + # . . push args + 68/push " 1 2 3 "/imm32 + 68/push Heap/imm32 + # . . call + e8/call parse-array-of-ints/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # eax = array-equal?(ecx, eax) + # . . push args + 50/push-eax + 51/push-ecx + # . . call + e8/call array-equal?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-ints-equal(eax, 1, msg) + # . . push args + 68/push "F - test-parse-array-of-ints-extra-whitespace"/imm32 + 68/push 1/imm32/true + 50/push-eax + # . . call + e8/call check-ints-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +# helper for later tests +# compare an array with a string representation of an array literal +check-array-equal: # a : (address array int), expected : (address string), msg : (address string) + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # . save registers + 50/push-eax + # var b/ecx = parse-array-of-ints(Heap, expected) + # . eax = parse-array-of-ints(Heap, expected) + # . . push args + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) + 68/push Heap/imm32 + # . . call + e8/call parse-array-of-ints/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . b = eax + 89/copy 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # copy eax to ecx + # eax = array-equal?(a, b) + # . . push args + 51/push-ecx + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) + # . . call + e8/call array-equal?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # check-ints-equal(eax, 1, msg) + # . . push args + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16) + 68/push 1/imm32 + 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 +$check-array-equal: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-check-array-equal: + # . prolog + 55/push-ebp + 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + # var ecx = [1, 2, 3] + 68/push 3/imm32 + 68/push 2/imm32 + 68/push 1/imm32 + 68/push 0xc/imm32/size + 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx + # check-array-equal(ecx, "1 2 3", "msg") + # . . push args + 68/push "F - test-check-array-equal"/imm32 + 68/push "1 2 3"/imm32 + 51/push-ecx + # . . call + e8/call check-array-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . epilog + 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 5d/pop-to-ebp + c3/return + +# . . vim:nowrap:textwidth=0 diff --git a/Readme.md b/Readme.md index 733e1bb8..d4eefbcd 100644 --- a/Readme.md +++ b/Readme.md @@ -104,12 +104,12 @@ You can use SubX to translate itself. For example, running natively on Linux: ```sh # generate translator phases using the C++ translator - $ ./subx translate init.linux 0[0-6]*.subx 070---hex.subx -o hex - $ ./subx translate init.linux 0*.subx apps/subx-common.subx apps/survey.subx -o survey - $ ./subx translate init.linux 0*.subx apps/subx-common.subx apps/pack.subx -o pack - $ ./subx translate init.linux 0*.subx apps/subx-common.subx apps/assort.subx -o assort - $ ./subx translate init.linux 0*.subx apps/subx-common.subx apps/dquotes.subx -o dquotes - $ ./subx translate init.linux 0*.subx apps/subx-common.subx apps/tests.subx -o tests + $ ./subx translate init.linux 0*.subx apps/subx-params.subx apps/hex.subx -o hex + $ ./subx translate init.linux 0*.subx apps/subx-params.subx apps/survey.subx -o survey + $ ./subx translate init.linux 0*.subx apps/subx-params.subx apps/pack.subx -o pack + $ ./subx translate init.linux 0*.subx apps/subx-params.subx apps/assort.subx -o assort + $ ./subx translate init.linux 0*.subx apps/subx-params.subx apps/dquotes.subx -o dquotes + $ ./subx translate init.linux 0*.subx apps/subx-params.subx apps/tests.subx -o tests $ chmod +x hex survey pack assort dquotes tests # use the generated translator phases to translate SubX programs diff --git a/apps/assort b/apps/assort index bdd302c3..0f876c2d 100755 Binary files a/apps/assort and b/apps/assort differ diff --git a/apps/assort.subx b/apps/assort.subx index 2e9fee47..6b3eadba 100644 --- a/apps/assort.subx +++ b/apps/assort.subx @@ -7,7 +7,7 @@ # because we don't know if they refer to the line above or the line below. # # To run: -# $ ./subx translate init.linux 0*.subx apps/subx-common.subx apps/assort.subx -o apps/assort +# $ ./subx translate init.linux 0*.subx apps/subx-params.subx apps/assort.subx -o apps/assort # $ cat x # == code # abc @@ -38,10 +38,10 @@ Entry: # run tests if necessary, convert stdin if not 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 + # if (argc <= 1) goto interactive 81 7/subop/compare 1/mod/*+disp8 5/rm32/ebp . . . . 0/disp8 1/imm32 # compare *ebp - 7e/jump-if-lesser-or-equal $run-main/disp8 - # if (!kernel-string-equal?(argv[1], "test")) goto run-main + 7e/jump-if-lesser-or-equal $subx-assort-main:interactive/disp8 + # if (!kernel-string-equal?(argv[1], "test")) goto interactive # . eax = kernel-string-equal?(argv[1], "test") # . . push args 68/push "test"/imm32 @@ -50,15 +50,15 @@ Entry: # run tests if necessary, convert stdin if not 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 + # . if (eax == 0) goto interactive 3d/compare-eax-and 0/imm32 - 74/jump-if-equal $run-main/disp8 + 74/jump-if-equal $subx-assort-main:interactive/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: + eb/jump $subx-assort-main:end/disp8 +$subx-assort-main:interactive: # - otherwise convert stdin # var ed/eax : exit-descriptor 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # subtract from esp @@ -66,19 +66,19 @@ $run-main: # configure ed to really exit() # . ed->target = 0 c7 0/subop/copy 0/mod/direct 0/rm32/eax . . . . . 0/imm32 # copy to *eax - # convert(Stdin, Stdout, Stderr, ed) + # subx-assort(Stdin, Stdout, Stderr, ed) # . . push args 50/push-eax/ed 68/push Stderr/imm32 68/push Stdout/imm32 68/push Stdin/imm32 # . . call - e8/call convert/disp32 + e8/call subx-assort/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: +$subx-assort-main:end: b8/copy-to-eax 1/imm32/exit cd/syscall 0x80/imm8 @@ -86,7 +86,7 @@ $main:end: # table: (address stream {string, (address stream byte)}) (8 bytes per row) # inefficient; uses sequential search for looking up segments by name -convert: # in : (address buffered-file), out : (address buffered-file) -> +subx-assort: # in : (address buffered-file), out : (address buffered-file) -> # pseudocode: # var table : (address stream) = new-stream(10 rows, 8 bytes each) # read-segments(in, table) @@ -110,7 +110,7 @@ convert: # in : (address buffered-file), out : (address buffered-file) -> 1 and argv[1] == "test", then return run_tests() - # if (argc <= 1) goto run-main + # - if argc > 1 and argv[1] == "test", then return run-tests() + # if (argc <= 1) goto interactive 81 7/subop/compare 1/mod/*+disp8 5/rm32/ebp . . . . 0/disp8 1/imm32 # compare *ebp - 7e/jump-if-lesser-or-equal $run-main/disp8 - # if (!kernel-string-equal?(argv[1], "test")) goto run-main + 7e/jump-if-lesser-or-equal $subx-dquotes-main:interactive/disp8 + # if (!kernel-string-equal?(argv[1], "test")) goto interactive # . eax = kernel-string-equal?(argv[1], "test") # . . push args 68/push "test"/imm32 @@ -46,15 +46,15 @@ Entry: # run tests if necessary, convert stdin if not 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 + # . if (eax == 0) goto interactive 3d/compare-eax-and 0/imm32 - 74/jump-if-equal $run-main/disp8 + 74/jump-if-equal $subx-dquotes-main:interactive/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: + eb/jump $subx-dquotes-main:end/disp8 +$subx-dquotes-main:interactive: # - otherwise convert stdin # var ed/eax : exit-descriptor 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # subtract from esp @@ -62,19 +62,19 @@ $run-main: # configure ed to really exit() # . ed->target = 0 c7 0/subop/copy 0/mod/direct 0/rm32/eax . . . . . 0/imm32 # copy to *eax - # convert(Stdin, 1/stdout, 2/stderr, ed) + # subx-dquotes(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/disp32 + e8/call subx-dquotes/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: +$subx-dquotes-main:end: b8/copy-to-eax 1/imm32/exit cd/syscall 0x80/imm8 @@ -82,7 +82,7 @@ $main:end: # line = words separated by ' ', maybe followed by comment starting with '#' # word = datum until '/', then 0 or more metadata separated by '/' -convert: # in : (address buffered-file), out : (address buffered-file) -> +subx-dquotes: # in : (address buffered-file), out : (address buffered-file) -> # pseudocode: # var line = new-stream(512, 1) # var new-data-segment = new-stream(Heap, Segment-size, 1) @@ -146,7 +146,7 @@ convert: # in : (address buffered-file), out : (address buffered-file) -> write == 0) break 81 7/subop/compare 0/mod/indirect 1/rm32/ecx . . . . . 0/imm32 # compare *ecx - 0f 84/jump-if-equal $convert:break/disp32 -$convert:word-loop: + 0f 84/jump-if-equal $subx-dquotes:break/disp32 +$subx-dquotes:word-loop: # next-word-or-string(line, word-slice) # . . push args 52/push-edx @@ -175,7 +175,7 @@ $convert:word-loop: e8/call next-word-or-string/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -$convert:check1: +$subx-dquotes:check1: # if (slice-empty?(word-slice)) break # . eax = slice-empty?(word-slice) # . . push args @@ -186,8 +186,8 @@ $convert:check1: 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # . if (eax != 0) break 3d/compare-eax-and 0/imm32 - 0f 85/jump-if-not-equal $convert:next-line/disp32 -$convert:check-for-comment: + 0f 85/jump-if-not-equal $subx-dquotes:next-line/disp32 +$subx-dquotes:check-for-comment: # if (slice-starts-with?(word-slice, "#")) continue # . start/esi = word-slice->start 8b/copy 0/mod/indirect 2/rm32/edx . . . 6/r32/esi . . # copy *edx to esi @@ -196,12 +196,12 @@ $convert:check-for-comment: 8a/copy-byte 0/mod/indirect 6/rm32/esi . . . 0/r32/AL . . # copy byte at *esi to AL # . if (eax == '#') continue 3d/compare-eax-and 0x23/imm32/hash - 74/jump-if-equal $convert:word-loop/disp8 -$convert:check-for-string-literal: + 74/jump-if-equal $subx-dquotes:word-loop/disp8 +$subx-dquotes:check-for-string-literal: # if (slice-starts-with?(word-slice, '"')) continue 3d/compare-eax-and 0x22/imm32/dquote - 75/jump-if-not-equal $convert:regular-word/disp8 -$convert:string-literal: + 75/jump-if-not-equal $subx-dquotes:regular-word/disp8 +$subx-dquotes:string-literal: # process-string-literal(word-slice, out, new-data-segment) # . . push args 57/push-edi @@ -212,8 +212,8 @@ $convert:string-literal: # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp # continue - eb/jump $convert:next-word/disp8 -$convert:regular-word: + eb/jump $subx-dquotes:next-word/disp8 +$subx-dquotes:regular-word: # write-slice-buffered(out, word-slice) # . . push args 52/push-edx @@ -223,7 +223,7 @@ $convert:regular-word: # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # fall through -$convert:next-word: +$subx-dquotes:next-word: # write-buffered(out, " ") # . . push args 68/push Space/imm32 @@ -233,8 +233,8 @@ $convert:next-word: # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # loop - eb/jump $convert:word-loop/disp8 -$convert:next-line: + eb/jump $subx-dquotes:word-loop/disp8 +$subx-dquotes:next-line: # write-buffered(out, "\n") # . . push args 68/push Newline/imm32 @@ -244,8 +244,8 @@ $convert:next-line: # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # loop - e9/jump $convert:line-loop/disp32 -$convert:break: + e9/jump $subx-dquotes:line-loop/disp32 +$subx-dquotes:break: # write-stream-data(out, new-data-segment) # . . push args 57/push-edi @@ -261,7 +261,7 @@ $convert:break: e8/call flush/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp -$convert:end: +$subx-dquotes:end: # . reclaim locals 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x214/imm32 # add to esp # . restore registers @@ -383,7 +383,7 @@ $process-string-literal:end: 5d/pop-to-ebp c3/return -test-convert-is-idempotent-by-default: +test-subx-dquotes-is-idempotent-by-default: # . prolog 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp @@ -502,12 +502,12 @@ test-convert-is-idempotent-by-default: e8/call write/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # convert(_test-input-buffered-file, _test-output-buffered-file) + # subx-dquotes(_test-input-buffered-file, _test-output-buffered-file) # . . push args 68/push _test-output-buffered-file/imm32 68/push _test-input-buffered-file/imm32 # . . call - e8/call convert/disp32 + e8/call subx-dquotes/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . flush(_test-output-buffered-file) @@ -556,7 +556,7 @@ test-convert-is-idempotent-by-default: #? # }}} # . check-next-stream-line-equal(_test-output-stream, "", msg) # . . push args - 68/push "F - test-convert-is-idempotent-by-default/0"/imm32 + 68/push "F - test-subx-dquotes-is-idempotent-by-default/0"/imm32 68/push ""/imm32 68/push _test-output-stream/imm32 # . . call @@ -565,7 +565,7 @@ test-convert-is-idempotent-by-default: 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp # . check-next-stream-line-equal(_test-output-stream, "", msg) # . . push args - 68/push "F - test-convert-is-idempotent-by-default/1"/imm32 + 68/push "F - test-subx-dquotes-is-idempotent-by-default/1"/imm32 68/push ""/imm32 68/push _test-output-stream/imm32 # . . call @@ -574,7 +574,7 @@ test-convert-is-idempotent-by-default: 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp # . check-next-stream-line-equal(_test-output-stream, "== code 0x1 ", msg) # . . push args - 68/push "F - test-convert-is-idempotent-by-default/2"/imm32 + 68/push "F - test-subx-dquotes-is-idempotent-by-default/2"/imm32 68/push "== code 0x1 "/imm32 68/push _test-output-stream/imm32 # . . call @@ -583,7 +583,7 @@ test-convert-is-idempotent-by-default: 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp # . check-next-stream-line-equal(_test-output-stream, "", msg) # . . push args - 68/push "F - test-convert-is-idempotent-by-default/3"/imm32 + 68/push "F - test-subx-dquotes-is-idempotent-by-default/3"/imm32 68/push ""/imm32 68/push _test-output-stream/imm32 # . . call @@ -592,7 +592,7 @@ test-convert-is-idempotent-by-default: 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp # . check-next-stream-line-equal(_test-output-stream, "1 ", msg) # . . push args - 68/push "F - test-convert-is-idempotent-by-default/4"/imm32 + 68/push "F - test-subx-dquotes-is-idempotent-by-default/4"/imm32 68/push "1 "/imm32 68/push _test-output-stream/imm32 # . . call @@ -601,7 +601,7 @@ test-convert-is-idempotent-by-default: 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp # . check-next-stream-line-equal(_test-output-stream, "", msg) # . . push args - 68/push "F - test-convert-is-idempotent-by-default/5"/imm32 + 68/push "F - test-subx-dquotes-is-idempotent-by-default/5"/imm32 68/push ""/imm32 68/push _test-output-stream/imm32 # . . call @@ -610,7 +610,7 @@ test-convert-is-idempotent-by-default: 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp # . check-next-stream-line-equal(_test-output-stream, "2 3 ", msg) # . . push args - 68/push "F - test-convert-is-idempotent-by-default/6"/imm32 + 68/push "F - test-subx-dquotes-is-idempotent-by-default/6"/imm32 68/push "2 3 "/imm32 68/push _test-output-stream/imm32 # . . call @@ -619,7 +619,7 @@ test-convert-is-idempotent-by-default: 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp # . check-next-stream-line-equal(_test-output-stream, "== data 0x2 ", msg) # . . push args - 68/push "F - test-convert-is-idempotent-by-default/7"/imm32 + 68/push "F - test-subx-dquotes-is-idempotent-by-default/7"/imm32 68/push "== data 0x2 "/imm32 68/push _test-output-stream/imm32 # . . call @@ -628,7 +628,7 @@ test-convert-is-idempotent-by-default: 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp # . check-next-stream-line-equal(_test-output-stream, "4 5/imm32 ", msg) # . . push args - 68/push "F - test-convert-is-idempotent-by-default/8"/imm32 + 68/push "F - test-subx-dquotes-is-idempotent-by-default/8"/imm32 68/push "4 5/imm32 "/imm32 68/push _test-output-stream/imm32 # . . call @@ -640,7 +640,7 @@ test-convert-is-idempotent-by-default: 5d/pop-to-ebp c3/return -test-convert-processes-string-literals: +test-subx-dquotes-processes-string-literals: # . prolog 55/push-ebp 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp @@ -703,12 +703,12 @@ test-convert-processes-string-literals: e8/call write/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # convert(_test-input-buffered-file, _test-output-buffered-file) + # subx-dquotes(_test-input-buffered-file, _test-output-buffered-file) # . . push args 68/push _test-output-buffered-file/imm32 68/push _test-input-buffered-file/imm32 # . . call - e8/call convert/disp32 + e8/call subx-dquotes/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . flush(_test-output-buffered-file) @@ -769,7 +769,7 @@ test-convert-processes-string-literals: #? # }}} # . check-next-stream-line-equal(_test-output-stream, "== code 0x1 ", msg) # . . push args - 68/push "F - test-convert-processes-string-literals/0"/imm32 + 68/push "F - test-subx-dquotes-processes-string-literals/0"/imm32 68/push "== code 0x1 "/imm32 68/push _test-output-stream/imm32 # . . call @@ -778,7 +778,7 @@ test-convert-processes-string-literals: 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp # . check-next-stream-line-equal(_test-output-stream, "1 _string1/x ", msg) # . . push args - 68/push "F - test-convert-processes-string-literals/1"/imm32 + 68/push "F - test-subx-dquotes-processes-string-literals/1"/imm32 68/push "1 _string1/x "/imm32 68/push _test-output-stream/imm32 # . . call @@ -787,7 +787,7 @@ test-convert-processes-string-literals: 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp # . check-next-stream-line-equal(_test-output-stream, "2 _string2/y ", msg) # . . push args - 68/push "F - test-convert-processes-string-literals/2"/imm32 + 68/push "F - test-subx-dquotes-processes-string-literals/2"/imm32 68/push "2 _string2/y "/imm32 68/push _test-output-stream/imm32 # . . call @@ -796,7 +796,7 @@ test-convert-processes-string-literals: 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp # . check-next-stream-line-equal(_test-output-stream, "== data", msg) # . . push args - 68/push "F - test-convert-processes-string-literals/3"/imm32 + 68/push "F - test-subx-dquotes-processes-string-literals/3"/imm32 68/push "== data"/imm32 68/push _test-output-stream/imm32 # . . call @@ -805,7 +805,7 @@ test-convert-processes-string-literals: 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp # . check-next-stream-line-equal(_test-output-stream, "_string1: ", msg) # . . push args - 68/push "F - test-convert-processes-string-literals/4"/imm32 + 68/push "F - test-subx-dquotes-processes-string-literals/4"/imm32 68/push "_string1:"/imm32 68/push _test-output-stream/imm32 # . . call @@ -814,7 +814,7 @@ test-convert-processes-string-literals: 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp # . check-next-stream-line-equal(_test-output-stream, "1/imm32 61/a ", msg) # . . push args - 68/push "F - test-convert-processes-string-literals/5"/imm32 + 68/push "F - test-subx-dquotes-processes-string-literals/5"/imm32 68/push "0x00000001/imm32 61/a "/imm32 68/push _test-output-stream/imm32 # . . call @@ -823,7 +823,7 @@ test-convert-processes-string-literals: 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp # . check-next-stream-line-equal(_test-output-stream, "_string2: ", msg) # . . push args - 68/push "F - test-convert-processes-string-literals/6"/imm32 + 68/push "F - test-subx-dquotes-processes-string-literals/6"/imm32 68/push "_string2:"/imm32 68/push _test-output-stream/imm32 # . . call @@ -832,7 +832,7 @@ test-convert-processes-string-literals: 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp # . check-next-stream-line-equal(_test-output-stream, "2/imm32 62/b 63/c ", msg) # . . push args - 68/push "F - test-convert-processes-string-literals/7"/imm32 + 68/push "F - test-subx-dquotes-processes-string-literals/7"/imm32 68/push "0x00000002/imm32 62/b 63/c "/imm32 68/push _test-output-stream/imm32 # . . call diff --git a/apps/factorial b/apps/factorial index 2641faa9..07792615 100755 Binary files a/apps/factorial and b/apps/factorial differ diff --git a/apps/handle b/apps/handle index 21e66ccb..237003dc 100755 Binary files a/apps/handle and b/apps/handle differ diff --git a/apps/hex b/apps/hex index 9dd432b7..64218e10 100755 Binary files a/apps/hex and b/apps/hex differ diff --git a/apps/hex.subx b/apps/hex.subx new file mode 100644 index 00000000..e5f13077 --- /dev/null +++ b/apps/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-params.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(Heap-size) + # . . push args + 68/push Heap/imm32 + 68/push Heap-size/imm32 + # . . 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 interactive + 81 7/subop/compare 1/mod/*+disp8 5/rm32/ebp . . . . 0/disp8 1/imm32 # compare *ebp + 7e/jump-if-lesser-or-equal $subx-hex-main:interactive/disp8 + # if (!kernel-string-equal?(argv[1], "test")) goto interactive + # . eax = kernel-string-equal?(argv[1], "test") + # . . push args + 68/push "test"/imm32 + ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) + # . . call + e8/call kernel-string-equal?/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + # . if (eax == 0) goto interactive + 3d/compare-eax-and 0/imm32 + 74/jump-if-equal $subx-hex-main:interactive/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 $subx-hex-main:end/disp8 +$subx-hex-main:interactive: + # - 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 + # subx-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 subx-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 +$subx-hex-main:end: + b8/copy-to-eax 1/imm32/exit + cd/syscall 0x80/imm8 + +# the main entry point +subx-hex: # in : (address buffered-file), out : (address buffered-file), err : (address buffered-file), ed : (address exit-descriptor) -> + # 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 +$subx-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 $subx-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 $subx-hex:loop/disp8 +$subx-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 +$subx-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) -> + # 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 diff --git a/apps/mulisp.subx b/apps/mulisp.subx index 4ae26e82..9bc2d0b7 100644 --- a/apps/mulisp.subx +++ b/apps/mulisp.subx @@ -1,7 +1,7 @@ # Toy lisp interpreter # # To run: -# $ ./ntranslate init.linux 0*.subx apps/subx-common.subx apps/mulisp.subx +# $ ./ntranslate init.linux 0*.subx apps/subx-params.subx apps/mulisp.subx # $ ./a.elf # 42 # => 42 diff --git a/apps/pack b/apps/pack index f9907948..95782b8b 100755 Binary files a/apps/pack and b/apps/pack differ diff --git a/apps/pack.subx b/apps/pack.subx index 69c37bd4..1be0c4c4 100644 --- a/apps/pack.subx +++ b/apps/pack.subx @@ -3,7 +3,7 @@ # uses are left untouched. # # To run: -# $ ./subx translate init.linux 0*.subx apps/subx-common.subx apps/pack.subx -o apps/pack +# $ ./subx translate init.linux 0*.subx apps/subx-params.subx apps/pack.subx -o apps/pack # $ echo '05/add-to-eax 0x20/imm32' |./subx run apps/pack # Expected output: # 05 20 00 00 00 # 05/add-to-eax 0x20/imm32 @@ -33,10 +33,10 @@ Entry: # run tests if necessary, convert stdin if not 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 + # if (argc <= 1) goto interactive 81 7/subop/compare 1/mod/*+disp8 5/rm32/ebp . . . . 0/disp8 1/imm32 # compare *ebp - 7e/jump-if-lesser-or-equal $run-main/disp8 - # if (!kernel-string-equal?(argv[1], "test")) goto run-main + 7e/jump-if-lesser-or-equal $subx-pack-main:interactive/disp8 + # if (!kernel-string-equal?(argv[1], "test")) goto interactive # . eax = kernel-string-equal?(argv[1], "test") # . . push args 68/push "test"/imm32 @@ -45,15 +45,15 @@ Entry: # run tests if necessary, convert stdin if not 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 + # . if (eax == 0) goto interactive 3d/compare-eax-and 0/imm32 - 74/jump-if-equal $run-main/disp8 + 74/jump-if-equal $subx-pack-main:interactive/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: + eb/jump $subx-pack-main:end/disp8 +$subx-pack-main:interactive: # - otherwise convert stdin # var ed/eax : exit-descriptor 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # subtract from esp @@ -61,19 +61,19 @@ $run-main: # configure ed to really exit() # . ed->target = 0 c7 0/subop/copy 0/mod/direct 0/rm32/eax . . . . . 0/imm32 # copy to *eax - # convert(Stdin, Stdout, Stderr, ed) + # subx-pack(Stdin, Stdout, Stderr, ed) # . . push args 50/push-eax/ed 68/push Stderr/imm32 68/push Stdout/imm32 68/push Stdin/imm32 # . . call - e8/call convert/disp32 + e8/call subx-pack/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: +$subx-pack-main:end: b8/copy-to-eax 1/imm32/exit cd/syscall 0x80/imm8 @@ -97,7 +97,7 @@ $main:end: # next-token-from-slice(start, end, delim char) -> slice # slice-equal?(slice, string) -convert: # in : (address buffered-file), out : (address buffered-file) -> +subx-pack: # in : (address buffered-file), out : (address buffered-file) -> # pseudocode: # var line = new-stream(512, 1) # var in-code? = false @@ -140,7 +140,7 @@ convert: # in : (address buffered-file), out : (address buffered-file) -> write == 0) break 81 7/subop/compare 0/mod/indirect 1/rm32/ecx . . . . . 0/imm32 # compare *ecx - 0f 84/jump-if-equal $convert:break/disp32 + 0f 84/jump-if-equal $subx-pack:break/disp32 #? # dump line {{{ #? # . write(2/stderr, "LL: ") #? # . . push args @@ -194,7 +194,7 @@ $convert:check0: e8/call next-word/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -$convert:check1: +$subx-pack:check1: # if (slice-empty?(word-slice)) write-stream-data(out, line) # . eax = slice-empty?(word-slice) # . . push args @@ -205,8 +205,8 @@ $convert:check1: 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # . if (eax != 0) write-stream-data(out, line) 3d/compare-eax-and 0/imm32 - 0f 85/jump-if-not-equal $convert:pass-through/disp32 -$convert:check2: + 0f 85/jump-if-not-equal $subx-pack:pass-through/disp32 +$subx-pack:check2: #? # dump word-slice {{{ #? # . write(2/stderr, "AA: ") #? # . . push args @@ -260,7 +260,7 @@ $convert:check2: 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . if (eax == 0) goto check3 3d/compare-eax-and 0/imm32 - 0f 84/jump-if-equal $convert:check3/disp32 + 0f 84/jump-if-equal $subx-pack:check3/disp32 # word-slice = next-word(line) # . . push args 52/push-edx @@ -322,8 +322,8 @@ $convert:check2: # . . in-code? = eax 89/copy 3/mod/direct 3/rm32/ebx . . . 0/r32/eax . . # copy eax to ebx # write-stream-data(out, line) - eb/jump $convert:pass-through/disp8 -$convert:check3: + eb/jump $subx-pack:pass-through/disp8 +$subx-pack:check3: # else rewind-stream(line) # . rewind-stream(line) # . . push args @@ -334,8 +334,8 @@ $convert:check3: 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # if (in-code? != 0) convert-instruction(line, out) 81 7/subop/compare 3/mod/direct 3/rm32/ebx . . . . . 0/imm32 # compare ebx - 74/jump-if-equal $convert:data/disp8 -$convert:code: + 74/jump-if-equal $subx-pack:data/disp8 +$subx-pack:code: # . convert-instruction(line, out) # . . push args ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) @@ -345,8 +345,8 @@ $convert:code: # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . loop - e9/jump $convert:loop/disp32 -$convert:data: + e9/jump $subx-pack:loop/disp32 +$subx-pack:data: # else convert-data(line, out) # . . push args ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) @@ -356,8 +356,8 @@ $convert:data: # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . loop - e9/jump $convert:loop/disp32 -$convert:pass-through: + e9/jump $subx-pack:loop/disp32 +$subx-pack:pass-through: # write-stream-data(out, line) # . . push args 51/push-ecx @@ -367,8 +367,8 @@ $convert:pass-through: # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . loop - e9/jump $convert:loop/disp32 -$convert:break: + e9/jump $subx-pack:loop/disp32 +$subx-pack:break: # flush(out) # . . push args ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) @@ -376,7 +376,7 @@ $convert:break: e8/call flush/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp -$convert:end: +$subx-pack:end: # . reclaim locals 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x214/imm32 # add to esp # . restore registers @@ -389,7 +389,7 @@ $convert:end: 5d/pop-to-ebp c3/return -test-convert-passes-empty-lines-through: +test-subx-pack-passes-empty-lines-through: # if a line is empty, pass it along unchanged # . prolog 55/push-ebp @@ -428,12 +428,12 @@ test-convert-passes-empty-lines-through: # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # write nothing to input - # convert(_test-input-buffered-file, _test-output-buffered-file) + # subx-pack(_test-input-buffered-file, _test-output-buffered-file) # . . push args 68/push _test-output-buffered-file/imm32 68/push _test-input-buffered-file/imm32 # . . call - e8/call convert/disp32 + e8/call subx-pack/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # check that the line just passed through @@ -446,7 +446,7 @@ test-convert-passes-empty-lines-through: 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # . check-stream-equal(_test-output-stream, "", msg) # . . push args - 68/push "F - test-convert-passes-empty-lines-through"/imm32 + 68/push "F - test-subx-pack-passes-empty-lines-through"/imm32 68/push ""/imm32 68/push _test-output-stream/imm32 # . . call @@ -458,7 +458,7 @@ test-convert-passes-empty-lines-through: 5d/pop-to-ebp c3/return -test-convert-passes-lines-with-just-whitespace-through: +test-subx-pack-passes-lines-with-just-whitespace-through: # if a line is empty, pass it along unchanged # . prolog 55/push-ebp @@ -505,12 +505,12 @@ test-convert-passes-lines-with-just-whitespace-through: e8/call write/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # convert(_test-input-buffered-file, _test-output-buffered-file) + # subx-pack(_test-input-buffered-file, _test-output-buffered-file) # . . push args 68/push _test-output-buffered-file/imm32 68/push _test-input-buffered-file/imm32 # . . call - e8/call convert/disp32 + e8/call subx-pack/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # check that the line just passed through @@ -523,7 +523,7 @@ test-convert-passes-lines-with-just-whitespace-through: 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # . check-next-stream-line-equal(_test-output-stream, " ", msg) # . . push args - 68/push "F - test-convert-passes-with-just-whitespace-through"/imm32 + 68/push "F - test-subx-pack-passes-with-just-whitespace-through"/imm32 68/push " "/imm32 68/push _test-output-stream/imm32 # . . call @@ -535,7 +535,7 @@ test-convert-passes-lines-with-just-whitespace-through: 5d/pop-to-ebp c3/return -test-convert-passes-segment-headers-through: +test-subx-pack-passes-segment-headers-through: # if a line starts with '==', pass it along unchanged # . prolog 55/push-ebp @@ -582,12 +582,12 @@ test-convert-passes-segment-headers-through: e8/call write/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # convert(_test-input-buffered-file, _test-output-buffered-file) + # subx-pack(_test-input-buffered-file, _test-output-buffered-file) # . . push args 68/push _test-output-buffered-file/imm32 68/push _test-input-buffered-file/imm32 # . . call - e8/call convert/disp32 + e8/call subx-pack/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # check that the line just passed through @@ -600,7 +600,7 @@ test-convert-passes-segment-headers-through: 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # . check-stream-equal(_test-output-stream, "== abcd 0x1", msg) # . . push args - 68/push "F - test-convert-passes-segment-headers-through"/imm32 + 68/push "F - test-subx-pack-passes-segment-headers-through"/imm32 68/push "== abcd 0x1"/imm32 68/push _test-output-stream/imm32 # . . call @@ -612,7 +612,7 @@ test-convert-passes-segment-headers-through: 5d/pop-to-ebp c3/return -test-convert-in-data-segment: +test-subx-pack-in-data-segment: # correctly process lines in the data segment # . prolog 55/push-ebp @@ -678,12 +678,12 @@ test-convert-in-data-segment: e8/call write/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # convert(_test-input-buffered-file, _test-output-buffered-file) + # subx-pack(_test-input-buffered-file, _test-output-buffered-file) # . . push args 68/push _test-output-buffered-file/imm32 68/push _test-input-buffered-file/imm32 # . . call - e8/call convert/disp32 + e8/call subx-pack/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # check output @@ -722,7 +722,7 @@ test-convert-in-data-segment: 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # . check-next-stream-line-equal(_test-output-stream, "== code 0x1", msg) # . . push args - 68/push "F - test-convert-in-data-segment/0"/imm32 + 68/push "F - test-subx-pack-in-data-segment/0"/imm32 68/push "== code 0x1"/imm32 68/push _test-output-stream/imm32 # . . call @@ -731,7 +731,7 @@ test-convert-in-data-segment: 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp # . check-next-stream-line-equal(_test-output-stream, "== data 0x2", msg) # . . push args - 68/push "F - test-convert-in-data-segment/1"/imm32 + 68/push "F - test-subx-pack-in-data-segment/1"/imm32 68/push "== data 0x2"/imm32 68/push _test-output-stream/imm32 # . . call @@ -740,7 +740,7 @@ test-convert-in-data-segment: 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp # . check-next-stream-line-equal(_test-output-stream, "03 04 00 00 00 ", msg) # . . push args - 68/push "F - test-convert-in-data-segment/2"/imm32 + 68/push "F - test-subx-pack-in-data-segment/2"/imm32 68/push "03 04 00 00 00 "/imm32 68/push _test-output-stream/imm32 # . . call @@ -752,7 +752,7 @@ test-convert-in-data-segment: 5d/pop-to-ebp c3/return -test-convert-code-and-data-segments: +test-subx-pack-code-and-data-segments: # correctly process lines in both code and data segments # . prolog 55/push-ebp @@ -836,12 +836,12 @@ test-convert-code-and-data-segments: e8/call write/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # convert(_test-input-buffered-file, _test-output-buffered-file) + # subx-pack(_test-input-buffered-file, _test-output-buffered-file) # . . push args 68/push _test-output-buffered-file/imm32 68/push _test-input-buffered-file/imm32 # . . call - e8/call convert/disp32 + e8/call subx-pack/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # check output @@ -885,7 +885,7 @@ test-convert-code-and-data-segments: 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # . check-next-stream-line-equal(_test-output-stream, "== code 0x1", msg) # . . push args - 68/push "F - test-convert-code-and-data-segments/0"/imm32 + 68/push "F - test-subx-pack-code-and-data-segments/0"/imm32 68/push "== code 0x1"/imm32 68/push _test-output-stream/imm32 # . . call @@ -894,7 +894,7 @@ test-convert-code-and-data-segments: 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp # . check-next-stream-line-equal(_test-output-stream, "e8 20 00 00 00 # e8/call 20/disp32", msg) # . . push args - 68/push "F - test-convert-code-and-data-segments/1"/imm32 + 68/push "F - test-subx-pack-code-and-data-segments/1"/imm32 68/push "e8 20 00 00 00 # e8/call 20/disp32"/imm32 68/push _test-output-stream/imm32 # . . call @@ -903,7 +903,7 @@ test-convert-code-and-data-segments: 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp # . check-next-stream-line-equal(_test-output-stream, "68 20 # 68/push 0x20/imm8", msg) # . . push args - 68/push "F - test-convert-code-and-data-segments/2"/imm32 + 68/push "F - test-subx-pack-code-and-data-segments/2"/imm32 68/push "68 20 # 68/push 0x20/imm8"/imm32 68/push _test-output-stream/imm32 # . . call @@ -912,7 +912,7 @@ test-convert-code-and-data-segments: 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp # . check-next-stream-line-equal(_test-output-stream, "== data 0x2", msg) # . . push args - 68/push "F - test-convert-code-and-data-segments/3"/imm32 + 68/push "F - test-subx-pack-code-and-data-segments/3"/imm32 68/push "== data 0x2"/imm32 68/push _test-output-stream/imm32 # . . call @@ -921,7 +921,7 @@ test-convert-code-and-data-segments: 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp # . check-next-stream-line-equal(_test-output-stream, "03 04 00 00 00 ", msg) # . . push args - 68/push "F - test-convert-code-and-data-segments/4"/imm32 + 68/push "F - test-subx-pack-code-and-data-segments/4"/imm32 68/push "03 04 00 00 00 "/imm32 68/push _test-output-stream/imm32 # . . call diff --git a/apps/sigils b/apps/sigils index fff9e7cd..c87a99f1 100755 Binary files a/apps/sigils and b/apps/sigils differ diff --git a/apps/sigils.subx b/apps/sigils.subx index 8c8beba7..f05bc6d4 100644 --- a/apps/sigils.subx +++ b/apps/sigils.subx @@ -2,7 +2,7 @@ # other related arguments. # # To run: -# $ ./subx translate init.linux 0*.subx apps/subx-common.subx apps/sigils.subx -o apps/sigils +# $ ./subx translate init.linux 0*.subx apps/subx-params.subx apps/sigils.subx -o apps/sigils # # We currently support the following notations: # diff --git a/apps/subx-common.subx b/apps/subx-common.subx deleted file mode 100644 index 829ed277..00000000 --- a/apps/subx-common.subx +++ /dev/null @@ -1,2084 +0,0 @@ -== 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 - -# (re)compute the bounds of the next word in the line -# return empty string on reaching end of file -next-word: # line : (address stream byte), out : (address slice) - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 50/push-eax - 51/push-ecx - 56/push-esi - 57/push-edi - # esi = line - 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 - # skip-chars-matching(line, ' ') - # . . push args - 68/push 0x20/imm32/space - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) - # . . call - e8/call skip-chars-matching/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -$next-word:check0: - # if (line->read >= line->write) clear out and return - # . eax = line->read - 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 0/r32/eax 4/disp8 . # copy *(esi+4) to eax - # . if (eax < line->write) goto next check - 3b/compare 0/mod/indirect 6/rm32/esi . . . 0/r32/eax . . # compare eax with *esi - 7c/jump-if-lesser $next-word:check-for-comment/disp8 - # . return out = {0, 0} - c7 0/subop/copy 0/mod/direct 7/rm32/edi . . . . . 0/imm32 # copy to *edi - c7 0/subop/copy 1/mod/*+disp8 7/rm32/edi . . . . 4/disp8 0/imm32 # copy to *(edi+4) - eb/jump $next-word:end/disp8 -$next-word:check-for-comment: - # out->start = &line->data[line->read] - 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy *(esi+4) to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/esi 1/index/ecx . 0/r32/eax 0xc/disp8 . # copy esi+ecx+12 to eax - 89/copy 0/mod/indirect 7/rm32/edi . . . 0/r32/eax . . # copy eax to *edi - # if (line->data[line->read] == '#') out->end = &line->data[line->write]), skip rest of stream and return - # . eax = line->data[line->read] - 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax - 8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/esi 1/index/ecx . 0/r32/AL 0xc/disp8 . # copy byte at *(esi+ecx+12) to AL - # . compare - 3d/compare-eax-and 0x23/imm32/pound - 75/jump-if-not-equal $next-word:regular-word/disp8 -$next-word:comment: - # . out->end = &line->data[line->write] - 8b/copy 0/mod/indirect 6/rm32/esi . . . 0/r32/eax . . # copy *esi to eax - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/esi 0/index/eax . 0/r32/eax 0xc/disp8 . # copy esi+eax+12 to eax - 89/copy 1/mod/*+disp8 7/rm32/edi . . . 0/r32/eax 4/disp8 . # copy eax to *(edi+4) - # . line->read = line->write - 89/copy 1/mod/*+disp8 6/rm32/esi . . . 0/r32/eax 4/disp8 . # copy eax to *(esi+4) - # . return - eb/jump $next-word:end/disp8 -$next-word:regular-word: - # otherwise skip-chars-not-matching-whitespace(line) # including trailing newline - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) - # . . call - e8/call skip-chars-not-matching-whitespace/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # out->end = &line->data[line->read] - 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 1/r32/ecx 4/disp8 . # copy *(esi+4) to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/esi 1/index/ecx . 0/r32/eax 0xc/disp8 . # copy esi+ecx+12 to eax - 89/copy 1/mod/*+disp8 7/rm32/edi . . . 0/r32/eax 4/disp8 . # copy eax to *(edi+4) -$next-word:end: - # . restore registers - 5f/pop-to-edi - 5e/pop-to-esi - 59/pop-to-ecx - 58/pop-to-eax - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-next-word: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # 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 - # var slice/ecx = {0, 0} - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # write(_test-stream, " ab") - # . . push args - 68/push " ab"/imm32 - 68/push _test-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # next-word(_test-stream, slice) - # . . push args - 51/push-ecx - 68/push _test-stream/imm32 - # . . call - e8/call next-word/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-ints-equal(slice->start - _test-stream->data, 2, msg) - # . check-ints-equal(slice->start - _test-stream, 14, msg) - # . . push args - 68/push "F - test-next-word: start"/imm32 - 68/push 0xe/imm32 - # . . push slice->start - _test-stream - 8b/copy 0/mod/indirect 1/rm32/ecx . . . 0/r32/eax . . # copy *ecx to eax - 81 5/subop/subtract 3/mod/direct 0/rm32/eax . . . . . _test-stream/imm32 # subtract from eax - 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 - # check-ints-equal(slice->end - _test-stream->data, 4, msg) - # . check-ints-equal(slice->end - _test-stream, 16, msg) - # . . push args - 68/push "F - test-next-word: end"/imm32 - 68/push 0x10/imm32 - # . . push slice->end - _test-stream - 8b/copy 1/mod/*+disp8 1/rm32/ecx . . . 0/r32/eax 4/disp8 . # copy *(ecx+4) to eax - 81 5/subop/subtract 3/mod/direct 0/rm32/eax . . . . . _test-stream/imm32 # subtract from eax - 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 - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-next-word-returns-whole-comment: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # 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 - # var slice/ecx = {0, 0} - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # 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 - # next-word(_test-stream, slice) - # . . push args - 51/push-ecx - 68/push _test-stream/imm32 - # . . call - e8/call next-word/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-ints-equal(slice->start - _test-stream->data, 2, msg) - # . check-ints-equal(slice->start - _test-stream, 14, msg) - # . . push args - 68/push "F - test-next-word-returns-whole-comment: start"/imm32 - 68/push 0xe/imm32 - # . . push slice->start - _test-stream - 8b/copy 0/mod/indirect 1/rm32/ecx . . . 0/r32/eax . . # copy *ecx to eax - 81 5/subop/subtract 3/mod/direct 0/rm32/eax . . . . . _test-stream/imm32 # subtract from eax - 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 - # check-ints-equal(slice->end - _test-stream->data, 5, msg) - # . check-ints-equal(slice->end - _test-stream, 17, msg) - # . . push args - 68/push "F - test-next-word-returns-whole-comment: end"/imm32 - 68/push 0x11/imm32 - # . . push slice->end - _test-stream - 8b/copy 1/mod/*+disp8 1/rm32/ecx . . . 0/r32/eax 4/disp8 . # copy *(ecx+4) to eax - 81 5/subop/subtract 3/mod/direct 0/rm32/eax . . . . . _test-stream/imm32 # subtract from eax - 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 - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-next-word-returns-empty-string-on-eof: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # 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 - # var slice/ecx = {0, 0} - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # write nothing to _test-stream - # next-word(_test-stream, slice) - # . . push args - 51/push-ecx - 68/push _test-stream/imm32 - # . . call - e8/call next-word/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-ints-equal(slice->end - slice->start, 0, msg) - # . . push args - 68/push "F - test-next-word-returns-empty-string-on-eof"/imm32 - 68/push 0/imm32 - # . . push slice->end - slice->start - 8b/copy 1/mod/*+disp8 1/rm32/ecx . . . 0/r32/eax 4/disp8 . # copy *(ecx+4) to eax - 2b/subtract 0/mod/indirect 1/rm32/ecx . . . 0/r32/eax . . # subtract *ecx from eax - 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 - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -# write an entire stream's contents to a buffered-file -# ways to do this: -# - construct a 'maximal slice' and pass it to write-slice-buffered -# - flush the buffered-file and pass the stream directly to its fd (disabling buffering) -# we'll go with the first way for now -write-stream-data: # f : (address buffered-file), s : (address stream) -> - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 50/push-eax - 51/push-ecx - 56/push-esi - # esi = s - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 0xc/disp8 . # copy *(ebp+12) to esi - # var slice/ecx = {s->data, s->data + s->write} - # . push s->data + s->write - 8b/copy 0/mod/indirect 6/rm32/esi . . . 0/r32/eax . . # copy *esi to eax - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/esi 0/index/eax . 0/r32/eax 0xc/disp8 . # copy esi+eax+12 to eax - 50/push-eax - # . push s->data - 8d/copy-address 1/mod/*+disp8 6/rm32/esi . . . 0/r32/eax 0xc/disp8 . # copy esi+12 to eax - 50/push-eax - # . ecx = esp - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # write-slice-buffered(f, slice) - # . . push args - 51/push-ecx - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) - # . . call - e8/call write-slice-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -$write-stream-data:end: - # . restore locals - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . restore registers - 5e/pop-to-esi - 59/pop-to-ecx - 58/pop-to-eax - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-write-stream-data: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # setup - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-eax _test-output-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-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # initialize input - # . write(_test-input-stream, "abcd") - # . . push args - 68/push "abcd"/imm32 - 68/push _test-input-stream/imm32 - # . . call - e8/call write/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # write-stream-data(_test-output-buffered-file, _test-input-stream) - # . . push args - 68/push _test-input-stream/imm32 - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call write-stream-data/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check that the write happened as expected - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . check-stream-equal(_test-output-stream, "abcd", msg) - # . . push args - 68/push "F - test-write-stream-data"/imm32 - 68/push "abcd"/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -has-metadata?: # word : (address slice), s : (address string) -> eax : boolean - # pseudocode: - # var twig : &slice = next-token-from-slice(word->start, word->end, '/') # skip name - # curr = twig->end - # while true - # twig = next-token-from-slice(curr, word->end, '/') - # if (twig.empty()) break - # if (slice-equal?(twig, s)) return true - # curr = twig->end - # return false - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 51/push-ecx - 52/push-edx - 56/push-esi - 57/push-edi - # esi = word - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi - # edx = word->end - 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 2/r32/edx 4/disp8 . # copy *(esi+4) to edx - # var twig/edi : (address slice) = {0, 0} - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 7/rm32/edi . . . 4/r32/esp . . # copy esp to edi - # next-token-from-slice(word->start, word->end, '/', twig) - # . . push args - 57/push-edi - 68/push 0x2f/imm32/slash - 52/push-edx - ff 6/subop/push 0/mod/indirect 6/rm32/esi . . . . . . # push *esi - # . . call - e8/call next-token-from-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp - # curr/ecx = twig->end - 8b/copy 1/mod/*+disp8 7/rm32/edi . . . 1/r32/ecx 4/disp8 . # copy *(edi+4) to ecx -$has-metadata?:loop: - # next-token-from-slice(curr, word->end, '/', twig) - # . . push args - 57/push-edi - 68/push 0x2f/imm32/slash - 52/push-edx - 51/push-ecx - # . . call - e8/call next-token-from-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp - # if (slice-empty?(twig)) return false - # . eax = slice-empty?(twig) - # . . push args - 57/push-edi - # . . call - e8/call slice-empty?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . if (eax != 0) return false - 3d/compare-eax-and 0/imm32 - 75/jump-if-not-equal $has-metadata?:false/disp8 - # if (slice-equal?(twig, s)) return true - # . eax = slice-equal?(twig, s) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) - 57/push-edi - # . . call - e8/call slice-equal?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . if (eax != 0) return true - 3d/compare-eax-and 0/imm32 - 75/jump-if-not-equal $has-metadata?:true/disp8 - # curr = twig->end - 8b/copy 1/mod/*+disp8 7/rm32/edi . . . 1/r32/ecx 4/disp8 . # copy *(edi+4) to ecx - eb/jump $has-metadata?:loop/disp8 -$has-metadata?:true: - b8/copy-to-eax 1/imm32/true - eb/jump $has-metadata?:end/disp8 -$has-metadata?:false: - b8/copy-to-eax 0/imm32/false -$has-metadata?:end: - # . reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . restore registers - 5f/pop-to-edi - 5e/pop-to-esi - 5a/pop-to-edx - 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-has-metadata-true: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # (eax..ecx) = "ab/imm32" - b8/copy-to-eax "ab/imm32"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # var in/esi : (address slice) = {eax, ecx} - 51/push-ecx - 50/push-eax - 89/copy 3/mod/direct 6/rm32/esi . . . 4/r32/esp . . # copy esp to esi - # eax = has-metadata?(esi, "imm32") - # . . push args - 68/push "imm32"/imm32 - 56/push-esi - # . . call - e8/call has-metadata?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-ints-equal(eax, 1, msg) - # . . push args - 68/push "F - test-has-metadata-true"/imm32 - 68/push 1/imm32/true - 50/push-eax - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-has-metadata-false: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # (eax..ecx) = "ab/c" - b8/copy-to-eax "ab/c"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # var in/esi : (address slice) = {eax, ecx} - 51/push-ecx - 50/push-eax - 89/copy 3/mod/direct 6/rm32/esi . . . 4/r32/esp . . # copy esp to esi - # eax = has-metadata?(esi, "d") - # . . push args - 68/push "d"/imm32 - 56/push-esi - # . . call - e8/call has-metadata?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-ints-equal(eax, 0, msg) - # . . push args - 68/push "F - test-has-metadata-false"/imm32 - 68/push 0/imm32/false - 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 - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-has-metadata-ignore-name: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # (eax..ecx) = "a/b" - b8/copy-to-eax "a/b"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # var in/esi : (address slice) = {eax, ecx} - 51/push-ecx - 50/push-eax - 89/copy 3/mod/direct 6/rm32/esi . . . 4/r32/esp . . # copy esp to esi - # eax = has-metadata?(esi, "a") - # . . push args - 68/push "a"/imm32 - 56/push-esi - # . . call - e8/call has-metadata?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-ints-equal(eax, 0, msg) - # . . push args - 68/push "F - test-has-metadata-ignore-name"/imm32 - 68/push 0/imm32/false - 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 - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-has-metadata-multiple-true: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # (eax..ecx) = "a/b/c" - b8/copy-to-eax "a/b/c"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # var in/esi : (address slice) = {eax, ecx} - 51/push-ecx - 50/push-eax - 89/copy 3/mod/direct 6/rm32/esi . . . 4/r32/esp . . # copy esp to esi - # eax = has-metadata?(esi, "c") - # . . push args - 68/push "c"/imm32 - 56/push-esi - # . . call - e8/call has-metadata?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-ints-equal(eax, 1, msg) - # . . push args - 68/push "F - test-has-metadata-multiple-true"/imm32 - 68/push 1/imm32/true - 50/push-eax - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-has-metadata-multiple-false: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # (eax..ecx) = "a/b/c" - b8/copy-to-eax "a/b/c"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # var in/esi : (address slice) = {eax, ecx} - 51/push-ecx - 50/push-eax - 89/copy 3/mod/direct 6/rm32/esi . . . 4/r32/esp . . # copy esp to esi - # eax = has-metadata?(esi, "d") - # . . push args - 68/push "d"/imm32 - 56/push-esi - # . . call - e8/call has-metadata?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # check-ints-equal(eax, 0, msg) - # . . push args - 68/push "F - test-has-metadata-multiple-false"/imm32 - 68/push 0/imm32/false - 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 - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -# If datum of 'word' is not a valid name, it must be a hex int. Parse and print -# it in 'width' bytes of hex, least significant first. -# Otherwise just print the entire word including metadata. -# Always print a trailing space. -emit: # out : (address buffered-file), word : (address slice), width : int -> - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 50/push-eax - 56/push-esi - 57/push-edi - # esi = word - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 0xc/disp8 . # copy *(ebp+12) to esi - # var name/edi : (address slice) = {0, 0} - 68/push 0/imm32/end - 68/push 0/imm32/start - 89/copy 3/mod/direct 7/rm32/edi . . . 4/r32/esp . . # copy esp to edi - # datum = next-token-from-slice(word->start, word->end, '/') - # . . push args - 57/push-edi - 68/push 0x2f/imm32/slash - ff 6/subop/push 1/mod/*+disp8 6/rm32/esi . . . . 4/disp8 . # push *(esi+4) - ff 6/subop/push 0/mod/indirect 6/rm32/esi . . . . . . # push *esi - # . . call - e8/call next-token-from-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp - # if (is-valid-name?(datum)) write-slice-buffered(out, word) and return - # . eax = is-valid-name?(name) - # . . push args - 57/push-edi - # . . call - e8/call is-valid-name?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . if (eax != 0) - 3d/compare-eax-and 0/imm32 - 74/jump-if-equal $emit:hex-int/disp8 -$emit:name: - # . write-slice-buffered(out, word) - # . . push args - 56/push-esi - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) - # . . call - e8/call write-slice-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . write-buffered(out, " ") - # . . push args - 68/push Space/imm32 - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) - # . . call - e8/call write-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . return - eb/jump $emit:end/disp8 - # otherwise emit-hex(out, parse-hex-int(datum), width) - # (Weird shit can happen here if the datum of 'word' isn't either a valid - # name or a hex number, but we're only going to be passing in real legal - # programs. We just want to make sure that valid names aren't treated as - # (valid) hex numbers.) -$emit:hex-int: - # . value/eax = parse-hex-int(datum) - # . . push args - 57/push-edi - # . . call - e8/call parse-hex-int/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . emit-hex(out, value, width) - # . . push args - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16) - 50/push-eax - ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) - # . . call - e8/call emit-hex/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp -$emit:end: - # . reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . restore registers - 5f/pop-to-edi - 5e/pop-to-esi - 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-emit-number: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # setup - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-eax _test-output-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 - # (eax..ecx) = "30" - b8/copy-to-eax "30"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # var slice/ecx = {eax, ecx} - 51/push-ecx - 50/push-eax - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # emit(_test-output-buffered-file, slice, 1) - # . . push args - 68/push 1/imm32 - 51/push-ecx - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call emit/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # check-stream-equal(_test-output-stream, "30 ", msg) - # . . push args - 68/push "F - test-emit-number/1"/imm32 - 68/push "30 "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-emit-negative-number: - # test support for sign-extending negative numbers - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # setup - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-eax _test-output-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 - # (eax..ecx) = "-2" - b8/copy-to-eax "-2"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # var slice/ecx = {eax, ecx} - 51/push-ecx - 50/push-eax - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # emit(_test-output-buffered-file, slice, 2) - # . . push args - 68/push 2/imm32 - 51/push-ecx - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call emit/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # check-stream-equal(_test-output-stream, "fe ff ", msg) - # . . push args - 68/push "F - test-emit-number/1"/imm32 - 68/push "fe ff "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-emit-number-with-metadata: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # setup - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-eax _test-output-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 - # (eax..ecx) = "-2/foo" - b8/copy-to-eax "-2/foo"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # var slice/ecx = {eax, ecx} - 51/push-ecx - 50/push-eax - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # emit(_test-output-buffered-file, slice, 2) - # . . push args - 68/push 2/imm32 - 51/push-ecx - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call emit/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # the '/foo' will have no impact on the output - # check-stream-equal(_test-output-stream, "fe ff ", msg) - # . . push args - 68/push "F - test-emit-number-with-metadata"/imm32 - 68/push "fe ff "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-emit-non-number: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # setup - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-eax _test-output-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 - # (eax..ecx) = "xyz" - b8/copy-to-eax "xyz"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # var slice/ecx = {eax, ecx} - 51/push-ecx - 50/push-eax - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # emit(_test-output-buffered-file, slice, 2) - # . . push args - 68/push 2/imm32 - 51/push-ecx - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call emit/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # check-stream-equal(_test-output-stream, "xyz", msg) - # . . push args - 68/push "F - test-emit-non-number"/imm32 - 68/push "xyz "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-emit-non-number-with-metadata: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # setup - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-eax _test-output-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 - # (eax..ecx) = "xyz/" - b8/copy-to-eax "xyz/"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # var slice/ecx = {eax, ecx} - 51/push-ecx - 50/push-eax - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # emit(_test-output-buffered-file, slice, 2) - # . . push args - 68/push 2/imm32 - 51/push-ecx - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call emit/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # check-stream-equal(_test-output-stream, "xyz/", msg) - # . . push args - 68/push "F - test-emit-non-number-with-metadata"/imm32 - 68/push "xyz/ "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-emit-non-number-with-all-hex-digits-and-metadata: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # setup - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-eax _test-output-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 - # (eax..ecx) = "abcd/xyz" - b8/copy-to-eax "abcd/xyz"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # var slice/ecx = {eax, ecx} - 51/push-ecx - 50/push-eax - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # emit(_test-output-buffered-file, slice, 2) - # . . push args - 68/push 2/imm32 - 51/push-ecx - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call emit/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp -#? # dump output {{{ -#? # . write(2/stderr, "^") -#? # . . push args -#? 68/push "^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # }}} - # check-stream-equal(_test-output-stream, "abcd/xyz") - # . . push args - 68/push "F - test-emit-non-number-with-all-hex-digits"/imm32 - 68/push "abcd/xyz "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -# conditions for 'valid' names that are not at risk of looking like hex numbers -# keep in sync with the rules in labels.cc -#: - if it starts with a digit, it's treated as a number. If it can't be -#: parsed as hex it will raise an error. -#: - if it starts with '-' it's treated as a number. -#: - if it starts with '0x' it's treated as a number. (redundant) -#: - if it's two characters long, it can't be a name. Either it's a hex -#: byte, or it raises an error. -is-valid-name?: # in : (address slice) -> eax : boolean - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 51/push-ecx - 56/push-esi - # esi = in - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi - # start/ecx = in->start - 8b/copy 0/mod/indirect 6/rm32/esi . . . 1/r32/ecx . . # copy *esi to ecx - # end/eax = in->end - 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 0/r32/eax 4/disp8 . # copy *(esi+4) to eax -$is-valid-name?:check0: - # if (start >= end) return false - 39/compare 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # compare ecx with eax - 73/jump-if-greater-or-equal-unsigned $is-valid-name?:false/disp8 -$is-valid-name?:check1: - # eax -= ecx - 29/subtract 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # subtract ecx from eax - # if (eax == 2) return false - 3d/compare-eax-and 2/imm32 - 74/jump-if-equal $is-valid-name?:false/disp8 -$is-valid-name?:check2: - # c/eax = *ecx - 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax - 8a/copy-byte 0/mod/indirect 1/rm32/ecx . . . 0/r32/AL . . # copy byte at *ecx to AL - # if (c == "-") return false - 3d/compare-eax-and 2d/imm32/- - 74/jump-if-equal $is-valid-name?:false/disp8 -$is-valid-name?:check3a: - # if (c < "0") return true - 3d/compare-eax-with 30/imm32/0 - 7c/jump-if-lesser $is-valid-name?:true/disp8 -$is-valid-name?:check3b: - # if (c > "9") return true - 3d/compare-eax-with 39/imm32/9 - 7f/jump-if-greater $is-valid-name?:true/disp8 -$is-valid-name?:false: - # return false - b8/copy-to-eax 0/imm32/false - eb/jump $is-valid-name?:end/disp8 -$is-valid-name?:true: - # return true - b8/copy-to-eax 1/imm32/true -$is-valid-name?:end: - # . restore registers - 5e/pop-to-esi - 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-is-valid-name-digit-prefix: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # (eax..ecx) = "34" - b8/copy-to-eax "34"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # var slice/ecx = {eax, ecx} - 51/push-ecx - 50/push-eax - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # eax = is-valid-name?(slice) - # . . push args - 51/push-ecx - # . . call - e8/call is-valid-name?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # check-ints-equal(eax, 0, msg) - # . . push args - 68/push "F - test-is-valid-name-digit-prefix"/imm32 - 68/push 0/imm32/false - 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 - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-is-valid-name-negative-prefix: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # (eax..ecx) = "-0x34" - b8/copy-to-eax "-0x34"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # var slice/ecx = {eax, ecx} - 51/push-ecx - 50/push-eax - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # eax = is-valid-name?(slice) - # . . push args - 51/push-ecx - # . . call - e8/call is-valid-name?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # check-ints-equal(eax, 0, msg) - # . . push args - 68/push "F - test-is-valid-name-negative-prefix"/imm32 - 68/push 0/imm32/false - 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 - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-is-valid-name-0x-prefix: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # (eax..ecx) = "0x34" - b8/copy-to-eax "0x34"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # var slice/ecx = {eax, ecx} - 51/push-ecx - 50/push-eax - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # eax = is-valid-name?(slice) - # . . push args - 51/push-ecx - # . . call - e8/call is-valid-name?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # check-ints-equal(eax, 0, msg) - # . . push args - 68/push "F - test-is-valid-name-0x-prefix"/imm32 - 68/push 0/imm32/false - 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 - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-is-valid-name-starts-with-pre-digit: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # (eax..ecx) = "/03" - b8/copy-to-eax "/03"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # var slice/ecx = {eax, ecx} - 51/push-ecx - 50/push-eax - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # eax = is-valid-name?(slice) - # . . push args - 51/push-ecx - # . . call - e8/call is-valid-name?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # check-ints-equal(eax, 1, msg) - # . . push args - 68/push "F - test-is-valid-name-starts-with-pre-digit"/imm32 - 68/push 1/imm32/true - 50/push-eax - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-is-valid-name-starts-with-post-digit: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # (eax..ecx) = "q34" - b8/copy-to-eax "q34"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # var slice/ecx = {eax, ecx} - 51/push-ecx - 50/push-eax - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # eax = is-valid-name?(slice) - # . . push args - 51/push-ecx - # . . call - e8/call is-valid-name?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # check-ints-equal(eax, 1, msg) - # . . push args - 68/push "F - test-is-valid-name-starts-with-post-digit"/imm32 - 68/push 1/imm32/true - 50/push-eax - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-is-valid-name-starts-with-digit: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # (eax..ecx) = "0x34" - b8/copy-to-eax "0x34"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # var slice/ecx = {eax, ecx} - 51/push-ecx - 50/push-eax - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # eax = is-valid-name?(slice) - # . . push args - 51/push-ecx - # . . call - e8/call is-valid-name?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # check-ints-equal(eax, 0, msg) - # . . push args - 68/push "F - test-is-valid-name-starts-with-digit"/imm32 - 68/push 0/imm32/false - 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 - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -# print 'n' in hex in 'width' bytes in lower-endian order, with a space after every byte -emit-hex: # out : (address buffered-file), n : int, width : int -> - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 50/push-eax - 51/push-ecx - 52/push-edx - 53/push-ebx - 57/push-edi - # edi = out - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 7/r32/edi 8/disp8 . # copy *(ebp+8) to edi - # ebx = n - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 3/r32/ebx 0xc/disp8 . # copy *(ebp+12) to ebx - # edx = width - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 2/r32/edx 0x10/disp8 . # copy *(ebp+16) to edx - # var curr/ecx = 0 - 31/xor 3/mod/direct 1/rm32/ecx . . . 1/r32/ecx . . # clear ecx -$emit-hex:loop: - # if (curr >= width) break - 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx - 7d/jump-if-greater-or-equal $emit-hex:end/disp8 - # print-byte-buffered(out, ebx) - # . . push args - 53/push-ebx - 57/push-edi - # . . call - e8/call print-byte-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # write-byte-buffered(out, ' ') - # . . push args - 68/push 0x20/imm32/space - 57/push-edi - # . . call - e8/call write-byte-buffered/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # ebx = ebx >> 8 - c1/shift 5/subop/logic-right 3/mod/direct 3/rm32/ebx . . . . . 8/imm8 # shift ebx right by 8 bits, while padding zeroes -$emit-hex:continue: - # ++curr - 41/increment-ecx - eb/jump $emit-hex:loop/disp8 -$emit-hex:end: - # . restore registers - 5f/pop-to-edi - 5b/pop-to-ebx - 5a/pop-to-edx - 59/pop-to-ecx - 58/pop-to-eax - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-emit-hex-single-byte: - # setup - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-eax _test-output-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 - # emit-hex(_test-output-buffered-file, 0xab, 1) - # . . push args - 68/push 1/imm32 - 68/push 0xab/imm32 - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call emit-hex/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # check-ints-equal(*_test-output-stream->data, 'ab ', msg) - # . . push args - 68/push "F - test-emit-hex-single-byte"/imm32 - 68/push 0x206261/imm32 - # . . push *_test-output-stream->data - 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 - # . end - c3/return - -test-emit-hex-multiple-byte: - # setup - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-eax _test-output-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 - # emit-hex(_test-output-buffered-file, 0x1234, 2) - # . . push args - 68/push 2/imm32 - 68/push 0x1234/imm32 - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call emit-hex/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # check-stream-equal(_test-output-stream, "34 12 ", msg) - # . . push args - 68/push "F - test-emit-hex-multiple-byte/1"/imm32 - 68/push "34 12 "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . end - c3/return - -test-emit-hex-zero-pad: - # setup - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-eax _test-output-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 - # emit-hex(_test-output-buffered-file, 0xab, 2) - # . . push args - 68/push 2/imm32 - 68/push 0xab/imm32 - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call emit-hex/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # check(_test-output-stream->data == 'ab 00 ') - # . . push args - 68/push "F - test-emit-hex-zero-pad/1"/imm32 - 68/push "ab 00 "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . end - c3/return - -test-emit-hex-negative: - # setup - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-eax _test-output-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 - # emit-hex(_test-output-buffered-file, -1, 2) - # . . push args - 68/push 2/imm32 - 68/push -1/imm32 - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call emit-hex/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # check-stream-equal(_test-output-stream == "ff ff ") - # . . push args - 68/push "F - test-emit-hex-negative/1"/imm32 - 68/push "ff ff "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-stream-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . end - c3/return - -# print 'arr' in hex with a space after every byte -emit-hex-array: # out : (address buffered-file), arr : (address array byte) -> - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 50/push-eax - 51/push-ecx - 52/push-edx - 57/push-edi - # edi = out - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 7/r32/edi 8/disp8 . # copy *(ebp+8) to edi - # edx = arr # <== 0xbdffffe4 - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 2/r32/edx 0xc/disp8 . # copy *(ebp+12) to edx - # curr/ecx = arr->data - 8d/copy-address 1/mod/*+disp8 2/rm32/edx . . . 1/r32/ecx 4/disp8 . # copy edx+4 to ecx - # max/edx = arr->data + arr->length - 8b/copy 0/mod/indirect 2/rm32/edx . . . 2/r32/edx . . # copy *edx to edx - 01/add 3/mod/direct 2/rm32/edx . . . 1/r32/ecx . . # add ecx to edx - # eax = 0 - 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax -$emit-hex-array:loop: - # if (curr >= width) break - 39/compare 3/mod/direct 1/rm32/ecx . . . 2/r32/edx . . # compare ecx with edx - 73/jump-if-greater-or-equal-unsigned $emit-hex-array:end/disp8 - # emit-hex(out, *curr, width=1) - # . . push args - 68/push 1/imm32/width - 8a/copy-byte 0/mod/indirect 1/rm32/ecx . . . 0/r32/AL . . # copy byte at *ecx to AL - 50/push-eax - 57/push-edi - # . . call - e8/call emit-hex/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # ++curr - 41/increment-ecx - eb/jump $emit-hex-array:loop/disp8 -$emit-hex-array:end: - # . restore registers - 5f/pop-to-edi - 5a/pop-to-edx - 59/pop-to-ecx - 58/pop-to-eax - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -test-emit-hex-array: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # setup - # . clear-stream(_test-output-stream) - # . . push args - 68/push _test-output-stream/imm32 - # . . call - e8/call clear-stream/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # . clear-stream(_test-output-buffered-file+4) - # . . push args - b8/copy-to-eax _test-output-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 - # var arr/ecx (address array byte) = [01, 02, 03] - 68/push 0x00030201/imm32 # bytes 01 02 03 - 68/push 3/imm32/length - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # emit-hex-array(_test-output-buffered-file, arr) - # . . push args - 51/push-ecx - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call emit-hex-array/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . flush(_test-output-buffered-file) - # . . push args - 68/push _test-output-buffered-file/imm32 - # . . call - e8/call flush/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp -#? # dump output {{{ -#? # . write(2/stderr, "result: ^") -#? # . . push args -#? 68/push "result: ^"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # . write-stream(2/stderr, _test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # . write(2/stderr, "$\n") -#? # . . push args -#? 68/push "$\n"/imm32 -#? 68/push 2/imm32/stderr -#? # . . call -#? e8/call write/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -#? # . rewind-stream(_test-output-stream) -#? # . . push args -#? 68/push _test-output-stream/imm32 -#? # . . call -#? e8/call rewind-stream/disp32 -#? # . . discard args -#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp -#? # }}} - # check-next-stream-line-equal(_test-output-stream, "01 02 03 ", msg) - # . . push args - 68/push "F - test-emit-hex-array"/imm32 - 68/push "01 02 03 "/imm32 - 68/push _test-output-stream/imm32 - # . . call - e8/call check-next-stream-line-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -compute-width: # word : (address array byte) -> eax : int - # . 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 = word - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 8/disp8 . # copy *(ebp+8) to ecx - # ecx = word + word->length - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - # eax = word->data - 05/add-to-eax 4/imm32 - # var in/ecx : (address slice) = {eax, ecx} - 51/push-ecx - 50/push-eax - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # return compute-width-of-slice(ecx) - # . . push args - 51/push-ecx - # . . call - e8/call compute-width-of-slice/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp -$compute-width:end: - # . reclaim locals - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . 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 - -compute-width-of-slice: # s : (address slice) -> eax : int - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 51/push-ecx - # ecx = s - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 8/disp8 . # copy *(ebp+8) to ecx - # if (has-metadata?(word, "imm32")) return 4 - # . eax = has-metadata?(word, "imm32") - # . . push args - 68/push "imm32"/imm32 - 51/push-ecx - # . . call - e8/call has-metadata?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . if (eax != 0) return 4 - 3d/compare-eax-and 0/imm32 - b8/copy-to-eax 4/imm32 # ZF is set, so we can overwrite eax now - 75/jump-if-not-equal $compute-width-of-slice:end/disp8 - # if (has-metadata?(word, "disp32")) return 4 - # . eax = has-metadata?(word, "disp32") - # . . push args - 68/push "disp32"/imm32 - 51/push-ecx - # . . call - e8/call has-metadata?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . if (eax != 0) return 4 - 3d/compare-eax-and 0/imm32 - b8/copy-to-eax 4/imm32 # ZF is set, so we can overwrite eax now - 75/jump-if-not-equal $compute-width-of-slice:end/disp8 - # if (has-metadata?(word, "imm16")) return 2 - # . eax = has-metadata?(word, "imm16") - # . . push args - 68/push "imm16"/imm32 - 51/push-ecx - # . . call - e8/call has-metadata?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . if (eax != 0) return 2 - 3d/compare-eax-and 0/imm32 - b8/copy-to-eax 2/imm32 # ZF is set, so we can overwrite eax now - 75/jump-if-not-equal $compute-width-of-slice:end/disp8 - # if (has-metadata?(word, "disp16")) return 2 - # . eax = has-metadata?(word, "disp16") - # . . push args - 68/push "disp16"/imm32 - 51/push-ecx - # . . call - e8/call has-metadata?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # . if (eax != 0) return 2 - 3d/compare-eax-and 0/imm32 - b8/copy-to-eax 2/imm32 # ZF is set, so we can overwrite eax now - 75/jump-if-not-equal $compute-width-of-slice:end/disp8 - # otherwise return 1 - b8/copy-to-eax 1/imm32 -$compute-width-of-slice: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-compute-width: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp -$test-compute-width:imm8: - # eax = compute-width("0x2/imm8") - # . . push args - 68/push "0x2/imm8"/imm32 - # . . call - e8/call compute-width/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # check-ints-equal(eax, 1, msg) - # . . push args - 68/push "F - test-compute-width: 0x2/imm8"/imm32 - 50/push-eax - 68/push 1/imm32 - # . . 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-compute-width:imm16: - # eax = compute-width("4/imm16") - # . . push args - 68/push "4/imm16"/imm32 - # . . call - e8/call compute-width/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # check-ints-equal(eax, 2, msg) - # . . push args - 68/push "F - test-compute-width: 4/imm16"/imm32 - 50/push-eax - 68/push 2/imm32 - # . . 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-compute-width:imm32: - # eax = compute-width("4/imm32") - # . . push args - 68/push "4/imm32"/imm32 - # . . call - e8/call compute-width/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # check-ints-equal(eax, 4, msg) - # . . push args - 68/push "F - test-compute-width: 4/imm32"/imm32 - 50/push-eax - 68/push 4/imm32 - # . . 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-compute-width:disp8: - # eax = compute-width("foo/disp8") - # . . push args - 68/push "foo/disp8"/imm32 - # . . call - e8/call compute-width/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # check-ints-equal(eax, 1, msg) - # . . push args - 68/push "F - test-compute-width: foo/disp8"/imm32 - 50/push-eax - 68/push 1/imm32 - # . . 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-compute-width:disp16: - # eax = compute-width("foo/disp16") - # . . push args - 68/push "foo/disp16"/imm32 - # . . call - e8/call compute-width/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # check-ints-equal(eax, 2, msg) - # . . push args - 68/push "F - test-compute-width: foo/disp16"/imm32 - 50/push-eax - 68/push 2/imm32 - # . . 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-compute-width:disp32: - # eax = compute-width("foo/disp32") - # . . push args - 68/push "foo/disp32"/imm32 - # . . call - e8/call compute-width/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # check-ints-equal(eax, 4, msg) - # . . push args - 68/push "F - test-compute-width: foo/disp32"/imm32 - 50/push-eax - 68/push 4/imm32 - # . . 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-compute-width:no-metadata: - # eax = compute-width("45") - # . . push args - 68/push "45"/imm32 - # . . call - e8/call compute-width/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # check-ints-equal(eax, 1, msg) - # . . push args - 68/push "F - test-compute-width: 45 (no metadata)"/imm32 - 50/push-eax - 68/push 1/imm32 - # . . call - e8/call check-ints-equal/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -is-label?: # word : (address slice) -> eax : boolean - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - # . save registers - 51/push-ecx - # ecx = word - 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 8/disp8 . # copy *(ebp+8) to ecx - # ecx = word->end - 8b/copy 1/mod/*+disp8 1/rm32/ecx . . . 1/r32/ecx 4/disp8 . # copy *(ecx+4) to ecx - # return *(word->end - 1) == ':' - # . eax = 0 - 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax - # . eax = *((char *) word->end - 1) - 8a/copy-byte 1/mod/*+disp8 1/rm32/ecx . . . 0/r32/AL -1/disp8 . # copy byte at *(ecx-1) to AL - # . return (eax == ':') - 3d/compare-eax-and 0x3a/imm32/colon - b8/copy-to-eax 1/imm32/true - 74/jump-if-equal $is-label?:end/disp8 - b8/copy-to-eax 0/imm32/false -$is-label?: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-is-label?: - # . prolog - 55/push-ebp - 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp -$test-is-label?:true: - # (eax..ecx) = "AAA:" - b8/copy-to-eax "AAA:"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # var slice/ecx = {eax, ecx} - 51/push-ecx - 50/push-eax - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # is-label?(slice/ecx) - # . . push args - 51/push-ecx - # . . call - e8/call is-label?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # check-ints-equal(eax, 1, msg) - # . . push args - 68/push "F - test-is-label?:true"/imm32 - 68/push 1/imm32 - 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-is-label?:false: - # (eax..ecx) = "AAA" - b8/copy-to-eax "AAA"/imm32 - 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx - 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx - 05/add-to-eax 4/imm32 - # var slice/ecx = {eax, ecx} - 51/push-ecx - 50/push-eax - 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - # is-label?(slice/ecx) - # . . push args - 51/push-ecx - # . . call - e8/call is-label?/disp32 - # . . discard args - 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp - # check-ints-equal(eax, 0, msg) - # . . push args - 68/push "F - test-is-label?:false"/imm32 - 68/push 0/imm32 - 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 - # . epilog - 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 5d/pop-to-ebp - c3/return - -== data - -_test-data-segment: - 64/d 61/a 74/t 61/a -_test-data-segment-end: - -# . . vim:nowrap:textwidth=0 diff --git a/apps/subx-params.subx b/apps/subx-params.subx new file mode 100644 index 00000000..38a34314 --- /dev/null +++ b/apps/subx-params.subx @@ -0,0 +1,15 @@ +# Various knobs for translating SubX programs using SubX. + +== data + +# largest segment that can be translated +Segment-size: + 0x80000/imm32/512KB + +# maximum size of input textual stream (spanning all segments) +Input-size: + 0x100000/imm32/1MB + +# number of labels we can translate to addresses +Max-labels: + 0x10000/imm32/4K-labels/64KB diff --git a/apps/survey b/apps/survey index cc94c6ca..cc9da808 100755 Binary files a/apps/survey and b/apps/survey differ diff --git a/apps/survey.subx b/apps/survey.subx index 678ed116..b943fe72 100644 --- a/apps/survey.subx +++ b/apps/survey.subx @@ -5,7 +5,7 @@ # b) add segment headers with addresses and offsets correctly filled in # # To build: -# $ ./subx translate init.linux 0*.subx apps/subx-common.subx apps/survey.subx -o apps/survey +# $ ./subx translate init.linux 0*.subx apps/subx-params.subx apps/survey.subx -o apps/survey # # The expected input is a stream of bytes with segment headers, comments and # some interspersed labels. @@ -62,10 +62,10 @@ Entry: # run tests if necessary, convert stdin if not 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # - if argc > 1 and argv[1] == "test", then return run_tests() - # if (argc <= 1) goto run-main + # if (argc <= 1) goto interactive 81 7/subop/compare 1/mod/*+disp8 5/rm32/ebp . . . . 0/disp8 1/imm32 # compare *ebp - 7e/jump-if-lesser-or-equal $run-main/disp8 - # if (!kernel-string-equal?(argv[1], "test")) goto run-main + 7e/jump-if-lesser-or-equal $subx-survey-main:interactive/disp8 + # if (!kernel-string-equal?(argv[1], "test")) goto interactive # . eax = kernel-string-equal?(argv[1], "test") # . . push args 68/push "test"/imm32 @@ -74,22 +74,22 @@ Entry: # run tests if necessary, convert stdin if not 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 + # . if (eax == 0) goto interactive 3d/compare-eax-and 0/imm32 - 74/jump-if-equal $run-main/disp8 + 74/jump-if-equal $subx-survey-main:interactive/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: + eb/jump $subx-survey-main:end/disp8 +$subx-survey-main:interactive: # - otherwise convert stdin - # convert(Stdin, Stdout) + # subx-survey(Stdin, Stdout) # . . push args 68/push Stdout/imm32 68/push Stdin/imm32 # . . call - e8/call convert/disp32 + e8/call subx-survey/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp #? # . write-stream(2/stderr, Trace-stream) @@ -102,7 +102,7 @@ $run-main: #? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # syscall(exit, 0) bb/copy-to-ebx 0/imm32 -$main:end: +$subx-survey-main:end: b8/copy-to-eax 1/imm32/exit cd/syscall 0x80/imm8 @@ -113,7 +113,7 @@ $main:end: # labels: (address stream {string, label-info}) (16 bytes per row) # these are all inefficient; use sequential scans for lookups -convert: # infile : (address buffered-file), out : (address buffered-file) -> +subx-survey: # infile : (address buffered-file), out : (address buffered-file) -> # pseudocode # var in : (address stream byte) = stream(4096) # slurp(infile, in) @@ -440,7 +440,7 @@ convert: # infile : (address buffered-file), out : (address buffered-file) -> < e8/call flush/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp -$convert:end: +$subx-survey:end: # . reclaim locals 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x30a0/imm32 # add to esp # . restore registers @@ -452,7 +452,7 @@ $convert:end: 5d/pop-to-ebp c3/return -test-convert-computes-addresses: +test-subx-survey-computes-addresses: # input: # == code 0x1 # Entry: @@ -552,12 +552,12 @@ test-convert-computes-addresses: e8/call write/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - # convert(_test-input-buffered-file, _test-output-buffered-file) + # subx-survey(_test-input-buffered-file, _test-output-buffered-file) # . . push args 68/push _test-output-buffered-file/imm32 68/push _test-input-buffered-file/imm32 # . . call - e8/call convert/disp32 + e8/call subx-survey/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # check trace @@ -589,7 +589,7 @@ test-convert-computes-addresses: #? # }}} # . check-trace-contains("label 'x' is at address 0x00001079.", msg) # . . push args - 68/push "F - test-convert-computes-addresses/0"/imm32 + 68/push "F - test-subx-survey-computes-addresses/0"/imm32 68/push "label 'x' is at address 0x00001079."/imm32 # . . call e8/call check-trace-contains/disp32 @@ -597,7 +597,7 @@ test-convert-computes-addresses: 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . check-trace-contains("segment 'code' starts at address 0x00000074.", msg) # . . push args - 68/push "F - test-convert-computes-addresses/1"/imm32 + 68/push "F - test-subx-survey-computes-addresses/1"/imm32 68/push "segment 'code' starts at address 0x00000074."/imm32 # . . call e8/call check-trace-contains/disp32 @@ -605,7 +605,7 @@ test-convert-computes-addresses: 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . check-trace-contains("segment 'code' has size 0x00000005.", msg) # . . push args - 68/push "F - test-convert-computes-addresses/2"/imm32 + 68/push "F - test-subx-survey-computes-addresses/2"/imm32 68/push "segment 'code' has size 0x00000005."/imm32 # . . call e8/call check-trace-contains/disp32 @@ -613,7 +613,7 @@ test-convert-computes-addresses: 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . check-trace-contains("segment 'data' starts at address 0x00001079.", msg) # . . push args - 68/push "F - test-convert-computes-addresses/3"/imm32 + 68/push "F - test-subx-survey-computes-addresses/3"/imm32 68/push "segment 'data' starts at address 0x00001079."/imm32 # . . call e8/call check-trace-contains/disp32 diff --git a/apps/tests b/apps/tests index cb6f4a7f..30ef5fcd 100755 Binary files a/apps/tests and b/apps/tests differ diff --git a/apps/tests.subx b/apps/tests.subx index cb6bd034..290483f0 100644 --- a/apps/tests.subx +++ b/apps/tests.subx @@ -2,7 +2,7 @@ # all functions starting with 'test-'. # # To build: -# $ ./subx translate init.linux 0*.subx apps/subx-common.subx apps/tests.subx -o apps/tests +# $ ./subx translate init.linux 0*.subx apps/subx-params.subx apps/tests.subx -o apps/tests == code # instruction effective address register displacement immediate @@ -33,7 +33,7 @@ Entry: # run tests if necessary, convert stdin if not # - 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 $run-main/disp8 + 7e/jump-if-lesser-or-equal $subx-tests-main:interactive/disp8 # if (!kernel-string-equal?(argv[1], "test")) goto run-main # . eax = kernel-string-equal?(argv[1], "test") # . . push args @@ -45,29 +45,29 @@ Entry: # run tests if necessary, convert stdin if not 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 $run-main/disp8 + 74/jump-if-equal $subx-tests-main:interactive/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: + eb/jump $subx-tests-main:end/disp8 +$subx-tests-main:interactive: # - otherwise convert stdin - # convert(Stdin, Stdout) + # subx-gen-run-tests(Stdin, Stdout) # . . push args 68/push Stdout/imm32 68/push Stdin/imm32 # . . call - e8/call convert/disp32 + e8/call subx-gen-run-tests/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # syscall(exit, 0) bb/copy-to-ebx 0/imm32 -$main:end: +$subx-tests-main:end: b8/copy-to-eax 1/imm32/exit cd/syscall 0x80/imm8 -convert: # in : (address buffered-file), out : (address buffered-file) -> +subx-gen-run-tests: # in : (address buffered-file), out : (address buffered-file) -> # pseudocode # bool tests-found = false # var line = new-stream(512, 1) @@ -141,7 +141,7 @@ convert: # in : (address buffered-file), out : (address buffered-file) -> write == 0) break 81 7/subop/compare 0/mod/indirect 1/rm32/ecx . . . . . 0/imm32 # compare *ecx - 0f 84/jump-if-equal $convert:break/disp32 + 0f 84/jump-if-equal $subx-gen-run-tests:break/disp32 # next-word(line, word-slice) # . . push args 52/push-edx @@ -169,7 +169,7 @@ $convert:check0: e8/call next-word/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -$convert:check-for-label: +$subx-gen-run-tests:check-for-label: # if (!is-label?(word-slice)) continue # . eax = is-label?(word-slice) # . . push args @@ -180,8 +180,8 @@ $convert:check-for-label: 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp # . if (eax == 0) continue 3d/compare-eax-and 0/imm32 - 74/jump-if-equal $convert:continue/disp8 -$convert:check-label-prefix: + 74/jump-if-equal $subx-gen-run-tests:continue/disp8 +$subx-gen-run-tests:check-label-prefix: # strip trailing ':' from word-slice ff 1/subop/decrement 1/mod/*+disp8 2/rm32/edx . . . . 4/disp8 . # decrement *(edx+4) # if !slice-starts-with?(word-slice, "test-") continue @@ -194,8 +194,8 @@ $convert:check-label-prefix: 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # . if (eax == 0) break 3d/compare-eax-and 0/imm32 - 74/jump-if-equal $convert:continue/disp8 -$convert:call-test-function: + 74/jump-if-equal $subx-gen-run-tests:continue/disp8 +$subx-gen-run-tests:call-test-function: # tests-found? = true bb/copy-to-ebx 1/imm32/true # write(new-code-segment, " e8/call ") @@ -222,7 +222,7 @@ $convert:call-test-function: e8/call write/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -$convert:continue: +$subx-gen-run-tests:continue: # rewind-stream(line) # . . push args 51/push-ecx @@ -239,11 +239,11 @@ $convert:continue: # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp # loop - e9/jump $convert:loop/disp32 -$convert:break: + e9/jump $subx-gen-run-tests:loop/disp32 +$subx-gen-run-tests:break: # if (!tests-found?) goto end 81 7/subop/compare 3/mod/direct 3/rm32/ebx . . . . . 0/imm32 # compare ebx - 74/jump-if-equal $convert:end/disp8 + 74/jump-if-equal $subx-gen-run-tests:end/disp8 # write(new-code-segment, " c3/return\n") # . . push args 68/push " c3/return\n"/imm32 @@ -260,7 +260,7 @@ $convert:break: e8/call write-stream-data/disp32 # . . discard args 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -$convert:end: +$subx-gen-run-tests:end: # flush(out) # . . push args ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) diff --git a/build b/build index 0d723656..9e95bb34 100755 --- a/build +++ b/build @@ -106,54 +106,4 @@ older_than subx_bin subx.cc *_list && { $CXX $CFLAGS subx.cc -o subx_bin } -# We ought to always rebuild all apps if any .subx layers are updated. -# But during development it's too slow to update _all_ apps when we're -# repeatedly running a single one. -if [ ! $ONLY_CPP ] -then - - # Assumption: SubX programs don't need to be retranslated every time we - # rebuild the C++ bootstrap. - - OS=${OS:-linux} - - # simple example programs - for n in `seq 1 12` - do - older_than examples/ex$n init.$OS examples/ex$n.subx && { - ./subx_bin translate init.$OS examples/ex$n.subx -o examples/ex$n - } - done - - # simple apps that use the standard library - for app in factorial crenshaw2-1 crenshaw2-1b handle - do - older_than apps/$app init.$OS [0-9]*.subx apps/$app.subx && { - ./subx_bin translate init.$OS [0-9]*.subx apps/$app.subx -o apps/$app - } - done - - # self-hosting translator - - older_than apps/hex init.$OS 0[0-6]*.subx 070---hex.subx && { - ./subx_bin translate init.$OS 0[0-6]*.subx 070---hex.subx -o apps/hex - } - - for phase in hex survey pack assort dquotes tests - do - older_than apps/$phase init.$OS [0-9]*.subx apps/subx-common.subx apps/$phase.subx && { - ./subx_bin translate init.$OS [0-9]*.subx apps/subx-common.subx apps/$phase.subx -o apps/$phase - } - done - - # higher-level syntax - for phase in sigils - do - older_than apps/$phase init.$OS [0-9]*.subx apps/subx-common.subx apps/$phase.subx && { - ./subx_bin translate init.$OS [0-9]*.subx apps/subx-common.subx apps/$phase.subx -o apps/$phase - } - done - -fi - exit 0 diff --git a/run_one_test b/run_one_test index b8eeaad5..04af6ba0 100755 --- a/run_one_test +++ b/run_one_test @@ -7,7 +7,7 @@ if [[ $2 == 'test-'* ]] then TEST_NAME=$2 envsubst '$TEST_NAME' < run_one_test.subx > /tmp/run_one_test.subx - FILES=$(ls [0-9]*.subx apps/subx-common.subx $1 |sort |uniq) + FILES=$(ls [0-9]*.subx apps/subx-params.subx $1 |sort |uniq) echo $FILES > /tmp/last_run_files elif [[ -e /tmp/last_run_files ]] then diff --git a/test_apps b/test_apps index bb645e97..d69e5b83 100755 --- a/test_apps +++ b/test_apps @@ -225,82 +225,28 @@ test $NATIVE && { # Phases of the self-hosted SubX translator. -echo hex -./subx translate init.$OS $(enumerate/enumerate --until 070---hex.subx |grep '\.subx$') -o apps/hex -test "$1" = 'record' || git diff --exit-code apps/hex -test $EMULATED && { - ./subx run apps/hex test - echo -} -test $NATIVE && { - apps/hex test - echo -} - -echo survey -./subx translate init.$OS 0*.subx apps/subx-common.subx apps/survey.subx -o apps/survey -test "$1" = 'record' || git diff --exit-code apps/survey -test $EMULATED && { - ./subx run apps/survey test - echo -} -test $NATIVE && { - apps/survey test - echo -} - -echo pack -./subx translate init.$OS 0*.subx apps/subx-common.subx apps/pack.subx -o apps/pack -test "$1" = 'record' || git diff --exit-code apps/pack -test $EMULATED && { - ./subx run apps/pack test - echo -} -test $NATIVE && { - apps/pack test - echo -} - -echo assort -./subx translate init.$OS 0*.subx apps/subx-common.subx apps/assort.subx -o apps/assort -test "$1" = 'record' || git diff --exit-code apps/assort -test $EMULATED && { - ./subx run apps/assort test - echo -} -test $NATIVE && { - apps/assort test - echo -} - -echo dquotes -./subx translate init.$OS 0*.subx apps/subx-common.subx apps/dquotes.subx -o apps/dquotes -test "$1" = 'record' || git diff --exit-code apps/dquotes -test $EMULATED && { - ./subx run apps/dquotes test - echo -} -test $NATIVE && { - apps/dquotes test - echo -} - -echo tests -./subx translate init.$OS 0*.subx apps/subx-common.subx apps/tests.subx -o apps/tests -test "$1" = 'record' || git diff --exit-code apps/tests -test $EMULATED && { - ./subx run apps/tests test - echo -} -test $NATIVE && { - apps/tests test - echo -} +for phase in hex survey pack assort dquotes tests +do + echo $phase + ./subx translate init.$OS 0*.subx apps/subx-params.subx apps/$phase.subx -o apps/$phase + test "$1" = 'record' || git diff --exit-code apps/hex + test $EMULATED && { + ./subx run apps/$phase test + echo + } + test $NATIVE && { + apps/$phase test + echo + } +done # Higher-level syntax. +# Certain phases of translation run native beyond this point. We're starting +# to go beyond functionality of the C++ bootstrap. + echo sigils -./subx translate init.$OS 0*.subx apps/subx-common.subx apps/sigils.subx -o apps/sigils +./subx translate init.$OS 0*.subx apps/subx-params.subx apps/sigils.subx -o apps/sigils [ "$1" != record ] && git diff --exit-code apps/sigils ./subx run apps/sigils test echo @@ -310,7 +256,7 @@ test `uname` = 'Linux' && { } echo calls -cat init.$OS 0*.subx apps/subx-common.subx apps/calls.subx | apps/sigils > a.sigils +cat init.$OS 0*.subx apps/subx-params.subx apps/calls.subx | apps/sigils > a.sigils ./subx translate a.sigils -o apps/calls [ "$1" != record ] && git diff --exit-code apps/calls ./subx run apps/calls test @@ -321,7 +267,7 @@ test `uname` = 'Linux' && { } echo braces -cat init.$OS 0*.subx apps/subx-common.subx apps/braces.subx | apps/calls | apps/sigils > a.sigils +cat init.$OS 0*.subx apps/subx-params.subx apps/braces.subx | apps/calls | apps/sigils > a.sigils ./subx translate a.sigils -o apps/braces [ "$1" != record ] && git diff --exit-code apps/braces ./subx run apps/braces test @@ -331,12 +277,8 @@ test `uname` = 'Linux' && { echo } -# Only native runs beyond this point. We start using syntax that the emulator -# doesn't support. -test $EMULATED && echo "skipping remaining runs in emulated mode" test $NATIVE || exit 0 - -echo "== translating using SubX" +echo "== translating using SubX (native only)" # example programs @@ -358,14 +300,10 @@ done # Phases of the self-hosted SubX translator. -echo hex -./ntranslate init.$OS $(enumerate/enumerate --until 070---hex.subx |grep '\.subx$') -diff apps/hex a.elf - -for app in survey pack assort dquotes tests sigils calls braces +for app in hex survey pack assort dquotes tests sigils calls braces do echo $app - ./ntranslate init.$OS 0*.subx apps/subx-common.subx apps/$app.subx + ./ntranslate init.$OS 0*.subx apps/subx-params.subx apps/$app.subx diff apps/$app a.elf done diff --git a/test_layers b/test_layers index ea7551df..522e413b 100755 --- a/test_layers +++ b/test_layers @@ -22,11 +22,11 @@ for f in [0-9]*.subx do echo "=== $f" ./subx translate init.linux $(enumerate/enumerate --until $f |grep '\.subx$') -o a.elf - ./subx run a.elf + ./subx run a.elf test echo test `uname` = 'Linux' && { chmod +x a.elf - ./a.elf + ./a.elf test echo } || true done -- cgit 1.4.1-2-gfad0