https://github.com/akkartik/mu/blob/master/subx/apps/hex.subx

       1 # Read a text file containing whitespace-separated pairs of ascii hex bytes
       2 # from stdin, and convert them into binary bytes (octets) on stdout. Ignore
       3 # comments between '#' and newline.
       4 #
       5 # To run (from the subx/ directory):
       6 #   $ ./subx translate *.subx apps/hex.subx -o apps/hex
       7 #   $ echo '80 81 82  # comment'  |./subx run apps/hex  |xxd -
       8 # Expected output:
       9 #   00000000: 8081 82
      10 #
      11 # Only hex bytes and comments are permitted. Outside of comments all words
      12 # must be exactly 2 characters long and contain only characters [0-9a-f]. No
      13 # uppercase hex.
      14
      15 == code
      16 #   instruction                     effective address                                                   register    displacement    immediate
      17 # . op          subop               mod             rm32          base        index         scale       r32
      18 # . 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
      19
      20 Entry:  # run tests if necessary, convert stdin if not
      21
      22 #?     # for debugging: run a single test
      23 #?     e8/call test-convert-next-octet-aborts-on-single-hex-byte/disp32
      24 #?     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
      25 #?     eb/jump  $main:end/disp8
      26
      27     # . prolog
      28     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
      29     # - if argc > 1 and argv[1] == "test", then return run_tests()
      30     # . argc > 1
      31     81          7/subop/compare     1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0/disp8         1/imm32           # compare *EBP
      32     7e/jump-if-lesser-or-equal  $run-main/disp8
      33     # . argv[1] == "test"
      34     # . . push args
      35     68/push  "test"/imm32
      36     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
      37     # . . call
      38     e8/call  kernel-string-equal?/disp32
      39     # . . discard args
      40     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
      41     # . check result
      42     3d/compare-EAX-and  1/imm32
      43     75/jump-if-not-equal  $run-main/disp8
      44     # . run-tests()
      45     e8/call  run-tests/disp32
      46     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
      47     eb/jump  $main:end/disp8
      48 $run-main:
      49     # - otherwise convert stdin
      50     # var ed/EAX : exit-descriptor
      51     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
      52     89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EAX
      53     # configure ed to really exit()
      54     # . ed->target = 0
      55     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # copy to *EAX
      56     # return convert(Stdin, 1/stdout, 2/stderr, ed)
      57     # . . push args
      58     50/push-EAX/ed
      59     68/push  Stderr/imm32
      60     68/push  Stdout/imm32
      61     68/push  Stdin/imm32
      62     # . . call
      63     e8/call  convert/disp32
      64     # . . discard args
      65     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
      66     # . syscall(exit, 0)
      67     bb/copy-to-EBX  0/imm32
      68 $main:end:
      69     b8/copy-to-EAX  1/imm32/exit
      70     cd/syscall  0x80/imm8
      71
      72 # the main entry point
      73 convert:  # in : (address buffered-file), out : (address buffered-file), err : (address buffered-file), ed : (address exit-descriptor) -> <void>
      74     # pseudocode:
      75     #   while true
      76     #     EAX = convert-next-octet(in, err, ed)
      77     #     if (EAX == Eof) break
      78     #     write-byte(out, AL)
      79     #   flush(out)
      80     #
      81     # . prolog
      82     55/push-EBP
      83     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
      84     # . save registers
      85     50/push-EAX
      86 $convert:loop:
      87     # EAX = convert-next-octet(in, err, ed)
      88     # . . push args
      89     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x14/disp8      .                 # push *(EBP+20)
      90     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
      91     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
      92     # . . call
      93     e8/call  convert-next-octet/disp32
      94     # . . discard first 2 args
      95     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
      96     # if (EAX == Eof) break
      97     3d/compare-EAX-and  0xffffffff/imm32/Eof
      98     74/jump-if-equal  $convert:loop-end/disp8
      99     # write-byte(out, AL)
     100     # . . push args
     101     50/push-EAX
     102     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
     103     # . . call
     104     e8/call  write-byte/disp32
     105     # . . discard args
     106     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     107     # loop
     108     eb/jump  $convert:loop/disp8
     109 $convert:loop-end:
     110     # flush(out)
     111     # . . push args
     112     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
     113     # . . call
     114     e8/call  flush/disp32
     115     # . . discard args
     116     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     117 $convert:end:
     118     # . restore registers
     119     58/pop-to-EAX
     120     # . epilog
     121     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
     122     5d/pop-to-EBP
     123     c3/return
     124
     125 # read bytes from 'in' until a sequence of two lowercase hex (0-9, a-f) bytes
     126 # skip spaces and newlines
     127 # on '#' skip bytes until newline
     128 # raise an error and abort on all other unexpected bytes
     129 # return in EAX an _octet_ containing the binary value of the two hex characters
     130 # return Eof on reaching end of file
     131 convert-next-octet:  # in : (address buffered-file), err : (address buffered-file), ed : (address exit-descriptor) -> byte-or-Eof/EAX
     132     # pseudocode:
     133     #   EAX = scan-next-byte(in, err, ed)
     134     #   if (EAX == Eof) return
     135     #   ECX = from-hex-char(EAX)
     136     #   EAX = scan-next-byte(in, err, ed)
     137     #   if (EAX == Eof) error("partial byte found.")
     138     #   EAX = from-hex-char(EAX)
     139     #   EAX = (ECX << 4) | EAX
     140     #   return
     141     #
     142     # . prolog
     143     55/push-EBP
     144     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
     145     # . save registers
     146     51/push-ECX
     147     # EAX = scan-next-byte(in, err, ed)
     148     # . . push args
     149     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
     150     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
     151     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
     152     # . . call
     153     e8/call  scan-next-byte/disp32
     154     # . . discard args
     155     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
     156     # if (EAX == Eof) return
     157     3d/compare-EAX-and  0xffffffff/imm32/Eof
     158     74/jump-if-equal  $convert-next-octet:end/disp8
     159     # EAX = from-hex-char(EAX)
     160     e8/call from-hex-char/disp32
     161     # ECX = EAX
     162     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy EAX to ECX
     163     # EAX = scan-next-byte(in, err, ed)
     164     # . . push args
     165     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
     166     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
     167     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
     168     # . . call
     169     e8/call  scan-next-byte/disp32
     170     # . . discard args
     171     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
     172     # if (EAX == Eof) error(ed, err, "partial byte found.")
     173     3d/compare-EAX-and  0xffffffff/imm32/Eof
     174     75/jump-if-not-equal  $convert-next-octet:convert/disp8
     175     # . error-byte(ed, err, msg, '.')  # reusing error-byte to avoid creating _yet_ another helper
     176     # . . push args
     177     68/push  0x2e/imm32/period/dummy
     178     68/push  "convert-next-octet: partial byte found"/imm32
     179     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
     180     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
     181     # . . call
     182     e8/call  error-byte/disp32  # never returns
     183 $convert-next-octet:convert:
     184     # EAX = from-hex-char(EAX)
     185     e8/call from-hex-char/disp32
     186     # EAX = (ECX << 4) | EAX
     187     # . ECX <<= 4
     188     c1/shift    4/subop/left        3/mod/direct    1/rm32/ECX    .           .             .           .           .               4/imm8            # shift ECX left by 4 bits
     189     # . EAX |= ECX
     190     09/or                           3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # EAX = bitwise OR with ECX
     191 $convert-next-octet:end:
     192     # . restore registers
     193     59/pop-to-ECX
     194     # . epilog
     195     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
     196     5d/pop-to-EBP
     197     c3/return
     198
     199 test-convert-next-octet:
     200     # - check that the first two bytes of the input are assembled into the resulting octet
     201     # This test uses exit-descriptors. Use EBP for setting up local variables.
     202     55/push-EBP
     203     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
     204     # clear all streams
     205     # . clear-stream(_test-stream)
     206     # . . push args
     207     68/push  _test-stream/imm32
     208     # . . call
     209     e8/call  clear-stream/disp32
     210     # . . discard args
     211     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     212     # . clear-stream(_test-buffered-file+4)
     213     # . . push args
     214     b8/copy-to-EAX  _test-buffered-file/imm32
     215     05/add-to-EAX  4/imm32
     216     50/push-EAX
     217     # . . call
     218     e8/call  clear-stream/disp32
     219     # . . discard args
     220     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     221     # . clear-stream(_test-error-stream)
     222     # . . push args
     223     68/push  _test-error-stream/imm32
     224     # . . call
     225     e8/call  clear-stream/disp32
     226     # . . discard args
     227     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     228     # . clear-stream(_test-error-buffered-file+4)
     229     # . . push args
     230     b8/copy-to-EAX  _test-error-buffered-file/imm32
     231     05/add-to-EAX  4/imm32
     232     50/push-EAX
     233     # . . call
     234     e8/call  clear-stream/disp32
     235     # . . discard args
     236     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     237     # initialize '_test-stream' to "abc"
     238     # . write(_test-stream, "abc")
     239     # . . push args
     240     68/push  "abc"/imm32
     241     68/push  _test-stream/imm32
     242     # . . call
     243     e8/call  write/disp32
     244     # . . discard args
     245     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     246     # initialize exit-descriptor 'ed' for the call to 'convert-next-octet' below
     247     # . var ed/ECX : exit-descriptor
     248     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
     249     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
     250     # . tailor-exit-descriptor(ed, 12)
     251     # . . push args
     252     68/push  0xc/imm32/nbytes-of-args-for-convert-next-octet
     253     51/push-ECX/ed
     254     # . . call
     255     e8/call  tailor-exit-descriptor/disp32
     256     # . . discard args
     257     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     258     # EAX = convert-next-octet(_test-buffered-file, _test-error-buffered-file, ed)
     259     # . . push args
     260     51/push-ECX/ed
     261     68/push  _test-error-buffered-file/imm32
     262     68/push  _test-buffered-file/imm32
     263     # . . call
     264     e8/call  convert-next-octet/disp32
     265     # registers except ESP may be clobbered at this point
     266     # pop args to convert-next-octet
     267     # . . discard first 2 args
     268     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     269     # . . restore ed
     270     59/pop-to-ECX
     271     # check that convert-next-octet didn't abort
     272     # . check-ints-equal(ed->value, 0, msg)
     273     # . . push args
     274     68/push  "F - test-convert-next-octet: unexpected abort"/imm32
     275     68/push  0/imm32
     276     # . . push ed->value
     277     ff          6/subop/push        1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         .                 # push *(ECX+4)
     278     # . . call
     279     e8/call  check-ints-equal/disp32
     280     # . . discard args
     281     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
     282     # return if abort
     283     81          7/subop/compare     1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         0/imm32           # compare *(ECX+4)
     284     75/jump-if-not-equal  $test-convert-next-octet:end/disp8
     285     # check-ints-equal(EAX, 0xab, msg)
     286     # . . push args
     287     68/push  "F - test-convert-next-octet"/imm32
     288     68/push  0xab/imm32/ab
     289     50/push-EAX
     290     # . . call
     291     e8/call  check-ints-equal/disp32
     292     # . . discard args
     293     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
     294 $test-convert-next-octet:end:
     295     # . epilog
     296     # don't restore ESP from EBP; manually reclaim locals
     297     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     298     5d/pop-to-EBP
     299     c3/return
     300
     301 test-convert-next-octet-handles-Eof:
     302     # - check that reaching end of file returns Eof
     303     # This test uses exit-descriptors. Use EBP for setting up local variables.
     304     55/push-EBP
     305     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
     306     # clear all streams
     307     # . clear-stream(_test-stream)
     308     # . . push args
     309     68/push  _test-stream/imm32
     310     # . . call
     311     e8/call  clear-stream/disp32
     312     # . . discard args
     313     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     314     # . clear-stream(_test-buffered-file+4)
     315     # . . push args
     316     b8/copy-to-EAX  _test-buffered-file/imm32
     317     05/add-to-EAX  4/imm32
     318     50/push-EAX
     319     # . . call
     320     e8/call  clear-stream/disp32
     321     # . . discard args
     322     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     323     # . clear-stream(_test-error-stream)
     324     # . . push args
     325     68/push  _test-error-stream/imm32
     326     # . . call
     327     e8/call  clear-stream/disp32
     328     # . . discard args
     329     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     330     # . clear-stream(_test-error-buffered-file+4)
     331     # . . push args
     332     b8/copy-to-EAX  _test-error-buffered-file/imm32
     333     05/add-to-EAX  4/imm32
     334     50/push-EAX
     335     # . . call
     336     e8/call  clear-stream/disp32
     337     # . . discard args
     338     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     339     # don't initialize '_test-stream'
     340     # initialize exit-descriptor 'ed' for the call to 'convert-next-octet' below
     341     # . var ed/ECX : exit-descriptor
     342     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
     343     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
     344     # . tailor-exit-descriptor(ed, 12)
     345     # . . push args
     346     68/push  0xc/imm32/nbytes-of-args-for-convert-next-octet
     347     51/push-ECX/ed
     348     # . . call
     349     e8/call  tailor-exit-descriptor/disp32
     350     # . . discard args
     351     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     352     # EAX = convert-next-octet(_test-buffered-file, _test-error-buffered-file, ed)
     353     # . . push args
     354     51/push-ECX/ed
     355     68/push  _test-error-buffered-file/imm32
     356     68/push  _test-buffered-file/imm32
     357     # . . call
     358     e8/call  convert-next-octet/disp32
     359     # registers except ESP may be clobbered at this point
     360     # pop args to convert-next-octet
     361     # . . discard first 2 args
     362     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     363     # . . restore ed
     364     59/pop-to-ECX
     365     # check that convert-next-octet didn't abort
     366     # . check-ints-equal(ed->value, 0, msg)
     367     # . . push args
     368     68/push  "F - test-convert-next-octet: unexpected abort"/imm32
     369     68/push  0/imm32
     370     # . . push ed->value
     371     ff          6/subop/push        1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         .                 # push *(ECX+4)
     372     # . . call
     373     e8/call  check-ints-equal/disp32
     374     # . . discard args
     375     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
     376     # return if abort
     377     81          7/subop/compare     1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         0/imm32           # compare *(ECX+4)
     378     75/jump-if-not-equal  $test-convert-next-octet-handles-Eof:end/disp8
     379     # check-ints-equal(EAX, Eof, msg)
     380     # . . push args
     381     68/push  "F - test-convert-next-octet-handles-Eof"/imm32
     382     68/push  0xffffffff/imm32/Eof
     383     50/push-EAX
     384     # . . call
     385     e8/call  check-ints-equal/disp32
     386     # . . discard args
     387     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
     388 $test-convert-next-octet-handles-Eof:end:
     389     # . epilog
     390     # don't restore ESP from EBP; manually reclaim locals
     391     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     392     5d/pop-to-EBP
     393     c3/return
     394
     395 test-convert-next-octet-aborts-on-single-hex-byte:
     396     # - check that a single unaccompanied hex byte aborts
     397     # This test uses exit-descriptors. Use EBP for setting up local variables.
     398     55/push-EBP
     399     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
     400     # clear all streams
     401     # . clear-stream(_test-stream)
     402     # . . push args
     403     68/push  _test-stream/imm32
     404     # . . call
     405     e8/call  clear-stream/disp32
     406     # . . discard args
     407     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     408     # . clear-stream(_test-buffered-file+4)
     409     # . . push args
     410     b8/copy-to-EAX  _test-buffered-file/imm32
     411     05/add-to-EAX  4/imm32
     412     50/push-EAX
     413     # . . call
     414     e8/call  clear-stream/disp32
     415     # . . discard args
     416     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     417     # . clear-stream(_test-error-stream)
     418     # . . push args
     419     68/push  _test-error-stream/imm32
     420     # . . call
     421     e8/call  clear-stream/disp32
     422     # . . discard args
     423     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     424     # . clear-stream(_test-error-buffered-file+4)
     425     # . . push args
     426     b8/copy-to-EAX  _test-error-buffered-file/imm32
     427     05/add-to-EAX  4/imm32
     428     50/push-EAX
     429     # . . call
     430     e8/call  clear-stream/disp32
     431     # . . discard args
     432     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     433     # initialize '_test-stream' to "a"
     434     # . write(_test-stream, "a")
     435     # . . push args
     436     68/push  "a"/imm32
     437     68/push  _test-stream/imm32
     438     # . . call
     439     e8/call  write/disp32
     440     # . . discard args
     441     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     442     # initialize exit-descriptor 'ed' for the call to 'convert-next-octet' below
     443     # . var ed/ECX : exit-descriptor
     444     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
     445     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
     446     # . tailor-exit-descriptor(ed, 12)
     447     # . . push args
     448     68/push  0xc/imm32/nbytes-of-args-for-convert-next-octet
     449     51/push-ECX/ed
     450     # . . call
     451     e8/call  tailor-exit-descriptor/disp32
     452     # . . discard args
     453     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     454     # EAX = convert-next-octet(_test-buffered-file, _test-error-buffered-file, ed)
     455     # . . push args
     456     51/push-ECX/ed
     457     68/push  _test-error-buffered-file/imm32
     458     68/push  _test-buffered-file/imm32
     459     # . . call
     460     e8/call  convert-next-octet/disp32
     461     # registers except ESP may be clobbered at this point
     462     # pop args to convert-next-octet
     463     # . . discard first 2 args
     464     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     465     # . . restore ed
     466     59/pop-to-ECX
     467     # check that convert-next-octet aborted
     468     # . check-ints-equal(ed->value, 2, msg)
     469     # . . push args
     470     68/push  "F - test-convert-next-octet-aborts-on-single-hex-byte: unexpected abort"/imm32
     471     68/push  2/imm32
     472     # . . push ed->value
     473     ff          6/subop/push        1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         .                 # push *(ECX+4)
     474     # . . call
     475     e8/call  check-ints-equal/disp32
     476     # . . discard args
     477     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
     478 $test-convert-next-octet-aborts-on-single-hex-byte:end:
     479     # . epilog
     480     # don't restore ESP from EBP; manually reclaim locals
     481     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     482     5d/pop-to-EBP
     483     c3/return
     484
     485 # read whitespace until a hex byte, and return it
     486 # return Eof if file ends without finding a hex byte
     487 # on '#' skip all bytes until newline
     488 # abort on any other byte
     489 scan-next-byte:  # in : (address buffered-file), err : (address buffered-file), ed : (address exit-descriptor) -> byte-or-Eof/EAX
     490     # pseudocode:
     491     #   while true
     492     #     EAX = read-byte(in)
     493     #     if (EAX == Eof) return EAX
     494     #     if (is-hex-digit?(EAX)) return EAX
     495     #     if (EAX == ' ' or '\t' or '\n') continue
     496     #     if (EAX == '#') skip-until-newline(in)
     497     #     else error-byte(ed, err, "invalid byte: " EAX)
     498     #
     499     # . prolog
     500     55/push-EBP
     501     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
     502     # . save registers
     503 $scan-next-byte:loop:
     504     # EAX = read-byte(in)
     505     # . . push args
     506     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
     507     # . . call
     508     e8/call  read-byte/disp32
     509     # . . discard args
     510     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     511     # if (EAX == Eof) return EAX
     512     3d/compare-with-EAX  0xffffffff/imm32/Eof
     513     74/jump-if-equal  $scan-next-byte:end/disp8
     514     # if (is-hex-digit?(EAX)) return EAX
     515     # . save EAX for now
     516     50/push-EAX
     517     # . is-hex-digit?(EAX)
     518     # . . push args
     519     50/push-EAX
     520     # . . call
     521     e8/call  is-hex-digit?/disp32
     522     # . . discard args
     523     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     524     # . compare with 'false'
     525     3d/compare-with-EAX  0/imm32
     526     # . restore EAX (does not affect flags)
     527     58/pop-to-EAX
     528     # . check whether to return
     529     75/jump-if-not-equal  $scan-next-byte:end/disp8
     530 $scan-next-byte:check1:
     531     # if (EAX == ' ') continue
     532     3d/compare-EAX-and  0x20/imm32/space
     533     74/jump-if-equal  $scan-next-byte:loop/disp8
     534     # if (EAX == '\t') continue
     535     3d/compare-EAX-and  9/imm32/tab
     536     74/jump-if-equal  $scan-next-byte:loop/disp8
     537     # if (EAX == '\n') continue
     538     3d/compare-EAX-and  0xa/imm32/newline
     539     74/jump-if-equal  $scan-next-byte:loop/disp8
     540 $scan-next-byte:check2:
     541     # if (EAX == '#') skip-until-newline(in)
     542     3d/compare-with-EAX  0x23/imm32
     543     75/jump-if-not-equal  $scan-next-byte:check3/disp8
     544     # . skip-until-newline(in)
     545     # . . push args
     546     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
     547     # . . call
     548     e8/call  skip-until-newline/disp32
     549     # . . discard args
     550     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     551     eb/jump  $scan-next-byte:loop/disp8
     552 $scan-next-byte:check3:
     553     # otherwise error-byte(ed, err, msg, EAX)
     554     # . . push args
     555     50/push-EAX
     556     68/push  "scan-next-byte: invalid byte"/imm32
     557     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
     558     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
     559     # . . call
     560     e8/call  error-byte/disp32  # never returns
     561 $scan-next-byte:end:
     562     # . restore registers
     563     # . epilog
     564     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
     565     5d/pop-to-EBP
     566     c3/return
     567
     568 test-scan-next-byte:
     569     # - check that the first byte of the input is returned
     570     # This test uses exit-descriptors. Use EBP for setting up local variables.
     571     55/push-EBP
     572     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
     573     # clear all streams
     574     # . clear-stream(_test-stream)
     575     # . . push args
     576     68/push  _test-stream/imm32
     577     # . . call
     578     e8/call  clear-stream/disp32
     579     # . . discard args
     580     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     581     # . clear-stream(_test-buffered-file+4)
     582     # . . push args
     583     b8/copy-to-EAX  _test-buffered-file/imm32
     584     05/add-to-EAX  4/imm32
     585     50/push-EAX
     586     # . . call
     587     e8/call  clear-stream/disp32
     588     # . . discard args
     589     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     590     # . clear-stream(_test-error-stream)
     591     # . . push args
     592     68/push  _test-error-stream/imm32
     593     # . . call
     594     e8/call  clear-stream/disp32
     595     # . . discard args
     596     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     597     # . clear-stream(_test-error-buffered-file+4)
     598     # . . push args
     599     b8/copy-to-EAX  _test-error-buffered-file/imm32
     600     05/add-to-EAX  4/imm32
     601     50/push-EAX
     602     # . . call
     603     e8/call  clear-stream/disp32
     604     # . . discard args
     605     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     606     # initialize '_test-stream' to "abc"
     607     # . write(_test-stream, "abc")
     608     # . . push args
     609     68/push  "abc"/imm32
     610     68/push  _test-stream/imm32
     611     # . . call
     612     e8/call  write/disp32
     613     # . . discard args
     614     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     615     # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below
     616     # . var ed/ECX : exit-descriptor
     617     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
     618     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
     619     # . tailor-exit-descriptor(ed, 12)
     620     # . . push args
     621     68/push  0xc/imm32/nbytes-of-args-for-scan-next-byte
     622     51/push-ECX/ed
     623     # . . call
     624     e8/call  tailor-exit-descriptor/disp32
     625     # . . discard args
     626     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     627     # EAX = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed)
     628     # . . push args
     629     51/push-ECX/ed
     630     68/push  _test-error-buffered-file/imm32
     631     68/push  _test-buffered-file/imm32
     632     # . . call
     633     e8/call  scan-next-byte/disp32
     634     # registers except ESP may be clobbered at this point
     635     # pop args to scan-next-byte
     636     # . . discard first 2 args
     637     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     638     # . . restore ed
     639     59/pop-to-ECX
     640     # check that scan-next-byte didn't abort
     641     # . check-ints-equal(ed->value, 0, msg)
     642     # . . push args
     643     68/push  "F - test-scan-next-byte: unexpected abort"/imm32
     644     68/push  0/imm32
     645     # . . push ed->value
     646     ff          6/subop/push        1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         .                 # push *(ECX+4)
     647     # . . call
     648     e8/call  check-ints-equal/disp32
     649     # . . discard args
     650     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
     651     # return if abort
     652     81          7/subop/compare     1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         0/imm32           # compare *(ECX+4)
     653     75/jump-if-not-equal  $test-scan-next-byte:end/disp8
     654     # check-ints-equal(EAX, 0x61/a, msg)
     655     # . . push args
     656     68/push  "F - test-scan-next-byte"/imm32
     657     68/push  0x61/imm32/a
     658     50/push-EAX
     659     # . . call
     660     e8/call  check-ints-equal/disp32
     661     # . . discard args
     662     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
     663 $test-scan-next-byte:end:
     664     # . epilog
     665     # don't restore ESP from EBP; manually reclaim locals
     666     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     667     5d/pop-to-EBP
     668     c3/return
     669
     670 test-scan-next-byte-skips-whitespace:
     671     # - check that the first byte after whitespace is returned
     672     # This test uses exit-descriptors. Use EBP for setting up local variables.
     673     55/push-EBP
     674     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
     675     # clear all streams
     676     # . clear-stream(_test-stream)
     677     # . . push args
     678     68/push  _test-stream/imm32
     679     # . . call
     680     e8/call  clear-stream/disp32
     681     # . . discard args
     682     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     683     # . clear-stream(_test-buffered-file+4)
     684     # . . push args
     685     b8/copy-to-EAX  _test-buffered-file/imm32
     686     05/add-to-EAX  4/imm32
     687     50/push-EAX
     688     # . . call
     689     e8/call  clear-stream/disp32
     690     # . . discard args
     691     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     692     # . clear-stream(_test-error-stream)
     693     # . . push args
     694     68/push  _test-error-stream/imm32
     695     # . . call
     696     e8/call  clear-stream/disp32
     697     # . . discard args
     698     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     699     # . clear-stream(_test-error-buffered-file+4)
     700     # . . push args
     701     b8/copy-to-EAX  _test-error-buffered-file/imm32
     702     05/add-to-EAX  4/imm32
     703     50/push-EAX
     704     # . . call
     705     e8/call  clear-stream/disp32
     706     # . . discard args
     707     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     708     # initialize '_test-stream' to input with leading whitespace
     709     # . write(_test-stream, text)
     710     # . . push args
     711     68/push  "  abc"/imm32
     712     68/push  _test-stream/imm32
     713     # . . call
     714     e8/call  write/disp32
     715     # . . discard args
     716     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     717     # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below
     718     # . var ed/ECX : exit-descriptor
     719     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
     720     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
     721     # . tailor-exit-descriptor(ed, 12)
     722     # . . push args
     723     68/push  0xc/imm32/nbytes-of-args-for-scan-next-byte
     724     51/push-ECX/ed
     725     # . . call
     726     e8/call  tailor-exit-descriptor/disp32
     727     # . . discard args
     728     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     729     # EAX = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed)
     730     # . . push args
     731     51/push-ECX/ed
     732     68/push  _test-error-buffered-file/imm32
     733     68/push  _test-buffered-file/imm32
     734     # . . call
     735     e8/call  scan-next-byte/disp32
     736     # registers except ESP may be clobbered at this point
     737     # pop args to scan-next-byte
     738     # . . discard first 2 args
     739     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     740     # . . restore ed
     741     59/pop-to-ECX
     742     # check that scan-next-byte didn't abort
     743     # . check-ints-equal(ed->value, 0, msg)
     744     # . . push args
     745     68/push  "F - test-scan-next-byte-skips-whitespace: unexpected abort"/imm32
     746     68/push  0/imm32
     747     # . . push ed->value
     748     ff          6/subop/push        1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         .                 # push *(ECX+4)
     749     # . . call
     750     e8/call  check-ints-equal/disp32
     751     # . . discard args
     752     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
     753     # return if abort
     754     81          7/subop/compare     1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         0/imm32           # compare *(ECX+4)
     755     75/jump-if-not-equal  $test-scan-next-byte-skips-whitespace:end/disp8
     756     # check-ints-equal(EAX, 0x61/a, msg)
     757     # . . push args
     758     68/push  "F - test-scan-next-byte-skips-whitespace"/imm32
     759     68/push  0x61/imm32/a
     760     50/push-EAX
     761     # . . call
     762     e8/call  check-ints-equal/disp32
     763     # . . discard args
     764     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
     765 $test-scan-next-byte-skips-whitespace:end:
     766     # . epilog
     767     # don't restore ESP from EBP; manually reclaim locals
     768     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     769     5d/pop-to-EBP
     770     c3/return
     771
     772 test-scan-next-byte-skips-comment:
     773     # - check that the first byte after a comment (and newline) is returned
     774     # This test uses exit-descriptors. Use EBP for setting up local variables.
     775     55/push-EBP
     776     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
     777     # clear all streams
     778     # . clear-stream(_test-stream)
     779     # . . push args
     780     68/push  _test-stream/imm32
     781     # . . call
     782     e8/call  clear-stream/disp32
     783     # . . discard args
     784     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     785     # . clear-stream(_test-buffered-file+4)
     786     # . . push args
     787     b8/copy-to-EAX  _test-buffered-file/imm32
     788     05/add-to-EAX  4/imm32
     789     50/push-EAX
     790     # . . call
     791     e8/call  clear-stream/disp32
     792     # . . discard args
     793     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     794     # . clear-stream(_test-error-stream)
     795     # . . push args
     796     68/push  _test-error-stream/imm32
     797     # . . call
     798     e8/call  clear-stream/disp32
     799     # . . discard args
     800     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     801     # . clear-stream(_test-error-buffered-file+4)
     802     # . . push args
     803     b8/copy-to-EAX  _test-error-buffered-file/imm32
     804     05/add-to-EAX  4/imm32
     805     50/push-EAX
     806     # . . call
     807     e8/call  clear-stream/disp32
     808     # . . discard args
     809     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     810     # initialize '_test-stream' to input with leading comment
     811     # . write(_test-stream, comment)
     812     # . . push args
     813     68/push  "#x\n"/imm32
     814     68/push  _test-stream/imm32
     815     # . . call
     816     e8/call  write/disp32
     817     # . . discard args
     818     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     819     # . write(_test-stream, real text)
     820     # . . push args
     821     68/push  "ab"/imm32
     822     68/push  _test-stream/imm32
     823     # . . call
     824     e8/call  write/disp32
     825     # . . discard args
     826     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     827     # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below
     828     # . var ed/ECX : exit-descriptor
     829     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
     830     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
     831     # . tailor-exit-descriptor(ed, 12)
     832     # . . push args
     833     68/push  0xc/imm32/nbytes-of-args-for-scan-next-byte
     834     51/push-ECX/ed
     835     # . . call
     836     e8/call  tailor-exit-descriptor/disp32
     837     # . . discard args
     838     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     839     # EAX = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed)
     840     # . . push args
     841     51/push-ECX/ed
     842     68/push  _test-error-buffered-file/imm32
     843     68/push  _test-buffered-file/imm32
     844     # . . call
     845     e8/call  scan-next-byte/disp32
     846     # registers except ESP may be clobbered at this point
     847     # pop args to scan-next-byte
     848     # . . discard first 2 args
     849     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     850     # . . restore ed
     851     59/pop-to-ECX
     852     # check that scan-next-byte didn't abort
     853     # . check-ints-equal(ed->value, 0, msg)
     854     # . . push args
     855     68/push  "F - test-scan-next-byte-skips-comment: unexpected abort"/imm32
     856     68/push  0/imm32
     857     # . . push ed->value
     858     ff          6/subop/push        1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         .                 # push *(ECX+4)
     859     # . . call
     860     e8/call  check-ints-equal/disp32
     861     # . . discard args
     862     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
     863     # return if abort
     864     81          7/subop/compare     1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         0/imm32           # compare *(ECX+4)
     865     75/jump-if-not-equal  $test-scan-next-byte-skips-comment:end/disp8
     866     # check-ints-equal(EAX, 0x61/a, msg)
     867     # . . push args
     868     68/push  "F - test-scan-next-byte-skips-comment"/imm32
     869     68/push  0x61/imm32/a
     870     50/push-EAX
     871     # . . call
     872     e8/call  check-ints-equal/disp32
     873     # . . discard args
     874     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
     875 $test-scan-next-byte-skips-comment:end:
     876     # . epilog
     877     # don't restore ESP from EBP; manually reclaim locals
     878     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     879     5d/pop-to-EBP
     880     c3/return
     881
     882 test-scan-next-byte-skips-comment-and-whitespace:
     883     # - check that the first byte after a comment and any further whitespace is returned
     884     # This test uses exit-descriptors. Use EBP for setting up local variables.
     885     55/push-EBP
     886     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
     887     # clear all streams
     888     # . clear-stream(_test-stream)
     889     # . . push args
     890     68/push  _test-stream/imm32
     891     # . . call
     892     e8/call  clear-stream/disp32
     893     # . . discard args
     894     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     895     # . clear-stream(_test-buffered-file+4)
     896     # . . push args
     897     b8/copy-to-EAX  _test-buffered-file/imm32
     898     05/add-to-EAX  4/imm32
     899     50/push-EAX
     900     # . . call
     901     e8/call  clear-stream/disp32
     902     # . . discard args
     903     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     904     # . clear-stream(_test-error-stream)
     905     # . . push args
     906     68/push  _test-error-stream/imm32
     907     # . . call
     908     e8/call  clear-stream/disp32
     909     # . . discard args
     910     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     911     # . clear-stream(_test-error-buffered-file+4)
     912     # . . push args
     913     b8/copy-to-EAX  _test-error-buffered-file/imm32
     914     05/add-to-EAX  4/imm32
     915     50/push-EAX
     916     # . . call
     917     e8/call  clear-stream/disp32
     918     # . . discard args
     919     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     920     # initialize '_test-stream' to input with leading comment and more whitespace after newline
     921     # . write(_test-stream, comment)
     922     # . . push args
     923     68/push  "#x\n"/imm32
     924     68/push  _test-stream/imm32
     925     # . . call
     926     e8/call  write/disp32
     927     # . . discard args
     928     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     929     # . write(_test-stream, real text)
     930     # . . push args
     931     68/push  " ab"/imm32
     932     68/push  _test-stream/imm32
     933     # . . call
     934     e8/call  write/disp32
     935     # . . discard args
     936     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     937     # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below
     938     # . var ed/ECX : exit-descriptor
     939     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
     940     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
     941     # . tailor-exit-descriptor(ed, 12)
     942     # . . push args
     943     68/push  0xc/imm32/nbytes-of-args-for-scan-next-byte
     944     51/push-ECX/ed
     945     # . . call
     946     e8/call  tailor-exit-descriptor/disp32
     947     # . . discard args
     948     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     949     # EAX = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed)
     950     # . . push args
     951     51/push-ECX/ed
     952     68/push  _test-error-buffered-file/imm32
     953     68/push  _test-buffered-file/imm32
     954     # . . call
     955     e8/call  scan-next-byte/disp32
     956     # registers except ESP may be clobbered at this point
     957     # pop args to scan-next-byte
     958     # . . discard first 2 args
     959     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     960     # . . restore ed
     961     59/pop-to-ECX
     962     # check that scan-next-byte didn't abort
     963     # . check-ints-equal(ed->value, 0, msg)
     964     # . . push args
     965     68/push  "F - test-scan-next-byte-skips-comment-and-whitespace: unexpected abort"/imm32
     966     68/push  0/imm32
     967     # . . push ed->value
     968     ff          6/subop/push        1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         .                 # push *(ECX+4)
     969     # . . call
     970     e8/call  check-ints-equal/disp32
     971     # . . discard args
     972     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
     973     # return if abort
     974     81          7/subop/compare     1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         0/imm32           # compare *(ECX+4)
     975     75/jump-if-not-equal  $test-scan-next-byte-skips-comment-and-whitespace:end/disp8
     976     # check-ints-equal(EAX, 0x61/a, msg)
     977     # . . push args
     978     68/push  "F - test-scan-next-byte-skips-comment-and-whitespace"/imm32
     979     68/push  0x61/imm32/a
     980     50/push-EAX
     981     # . . call
     982     e8/call  check-ints-equal/disp32
     983     # . . discard args
     984     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
     985 $test-scan-next-byte-skips-comment-and-whitespace:end:
     986     # . epilog
     987     # don't restore ESP from EBP; manually reclaim locals
     988     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     989     5d/pop-to-EBP
     990     c3/return
     991
     992 test-scan-next-byte-skips-whitespace-and-comment:
     993     # - check that the first byte after any whitespace and comments is returned
     994     # This test uses exit-descriptors. Use EBP for setting up local variables.
     995     55/push-EBP
     996     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
     997     # clear all streams
     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     # . clear-stream(_test-error-stream)
    1015     # . . push args
    1016     68/push  _test-error-stream/imm32
    1017     # . . call
    1018     e8/call  clear-stream/disp32
    1019     # . . discard args
    1020     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1021     # . clear-stream(_test-error-buffered-file+4)
    1022     # . . push args
    1023     b8/copy-to-EAX  _test-error-buffered-file/imm32
    1024     05/add-to-EAX  4/imm32
    1025     50/push-EAX
    1026     # . . call
    1027     e8/call  clear-stream/disp32
    1028     # . . discard args
    1029     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1030     # initialize '_test-stream' to input with leading whitespace and comment
    1031     # . write(_test-stream, comment)
    1032     # . . push args
    1033     68/push  " #x\n"/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     # . write(_test-stream, real text)
    1040     # . . push args
    1041     68/push  "ab"/imm32
    1042     68/push  _test-stream/imm32
    1043     # . . call
    1044     e8/call  write/disp32
    1045     # . . discard args
    1046     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1047     # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below
    1048     # . var ed/ECX : exit-descriptor
    1049     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
    1050     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    1051     # . tailor-exit-descriptor(ed, 12)
    1052     # . . push args
    1053     68/push  0xc/imm32/nbytes-of-args-for-scan-next-byte
    1054     51/push-ECX/ed
    1055     # . . call
    1056     e8/call  tailor-exit-descriptor/disp32
    1057     # . . discard args
    1058     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1059     # EAX = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed)
    1060     # . . push args
    1061     51/push-ECX/ed
    1062     68/push  _test-error-buffered-file/imm32
    1063     68/push  _test-buffered-file/imm32
    1064     # . . call
    1065     e8/call  scan-next-byte/disp32
    1066     # registers except ESP may be clobbered at this point
    1067     # pop args to scan-next-byte
    1068     # . . discard first 2 args
    1069     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1070     # . . restore ed
    1071     59/pop-to-ECX
    1072     # check that scan-next-byte didn't abort
    1073     # . check-ints-equal(ed->value, 0, msg)
    1074     # . . push args
    1075     68/push  "F - test-scan-next-byte-skips-whitespace-and-comment: unexpected abort"/imm32
    1076     68/push  0/imm32
    1077     # . . push ed->value
    1078     ff          6/subop/push        1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         .                 # push *(ECX+4)
    1079     # . . call
    1080     e8/call  check-ints-equal/disp32
    1081     # . . discard args
    1082     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    1083     # return if abort
    1084     81          7/subop/compare     1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         0/imm32           # compare *(ECX+4)
    1085     75/jump-if-not-equal  $test-scan-next-byte-skips-whitespace-and-comment:end/disp8
    1086     # check-ints-equal(EAX, 0x61/a, msg)
    1087     # . . push args
    1088     68/push  "F - test-scan-next-byte-skips-whitespace-and-comment"/imm32
    1089     68/push  0x61/imm32/a
    1090     50/push-EAX
    1091     # . . call
    1092     e8/call  check-ints-equal/disp32
    1093     # . . discard args
    1094     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    1095 $test-scan-next-byte-skips-whitespace-and-comment:end:
    1096     # . epilog
    1097     # don't restore ESP from EBP; manually reclaim locals
    1098     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1099     5d/pop-to-EBP
    1100     c3/return
    1101
    1102 test-scan-next-byte-reads-final-byte:
    1103     # - check that the final byte in input is returned
    1104     # This test uses exit-descriptors. Use EBP for setting up local variables.
    1105     55/push-EBP
    1106     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    1107     # clear all streams
    1108     # . clear-stream(_test-stream)
    1109     # . . push args
    1110     68/push  _test-stream/imm32
    1111     # . . call
    1112     e8/call  clear-stream/disp32
    1113     # . . discard args
    1114     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1115     # . clear-stream(_test-buffered-file+4)
    1116     # . . push args
    1117     b8/copy-to-EAX  _test-buffered-file/imm32
    1118     05/add-to-EAX  4/imm32
    1119     50/push-EAX
    1120     # . . call
    1121     e8/call  clear-stream/disp32
    1122     # . . discard args
    1123     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1124     # . clear-stream(_test-error-stream)
    1125     # . . push args
    1126     68/push  _test-error-stream/imm32
    1127     # . . call
    1128     e8/call  clear-stream/disp32
    1129     # . . discard args
    1130     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1131     # . clear-stream(_test-error-buffered-file+4)
    1132     # . . push args
    1133     b8/copy-to-EAX  _test-error-buffered-file/imm32
    1134     05/add-to-EAX  4/imm32
    1135     50/push-EAX
    1136     # . . call
    1137     e8/call  clear-stream/disp32
    1138     # . . discard args
    1139     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1140     # initialize '_test-stream' to input with single character
    1141     # . write(_test-stream, character)
    1142     # . . push args
    1143     68/push  "a"/imm32
    1144     68/push  _test-stream/imm32
    1145     # . . call
    1146     e8/call  write/disp32
    1147     # . . discard args
    1148     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1149     # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below
    1150     # . var ed/ECX : exit-descriptor
    1151     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
    1152     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    1153     # . tailor-exit-descriptor(ed, 12)
    1154     # . . push args
    1155     68/push  0xc/imm32/nbytes-of-args-for-scan-next-byte
    1156     51/push-ECX/ed
    1157     # . . call
    1158     e8/call  tailor-exit-descriptor/disp32
    1159     # . . discard args
    1160     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1161     # EAX = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed)
    1162     # . . push args
    1163     51/push-ECX/ed
    1164     68/push  _test-error-buffered-file/imm32
    1165     68/push  _test-buffered-file/imm32
    1166     # . . call
    1167     e8/call  scan-next-byte/disp32
    1168     # registers except ESP may be clobbered at this point
    1169     # pop args to scan-next-byte
    1170     # . . discard first 2 args
    1171     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1172     # . . restore ed
    1173     59/pop-to-ECX
    1174     # check that scan-next-byte didn't abort
    1175     # . check-ints-equal(ed->value, 0, msg)
    1176     # . . push args
    1177     68/push  "F - test-scan-next-byte-reads-final-byte: unexpected abort"/imm32
    1178     68/push  0/imm32
    1179     # . . push ed->value
    1180     ff          6/subop/push        1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         .                 # push *(ECX+4)
    1181     # . . call
    1182     e8/call  check-ints-equal/disp32
    1183     # . . discard args
    1184     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    1185     # return if abort
    1186     81          7/subop/compare     1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         0/imm32           # compare *(ECX+4)
    1187     75/jump-if-not-equal  $test-scan-next-byte-reads-final-byte:end/disp8
    1188     # check-ints-equal(EAX, 0x61/a, msg)
    1189     # . . push args
    1190     68/push  "F - test-scan-next-byte-reads-final-byte"/imm32
    1191     68/push  0x61/imm32/a
    1192     50/push-EAX
    1193     # . . call
    1194     e8/call  check-ints-equal/disp32
    1195     # . . discard args
    1196     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    1197 $test-scan-next-byte-reads-final-byte:end:
    1198     # . epilog
    1199     # don't restore ESP from EBP; manually reclaim locals
    1200     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1201     5d/pop-to-EBP
    1202     c3/return
    1203
    1204 test-scan-next-byte-handles-Eof:
    1205     # - check that the right sentinel value is returned when there's no data remaining to be read
    1206     # This test uses exit-descriptors. Use EBP for setting up local variables.
    1207     55/push-EBP
    1208     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    1209     # clear all streams
    1210     # . clear-stream(_test-stream)
    1211     # . . push args
    1212     68/push  _test-stream/imm32
    1213     # . . call
    1214     e8/call  clear-stream/disp32
    1215     # . . discard args
    1216     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1217     # . clear-stream(_test-buffered-file+4)
    1218     # . . push args
    1219     b8/copy-to-EAX  _test-buffered-file/imm32
    1220     05/add-to-EAX  4/imm32
    1221     50/push-EAX
    1222     # . . call
    1223     e8/call  clear-stream/disp32
    1224     # . . discard args
    1225     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1226     # . clear-stream(_test-error-stream)
    1227     # . . push args
    1228     68/push  _test-error-stream/imm32
    1229     # . . call
    1230     e8/call  clear-stream/disp32
    1231     # . . discard args
    1232     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1233     # . clear-stream(_test-error-buffered-file+4)
    1234     # . . push args
    1235     b8/copy-to-EAX  _test-error-buffered-file/imm32
    1236     05/add-to-EAX  4/imm32
    1237     50/push-EAX
    1238     # . . call
    1239     e8/call  clear-stream/disp32
    1240     # . . discard args
    1241     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1242     # leave '_test-stream' empty
    1243     # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below
    1244     # . var ed/ECX : exit-descriptor
    1245     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
    1246     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    1247     # . tailor-exit-descriptor(ed, 12)
    1248     # . . push args
    1249     68/push  0xc/imm32/nbytes-of-args-for-scan-next-byte
    1250     51/push-ECX/ed
    1251     # . . call
    1252     e8/call  tailor-exit-descriptor/disp32
    1253     # . . discard args
    1254     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1255     # EAX = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed)
    1256     # . . push args
    1257     51/push-ECX/ed
    1258     68/push  _test-error-buffered-file/imm32
    1259     68/push  _test-buffered-file/imm32
    1260     # . . call
    1261     e8/call  scan-next-byte/disp32
    1262     # registers except ESP may be clobbered at this point
    1263     # pop args to scan-next-byte
    1264     # . . discard first 2 args
    1265     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1266     # . . restore ed
    1267     59/pop-to-ECX
    1268     # check that scan-next-byte didn't abort
    1269     # . check-ints-equal(ed->value, 0, msg)
    1270     # . . push args
    1271     68/push  "F - test-scan-next-byte-handles-Eof: unexpected abort"/imm32
    1272     68/push  0/imm32
    1273     # . . push ed->value
    1274     ff          6/subop/push        1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         .                 # push *(ECX+4)
    1275     # . . call
    1276     e8/call  check-ints-equal/disp32
    1277     # . . discard args
    1278     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    1279     # return if abort
    1280     81          7/subop/compare     1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         0/imm32           # compare *(ECX+4)
    1281     75/jump-if-not-equal  $test-scan-next-byte-handles-Eof:end/disp8
    1282     # check-ints-equal(EAX, Eof, msg)
    1283     # . . push args
    1284     68/push  "F - test-scan-next-byte-handles-Eof"/imm32
    1285     68/push  0xffffffff/imm32/Eof
    1286     50/push-EAX
    1287     # . . call
    1288     e8/call  check-ints-equal/disp32
    1289     # . . discard args
    1290     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    1291 $test-scan-next-byte-handles-Eof:end:
    1292     # . epilog
    1293     # don't restore ESP from EBP; manually reclaim locals
    1294     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1295     5d/pop-to-EBP
    1296     c3/return
    1297
    1298 test-scan-next-byte-aborts-on-invalid-byte:
    1299     # - check that the a bad byte immediately aborts
    1300     # This test uses exit-descriptors. Use EBP for setting up local variables.
    1301     55/push-EBP
    1302     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    1303     # clear all streams
    1304     # . clear-stream(_test-stream)
    1305     # . . push args
    1306     68/push  _test-stream/imm32
    1307     # . . call
    1308     e8/call  clear-stream/disp32
    1309     # . . discard args
    1310     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1311     # . clear-stream(_test-buffered-file+4)
    1312     # . . push args
    1313     b8/copy-to-EAX  _test-buffered-file/imm32
    1314     05/add-to-EAX  4/imm32
    1315     50/push-EAX
    1316     # . . call
    1317     e8/call  clear-stream/disp32
    1318     # . . discard args
    1319     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1320     # . clear-stream(_test-error-stream)
    1321     # . . push args
    1322     68/push  _test-error-stream/imm32
    1323     # . . call
    1324     e8/call  clear-stream/disp32
    1325     # . . discard args
    1326     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1327     # . clear-stream(_test-error-buffered-file+4)
    1328     # . . push args
    1329     b8/copy-to-EAX  _test-error-buffered-file/imm32
    1330     05/add-to-EAX  4/imm32
    1331     50/push-EAX
    1332     # . . call
    1333     e8/call  clear-stream/disp32
    1334     # . . discard args
    1335     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1336     # initialize '_test-stream' to "x"
    1337     # . write(_test-stream, "x")
    1338     # . . push args
    1339     68/push  "x"/imm32
    1340     68/push  _test-stream/imm32
    1341     # . . call
    1342     e8/call  write/disp32
    1343     # . . discard args
    1344     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1345     # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below
    1346     # . var ed/ECX : exit-descriptor
    1347     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
    1348     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    1349     # . tailor-exit-descriptor(ed, 12)
    1350     # . . push args
    1351     68/push  0xc/imm32/nbytes-of-args-for-scan-next-byte
    1352     51/push-ECX/ed
    1353     # . . call
    1354     e8/call  tailor-exit-descriptor/disp32
    1355     # . . discard args
    1356     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1357     # EAX = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed)
    1358     # . . push args
    1359     51/push-ECX/ed
    1360     68/push  _test-error-buffered-file/imm32
    1361     68/push  _test-buffered-file/imm32
    1362     # . . call
    1363     e8/call  scan-next-byte/disp32
    1364     # registers except ESP may be clobbered at this point
    1365     # pop args to scan-next-byte
    1366     # . . discard first 2 args
    1367     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1368     # . . restore ed
    1369     59/pop-to-ECX
    1370     # check that scan-next-byte aborted
    1371     # . check-ints-equal(ed->value, 2, msg)
    1372     # . . push args
    1373     68/push  "F - test-scan-next-byte-aborts-on-invalid-byte"/imm32
    1374     68/push  2/imm32
    1375     # . . push ed->value
    1376     ff          6/subop/push        1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         .                 # push *(ECX+4)
    1377     # . . call
    1378     e8/call  check-ints-equal/disp32
    1379     # . . discard args
    1380     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    1381 $test-scan-next-byte-aborts-on-invalid-byte:end:
    1382     # . epilog
    1383     # don't restore ESP from EBP; manually reclaim locals
    1384     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1385     5d/pop-to-EBP
    1386     c3/return
    1387
    1388 skip-until-newline:  # in : (address buffered-file) -> <void>
    1389     # pseudocode:
    1390     #   push EAX
    1391     #   while true
    1392     #     EAX = read-byte(in)
    1393     #     if (EAX == Eof) break
    1394     #     if (EAX == 0x0a) break
    1395     #   pop EAX
    1396     # . prolog
    1397     55/push-EBP
    1398     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    1399     # . save registers
    1400     50/push-EAX
    1401 $skip-until-newline:loop:
    1402     # . EAX = read-byte(in)
    1403     # . . push args
    1404     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    1405     # . . call
    1406     e8/call  read-byte/disp32
    1407     # . . discard args
    1408     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1409     # . if (EAX == Eof) break
    1410     3d/compare-EAX-and  0xffffffff/imm32/Eof
    1411     74/jump-if-equal  $skip-until-newline:end/disp8
    1412     # . if (EAX != 0xa/newline) loop
    1413     3d/compare-EAX-and  0xa/imm32/newline
    1414     75/jump-if-not-equal  $skip-until-newline:loop/disp8
    1415 $skip-until-newline:end:
    1416     # . restore registers
    1417     58/pop-to-EAX
    1418     # . epilog
    1419     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    1420     5d/pop-to-EBP
    1421     c3/return
    1422
    1423 test-skip-until-newline:
    1424     # - check that the read pointer points after the newline
    1425     # setup
    1426     # . clear-stream(_test-stream)
    1427     # . . push args
    1428     68/push  _test-stream/imm32
    1429     # . . call
    1430     e8/call  clear-stream/disp32
    1431     # . . discard args
    1432     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1433     # . clear-stream(_test-buffered-file+4)
    1434     # . . push args
    1435     b8/copy-to-EAX  _test-buffered-file/imm32
    1436     05/add-to-EAX  4/imm32
    1437     50/push-EAX
    1438     # . . call
    1439     e8/call  clear-stream/disp32
    1440     # . . discard args
    1441     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1442     # initialize '_test-stream' to "abc\nde"
    1443     # . write(_test-stream, "abc")
    1444     # . . push args
    1445     68/push  "abc\n"/imm32
    1446     68/push  _test-stream/imm32
    1447     # . . call
    1448     e8/call  write/disp32
    1449     # . . discard args
    1450     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1451     # . write(_test-stream, "de")
    1452     # . . push args
    1453     68/push  "de"/imm32
    1454     68/push  _test-stream/imm32
    1455     # . . call
    1456     e8/call  write/disp32
    1457     # . . discard args
    1458     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1459     # skip-until-newline(_test-buffered-file)
    1460     # . . push args
    1461     68/push  _test-buffered-file/imm32
    1462     # . . call
    1463     e8/call  skip-until-newline/disp32
    1464     # . . discard args
    1465     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1466     # check-ints-equal(_test-buffered-file->read, 4, msg)
    1467     # . . push args
    1468     68/push  "F - test-skip-until-newline"/imm32
    1469     68/push  4/imm32
    1470     b8/copy-to-EAX  _test-buffered-file/imm32
    1471     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           8/disp8         .                 # push *(EAX+8)
    1472     # . . call
    1473     e8/call  check-ints-equal/disp32
    1474     # . . discard args
    1475     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    1476     # . end
    1477     c3/return
    1478
    1479 == data
    1480
    1481 _test-error-stream:
    1482     # current write index
    1483     0/imm32
    1484     # current read index
    1485     0/imm32
    1486     # line
    1487     0x80/imm32  # 128 bytes
    1488     # data (8 lines x 16 bytes/line)
    1489     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    1490     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    1491     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    1492     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    1493     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    1494     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    1495     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    1496     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    1497
    1498 # a test buffered file for _test-error-stream
    1499 _test-error-buffered-file:
    1500     # file descriptor or (address stream)
    1501     _test-error-stream/imm32
    1502     # current write index
    1503     0/imm32
    1504     # current read index
    1505     0/imm32
    1506     # length
    1507     6/imm32
    1508     # data
    1509     00 00 00 00 00 00  # 6 bytes
    1510
    1511 # . . vim:nowrap:textwidth=0