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