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