https://github.com/akkartik/mu/blob/main/linux/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:
       6 #   $ bootstrap/bootstrap translate [01]*.subx subx-params.subx hex.subx  -o hex
       7 #   $ echo '80 81 82  # comment'  |bootstrap/bootstrap run 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     # . prologue
      22     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
      23
      24     # initialize heap
      25     # . Heap = new-segment(Heap-size)
      26     # . . push args
      27     68/push  Heap/imm32
      28     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Heap-size/disp32                  # push *Heap-size
      29     # . . call
      30     e8/call  new-segment/disp32
      31     # . . discard args
      32     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
      33
      34     # - if argc > 1 and argv[1] == "test", then return run_tests()
      35     # if (argc <= 1) goto interactive
      36     81          7/subop/compare     1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0/disp8         1/imm32           # compare *ebp
      37     7e/jump-if-<=  $subx-hex-main:interactive/disp8
      38     # if (!kernel-string-equal?(argv[1], "test")) goto interactive
      39     # . eax = kernel-string-equal?(argv[1], "test")
      40     # . . push args
      41     68/push  "test"/imm32
      42     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
      43     # . . call
      44     e8/call  kernel-string-equal?/disp32
      45     # . . discard args
      46     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
      47     # . if (eax == false) goto interactive
      48     3d/compare-eax-and  0/imm32/false
      49     74/jump-if-=  $subx-hex-main:interactive/disp8
      50     # run-tests()
      51     e8/call  run-tests/disp32
      52     # syscall(exit, *Num-test-failures)
      53     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/ebx   Num-test-failures/disp32          # copy *Num-test-failures to ebx
      54     eb/jump  $subx-hex-main:end/disp8
      55 $subx-hex-main:interactive:
      56     # - otherwise convert stdin
      57     # . subx-hex(Stdin, Stdout, Stderr, 0)
      58     # . . push args
      59     68/push  0/imm32/exit-descriptor
      60     68/push  Stderr/imm32
      61     68/push  Stdout/imm32
      62     68/push  Stdin/imm32
      63     # . . call
      64     e8/call  subx-hex/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 $subx-hex-main:end:
      70     e8/call  syscall_exit/disp32
      71
      72 # the main entry point
      73 subx-hex:  # in: (addr buffered-file), out: (addr buffered-file), err: (addr buffered-file), ed: (addr exit-descriptor)
      74     # pseudocode:
      75     #   while true
      76     #     eax = convert-next-octet(in, err, ed)
      77     #     if (eax == Eof) break
      78     #     write-byte-buffered(out, AL)
      79     #   flush(out)
      80     #
      81     # . prologue
      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 $subx-hex: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-=  $subx-hex:loop-end/disp8
      99     # write-byte-buffered(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-buffered/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  $subx-hex:loop/disp8
     109 $subx-hex: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 $subx-hex:end:
     118     # . restore registers
     119     58/pop-to-eax
     120     # . epilogue
     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: (addr buffered-file), err: (addr buffered-file), ed: (addr 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     # . prologue
     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-=  $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-!=  $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     # . epilogue
     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->buffer)
     213     # . . push args
     214     68/push  $_test-buffered-file->buffer/imm32
     215     # . . call
     216     e8/call  clear-stream/disp32
     217     # . . discard args
     218     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     219     # . clear-stream(_test-error-stream)
     220     # . . push args
     221     68/push  _test-error-stream/imm32
     222     # . . call
     223     e8/call  clear-stream/disp32
     224     # . . discard args
     225     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     226     # . clear-stream($_test-error-buffered-file->buffer)
     227     # . . push args
     228     68/push  $_test-error-buffered-file->buffer/imm32
     229     # . . call
     230     e8/call  clear-stream/disp32
     231     # . . discard args
     232     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     233     # initialize '_test-stream' to "abc"
     234     # . write(_test-stream, "abc")
     235     # . . push args
     236     68/push  "abc"/imm32
     237     68/push  _test-stream/imm32
     238     # . . call
     239     e8/call  write/disp32
     240     # . . discard args
     241     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     242     # initialize exit-descriptor 'ed' for the call to 'convert-next-octet' below
     243     # . var ed/ecx: exit-descriptor
     244     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
     245     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
     246     # . tailor-exit-descriptor(ed, 12)
     247     # . . push args
     248     68/push  0xc/imm32/nbytes-of-args-for-convert-next-octet
     249     51/push-ecx/ed
     250     # . . call
     251     e8/call  tailor-exit-descriptor/disp32
     252     # . . discard args
     253     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     254     # eax = convert-next-octet(_test-buffered-file, _test-error-buffered-file, ed)
     255     # . . push args
     256     51/push-ecx/ed
     257     68/push  _test-error-buffered-file/imm32
     258     68/push  _test-buffered-file/imm32
     259     # . . call
     260     e8/call  convert-next-octet/disp32
     261     # registers except esp may be clobbered at this point
     262     # pop args to convert-next-octet
     263     # . . discard first 2 args
     264     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     265     # . . restore ed
     266     59/pop-to-ecx
     267     # check that convert-next-octet didn't abort
     268     # . check-ints-equal(ed->value, 0, msg)
     269     # . . push args
     270     68/push  "F - test-convert-next-octet: unexpected abort"/imm32
     271     68/push  0/imm32
     272     # . . push ed->value
     273     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         .                 # push *(ecx+4)
     274     # . . call
     275     e8/call  check-ints-equal/disp32
     276     # . . discard args
     277     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     278     # return if abort
     279     81          7/subop/compare     1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         0/imm32           # compare *(ecx+4)
     280     75/jump-if-!=  $test-convert-next-octet:end/disp8
     281     # check-ints-equal(eax, 0xab, msg)
     282     # . . push args
     283     68/push  "F - test-convert-next-octet"/imm32
     284     68/push  0xab/imm32/ab
     285     50/push-eax
     286     # . . call
     287     e8/call  check-ints-equal/disp32
     288     # . . discard args
     289     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     290 $test-convert-next-octet:end:
     291     # . epilogue
     292     # don't restore esp from ebp; manually reclaim locals
     293     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     294     5d/pop-to-ebp
     295     c3/return
     296
     297 test-convert-next-octet-handles-Eof:
     298     # - check that reaching end of file returns Eof
     299     # This test uses exit-descriptors. Use ebp for setting up local variables.
     300     55/push-ebp
     301     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     302     # clear all streams
     303     # . clear-stream(_test-stream)
     304     # . . push args
     305     68/push  _test-stream/imm32
     306     # . . call
     307     e8/call  clear-stream/disp32
     308     # . . discard args
     309     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     310     # . clear-stream($_test-buffered-file->buffer)
     311     # . . push args
     312     68/push  $_test-buffered-file->buffer/imm32
     313     # . . call
     314     e8/call  clear-stream/disp32
     315     # . . discard args
     316     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     317     # . clear-stream(_test-error-stream)
     318     # . . push args
     319     68/push  _test-error-stream/imm32
     320     # . . call
     321     e8/call  clear-stream/disp32
     322     # . . discard args
     323     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     324     # . clear-stream($_test-error-buffered-file->buffer)
     325     # . . push args
     326     68/push  $_test-error-buffered-file->buffer/imm32
     327     # . . call
     328     e8/call  clear-stream/disp32
     329     # . . discard args
     330     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     331     # don't initialize '_test-stream'
     332     # initialize exit-descriptor 'ed' for the call to 'convert-next-octet' below
     333     # . var ed/ecx: exit-descriptor
     334     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
     335     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
     336     # . tailor-exit-descriptor(ed, 12)
     337     # . . push args
     338     68/push  0xc/imm32/nbytes-of-args-for-convert-next-octet
     339     51/push-ecx/ed
     340     # . . call
     341     e8/call  tailor-exit-descriptor/disp32
     342     # . . discard args
     343     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     344     # eax = convert-next-octet(_test-buffered-file, _test-error-buffered-file, ed)
     345     # . . push args
     346     51/push-ecx/ed
     347     68/push  _test-error-buffered-file/imm32
     348     68/push  _test-buffered-file/imm32
     349     # . . call
     350     e8/call  convert-next-octet/disp32
     351     # registers except esp may be clobbered at this point
     352     # pop args to convert-next-octet
     353     # . . discard first 2 args
     354     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     355     # . . restore ed
     356     59/pop-to-ecx
     357     # check that convert-next-octet didn't abort
     358     # . check-ints-equal(ed->value, 0, msg)
     359     # . . push args
     360     68/push  "F - test-convert-next-octet: unexpected abort"/imm32
     361     68/push  0/imm32
     362     # . . push ed->value
     363     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         .                 # push *(ecx+4)
     364     # . . call
     365     e8/call  check-ints-equal/disp32
     366     # . . discard args
     367     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     368     # return if abort
     369     81          7/subop/compare     1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         0/imm32           # compare *(ecx+4)
     370     75/jump-if-!=  $test-convert-next-octet-handles-Eof:end/disp8
     371     # check-ints-equal(eax, Eof, msg)
     372     # . . push args
     373     68/push  "F - test-convert-next-octet-handles-Eof"/imm32
     374     68/push  0xffffffff/imm32/Eof
     375     50/push-eax
     376     # . . call
     377     e8/call  check-ints-equal/disp32
     378     # . . discard args
     379     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     380 $test-convert-next-octet-handles-Eof:end:
     381     # . epilogue
     382     # don't restore esp from ebp; manually reclaim locals
     383     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     384     5d/pop-to-ebp
     385     c3/return
     386
     387 test-convert-next-octet-aborts-on-single-hex-byte:
     388     # - check that a single unaccompanied hex byte aborts
     389     # This test uses exit-descriptors. Use ebp for setting up local variables.
     390     55/push-ebp
     391     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     392     # clear all streams
     393     # . clear-stream(_test-stream)
     394     # . . push args
     395     68/push  _test-stream/imm32
     396     # . . call
     397     e8/call  clear-stream/disp32
     398     # . . discard args
     399     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     400     # . clear-stream($_test-buffered-file->buffer)
     401     # . . push args
     402     68/push  $_test-buffered-file->buffer/imm32
     403     # . . call
     404     e8/call  clear-stream/disp32
     405     # . . discard args
     406     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     407     # . clear-stream(_test-error-stream)
     408     # . . push args
     409     68/push  _test-error-stream/imm32
     410     # . . call
     411     e8/call  clear-stream/disp32
     412     # . . discard args
     413     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     414     # . clear-stream($_test-error-buffered-file->buffer)
     415     # . . push args
     416     68/push  $_test-error-buffered-file->buffer/imm32
     417     # . . call
     418     e8/call  clear-stream/disp32
     419     # . . discard args
     420     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     421     # initialize '_test-stream' to "a"
     422     # . write(_test-stream, "a")
     423     # . . push args
     424     68/push  "a"/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     # initialize exit-descriptor 'ed' for the call to 'convert-next-octet' below
     431     # . var ed/ecx: exit-descriptor
     432     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
     433     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
     434     # . tailor-exit-descriptor(ed, 12)
     435     # . . push args
     436     68/push  0xc/imm32/nbytes-of-args-for-convert-next-octet
     437     51/push-ecx/ed
     438     # . . call
     439     e8/call  tailor-exit-descriptor/disp32
     440     # . . discard args
     441     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     442     # eax = convert-next-octet(_test-buffered-file, _test-error-buffered-file, ed)
     443     # . . push args
     444     51/push-ecx/ed
     445     68/push  _test-error-buffered-file/imm32
     446     68/push  _test-buffered-file/imm32
     447     # . . call
     448     e8/call  convert-next-octet/disp32
     449     # registers except esp may be clobbered at this point
     450     # pop args to convert-next-octet
     451     # . . discard first 2 args
     452     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     453     # . . restore ed
     454     59/pop-to-ecx
     455     # check that convert-next-octet aborted
     456     # . check-ints-equal(ed->value, 2, msg)
     457     # . . push args
     458     68/push  "F - test-convert-next-octet-aborts-on-single-hex-byte: unexpected abort"/imm32
     459     68/push  2/imm32
     460     # . . push ed->value
     461     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         .                 # push *(ecx+4)
     462     # . . call
     463     e8/call  check-ints-equal/disp32
     464     # . . discard args
     465     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     466 $test-convert-next-octet-aborts-on-single-hex-byte:end:
     467     # . epilogue
     468     # don't restore esp from ebp; manually reclaim locals
     469     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     470     5d/pop-to-ebp
     471     c3/return
     472
     473 # read whitespace until a hex byte, and return it
     474 # return Eof if file ends without finding a hex byte
     475 # on '#' skip all bytes until newline
     476 # abort on any other byte
     477 scan-next-byte:  # in: (addr buffered-file), err: (addr buffered-file), ed: (addr exit-descriptor) -> byte-or-Eof/eax
     478     # pseudocode:
     479     #   while true
     480     #     eax = read-byte-buffered(in)
     481     #     if (eax == Eof) return eax
     482     #     if (hex-digit?(eax)) return eax
     483     #     if (eax == ' ' or '\t' or '\n') continue
     484     #     if (eax == '#') skip-until-newline(in)
     485     #     else error-byte(ed, err, "invalid byte: " eax)
     486     #
     487     # . prologue
     488     55/push-ebp
     489     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     490     # . save registers
     491 $scan-next-byte:loop:
     492     # eax = read-byte-buffered(in)
     493     # . . push args
     494     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
     495     # . . call
     496     e8/call  read-byte-buffered/disp32
     497     # . . discard args
     498     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     499     # if (eax == Eof) return eax
     500     3d/compare-with-eax  0xffffffff/imm32/Eof
     501     74/jump-if-=  $scan-next-byte:end/disp8
     502     # if (hex-digit?(eax)) return eax
     503     # . save eax for now
     504     50/push-eax
     505     # . hex-digit?(eax)
     506     # . . push args
     507     50/push-eax
     508     # . . call
     509     e8/call  hex-digit?/disp32
     510     # . . discard args
     511     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     512     # . compare with 'false'
     513     3d/compare-with-eax  0/imm32/false
     514     # . restore eax (does not affect flags)
     515     58/pop-to-eax
     516     # . check whether to return
     517     75/jump-if-!=  $scan-next-byte:end/disp8
     518 $scan-next-byte:check1:
     519     # if (eax == ' ') continue
     520     3d/compare-eax-and  0x20/imm32/space
     521     74/jump-if-=  $scan-next-byte:loop/disp8
     522     # if (eax == '\t') continue
     523     3d/compare-eax-and  9/imm32/tab
     524     74/jump-if-=  $scan-next-byte:loop/disp8
     525     # if (eax == '\n') continue
     526     3d/compare-eax-and  0xa/imm32/newline
     527     74/jump-if-=  $scan-next-byte:loop/disp8
     528 $scan-next-byte:check2:
     529     # if (eax == '#') skip-until-newline(in)
     530     3d/compare-with-eax  0x23/imm32/hash
     531     75/jump-if-!=  $scan-next-byte:check3/disp8
     532     # . skip-until-newline(in)
     533     # . . push args
     534     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
     535     # . . call
     536     e8/call  skip-until-newline/disp32
     537     # . . discard args
     538     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     539     eb/jump  $scan-next-byte:loop/disp8
     540 $scan-next-byte:check3:
     541     # otherwise error-byte(ed, err, msg, eax)
     542     # . . push args
     543     50/push-eax
     544     68/push  "scan-next-byte: invalid byte"/imm32
     545     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     546     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
     547     # . . call
     548     e8/call  error-byte/disp32  # never returns
     549 $scan-next-byte:end:
     550     # . restore registers
     551     # . epilogue
     552     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
     553     5d/pop-to-ebp
     554     c3/return
     555
     556 test-scan-next-byte:
     557     # - check that the first byte of the input is returned
     558     # This test uses exit-descriptors. Use ebp for setting up local variables.
     559     55/push-ebp
     560     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     561     # clear all streams
     562     # . clear-stream(_test-stream)
     563     # . . push args
     564     68/push  _test-stream/imm32
     565     # . . call
     566     e8/call  clear-stream/disp32
     567     # . . discard args
     568     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     569     # . clear-stream($_test-buffered-file->buffer)
     570     # . . push args
     571     68/push  $_test-buffered-file->buffer/imm32
     572     # . . call
     573     e8/call  clear-stream/disp32
     574     # . . discard args
     575     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     576     # . clear-stream(_test-error-stream)
     577     # . . push args
     578     68/push  _test-error-stream/imm32
     579     # . . call
     580     e8/call  clear-stream/disp32
     581     # . . discard args
     582     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     583     # . clear-stream($_test-error-buffered-file->buffer)
     584     # . . push args
     585     68/push  $_test-error-buffered-file->buffer/imm32
     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     # initialize '_test-stream' to "abc"
     591     # . write(_test-stream, "abc")
     592     # . . push args
     593     68/push  "abc"/imm32
     594     68/push  _test-stream/imm32
     595     # . . call
     596     e8/call  write/disp32
     597     # . . discard args
     598     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     599     # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below
     600     # . var ed/ecx: exit-descriptor
     601     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
     602     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
     603     # . tailor-exit-descriptor(ed, 12)
     604     # . . push args
     605     68/push  0xc/imm32/nbytes-of-args-for-scan-next-byte
     606     51/push-ecx/ed
     607     # . . call
     608     e8/call  tailor-exit-descriptor/disp32
     609     # . . discard args
     610     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     611     # eax = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed)
     612     # . . push args
     613     51/push-ecx/ed
     614     68/push  _test-error-buffered-file/imm32
     615     68/push  _test-buffered-file/imm32
     616     # . . call
     617     e8/call  scan-next-byte/disp32
     618     # registers except esp may be clobbered at this point
     619     # pop args to scan-next-byte
     620     # . . discard first 2 args
     621     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     622     # . . restore ed
     623     59/pop-to-ecx
     624     # check that scan-next-byte didn't abort
     625     # . check-ints-equal(ed->value, 0, msg)
     626     # . . push args
     627     68/push  "F - test-scan-next-byte: unexpected abort"/imm32
     628     68/push  0/imm32
     629     # . . push ed->value
     630     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         .                 # push *(ecx+4)
     631     # . . call
     632     e8/call  check-ints-equal/disp32
     633     # . . discard args
     634     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     635     # return if abort
     636     81          7/subop/compare     1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         0/imm32           # compare *(ecx+4)
     637     75/jump-if-!=  $test-scan-next-byte:end/disp8
     638     # check-ints-equal(eax, 0x61/a, msg)
     639     # . . push args
     640     68/push  "F - test-scan-next-byte"/imm32
     641     68/push  0x61/imm32/a
     642     50/push-eax
     643     # . . call
     644     e8/call  check-ints-equal/disp32
     645     # . . discard args
     646     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     647 $test-scan-next-byte:end:
     648     # . epilogue
     649     # don't restore esp from ebp; manually reclaim locals
     650     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     651     5d/pop-to-ebp
     652     c3/return
     653
     654 test-scan-next-byte-skips-whitespace:
     655     # - check that the first byte after whitespace is returned
     656     # This test uses exit-descriptors. Use ebp for setting up local variables.
     657     55/push-ebp
     658     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     659     # clear all streams
     660     # . clear-stream(_test-stream)
     661     # . . push args
     662     68/push  _test-stream/imm32
     663     # . . call
     664     e8/call  clear-stream/disp32
     665     # . . discard args
     666     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     667     # . clear-stream($_test-buffered-file->buffer)
     668     # . . push args
     669     68/push  $_test-buffered-file->buffer/imm32
     670     # . . call
     671     e8/call  clear-stream/disp32
     672     # . . discard args
     673     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     674     # . clear-stream(_test-error-stream)
     675     # . . push args
     676     68/push  _test-error-stream/imm32
     677     # . . call
     678     e8/call  clear-stream/disp32
     679     # . . discard args
     680     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     681     # . clear-stream($_test-error-buffered-file->buffer)
     682     # . . push args
     683     68/push  $_test-error-buffered-file->buffer/imm32
     684     # . . call
     685     e8/call  clear-stream/disp32
     686     # . . discard args
     687     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     688     # initialize '_test-stream' to input with leading whitespace
     689     # . write(_test-stream, text)
     690     # . . push args
     691     68/push  "  abc"/imm32
     692     68/push  _test-stream/imm32
     693     # . . call
     694     e8/call  write/disp32
     695     # . . discard args
     696     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     697     # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below
     698     # . var ed/ecx: exit-descriptor
     699     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
     700     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
     701     # . tailor-exit-descriptor(ed, 12)
     702     # . . push args
     703     68/push  0xc/imm32/nbytes-of-args-for-scan-next-byte
     704     51/push-ecx/ed
     705     # . . call
     706     e8/call  tailor-exit-descriptor/disp32
     707     # . . discard args
     708     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     709     # eax = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed)
     710     # . . push args
     711     51/push-ecx/ed
     712     68/push  _test-error-buffered-file/imm32
     713     68/push  _test-buffered-file/imm32
     714     # . . call
     715     e8/call  scan-next-byte/disp32
     716     # registers except esp may be clobbered at this point
     717     # pop args to scan-next-byte
     718     # . . discard first 2 args
     719     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     720     # . . restore ed
     721     59/pop-to-ecx
     722     # check that scan-next-byte didn't abort
     723     # . check-ints-equal(ed->value, 0, msg)
     724     # . . push args
     725     68/push  "F - test-scan-next-byte-skips-whitespace: unexpected abort"/imm32
     726     68/push  0/imm32
     727     # . . push ed->value
     728     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         .                 # push *(ecx+4)
     729     # . . call
     730     e8/call  check-ints-equal/disp32
     731     # . . discard args
     732     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     733     # return if abort
     734     81          7/subop/compare     1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         0/imm32           # compare *(ecx+4)
     735     75/jump-if-!=  $test-scan-next-byte-skips-whitespace:end/disp8
     736     # check-ints-equal(eax, 0x61/a, msg)
     737     # . . push args
     738     68/push  "F - test-scan-next-byte-skips-whitespace"/imm32
     739     68/push  0x61/imm32/a
     740     50/push-eax
     741     # . . call
     742     e8/call  check-ints-equal/disp32
     743     # . . discard args
     744     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     745 $test-scan-next-byte-skips-whitespace:end:
     746     # . epilogue
     747     # don't restore esp from ebp; manually reclaim locals
     748     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     749     5d/pop-to-ebp
     750     c3/return
     751
     752 test-scan-next-byte-skips-comment:
     753     # - check that the first byte after a comment (and newline) is returned
     754     # This test uses exit-descriptors. Use ebp for setting up local variables.
     755     55/push-ebp
     756     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     757     # clear all streams
     758     # . clear-stream(_test-stream)
     759     # . . push args
     760     68/push  _test-stream/imm32
     761     # . . call
     762     e8/call  clear-stream/disp32
     763     # . . discard args
     764     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     765     # . clear-stream($_test-buffered-file->buffer)
     766     # . . push args
     767     68/push  $_test-buffered-file->buffer/imm32
     768     # . . call
     769     e8/call  clear-stream/disp32
     770     # . . discard args
     771     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     772     # . clear-stream(_test-error-stream)
     773     # . . push args
     774     68/push  _test-error-stream/imm32
     775     # . . call
     776     e8/call  clear-stream/disp32
     777     # . . discard args
     778     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     779     # . clear-stream($_test-error-buffered-file->buffer)
     780     # . . push args
     781     68/push  $_test-error-buffered-file->buffer/imm32
     782     # . . call
     783     e8/call  clear-stream/disp32
     784     # . . discard args
     785     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     786     # initialize '_test-stream' to input with leading comment
     787     # . write(_test-stream, comment)
     788     # . . push args
     789     68/push  "#x\n"/imm32
     790     68/push  _test-stream/imm32
     791     # . . call
     792     e8/call  write/disp32
     793     # . . discard args
     794     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     795     # . write(_test-stream, real text)
     796     # . . push args
     797     68/push  "ab"/imm32
     798     68/push  _test-stream/imm32
     799     # . . call
     800     e8/call  write/disp32
     801     # . . discard args
     802     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     803     # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below
     804     # . var ed/ecx: exit-descriptor
     805     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
     806     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
     807     # . tailor-exit-descriptor(ed, 12)
     808     # . . push args
     809     68/push  0xc/imm32/nbytes-of-args-for-scan-next-byte
     810     51/push-ecx/ed
     811     # . . call
     812     e8/call  tailor-exit-descriptor/disp32
     813     # . . discard args
     814     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     815     # eax = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed)
     816     # . . push args
     817     51/push-ecx/ed
     818     68/push  _test-error-buffered-file/imm32
     819     68/push  _test-buffered-file/imm32
     820     # . . call
     821     e8/call  scan-next-byte/disp32
     822     # registers except esp may be clobbered at this point
     823     # pop args to scan-next-byte
     824     # . . discard first 2 args
     825     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     826     # . . restore ed
     827     59/pop-to-ecx
     828     # check that scan-next-byte didn't abort
     829     # . check-ints-equal(ed->value, 0, msg)
     830     # . . push args
     831     68/push  "F - test-scan-next-byte-skips-comment: unexpected abort"/imm32
     832     68/push  0/imm32
     833     # . . push ed->value
     834     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         .                 # push *(ecx+4)
     835     # . . call
     836     e8/call  check-ints-equal/disp32
     837     # . . discard args
     838     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     839     # return if abort
     840     81          7/subop/compare     1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         0/imm32           # compare *(ecx+4)
     841     75/jump-if-!=  $test-scan-next-byte-skips-comment:end/disp8
     842     # check-ints-equal(eax, 0x61/a, msg)
     843     # . . push args
     844     68/push  "F - test-scan-next-byte-skips-comment"/imm32
     845     68/push  0x61/imm32/a
     846     50/push-eax
     847     # . . call
     848     e8/call  check-ints-equal/disp32
     849     # . . discard args
     850     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     851 $test-scan-next-byte-skips-comment:end:
     852     # . epilogue
     853     # don't restore esp from ebp; manually reclaim locals
     854     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     855     5d/pop-to-ebp
     856     c3/return
     857
     858 test-scan-next-byte-skips-comment-and-whitespace:
     859     # - check that the first byte after a comment and any further whitespace is returned
     860     # This test uses exit-descriptors. Use ebp for setting up local variables.
     861     55/push-ebp
     862     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     863     # clear all streams
     864     # . clear-stream(_test-stream)
     865     # . . push args
     866     68/push  _test-stream/imm32
     867     # . . call
     868     e8/call  clear-stream/disp32
     869     # . . discard args
     870     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     871     # . clear-stream($_test-buffered-file->buffer)
     872     # . . push args
     873     68/push  $_test-buffered-file->buffer/imm32
     874     # . . call
     875     e8/call  clear-stream/disp32
     876     # . . discard args
     877     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     878     # . clear-stream(_test-error-stream)
     879     # . . push args
     880     68/push  _test-error-stream/imm32
     881     # . . call
     882     e8/call  clear-stream/disp32
     883     # . . discard args
     884     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     885     # . clear-stream($_test-error-buffered-file->buffer)
     886     # . . push args
     887     68/push  $_test-error-buffered-file->buffer/imm32
     888     # . . call
     889     e8/call  clear-stream/disp32
     890     # . . discard args
     891     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     892     # initialize '_test-stream' to input with leading comment and more whitespace after newline
     893     # . write(_test-stream, comment)
     894     # . . push args
     895     68/push  "#x\n"/imm32
     896     68/push  _test-stream/imm32
     897     # . . call
     898     e8/call  write/disp32
     899     # . . discard args
     900     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     901     # . write(_test-stream, real text)
     902     # . . push args
     903     68/push  " ab"/imm32
     904     68/push  _test-stream/imm32
     905     # . . call
     906     e8/call  write/disp32
     907     # . . discard args
     908     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     909     # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below
     910     # . var ed/ecx: exit-descriptor
     911     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
     912     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
     913     # . tailor-exit-descriptor(ed, 12)
     914     # . . push args
     915     68/push  0xc/imm32/nbytes-of-args-for-scan-next-byte
     916     51/push-ecx/ed
     917     # . . call
     918     e8/call  tailor-exit-descriptor/disp32
     919     # . . discard args
     920     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     921     # eax = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed)
     922     # . . push args
     923     51/push-ecx/ed
     924     68/push  _test-error-buffered-file/imm32
     925     68/push  _test-buffered-file/imm32
     926     # . . call
     927     e8/call  scan-next-byte/disp32
     928     # registers except esp may be clobbered at this point
     929     # pop args to scan-next-byte
     930     # . . discard first 2 args
     931     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     932     # . . restore ed
     933     59/pop-to-ecx
     934     # check that scan-next-byte didn't abort
     935     # . check-ints-equal(ed->value, 0, msg)
     936     # . . push args
     937     68/push  "F - test-scan-next-byte-skips-comment-and-whitespace: unexpected abort"/imm32
     938     68/push  0/imm32
     939     # . . push ed->value
     940     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         .                 # push *(ecx+4)
     941     # . . call
     942     e8/call  check-ints-equal/disp32
     943     # . . discard args
     944     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     945     # return if abort
     946     81          7/subop/compare     1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         0/imm32           # compare *(ecx+4)
     947     75/jump-if-!=  $test-scan-next-byte-skips-comment-and-whitespace:end/disp8
     948     # check-ints-equal(eax, 0x61/a, msg)
     949     # . . push args
     950     68/push  "F - test-scan-next-byte-skips-comment-and-whitespace"/imm32
     951     68/push  0x61/imm32/a
     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 $test-scan-next-byte-skips-comment-and-whitespace:end:
     958     # . epilogue
     959     # don't restore esp from ebp; manually reclaim locals
     960     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     961     5d/pop-to-ebp
     962     c3/return
     963
     964 test-scan-next-byte-skips-whitespace-and-comment:
     965     # - check that the first byte after any whitespace and comments is returned
     966     # This test uses exit-descriptors. Use ebp for setting up local variables.
     967     55/push-ebp
     968     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     969     # clear all streams
     970     # . clear-stream(_test-stream)
     971     # . . push args
     972     68/push  _test-stream/imm32
     973     # . . call
     974     e8/call  clear-stream/disp32
     975     # . . discard args
     976     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     977     # . clear-stream($_test-buffered-file->buffer)
     978     # . . push args
     979     b8/copy-to-eax  _test-buffered-file/imm32
     980     05/add-to-eax  4/imm32
     981     50/push-eax
     982     # . . call
     983     e8/call  clear-stream/disp32
     984     # . . discard args
     985     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     986     # . clear-stream(_test-error-stream)
     987     # . . push args
     988     68/push  _test-error-stream/imm32
     989     # . . call
     990     e8/call  clear-stream/disp32
     991     # . . discard args
     992     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     993     # . clear-stream($_test-error-buffered-file->buffer)
     994     # . . push args
     995     68/push  $_test-error-buffered-file->buffer/imm32
     996     # . . call
     997     e8/call  clear-stream/disp32
     998     # . . discard args
     999     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1000     # initialize '_test-stream' to input with leading whitespace and comment
    1001     # . write(_test-stream, comment)
    1002     # . . push args
    1003     68/push  " #x\n"/imm32
    1004     68/push  _test-stream/imm32
    1005     # . . call
    1006     e8/call  write/disp32
    1007     # . . discard args
    1008     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1009     # . write(_test-stream, real text)
    1010     # . . push args
    1011     68/push  "ab"/imm32
    1012     68/push  _test-stream/imm32
    1013     # . . call
    1014     e8/call  write/disp32
    1015     # . . discard args
    1016     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1017     # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below
    1018     # . var ed/ecx: exit-descriptor
    1019     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
    1020     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
    1021     # . tailor-exit-descriptor(ed, 12)
    1022     # . . push args
    1023     68/push  0xc/imm32/nbytes-of-args-for-scan-next-byte
    1024     51/push-ecx/ed
    1025     # . . call
    1026     e8/call  tailor-exit-descriptor/disp32
    1027     # . . discard args
    1028     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1029     # eax = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed)
    1030     # . . push args
    1031     51/push-ecx/ed
    1032     68/push  _test-error-buffered-file/imm32
    1033     68/push  _test-buffered-file/imm32
    1034     # . . call
    1035     e8/call  scan-next-byte/disp32
    1036     # registers except esp may be clobbered at this point
    1037     # pop args to scan-next-byte
    1038     # . . discard first 2 args
    1039     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1040     # . . restore ed
    1041     59/pop-to-ecx
    1042     # check that scan-next-byte didn't abort
    1043     # . check-ints-equal(ed->value, 0, msg)
    1044     # . . push args
    1045     68/push  "F - test-scan-next-byte-skips-whitespace-and-comment: unexpected abort"/imm32
    1046     68/push  0/imm32
    1047     # . . push ed->value
    1048     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         .                 # push *(ecx+4)
    1049     # . . call
    1050     e8/call  check-ints-equal/disp32
    1051     # . . discard args
    1052     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1053     # return if abort
    1054     81          7/subop/compare     1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         0/imm32           # compare *(ecx+4)
    1055     75/jump-if-!=  $test-scan-next-byte-skips-whitespace-and-comment:end/disp8
    1056     # check-ints-equal(eax, 0x61/a, msg)
    1057     # . . push args
    1058     68/push  "F - test-scan-next-byte-skips-whitespace-and-comment"/imm32
    1059     68/push  0x61/imm32/a
    1060     50/push-eax
    1061     # . . call
    1062     e8/call  check-ints-equal/disp32
    1063     # . . discard args
    1064     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1065 $test-scan-next-byte-skips-whitespace-and-comment:end:
    1066     # . epilogue
    1067     # don't restore esp from ebp; manually reclaim locals
    1068     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1069     5d/pop-to-ebp
    1070     c3/return
    1071
    1072 test-scan-next-byte-reads-final-byte:
    1073     # - check that the final byte in input is returned
    1074     # This test uses exit-descriptors. Use ebp for setting up local variables.
    1075     55/push-ebp
    1076     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1077     # clear all streams
    1078     # . clear-stream(_test-stream)
    1079     # . . push args
    1080     68/push  _test-stream/imm32
    1081     # . . call
    1082     e8/call  clear-stream/disp32
    1083     # . . discard args
    1084     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1085     # . clear-stream($_test-buffered-file->buffer)
    1086     # . . push args
    1087     68/push  $_test-buffered-file->buffer/imm32
    1088     # . . call
    1089     e8/call  clear-stream/disp32
    1090     # . . discard args
    1091     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1092     # . clear-stream(_test-error-stream)
    1093     # . . push args
    1094     68/push  _test-error-stream/imm32
    1095     # . . call
    1096     e8/call  clear-stream/disp32
    1097     # . . discard args
    1098     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1099     # . clear-stream($_test-error-buffered-file->buffer)
    1100     # . . push args
    1101     68/push  $_test-error-buffered-file->buffer/imm32
    1102     # . . call
    1103     e8/call  clear-stream/disp32
    1104     # . . discard args
    1105     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1106     # initialize '_test-stream' to input with single character
    1107     # . write(_test-stream, character)
    1108     # . . push args
    1109     68/push  "a"/imm32
    1110     68/push  _test-stream/imm32
    1111     # . . call
    1112     e8/call  write/disp32
    1113     # . . discard args
    1114     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1115     # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below
    1116     # . var ed/ecx: exit-descriptor
    1117     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
    1118     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
    1119     # . tailor-exit-descriptor(ed, 12)
    1120     # . . push args
    1121     68/push  0xc/imm32/nbytes-of-args-for-scan-next-byte
    1122     51/push-ecx/ed
    1123     # . . call
    1124     e8/call  tailor-exit-descriptor/disp32
    1125     # . . discard args
    1126     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1127     # eax = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed)
    1128     # . . push args
    1129     51/push-ecx/ed
    1130     68/push  _test-error-buffered-file/imm32
    1131     68/push  _test-buffered-file/imm32
    1132     # . . call
    1133     e8/call  scan-next-byte/disp32
    1134     # registers except esp may be clobbered at this point
    1135     # pop args to scan-next-byte
    1136     # . . discard first 2 args
    1137     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1138     # . . restore ed
    1139     59/pop-to-ecx
    1140     # check that scan-next-byte didn't abort
    1141     # . check-ints-equal(ed->value, 0, msg)
    1142     # . . push args
    1143     68/push  "F - test-scan-next-byte-reads-final-byte: unexpected abort"/imm32
    1144     68/push  0/imm32
    1145     # . . push ed->value
    1146     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         .                 # push *(ecx+4)
    1147     # . . call
    1148     e8/call  check-ints-equal/disp32
    1149     # . . discard args
    1150     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1151     # return if abort
    1152     81          7/subop/compare     1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         0/imm32           # compare *(ecx+4)
    1153     75/jump-if-!=  $test-scan-next-byte-reads-final-byte:end/disp8
    1154     # check-ints-equal(eax, 0x61/a, msg)
    1155     # . . push args
    1156     68/push  "F - test-scan-next-byte-reads-final-byte"/imm32
    1157     68/push  0x61/imm32/a
    1158     50/push-eax
    1159     # . . call
    1160     e8/call  check-ints-equal/disp32
    1161     # . . discard args
    1162     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1163 $test-scan-next-byte-reads-final-byte:end:
    1164     # . epilogue
    1165     # don't restore esp from ebp; manually reclaim locals
    1166     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1167     5d/pop-to-ebp
    1168     c3/return
    1169
    1170 test-scan-next-byte-handles-Eof:
    1171     # - check that the right sentinel value is returned when there's no data remaining to be read
    1172     # This test uses exit-descriptors. Use ebp for setting up local variables.
    1173     55/push-ebp
    1174     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1175     # clear all streams
    1176     # . clear-stream(_test-stream)
    1177     # . . push args
    1178     68/push  _test-stream/imm32
    1179     # . . call
    1180     e8/call  clear-stream/disp32
    1181     # . . discard args
    1182     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1183     # . clear-stream($_test-buffered-file->buffer)
    1184     # . . push args
    1185     68/push  $_test-buffered-file->buffer/imm32
    1186     # . . call
    1187     e8/call  clear-stream/disp32
    1188     # . . discard args
    1189     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1190     # . clear-stream(_test-error-stream)
    1191     # . . push args
    1192     68/push  _test-error-stream/imm32
    1193     # . . call
    1194     e8/call  clear-stream/disp32
    1195     # . . discard args
    1196     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1197     # . clear-stream($_test-error-buffered-file->buffer)
    1198     # . . push args
    1199     68/push  $_test-error-buffered-file->buffer/imm32
    1200     # . . call
    1201     e8/call  clear-stream/disp32
    1202     # . . discard args
    1203     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1204     # leave '_test-stream' empty
    1205     # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below
    1206     # . var ed/ecx: exit-descriptor
    1207     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
    1208     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
    1209     # . tailor-exit-descriptor(ed, 12)
    1210     # . . push args
    1211     68/push  0xc/imm32/nbytes-of-args-for-scan-next-byte
    1212     51/push-ecx/ed
    1213     # . . call
    1214     e8/call  tailor-exit-descriptor/disp32
    1215     # . . discard args
    1216     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1217     # eax = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed)
    1218     # . . push args
    1219     51/push-ecx/ed
    1220     68/push  _test-error-buffered-file/imm32
    1221     68/push  _test-buffered-file/imm32
    1222     # . . call
    1223     e8/call  scan-next-byte/disp32
    1224     # registers except esp may be clobbered at this point
    1225     # pop args to scan-next-byte
    1226     # . . discard first 2 args
    1227     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1228     # . . restore ed
    1229     59/pop-to-ecx
    1230     # check that scan-next-byte didn't abort
    1231     # . check-ints-equal(ed->value, 0, msg)
    1232     # . . push args
    1233     68/push  "F - test-scan-next-byte-handles-Eof: unexpected abort"/imm32
    1234     68/push  0/imm32
    1235     # . . push ed->value
    1236     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         .                 # push *(ecx+4)
    1237     # . . call
    1238     e8/call  check-ints-equal/disp32
    1239     # . . discard args
    1240     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1241     # return if abort
    1242     81          7/subop/compare     1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         0/imm32           # compare *(ecx+4)
    1243     75/jump-if-!=  $test-scan-next-byte-handles-Eof:end/disp8
    1244     # check-ints-equal(eax, Eof, msg)
    1245     # . . push args
    1246     68/push  "F - test-scan-next-byte-handles-Eof"/imm32
    1247     68/push  0xffffffff/imm32/Eof
    1248     50/push-eax
    1249     # . . call
    1250     e8/call  check-ints-equal/disp32
    1251     # . . discard args
    1252     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1253 $test-scan-next-byte-handles-Eof:end:
    1254     # . epilogue
    1255     # don't restore esp from ebp; manually reclaim locals
    1256     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1257     5d/pop-to-ebp
    1258     c3/return
    1259
    1260 test-scan-next-byte-aborts-on-invalid-byte:
    1261     # - check that the a bad byte immediately aborts
    1262     # This test uses exit-descriptors. Use ebp for setting up local variables.
    1263     55/push-ebp
    1264     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1265     # clear all streams
    1266     # . clear-stream(_test-stream)
    1267     # . . push args
    1268     68/push  _test-stream/imm32
    1269     # . . call
    1270     e8/call  clear-stream/disp32
    1271     # . . discard args
    1272     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1273     # . clear-stream($_test-buffered-file->buffer)
    1274     # . . push args
    1275     68/push  $_test-buffered-file->buffer/imm32
    1276     # . . call
    1277     e8/call  clear-stream/disp32
    1278     # . . discard args
    1279     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1280     # . clear-stream(_test-error-stream)
    1281     # . . push args
    1282     68/push  _test-error-stream/imm32
    1283     # . . call
    1284     e8/call  clear-stream/disp32
    1285     # . . discard args
    1286     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1287     # . clear-stream($_test-error-buffered-file->buffer)
    1288     # . . push args
    1289     68/push  $_test-error-buffered-file->buffer/imm32
    1290     # . . call
    1291     e8/call  clear-stream/disp32
    1292     # . . discard args
    1293     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1294     # initialize '_test-stream' to "x"
    1295     # . write(_test-stream, "x")
    1296     # . . push args
    1297     68/push  "x"/imm32
    1298     68/push  _test-stream/imm32
    1299     # . . call
    1300     e8/call  write/disp32
    1301     # . . discard args
    1302     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1303     # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below
    1304     # . var ed/ecx: exit-descriptor
    1305     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
    1306     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
    1307     # . tailor-exit-descriptor(ed, 12)
    1308     # . . push args
    1309     68/push  0xc/imm32/nbytes-of-args-for-scan-next-byte
    1310     51/push-ecx/ed
    1311     # . . call
    1312     e8/call  tailor-exit-descriptor/disp32
    1313     # . . discard args
    1314     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1315     # eax = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed)
    1316     # . . push args
    1317     51/push-ecx/ed
    1318     68/push  _test-error-buffered-file/imm32
    1319     68/push  _test-buffered-file/imm32
    1320     # . . call
    1321     e8/call  scan-next-byte/disp32
    1322     # registers except esp may be clobbered at this point
    1323     # pop args to scan-next-byte
    1324     # . . discard first 2 args
    1325     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1326     # . . restore ed
    1327     59/pop-to-ecx
    1328     # check that scan-next-byte aborted
    1329     # . check-ints-equal(ed->value, 2, msg)
    1330     # . . push args
    1331     68/push  "F - test-scan-next-byte-aborts-on-invalid-byte"/imm32
    1332     68/push  2/imm32
    1333     # . . push ed->value
    1334     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         .                 # push *(ecx+4)
    1335     # . . call
    1336     e8/call  check-ints-equal/disp32
    1337     # . . discard args
    1338     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1339 $test-scan-next-byte-aborts-on-invalid-byte:end:
    1340     # . epilogue
    1341     # don't restore esp from ebp; manually reclaim locals
    1342     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1343     5d/pop-to-ebp
    1344     c3/return
    1345
    1346 skip-until-newline:  # in: (addr buffered-file)
    1347     # pseudocode:
    1348     #   push eax
    1349     #   while true
    1350     #     eax = read-byte-buffered(in)
    1351     #     if (eax == Eof) break
    1352     #     if (eax == 0x0a) break
    1353     #   pop eax
    1354     # . prologue
    1355     55/push-ebp
    1356     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1357     # . save registers
    1358     50/push-eax
    1359 $skip-until-newline:loop:
    1360     # . eax = read-byte-buffered(in)
    1361     # . . push args
    1362     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    1363     # . . call
    1364     e8/call  read-byte-buffered/disp32
    1365     # . . discard args
    1366     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1367     # . if (eax == Eof) break
    1368     3d/compare-eax-and  0xffffffff/imm32/Eof
    1369     74/jump-if-=  $skip-until-newline:end/disp8
    1370     # . if (eax != 0xa/newline) loop
    1371     3d/compare-eax-and  0xa/imm32/newline
    1372     75/jump-if-!=  $skip-until-newline:loop/disp8
    1373 $skip-until-newline:end:
    1374     # . restore registers
    1375     58/pop-to-eax
    1376     # . epilogue
    1377     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1378     5d/pop-to-ebp
    1379     c3/return
    1380
    1381 test-skip-until-newline:
    1382     # - check that the read pointer points after the newline
    1383     # setup
    1384     # . clear-stream(_test-stream)
    1385     # . . push args
    1386     68/push  _test-stream/imm32
    1387     # . . call
    1388     e8/call  clear-stream/disp32
    1389     # . . discard args
    1390     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1391     # . clear-stream($_test-buffered-file->buffer)
    1392     # . . push args
    1393     68/push  $_test-buffered-file->buffer/imm32
    1394     # . . call
    1395     e8/call  clear-stream/disp32
    1396     # . . discard args
    1397     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1398     # initialize '_test-stream' to "abc\nde"
    1399     # . write(_test-stream, "abc")
    1400     # . . push args
    1401     68/push  "abc\n"/imm32
    1402     68/push  _test-stream/imm32
    1403     # . . call
    1404     e8/call  write/disp32
    1405     # . . discard args
    1406     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1407     # . write(_test-stream, "de")
    1408     # . . push args
    1409     68/push  "de"/imm32
    1410     68/push  _test-stream/imm32
    1411     # . . call
    1412     e8/call  write/disp32
    1413     # . . discard args
    1414     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1415     # skip-until-newline(_test-buffered-file)
    1416     # . . push args
    1417     68/push  _test-buffered-file/imm32
    1418     # . . call
    1419     e8/call  skip-until-newline/disp32
    1420     # . . discard args
    1421     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1422     # check-ints-equal(_test-buffered-file->read, 4, msg)
    1423     # . . push args
    1424     68/push  "F - test-skip-until-newline"/imm32
    1425     68/push  4/imm32
    1426     b8/copy-to-eax  _test-buffered-file/imm32
    1427     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           8/disp8         .                 # push *(eax+8)
    1428     # . . call
    1429     e8/call  check-ints-equal/disp32
    1430     # . . discard args
    1431     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1432     # . end
    1433     c3/return
    1434
    1435 # . . vim:nowrap:textwidth=0