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     # 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 # . . vim:nowrap:textwidth=0