From 33e7c3a75196fbf8bd8412eda797cfa170eb189d Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Thu, 6 Dec 2018 09:27:26 -0800 Subject: 4846 Clean up a few things: a) Call scan-next-byte in hex.subx with the right number of args. Turns out tests continue to work fine if they never use the other args. b) Tear down a test for 'stop' in the right order. Not important since we have no EBP to restore. But can still be misleading. c) Have 'check-ints-equal' return nothing. Handy for it to not mess up EAX. I never use the result anyway, and the name also is imperative suggesting callers won't expect a return value. --- html/subx/apps/hex.subx.html | 1902 ++++++++++++++++++++++++------------------ 1 file changed, 1082 insertions(+), 820 deletions(-) (limited to 'html/subx/apps/hex.subx.html') diff --git a/html/subx/apps/hex.subx.html b/html/subx/apps/hex.subx.html index f0accae1..d81e587f 100644 --- a/html/subx/apps/hex.subx.html +++ b/html/subx/apps/hex.subx.html @@ -25,6 +25,7 @@ body { font-size:12pt; font-family: monospace; color: #aaaaaa; background-color: .CommentedCode { color: #6c6c6c; } .Constant { color:#00a0a0; } .subxFunction { color: #ff8700; } +.subxMinorFunction { color: #875f5f; } .subxTest { color: #00af00; } .SpecialChar { color: #ff0000; } .subxH1Comment { color:#00ffff; } @@ -63,826 +64,1087 @@ if ('onhashchange' in window) { https://github.com/akkartik/mu/blob/master/subx/apps/hex.subx
-  1 # Read a text file containing whitespace-separated ascii hex bytes from stdin,
-  2 # and convert them into a binary file.
-  3 #
-  4 # To run (from the subx/ directory):
-  5 #   $ ./subx translate *.subx apps/hex.subx -o apps/hex
-  6 #   $ echo '80 81 82  # comment'  |./subx run apps/hex  |xxd -
-  7 # Expected output:
-  8 #   09000000: 8081 82
-  9 #
- 10 # Only hex bytes and comments are permitted. Outside of comments all words
- 11 # must be exactly 2 characters long and contain only characters [0-9a-f]. No
- 12 # uppercase hex.
- 13 
- 14 == code
- 15 #   instruction                     effective address                                                   register    displacement    immediate
- 16 # . op          subop               mod             rm32          base        index         scale       r32
- 17 # . 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
- 18 
- 19     # for debugging: run a single test
- 20 #?     e8/call test-skip-until-newline/disp32
- 21 #?     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
- 22 #?     eb/jump  $main:end/disp8
- 23 
- 24 # main: run tests if necessary, convert stdin if not
- 25     # . prolog
- 26     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 27     # - if argc > 1 and argv[1] == "test" then return run_tests()
- 28     # . argc > 1
- 29     81          7/subop/compare     1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0/disp8         1/imm32           # compare *EBP
- 30     7e/jump-if-lesser-or-equal  $run-main/disp8
- 31     # . argv[1] == "test"
- 32     # . . push args
- 33     68/push  "test"/imm32
- 34     ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0x8/disp8       .                 # push *(EBP+8)
- 35     # . . call
- 36     e8/call  kernel-string-equal/disp32
- 37     # . . discard args
- 38     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 39     # . check result
- 40     3d/compare-EAX  1/imm32
- 41     75/jump-if-not-equal  $run-main/disp8
- 42     # . run-tests()
- 43 #?     e8/call test-hex-below-0/disp32
- 44 #?     e8/call test-scan-next-byte/disp32
- 45 #?     e8/call test-scan-next-byte-skips-comment/disp32
- 46     e8/call  run-tests/disp32
- 47     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
- 48     eb/jump  $main:end/disp8
- 49 $run-main:
- 50     # - otherwise convert stdin
- 51     # var ed/EAX : exit-descriptor
- 52     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
- 53     8d/copy-address                 0/mod/indirect  4/rm32/sib    4/base/ESP  4/index/none  .           0/r32/EAX   .               .                 # copy ESP to EAX
- 54     # configure ed to really exit()
- 55     # . ed->target = 0
- 56     c7/copy                         0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # copy to *EAX
- 57     # return convert(Stdin, 1/stdout, 2/stderr, ed)
- 58     # . . push args
- 59     50/push-EAX/ed
- 60     68/push  2/imm32/stderr
- 61     68/push  1/imm32/stdout
- 62     68/push  Stdin/imm32
- 63     # . . call
- 64     e8/call  convert/disp32
- 65     # . . discard args
- 66     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
- 67     # . syscall(exit, 0)
- 68     bb/copy-to-EBX  0/imm32
- 69 $main:end:
- 70     b8/copy-to-EAX  1/imm32/exit
- 71     cd/syscall  0x80/imm8
- 72 
- 73 # the main entry point
- 74 convert:  # in : (address buffered-file), out : (address buffered-file), err : (address buffered-file), ed : (address exit-descriptor) -> <void>
- 75     # pseudocode:
- 76     #   repeatedly
- 77     #     EAX = convert-next-hex-byte(in, err, ed)
- 78     #     if EAX == 0xffffffff break  # eof
- 79     #     write-byte(out, EAX)
- 80     #   flush(out)
- 81     #
- 82     # . prolog
- 83     55/push-EBP
- 84     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 85     # . save registers
- 86     # var buf/ECX : (address stream) on the stack
- 87     # It occupies 1KB.
- 88     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x400/imm32       # subtract from ESP
- 89     8d/copy-address                 0/mod/indirect  4/rm32/sib    4/base/ESP  4/index/none  .           1/r32/ECX   .               .                 # copy ESP to ECX
- 90     # initialize the stream
- 91     # . length = 1KB - 12 bytes for 'read', 'write' and 'length' fields.
- 92     c7/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           .           8/disp8         0x3f4/imm32       # copy to *(ECX+8)
- 93     # . clear-stream(buf)
- 94     # . . push args
- 95     51/push-ECX
- 96     # . . call
- 97     e8/call  clear-stream/disp32
- 98     # . . discard args
- 99     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-100 $convert:loop:
-101     # . restore registers
-102     # . epilog
-103     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-104     5d/pop-to-EBP
-105     c3/return
-106 
-107 # read bytes from 'in' until a sequence of two lowercase hex characters (0-9, a-f)
-108 # skip spaces and newlines
-109 # on '#' skip bytes until newline
-110 # raise an error and abort on all other unexpected bytes
-111 # return the binary value of the two hex characters in EAX
-112 # return 0xffffffff on end of file
-113 convert-next-hex-byte:  # in : (address buffered-file), err : (address buffered-file), ed : (address exit-descriptor) -> byte-or-eof/EAX
-114     # pseudocode:
-115     #   EAX = scan-next-byte(in, err, ed)
-116     #   if (EAX == 0xffffffff) return
-117     #   ECX = EAX
-118     #   EAX = scan-next-byte(in, err, ed)
-119     #   if (EAX == 0xffffffff) error("partial byte found")
-120     #   ECX = (ECX << 8) | EAX
-121     #   return
-122     #
-123     # . prolog
-124     55/push-EBP
-125     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-126     # . save registers
-127     # var buf/ECX : (address stream) on the stack
-128     # It occupies 1KB.
-129     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x400/imm32       # subtract from ESP
-130     8d/copy-address                 0/mod/indirect  4/rm32/sib    4/base/ESP  4/index/none  .           1/r32/ECX   .               .                 # copy ESP to ECX
-131     # initialize the stream
-132     # . length = 1KB - 12 bytes for 'read', 'write' and 'length' fields.
-133     c7/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           .           8/disp8         0x3f4/imm32       # copy to *(ECX+8)
-134     # . clear-stream(buf)
-135     # . . push args
-136     51/push-ECX
-137     # . . call
-138     e8/call  clear-stream/disp32
-139     # . . discard args
-140     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-141 $convert:loop:
-142     # . restore registers
-143     # . epilog
-144     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-145     5d/pop-to-EBP
-146     c3/return
-147 
-148 # read whitespace until a hex byte, and return it
-149 # return 0xffffffff if file ends without finding a hex byte
-150 # on '#' skip all bytes until newline
-151 # abort on any other byte
-152 scan-next-byte:  # in : (address buffered-file), err : (address buffered-file), ed : (address exit-descriptor) -> byte-or-eof/EAX
-153     # pseudocode:
-154     #   repeatedly
-155     #     EAX = read-byte(in)
-156     #     if is-hex-lowercase-byte?(EAX) return EAX
-157     #     if EAX == 0x20 continue
-158     #     if EAX == '#' skip-until-newline(in)
-159     #     else error-byte(ed, err, "unexpected byte: " EAX)
-160     #   return 0xffffffff
-161     #
-162     # . prolog
-163     55/push-EBP
-164     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-165     # . save registers
-166 $scan-next-byte:loop:
-167     # EAX = read-byte(in)
-168     # . . push args
-169     ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0x8/disp8       .                 # push *(EBP+8)
-170     # . . call
-171     e8/call  read-byte/disp32
-172     # . . discard args
-173     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-174     # if is-hex-lowercase-byte?(EAX) return EAX
-175     # . save EAX for now
-176     50/push-EAX
-177     # . is-hex-lowercase-byte?(EAX)
-178     # . . push args
-179     50/push-EAX
-180     # . . call
-181     e8/call  is-hex-lowercase-byte?/disp32
-182     # . . discard args
-183     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-184     # . compare with 'false'
-185     81          7/subop/compare     3/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # compare EAX
-186     # . restore EAX (does not affect flags)
-187     58/pop-to-EAX
-188     # . check whether to return
-189     75/jump-if-not-equal  $scan-next-byte:end/disp8
-190 $scan-next-byte:check1:
-191     # if EAX == ' ' continue
-192     81          7/subop/compare     3/mod/direct    0/rm32/EAX    .           .             .           .           .               0x20/imm32        # compare EAX
-193     74/jump-if-equal  $scan-next-byte:loop/disp8
-194     # if EAX == '\t' continue
-195     81          7/subop/compare     3/mod/direct    0/rm32/EAX    .           .             .           .           .               0x9/imm32         # compare EAX
-196     74/jump-if-equal  $scan-next-byte:loop/disp8
-197     # if EAX == '\n' continue
-198     81          7/subop/compare     3/mod/direct    0/rm32/EAX    .           .             .           .           .               0xa/imm32         # compare EAX
-199     74/jump-if-equal  $scan-next-byte:loop/disp8
-200 $scan-next-byte:check2:
-201     # if EAX == '#' skip-until-newline(in)
-202     3d/compare-with-EAX  0x23/imm32
-203     75/jump-if-not-equal  $scan-next-byte:check3/disp8
-204     # . skip-until-newline(in)
-205     # . . push args
-206     ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0x8/disp8       .                 # push *(EBP+8)
-207     # . . call
-208     e8/call  skip-until-newline/disp32
-209     # . . discard args
-210     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-211     eb/jump  $scan-next-byte:loop/disp8
-212 $scan-next-byte:check3:
-213 # TODO: error-byte takes a buffered-file, not a (fd or (address stream))
-214     # otherwise error-byte(ed, err, msg, EAX)
-215     # . . push args
-216     50/push-EAX
-217     68/push  "scan-next-byte: invalid byte"/imm32
-218     ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0xc/disp8       .                 # push *(EBP+12)
-219     ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0x10/disp8      .                 # push *(EBP+16)
-220     # . . call
-221     e8/call  error-byte/disp32  # never returns
-222 $scan-next-byte:end:
-223     # . restore registers
-224     # . epilog
-225     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-226     5d/pop-to-EBP
-227     c3/return
-228 
-229 test-scan-next-byte:
-230     # - check that the first two bytes of the input are assembled into the resulting number
-231     # setup
-232     # . clear-stream(_test-stream)
-233     # . . push args
-234     68/push  _test-stream/imm32
-235     # . . call
-236     e8/call  clear-stream/disp32
-237     # . . discard args
-238     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-239     # . clear-stream(_test-buffered-file+4)
-240     # . . push args
-241     b8/copy-to-EAX  _test-buffered-file/imm32
-242     05/add-to-EAX  4/imm32
-243     50/push-EAX
-244     # . . call
-245     e8/call  clear-stream/disp32
-246     # . . discard args
-247     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-248     # initialize '_test-stream' to "abc"
-249     # . write(_test-stream, "abc")
-250     # . . push args
-251     68/push  "abc"/imm32
-252     68/push  _test-stream/imm32
-253     # . . call
-254     e8/call  write/disp32
-255     # . . discard args
-256     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-257     # scan-next-byte(_test-buffered-file)
-258     # . . push args
-259     68/push  _test-buffered-file/imm32
-260     # . . call
-261     e8/call  scan-next-byte/disp32
-262     # . . discard args
-263     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-264     # check-ints-equal(EAX, 0x61/a, msg)
-265     # . . push args
-266     68/push  "F - test-scan-next-byte"/imm32
-267     68/push  0x61/imm32/a
-268     50/push-EAX
-269     # . . call
-270     e8/call  check-ints-equal/disp32
-271     # . . discard args
-272     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-273     # . end
-274     c3/return
-275 
-276 test-scan-next-byte-skips-whitespace:
-277     # - check that the first two bytes of the input are assembled into the resulting number
-278     # setup
-279     # . clear-stream(_test-stream)
-280     # . . push args
-281     68/push  _test-stream/imm32
-282     # . . call
-283     e8/call  clear-stream/disp32
-284     # . . discard args
-285     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-286     # . clear-stream(_test-buffered-file+4)
-287     # . . push args
-288     b8/copy-to-EAX  _test-buffered-file/imm32
-289     05/add-to-EAX  4/imm32
-290     50/push-EAX
-291     # . . call
-292     e8/call  clear-stream/disp32
-293     # . . discard args
-294     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-295     # initialize '_test-stream' to input with leading whitespace
-296     # . write(_test-stream, text)
-297     # . . push args
-298     68/push  "  abc"/imm32
-299     68/push  _test-stream/imm32
-300     # . . call
-301     e8/call  write/disp32
-302     # . . discard args
-303     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-304     # scan-next-byte(_test-buffered-file)
-305     # . . push args
-306     68/push  _test-buffered-file/imm32
-307     # . . call
-308     e8/call  scan-next-byte/disp32
-309     # . . discard args
-310     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-311     # check-ints-equal(EAX, 0x61/a, msg)
-312     # . . push args
-313     68/push  "F - test-scan-next-byte-skips-whitespace"/imm32
-314     68/push  0x61/imm32/a
-315     50/push-EAX
-316     # . . call
-317     e8/call  check-ints-equal/disp32
-318     # . . discard args
-319     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-320     # . end
-321     c3/return
-322 
-323 test-scan-next-byte-skips-comment:
-324     # - check that the first two bytes of the input are assembled into the resulting number
-325     # setup
-326     # . clear-stream(_test-stream)
-327     # . . push args
-328     68/push  _test-stream/imm32
-329     # . . call
-330     e8/call  clear-stream/disp32
-331     # . . discard args
-332     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-333     # . clear-stream(_test-buffered-file+4)
-334     # . . push args
-335     b8/copy-to-EAX  _test-buffered-file/imm32
-336     05/add-to-EAX  4/imm32
-337     50/push-EAX
-338     # . . call
-339     e8/call  clear-stream/disp32
-340     # . . discard args
-341     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-342     # initialize '_test-stream' to input with leading comment
-343     # . write(_test-stream, comment)
-344     # . . push args
-345     68/push  "#x"/imm32
-346     68/push  _test-stream/imm32
-347     # . . call
-348     e8/call  write/disp32
-349     # . . discard args
-350     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-351     # . write(_test-stream, Newline)
-352     # . . push args
-353     68/push  Newline/imm32
-354     68/push  _test-stream/imm32
-355     # . . call
-356     e8/call  write/disp32
-357     # . . discard args
-358     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-359     # . write(_test-stream, real text)
-360     # . . push args
-361     68/push  "ab"/imm32
-362     68/push  _test-stream/imm32
-363     # . . call
-364     e8/call  write/disp32
-365     # . . discard args
-366     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-367     # scan-next-byte(_test-buffered-file)
-368     # . . push args
-369     68/push  _test-buffered-file/imm32
-370     # . . call
-371     e8/call  scan-next-byte/disp32
-372     # . . discard args
-373     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-374     # check-ints-equal(EAX, 0x61/a, msg)
-375     # . . push args
-376     68/push  "F - test-scan-next-byte-skips-comment"/imm32
-377     68/push  0x61/imm32/a
-378     50/push-EAX
-379     # . . call
-380     e8/call  check-ints-equal/disp32
-381     # . . discard args
-382     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-383     # . end
-384     c3/return
-385 
-386 test-scan-next-byte-skips-comment-and-whitespace:
-387     # - check that the first two bytes of the input are assembled into the resulting number
-388     # setup
-389     # . clear-stream(_test-stream)
-390     # . . push args
-391     68/push  _test-stream/imm32
-392     # . . call
-393     e8/call  clear-stream/disp32
-394     # . . discard args
-395     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-396     # . clear-stream(_test-buffered-file+4)
-397     # . . push args
-398     b8/copy-to-EAX  _test-buffered-file/imm32
-399     05/add-to-EAX  4/imm32
-400     50/push-EAX
-401     # . . call
-402     e8/call  clear-stream/disp32
-403     # . . discard args
-404     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-405     # initialize '_test-stream' to input with leading comment and more whitespace after newline
-406     # . write(_test-stream, comment)
-407     # . . push args
-408     68/push  "#x"/imm32
-409     68/push  _test-stream/imm32
-410     # . . call
-411     e8/call  write/disp32
-412     # . . discard args
-413     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-414     # . write(_test-stream, Newline)
-415     # . . push args
-416     68/push  Newline/imm32
-417     68/push  _test-stream/imm32
-418     # . . call
-419     e8/call  write/disp32
-420     # . . discard args
-421     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-422     # . write(_test-stream, real text)
-423     # . . push args
-424     68/push  " ab"/imm32
-425     68/push  _test-stream/imm32
-426     # . . call
-427     e8/call  write/disp32
-428     # . . discard args
-429     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-430     # scan-next-byte(_test-buffered-file)
-431     # . . push args
-432     68/push  _test-buffered-file/imm32
-433     # . . call
-434     e8/call  scan-next-byte/disp32
-435     # . . discard args
-436     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-437     # check-ints-equal(EAX, 0x61/a, msg)
-438     # . . push args
-439     68/push  "F - test-scan-next-byte-skips-comment"/imm32
-440     68/push  0x61/imm32/a
-441     50/push-EAX
-442     # . . call
-443     e8/call  check-ints-equal/disp32
-444     # . . discard args
-445     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-446     # . end
-447     c3/return
-448 
-449 test-scan-next-byte-skips-whitespace-and-comment:
-450     # - check that the first two bytes of the input are assembled into the resulting number
-451     # setup
-452     # . clear-stream(_test-stream)
-453     # . . push args
-454     68/push  _test-stream/imm32
-455     # . . call
-456     e8/call  clear-stream/disp32
-457     # . . discard args
-458     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-459     # . clear-stream(_test-buffered-file+4)
-460     # . . push args
-461     b8/copy-to-EAX  _test-buffered-file/imm32
-462     05/add-to-EAX  4/imm32
-463     50/push-EAX
-464     # . . call
-465     e8/call  clear-stream/disp32
-466     # . . discard args
-467     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-468     # initialize '_test-stream' to input with leading whitespace and comment
-469     # . write(_test-stream, comment)
-470     # . . push args
-471     68/push  " #x"/imm32
-472     68/push  _test-stream/imm32
-473     # . . call
-474     e8/call  write/disp32
-475     # . . discard args
-476     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-477     # . write(_test-stream, Newline)
-478     # . . push args
-479     68/push  Newline/imm32
-480     68/push  _test-stream/imm32
-481     # . . call
-482     e8/call  write/disp32
-483     # . . discard args
-484     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-485     # . write(_test-stream, real text)
-486     # . . push args
-487     68/push  "ab"/imm32
-488     68/push  _test-stream/imm32
-489     # . . call
-490     e8/call  write/disp32
-491     # . . discard args
-492     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-493     # scan-next-byte(_test-buffered-file)
-494     # . . push args
-495     68/push  _test-buffered-file/imm32
-496     # . . call
-497     e8/call  scan-next-byte/disp32
-498     # . . discard args
-499     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-500     # check-ints-equal(EAX, 0x61/a, msg)
-501     # . . push args
-502     68/push  "F - test-scan-next-byte-skips-comment"/imm32
-503     68/push  0x61/imm32/a
-504     50/push-EAX
-505     # . . call
-506     e8/call  check-ints-equal/disp32
-507     # . . discard args
-508     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-509     # . end
-510     c3/return
-511 
-512 test-scan-next-byte-reads-final-byte:
-513     # - check that the first two bytes of the input are assembled into the resulting number
-514     # setup
-515     # . clear-stream(_test-stream)
-516     # . . push args
-517     68/push  _test-stream/imm32
-518     # . . call
-519     e8/call  clear-stream/disp32
-520     # . . discard args
-521     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-522     # . clear-stream(_test-buffered-file+4)
-523     # . . push args
-524     b8/copy-to-EAX  _test-buffered-file/imm32
-525     05/add-to-EAX  4/imm32
-526     50/push-EAX
-527     # . . call
-528     e8/call  clear-stream/disp32
-529     # . . discard args
-530     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-531     # initialize '_test-stream' to input with single character
-532     # . write(_test-stream, character)
-533     # . . push args
-534     68/push  "a"/imm32
-535     68/push  _test-stream/imm32
-536     # . . call
-537     e8/call  write/disp32
-538     # . . discard args
-539     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-540     # scan-next-byte(_test-buffered-file)
-541     # . . push args
-542     68/push  _test-buffered-file/imm32
-543     # . . call
-544     e8/call  scan-next-byte/disp32
-545     # . . discard args
-546     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-547     # check-ints-equal(EAX, 0x61/a, msg)
-548     # . . push args
-549     68/push  "F - test-scan-next-byte-skips-comment"/imm32
-550     68/push  0x61/imm32/a
-551     50/push-EAX
-552     # . . call
-553     e8/call  check-ints-equal/disp32
-554     # . . discard args
-555     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-556     # . end
-557     c3/return
-558 
-559 is-hex-lowercase-byte?:  # c : byte -> bool/EAX
-560     # . prolog
-561     55/push-EBP
-562     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-563     # . save registers
-564     51/push-ECX
-565     # ECX = c
-566     8b/copy                         1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           1/r32/ECX   0x8/disp8       .                 # copy *(EBP+8) to ECX
-567     # return false if c < '0'
-568     b8/copy-to-EAX  0/imm32/false
-569     81          7/subop/compare     3/mod/direct    1/rm32/ECX    .           .             .           .           .               0x30/imm32        # compare ECX
-570     7c/jump-if-lesser  $is-hex-lowercase-byte?:end/disp8
-571     # return false if c > 'f'
-572     81          7/subop/compare     3/mod/direct    1/rm32/ECX    .           .             .           .           .               0x66/imm32        # compare ECX
-573     7f/jump-if-greater  $is-hex-lowercase-byte?:end/disp8
-574     # return true if c <= '9'
-575     b8/copy-to-EAX  1/imm32/true
-576     81          7/subop/compare     3/mod/direct    1/rm32/ECX    .           .             .           .           .               0x39/imm32        # compare ECX
-577     7e/jump-if-lesser-or-equal  $is-hex-lowercase-byte?:end/disp8
-578     # return true if c >= 'a'
-579     81          7/subop/compare     3/mod/direct    1/rm32/ECX    .           .             .           .           .               0x61/imm32        # compare ECX
-580     7d/jump-if-greater-or-equal  $is-hex-lowercase-byte?:end/disp8
-581     # otherwise return false
-582     b8/copy-to-EAX  0/imm32/false
-583 $is-hex-lowercase-byte?:end:
-584     # . restore registers
-585     59/pop-to-ECX
-586     # . epilog
-587     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-588     5d/pop-to-EBP
-589     c3/return
-590 
-591 test-hex-below-0:
-592     # is-hex-lowercase-byte?(0x2f)
-593     # . . push args
-594     68/push  0x2f/imm32
-595     # . . call
-596     e8/call  is-hex-lowercase-byte?/disp32
-597     # . . discard args
-598     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-599     # check-ints-equal(EAX, 0, msg)
-600     # . . push args
-601     68/push  "F - test-hex-below-0"/imm32
-602     68/push  0/imm32/false
-603     50/push-EAX
-604     # . . call
-605     e8/call  check-ints-equal/disp32
-606     # . . discard args
-607     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-608     c3/return
-609 
-610 test-hex-0-to-9:
-611     # is-hex-lowercase-byte?(0x30)
-612     # . . push args
-613     68/push  0x30/imm32
-614     # . . call
-615     e8/call  is-hex-lowercase-byte?/disp32
-616     # . . discard args
-617     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-618     # check-ints-equal(EAX, 1, msg)
-619     # . . push args
-620     68/push  "F - test-hex-at-0"/imm32
-621     68/push  1/imm32/true
-622     50/push-EAX
-623     # . . call
-624     e8/call  check-ints-equal/disp32
-625     # . . discard args
-626     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-627     # is-hex-lowercase-byte?(0x39)
-628     # . . push args
-629     68/push  0x39/imm32
-630     # . . call
-631     e8/call  is-hex-lowercase-byte?/disp32
-632     # . . discard args
-633     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-634     # check-ints-equal(EAX, 1, msg)
-635     # . . push args
-636     68/push  "F - test-hex-at-9"/imm32
-637     68/push  1/imm32/true
-638     50/push-EAX
-639     # . . call
-640     e8/call  check-ints-equal/disp32
-641     # . . discard args
-642     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-643     c3/return
-644 
-645 test-hex-above-9-to-a:
-646     # is-hex-lowercase-byte?(0x3a)
-647     # . . push args
-648     68/push  0x3a/imm32
-649     # . . call
-650     e8/call  is-hex-lowercase-byte?/disp32
-651     # . . discard args
-652     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-653     # check-ints-equal(EAX, 0, msg)
-654     # . . push args
-655     68/push  "F - test-hex-above-9-to-a"/imm32
-656     68/push  0/imm32/false
-657     50/push-EAX
-658     # . . call
-659     e8/call  check-ints-equal/disp32
-660     # . . discard args
-661     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-662     c3/return
-663 
-664 test-hex-a-to-f:
-665     # is-hex-lowercase-byte?(0x61)
-666     # . . push args
-667     68/push  0x61/imm32
-668     # . . call
-669     e8/call  is-hex-lowercase-byte?/disp32
-670     # . . discard args
-671     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-672     # check-ints-equal(EAX, 1, msg)
-673     # . . push args
-674     68/push  "F - test-hex-at-a"/imm32
-675     68/push  1/imm32/true
-676     50/push-EAX
-677     # . . call
-678     e8/call  check-ints-equal/disp32
-679     # . . discard args
-680     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-681     # is-hex-lowercase-byte?(0x66)
-682     # . . push args
-683     68/push  0x66/imm32
-684     # . . call
-685     e8/call  is-hex-lowercase-byte?/disp32
-686     # . . discard args
-687     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-688     # check-ints-equal(EAX, 1, msg)
-689     # . . push args
-690     68/push  "F - test-hex-at-f"/imm32
-691     68/push  1/imm32/true
-692     50/push-EAX
-693     # . . call
-694     e8/call  check-ints-equal/disp32
-695     # . . discard args
-696     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-697     c3/return
-698 
-699 test-hex-above-f:
-700     # is-hex-lowercase-byte?(0x67)
-701     # . . push args
-702     68/push  0x67/imm32
-703     # . . call
-704     e8/call  is-hex-lowercase-byte?/disp32
-705     # . . discard args
-706     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-707     # check-ints-equal(EAX, 0, msg)
-708     # . . push args
-709     68/push  "F - test-hex-above-f"/imm32
-710     68/push  0/imm32/false
-711     50/push-EAX
-712     # . . call
-713     e8/call  check-ints-equal/disp32
-714     # . . discard args
-715     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-716     c3/return
-717 
-718 skip-until-newline:  # in : (address buffered-file) -> <void>
-719     # pseudocode:
-720     #   push EAX
-721     #   repeatedly:
-722     #     EAX = read-byte(in)
-723     #     if EAX == 0xffffffff break
-724     #     if EAX == 0x0a break
-725     #   pop EAX
-726     # . prolog
-727     55/push-EBP
-728     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-729     # . save registers
-730     50/push-EAX
-731 $skip-until-newline:loop:
-732     # . EAX = read-byte(in)
-733     # . . push args
-734     ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0x8/disp8       .                 # push *(EBP+8)
-735     # . . call
-736     e8/call  read-byte/disp32
-737     # . . discard args
-738     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-739     # . if EAX == 0xffffffff break
-740     81          7/subop/compare     3/mod/direct    0/rm32/EAX    .           .             .           .           .               0xffffffff/imm32  # compare EAX
-741     74/jump-if-equal  $skip-until-newline:end/disp8
-742 $aa:
-743     # . if EAX != 0xa/newline loop
-744     81          7/subop/compare     3/mod/direct    0/rm32/EAX    .           .             .           .           .               0xa/imm32         # compare EAX
-745     75/jump-if-not-equal  $skip-until-newline:loop/disp8
-746 $skip-until-newline:end:
-747     # . restore registers
-748     58/pop-to-EAX
-749     # . epilog
-750     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-751     5d/pop-to-EBP
-752     c3/return
-753 
-754 test-skip-until-newline:
-755     # - check that the read pointer points after the newline
-756     # setup
-757     # . clear-stream(_test-stream)
-758     # . . push args
-759     68/push  _test-stream/imm32
-760     # . . call
-761     e8/call  clear-stream/disp32
-762     # . . discard args
-763     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-764     # . clear-stream(_test-buffered-file+4)
-765     # . . push args
-766     b8/copy-to-EAX  _test-buffered-file/imm32
-767     05/add-to-EAX  4/imm32
-768     50/push-EAX
-769     # . . call
-770     e8/call  clear-stream/disp32
-771     # . . discard args
-772     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-773     # initialize '_test-stream' to "abc\nde"
-774     # . write(_test-stream, "abc")
-775     # . . push args
-776     68/push  "abc"/imm32
-777     68/push  _test-stream/imm32
-778     # . . call
-779     e8/call  write/disp32
-780     # . . discard args
-781     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-782     # . write(_test-stream, Newline)
-783     # . . push args
-784     68/push  Newline/imm32
-785     68/push  _test-stream/imm32
-786     # . . call
-787     e8/call  write/disp32
-788     # . . discard args
-789     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-790     # . write(_test-stream, "de")
-791     # . . push args
-792     68/push  "de"/imm32
-793     68/push  _test-stream/imm32
-794     # . . call
-795     e8/call  write/disp32
-796     # . . discard args
-797     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-798     # skip-until-newline(_test-buffered-file)
-799     # . . push args
-800     68/push  _test-buffered-file/imm32
-801     # . . call
-802     e8/call  skip-until-newline/disp32
-803     # . . discard args
-804     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-805     # check-ints-equal(_test-buffered-file->read, 4, msg)
-806     # . . push args
-807     68/push  "F - test-skip-until-newline"/imm32
-808     68/push  4/imm32
-809     b8/copy-to-EAX  _test-buffered-file/imm32
-810     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           8/disp8         .                 # push *(EAX+8)
-811     # . . call
-812     e8/call  check-ints-equal/disp32
-813     # . . discard args
-814     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-815     # . end
-816     c3/return
-817 
-818 == data
-819 
-820 # . . vim:nowrap:textwidth=0
+   1 # Read a text file containing whitespace-separated ascii hex bytes from stdin,
+   2 # and convert them into a binary file.
+   3 #
+   4 # To run (from the subx/ directory):
+   5 #   $ ./subx translate *.subx apps/hex.subx -o apps/hex
+   6 #   $ echo '80 81 82  # comment'  |./subx run apps/hex  |xxd -
+   7 # Expected output:
+   8 #   00000000: 8081 82
+   9 #
+  10 # Only hex bytes and comments are permitted. Outside of comments all words
+  11 # must be exactly 2 characters long and contain only characters [0-9a-f]. No
+  12 # uppercase hex.
+  13 
+  14 == code
+  15 #   instruction                     effective address                                                   register    displacement    immediate
+  16 # . op          subop               mod             rm32          base        index         scale       r32
+  17 # . 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
+  18 
+  19     # for debugging: run a single test
+  20 #?     e8/call test-skip-until-newline/disp32
+  21 #?     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
+  22 #?     eb/jump  $main:end/disp8
+  23 
+  24 # main: run tests if necessary, convert stdin if not
+  25     # . prolog
+  26     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+  27     # - if argc > 1 and argv[1] == "test" then return run_tests()
+  28     # . argc > 1
+  29     81          7/subop/compare     1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0/disp8         1/imm32           # compare *EBP
+  30     7e/jump-if-lesser-or-equal  $run-main/disp8
+  31     # . argv[1] == "test"
+  32     # . . push args
+  33     68/push  "test"/imm32
+  34     ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0x8/disp8       .                 # push *(EBP+8)
+  35     # . . call
+  36     e8/call  kernel-string-equal/disp32
+  37     # . . discard args
+  38     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+  39     # . check result
+  40     3d/compare-EAX  1/imm32
+  41     75/jump-if-not-equal  $run-main/disp8
+  42     # . run-tests()
+  43 #?     e8/call test-hex-below-0/disp32
+  44 #?     e8/call test-scan-next-byte/disp32
+  45 #?     e8/call test-scan-next-byte-skips-comment/disp32
+  46     e8/call  run-tests/disp32
+  47     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
+  48     eb/jump  $main:end/disp8
+  49 $run-main:
+  50     # - otherwise convert stdin
+  51     # var ed/EAX : exit-descriptor
+  52     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
+  53     8d/copy-address                 0/mod/indirect  4/rm32/sib    4/base/ESP  4/index/none  .           0/r32/EAX   .               .                 # copy ESP to EAX
+  54     # configure ed to really exit()
+  55     # . ed->target = 0
+  56     c7/copy                         0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # copy to *EAX
+  57     # return convert(Stdin, 1/stdout, 2/stderr, ed)
+  58     # . . push args
+  59     50/push-EAX/ed
+  60     68/push  2/imm32/stderr
+  61     68/push  1/imm32/stdout
+  62     68/push  Stdin/imm32
+  63     # . . call
+  64     e8/call  convert/disp32
+  65     # . . discard args
+  66     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
+  67     # . syscall(exit, 0)
+  68     bb/copy-to-EBX  0/imm32
+  69 $main:end:
+  70     b8/copy-to-EAX  1/imm32/exit
+  71     cd/syscall  0x80/imm8
+  72 
+  73 # the main entry point
+  74 convert:  # in : (address buffered-file), out : (address buffered-file), err : (address buffered-file), ed : (address exit-descriptor) -> <void>
+  75     # . prolog
+  76     55/push-EBP
+  77     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+  78     # . save registers
+  79 $convert:end:
+  80     # . restore registers
+  81     # . epilog
+  82     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+  83     5d/pop-to-EBP
+  84     c3/return
+  85 
+  86 # read bytes from 'in' until a sequence of two lowercase hex characters (0-9, a-f)
+  87 # skip spaces and newlines
+  88 # on '#' skip bytes until newline
+  89 # raise an error and abort on all other unexpected bytes
+  90 # return the binary value of the two hex characters in EAX
+  91 # return 0xffffffff on end of file
+  92 convert-next-hex-byte:  # in : (address buffered-file), err : (address buffered-file), ed : (address exit-descriptor) -> byte-or-eof/EAX
+  93     # pseudocode:
+  94     #   EAX = scan-next-byte(in, err, ed)
+  95     #   if (EAX == 0xffffffff) return
+  96     #   ECX = EAX
+  97     #   EAX = scan-next-byte(in, err, ed)
+  98     #   if (EAX == 0xffffffff) error("partial byte found")
+  99     #   ECX = (ECX << 8) | EAX
+ 100     #   return
+ 101     #
+ 102     # . prolog
+ 103     55/push-EBP
+ 104     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 105     # . save registers
+ 106 $convert-next-hex-byte:end:
+ 107     # . restore registers
+ 108     # . epilog
+ 109     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 110     5d/pop-to-EBP
+ 111     c3/return
+ 112 
+ 113 # read whitespace until a hex byte, and return it
+ 114 # return 0xffffffff if file ends without finding a hex byte
+ 115 # on '#' skip all bytes until newline
+ 116 # abort on any other byte
+ 117 scan-next-byte:  # in : (address buffered-file), err : (address buffered-file), ed : (address exit-descriptor) -> byte-or-eof/EAX
+ 118     # pseudocode:
+ 119     #   repeatedly
+ 120     #     EAX = read-byte(in)
+ 121     #     if is-hex-lowercase-byte?(EAX) return EAX
+ 122     #     if EAX == 0x20 continue
+ 123     #     if EAX == '#' skip-until-newline(in)
+ 124     #     else error-byte(ed, err, "unexpected byte: " EAX)
+ 125     #   return 0xffffffff
+ 126     #
+ 127     # . prolog
+ 128     55/push-EBP
+ 129     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 130     # . save registers
+ 131 $scan-next-byte:loop:
+ 132     # EAX = read-byte(in)
+ 133     # . . push args
+ 134     ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0x8/disp8       .                 # push *(EBP+8)
+ 135     # . . call
+ 136     e8/call  read-byte/disp32
+ 137     # . . discard args
+ 138     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 139     # if is-hex-lowercase-byte?(EAX) return EAX
+ 140     # . save EAX for now
+ 141     50/push-EAX
+ 142     # . is-hex-lowercase-byte?(EAX)
+ 143     # . . push args
+ 144     50/push-EAX
+ 145     # . . call
+ 146     e8/call  is-hex-lowercase-byte?/disp32
+ 147     # . . discard args
+ 148     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 149     # . compare with 'false'
+ 150     3d/compare-with-EAX  0/imm32
+ 151     # . restore EAX (does not affect flags)
+ 152     58/pop-to-EAX
+ 153     # . check whether to return
+ 154     75/jump-if-not-equal  $scan-next-byte:end/disp8
+ 155 $scan-next-byte:check1:
+ 156     # if EAX == ' ' continue
+ 157     81          7/subop/compare     3/mod/direct    0/rm32/EAX    .           .             .           .           .               0x20/imm32        # compare EAX
+ 158     74/jump-if-equal  $scan-next-byte:loop/disp8
+ 159     # if EAX == '\t' continue
+ 160     81          7/subop/compare     3/mod/direct    0/rm32/EAX    .           .             .           .           .               0x9/imm32         # compare EAX
+ 161     74/jump-if-equal  $scan-next-byte:loop/disp8
+ 162     # if EAX == '\n' continue
+ 163     81          7/subop/compare     3/mod/direct    0/rm32/EAX    .           .             .           .           .               0xa/imm32         # compare EAX
+ 164     74/jump-if-equal  $scan-next-byte:loop/disp8
+ 165 $scan-next-byte:check2:
+ 166     # if EAX == '#' skip-until-newline(in)
+ 167     3d/compare-with-EAX  0x23/imm32
+ 168     75/jump-if-not-equal  $scan-next-byte:check3/disp8
+ 169     # . skip-until-newline(in)
+ 170     # . . push args
+ 171     ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0x8/disp8       .                 # push *(EBP+8)
+ 172     # . . call
+ 173     e8/call  skip-until-newline/disp32
+ 174     # . . discard args
+ 175     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 176     eb/jump  $scan-next-byte:loop/disp8
+ 177 $scan-next-byte:check3:
+ 178 # TODO: error-byte takes a buffered-file, not a (fd or (address stream))
+ 179     # otherwise error-byte(ed, err, msg, EAX)
+ 180     # . . push args
+ 181     50/push-EAX
+ 182     68/push  "scan-next-byte: invalid byte"/imm32
+ 183     ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0xc/disp8       .                 # push *(EBP+12)
+ 184     ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0x10/disp8      .                 # push *(EBP+16)
+ 185     # . . call
+ 186     e8/call  error-byte/disp32  # never returns
+ 187 $scan-next-byte:end:
+ 188     # . restore registers
+ 189     # . epilog
+ 190     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 191     5d/pop-to-EBP
+ 192     c3/return
+ 193 
+ 194 test-scan-next-byte:
+ 195     # - check that the first two bytes of the input are assembled into the resulting number
+ 196     # This test uses exit-descriptors. Use EBP for setting up local variables.
+ 197     55/push-EBP
+ 198     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 199     # clear all streams
+ 200     # . clear-stream(_test-stream)
+ 201     # . . push args
+ 202     68/push  _test-stream/imm32
+ 203     # . . call
+ 204     e8/call  clear-stream/disp32
+ 205     # . . discard args
+ 206     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 207     # . clear-stream(_test-buffered-file+4)
+ 208     # . . push args
+ 209     b8/copy-to-EAX  _test-buffered-file/imm32
+ 210     05/add-to-EAX  4/imm32
+ 211     50/push-EAX
+ 212     # . . call
+ 213     e8/call  clear-stream/disp32
+ 214     # . . discard args
+ 215     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 216     # . clear-stream(_test-error-stream)
+ 217     # . . push args
+ 218     68/push  _test-error-stream/imm32
+ 219     # . . call
+ 220     e8/call  clear-stream/disp32
+ 221     # . . discard args
+ 222     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 223     # initialize '_test-stream' to "abc"
+ 224     # . write(_test-stream, "abc")
+ 225     # . . push args
+ 226     68/push  "abc"/imm32
+ 227     68/push  _test-stream/imm32
+ 228     # . . call
+ 229     e8/call  write/disp32
+ 230     # . . discard args
+ 231     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 232     # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below
+ 233     # . var ed/ECX : exit-descriptor
+ 234     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
+ 235     8d/copy-address                 0/mod/indirect  4/rm32/sib    4/base/ESP  4/index/none  .           1/r32/ECX   .               .                 # copy ESP to ECX
+ 236     # . tailor-exit-descriptor(ed, 12)
+ 237     # . . push args
+ 238     68/push  0xc/imm32/nbytes-of-args-for-scan-next-byte
+ 239     51/push-ECX/ed
+ 240     # . . call
+ 241     e8/call  tailor-exit-descriptor/disp32
+ 242     # . . discard args
+ 243     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 244     # EAX = scan-next-byte(_test-buffered-file, _test-error-stream, ed)
+ 245     # . . push args
+ 246     51/push-ECX/ed
+ 247     68/push  _test-error-stream/imm32
+ 248     68/push  _test-buffered-file/imm32
+ 249     # . . call
+ 250     e8/call  scan-next-byte/disp32
+ 251     # registers except ESP may be clobbered at this point
+ 252     # pop args to scan-next-bytes
+ 253     # . . discard first 2 args
+ 254     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 255     # . . restore ed
+ 256     59/pop-to-ECX
+ 257     # check that scan-next-byte didn't abort
+ 258     # . check-ints-equal(ed->value, 0, msg)
+ 259     # . . push args
+ 260     68/push  "F - test-scan-next-byte: unexpected abort"/imm32
+ 261     68/push  0/imm32
+ 262     # . . push ed->value
+ 263     ff          6/subop/push        1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         .                 # push *(ECX+4)
+ 264     # . . call
+ 265     e8/call  check-ints-equal/disp32
+ 266     # . . discard args
+ 267     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 268     # return if abort
+ 269     81          7/subop/compare     1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         0/imm32           # compare *(ECX+4)
+ 270     75/jump-if-not-equal  $test-scan-next-byte:end/disp8
+ 271     # check-ints-equal(EAX, 0x61/a, msg)
+ 272     # . . push args
+ 273     68/push  "F - test-scan-next-byte"/imm32
+ 274     68/push  0x61/imm32/a
+ 275     50/push-EAX
+ 276     # . . call
+ 277     e8/call  check-ints-equal/disp32
+ 278     # . . discard args
+ 279     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 280 $test-scan-next-byte:end:
+ 281     # . epilog
+ 282     # don't restore ESP from EBP; manually reclaim locals
+ 283     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 284     5d/pop-to-EBP
+ 285     c3/return
+ 286 
+ 287 test-scan-next-byte-skips-whitespace:
+ 288     # - check that the first two bytes of the input are assembled into the resulting number
+ 289     # This test uses exit-descriptors. Use EBP for setting up local variables.
+ 290     55/push-EBP
+ 291     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 292     # clear all streams
+ 293     # . clear-stream(_test-stream)
+ 294     # . . push args
+ 295     68/push  _test-stream/imm32
+ 296     # . . call
+ 297     e8/call  clear-stream/disp32
+ 298     # . . discard args
+ 299     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 300     # . clear-stream(_test-buffered-file+4)
+ 301     # . . push args
+ 302     b8/copy-to-EAX  _test-buffered-file/imm32
+ 303     05/add-to-EAX  4/imm32
+ 304     50/push-EAX
+ 305     # . . call
+ 306     e8/call  clear-stream/disp32
+ 307     # . . discard args
+ 308     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 309     # . clear-stream(_test-error-stream)
+ 310     # . . push args
+ 311     68/push  _test-error-stream/imm32
+ 312     # . . call
+ 313     e8/call  clear-stream/disp32
+ 314     # . . discard args
+ 315     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 316     # initialize '_test-stream' to input with leading whitespace
+ 317     # . write(_test-stream, text)
+ 318     # . . push args
+ 319     68/push  "  abc"/imm32
+ 320     68/push  _test-stream/imm32
+ 321     # . . call
+ 322     e8/call  write/disp32
+ 323     # . . discard args
+ 324     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 325     # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below
+ 326     # . var ed/ECX : exit-descriptor
+ 327     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
+ 328     8d/copy-address                 0/mod/indirect  4/rm32/sib    4/base/ESP  4/index/none  .           1/r32/ECX   .               .                 # copy ESP to ECX
+ 329     # . tailor-exit-descriptor(ed, 12)
+ 330     # . . push args
+ 331     68/push  0xc/imm32/nbytes-of-args-for-scan-next-byte
+ 332     51/push-ECX/ed
+ 333     # . . call
+ 334     e8/call  tailor-exit-descriptor/disp32
+ 335     # . . discard args
+ 336     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 337     # EAX = scan-next-byte(_test-buffered-file, _test-error-stream, ed)
+ 338     # . . push args
+ 339     51/push-ECX/ed
+ 340     68/push  _test-error-stream/imm32
+ 341     68/push  _test-buffered-file/imm32
+ 342     # . . call
+ 343     e8/call  scan-next-byte/disp32
+ 344     # registers except ESP may be clobbered at this point
+ 345     # pop args to scan-next-bytes
+ 346     # . . discard first 2 args
+ 347     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 348     # . . restore ed
+ 349     59/pop-to-ECX
+ 350     # check that scan-next-byte didn't abort
+ 351     # . check-ints-equal(ed->value, 0, msg)
+ 352     # . . push args
+ 353     68/push  "F - test-scan-next-byte-skips-whitespace: unexpected abort"/imm32
+ 354     68/push  0/imm32
+ 355     # . . push ed->value
+ 356     ff          6/subop/push        1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         .                 # push *(ECX+4)
+ 357     # . . call
+ 358     e8/call  check-ints-equal/disp32
+ 359     # . . discard args
+ 360     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 361     # return if abort
+ 362     81          7/subop/compare     1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         0/imm32           # compare *(ECX+4)
+ 363     75/jump-if-not-equal  $test-scan-next-byte-skips-whitespace:end/disp8
+ 364     # check-ints-equal(EAX, 0x61/a, msg)
+ 365     # . . push args
+ 366     68/push  "F - test-scan-next-byte-skips-whitespace"/imm32
+ 367     68/push  0x61/imm32/a
+ 368     50/push-EAX
+ 369     # . . call
+ 370     e8/call  check-ints-equal/disp32
+ 371     # . . discard args
+ 372     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 373 $test-scan-next-byte-skips-whitespace:end:
+ 374     # . epilog
+ 375     # don't restore ESP from EBP; manually reclaim locals
+ 376     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 377     5d/pop-to-EBP
+ 378     c3/return
+ 379 
+ 380 test-scan-next-byte-skips-comment:
+ 381     # - check that the first two bytes of the input are assembled into the resulting number
+ 382     # This test uses exit-descriptors. Use EBP for setting up local variables.
+ 383     55/push-EBP
+ 384     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 385     # clear all streams
+ 386     # . clear-stream(_test-stream)
+ 387     # . . push args
+ 388     68/push  _test-stream/imm32
+ 389     # . . call
+ 390     e8/call  clear-stream/disp32
+ 391     # . . discard args
+ 392     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 393     # . clear-stream(_test-buffered-file+4)
+ 394     # . . push args
+ 395     b8/copy-to-EAX  _test-buffered-file/imm32
+ 396     05/add-to-EAX  4/imm32
+ 397     50/push-EAX
+ 398     # . . call
+ 399     e8/call  clear-stream/disp32
+ 400     # . . discard args
+ 401     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 402     # . clear-stream(_test-error-stream)
+ 403     # . . push args
+ 404     68/push  _test-error-stream/imm32
+ 405     # . . call
+ 406     e8/call  clear-stream/disp32
+ 407     # . . discard args
+ 408     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 409     # initialize '_test-stream' to input with leading comment
+ 410     # . write(_test-stream, comment)
+ 411     # . . push args
+ 412     68/push  "#x"/imm32
+ 413     68/push  _test-stream/imm32
+ 414     # . . call
+ 415     e8/call  write/disp32
+ 416     # . . discard args
+ 417     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 418     # . write(_test-stream, Newline)
+ 419     # . . push args
+ 420     68/push  Newline/imm32
+ 421     68/push  _test-stream/imm32
+ 422     # . . call
+ 423     e8/call  write/disp32
+ 424     # . . discard args
+ 425     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 426     # . write(_test-stream, real text)
+ 427     # . . push args
+ 428     68/push  "ab"/imm32
+ 429     68/push  _test-stream/imm32
+ 430     # . . call
+ 431     e8/call  write/disp32
+ 432     # . . discard args
+ 433     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 434     # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below
+ 435     # . var ed/ECX : exit-descriptor
+ 436     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
+ 437     8d/copy-address                 0/mod/indirect  4/rm32/sib    4/base/ESP  4/index/none  .           1/r32/ECX   .               .                 # copy ESP to ECX
+ 438     # . tailor-exit-descriptor(ed, 12)
+ 439     # . . push args
+ 440     68/push  0xc/imm32/nbytes-of-args-for-scan-next-byte
+ 441     51/push-ECX/ed
+ 442     # . . call
+ 443     e8/call  tailor-exit-descriptor/disp32
+ 444     # . . discard args
+ 445     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 446     # EAX = scan-next-byte(_test-buffered-file, _test-error-stream, ed)
+ 447     # . . push args
+ 448     51/push-ECX/ed
+ 449     68/push  _test-error-stream/imm32
+ 450     68/push  _test-buffered-file/imm32
+ 451     # . . call
+ 452     e8/call  scan-next-byte/disp32
+ 453     # registers except ESP may be clobbered at this point
+ 454     # pop args to scan-next-bytes
+ 455     # . . discard first 2 args
+ 456     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 457     # . . restore ed
+ 458     59/pop-to-ECX
+ 459     # check that scan-next-byte didn't abort
+ 460     # . check-ints-equal(ed->value, 0, msg)
+ 461     # . . push args
+ 462     68/push  "F - test-scan-next-byte-skips-comment: unexpected abort"/imm32
+ 463     68/push  0/imm32
+ 464     # . . push ed->value
+ 465     ff          6/subop/push        1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         .                 # push *(ECX+4)
+ 466     # . . call
+ 467     e8/call  check-ints-equal/disp32
+ 468     # . . discard args
+ 469     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 470     # return if abort
+ 471     81          7/subop/compare     1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         0/imm32           # compare *(ECX+4)
+ 472     75/jump-if-not-equal  $test-scan-next-byte-skips-comment:end/disp8
+ 473     # check-ints-equal(EAX, 0x61/a, msg)
+ 474     # . . push args
+ 475     68/push  "F - test-scan-next-byte-skips-comment"/imm32
+ 476     68/push  0x61/imm32/a
+ 477     50/push-EAX
+ 478     # . . call
+ 479     e8/call  check-ints-equal/disp32
+ 480     # . . discard args
+ 481     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 482 $test-scan-next-byte-skips-comment:end:
+ 483     # . epilog
+ 484     # don't restore ESP from EBP; manually reclaim locals
+ 485     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 486     5d/pop-to-EBP
+ 487     c3/return
+ 488 
+ 489 test-scan-next-byte-skips-comment-and-whitespace:
+ 490     # - check that the first two bytes of the input are assembled into the resulting number
+ 491     # This test uses exit-descriptors. Use EBP for setting up local variables.
+ 492     55/push-EBP
+ 493     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 494     # clear all streams
+ 495     # . clear-stream(_test-stream)
+ 496     # . . push args
+ 497     68/push  _test-stream/imm32
+ 498     # . . call
+ 499     e8/call  clear-stream/disp32
+ 500     # . . discard args
+ 501     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 502     # . clear-stream(_test-buffered-file+4)
+ 503     # . . push args
+ 504     b8/copy-to-EAX  _test-buffered-file/imm32
+ 505     05/add-to-EAX  4/imm32
+ 506     50/push-EAX
+ 507     # . . call
+ 508     e8/call  clear-stream/disp32
+ 509     # . . discard args
+ 510     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 511     # . clear-stream(_test-error-stream)
+ 512     # . . push args
+ 513     68/push  _test-error-stream/imm32
+ 514     # . . call
+ 515     e8/call  clear-stream/disp32
+ 516     # . . discard args
+ 517     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 518     # initialize '_test-stream' to input with leading comment and more whitespace after newline
+ 519     # . write(_test-stream, comment)
+ 520     # . . push args
+ 521     68/push  "#x"/imm32
+ 522     68/push  _test-stream/imm32
+ 523     # . . call
+ 524     e8/call  write/disp32
+ 525     # . . discard args
+ 526     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 527     # . write(_test-stream, Newline)
+ 528     # . . push args
+ 529     68/push  Newline/imm32
+ 530     68/push  _test-stream/imm32
+ 531     # . . call
+ 532     e8/call  write/disp32
+ 533     # . . discard args
+ 534     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 535     # . write(_test-stream, real text)
+ 536     # . . push args
+ 537     68/push  " ab"/imm32
+ 538     68/push  _test-stream/imm32
+ 539     # . . call
+ 540     e8/call  write/disp32
+ 541     # . . discard args
+ 542     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 543     # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below
+ 544     # . var ed/ECX : exit-descriptor
+ 545     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
+ 546     8d/copy-address                 0/mod/indirect  4/rm32/sib    4/base/ESP  4/index/none  .           1/r32/ECX   .               .                 # copy ESP to ECX
+ 547     # . tailor-exit-descriptor(ed, 12)
+ 548     # . . push args
+ 549     68/push  0xc/imm32/nbytes-of-args-for-scan-next-byte
+ 550     51/push-ECX/ed
+ 551     # . . call
+ 552     e8/call  tailor-exit-descriptor/disp32
+ 553     # . . discard args
+ 554     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 555     # EAX = scan-next-byte(_test-buffered-file, _test-error-stream, ed)
+ 556     # . . push args
+ 557     51/push-ECX/ed
+ 558     68/push  _test-error-stream/imm32
+ 559     68/push  _test-buffered-file/imm32
+ 560     # . . call
+ 561     e8/call  scan-next-byte/disp32
+ 562     # registers except ESP may be clobbered at this point
+ 563     # pop args to scan-next-bytes
+ 564     # . . discard first 2 args
+ 565     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 566     # . . restore ed
+ 567     59/pop-to-ECX
+ 568     # check that scan-next-byte didn't abort
+ 569     # . check-ints-equal(ed->value, 0, msg)
+ 570     # . . push args
+ 571     68/push  "F - test-scan-next-byte-skips-comment-and-whitespace: unexpected abort"/imm32
+ 572     68/push  0/imm32
+ 573     # . . push ed->value
+ 574     ff          6/subop/push        1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         .                 # push *(ECX+4)
+ 575     # . . call
+ 576     e8/call  check-ints-equal/disp32
+ 577     # . . discard args
+ 578     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 579     # return if abort
+ 580     81          7/subop/compare     1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         0/imm32           # compare *(ECX+4)
+ 581     75/jump-if-not-equal  $test-scan-next-byte-skips-comment-and-whitespace:end/disp8
+ 582     # check-ints-equal(EAX, 0x61/a, msg)
+ 583     # . . push args
+ 584     68/push  "F - test-scan-next-byte-skips-comment-and-whitespace"/imm32
+ 585     68/push  0x61/imm32/a
+ 586     50/push-EAX
+ 587     # . . call
+ 588     e8/call  check-ints-equal/disp32
+ 589     # . . discard args
+ 590     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 591 $test-scan-next-byte-skips-comment-and-whitespace:end:
+ 592     # . epilog
+ 593     # don't restore ESP from EBP; manually reclaim locals
+ 594     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 595     5d/pop-to-EBP
+ 596     c3/return
+ 597 
+ 598 test-scan-next-byte-skips-whitespace-and-comment:
+ 599     # - check that the first two bytes of the input are assembled into the resulting number
+ 600     # This test uses exit-descriptors. Use EBP for setting up local variables.
+ 601     55/push-EBP
+ 602     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 603     # clear all streams
+ 604     # . clear-stream(_test-stream)
+ 605     # . . push args
+ 606     68/push  _test-stream/imm32
+ 607     # . . call
+ 608     e8/call  clear-stream/disp32
+ 609     # . . discard args
+ 610     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 611     # . clear-stream(_test-buffered-file+4)
+ 612     # . . push args
+ 613     b8/copy-to-EAX  _test-buffered-file/imm32
+ 614     05/add-to-EAX  4/imm32
+ 615     50/push-EAX
+ 616     # . . call
+ 617     e8/call  clear-stream/disp32
+ 618     # . . discard args
+ 619     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 620     # . clear-stream(_test-error-stream)
+ 621     # . . push args
+ 622     68/push  _test-error-stream/imm32
+ 623     # . . call
+ 624     e8/call  clear-stream/disp32
+ 625     # . . discard args
+ 626     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 627     # initialize '_test-stream' to input with leading whitespace and comment
+ 628     # . write(_test-stream, comment)
+ 629     # . . push args
+ 630     68/push  " #x"/imm32
+ 631     68/push  _test-stream/imm32
+ 632     # . . call
+ 633     e8/call  write/disp32
+ 634     # . . discard args
+ 635     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 636     # . write(_test-stream, Newline)
+ 637     # . . push args
+ 638     68/push  Newline/imm32
+ 639     68/push  _test-stream/imm32
+ 640     # . . call
+ 641     e8/call  write/disp32
+ 642     # . . discard args
+ 643     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 644     # . write(_test-stream, real text)
+ 645     # . . push args
+ 646     68/push  "ab"/imm32
+ 647     68/push  _test-stream/imm32
+ 648     # . . call
+ 649     e8/call  write/disp32
+ 650     # . . discard args
+ 651     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 652     # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below
+ 653     # . var ed/ECX : exit-descriptor
+ 654     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
+ 655     8d/copy-address                 0/mod/indirect  4/rm32/sib    4/base/ESP  4/index/none  .           1/r32/ECX   .               .                 # copy ESP to ECX
+ 656     # . tailor-exit-descriptor(ed, 12)
+ 657     # . . push args
+ 658     68/push  0xc/imm32/nbytes-of-args-for-scan-next-byte
+ 659     51/push-ECX/ed
+ 660     # . . call
+ 661     e8/call  tailor-exit-descriptor/disp32
+ 662     # . . discard args
+ 663     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 664     # EAX = scan-next-byte(_test-buffered-file, _test-error-stream, ed)
+ 665     # . . push args
+ 666     51/push-ECX/ed
+ 667     68/push  _test-error-stream/imm32
+ 668     68/push  _test-buffered-file/imm32
+ 669     # . . call
+ 670     e8/call  scan-next-byte/disp32
+ 671     # registers except ESP may be clobbered at this point
+ 672     # pop args to scan-next-bytes
+ 673     # . . discard first 2 args
+ 674     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 675     # . . restore ed
+ 676     59/pop-to-ECX
+ 677     # check that scan-next-byte didn't abort
+ 678     # . check-ints-equal(ed->value, 0, msg)
+ 679     # . . push args
+ 680     68/push  "F - test-scan-next-byte-skips-whitespace-and-comment: unexpected abort"/imm32
+ 681     68/push  0/imm32
+ 682     # . . push ed->value
+ 683     ff          6/subop/push        1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         .                 # push *(ECX+4)
+ 684     # . . call
+ 685     e8/call  check-ints-equal/disp32
+ 686     # . . discard args
+ 687     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 688     # return if abort
+ 689     81          7/subop/compare     1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         0/imm32           # compare *(ECX+4)
+ 690     75/jump-if-not-equal  $test-scan-next-byte-skips-whitespace-and-comment:end/disp8
+ 691     # check-ints-equal(EAX, 0x61/a, msg)
+ 692     # . . push args
+ 693     68/push  "F - test-scan-next-byte-skips-whitespace-and-comment"/imm32
+ 694     68/push  0x61/imm32/a
+ 695     50/push-EAX
+ 696     # . . call
+ 697     e8/call  check-ints-equal/disp32
+ 698     # . . discard args
+ 699     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 700 $test-scan-next-byte-skips-whitespace-and-comment:end:
+ 701     # . epilog
+ 702     # don't restore ESP from EBP; manually reclaim locals
+ 703     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 704     5d/pop-to-EBP
+ 705     c3/return
+ 706 
+ 707 test-scan-next-byte-reads-final-byte:
+ 708     # - check that the first two bytes of the input are assembled into the resulting number
+ 709     # This test uses exit-descriptors. Use EBP for setting up local variables.
+ 710     55/push-EBP
+ 711     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 712     # clear all streams
+ 713     # . clear-stream(_test-stream)
+ 714     # . . push args
+ 715     68/push  _test-stream/imm32
+ 716     # . . call
+ 717     e8/call  clear-stream/disp32
+ 718     # . . discard args
+ 719     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 720     # . clear-stream(_test-buffered-file+4)
+ 721     # . . push args
+ 722     b8/copy-to-EAX  _test-buffered-file/imm32
+ 723     05/add-to-EAX  4/imm32
+ 724     50/push-EAX
+ 725     # . . call
+ 726     e8/call  clear-stream/disp32
+ 727     # . . discard args
+ 728     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 729     # . clear-stream(_test-error-stream)
+ 730     # . . push args
+ 731     68/push  _test-error-stream/imm32
+ 732     # . . call
+ 733     e8/call  clear-stream/disp32
+ 734     # . . discard args
+ 735     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 736     # initialize '_test-stream' to input with single character
+ 737     # . write(_test-stream, character)
+ 738     # . . push args
+ 739     68/push  "a"/imm32
+ 740     68/push  _test-stream/imm32
+ 741     # . . call
+ 742     e8/call  write/disp32
+ 743     # . . discard args
+ 744     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 745     # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below
+ 746     # . var ed/ECX : exit-descriptor
+ 747     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
+ 748     8d/copy-address                 0/mod/indirect  4/rm32/sib    4/base/ESP  4/index/none  .           1/r32/ECX   .               .                 # copy ESP to ECX
+ 749     # . tailor-exit-descriptor(ed, 12)
+ 750     # . . push args
+ 751     68/push  0xc/imm32/nbytes-of-args-for-scan-next-byte
+ 752     51/push-ECX/ed
+ 753     # . . call
+ 754     e8/call  tailor-exit-descriptor/disp32
+ 755     # . . discard args
+ 756     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 757     # EAX = scan-next-byte(_test-buffered-file, _test-error-stream, ed)
+ 758     # . . push args
+ 759     51/push-ECX/ed
+ 760     68/push  _test-error-stream/imm32
+ 761     68/push  _test-buffered-file/imm32
+ 762     # . . call
+ 763     e8/call  scan-next-byte/disp32
+ 764     # registers except ESP may be clobbered at this point
+ 765     # pop args to scan-next-bytes
+ 766     # . . discard first 2 args
+ 767     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 768     # . . restore ed
+ 769     59/pop-to-ECX
+ 770     # check that scan-next-byte didn't abort
+ 771     # . check-ints-equal(ed->value, 0, msg)
+ 772     # . . push args
+ 773     68/push  "F - test-scan-next-byte-reads-final-byte: unexpected abort"/imm32
+ 774     68/push  0/imm32
+ 775     # . . push ed->value
+ 776     ff          6/subop/push        1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         .                 # push *(ECX+4)
+ 777     # . . call
+ 778     e8/call  check-ints-equal/disp32
+ 779     # . . discard args
+ 780     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 781     # return if abort
+ 782     81          7/subop/compare     1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         0/imm32           # compare *(ECX+4)
+ 783     75/jump-if-not-equal  $test-scan-next-byte-reads-final-byte:end/disp8
+ 784     # check-ints-equal(EAX, 0x61/a, msg)
+ 785     # . . push args
+ 786     68/push  "F - test-scan-next-byte-reads-final-byte"/imm32
+ 787     68/push  0x61/imm32/a
+ 788     50/push-EAX
+ 789     # . . call
+ 790     e8/call  check-ints-equal/disp32
+ 791     # . . discard args
+ 792     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 793 $test-scan-next-byte-reads-final-byte:end:
+ 794     # . epilog
+ 795     # don't restore ESP from EBP; manually reclaim locals
+ 796     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 797     5d/pop-to-EBP
+ 798     c3/return
+ 799 
+ 800 is-hex-lowercase-byte?:  # c : byte -> bool/EAX
+ 801     # . prolog
+ 802     55/push-EBP
+ 803     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 804     # . save registers
+ 805     51/push-ECX
+ 806     # ECX = c
+ 807     8b/copy                         1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           1/r32/ECX   0x8/disp8       .                 # copy *(EBP+8) to ECX
+ 808     # return false if c < '0'
+ 809     b8/copy-to-EAX  0/imm32/false
+ 810     81          7/subop/compare     3/mod/direct    1/rm32/ECX    .           .             .           .           .               0x30/imm32        # compare ECX
+ 811     7c/jump-if-lesser  $is-hex-lowercase-byte?:end/disp8
+ 812     # return false if c > 'f'
+ 813     81          7/subop/compare     3/mod/direct    1/rm32/ECX    .           .             .           .           .               0x66/imm32        # compare ECX
+ 814     7f/jump-if-greater  $is-hex-lowercase-byte?:end/disp8
+ 815     # return true if c <= '9'
+ 816     b8/copy-to-EAX  1/imm32/true
+ 817     81          7/subop/compare     3/mod/direct    1/rm32/ECX    .           .             .           .           .               0x39/imm32        # compare ECX
+ 818     7e/jump-if-lesser-or-equal  $is-hex-lowercase-byte?:end/disp8
+ 819     # return true if c >= 'a'
+ 820     81          7/subop/compare     3/mod/direct    1/rm32/ECX    .           .             .           .           .               0x61/imm32        # compare ECX
+ 821     7d/jump-if-greater-or-equal  $is-hex-lowercase-byte?:end/disp8
+ 822     # otherwise return false
+ 823     b8/copy-to-EAX  0/imm32/false
+ 824 $is-hex-lowercase-byte?:end:
+ 825     # . restore registers
+ 826     59/pop-to-ECX
+ 827     # . epilog
+ 828     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 829     5d/pop-to-EBP
+ 830     c3/return
+ 831 
+ 832 test-hex-below-0:
+ 833     # is-hex-lowercase-byte?(0x2f)
+ 834     # . . push args
+ 835     68/push  0x2f/imm32
+ 836     # . . call
+ 837     e8/call  is-hex-lowercase-byte?/disp32
+ 838     # . . discard args
+ 839     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 840     # check-ints-equal(EAX, 0, msg)
+ 841     # . . push args
+ 842     68/push  "F - test-hex-below-0"/imm32
+ 843     68/push  0/imm32/false
+ 844     50/push-EAX
+ 845     # . . call
+ 846     e8/call  check-ints-equal/disp32
+ 847     # . . discard args
+ 848     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 849     c3/return
+ 850 
+ 851 test-hex-0-to-9:
+ 852     # is-hex-lowercase-byte?(0x30)
+ 853     # . . push args
+ 854     68/push  0x30/imm32
+ 855     # . . call
+ 856     e8/call  is-hex-lowercase-byte?/disp32
+ 857     # . . discard args
+ 858     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 859     # check-ints-equal(EAX, 1, msg)
+ 860     # . . push args
+ 861     68/push  "F - test-hex-at-0"/imm32
+ 862     68/push  1/imm32/true
+ 863     50/push-EAX
+ 864     # . . call
+ 865     e8/call  check-ints-equal/disp32
+ 866     # . . discard args
+ 867     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 868     # is-hex-lowercase-byte?(0x39)
+ 869     # . . push args
+ 870     68/push  0x39/imm32
+ 871     # . . call
+ 872     e8/call  is-hex-lowercase-byte?/disp32
+ 873     # . . discard args
+ 874     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 875     # check-ints-equal(EAX, 1, msg)
+ 876     # . . push args
+ 877     68/push  "F - test-hex-at-9"/imm32
+ 878     68/push  1/imm32/true
+ 879     50/push-EAX
+ 880     # . . call
+ 881     e8/call  check-ints-equal/disp32
+ 882     # . . discard args
+ 883     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 884     c3/return
+ 885 
+ 886 test-hex-above-9-to-a:
+ 887     # is-hex-lowercase-byte?(0x3a)
+ 888     # . . push args
+ 889     68/push  0x3a/imm32
+ 890     # . . call
+ 891     e8/call  is-hex-lowercase-byte?/disp32
+ 892     # . . discard args
+ 893     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 894     # check-ints-equal(EAX, 0, msg)
+ 895     # . . push args
+ 896     68/push  "F - test-hex-above-9-to-a"/imm32
+ 897     68/push  0/imm32/false
+ 898     50/push-EAX
+ 899     # . . call
+ 900     e8/call  check-ints-equal/disp32
+ 901     # . . discard args
+ 902     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 903     c3/return
+ 904 
+ 905 test-hex-a-to-f:
+ 906     # is-hex-lowercase-byte?(0x61)
+ 907     # . . push args
+ 908     68/push  0x61/imm32
+ 909     # . . call
+ 910     e8/call  is-hex-lowercase-byte?/disp32
+ 911     # . . discard args
+ 912     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 913     # check-ints-equal(EAX, 1, msg)
+ 914     # . . push args
+ 915     68/push  "F - test-hex-at-a"/imm32
+ 916     68/push  1/imm32/true
+ 917     50/push-EAX
+ 918     # . . call
+ 919     e8/call  check-ints-equal/disp32
+ 920     # . . discard args
+ 921     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 922     # is-hex-lowercase-byte?(0x66)
+ 923     # . . push args
+ 924     68/push  0x66/imm32
+ 925     # . . call
+ 926     e8/call  is-hex-lowercase-byte?/disp32
+ 927     # . . discard args
+ 928     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 929     # check-ints-equal(EAX, 1, msg)
+ 930     # . . push args
+ 931     68/push  "F - test-hex-at-f"/imm32
+ 932     68/push  1/imm32/true
+ 933     50/push-EAX
+ 934     # . . call
+ 935     e8/call  check-ints-equal/disp32
+ 936     # . . discard args
+ 937     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 938     c3/return
+ 939 
+ 940 test-hex-above-f:
+ 941     # is-hex-lowercase-byte?(0x67)
+ 942     # . . push args
+ 943     68/push  0x67/imm32
+ 944     # . . call
+ 945     e8/call  is-hex-lowercase-byte?/disp32
+ 946     # . . discard args
+ 947     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 948     # check-ints-equal(EAX, 0, msg)
+ 949     # . . push args
+ 950     68/push  "F - test-hex-above-f"/imm32
+ 951     68/push  0/imm32/false
+ 952     50/push-EAX
+ 953     # . . call
+ 954     e8/call  check-ints-equal/disp32
+ 955     # . . discard args
+ 956     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 957     c3/return
+ 958 
+ 959 skip-until-newline:  # in : (address buffered-file) -> <void>
+ 960     # pseudocode:
+ 961     #   push EAX
+ 962     #   repeatedly:
+ 963     #     EAX = read-byte(in)
+ 964     #     if EAX == 0xffffffff break
+ 965     #     if EAX == 0x0a break
+ 966     #   pop EAX
+ 967     # . prolog
+ 968     55/push-EBP
+ 969     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 970     # . save registers
+ 971     50/push-EAX
+ 972 $skip-until-newline:loop:
+ 973     # . EAX = read-byte(in)
+ 974     # . . push args
+ 975     ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0x8/disp8       .                 # push *(EBP+8)
+ 976     # . . call
+ 977     e8/call  read-byte/disp32
+ 978     # . . discard args
+ 979     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 980     # . if EAX == 0xffffffff break
+ 981     81          7/subop/compare     3/mod/direct    0/rm32/EAX    .           .             .           .           .               0xffffffff/imm32  # compare EAX
+ 982     74/jump-if-equal  $skip-until-newline:end/disp8
+ 983 $aa:
+ 984     # . if EAX != 0xa/newline loop
+ 985     81          7/subop/compare     3/mod/direct    0/rm32/EAX    .           .             .           .           .               0xa/imm32         # compare EAX
+ 986     75/jump-if-not-equal  $skip-until-newline:loop/disp8
+ 987 $skip-until-newline:end:
+ 988     # . restore registers
+ 989     58/pop-to-EAX
+ 990     # . epilog
+ 991     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 992     5d/pop-to-EBP
+ 993     c3/return
+ 994 
+ 995 test-skip-until-newline:
+ 996     # - check that the read pointer points after the newline
+ 997     # setup
+ 998     # . clear-stream(_test-stream)
+ 999     # . . push args
+1000     68/push  _test-stream/imm32
+1001     # . . call
+1002     e8/call  clear-stream/disp32
+1003     # . . discard args
+1004     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1005     # . clear-stream(_test-buffered-file+4)
+1006     # . . push args
+1007     b8/copy-to-EAX  _test-buffered-file/imm32
+1008     05/add-to-EAX  4/imm32
+1009     50/push-EAX
+1010     # . . call
+1011     e8/call  clear-stream/disp32
+1012     # . . discard args
+1013     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1014     # initialize '_test-stream' to "abc\nde"
+1015     # . write(_test-stream, "abc")
+1016     # . . push args
+1017     68/push  "abc"/imm32
+1018     68/push  _test-stream/imm32
+1019     # . . call
+1020     e8/call  write/disp32
+1021     # . . discard args
+1022     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1023     # . write(_test-stream, Newline)
+1024     # . . push args
+1025     68/push  Newline/imm32
+1026     68/push  _test-stream/imm32
+1027     # . . call
+1028     e8/call  write/disp32
+1029     # . . discard args
+1030     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1031     # . write(_test-stream, "de")
+1032     # . . push args
+1033     68/push  "de"/imm32
+1034     68/push  _test-stream/imm32
+1035     # . . call
+1036     e8/call  write/disp32
+1037     # . . discard args
+1038     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1039     # skip-until-newline(_test-buffered-file)
+1040     # . . push args
+1041     68/push  _test-buffered-file/imm32
+1042     # . . call
+1043     e8/call  skip-until-newline/disp32
+1044     # . . discard args
+1045     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1046     # check-ints-equal(_test-buffered-file->read, 4, msg)
+1047     # . . push args
+1048     68/push  "F - test-skip-until-newline"/imm32
+1049     68/push  4/imm32
+1050     b8/copy-to-EAX  _test-buffered-file/imm32
+1051     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           8/disp8         .                 # push *(EAX+8)
+1052     # . . call
+1053     e8/call  check-ints-equal/disp32
+1054     # . . discard args
+1055     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1056     # . end
+1057     c3/return
+1058 
+1059 == data
+1060 
+1061 _test-output-stream:
+1062     # current write index
+1063     00 00 00 00
+1064     # current read index
+1065     00 00 00 00
+1066     # length (= 8)
+1067     08 00 00 00
+1068     # data
+1069     00 00 00 00 00 00 00 00  # 8 bytes
+1070 
+1071 _test-error-stream:
+1072     # current write index
+1073     00 00 00 00
+1074     # current read index
+1075     00 00 00 00
+1076     # length (= 8)
+1077     08 00 00 00
+1078     # data
+1079     00 00 00 00 00 00 00 00  # 8 bytes
+1080 
+1081 # . . vim:nowrap:textwidth=0
 
-- cgit 1.4.1-2-gfad0