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