https://github.com/akkartik/mu/blob/master/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:
   6 #   $ ./subx translate init.linux 0*.subx apps/subx-params.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     # . 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-lesser-or-equal  $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-equal  $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     # var ed/eax : exit-descriptor
  58     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
  59     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           4/r32/esp   .               .                 # copy esp to eax
  60     # configure ed to really exit()
  61     # . ed->target = 0
  62     c7          0/subop/copy        0/mod/direct    0/rm32/eax    .           .             .           .           .               0/imm32           # copy to *eax
  63     # subx-hex(Stdin, 1/stdout, 2/stderr, ed)
  64     # . . push args
  65     50/push-eax/ed
  66     68/push  Stderr/imm32
  67     68/push  Stdout/imm32
  68     68/push  Stdin/imm32
  69     # . . call
  70     e8/call  subx-hex/disp32
  71     # . . discard args
  72     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
  73     # syscall(exit, 0)
  74     bb/copy-to-ebx  0/imm32
  75 $subx-hex-main:end:
  76     b8/copy-to-eax  1/imm32/exit
  77     cd/syscall  0x80/imm8
  78 
  79 # the main entry point
  80 subx-hex:  # in : (addr buffered-file), out : (addr buffered-file), err : (addr buffered-file), ed : (addr exit-descriptor)
  81     # pseudocode:
  82     #   while true
  83     #     eax = convert-next-octet(in, err, ed)
  84     #     if (eax == Eof) break
  85     #     write-byte-buffered(out, AL)
  86     #   flush(out)
  87     #
  88     # . prologue
  89     55/push-ebp
  90     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
  91     # . save registers
  92     50/push-eax
  93 $subx-hex:loop:
  94     # eax = convert-next-octet(in, err, ed)
  95     # . . push args
  96     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x14/disp8      .                 # push *(ebp+20)
  97     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
  98     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
  99     # . . call
 100     e8/call  convert-next-octet/disp32
 101     # . . discard first 2 args
 102     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 103     # if (eax == Eof) break
 104     3d/compare-eax-and  0xffffffff/imm32/Eof
 105     74/jump-if-equal  $subx-hex:loop-end/disp8
 106     # write-byte-buffered(out, AL)
 107     # . . push args
 108     50/push-eax
 109     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 110     # . . call
 111     e8/call  write-byte-buffered/disp32
 112     # . . discard args
 113     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 114     # loop
 115     eb/jump  $subx-hex:loop/disp8
 116 $subx-hex:loop-end:
 117     # flush(out)
 118     # . . push args
 119     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 120     # . . call
 121     e8/call  flush/disp32
 122     # . . discard args
 123     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 124 $subx-hex:end:
 125     # . restore registers
 126     58/pop-to-eax
 127     # . epilogue
 128     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 129     5d/pop-to-ebp
 130     c3/return
 131 
 132 # read bytes from 'in' until a sequence of two lowercase hex (0-9, a-f) bytes
 133 # skip spaces and newlines
 134 # on '#' skip bytes until newline
 135 # raise an error and abort on all other unexpected bytes
 136 # return in eax an _octet_ containing the binary value of the two hex characters
 137 # return Eof on reaching end of file
 138 convert-next-octet:  # in : (addr buffered-file), err : (addr buffered-file), ed : (addr exit-descriptor) -> byte-or-Eof/eax
 139     # pseudocode:
 140     #   eax = scan-next-byte(in, err, ed)
 141     #   if (eax == Eof) return
 142     #   ecx = from-hex-char(eax)
 143     #   eax = scan-next-byte(in, err, ed)
 144     #   if (eax == Eof) error("partial byte found.")
 145     #   eax = from-hex-char(eax)
 146     #   eax = (ecx << 4) | eax
 147     #   return
 148     #
 149     # . prologue
 150     55/push-ebp
 151     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 152     # . save registers
 153     51/push-ecx
 154     # eax = scan-next-byte(in, err, ed)
 155     # . . push args
 156     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
 157     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 158     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 159     # . . call
 160     e8/call  scan-next-byte/disp32
 161     # . . discard args
 162     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 163     # if (eax == Eof) return
 164     3d/compare-eax-and  0xffffffff/imm32/Eof
 165     74/jump-if-equal  $convert-next-octet:end/disp8
 166     # eax = from-hex-char(eax)
 167     e8/call from-hex-char/disp32
 168     # ecx = eax
 169     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy eax to ecx
 170     # eax = scan-next-byte(in, err, ed)
 171     # . . push args
 172     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
 173     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 174     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 175     # . . call
 176     e8/call  scan-next-byte/disp32
 177     # . . discard args
 178     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 179     # if (eax == Eof) error(ed, err, "partial byte found.")
 180     3d/compare-eax-and  0xffffffff/imm32/Eof
 181     75/jump-if-not-equal  $convert-next-octet:convert/disp8
 182     # . error-byte(ed, err, msg, '.')  # reusing error-byte to avoid creating _yet_ another helper
 183     # . . push args
 184     68/push  0x2e/imm32/period/dummy
 185     68/push  "convert-next-octet: partial byte found"/imm32
 186     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 187     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
 188     # . . call
 189     e8/call  error-byte/disp32  # never returns
 190 $convert-next-octet:convert:
 191     # eax = from-hex-char(eax)
 192     e8/call from-hex-char/disp32
 193     # eax = (ecx << 4) | eax
 194     # . ecx <<= 4
 195     c1/shift    4/subop/left        3/mod/direct    1/rm32/ecx    .           .             .           .           .               4/imm8            # shift ecx left by 4 bits
 196     # . eax |= ecx
 197     09/or                           3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # eax = bitwise OR with ecx
 198 $convert-next-octet:end:
 199     # . restore registers
 200     59/pop-to-ecx
 201     # . epilogue
 202     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 203     5d/pop-to-ebp
 204     c3/return
 205 
 206 test-convert-next-octet:
 207     # - check that the first two bytes of the input are assembled into the resulting octet
 208     # This test uses exit-descriptors. Use ebp for setting up local variables.
 209     55/push-ebp
 210     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 211     # clear all streams
 212     # . clear-stream(_test-stream)
 213     # . . push args
 214     68/push  _test-stream/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-buffered-file->buffer)
 220     # . . push args
 221     68/push  $_test-buffered-file->buffer/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-stream)
 227     # . . push args
 228     68/push  _test-error-stream/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     # . clear-stream($_test-error-buffered-file->buffer)
 234     # . . push args
 235     68/push  $_test-error-buffered-file->buffer/imm32
 236     # . . call
 237     e8/call  clear-stream/disp32
 238     # . . discard args
 239     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 240     # initialize '_test-stream' to "abc"
 241     # . write(_test-stream, "abc")
 242     # . . push args
 243     68/push  "abc"/imm32
 244     68/push  _test-stream/imm32
 245     # . . call
 246     e8/call  write/disp32
 247     # . . discard args
 248     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 249     # initialize exit-descriptor 'ed' for the call to 'convert-next-octet' below
 250     # . var ed/ecx : exit-descriptor
 251     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
 252     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
 253     # . tailor-exit-descriptor(ed, 12)
 254     # . . push args
 255     68/push  0xc/imm32/nbytes-of-args-for-convert-next-octet
 256     51/push-ecx/ed
 257     # . . call
 258     e8/call  tailor-exit-descriptor/disp32
 259     # . . discard args
 260     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 261     # eax = convert-next-octet(_test-buffered-file, _test-error-buffered-file, ed)
 262     # . . push args
 263     51/push-ecx/ed
 264     68/push  _test-error-buffered-file/imm32
 265     68/push  _test-buffered-file/imm32
 266     # . . call
 267     e8/call  convert-next-octet/disp32
 268     # registers except esp may be clobbered at this point
 269     # pop args to convert-next-octet
 270     # . . discard first 2 args
 271     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 272     # . . restore ed
 273     59/pop-to-ecx
 274     # check that convert-next-octet didn't abort
 275     # . check-ints-equal(ed->value, 0, msg)
 276     # . . push args
 277     68/push  "F - test-convert-next-octet: unexpected abort"/imm32
 278     68/push  0/imm32
 279     # . . push ed->value
 280     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         .                 # push *(ecx+4)
 281     # . . call
 282     e8/call  check-ints-equal/disp32
 283     # . . discard args
 284     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 285     # return if abort
 286     81          7/subop/compare     1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         0/imm32           # compare *(ecx+4)
 287     75/jump-if-not-equal  $test-convert-next-octet:end/disp8
 288     # check-ints-equal(eax, 0xab, msg)
 289     # . . push args
 290     68/push  "F - test-convert-next-octet"/imm32
 291     68/push  0xab/imm32/ab
 292     50/push-eax
 293     # . . call
 294     e8/call  check-ints-equal/disp32
 295     # . . discard args
 296     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 297 $test-convert-next-octet:end:
 298     # . epilogue
 299     # don't restore esp from ebp; manually reclaim locals
 300     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 301     5d/pop-to-ebp
 302     c3/return
 303 
 304 test-convert-next-octet-handles-Eof:
 305     # - check that reaching end of file returns Eof
 306     # This test uses exit-descriptors. Use ebp for setting up local variables.
 307     55/push-ebp
 308     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 309     # clear all streams
 310     # . clear-stream(_test-stream)
 311     # . . push args
 312     68/push  _test-stream/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-buffered-file->buffer)
 318     # . . push args
 319     68/push  $_test-buffered-file->buffer/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-stream)
 325     # . . push args
 326     68/push  _test-error-stream/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     # . clear-stream($_test-error-buffered-file->buffer)
 332     # . . push args
 333     68/push  $_test-error-buffered-file->buffer/imm32
 334     # . . call
 335     e8/call  clear-stream/disp32
 336     # . . discard args
 337     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 338     # don't initialize '_test-stream'
 339     # initialize exit-descriptor 'ed' for the call to 'convert-next-octet' below
 340     # . var ed/ecx : exit-descriptor
 341     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
 342     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
 343     # . tailor-exit-descriptor(ed, 12)
 344     # . . push args
 345     68/push  0xc/imm32/nbytes-of-args-for-convert-next-octet
 346     51/push-ecx/ed
 347     # . . call
 348     e8/call  tailor-exit-descriptor/disp32
 349     # . . discard args
 350     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 351     # eax = convert-next-octet(_test-buffered-file, _test-error-buffered-file, ed)
 352     # . . push args
 353     51/push-ecx/ed
 354     68/push  _test-error-buffered-file/imm32
 355     68/push  _test-buffered-file/imm32
 356     # . . call
 357     e8/call  convert-next-octet/disp32
 358     # registers except esp may be clobbered at this point
 359     # pop args to convert-next-octet
 360     # . . discard first 2 args
 361     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 362     # . . restore ed
 363     59/pop-to-ecx
 364     # check that convert-next-octet didn't abort
 365     # . check-ints-equal(ed->value, 0, msg)
 366     # . . push args
 367     68/push  "F - test-convert-next-octet: unexpected abort"/imm32
 368     68/push  0/imm32
 369     # . . push ed->value
 370     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         .                 # push *(ecx+4)
 371     # . . call
 372     e8/call  check-ints-equal/disp32
 373     # . . discard args
 374     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 375     # return if abort
 376     81          7/subop/compare     1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         0/imm32           # compare *(ecx+4)
 377     75/jump-if-not-equal  $test-convert-next-octet-handles-Eof:end/disp8
 378     # check-ints-equal(eax, Eof, msg)
 379     # . . push args
 380     68/push  "F - test-convert-next-octet-handles-Eof"/imm32
 381     68/push  0xffffffff/imm32/Eof
 382     50/push-eax
 383     # . . call
 384     e8/call  check-ints-equal/disp32
 385     # . . discard args
 386     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 387 $test-convert-next-octet-handles-Eof:end:
 388     # . epilogue
 389     # don't restore esp from ebp; manually reclaim locals
 390     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 391     5d/pop-to-ebp
 392     c3/return
 393 
 394 test-convert-next-octet-aborts-on-single-hex-byte:
 395     # - check that a single unaccompanied hex byte aborts
 396     # This test uses exit-descriptors. Use ebp for setting up local variables.
 397     55/push-ebp
 398     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 399     # clear all streams
 400     # . clear-stream(_test-stream)
 401     # . . push args
 402     68/push  _test-stream/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-buffered-file->buffer)
 408     # . . push args
 409     68/push  $_test-buffered-file->buffer/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-stream)
 415     # . . push args
 416     68/push  _test-error-stream/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     # . clear-stream($_test-error-buffered-file->buffer)
 422     # . . push args
 423     68/push  $_test-error-buffered-file->buffer/imm32
 424     # . . call
 425     e8/call  clear-stream/disp32
 426     # . . discard args
 427     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 428     # initialize '_test-stream' to "a"
 429     # . write(_test-stream, "a")
 430     # . . push args
 431     68/push  "a"/imm32
 432     68/push  _test-stream/imm32
 433     # . . call
 434     e8/call  write/disp32
 435     # . . discard args
 436     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 437     # initialize exit-descriptor 'ed' for the call to 'convert-next-octet' below
 438     # . var ed/ecx : exit-descriptor
 439     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
 440     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
 441     # . tailor-exit-descriptor(ed, 12)
 442     # . . push args
 443     68/push  0xc/imm32/nbytes-of-args-for-convert-next-octet
 444     51/push-ecx/ed
 445     # . . call
 446     e8/call  tailor-exit-descriptor/disp32
 447     # . . discard args
 448     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 449     # eax = convert-next-octet(_test-buffered-file, _test-error-buffered-file, ed)
 450     # . . push args
 451     51/push-ecx/ed
 452     68/push  _test-error-buffered-file/imm32
 453     68/push  _test-buffered-file/imm32
 454     # . . call
 455     e8/call  convert-next-octet/disp32
 456     # registers except esp may be clobbered at this point
 457     # pop args to convert-next-octet
 458     # . . discard first 2 args
 459     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 460     # . . restore ed
 461     59/pop-to-ecx
 462     # check that convert-next-octet aborted
 463     # . check-ints-equal(ed->value, 2, msg)
 464     # . . push args
 465     68/push  "F - test-convert-next-octet-aborts-on-single-hex-byte: unexpected abort"/imm32
 466     68/push  2/imm32
 467     # . . push ed->value
 468     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         .                 # push *(ecx+4)
 469     # . . call
 470     e8/call  check-ints-equal/disp32
 471     # . . discard args
 472     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 473 $test-convert-next-octet-aborts-on-single-hex-byte:end:
 474     # . epilogue
 475     # don't restore esp from ebp; manually reclaim locals
 476     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 477     5d/pop-to-ebp
 478     c3/return
 479 
 480 # read whitespace until a hex byte, and return it
 481 # return Eof if file ends without finding a hex byte
 482 # on '#' skip all bytes until newline
 483 # abort on any other byte
 484 scan-next-byte:  # in : (addr buffered-file), err : (addr buffered-file), ed : (addr exit-descriptor) -> byte-or-Eof/eax
 485     # pseudocode:
 486     #   while true
 487     #     eax = read-byte-buffered(in)
 488     #     if (eax == Eof) return eax
 489     #     if (is-hex-digit?(eax)) return eax
 490     #     if (eax == ' ' or '\t' or '\n') continue
 491     #     if (eax == '#') skip-until-newline(in)
 492     #     else error-byte(ed, err, "invalid byte: " eax)
 493     #
 494     # . prologue
 495     55/push-ebp
 496     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 497     # . save registers
 498 $scan-next-byte:loop:
 499     # eax = read-byte-buffered(in)
 500     # . . push args
 501     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 502     # . . call
 503     e8/call  read-byte-buffered/disp32
 504     # . . discard args
 505     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 506     # if (eax == Eof) return eax
 507     3d/compare-with-eax  0xffffffff/imm32/Eof
 508     74/jump-if-equal  $scan-next-byte:end/disp8
 509     # if (is-hex-digit?(eax)) return eax
 510     # . save eax for now
 511     50/push-eax
 512     # . is-hex-digit?(eax)
 513     # . . push args
 514     50/push-eax
 515     # . . call
 516     e8/call  is-hex-digit?/disp32
 517     # . . discard args
 518     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 519     # . compare with 'false'
 520     3d/compare-with-eax  0/imm32
 521     # . restore eax (does not affect flags)
 522     58/pop-to-eax
 523     # . check whether to return
 524     75/jump-if-not-equal  $scan-next-byte:end/disp8
 525 $scan-next-byte:check1:
 526     # if (eax == ' ') continue
 527     3d/compare-eax-and  0x20/imm32/space
 528     74/jump-if-equal  $scan-next-byte:loop/disp8
 529     # if (eax == '\t') continue
 530     3d/compare-eax-and  9/imm32/tab
 531     74/jump-if-equal  $scan-next-byte:loop/disp8
 532     # if (eax == '\n') continue
 533     3d/compare-eax-and  0xa/imm32/newline
 534     74/jump-if-equal  $scan-next-byte:loop/disp8
 535 $scan-next-byte:check2:
 536     # if (eax == '#') skip-until-newline(in)
 537     3d/compare-with-eax  0x23/imm32
 538     75/jump-if-not-equal  $scan-next-byte:check3/disp8
 539     # . skip-until-newline(in)
 540     # . . push args
 541     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 542     # . . call
 543     e8/call  skip-until-newline/disp32
 544     # . . discard args
 545     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 546     eb/jump  $scan-next-byte:loop/disp8
 547 $scan-next-byte:check3:
 548     # otherwise error-byte(ed, err, msg, eax)
 549     # . . push args
 550     50/push-eax
 551     68/push  "scan-next-byte: invalid byte"/imm32
 552     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 553     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
 554     # . . call
 555     e8/call  error-byte/disp32  # never returns
 556 $scan-next-byte:end:
 557     # . restore registers
 558     # . epilogue
 559     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 560     5d/pop-to-ebp
 561     c3/return
 562 
 563 test-scan-next-byte:
 564     # - check that the first byte of the input is returned
 565     # This test uses exit-descriptors. Use ebp for setting up local variables.
 566     55/push-ebp
 567     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 568     # clear all streams
 569     # . clear-stream(_test-stream)
 570     # . . push args
 571     68/push  _test-stream/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-buffered-file->buffer)
 577     # . . push args
 578     68/push  $_test-buffered-file->buffer/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-stream)
 584     # . . push args
 585     68/push  _test-error-stream/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     # . clear-stream($_test-error-buffered-file->buffer)
 591     # . . push args
 592     68/push  $_test-error-buffered-file->buffer/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     # initialize '_test-stream' to "abc"
 598     # . write(_test-stream, "abc")
 599     # . . push args
 600     68/push  "abc"/imm32
 601     68/push  _test-stream/imm32
 602     # . . call
 603     e8/call  write/disp32
 604     # . . discard args
 605     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 606     # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below
 607     # . var ed/ecx : exit-descriptor
 608     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
 609     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
 610     # . tailor-exit-descriptor(ed, 12)
 611     # . . push args
 612     68/push  0xc/imm32/nbytes-of-args-for-scan-next-byte
 613     51/push-ecx/ed
 614     # . . call
 615     e8/call  tailor-exit-descriptor/disp32
 616     # . . discard args
 617     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 618     # eax = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed)
 619     # . . push args
 620     51/push-ecx/ed
 621     68/push  _test-error-buffered-file/imm32
 622     68/push  _test-buffered-file/imm32
 623     # . . call
 624     e8/call  scan-next-byte/disp32
 625     # registers except esp may be clobbered at this point
 626     # pop args to scan-next-byte
 627     # . . discard first 2 args
 628     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 629     # . . restore ed
 630     59/pop-to-ecx
 631     # check that scan-next-byte didn't abort
 632     # . check-ints-equal(ed->value, 0, msg)
 633     # . . push args
 634     68/push  "F - test-scan-next-byte: unexpected abort"/imm32
 635     68/push  0/imm32
 636     # . . push ed->value
 637     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         .                 # push *(ecx+4)
 638     # . . call
 639     e8/call  check-ints-equal/disp32
 640     # . . discard args
 641     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 642     # return if abort
 643     81          7/subop/compare     1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         0/imm32           # compare *(ecx+4)
 644     75/jump-if-not-equal  $test-scan-next-byte:end/disp8
 645     # check-ints-equal(eax, 0x61/a, msg)
 646     # . . push args
 647     68/push  "F - test-scan-next-byte"/imm32
 648     68/push  0x61/imm32/a
 649     50/push-eax
 650     # . . call
 651     e8/call  check-ints-equal/disp32
 652     # . . discard args
 653     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 654 $test-scan-next-byte:end:
 655     # . epilogue
 656     # don't restore esp from ebp; manually reclaim locals
 657     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 658     5d/pop-to-ebp
 659     c3/return
 660 
 661 test-scan-next-byte-skips-whitespace:
 662     # - check that the first byte after whitespace is returned
 663     # This test uses exit-descriptors. Use ebp for setting up local variables.
 664     55/push-ebp
 665     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 666     # clear all streams
 667     # . clear-stream(_test-stream)
 668     # . . push args
 669     68/push  _test-stream/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-buffered-file->buffer)
 675     # . . push args
 676     68/push  $_test-buffered-file->buffer/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-stream)
 682     # . . push args
 683     68/push  _test-error-stream/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     # . clear-stream($_test-error-buffered-file->buffer)
 689     # . . push args
 690     68/push  $_test-error-buffered-file->buffer/imm32
 691     # . . call
 692     e8/call  clear-stream/disp32
 693     # . . discard args
 694     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 695     # initialize '_test-stream' to input with leading whitespace
 696     # . write(_test-stream, text)
 697     # . . push args
 698     68/push  "  abc"/imm32
 699     68/push  _test-stream/imm32
 700     # . . call
 701     e8/call  write/disp32
 702     # . . discard args
 703     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 704     # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below
 705     # . var ed/ecx : exit-descriptor
 706     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
 707     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
 708     # . tailor-exit-descriptor(ed, 12)
 709     # . . push args
 710     68/push  0xc/imm32/nbytes-of-args-for-scan-next-byte
 711     51/push-ecx/ed
 712     # . . call
 713     e8/call  tailor-exit-descriptor/disp32
 714     # . . discard args
 715     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 716     # eax = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed)
 717     # . . push args
 718     51/push-ecx/ed
 719     68/push  _test-error-buffered-file/imm32
 720     68/push  _test-buffered-file/imm32
 721     # . . call
 722     e8/call  scan-next-byte/disp32
 723     # registers except esp may be clobbered at this point
 724     # pop args to scan-next-byte
 725     # . . discard first 2 args
 726     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 727     # . . restore ed
 728     59/pop-to-ecx
 729     # check that scan-next-byte didn't abort
 730     # . check-ints-equal(ed->value, 0, msg)
 731     # . . push args
 732     68/push  "F - test-scan-next-byte-skips-whitespace: unexpected abort"/imm32
 733     68/push  0/imm32
 734     # . . push ed->value
 735     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         .                 # push *(ecx+4)
 736     # . . call
 737     e8/call  check-ints-equal/disp32
 738     # . . discard args
 739     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 740     # return if abort
 741     81          7/subop/compare     1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         0/imm32           # compare *(ecx+4)
 742     75/jump-if-not-equal  $test-scan-next-byte-skips-whitespace:end/disp8
 743     # check-ints-equal(eax, 0x61/a, msg)
 744     # . . push args
 745     68/push  "F - test-scan-next-byte-skips-whitespace"/imm32
 746     68/push  0x61/imm32/a
 747     50/push-eax
 748     # . . call
 749     e8/call  check-ints-equal/disp32
 750     # . . discard args
 751     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 752 $test-scan-next-byte-skips-whitespace:end:
 753     # . epilogue
 754     # don't restore esp from ebp; manually reclaim locals
 755     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 756     5d/pop-to-ebp
 757     c3/return
 758 
 759 test-scan-next-byte-skips-comment:
 760     # - check that the first byte after a comment (and newline) is returned
 761     # This test uses exit-descriptors. Use ebp for setting up local variables.
 762     55/push-ebp
 763     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 764     # clear all streams
 765     # . clear-stream(_test-stream)
 766     # . . push args
 767     68/push  _test-stream/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-buffered-file->buffer)
 773     # . . push args
 774     68/push  $_test-buffered-file->buffer/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-stream)
 780     # . . push args
 781     68/push  _test-error-stream/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     # . clear-stream($_test-error-buffered-file->buffer)
 787     # . . push args
 788     68/push  $_test-error-buffered-file->buffer/imm32
 789     # . . call
 790     e8/call  clear-stream/disp32
 791     # . . discard args
 792     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 793     # initialize '_test-stream' to input with leading comment
 794     # . write(_test-stream, comment)
 795     # . . push args
 796     68/push  "#x\n"/imm32
 797     68/push  _test-stream/imm32
 798     # . . call
 799     e8/call  write/disp32
 800     # . . discard args
 801     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 802     # . write(_test-stream, real text)
 803     # . . push args
 804     68/push  "ab"/imm32
 805     68/push  _test-stream/imm32
 806     # . . call
 807     e8/call  write/disp32
 808     # . . discard args
 809     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 810     # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below
 811     # . var ed/ecx : exit-descriptor
 812     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
 813     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
 814     # . tailor-exit-descriptor(ed, 12)
 815     # . . push args
 816     68/push  0xc/imm32/nbytes-of-args-for-scan-next-byte
 817     51/push-ecx/ed
 818     # . . call
 819     e8/call  tailor-exit-descriptor/disp32
 820     # . . discard args
 821     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 822     # eax = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed)
 823     # . . push args
 824     51/push-ecx/ed
 825     68/push  _test-error-buffered-file/imm32
 826     68/push  _test-buffered-file/imm32
 827     # . . call
 828     e8/call  scan-next-byte/disp32
 829     # registers except esp may be clobbered at this point
 830     # pop args to scan-next-byte
 831     # . . discard first 2 args
 832     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 833     # . . restore ed
 834     59/pop-to-ecx
 835     # check that scan-next-byte didn't abort
 836     # . check-ints-equal(ed->value, 0, msg)
 837     # . . push args
 838     68/push  "F - test-scan-next-byte-skips-comment: unexpected abort"/imm32
 839     68/push  0/imm32
 840     # . . push ed->value
 841     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         .                 # push *(ecx+4)
 842     # . . call
 843     e8/call  check-ints-equal/disp32
 844     # . . discard args
 845     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 846     # return if abort
 847     81          7/subop/compare     1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         0/imm32           # compare *(ecx+4)
 848     75/jump-if-not-equal  $test-scan-next-byte-skips-comment:end/disp8
 849     # check-ints-equal(eax, 0x61/a, msg)
 850     # . . push args
 851     68/push  "F - test-scan-next-byte-skips-comment"/imm32
 852     68/push  0x61/imm32/a
 853     50/push-eax
 854     # . . call
 855     e8/call  check-ints-equal/disp32
 856     # . . discard args
 857     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 858 $test-scan-next-byte-skips-comment:end:
 859     # . epilogue
 860     # don't restore esp from ebp; manually reclaim locals
 861     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 862     5d/pop-to-ebp
 863     c3/return
 864 
 865 test-scan-next-byte-skips-comment-and-whitespace:
 866     # - check that the first byte after a comment and any further whitespace is returned
 867     # This test uses exit-descriptors. Use ebp for setting up local variables.
 868     55/push-ebp
 869     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 870     # clear all streams
 871     # . clear-stream(_test-stream)
 872     # . . push args
 873     68/push  _test-stream/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-buffered-file->buffer)
 879     # . . push args
 880     68/push  $_test-buffered-file->buffer/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-stream)
 886     # . . push args
 887     68/push  _test-error-stream/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     # . clear-stream($_test-error-buffered-file->buffer)
 893     # . . push args
 894     68/push  $_test-error-buffered-file->buffer/imm32
 895     # . . call
 896     e8/call  clear-stream/disp32
 897     # . . discard args
 898     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 899     # initialize '_test-stream' to input with leading comment and more whitespace after newline
 900     # . write(_test-stream, comment)
 901     # . . push args
 902     68/push  "#x\n"/imm32
 903     68/push  _test-stream/imm32
 904     # . . call
 905     e8/call  write/disp32
 906     # . . discard args
 907     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 908     # . write(_test-stream, real text)
 909     # . . push args
 910     68/push  " ab"/imm32
 911     68/push  _test-stream/imm32
 912     # . . call
 913     e8/call  write/disp32
 914     # . . discard args
 915     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 916     # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below
 917     # . var ed/ecx : exit-descriptor
 918     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
 919     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
 920     # . tailor-exit-descriptor(ed, 12)
 921     # . . push args
 922     68/push  0xc/imm32/nbytes-of-args-for-scan-next-byte
 923     51/push-ecx/ed
 924     # . . call
 925     e8/call  tailor-exit-descriptor/disp32
 926     # . . discard args
 927     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 928     # eax = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed)
 929     # . . push args
 930     51/push-ecx/ed
 931     68/push  _test-error-buffered-file/imm32
 932     68/push  _test-buffered-file/imm32
 933     # . . call
 934     e8/call  scan-next-byte/disp32
 935     # registers except esp may be clobbered at this point
 936     # pop args to scan-next-byte
 937     # . . discard first 2 args
 938     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 939     # . . restore ed
 940     59/pop-to-ecx
 941     # check that scan-next-byte didn't abort
 942     # . check-ints-equal(ed->value, 0, msg)
 943     # . . push args
 944     68/push  "F - test-scan-next-byte-skips-comment-and-whitespace: unexpected abort"/imm32
 945     68/push  0/imm32
 946     # . . push ed->value
 947     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         .                 # push *(ecx+4)
 948     # . . call
 949     e8/call  check-ints-equal/disp32
 950     # . . discard args
 951     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 952     # return if abort
 953     81          7/subop/compare     1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         0/imm32           # compare *(ecx+4)
 954     75/jump-if-not-equal  $test-scan-next-byte-skips-comment-and-whitespace:end/disp8
 955     # check-ints-equal(eax, 0x61/a, msg)
 956     # . . push args
 957     68/push  "F - test-scan-next-byte-skips-comment-and-whitespace"/imm32
 958     68/push  0x61/imm32/a
 959     50/push-eax
 960     # . . call
 961     e8/call  check-ints-equal/disp32
 962     # . . discard args
 963     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 964 $test-scan-next-byte-skips-comment-and-whitespace:end:
 965     # . epilogue
 966     # don't restore esp from ebp; manually reclaim locals
 967     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 968     5d/pop-to-ebp
 969     c3/return
 970 
 971 test-scan-next-byte-skips-whitespace-and-comment:
 972     # - check that the first byte after any whitespace and comments is returned
 973     # This test uses exit-descriptors. Use ebp for setting up local variables.
 974     55/push-ebp
 975     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 976     # clear all streams
 977     # . clear-stream(_test-stream)
 978     # . . push args
 979     68/push  _test-stream/imm32
 980     # . . call
 981     e8/call  clear-stream/disp32
 982     # . . discard args
 983     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 984     # . clear-stream($_test-buffered-file->buffer)
 985     # . . push args
 986     b8/copy-to-eax  _test-buffered-file/imm32
 987     05/add-to-eax  4/imm32
 988     50/push-eax
 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-stream)
 994     # . . push args
 995     68/push  _test-error-stream/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     # . clear-stream($_test-error-buffered-file->buffer)
1001     # . . push args
1002     68/push  $_test-error-buffered-file->buffer/imm32
1003     # . . call
1004     e8/call  clear-stream/disp32
1005     # . . discard args
1006     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1007     # initialize '_test-stream' to input with leading whitespace and comment
1008     # . write(_test-stream, comment)
1009     # . . push args
1010     68/push  " #x\n"/imm32
1011     68/push  _test-stream/imm32
1012     # . . call
1013     e8/call  write/disp32
1014     # . . discard args
1015     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1016     # . write(_test-stream, real text)
1017     # . . push args
1018     68/push  "ab"/imm32
1019     68/push  _test-stream/imm32
1020     # . . call
1021     e8/call  write/disp32
1022     # . . discard args
1023     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1024     # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below
1025     # . var ed/ecx : exit-descriptor
1026     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
1027     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1028     # . tailor-exit-descriptor(ed, 12)
1029     # . . push args
1030     68/push  0xc/imm32/nbytes-of-args-for-scan-next-byte
1031     51/push-ecx/ed
1032     # . . call
1033     e8/call  tailor-exit-descriptor/disp32
1034     # . . discard args
1035     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1036     # eax = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed)
1037     # . . push args
1038     51/push-ecx/ed
1039     68/push  _test-error-buffered-file/imm32
1040     68/push  _test-buffered-file/imm32
1041     # . . call
1042     e8/call  scan-next-byte/disp32
1043     # registers except esp may be clobbered at this point
1044     # pop args to scan-next-byte
1045     # . . discard first 2 args
1046     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1047     # . . restore ed
1048     59/pop-to-ecx
1049     # check that scan-next-byte didn't abort
1050     # . check-ints-equal(ed->value, 0, msg)
1051     # . . push args
1052     68/push  "F - test-scan-next-byte-skips-whitespace-and-comment: unexpected abort"/imm32
1053     68/push  0/imm32
1054     # . . push ed->value
1055     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         .                 # push *(ecx+4)
1056     # . . call
1057     e8/call  check-ints-equal/disp32
1058     # . . discard args
1059     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1060     # return if abort
1061     81          7/subop/compare     1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         0/imm32           # compare *(ecx+4)
1062     75/jump-if-not-equal  $test-scan-next-byte-skips-whitespace-and-comment:end/disp8
1063     # check-ints-equal(eax, 0x61/a, msg)
1064     # . . push args
1065     68/push  "F - test-scan-next-byte-skips-whitespace-and-comment"/imm32
1066     68/push  0x61/imm32/a
1067     50/push-eax
1068     # . . call
1069     e8/call  check-ints-equal/disp32
1070     # . . discard args
1071     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1072 $test-scan-next-byte-skips-whitespace-and-comment:end:
1073     # . epilogue
1074     # don't restore esp from ebp; manually reclaim locals
1075     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1076     5d/pop-to-ebp
1077     c3/return
1078 
1079 test-scan-next-byte-reads-final-byte:
1080     # - check that the final byte in input is returned
1081     # This test uses exit-descriptors. Use ebp for setting up local variables.
1082     55/push-ebp
1083     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1084     # clear all streams
1085     # . clear-stream(_test-stream)
1086     # . . push args
1087     68/push  _test-stream/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-buffered-file->buffer)
1093     # . . push args
1094     68/push  $_test-buffered-file->buffer/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-stream)
1100     # . . push args
1101     68/push  _test-error-stream/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     # . clear-stream($_test-error-buffered-file->buffer)
1107     # . . push args
1108     68/push  $_test-error-buffered-file->buffer/imm32
1109     # . . call
1110     e8/call  clear-stream/disp32
1111     # . . discard args
1112     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1113     # initialize '_test-stream' to input with single character
1114     # . write(_test-stream, character)
1115     # . . push args
1116     68/push  "a"/imm32
1117     68/push  _test-stream/imm32
1118     # . . call
1119     e8/call  write/disp32
1120     # . . discard args
1121     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1122     # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below
1123     # . var ed/ecx : exit-descriptor
1124     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
1125     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1126     # . tailor-exit-descriptor(ed, 12)
1127     # . . push args
1128     68/push  0xc/imm32/nbytes-of-args-for-scan-next-byte
1129     51/push-ecx/ed
1130     # . . call
1131     e8/call  tailor-exit-descriptor/disp32
1132     # . . discard args
1133     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1134     # eax = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed)
1135     # . . push args
1136     51/push-ecx/ed
1137     68/push  _test-error-buffered-file/imm32
1138     68/push  _test-buffered-file/imm32
1139     # . . call
1140     e8/call  scan-next-byte/disp32
1141     # registers except esp may be clobbered at this point
1142     # pop args to scan-next-byte
1143     # . . discard first 2 args
1144     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1145     # . . restore ed
1146     59/pop-to-ecx
1147     # check that scan-next-byte didn't abort
1148     # . check-ints-equal(ed->value, 0, msg)
1149     # . . push args
1150     68/push  "F - test-scan-next-byte-reads-final-byte: unexpected abort"/imm32
1151     68/push  0/imm32
1152     # . . push ed->value
1153     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         .                 # push *(ecx+4)
1154     # . . call
1155     e8/call  check-ints-equal/disp32
1156     # . . discard args
1157     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1158     # return if abort
1159     81          7/subop/compare     1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         0/imm32           # compare *(ecx+4)
1160     75/jump-if-not-equal  $test-scan-next-byte-reads-final-byte:end/disp8
1161     # check-ints-equal(eax, 0x61/a, msg)
1162     # . . push args
1163     68/push  "F - test-scan-next-byte-reads-final-byte"/imm32
1164     68/push  0x61/imm32/a
1165     50/push-eax
1166     # . . call
1167     e8/call  check-ints-equal/disp32
1168     # . . discard args
1169     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1170 $test-scan-next-byte-reads-final-byte:end:
1171     # . epilogue
1172     # don't restore esp from ebp; manually reclaim locals
1173     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1174     5d/pop-to-ebp
1175     c3/return
1176 
1177 test-scan-next-byte-handles-Eof:
1178     # - check that the right sentinel value is returned when there's no data remaining to be read
1179     # This test uses exit-descriptors. Use ebp for setting up local variables.
1180     55/push-ebp
1181     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1182     # clear all streams
1183     # . clear-stream(_test-stream)
1184     # . . push args
1185     68/push  _test-stream/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-buffered-file->buffer)
1191     # . . push args
1192     68/push  $_test-buffered-file->buffer/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-stream)
1198     # . . push args
1199     68/push  _test-error-stream/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     # . clear-stream($_test-error-buffered-file->buffer)
1205     # . . push args
1206     68/push  $_test-error-buffered-file->buffer/imm32
1207     # . . call
1208     e8/call  clear-stream/disp32
1209     # . . discard args
1210     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1211     # leave '_test-stream' empty
1212     # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below
1213     # . var ed/ecx : exit-descriptor
1214     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
1215     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1216     # . tailor-exit-descriptor(ed, 12)
1217     # . . push args
1218     68/push  0xc/imm32/nbytes-of-args-for-scan-next-byte
1219     51/push-ecx/ed
1220     # . . call
1221     e8/call  tailor-exit-descriptor/disp32
1222     # . . discard args
1223     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1224     # eax = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed)
1225     # . . push args
1226     51/push-ecx/ed
1227     68/push  _test-error-buffered-file/imm32
1228     68/push  _test-buffered-file/imm32
1229     # . . call
1230     e8/call  scan-next-byte/disp32
1231     # registers except esp may be clobbered at this point
1232     # pop args to scan-next-byte
1233     # . . discard first 2 args
1234     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1235     # . . restore ed
1236     59/pop-to-ecx
1237     # check that scan-next-byte didn't abort
1238     # . check-ints-equal(ed->value, 0, msg)
1239     # . . push args
1240     68/push  "F - test-scan-next-byte-handles-Eof: unexpected abort"/imm32
1241     68/push  0/imm32
1242     # . . push ed->value
1243     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         .                 # push *(ecx+4)
1244     # . . call
1245     e8/call  check-ints-equal/disp32
1246     # . . discard args
1247     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1248     # return if abort
1249     81          7/subop/compare     1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         0/imm32           # compare *(ecx+4)
1250     75/jump-if-not-equal  $test-scan-next-byte-handles-Eof:end/disp8
1251     # check-ints-equal(eax, Eof, msg)
1252     # . . push args
1253     68/push  "F - test-scan-next-byte-handles-Eof"/imm32
1254     68/push  0xffffffff/imm32/Eof
1255     50/push-eax
1256     # . . call
1257     e8/call  check-ints-equal/disp32
1258     # . . discard args
1259     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1260 $test-scan-next-byte-handles-Eof:end:
1261     # . epilogue
1262     # don't restore esp from ebp; manually reclaim locals
1263     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1264     5d/pop-to-ebp
1265     c3/return
1266 
1267 test-scan-next-byte-aborts-on-invalid-byte:
1268     # - check that the a bad byte immediately aborts
1269     # This test uses exit-descriptors. Use ebp for setting up local variables.
1270     55/push-ebp
1271     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1272     # clear all streams
1273     # . clear-stream(_test-stream)
1274     # . . push args
1275     68/push  _test-stream/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-buffered-file->buffer)
1281     # . . push args
1282     68/push  $_test-buffered-file->buffer/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-stream)
1288     # . . push args
1289     68/push  _test-error-stream/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     # . clear-stream($_test-error-buffered-file->buffer)
1295     # . . push args
1296     68/push  $_test-error-buffered-file->buffer/imm32
1297     # . . call
1298     e8/call  clear-stream/disp32
1299     # . . discard args
1300     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1301     # initialize '_test-stream' to "x"
1302     # . write(_test-stream, "x")
1303     # . . push args
1304     68/push  "x"/imm32
1305     68/push  _test-stream/imm32
1306     # . . call
1307     e8/call  write/disp32
1308     # . . discard args
1309     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1310     # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below
1311     # . var ed/ecx : exit-descriptor
1312     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
1313     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1314     # . tailor-exit-descriptor(ed, 12)
1315     # . . push args
1316     68/push  0xc/imm32/nbytes-of-args-for-scan-next-byte
1317     51/push-ecx/ed
1318     # . . call
1319     e8/call  tailor-exit-descriptor/disp32
1320     # . . discard args
1321     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1322     # eax = scan-next-byte(_test-buffered-file, _test-error-buffered-file, ed)
1323     # . . push args
1324     51/push-ecx/ed
1325     68/push  _test-error-buffered-file/imm32
1326     68/push  _test-buffered-file/imm32
1327     # . . call
1328     e8/call  scan-next-byte/disp32
1329     # registers except esp may be clobbered at this point
1330     # pop args to scan-next-byte
1331     # . . discard first 2 args
1332     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1333     # . . restore ed
1334     59/pop-to-ecx
1335     # check that scan-next-byte aborted
1336     # . check-ints-equal(ed->value, 2, msg)
1337     # . . push args
1338     68/push  "F - test-scan-next-byte-aborts-on-invalid-byte"/imm32
1339     68/push  2/imm32
1340     # . . push ed->value
1341     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         .                 # push *(ecx+4)
1342     # . . call
1343     e8/call  check-ints-equal/disp32
1344     # . . discard args
1345     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1346 $test-scan-next-byte-aborts-on-invalid-byte:end:
1347     # . epilogue
1348     # don't restore esp from ebp; manually reclaim locals
1349     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1350     5d/pop-to-ebp
1351     c3/return
1352 
1353 skip-until-newline:  # in : (addr buffered-file)
1354     # pseudocode:
1355     #   push eax
1356     #   while true
1357     #     eax = read-byte-buffered(in)
1358     #     if (eax == Eof) break
1359     #     if (eax == 0x0a) break
1360     #   pop eax
1361     # . prologue
1362     55/push-ebp
1363     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1364     # . save registers
1365     50/push-eax
1366 $skip-until-newline:loop:
1367     # . eax = read-byte-buffered(in)
1368     # . . push args
1369     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1370     # . . call
1371     e8/call  read-byte-buffered/disp32
1372     # . . discard args
1373     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1374     # . if (eax == Eof) break
1375     3d/compare-eax-and  0xffffffff/imm32/Eof
1376     74/jump-if-equal  $skip-until-newline:end/disp8
1377     # . if (eax != 0xa/newline) loop
1378     3d/compare-eax-and  0xa/imm32/newline
1379     75/jump-if-not-equal  $skip-until-newline:loop/disp8
1380 $skip-until-newline:end:
1381     # . restore registers
1382     58/pop-to-eax
1383     # . epilogue
1384     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1385     5d/pop-to-ebp
1386     c3/return
1387 
1388 test-skip-until-newline:
1389     # - check that the read pointer points after the newline
1390     # setup
1391     # . clear-stream(_test-stream)
1392     # . . push args
1393     68/push  _test-stream/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     # . clear-stream($_test-buffered-file->buffer)
1399     # . . push args
1400     68/push  $_test-buffered-file->buffer/imm32
1401     # . . call
1402     e8/call  clear-stream/disp32
1403     # . . discard args
1404     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1405     # initialize '_test-stream' to "abc\nde"
1406     # . write(_test-stream, "abc")
1407     # . . push args
1408     68/push  "abc\n"/imm32
1409     68/push  _test-stream/imm32
1410     # . . call
1411     e8/call  write/disp32
1412     # . . discard args
1413     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1414     # . write(_test-stream, "de")
1415     # . . push args
1416     68/push  "de"/imm32
1417     68/push  _test-stream/imm32
1418     # . . call
1419     e8/call  write/disp32
1420     # . . discard args
1421     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1422     # skip-until-newline(_test-buffered-file)
1423     # . . push args
1424     68/push  _test-buffered-file/imm32
1425     # . . call
1426     e8/call  skip-until-newline/disp32
1427     # . . discard args
1428     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1429     # check-ints-equal(_test-buffered-file->read, 4, msg)
1430     # . . push args
1431     68/push  "F - test-skip-until-newline"/imm32
1432     68/push  4/imm32
1433     b8/copy-to-eax  _test-buffered-file/imm32
1434     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           8/disp8         .                 # push *(eax+8)
1435     # . . call
1436     e8/call  check-ints-equal/disp32
1437     # . . discard args
1438     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1439     # . end
1440     c3/return
1441 
1442 # . . vim:nowrap:textwidth=0