https://github.com/akkartik/mu/blob/main/124next-token.subx
   1 # Some tokenization primitives.
   2 
   3 == code
   4 #   instruction                     effective address                                                   register    displacement    immediate
   5 # . op          subop               mod             rm32          base        index         scale       r32
   6 # . 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
   7 
   8 # extract the next run of characters that are different from a given 'delimiter' (skipping multiple delimiters if necessary)
   9 # on reaching end of file, return an empty interval
  10 next-token-from-slice:  # start: (addr byte), end: (addr byte), delimiter: byte, out: (addr slice)
  11     # . prologue
  12     55/push-ebp
  13     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
  14     # . save registers
  15     50/push-eax
  16     51/push-ecx
  17     52/push-edx
  18     57/push-edi
  19     # ecx = end
  20     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   0xc/disp8       .                 # copy *(ebp+12) to ecx
  21     # edx = delimiter
  22     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           2/r32/edx   0x10/disp8      .                 # copy *(ebp+16) to edx
  23     # edi = out
  24     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           7/r32/edi   0x14/disp8      .                 # copy *(ebp+20) to edi
  25     # eax = skip-chars-matching-in-slice(start, end, delimiter)
  26     # . . push args
  27     52/push-edx
  28     51/push-ecx
  29     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
  30     # . . call
  31     e8/call  skip-chars-matching-in-slice/disp32
  32     # . . discard args
  33     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
  34     # out->start = eax
  35     89/copy                         0/mod/indirect  7/rm32/edi    .           .             .           0/r32/eax   .               .                 # copy eax to *edi
  36     # eax = skip-chars-not-matching-in-slice(eax, end, delimiter)
  37     # . . push args
  38     52/push-edx
  39     51/push-ecx
  40     50/push-eax
  41     # . . call
  42     e8/call  skip-chars-not-matching-in-slice/disp32
  43     # . . discard args
  44     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
  45     # out->end = eax
  46     89/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           0/r32/eax   4/disp8         .                 # copy eax to *(edi+4)
  47     # . restore registers
  48     5f/pop-to-edi
  49     5a/pop-to-edx
  50     59/pop-to-ecx
  51     58/pop-to-eax
  52     # . epilogue
  53     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
  54     5d/pop-to-ebp
  55     c3/return
  56 
  57 test-next-token-from-slice:
  58     # . prologue
  59     55/push-ebp
  60     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
  61     # (eax..ecx) = "  ab"
  62     b8/copy-to-eax  "  ab"/imm32
  63     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
  64     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
  65     05/add-to-eax  4/imm32
  66     # var out/edi: slice
  67     68/push  0/imm32/end
  68     68/push  0/imm32/start
  69     89/copy                         3/mod/direct    7/rm32/edi    .           .             .           4/r32/esp   .               .                 # copy esp to edi
  70     # next-token-from-slice(eax, ecx, 0x20/space, out)
  71     # . . push args
  72     57/push-edi
  73     68/push  0x20/imm32
  74     51/push-ecx
  75     50/push-eax
  76     # . . call
  77     e8/call  next-token-from-slice/disp32
  78     # . . discard args
  79     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
  80     # out->start should be at the 'a'
  81     # . check-ints-equal(out->start - in->start, 2, msg)
  82     # . . push args
  83     68/push  "F - test-next-token-from-slice: start"/imm32
  84     68/push  2/imm32
  85     # . . push out->start - in->start
  86     8b/copy                         0/mod/indirect  7/rm32/edi    .           .             .           1/r32/ecx   .               .                 # copy *edi to ecx
  87     2b/subtract                     3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # subtract eax from ecx
  88     51/push-ecx
  89     # . . call
  90     e8/call  check-ints-equal/disp32
  91     # . . discard args
  92     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
  93     # out->end should be after the 'b'
  94     # check-ints-equal(out->end - in->start, 4, msg)
  95     # . . push args
  96     68/push  "F - test-next-token-from-slice: end"/imm32
  97     68/push  4/imm32
  98     # . . push out->end - in->start
  99     8b/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(edi+4) to ecx
 100     2b/subtract                     3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # subtract eax from ecx
 101     51/push-ecx
 102     # . . call
 103     e8/call  check-ints-equal/disp32
 104     # . . discard args
 105     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 106     # . epilogue
 107     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 108     5d/pop-to-ebp
 109     c3/return
 110 
 111 test-next-token-from-slice-Eof:
 112     # . prologue
 113     55/push-ebp
 114     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 115     # var out/edi: slice
 116     68/push  0/imm32/end
 117     68/push  0/imm32/start
 118     89/copy                         3/mod/direct    7/rm32/edi    .           .             .           4/r32/esp   .               .                 # copy esp to edi
 119     # next-token-from-slice(0, 0, 0x20/space, out)
 120     # . . push args
 121     57/push-edi
 122     68/push  0x20/imm32
 123     68/push  0/imm32
 124     68/push  0/imm32
 125     # . . call
 126     e8/call  next-token-from-slice/disp32
 127     # . . discard args
 128     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
 129     # out should be empty
 130     # . check-ints-equal(out->end - out->start, 0, msg)
 131     # . . push args
 132     68/push  "F - test-next-token-from-slice-Eof"/imm32
 133     68/push  0/imm32
 134     # . . push out->start - in->start
 135     8b/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(edi+4) to ecx
 136     2b/subtract                     0/mod/indirect  7/rm32/edi    .           .             .           1/r32/ecx   .               .                 # subtract *edi from ecx
 137     51/push-ecx
 138     # . . call
 139     e8/call  check-ints-equal/disp32
 140     # . . discard args
 141     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 142     # . epilogue
 143     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 144     5d/pop-to-ebp
 145     c3/return
 146 
 147 test-next-token-from-slice-nothing:
 148     # . prologue
 149     55/push-ebp
 150     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 151     # (eax..ecx) = "    "
 152     b8/copy-to-eax  "    "/imm32
 153     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
 154     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
 155     05/add-to-eax  4/imm32
 156     # var out/edi: slice
 157     68/push  0/imm32/end
 158     68/push  0/imm32/start
 159     89/copy                         3/mod/direct    7/rm32/edi    .           .             .           4/r32/esp   .               .                 # copy esp to edi
 160     # next-token-from-slice(in, 0x20/space, out)
 161     # . . push args
 162     57/push-edi
 163     68/push  0x20/imm32
 164     51/push-ecx
 165     50/push-eax
 166     # . . call
 167     e8/call  next-token-from-slice/disp32
 168     # . . discard args
 169     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
 170     # out should be empty
 171     # . check-ints-equal(out->end - out->start, 0, msg)
 172     # . . push args
 173     68/push  "F - test-next-token-from-slice-Eof"/imm32
 174     68/push  0/imm32
 175     # . . push out->start - in->start
 176     8b/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(edi+4) to ecx
 177     2b/subtract                     0/mod/indirect  7/rm32/edi    .           .             .           1/r32/ecx   .               .                 # subtract *edi from ecx
 178     51/push-ecx
 179     # . . call
 180     e8/call  check-ints-equal/disp32
 181     # . . discard args
 182     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 183     # . epilogue
 184     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 185     5d/pop-to-ebp
 186     c3/return
 187 
 188 skip-chars-matching:  # in: (addr stream byte), delimiter: byte
 189     # . prologue
 190     55/push-ebp
 191     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 192     # . save registers
 193     50/push-eax
 194     51/push-ecx
 195     52/push-edx
 196     53/push-ebx
 197     56/push-esi
 198     # esi = in
 199     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
 200     # ecx = in->read
 201     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(esi+4) to ecx
 202     # ebx = in->write
 203     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           3/r32/ebx   .               .                 # copy *esi to ebx
 204     # edx = delimiter
 205     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           2/r32/edx   0xc/disp8       .                 # copy *(ebp+12) to edx
 206 $skip-chars-matching:loop:
 207     # if (in->read >= in->write) break
 208     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           3/r32/ebx   .               .                 # compare ecx with ebx
 209     7d/jump-if->=  $skip-chars-matching:end/disp8
 210     # eax = in->data[in->read]
 211     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
 212     8a/copy-byte                    1/mod/*+disp8   4/rm32/sib    6/base/esi  1/index/ecx   .           0/r32/AL    0xc/disp8       .                 # copy byte at *(esi+ecx+12) to AL
 213     # if (eax != delimiter) break
 214     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # compare eax and edx
 215     75/jump-if-!=  $skip-chars-matching:end/disp8
 216     # ++in->read
 217     41/increment-ecx
 218     eb/jump  $skip-chars-matching:loop/disp8
 219 $skip-chars-matching:end:
 220     # persist in->read
 221     89/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy ecx to *(esi+4)
 222     # . restore registers
 223     5e/pop-to-esi
 224     5b/pop-to-ebx
 225     5a/pop-to-edx
 226     59/pop-to-ecx
 227     58/pop-to-eax
 228     # . epilogue
 229     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 230     5d/pop-to-ebp
 231     c3/return
 232 
 233 test-skip-chars-matching:
 234     # setup
 235     # . clear-stream(_test-stream)
 236     # . . push args
 237     68/push  _test-stream/imm32
 238     # . . call
 239     e8/call  clear-stream/disp32
 240     # . . discard args
 241     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 242     # write(_test-stream, "  ab")
 243     # . . push args
 244     68/push  "  ab"/imm32
 245     68/push  _test-stream/imm32
 246     # . . call
 247     e8/call  write/disp32
 248     # . . discard args
 249     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 250     # skip-chars-matching(_test-stream, 0x20/space)
 251     # . . push args
 252     68/push  0x20/imm32
 253     68/push  _test-stream/imm32
 254     # . . call
 255     e8/call  skip-chars-matching/disp32
 256     # . . discard args
 257     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 258     # check-ints-equal(_test-stream->read, 2, msg)
 259     # . . push args
 260     68/push  "F - test-skip-chars-matching"/imm32
 261     68/push  2/imm32
 262     # . . push *_test-stream->read
 263     b8/copy-to-eax  _test-stream/imm32
 264     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
 265     # . . call
 266     e8/call  check-ints-equal/disp32
 267     # . . discard args
 268     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 269     # end
 270     c3/return
 271 
 272 test-skip-chars-matching-none:
 273     # setup
 274     # . clear-stream(_test-stream)
 275     # . . push args
 276     68/push  _test-stream/imm32
 277     # . . call
 278     e8/call  clear-stream/disp32
 279     # . . discard args
 280     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 281     # write(_test-stream, "ab")
 282     # . . push args
 283     68/push  "ab"/imm32
 284     68/push  _test-stream/imm32
 285     # . . call
 286     e8/call  write/disp32
 287     # . . discard args
 288     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 289     # skip-chars-matching(_test-stream, 0x20/space)
 290     # . . push args
 291     68/push  0x20/imm32
 292     68/push  _test-stream/imm32
 293     # . . call
 294     e8/call  skip-chars-matching/disp32
 295     # . . discard args
 296     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 297     # check-ints-equal(_test-stream->read, 0, msg)
 298     # . . push args
 299     68/push  "F - test-skip-chars-matching-none"/imm32
 300     68/push  0/imm32
 301     # . . push *_test-stream->read
 302     b8/copy-to-eax  _test-stream/imm32
 303     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
 304     # . . call
 305     e8/call  check-ints-equal/disp32
 306     # . . discard args
 307     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 308     # end
 309     c3/return
 310 
 311 skip-chars-matching-whitespace:  # in: (addr stream byte)
 312     # . prologue
 313     55/push-ebp
 314     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 315     # . save registers
 316     50/push-eax
 317     51/push-ecx
 318     53/push-ebx
 319     56/push-esi
 320     # esi = in
 321     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
 322     # ecx = in->read
 323     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(esi+4) to ecx
 324     # ebx = in->write
 325     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           3/r32/ebx   .               .                 # copy *esi to ebx
 326 $skip-chars-matching-whitespace:loop:
 327     # if (in->read >= in->write) break
 328     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           3/r32/ebx   .               .                 # compare ecx with ebx
 329     7d/jump-if->=  $skip-chars-matching-whitespace:end/disp8
 330     # eax = in->data[in->read]
 331     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
 332     8a/copy-byte                    1/mod/*+disp8   4/rm32/sib    6/base/esi  1/index/ecx   .           0/r32/AL    0xc/disp8       .                 # copy byte at *(esi+ecx+12) to AL
 333     # if (eax == ' ') goto body
 334     3d/compare-eax-and  0x20/imm32/space
 335     74/jump-if-=  $skip-chars-matching-whitespace:body/disp8
 336     # if (eax == '\n') goto body
 337     3d/compare-eax-and  0x0a/imm32/newline
 338     74/jump-if-=  $skip-chars-matching-whitespace:body/disp8
 339     # if (eax == '\t') goto body
 340     3d/compare-eax-and  0x09/imm32/tab
 341     74/jump-if-=  $skip-chars-matching-whitespace:body/disp8
 342     # if (eax != '\r') break
 343     3d/compare-eax-and  0x0d/imm32/cr
 344     75/jump-if-!=  $skip-chars-matching-whitespace:end/disp8
 345 $skip-chars-matching-whitespace:body:
 346     # ++in->read
 347     41/increment-ecx
 348     eb/jump  $skip-chars-matching-whitespace:loop/disp8
 349 $skip-chars-matching-whitespace:end:
 350     # persist in->read
 351     89/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy ecx to *(esi+4)
 352     # . restore registers
 353     5e/pop-to-esi
 354     5b/pop-to-ebx
 355     59/pop-to-ecx
 356     58/pop-to-eax
 357     # . epilogue
 358     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 359     5d/pop-to-ebp
 360     c3/return
 361 
 362 test-skip-chars-matching-whitespace:
 363     # setup
 364     # . clear-stream(_test-stream)
 365     # . . push args
 366     68/push  _test-stream/imm32
 367     # . . call
 368     e8/call  clear-stream/disp32
 369     # . . discard args
 370     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 371     # write(_test-stream, " \nab")
 372     # . . push args
 373     68/push  " \nab"/imm32
 374     68/push  _test-stream/imm32
 375     # . . call
 376     e8/call  write/disp32
 377     # . . discard args
 378     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 379     # skip-chars-matching-whitespace(_test-stream)
 380     # . . push args
 381     68/push  _test-stream/imm32
 382     # . . call
 383     e8/call  skip-chars-matching-whitespace/disp32
 384     # . . discard args
 385     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 386     # check-ints-equal(_test-stream->read, 2, msg)
 387     # . . push args
 388     68/push  "F - test-skip-chars-matching-whitespace"/imm32
 389     68/push  2/imm32
 390     # . . push *_test-stream->read
 391     b8/copy-to-eax  _test-stream/imm32
 392     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
 393     # . . call
 394     e8/call  check-ints-equal/disp32
 395     # . . discard args
 396     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 397     # end
 398     c3/return
 399 
 400 # minor fork of 'skip-chars-matching'
 401 skip-chars-not-matching:  # in: (addr stream byte), delimiter: byte
 402     # . prologue
 403     55/push-ebp
 404     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 405     # . save registers
 406     50/push-eax
 407     51/push-ecx
 408     52/push-edx
 409     53/push-ebx
 410     56/push-esi
 411     # esi = in
 412     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
 413     # ecx = in->read
 414     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(esi+4) to ecx
 415     # ebx = in->write
 416     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           3/r32/ebx   .               .                 # copy *esi to ebx
 417     # edx = delimiter
 418     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           2/r32/edx   0xc/disp8       .                 # copy *(ebp+12) to edx
 419 $skip-chars-not-matching:loop:
 420     # if (in->read >= in->write) break
 421     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           3/r32/ebx   .               .                 # compare ecx with ebx
 422     7d/jump-if->=  $skip-chars-not-matching:end/disp8
 423     # eax = in->data[in->read]
 424     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
 425     8a/copy-byte                    1/mod/*+disp8   4/rm32/sib    6/base/esi  1/index/ecx   .           0/r32/AL    0xc/disp8       .                 # copy byte at *(esi+ecx+12) to AL
 426     # if (eax == delimiter) break
 427     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # compare eax and edx
 428     74/jump-if-=  $skip-chars-not-matching:end/disp8
 429     # ++in->read
 430     41/increment-ecx
 431     eb/jump  $skip-chars-not-matching:loop/disp8
 432 $skip-chars-not-matching:end:
 433     # persist in->read
 434     89/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy ecx to *(esi+4)
 435     # . restore registers
 436     5e/pop-to-esi
 437     5b/pop-to-ebx
 438     5a/pop-to-edx
 439     59/pop-to-ecx
 440     58/pop-to-eax
 441     # . epilogue
 442     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 443     5d/pop-to-ebp
 444     c3/return
 445 
 446 test-skip-chars-not-matching:
 447     # setup
 448     # . clear-stream(_test-stream)
 449     # . . push args
 450     68/push  _test-stream/imm32
 451     # . . call
 452     e8/call  clear-stream/disp32
 453     # . . discard args
 454     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 455     # write(_test-stream, "ab ")
 456     # . . push args
 457     68/push  "ab "/imm32
 458     68/push  _test-stream/imm32
 459     # . . call
 460     e8/call  write/disp32
 461     # . . discard args
 462     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 463     # skip-chars-not-matching(_test-stream, 0x20/space)
 464     # . . push args
 465     68/push  0x20/imm32
 466     68/push  _test-stream/imm32
 467     # . . call
 468     e8/call  skip-chars-not-matching/disp32
 469     # . . discard args
 470     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 471     # check-ints-equal(_test-stream->read, 2, msg)
 472     # . . push args
 473     68/push  "F - test-skip-chars-not-matching"/imm32
 474     68/push  2/imm32
 475     # . . push *_test-stream->read
 476     b8/copy-to-eax  _test-stream/imm32
 477     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
 478     # . . call
 479     e8/call  check-ints-equal/disp32
 480     # . . discard args
 481     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 482     # end
 483     c3/return
 484 
 485 test-skip-chars-not-matching-none:
 486     # setup
 487     # . clear-stream(_test-stream)
 488     # . . push args
 489     68/push  _test-stream/imm32
 490     # . . call
 491     e8/call  clear-stream/disp32
 492     # . . discard args
 493     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 494     # write(_test-stream, " ab")
 495     # . . push args
 496     68/push  " ab"/imm32
 497     68/push  _test-stream/imm32
 498     # . . call
 499     e8/call  write/disp32
 500     # . . discard args
 501     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 502     # skip-chars-not-matching(_test-stream, 0x20/space)
 503     # . . push args
 504     68/push  0x20/imm32
 505     68/push  _test-stream/imm32
 506     # . . call
 507     e8/call  skip-chars-not-matching/disp32
 508     # . . discard args
 509     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 510     # check-ints-equal(_test-stream->read, 0, msg)
 511     # . . push args
 512     68/push  "F - test-skip-chars-not-matching-none"/imm32
 513     68/push  0/imm32
 514     # . . push *_test-stream->read
 515     b8/copy-to-eax  _test-stream/imm32
 516     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
 517     # . . call
 518     e8/call  check-ints-equal/disp32
 519     # . . discard args
 520     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 521     # end
 522     c3/return
 523 
 524 test-skip-chars-not-matching-all:
 525     # setup
 526     # . clear-stream(_test-stream)
 527     # . . push args
 528     68/push  _test-stream/imm32
 529     # . . call
 530     e8/call  clear-stream/disp32
 531     # . . discard args
 532     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 533     # write(_test-stream, "ab")
 534     # . . push args
 535     68/push  "ab"/imm32
 536     68/push  _test-stream/imm32
 537     # . . call
 538     e8/call  write/disp32
 539     # . . discard args
 540     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 541     # skip-chars-not-matching(_test-stream, 0x20/space)
 542     # . . push args
 543     68/push  0x20/imm32
 544     68/push  _test-stream/imm32
 545     # . . call
 546     e8/call  skip-chars-not-matching/disp32
 547     # . . discard args
 548     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 549     # check-ints-equal(_test-stream->read, 2, msg)
 550     # . . push args
 551     68/push  "F - test-skip-chars-not-matching-all"/imm32
 552     68/push  2/imm32
 553     # . . push *_test-stream->read
 554     b8/copy-to-eax  _test-stream/imm32
 555     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
 556     # . . call
 557     e8/call  check-ints-equal/disp32
 558     # . . discard args
 559     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 560     # end
 561     c3/return
 562 
 563 skip-chars-not-matching-whitespace:  # in: (addr stream byte)
 564     # . prologue
 565     55/push-ebp
 566     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 567     # . save registers
 568     50/push-eax
 569     51/push-ecx
 570     53/push-ebx
 571     56/push-esi
 572     # esi = in
 573     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
 574     # ecx = in->read
 575     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(esi+4) to ecx
 576     # ebx = in->write
 577     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           3/r32/ebx   .               .                 # copy *esi to ebx
 578 $skip-chars-not-matching-whitespace:loop:
 579     # if (in->read >= in->write) break
 580     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           3/r32/ebx   .               .                 # compare ecx with ebx
 581     7d/jump-if->=  $skip-chars-not-matching-whitespace:end/disp8
 582     # eax = in->data[in->read]
 583     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
 584     8a/copy-byte                    1/mod/*+disp8   4/rm32/sib    6/base/esi  1/index/ecx   .           0/r32/AL    0xc/disp8       .                 # copy byte at *(esi+ecx+12) to AL
 585     # if (eax == ' ') break
 586     3d/compare-eax-and  0x20/imm32/space
 587     74/jump-if-=  $skip-chars-not-matching-whitespace:end/disp8
 588     # if (eax == '\n') break
 589     3d/compare-eax-and  0x0a/imm32/newline
 590     74/jump-if-=  $skip-chars-not-matching-whitespace:end/disp8
 591     # if (eax == '\t') break
 592     3d/compare-eax-and  0x09/imm32/tab
 593     74/jump-if-=  $skip-chars-not-matching-whitespace:end/disp8
 594     # if (eax == '\r') break
 595     3d/compare-eax-and  0x0d/imm32/cr
 596     74/jump-if-=  $skip-chars-not-matching-whitespace:end/disp8
 597     # ++in->read
 598     41/increment-ecx
 599     eb/jump  $skip-chars-not-matching-whitespace:loop/disp8
 600 $skip-chars-not-matching-whitespace:end:
 601     # persist in->read
 602     89/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy ecx to *(esi+4)
 603     # . restore registers
 604     5e/pop-to-esi
 605     5b/pop-to-ebx
 606     59/pop-to-ecx
 607     58/pop-to-eax
 608     # . epilogue
 609     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 610     5d/pop-to-ebp
 611     c3/return
 612 
 613 test-skip-chars-not-matching-whitespace:
 614     # setup
 615     # . clear-stream(_test-stream)
 616     # . . push args
 617     68/push  _test-stream/imm32
 618     # . . call
 619     e8/call  clear-stream/disp32
 620     # . . discard args
 621     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 622     # write(_test-stream, "ab\n")
 623     # . . push args
 624     68/push  "ab\n"/imm32
 625     68/push  _test-stream/imm32
 626     # . . call
 627     e8/call  write/disp32
 628     # . . discard args
 629     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 630     # skip-chars-not-matching-whitespace(_test-stream)
 631     # . . push args
 632     68/push  _test-stream/imm32
 633     # . . call
 634     e8/call  skip-chars-not-matching-whitespace/disp32
 635     # . . discard args
 636     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 637     # check-ints-equal(_test-stream->read, 2, msg)
 638     # . . push args
 639     68/push  "F - test-skip-chars-not-matching-whitespace"/imm32
 640     68/push  2/imm32
 641     # . . push *_test-stream->read
 642     b8/copy-to-eax  _test-stream/imm32
 643     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
 644     # . . call
 645     e8/call  check-ints-equal/disp32
 646     # . . discard args
 647     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 648     # end
 649     c3/return
 650 
 651 skip-chars-matching-in-slice:  # curr: (addr byte), end: (addr byte), delimiter: byte -> curr/eax: (addr byte)
 652     # . prologue
 653     55/push-ebp
 654     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 655     # . save registers
 656     51/push-ecx
 657     52/push-edx
 658     53/push-ebx
 659     # eax = curr
 660     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   8/disp8         .                 # copy *(ebp+8) to eax
 661     # ecx = end
 662     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   0xc/disp8       .                 # copy *(ebp+12) to ecx
 663     # edx = delimiter
 664     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           2/r32/edx   0x10/disp8       .                 # copy *(ebp+16) to edx
 665     # var c/ebx: byte = 0
 666     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
 667 $skip-chars-matching-in-slice:loop:
 668     # if (curr >= end) break
 669     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # compare eax with ecx
 670     73/jump-if-addr>=  $skip-chars-matching-in-slice:end/disp8
 671     # c = *curr
 672     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           3/r32/BL    .               .                 # copy byte at *eax to BL
 673     # if (c != delimiter) break
 674     39/compare                      3/mod/direct    3/rm32/ebx    .           .             .           2/r32/edx   .               .                 # compare ebx and edx
 675     75/jump-if-!=  $skip-chars-matching-in-slice:end/disp8
 676     # ++curr
 677     40/increment-eax
 678     eb/jump  $skip-chars-matching-in-slice:loop/disp8
 679 $skip-chars-matching-in-slice:end:
 680     # . restore registers
 681     5b/pop-to-ebx
 682     5a/pop-to-edx
 683     59/pop-to-ecx
 684     # . epilogue
 685     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 686     5d/pop-to-ebp
 687     c3/return
 688 
 689 test-skip-chars-matching-in-slice:
 690     # (eax..ecx) = "  ab"
 691     b8/copy-to-eax  "  ab"/imm32
 692     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
 693     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
 694     05/add-to-eax  4/imm32
 695     # eax = skip-chars-matching-in-slice(eax, ecx, 0x20/space)
 696     # . . push args
 697     68/push  0x20/imm32/space
 698     51/push-ecx
 699     50/push-eax
 700     # . . call
 701     e8/call  skip-chars-matching-in-slice/disp32
 702     # . . discard args
 703     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 704     # check-ints-equal(ecx-eax, 2, msg)
 705     # . . push args
 706     68/push  "F - test-skip-chars-matching-in-slice"/imm32
 707     68/push  2/imm32
 708     # . . push ecx-eax
 709     29/subtract                     3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract eax from ecx
 710     51/push-ecx
 711     # . . call
 712     e8/call  check-ints-equal/disp32
 713     # . . discard args
 714     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 715     # end
 716     c3/return
 717 
 718 test-skip-chars-matching-in-slice-none:
 719     # (eax..ecx) = "ab"
 720     b8/copy-to-eax  "ab"/imm32
 721     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
 722     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
 723     05/add-to-eax  4/imm32
 724     # eax = skip-chars-matching-in-slice(eax, ecx, 0x20/space)
 725     # . . push args
 726     68/push  0x20/imm32/space
 727     51/push-ecx
 728     50/push-eax
 729     # . . call
 730     e8/call  skip-chars-matching-in-slice/disp32
 731     # . . discard args
 732     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 733     # check-ints-equal(ecx-eax, 2, msg)
 734     # . . push args
 735     68/push  "F - test-skip-chars-matching-in-slice-none"/imm32
 736     68/push  2/imm32
 737     # . . push ecx-eax
 738     29/subtract                     3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract eax from ecx
 739     51/push-ecx
 740     # . . call
 741     e8/call  check-ints-equal/disp32
 742     # . . discard args
 743     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 744     # end
 745     c3/return
 746 
 747 skip-chars-matching-whitespace-in-slice:  # curr: (addr byte), end: (addr byte) -> curr/eax: (addr byte)
 748     # . prologue
 749     55/push-ebp
 750     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 751     # . save registers
 752     51/push-ecx
 753     53/push-ebx
 754     # eax = curr
 755     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   8/disp8         .                 # copy *(ebp+8) to eax
 756     # ecx = end
 757     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   0xc/disp8       .                 # copy *(ebp+12) to ecx
 758     # var c/ebx: byte = 0
 759     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
 760 $skip-chars-matching-whitespace-in-slice:loop:
 761     # if (curr >= end) break
 762     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # compare eax with ecx
 763     0f 83/jump-if-addr>=  $skip-chars-matching-in-slice:end/disp32
 764     # c = *curr
 765     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           3/r32/BL    .               .                 # copy byte at *eax to BL
 766     # if (c == ' ') goto body
 767     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x20/imm32/space  # compare ebx
 768     74/jump-if-=  $skip-chars-matching-whitespace-in-slice:body/disp8
 769     # if (c == '\n') goto body
 770     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x0a/imm32/newline  # compare ebx
 771     74/jump-if-=  $skip-chars-matching-whitespace-in-slice:body/disp8
 772     # if (c == '\t') goto body
 773     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x09/imm32/tab    # compare ebx
 774     74/jump-if-=  $skip-chars-matching-whitespace-in-slice:body/disp8
 775     # if (c != '\r') break
 776     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x0d/imm32/cr     # compare ebx
 777     75/jump-if-!=  $skip-chars-matching-whitespace-in-slice:end/disp8
 778 $skip-chars-matching-whitespace-in-slice:body:
 779     # ++curr
 780     40/increment-eax
 781     eb/jump  $skip-chars-matching-whitespace-in-slice:loop/disp8
 782 $skip-chars-matching-whitespace-in-slice:end:
 783     # . restore registers
 784     5b/pop-to-ebx
 785     59/pop-to-ecx
 786     # . epilogue
 787     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 788     5d/pop-to-ebp
 789     c3/return
 790 
 791 test-skip-chars-matching-whitespace-in-slice:
 792     # (eax..ecx) = " \nab"
 793     b8/copy-to-eax  " \nab"/imm32
 794     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
 795     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
 796     05/add-to-eax  4/imm32
 797     # eax = skip-chars-matching-whitespace-in-slice(eax, ecx)
 798     # . . push args
 799     51/push-ecx
 800     50/push-eax
 801     # . . call
 802     e8/call  skip-chars-matching-whitespace-in-slice/disp32
 803     # . . discard args
 804     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 805     # check-ints-equal(ecx-eax, 2, msg)
 806     # . . push args
 807     68/push  "F - test-skip-chars-matching-whitespace-in-slice"/imm32
 808     68/push  2/imm32
 809     # . . push ecx-eax
 810     29/subtract                     3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract eax from ecx
 811     51/push-ecx
 812     # . . call
 813     e8/call  check-ints-equal/disp32
 814     # . . discard args
 815     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 816     # end
 817     c3/return
 818 
 819 # minor fork of 'skip-chars-matching-in-slice'
 820 skip-chars-not-matching-in-slice:  # curr: (addr byte), end: (addr byte), delimiter: byte -> curr/eax: (addr byte)
 821     # . prologue
 822     55/push-ebp
 823     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 824     # . save registers
 825     51/push-ecx
 826     52/push-edx
 827     53/push-ebx
 828     # eax = curr
 829     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   8/disp8         .                 # copy *(ebp+8) to eax
 830     # ecx = end
 831     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   0xc/disp8       .                 # copy *(ebp+12) to ecx
 832     # edx = delimiter
 833     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           2/r32/edx   0x10/disp8       .                 # copy *(ebp+16) to edx
 834     # var c/ebx: byte = 0
 835     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
 836 $skip-chars-not-matching-in-slice:loop:
 837     # if (curr >= end) break
 838     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # compare eax with ecx
 839     73/jump-if-addr>=  $skip-chars-not-matching-in-slice:end/disp8
 840     # c = *curr
 841     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           3/r32/BL    .               .                 # copy byte at *eax to BL
 842     # if (c == delimiter) break
 843     39/compare                      3/mod/direct    3/rm32/ebx    .           .             .           2/r32/edx   .               .                 # compare ebx and edx
 844     74/jump-if-=  $skip-chars-not-matching-in-slice:end/disp8
 845     # ++curr
 846     40/increment-eax
 847     eb/jump  $skip-chars-not-matching-in-slice:loop/disp8
 848 $skip-chars-not-matching-in-slice:end:
 849     # . restore registers
 850     5b/pop-to-ebx
 851     5a/pop-to-edx
 852     59/pop-to-ecx
 853     # . epilogue
 854     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 855     5d/pop-to-ebp
 856     c3/return
 857 
 858 test-skip-chars-not-matching-in-slice:
 859     # (eax..ecx) = "ab "
 860     b8/copy-to-eax  "ab "/imm32
 861     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
 862     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
 863     05/add-to-eax  4/imm32
 864     # eax = skip-chars-not-matching-in-slice(eax, ecx, 0x20/space)
 865     # . . push args
 866     68/push  0x20/imm32/space
 867     51/push-ecx
 868     50/push-eax
 869     # . . call
 870     e8/call  skip-chars-not-matching-in-slice/disp32
 871     # . . discard args
 872     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 873     # check-ints-equal(ecx-eax, 1, msg)
 874     # . . push args
 875     68/push  "F - test-skip-chars-not-matching-in-slice"/imm32
 876     68/push  1/imm32
 877     # . . push ecx-eax
 878     29/subtract                     3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract eax from ecx
 879     51/push-ecx
 880     # . . call
 881     e8/call  check-ints-equal/disp32
 882     # . . discard args
 883     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 884     # end
 885     c3/return
 886 
 887 test-skip-chars-not-matching-in-slice-none:
 888     # (eax..ecx) = " ab"
 889     b8/copy-to-eax  " ab"/imm32
 890     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
 891     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
 892     05/add-to-eax  4/imm32
 893     # eax = skip-chars-not-matching-in-slice(eax, ecx, 0x20/space)
 894     # . . push args
 895     68/push  0x20/imm32/space
 896     51/push-ecx
 897     50/push-eax
 898     # . . call
 899     e8/call  skip-chars-not-matching-in-slice/disp32
 900     # . . discard args
 901     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 902     # check-ints-equal(ecx-eax, 3, msg)
 903     # . . push args
 904     68/push  "F - test-skip-chars-not-matching-in-slice-none"/imm32
 905     68/push  3/imm32
 906     # . . push ecx-eax
 907     29/subtract                     3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract eax from ecx
 908     51/push-ecx
 909     # . . call
 910     e8/call  check-ints-equal/disp32
 911     # . . discard args
 912     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 913     # end
 914     c3/return
 915 
 916 test-skip-chars-not-matching-in-slice-all:
 917     # (eax..ecx) = "ab"
 918     b8/copy-to-eax  "ab"/imm32
 919     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
 920     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
 921     05/add-to-eax  4/imm32
 922     # eax = skip-chars-not-matching-in-slice(eax, ecx, 0x20/space)
 923     # . . push args
 924     68/push  0x20/imm32/space
 925     51/push-ecx
 926     50/push-eax
 927     # . . call
 928     e8/call  skip-chars-not-matching-in-slice/disp32
 929     # . . discard args
 930     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 931     # check-ints-equal(ecx-eax, 0, msg)
 932     # . . push args
 933     68/push  "F - test-skip-chars-not-matching-in-slice-all"/imm32
 934     68/push  0/imm32
 935     # . . push ecx-eax
 936     29/subtract                     3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract eax from ecx
 937     51/push-ecx
 938     # . . call
 939     e8/call  check-ints-equal/disp32
 940     # . . discard args
 941     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 942     # end
 943     c3/return
 944 
 945 skip-chars-not-matching-whitespace-in-slice:  # curr: (addr byte), end: (addr byte) -> curr/eax: (addr byte)
 946     # . prologue
 947     55/push-ebp
 948     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 949     # . save registers
 950     51/push-ecx
 951     53/push-ebx
 952     # eax = curr
 953     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   8/disp8         .                 # copy *(ebp+8) to eax
 954     # ecx = end
 955     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   0xc/disp8       .                 # copy *(ebp+12) to ecx
 956     # var c/ebx: byte = 0
 957     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
 958 $skip-chars-not-matching-whitespace-in-slice:loop:
 959     # if (curr >= end) break
 960     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # compare eax with ecx
 961     0f 83/jump-if-addr>=  $skip-chars-not-matching-in-slice:end/disp32
 962     # c = *curr
 963     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           3/r32/BL    .               .                 # copy byte at *eax to BL
 964     # if (c == ' ') break
 965     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x20/imm32/space  # compare ebx
 966     74/jump-if-=  $skip-chars-not-matching-whitespace-in-slice:end/disp8
 967     # if (c == '\n') break
 968     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x0a/imm32/newline  # compare ebx
 969     74/jump-if-=  $skip-chars-not-matching-whitespace-in-slice:end/disp8
 970     # if (c == '\t') break
 971     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x09/imm32/tab    # compare ebx
 972     74/jump-if-=  $skip-chars-not-matching-whitespace-in-slice:end/disp8
 973     # if (c == '\r') break
 974     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x0d/imm32/cr     # compare ebx
 975     74/jump-if-=  $skip-chars-not-matching-whitespace-in-slice:end/disp8
 976     # ++curr
 977     40/increment-eax
 978     eb/jump  $skip-chars-not-matching-whitespace-in-slice:loop/disp8
 979 $skip-chars-not-matching-whitespace-in-slice:end:
 980     # . restore registers
 981     5b/pop-to-ebx
 982     59/pop-to-ecx
 983     # . epilogue
 984     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 985     5d/pop-to-ebp
 986     c3/return
 987 
 988 test-skip-chars-not-matching-whitespace-in-slice:
 989     # (eax..ecx) = "ab\n"
 990     b8/copy-to-eax  "ab\n"/imm32
 991     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
 992     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
 993     05/add-to-eax  4/imm32
 994     # eax = skip-chars-not-matching-whitespace-in-slice(eax, ecx)
 995     # . . push args
 996     51/push-ecx
 997     50/push-eax
 998     # . . call
 999     e8/call  skip-chars-not-matching-whitespace-in-slice/disp32
1000     # . . discard args
1001     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1002     # check-ints-equal(ecx-eax, 1, msg)
1003     # . . push args
1004     68/push  "F - test-skip-chars-not-matching-whitespace-in-slice"/imm32
1005     68/push  1/imm32
1006     # . . push ecx-eax
1007     29/subtract                     3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract eax from ecx
1008     51/push-ecx
1009     # . . call
1010     e8/call  check-ints-equal/disp32
1011     # . . discard args
1012     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1013     # end
1014     c3/return
1015 
1016 # update line->read to end of string literal surrounded by double quotes
1017 # line->read must start out at a double-quote
1018 skip-string:  # line: (addr stream byte)
1019     # . prologue
1020     55/push-ebp
1021     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1022     # . save registers
1023     50/push-eax
1024     51/push-ecx
1025     52/push-edx
1026     # ecx = line
1027     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
1028     # eax = skip-string-in-slice(&line->data[line->read], &line->data[line->write])
1029     # . . push &line->data[line->write]
1030     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .                         2/r32/edx   8/disp8         .                 # copy *(ecx+8) to edx
1031     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    1/base/ecx  2/index/edx   .           2/r32/edx   0xc/disp8       .                 # copy ecx+edx+12 to edx
1032     52/push-edx
1033     # . . push &line->data[line->read]
1034     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .                         2/r32/edx   4/disp8         .                 # copy *(ecx+4) to edx
1035     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    1/base/ecx  2/index/edx   .           2/r32/edx   0xc/disp8       .                 # copy ecx+edx+12 to edx
1036     52/push-edx
1037     # . . call
1038     e8/call  skip-string-in-slice/disp32
1039     # . . discard args
1040     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1041     # line->read = eax - line->data
1042     29/subtract                     3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # subtract ecx from eax
1043     2d/subtract-from-eax  0xc/imm32
1044     89/copy                         1/mod/*+disp8   1/rm32/ecx    .           .                         0/r32/eax   4/disp8         .                 # copy eax to *(ecx+4)
1045 $skip-string:end:
1046     # . restore registers
1047     5a/pop-to-edx
1048     59/pop-to-ecx
1049     58/pop-to-eax
1050     # . epilogue
1051     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1052     5d/pop-to-ebp
1053     c3/return
1054 
1055 test-skip-string:
1056     # . prologue
1057     55/push-ebp
1058     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1059     # setup
1060     # . clear-stream(_test-stream)
1061     # . . push args
1062     68/push  _test-stream/imm32
1063     # . . call
1064     e8/call  clear-stream/disp32
1065     # . . discard args
1066     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1067     # . write(_test-stream, "\"abc\" def")
1068     # .                   indices:  0123 45
1069     # . . push args
1070     68/push  "\"abc\" def"/imm32
1071     68/push  _test-stream/imm32
1072     # . . call
1073     e8/call  write/disp32
1074     # . . discard args
1075     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1076     # precondition: line->read == 0
1077     # . . push args
1078     68/push  "F - test-skip-string/precondition"/imm32
1079     68/push  0/imm32
1080     b8/copy-to-eax  _test-stream/imm32
1081     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1082     # . . call
1083     e8/call  check-ints-equal/disp32
1084     # . . discard args
1085     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1086     # skip-string(_test-stream)
1087     # . . push args
1088     68/push  _test-stream/imm32
1089     # . . call
1090     e8/call  skip-string/disp32
1091     # . . discard args
1092     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1093     # check-ints-equal(line->read, 5, msg)
1094     # . . push args
1095     68/push  "F - test-skip-string"/imm32
1096     68/push  5/imm32
1097     b8/copy-to-eax  _test-stream/imm32
1098     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1099     # . . call
1100     e8/call  check-ints-equal/disp32
1101     # . . discard args
1102     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1103     # . epilogue
1104     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1105     5d/pop-to-ebp
1106     c3/return
1107 
1108 test-skip-string-ignores-spaces:
1109     # . prologue
1110     55/push-ebp
1111     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1112     # setup
1113     # . clear-stream(_test-stream)
1114     # . . push args
1115     68/push  _test-stream/imm32
1116     # . . call
1117     e8/call  clear-stream/disp32
1118     # . . discard args
1119     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1120     # . write(_test-stream, "\"a b\"/yz")
1121     # .                   indices:  0123 45
1122     # . . push args
1123     68/push  "\"a b\"/yz"/imm32
1124     68/push  _test-stream/imm32
1125     # . . call
1126     e8/call  write/disp32
1127     # . . discard args
1128     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1129     # precondition: line->read == 0
1130     # . . push args
1131     68/push  "F - test-skip-string-ignores-spaces/precondition"/imm32
1132     68/push  0/imm32
1133     b8/copy-to-eax  _test-stream/imm32
1134     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1135     # . . call
1136     e8/call  check-ints-equal/disp32
1137     # . . discard args
1138     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1139     # skip-string(_test-stream)
1140     # . . push args
1141     68/push  _test-stream/imm32
1142     # . . call
1143     e8/call  skip-string/disp32
1144     # . . discard args
1145     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1146     # check-ints-equal(line->read, 5, msg)
1147     # . . push args
1148     68/push  "F - test-skip-string-ignores-spaces"/imm32
1149     68/push  5/imm32
1150     b8/copy-to-eax  _test-stream/imm32
1151     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1152     # . . call
1153     e8/call  check-ints-equal/disp32
1154     # . . discard args
1155     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1156     # . epilogue
1157     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1158     5d/pop-to-ebp
1159     c3/return
1160 
1161 test-skip-string-ignores-escapes:
1162     # . prologue
1163     55/push-ebp
1164     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1165     # setup
1166     # . clear-stream(_test-stream)
1167     # . . push args
1168     68/push  _test-stream/imm32
1169     # . . call
1170     e8/call  clear-stream/disp32
1171     # . . discard args
1172     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1173     # . write(_test-stream, "\"a\\\"b\"/yz")
1174     # .                   indices:  01 2 34 56
1175     # . . push args
1176     68/push  "\"a\\\"b\"/yz"/imm32
1177     68/push  _test-stream/imm32
1178     # . . call
1179     e8/call  write/disp32
1180     # . . discard args
1181     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1182     # precondition: line->read == 0
1183     # . . push args
1184     68/push  "F - test-skip-string-ignores-escapes/precondition"/imm32
1185     68/push  0/imm32
1186     b8/copy-to-eax  _test-stream/imm32
1187     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1188     # . . call
1189     e8/call  check-ints-equal/disp32
1190     # . . discard args
1191     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1192     # skip-string(_test-stream)
1193     # . . push args
1194     68/push  _test-stream/imm32
1195     # . . call
1196     e8/call  skip-string/disp32
1197     # . . discard args
1198     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1199     # check-ints-equal(line->read, 6, msg)
1200     # . . push args
1201     68/push  "F - test-skip-string-ignores-escapes"/imm32
1202     68/push  6/imm32
1203     b8/copy-to-eax  _test-stream/imm32
1204     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+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     # . epilogue
1210     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1211     5d/pop-to-ebp
1212     c3/return
1213 
1214 test-skip-string-works-from-mid-stream:
1215     # . prologue
1216     55/push-ebp
1217     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1218     # setup
1219     # . clear-stream(_test-stream)
1220     # . . push args
1221     68/push  _test-stream/imm32
1222     # . . call
1223     e8/call  clear-stream/disp32
1224     # . . discard args
1225     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1226     # . write(_test-stream, "0 \"a\\\"b\"/yz")
1227     # .                   indices:  01 2 34 56
1228     # . . push args
1229     68/push  "0 \"a\\\"b\"/yz"/imm32
1230     68/push  _test-stream/imm32
1231     # . . call
1232     e8/call  write/disp32
1233     # . . discard args
1234     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1235     # precondition: line->read == 2
1236     b8/copy-to-eax  _test-stream/imm32
1237     c7          0/subop/copy        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         2/imm32           # copy to *(eax+4)
1238     # skip-string(_test-stream)
1239     # . . push args
1240     68/push  _test-stream/imm32
1241     # . . call
1242     e8/call  skip-string/disp32
1243     # . . discard args
1244     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1245     # check-ints-equal(line->read, 8, msg)
1246     # . . push args
1247     68/push  "F - test-skip-string-works-from-mid-stream"/imm32
1248     68/push  8/imm32
1249     b8/copy-to-eax  _test-stream/imm32
1250     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1251     # . . call
1252     e8/call  check-ints-equal/disp32
1253     # . . discard args
1254     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1255     # . epilogue
1256     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1257     5d/pop-to-ebp
1258     c3/return
1259 
1260 skip-string-in-slice:  # curr: (addr byte), end: (addr byte) -> curr/eax: (addr byte)
1261     # . prologue
1262     55/push-ebp
1263     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1264     # . save registers
1265     51/push-ecx
1266     52/push-edx
1267     53/push-ebx
1268     # ecx = curr
1269     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
1270     # edx = end
1271     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         2/r32/edx   0xc/disp8         .               # copy *(ebp+12) to edx
1272     # var c/eax: byte = 0
1273     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
1274     # skip initial dquote
1275     41/increment-ecx
1276 $skip-string-in-slice:loop:
1277     # if (curr >= end) return curr
1278     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
1279     73/jump-if-addr>=  $skip-string-in-slice:return-curr/disp8
1280     # c = *curr
1281     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
1282 $skip-string-in-slice:dquote:
1283     # if (c == '"') break
1284     3d/compare-eax-and  0x22/imm32/double-quote
1285     74/jump-if-=  $skip-string-in-slice:break/disp8
1286 $skip-string-in-slice:check-for-escape:
1287     # if (c == '\') escape next char
1288     3d/compare-eax-and  0x5c/imm32/backslash
1289     75/jump-if-!=  $skip-string-in-slice:continue/disp8
1290 $skip-string-in-slice:escape:
1291     41/increment-ecx
1292 $skip-string-in-slice:continue:
1293     # ++curr
1294     41/increment-ecx
1295     eb/jump  $skip-string-in-slice:loop/disp8
1296 $skip-string-in-slice:break:
1297     # skip final dquote
1298     41/increment-ecx
1299 $skip-string-in-slice:return-curr:
1300     # return curr
1301     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy ecx to eax
1302 $skip-string-in-slice:end:
1303     # . restore registers
1304     5b/pop-to-ebx
1305     5a/pop-to-edx
1306     59/pop-to-ecx
1307     # . epilogue
1308     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1309     5d/pop-to-ebp
1310     c3/return
1311 
1312 test-skip-string-in-slice:
1313     # . prologue
1314     55/push-ebp
1315     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1316     # setup: (eax..ecx) = "\"abc\" def"
1317     b8/copy-to-eax  "\"abc\" def"/imm32
1318     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1319     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
1320     05/add-to-eax  4/imm32
1321     # eax = skip-string-in-slice(eax, ecx)
1322     # . . push args
1323     51/push-ecx
1324     50/push-eax
1325     # . . call
1326     e8/call  skip-string-in-slice/disp32
1327     # . . discard args
1328     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1329     # check-ints-equal(ecx-eax, 4, msg)  # number of chars remaining after the string literal
1330     # . . push args
1331     68/push  "F - test-skip-string-in-slice"/imm32
1332     68/push  4/imm32
1333     # . . push ecx-eax
1334     29/subtract                     3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract eax from ecx
1335     51/push-ecx
1336     # . . call
1337     e8/call  check-ints-equal/disp32
1338     # . . discard args
1339     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1340     # . epilogue
1341     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1342     5d/pop-to-ebp
1343     c3/return
1344 
1345 test-skip-string-in-slice-ignores-spaces:
1346     # . prologue
1347     55/push-ebp
1348     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1349     # setup: (eax..ecx) = "\"a b\"/yz"
1350     b8/copy-to-eax  "\"a b\"/yz"/imm32
1351     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1352     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
1353     05/add-to-eax  4/imm32
1354     # eax = skip-string-in-slice(eax, ecx)
1355     # . . push args
1356     51/push-ecx
1357     50/push-eax
1358     # . . call
1359     e8/call  skip-string-in-slice/disp32
1360     # . . discard args
1361     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1362     # check-ints-equal(ecx-eax, 3, msg)  # number of chars remaining after the string literal
1363     # . . push args
1364     68/push  "F - test-skip-string-in-slice-ignores-spaces"/imm32
1365     68/push  3/imm32
1366     # . . push ecx-eax
1367     29/subtract                     3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract eax from ecx
1368     51/push-ecx
1369     # . . call
1370     e8/call  check-ints-equal/disp32
1371     # . . discard args
1372     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1373     # . epilogue
1374     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1375     5d/pop-to-ebp
1376     c3/return
1377 
1378 test-skip-string-in-slice-ignores-escapes:
1379     # . prologue
1380     55/push-ebp
1381     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1382     # setup: (eax..ecx) = "\"a\\\"b\"/yz"
1383     b8/copy-to-eax  "\"a\\\"b\"/yz"/imm32
1384     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1385     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
1386     05/add-to-eax  4/imm32
1387     # eax = skip-string-in-slice(eax, ecx)
1388     # . . push args
1389     51/push-ecx
1390     50/push-eax
1391     # . . call
1392     e8/call  skip-string-in-slice/disp32
1393     # . . discard args
1394     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1395     # check-ints-equal(ecx-eax, 3, msg)  # number of chars remaining after the string literal
1396     # . . push args
1397     68/push  "F - test-skip-string-in-slice-ignores-escapes"/imm32
1398     68/push  3/imm32
1399     # . . push ecx-eax
1400     29/subtract                     3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract eax from ecx
1401     51/push-ecx
1402     # . . call
1403     e8/call  check-ints-equal/disp32
1404     # . . discard args
1405     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1406     # . epilogue
1407     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1408     5d/pop-to-ebp
1409     c3/return
1410 
1411 test-skip-string-in-slice-stops-at-end:
1412     # . prologue
1413     55/push-ebp
1414     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1415     # setup: (eax..ecx) = "\"abc"  # unbalanced dquote
1416     b8/copy-to-eax  "\"abc"/imm32
1417     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1418     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
1419     05/add-to-eax  4/imm32
1420     # eax = skip-string-in-slice(eax, ecx)
1421     # . . push args
1422     51/push-ecx
1423     50/push-eax
1424     # . . call
1425     e8/call  skip-string-in-slice/disp32
1426     # . . discard args
1427     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1428     # check-ints-equal(ecx-eax, 0, msg)  # skipped to end of slice
1429     # . . push args
1430     68/push  "F - test-skip-string-in-slice-stops-at-end"/imm32
1431     68/push  0/imm32
1432     # . . push ecx-eax
1433     29/subtract                     3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract eax from ecx
1434     51/push-ecx
1435     # . . call
1436     e8/call  check-ints-equal/disp32
1437     # . . discard args
1438     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1439     # . epilogue
1440     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1441     5d/pop-to-ebp
1442     c3/return
1443 
1444 # update line->read to ')'
1445 # line->read ends at ')'
1446 skip-until-close-paren:  # line: (addr stream byte)
1447     # . prologue
1448     55/push-ebp
1449     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1450     # . save registers
1451     50/push-eax
1452     51/push-ecx
1453     52/push-edx
1454     # ecx = line
1455     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
1456     # eax = skip-until-close-paren-in-slice(&line->data[line->read], &line->data[line->write])
1457     # . . push &line->data[line->write]
1458     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .                         2/r32/edx   8/disp8         .                 # copy *(ecx+8) to edx
1459     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    1/base/ecx  2/index/edx   .           2/r32/edx   0xc/disp8       .                 # copy ecx+edx+12 to edx
1460     52/push-edx
1461     # . . push &line->data[line->read]
1462     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .                         2/r32/edx   4/disp8         .                 # copy *(ecx+4) to edx
1463     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    1/base/ecx  2/index/edx   .           2/r32/edx   0xc/disp8       .                 # copy ecx+edx+12 to edx
1464     52/push-edx
1465     # . . call
1466     e8/call  skip-until-close-paren-in-slice/disp32
1467     # . . discard args
1468     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1469     # line->read = eax - line->data
1470     29/subtract                     3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # subtract ecx from eax
1471     2d/subtract-from-eax  0xc/imm32
1472     89/copy                         1/mod/*+disp8   1/rm32/ecx    .           .                         0/r32/eax   4/disp8         .                 # copy eax to *(ecx+4)
1473 $skip-until-close-paren:end:
1474     # . restore registers
1475     5a/pop-to-edx
1476     59/pop-to-ecx
1477     58/pop-to-eax
1478     # . epilogue
1479     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1480     5d/pop-to-ebp
1481     c3/return
1482 
1483 test-skip-until-close-paren:
1484     # . prologue
1485     55/push-ebp
1486     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1487     # setup
1488     # . clear-stream(_test-stream)
1489     # . . push args
1490     68/push  _test-stream/imm32
1491     # . . call
1492     e8/call  clear-stream/disp32
1493     # . . discard args
1494     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1495     # . write(_test-stream, "*(abc) def")
1496     # .                   indices:  0123 45
1497     # . . push args
1498     68/push  "*(abc) def"/imm32
1499     68/push  _test-stream/imm32
1500     # . . call
1501     e8/call  write/disp32
1502     # . . discard args
1503     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1504     # precondition: line->read == 0
1505     # . . push args
1506     68/push  "F - test-skip-until-close-paren/precondition"/imm32
1507     68/push  0/imm32
1508     b8/copy-to-eax  _test-stream/imm32
1509     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1510     # . . call
1511     e8/call  check-ints-equal/disp32
1512     # . . discard args
1513     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1514     # skip-until-close-paren(_test-stream)
1515     # . . push args
1516     68/push  _test-stream/imm32
1517     # . . call
1518     e8/call  skip-until-close-paren/disp32
1519     # . . discard args
1520     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1521     # check-ints-equal(line->read, 5, msg)
1522     # . . push args
1523     68/push  "F - test-skip-until-close-paren"/imm32
1524     68/push  5/imm32
1525     b8/copy-to-eax  _test-stream/imm32
1526     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1527     # . . call
1528     e8/call  check-ints-equal/disp32
1529     # . . discard args
1530     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1531     # . epilogue
1532     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1533     5d/pop-to-ebp
1534     c3/return
1535 
1536 test-skip-until-close-paren-ignores-spaces:
1537     # . prologue
1538     55/push-ebp
1539     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1540     # setup
1541     # . clear-stream(_test-stream)
1542     # . . push args
1543     68/push  _test-stream/imm32
1544     # . . call
1545     e8/call  clear-stream/disp32
1546     # . . discard args
1547     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1548     # . write(_test-stream, "*(a b)/yz")
1549     # . . push args
1550     68/push  "*(a b)/yz"/imm32
1551     68/push  _test-stream/imm32
1552     # . . call
1553     e8/call  write/disp32
1554     # . . discard args
1555     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1556     # precondition: line->read == 0
1557     # . . push args
1558     68/push  "F - test-skip-until-close-paren-ignores-spaces/precondition"/imm32
1559     68/push  0/imm32
1560     b8/copy-to-eax  _test-stream/imm32
1561     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1562     # . . call
1563     e8/call  check-ints-equal/disp32
1564     # . . discard args
1565     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1566     # skip-until-close-paren(_test-stream)
1567     # . . push args
1568     68/push  _test-stream/imm32
1569     # . . call
1570     e8/call  skip-until-close-paren/disp32
1571     # . . discard args
1572     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1573     # check-ints-equal(line->read, 5, msg)
1574     # . . push args
1575     68/push  "F - test-skip-until-close-paren-ignores-spaces"/imm32
1576     68/push  5/imm32
1577     b8/copy-to-eax  _test-stream/imm32
1578     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1579     # . . call
1580     e8/call  check-ints-equal/disp32
1581     # . . discard args
1582     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1583     # . epilogue
1584     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1585     5d/pop-to-ebp
1586     c3/return
1587 
1588 test-skip-until-close-paren-works-from-mid-stream:
1589     # . prologue
1590     55/push-ebp
1591     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1592     # setup
1593     # . clear-stream(_test-stream)
1594     # . . push args
1595     68/push  _test-stream/imm32
1596     # . . call
1597     e8/call  clear-stream/disp32
1598     # . . discard args
1599     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1600     # . write(_test-stream, "0 *(a b)/yz")
1601     # . . push args
1602     68/push  "0 *(a b)/yz"/imm32
1603     68/push  _test-stream/imm32
1604     # . . call
1605     e8/call  write/disp32
1606     # . . discard args
1607     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1608     # precondition: _test-stream->read == 2
1609     b8/copy-to-eax  _test-stream/imm32
1610     c7          0/subop/copy        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         2/imm32           # copy to *(eax+4)
1611     # skip-until-close-paren(_test-stream)
1612     # . . push args
1613     68/push  _test-stream/imm32
1614     # . . call
1615     e8/call  skip-until-close-paren/disp32
1616     # . . discard args
1617     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1618     # check-ints-equal(_test-stream->read, 7, msg)
1619     # . . push args
1620     68/push  "F - test-skip-until-close-paren-works-from-mid-stream"/imm32
1621     68/push  7/imm32
1622     b8/copy-to-eax  _test-stream/imm32
1623     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1624     # . . call
1625     e8/call  check-ints-equal/disp32
1626     # . . discard args
1627     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1628     # . epilogue
1629     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1630     5d/pop-to-ebp
1631     c3/return
1632 
1633 skip-until-close-paren-in-slice:  # curr: (addr byte), end: (addr byte) -> curr/eax: (addr byte)
1634     # . prologue
1635     55/push-ebp
1636     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1637     # . save registers
1638     51/push-ecx
1639     52/push-edx
1640     # ecx = curr
1641     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
1642     # edx = end
1643     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         2/r32/edx   0xc/disp8         .               # copy *(ebp+12) to edx
1644     # var c/eax: byte = 0
1645     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
1646     # skip initial dquote
1647     41/increment-ecx
1648 $skip-until-close-paren-in-slice:loop:
1649     # if (curr >= end) break
1650     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
1651     73/jump-if-addr>=  $skip-until-close-paren-in-slice:break/disp8
1652     # c = *curr
1653     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
1654 $skip-until-close-paren-in-slice:check-close:
1655     # if (c == ')') break
1656     3d/compare-eax-and  0x29/imm32/close-paren
1657     74/jump-if-=  $skip-until-close-paren-in-slice:break/disp8
1658     # ++curr
1659     41/increment-ecx
1660     eb/jump  $skip-until-close-paren-in-slice:loop/disp8
1661 $skip-until-close-paren-in-slice:break:
1662     # return curr
1663     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy ecx to eax
1664 $skip-until-close-paren-in-slice:end:
1665     # . restore registers
1666     5a/pop-to-edx
1667     59/pop-to-ecx
1668     # . epilogue
1669     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1670     5d/pop-to-ebp
1671     c3/return
1672 
1673 test-skip-until-close-paren-in-slice:
1674     # . prologue
1675     55/push-ebp
1676     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1677     # setup: (eax..ecx) = "*(abc) def"
1678     b8/copy-to-eax  "*(abc) def"/imm32
1679     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1680     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
1681     05/add-to-eax  4/imm32
1682     # eax = skip-until-close-paren-in-slice(eax, ecx)
1683     # . . push args
1684     51/push-ecx
1685     50/push-eax
1686     # . . call
1687     e8/call  skip-until-close-paren-in-slice/disp32
1688     # . . discard args
1689     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1690     # check-ints-equal(ecx-eax, 5, msg)  # eax is at the ')'
1691     # . . push args
1692     68/push  "F - test-skip-until-close-paren-in-slice"/imm32
1693     68/push  5/imm32
1694     # . . push ecx-eax
1695     29/subtract                     3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract eax from ecx
1696     51/push-ecx
1697     # . . call
1698     e8/call  check-ints-equal/disp32
1699     # . . discard args
1700     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1701     # . epilogue
1702     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1703     5d/pop-to-ebp
1704     c3/return
1705 
1706 test-skip-until-close-paren-in-slice-ignores-spaces:
1707     # . prologue
1708     55/push-ebp
1709     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1710     # setup: (eax..ecx) = "*(a b)/yz"
1711     b8/copy-to-eax  "*(a b)/yz"/imm32
1712     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1713     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
1714     05/add-to-eax  4/imm32
1715     # eax = skip-until-close-paren-in-slice(eax, ecx)
1716     # . . push args
1717     51/push-ecx
1718     50/push-eax
1719     # . . call
1720     e8/call  skip-until-close-paren-in-slice/disp32
1721     # . . discard args
1722     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1723     # check-ints-equal(ecx-eax, 4, msg)  # eax is at the ')'
1724     # . . push args
1725     68/push  "F - test-skip-until-close-paren-in-slice-ignores-spaces"/imm32
1726     68/push  4/imm32
1727     # . . push ecx-eax
1728     29/subtract                     3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract eax from ecx
1729     51/push-ecx
1730     # . . call
1731     e8/call  check-ints-equal/disp32
1732     # . . discard args
1733     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1734     # . epilogue
1735     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1736     5d/pop-to-ebp
1737     c3/return
1738 
1739 test-skip-until-close-paren-in-slice-stops-at-end:
1740     # . prologue
1741     55/push-ebp
1742     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1743     # setup: (eax..ecx) = "*(abc"  # unbalanced dquote
1744     b8/copy-to-eax  "*(abc"/imm32
1745     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1746     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
1747     05/add-to-eax  4/imm32
1748     # eax = skip-until-close-paren-in-slice(eax, ecx)
1749     # . . push args
1750     51/push-ecx
1751     50/push-eax
1752     # . . call
1753     e8/call  skip-until-close-paren-in-slice/disp32
1754     # . . discard args
1755     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1756     # check-ints-equal(ecx-eax, 0, msg)  # skipped to end of slice
1757     # . . push args
1758     68/push  "F - test-skip-until-close-paren-in-slice-stops-at-end"/imm32
1759     68/push  0/imm32
1760     # . . push ecx-eax
1761     29/subtract                     3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract eax from ecx
1762     51/push-ecx
1763     # . . call
1764     e8/call  check-ints-equal/disp32
1765     # . . discard args
1766     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1767     # . epilogue
1768     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1769     5d/pop-to-ebp
1770     c3/return
1771 
1772 # . . vim:nowrap:textwidth=0