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