https://github.com/akkartik/mu/blob/master/073next-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:  # in : (address stream), delimiter : byte, out : (address slice)
  11     # . prolog
  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     56/push-ESI
  18     57/push-EDI
  19     # ESI = in
  20     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
  21     # EDI = out
  22     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   0x10/disp8      .                 # copy *(EBP+16) to EDI
  23     # skip-chars-matching(in, delimiter)
  24     # . . push args
  25     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
  26     56/push-ESI
  27     # . . call
  28     e8/call  skip-chars-matching/disp32
  29     # . . discard args
  30     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
  31     # out->start = &in->data[in->read]
  32     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
  33     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/EAX   0xc/disp8       .                 # copy ESI+ECX+12 to EAX
  34     89/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *EDI
  35     # skip-chars-not-matching(in, delimiter)
  36     # . . push args
  37     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
  38     56/push-ESI
  39     # . . call
  40     e8/call  skip-chars-not-matching/disp32
  41     # . . discard args
  42     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
  43     # out->end = &in->data[in->read]
  44     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
  45     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/EAX   0xc/disp8       .                 # copy ESI+ECX+12 to 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     5e/pop-to-ESI
  50     59/pop-to-ECX
  51     58/pop-to-EAX
  52     # . epilog
  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:
  58     # . prolog
  59     55/push-EBP
  60     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
  61     # setup
  62     # . clear-stream(_test-stream)
  63     # . . push args
  64     68/push  _test-stream/imm32
  65     # . . call
  66     e8/call  clear-stream/disp32
  67     # . . discard args
  68     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
  69     # var slice/ECX = {0, 0}
  70     68/push  0/imm32/end
  71     68/push  0/imm32/start
  72     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
  73     # write(_test-stream, "  ab")
  74     # . . push args
  75     68/push  "  ab"/imm32
  76     68/push  _test-stream/imm32
  77     # . . call
  78     e8/call  write/disp32
  79     # . . discard args
  80     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
  81     # next-token(_test-stream, 0x20/space, slice)
  82     # . . push args
  83     51/push-ECX
  84     68/push  0x20/imm32
  85     68/push  _test-stream/imm32
  86     # . . call
  87     e8/call  next-token/disp32
  88     # . . discard args
  89     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
  90     # check-ints-equal(slice->start - _test-stream->data, 2, msg)
  91     # . check-ints-equal(slice->start - _test-stream, 14, msg)
  92     # . . push args
  93     68/push  "F - test-next-token: start"/imm32
  94     68/push  0xe/imm32
  95     # . . push slice->start - _test-stream
  96     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
  97     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
  98     50/push-EAX
  99     # . . call
 100     e8/call  check-ints-equal/disp32
 101     # . . discard args
 102     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 103     # check-ints-equal(slice->end - _test-stream->data, 4, msg)
 104     # . check-ints-equal(slice->end - _test-stream, 16, msg)
 105     # . . push args
 106     68/push  "F - test-next-token: end"/imm32
 107     68/push  0x10/imm32
 108     # . . push slice->end - _test-stream
 109     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
 110     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
 111     50/push-EAX
 112     # . . call
 113     e8/call  check-ints-equal/disp32
 114     # . . discard args
 115     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 116     # . epilog
 117     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 118     5d/pop-to-EBP
 119     c3/return
 120 
 121 test-next-token-Eof:
 122     # . prolog
 123     55/push-EBP
 124     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 125     # setup
 126     # . clear-stream(_test-stream)
 127     # . . push args
 128     68/push  _test-stream/imm32
 129     # . . call
 130     e8/call  clear-stream/disp32
 131     # . . discard args
 132     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 133     # var slice/ECX = {0, 0}
 134     68/push  0/imm32/end
 135     68/push  0/imm32/start
 136     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
 137     # write nothing to _test-stream
 138     # next-token(_test-stream, 0x20/space, slice)
 139     # . . push args
 140     51/push-ECX
 141     68/push  0x20/imm32
 142     68/push  _test-stream/imm32
 143     # . . call
 144     e8/call  next-token/disp32
 145     # . . discard args
 146     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 147     # check-ints-equal(slice->end, slice->start, msg)
 148     # . . push args
 149     68/push  "F - test-next-token-Eof"/imm32
 150     ff          6/subop/push        1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         .                 # push *(ECX+4)
 151     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
 152     # . . call
 153     e8/call  check-ints-equal/disp32
 154     # . . discard args
 155     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 156     # . epilog
 157     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 158     5d/pop-to-EBP
 159     c3/return
 160 
 161 # extract the next run of characters that are different from a given 'delimiter' (skipping multiple delimiters if necessary)
 162 # on reaching end of file, return an empty interval
 163 next-token-from-slice:  # start : (address byte), end : (address byte), delimiter : byte, out : (address slice) -> <void>
 164     # . prolog
 165     55/push-EBP
 166     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 167     # . save registers
 168     50/push-EAX
 169     51/push-ECX
 170     52/push-EDX
 171     57/push-EDI
 172     # ECX = end
 173     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   0xc/disp8       .                 # copy *(EBP+12) to ECX
 174     # EDX = delimiter
 175     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           2/r32/EDX   0x10/disp8      .                 # copy *(EBP+16) to EDX
 176     # EDI = out
 177     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   0x14/disp8      .                 # copy *(EBP+20) to EDI
 178     # EAX = skip-chars-matching-in-slice(start, end, delimiter)
 179     # . . push args
 180     52/push-EDX
 181     51/push-ECX
 182     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 183     # . . call
 184     e8/call  skip-chars-matching-in-slice/disp32
 185     # . . discard args
 186     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 187     # out->start = EAX
 188     89/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *EDI
 189     # EAX = skip-chars-not-matching-in-slice(EAX, end, delimiter)
 190     # . . push args
 191     52/push-EDX
 192     51/push-ECX
 193     50/push-EAX
 194     # . . call
 195     e8/call  skip-chars-not-matching-in-slice/disp32
 196     # . . discard args
 197     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 198     # out->end = EAX
 199     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
 200     # . restore registers
 201     5f/pop-to-EDI
 202     5a/pop-to-EDX
 203     59/pop-to-ECX
 204     58/pop-to-EAX
 205     # . epilog
 206     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 207     5d/pop-to-EBP
 208     c3/return
 209 
 210 test-next-token-from-slice:
 211     # . prolog
 212     55/push-EBP
 213     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 214     # (EAX..ECX) = "  ab"
 215     b8/copy-to-EAX  "  ab"/imm32
 216     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
 217     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
 218     05/add-to-EAX  4/imm32
 219     # var out/EDI : (address slice) = {0, 0}
 220     68/push  0/imm32/end
 221     68/push  0/imm32/start
 222     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDI
 223     # next-token-from-slice(EAX, ECX, 0x20/space, out)
 224     # . . push args
 225     57/push-EDI
 226     68/push  0x20/imm32
 227     51/push-ECX
 228     50/push-EAX
 229     # . . call
 230     e8/call  next-token-from-slice/disp32
 231     # . . discard args
 232     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
 233     # out->start should be at the 'a'
 234     # . check-ints-equal(out->start - in->start, 2, msg)
 235     # . . push args
 236     68/push  "F - test-next-token-from-slice: start"/imm32
 237     68/push  2/imm32
 238     # . . push out->start - in->start
 239     8b/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           1/r32/ECX   .               .                 # copy *EDI to ECX
 240     2b/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # subtract EAX from ECX
 241     51/push-ECX
 242     # . . call
 243     e8/call  check-ints-equal/disp32
 244     # . . discard args
 245     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 246     # out->end should be after the 'b'
 247     # check-ints-equal(out->end - in->start, 4, msg)
 248     # . . push args
 249     68/push  "F - test-next-token-from-slice: end"/imm32
 250     68/push  4/imm32
 251     # . . push out->end - in->start
 252     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(EDI+4) to ECX
 253     2b/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # subtract EAX from ECX
 254     51/push-ECX
 255     # . . call
 256     e8/call  check-ints-equal/disp32
 257     # . . discard args
 258     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 259     # . epilog
 260     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 261     5d/pop-to-EBP
 262     c3/return
 263 
 264 test-next-token-from-slice-Eof:
 265     # . prolog
 266     55/push-EBP
 267     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 268     # var out/EDI : (address slice) = {0, 0}
 269     68/push  0/imm32/end
 270     68/push  0/imm32/start
 271     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDI
 272     # next-token-from-slice(0, 0, 0x20/space, out)
 273     # . . push args
 274     57/push-EDI
 275     68/push  0x20/imm32
 276     68/push  0/imm32
 277     68/push  0/imm32
 278     # . . call
 279     e8/call  next-token-from-slice/disp32
 280     # . . discard args
 281     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
 282     # out should be empty
 283     # . check-ints-equal(out->end - out->start, 0, msg)
 284     # . . push args
 285     68/push  "F - test-next-token-from-slice-Eof"/imm32
 286     68/push  0/imm32
 287     # . . push out->start - in->start
 288     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(EDI+4) to ECX
 289     2b/subtract                     0/mod/indirect  7/rm32/EDI    .           .             .           1/r32/ECX   .               .                 # subtract *EDI from ECX
 290     51/push-ECX
 291     # . . call
 292     e8/call  check-ints-equal/disp32
 293     # . . discard args
 294     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 295     # . epilog
 296     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 297     5d/pop-to-EBP
 298     c3/return
 299 
 300 test-next-token-from-slice-nothing:
 301     # . prolog
 302     55/push-EBP
 303     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 304     # (EAX..ECX) = "    "
 305     b8/copy-to-EAX  "    "/imm32
 306     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
 307     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
 308     05/add-to-EAX  4/imm32
 309     # var out/EDI : (address slice) = {0, 0}
 310     68/push  0/imm32/end
 311     68/push  0/imm32/start
 312     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDI
 313     # next-token-from-slice(in, 0x20/space, out)
 314     # . . push args
 315     57/push-EDI
 316     68/push  0x20/imm32
 317     51/push-ECX
 318     50/push-EAX
 319     # . . call
 320     e8/call  next-token-from-slice/disp32
 321     # . . discard args
 322     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
 323     # out should be empty
 324     # . check-ints-equal(out->end - out->start, 0, msg)
 325     # . . push args
 326     68/push  "F - test-next-token-from-slice-Eof"/imm32
 327     68/push  0/imm32
 328     # . . push out->start - in->start
 329     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(EDI+4) to ECX
 330     2b/subtract                     0/mod/indirect  7/rm32/EDI    .           .             .           1/r32/ECX   .               .                 # subtract *EDI from ECX
 331     51/push-ECX
 332     # . . call
 333     e8/call  check-ints-equal/disp32
 334     # . . discard args
 335     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 336     # . epilog
 337     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 338     5d/pop-to-EBP
 339     c3/return
 340 
 341 skip-chars-matching:  # in : (address stream), delimiter : byte
 342     # . prolog
 343     55/push-EBP
 344     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 345     # . save registers
 346     50/push-EAX
 347     51/push-ECX
 348     52/push-EDX
 349     53/push-EBX
 350     56/push-ESI
 351     # ESI = in
 352     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
 353     # ECX = in->read
 354     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
 355     # EBX = in->write
 356     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           3/r32/EBX   .               .                 # copy *ESI to EBX
 357     # EDX = delimiter
 358     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           2/r32/EDX   0xc/disp8       .                 # copy *(EBP+12) to EDX
 359 $skip-chars-matching:loop:
 360     # if (in->read >= in->write) break
 361     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           3/r32/EBX   .               .                 # compare ECX with EBX
 362     7d/jump-if-greater-or-equal  $skip-chars-matching:end/disp8
 363     # EAX = in->data[in->read]
 364     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
 365     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
 366     # if (EAX != delimiter) break
 367     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           2/r32/EDX   .               .                 # compare EAX and EDX
 368     75/jump-if-not-equal  $skip-chars-matching:end/disp8
 369     # ++in->read
 370     41/increment-ECX
 371     eb/jump  $skip-chars-matching:loop/disp8
 372 $skip-chars-matching:end:
 373     # persist in->read
 374     89/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy ECX to *(ESI+4)
 375     # . restore registers
 376     5e/pop-to-ESI
 377     5b/pop-to-EBX
 378     5a/pop-to-EDX
 379     59/pop-to-ECX
 380     58/pop-to-EAX
 381     # . epilog
 382     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 383     5d/pop-to-EBP
 384     c3/return
 385 
 386 test-skip-chars-matching:
 387     # setup
 388     # . clear-stream(_test-stream)
 389     # . . push args
 390     68/push  _test-stream/imm32
 391     # . . call
 392     e8/call  clear-stream/disp32
 393     # . . discard args
 394     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 395     # write(_test-stream, "  ab")
 396     # . . push args
 397     68/push  "  ab"/imm32
 398     68/push  _test-stream/imm32
 399     # . . call
 400     e8/call  write/disp32
 401     # . . discard args
 402     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 403     # skip-chars-matching(_test-stream, 0x20/space)
 404     # . . push args
 405     68/push  0x20/imm32
 406     68/push  _test-stream/imm32
 407     # . . call
 408     e8/call  skip-chars-matching/disp32
 409     # . . discard args
 410     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 411     # check-ints-equal(_test-stream->read, 2, msg)
 412     # . . push args
 413     68/push  "F - test-skip-chars-matching"/imm32
 414     68/push  2/imm32
 415     # . . push *_test-stream->read
 416     b8/copy-to-EAX  _test-stream/imm32
 417     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
 418     # . . call
 419     e8/call  check-ints-equal/disp32
 420     # . . discard args
 421     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 422     # end
 423     c3/return
 424 
 425 test-skip-chars-matching-none:
 426     # setup
 427     # . clear-stream(_test-stream)
 428     # . . push args
 429     68/push  _test-stream/imm32
 430     # . . call
 431     e8/call  clear-stream/disp32
 432     # . . discard args
 433     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 434     # write(_test-stream, "ab")
 435     # . . push args
 436     68/push  "ab"/imm32
 437     68/push  _test-stream/imm32
 438     # . . call
 439     e8/call  write/disp32
 440     # . . discard args
 441     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 442     # skip-chars-matching(_test-stream, 0x20/space)
 443     # . . push args
 444     68/push  0x20/imm32
 445     68/push  _test-stream/imm32
 446     # . . call
 447     e8/call  skip-chars-matching/disp32
 448     # . . discard args
 449     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 450     # check-ints-equal(_test-stream->read, 0, msg)
 451     # . . push args
 452     68/push  "F - test-skip-chars-matching-none"/imm32
 453     68/push  0/imm32
 454     # . . push *_test-stream->read
 455     b8/copy-to-EAX  _test-stream/imm32
 456     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
 457     # . . call
 458     e8/call  check-ints-equal/disp32
 459     # . . discard args
 460     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 461     # end
 462     c3/return
 463 
 464 skip-chars-matching-whitespace:  # in : (address stream)
 465     # . prolog
 466     55/push-EBP
 467     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 468     # . save registers
 469     50/push-EAX
 470     51/push-ECX
 471     53/push-EBX
 472     56/push-ESI
 473     # ESI = in
 474     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
 475     # ECX = in->read
 476     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
 477     # EBX = in->write
 478     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           3/r32/EBX   .               .                 # copy *ESI to EBX
 479 $skip-chars-matching-whitespace:loop:
 480     # if (in->read >= in->write) break
 481     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           3/r32/EBX   .               .                 # compare ECX with EBX
 482     7d/jump-if-greater-or-equal  $skip-chars-matching-whitespace:end/disp8
 483     # EAX = in->data[in->read]
 484     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
 485     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
 486     # if (EAX == ' ') goto body
 487     3d/compare-EAX-and  0x20/imm32/space
 488     74/jump-if-equal  $skip-chars-matching-whitespace:body/disp8
 489     # if (EAX == '\n') goto body
 490     3d/compare-EAX-and  0x0a/imm32/newline
 491     74/jump-if-equal  $skip-chars-matching-whitespace:body/disp8
 492     # if (EAX == '\t') goto body
 493     3d/compare-EAX-and  0x09/imm32/tab
 494     74/jump-if-equal  $skip-chars-matching-whitespace:body/disp8
 495     # if (EAX != '\r') break
 496     3d/compare-EAX-and  0x0d/imm32/cr
 497     75/jump-if-not-equal  $skip-chars-matching-whitespace:end/disp8
 498 $skip-chars-matching-whitespace:body:
 499     # ++in->read
 500     41/increment-ECX
 501     eb/jump  $skip-chars-matching-whitespace:loop/disp8
 502 $skip-chars-matching-whitespace:end:
 503     # persist in->read
 504     89/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy ECX to *(ESI+4)
 505     # . restore registers
 506     5e/pop-to-ESI
 507     5b/pop-to-EBX
 508     59/pop-to-ECX
 509     58/pop-to-EAX
 510     # . epilog
 511     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 512     5d/pop-to-EBP
 513     c3/return
 514 
 515 test-skip-chars-matching-whitespace:
 516     # setup
 517     # . clear-stream(_test-stream)
 518     # . . push args
 519     68/push  _test-stream/imm32
 520     # . . call
 521     e8/call  clear-stream/disp32
 522     # . . discard args
 523     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 524     # write(_test-stream, " \nab")
 525     # . . push args
 526     68/push  " \nab"/imm32
 527     68/push  _test-stream/imm32
 528     # . . call
 529     e8/call  write/disp32
 530     # . . discard args
 531     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 532     # skip-chars-matching-whitespace(_test-stream)
 533     # . . push args
 534     68/push  _test-stream/imm32
 535     # . . call
 536     e8/call  skip-chars-matching-whitespace/disp32
 537     # . . discard args
 538     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 539     # check-ints-equal(_test-stream->read, 2, msg)
 540     # . . push args
 541     68/push  "F - test-skip-chars-matching-whitespace"/imm32
 542     68/push  2/imm32
 543     # . . push *_test-stream->read
 544     b8/copy-to-EAX  _test-stream/imm32
 545     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
 546     # . . call
 547     e8/call  check-ints-equal/disp32
 548     # . . discard args
 549     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 550     # end
 551     c3/return
 552 
 553 # minor fork of 'skip-chars-matching'
 554 skip-chars-not-matching:  # in : (address stream), delimiter : byte
 555     # . prolog
 556     55/push-EBP
 557     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 558     # . save registers
 559     50/push-EAX
 560     51/push-ECX
 561     52/push-EDX
 562     53/push-EBX
 563     56/push-ESI
 564     # ESI = in
 565     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
 566     # ECX = in->read
 567     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
 568     # EBX = in->write
 569     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           3/r32/EBX   .               .                 # copy *ESI to EBX
 570     # EDX = delimiter
 571     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           2/r32/EDX   0xc/disp8       .                 # copy *(EBP+12) to EDX
 572 $skip-chars-not-matching:loop:
 573     # if (in->read >= in->write) break
 574     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           3/r32/EBX   .               .                 # compare ECX with EBX
 575     7d/jump-if-greater-or-equal  $skip-chars-not-matching:end/disp8
 576     # EAX = in->data[in->read]
 577     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
 578     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
 579     # if (EAX == delimiter) break
 580     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           2/r32/EDX   .               .                 # compare EAX and EDX
 581     74/jump-if-equal  $skip-chars-not-matching:end/disp8
 582     # ++in->read
 583     41/increment-ECX
 584     eb/jump  $skip-chars-not-matching:loop/disp8
 585 $skip-chars-not-matching:end:
 586     # persist in->read
 587     89/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy ECX to *(ESI+4)
 588     # . restore registers
 589     5e/pop-to-ESI
 590     5b/pop-to-EBX
 591     5a/pop-to-EDX
 592     59/pop-to-ECX
 593     58/pop-to-EAX
 594     # . epilog
 595     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 596     5d/pop-to-EBP
 597     c3/return
 598 
 599 test-skip-chars-not-matching:
 600     # setup
 601     # . clear-stream(_test-stream)
 602     # . . push args
 603     68/push  _test-stream/imm32
 604     # . . call
 605     e8/call  clear-stream/disp32
 606     # . . discard args
 607     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 608     # write(_test-stream, "ab ")
 609     # . . push args
 610     68/push  "ab "/imm32
 611     68/push  _test-stream/imm32
 612     # . . call
 613     e8/call  write/disp32
 614     # . . discard args
 615     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 616     # skip-chars-not-matching(_test-stream, 0x20/space)
 617     # . . push args
 618     68/push  0x20/imm32
 619     68/push  _test-stream/imm32
 620     # . . call
 621     e8/call  skip-chars-not-matching/disp32
 622     # . . discard args
 623     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 624     # check-ints-equal(_test-stream->read, 2, msg)
 625     # . . push args
 626     68/push  "F - test-skip-chars-not-matching"/imm32
 627     68/push  2/imm32
 628     # . . push *_test-stream->read
 629     b8/copy-to-EAX  _test-stream/imm32
 630     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
 631     # . . call
 632     e8/call  check-ints-equal/disp32
 633     # . . discard args
 634     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 635     # end
 636     c3/return
 637 
 638 test-skip-chars-not-matching-none:
 639     # setup
 640     # . clear-stream(_test-stream)
 641     # . . push args
 642     68/push  _test-stream/imm32
 643     # . . call
 644     e8/call  clear-stream/disp32
 645     # . . discard args
 646     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 647     # write(_test-stream, " ab")
 648     # . . push args
 649     68/push  " ab"/imm32
 650     68/push  _test-stream/imm32
 651     # . . call
 652     e8/call  write/disp32
 653     # . . discard args
 654     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 655     # skip-chars-not-matching(_test-stream, 0x20/space)
 656     # . . push args
 657     68/push  0x20/imm32
 658     68/push  _test-stream/imm32
 659     # . . call
 660     e8/call  skip-chars-not-matching/disp32
 661     # . . discard args
 662     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 663     # check-ints-equal(_test-stream->read, 0, msg)
 664     # . . push args
 665     68/push  "F - test-skip-chars-not-matching-none"/imm32
 666     68/push  0/imm32
 667     # . . push *_test-stream->read
 668     b8/copy-to-EAX  _test-stream/imm32
 669     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
 670     # . . call
 671     e8/call  check-ints-equal/disp32
 672     # . . discard args
 673     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 674     # end
 675     c3/return
 676 
 677 test-skip-chars-not-matching-all:
 678     # setup
 679     # . clear-stream(_test-stream)
 680     # . . push args
 681     68/push  _test-stream/imm32
 682     # . . call
 683     e8/call  clear-stream/disp32
 684     # . . discard args
 685     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 686     # write(_test-stream, "ab")
 687     # . . push args
 688     68/push  "ab"/imm32
 689     68/push  _test-stream/imm32
 690     # . . call
 691     e8/call  write/disp32
 692     # . . discard args
 693     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 694     # skip-chars-not-matching(_test-stream, 0x20/space)
 695     # . . push args
 696     68/push  0x20/imm32
 697     68/push  _test-stream/imm32
 698     # . . call
 699     e8/call  skip-chars-not-matching/disp32
 700     # . . discard args
 701     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 702     # check-ints-equal(_test-stream->read, 2, msg)
 703     # . . push args
 704     68/push  "F - test-skip-chars-not-matching-all"/imm32
 705     68/push  2/imm32
 706     # . . push *_test-stream->read
 707     b8/copy-to-EAX  _test-stream/imm32
 708     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
 709     # . . call
 710     e8/call  check-ints-equal/disp32
 711     # . . discard args
 712     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 713     # end
 714     c3/return
 715 
 716 skip-chars-not-matching-whitespace:  # in : (address stream)
 717     # . prolog
 718     55/push-EBP
 719     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 720     # . save registers
 721     50/push-EAX
 722     51/push-ECX
 723     53/push-EBX
 724     56/push-ESI
 725     # ESI = in
 726     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
 727     # ECX = in->read
 728     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
 729     # EBX = in->write
 730     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           3/r32/EBX   .               .                 # copy *ESI to EBX
 731 $skip-chars-not-matching-whitespace:loop:
 732     # if (in->read >= in->write) break
 733     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           3/r32/EBX   .               .                 # compare ECX with EBX
 734     7d/jump-if-greater-or-equal  $skip-chars-not-matching-whitespace:end/disp8
 735     # EAX = in->data[in->read]
 736     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
 737     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
 738     # if (EAX == ' ') break
 739     3d/compare-EAX-and  0x20/imm32/space
 740     74/jump-if-equal  $skip-chars-not-matching-whitespace:end/disp8
 741     # if (EAX == '\n') break
 742     3d/compare-EAX-and  0x0a/imm32/newline
 743     74/jump-if-equal  $skip-chars-not-matching-whitespace:end/disp8
 744     # if (EAX == '\t') break
 745     3d/compare-EAX-and  0x09/imm32/tab
 746     74/jump-if-equal  $skip-chars-not-matching-whitespace:end/disp8
 747     # if (EAX == '\r') break
 748     3d/compare-EAX-and  0x0d/imm32/cr
 749     74/jump-if-equal  $skip-chars-not-matching-whitespace:end/disp8
 750     # ++in->read
 751     41/increment-ECX
 752     eb/jump  $skip-chars-not-matching-whitespace:loop/disp8
 753 $skip-chars-not-matching-whitespace:end:
 754     # persist in->read
 755     89/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy ECX to *(ESI+4)
 756     # . restore registers
 757     5e/pop-to-ESI
 758     5b/pop-to-EBX
 759     59/pop-to-ECX
 760     58/pop-to-EAX
 761     # . epilog
 762     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 763     5d/pop-to-EBP
 764     c3/return
 765 
 766 test-skip-chars-not-matching-whitespace:
 767     # setup
 768     # . clear-stream(_test-stream)
 769     # . . push args
 770     68/push  _test-stream/imm32
 771     # . . call
 772     e8/call  clear-stream/disp32
 773     # . . discard args
 774     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 775     # write(_test-stream, "ab\n")
 776     # . . push args
 777     68/push  "ab\n"/imm32
 778     68/push  _test-stream/imm32
 779     # . . call
 780     e8/call  write/disp32
 781     # . . discard args
 782     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 783     # skip-chars-not-matching-whitespace(_test-stream)
 784     # . . push args
 785     68/push  _test-stream/imm32
 786     # . . call
 787     e8/call  skip-chars-not-matching-whitespace/disp32
 788     # . . discard args
 789     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 790     # check-ints-equal(_test-stream->read, 2, msg)
 791     # . . push args
 792     68/push  "F - test-skip-chars-not-matching-whitespace"/imm32
 793     68/push  2/imm32
 794     # . . push *_test-stream->read
 795     b8/copy-to-EAX  _test-stream/imm32
 796     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
 797     # . . call
 798     e8/call  check-ints-equal/disp32
 799     # . . discard args
 800     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 801     # end
 802     c3/return
 803 
 804 skip-chars-matching-in-slice:  # curr : (address byte), end : (address byte), delimiter : byte -> curr/EAX
 805     # . prolog
 806     55/push-EBP
 807     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 808     # . save registers
 809     51/push-ECX
 810     52/push-EDX
 811     53/push-EBX
 812     # EAX = curr
 813     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   8/disp8         .                 # copy *(EBP+8) to EAX
 814     # ECX = end
 815     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   0xc/disp8       .                 # copy *(EBP+12) to ECX
 816     # EDX = delimiter
 817     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           2/r32/EDX   0x10/disp8       .                 # copy *(EBP+16) to EDX
 818     # EBX = 0
 819     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
 820 $skip-chars-matching-in-slice:loop:
 821     # if (curr >= end) break
 822     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # compare EAX with ECX
 823     73/jump-if-greater-or-equal-unsigned  $skip-chars-matching-in-slice:end/disp8
 824     # if (*curr != delimiter) break
 825     8a/copy-byte                    0/mod/indirect  0/rm32/EAX    .           .             .           3/r32/BL    .               .                 # copy byte at *EAX to BL
 826     39/compare                      3/mod/direct    3/rm32/EBX    .           .             .           2/r32/EDX   .               .                 # compare EBX and EDX
 827     75/jump-if-not-equal  $skip-chars-matching-in-slice:end/disp8
 828     # ++curr
 829     40/increment-EAX
 830     eb/jump  $skip-chars-matching-in-slice:loop/disp8
 831 $skip-chars-matching-in-slice:end:
 832     # . restore registers
 833     5b/pop-to-EBX
 834     5a/pop-to-EDX
 835     59/pop-to-ECX
 836     # . epilog
 837     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 838     5d/pop-to-EBP
 839     c3/return
 840 
 841 test-skip-chars-matching-in-slice:
 842     # (EAX..ECX) = "  ab"
 843     b8/copy-to-EAX  "  ab"/imm32
 844     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
 845     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
 846     05/add-to-EAX  4/imm32
 847     # EAX = skip-chars-matching-in-slice(EAX, ECX, 0x20/space)
 848     # . . push args
 849     68/push  0x20/imm32/space
 850     51/push-ECX
 851     50/push-EAX
 852     # . . call
 853     e8/call  skip-chars-matching-in-slice/disp32
 854     # . . discard args
 855     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 856     # check-ints-equal(ECX-EAX, 2, msg)
 857     # . . push args
 858     68/push  "F - test-skip-chars-matching-in-slice"/imm32
 859     68/push  2/imm32
 860     # . . push ECX-EAX
 861     29/subtract                     3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract EAX from ECX
 862     51/push-ECX
 863     # . . call
 864     e8/call  check-ints-equal/disp32
 865     # . . discard args
 866     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 867     # end
 868     c3/return
 869 
 870 test-skip-chars-matching-in-slice-none:
 871     # (EAX..ECX) = "ab"
 872     b8/copy-to-EAX  "ab"/imm32
 873     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
 874     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
 875     05/add-to-EAX  4/imm32
 876     # EAX = skip-chars-matching-in-slice(EAX, ECX, 0x20/space)
 877     # . . push args
 878     68/push  0x20/imm32/space
 879     51/push-ECX
 880     50/push-EAX
 881     # . . call
 882     e8/call  skip-chars-matching-in-slice/disp32
 883     # . . discard args
 884     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 885     # check-ints-equal(ECX-EAX, 2, msg)
 886     # . . push args
 887     68/push  "F - test-skip-chars-matching-in-slice-none"/imm32
 888     68/push  2/imm32
 889     # . . push ECX-EAX
 890     29/subtract                     3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract EAX from ECX
 891     51/push-ECX
 892     # . . call
 893     e8/call  check-ints-equal/disp32
 894     # . . discard args
 895     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 896     # end
 897     c3/return
 898 
 899 skip-chars-matching-whitespace-in-slice:  # in : (address stream)
 900     # . prolog
 901     55/push-EBP
 902     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 903     # . save registers
 904     51/push-ECX
 905     53/push-EBX
 906     # EAX = curr
 907     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   8/disp8         .                 # copy *(EBP+8) to EAX
 908     # ECX = end
 909     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   0xc/disp8       .                 # copy *(EBP+12) to ECX
 910     # EBX = 0
 911     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
 912 $skip-chars-matching-whitespace-in-slice:loop:
 913     # if (curr >= end) break
 914     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # compare EAX with ECX
 915     0f 83/jump-if-greater-or-equal-unsigned  $skip-chars-matching-in-slice:end/disp32
 916     # EBX = *curr
 917     8a/copy-byte                    0/mod/indirect  0/rm32/EAX    .           .             .           3/r32/BL    .               .                 # copy byte at *EAX to BL
 918     # if (*curr == ' ') goto body
 919     81          7/subop/compare     3/mod/direct    3/rm32/EBX    .           .             .           .           .               0x20/imm32/space  # compare EBX
 920     74/jump-if-equal  $skip-chars-matching-whitespace-in-slice:body/disp8
 921     # if (*curr == '\n') goto body
 922     81          7/subop/compare     3/mod/direct    3/rm32/EBX    .           .             .           .           .               0x0a/imm32/newline  # compare EBX
 923     74/jump-if-equal  $skip-chars-matching-whitespace-in-slice:body/disp8
 924     # if (*curr == '\t') goto body
 925     81          7/subop/compare     3/mod/direct    3/rm32/EBX    .           .             .           .           .               0x09/imm32/tab    # compare EBX
 926     74/jump-if-equal  $skip-chars-matching-whitespace-in-slice:body/disp8
 927     # if (*curr != '\r') break
 928     81          7/subop/compare     3/mod/direct    3/rm32/EBX    .           .             .           .           .               0x0d/imm32/cr     # compare EBX
 929     75/jump-if-not-equal  $skip-chars-matching-whitespace-in-slice:end/disp8
 930 $skip-chars-matching-whitespace-in-slice:body:
 931     # ++curr
 932     40/increment-EAX
 933     eb/jump  $skip-chars-matching-whitespace-in-slice:loop/disp8
 934 $skip-chars-matching-whitespace-in-slice:end:
 935     # . restore registers
 936     5b/pop-to-EBX
 937     59/pop-to-ECX
 938     # . epilog
 939     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 940     5d/pop-to-EBP
 941     c3/return
 942 
 943 test-skip-chars-matching-whitespace-in-slice:
 944     # (EAX..ECX) = " \nab"
 945     b8/copy-to-EAX  " \nab"/imm32
 946     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
 947     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
 948     05/add-to-EAX  4/imm32
 949     # EAX = skip-chars-matching-whitespace-in-slice(EAX, ECX)
 950     # . . push args
 951     51/push-ECX
 952     50/push-EAX
 953     # . . call
 954     e8/call  skip-chars-matching-whitespace-in-slice/disp32
 955     # . . discard args
 956     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 957     # check-ints-equal(ECX-EAX, 2, msg)
 958     # . . push args
 959     68/push  "F - test-skip-chars-matching-whitespace-in-slice"/imm32
 960     68/push  2/imm32
 961     # . . push ECX-EAX
 962     29/subtract                     3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract EAX from ECX
 963     51/push-ECX
 964     # . . call
 965     e8/call  check-ints-equal/disp32
 966     # . . discard args
 967     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 968     # end
 969     c3/return
 970 
 971 # minor fork of 'skip-chars-matching-in-slice'
 972 skip-chars-not-matching-in-slice:  # curr : (address byte), end : (address byte), delimiter : byte -> curr/EAX
 973     # . prolog
 974     55/push-EBP
 975     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 976     # . save registers
 977     51/push-ECX
 978     52/push-EDX
 979     53/push-EBX
 980     # EAX = curr
 981     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   8/disp8         .                 # copy *(EBP+8) to EAX
 982     # ECX = end
 983     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   0xc/disp8       .                 # copy *(EBP+12) to ECX
 984     # EDX = delimiter
 985     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           2/r32/EDX   0x10/disp8       .                 # copy *(EBP+16) to EDX
 986     # EBX = 0
 987     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
 988 $skip-chars-not-matching-in-slice:loop:
 989     # if (curr >= end) break
 990     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # compare EAX with ECX
 991     73/jump-if-greater-or-equal-unsigned  $skip-chars-not-matching-in-slice:end/disp8
 992     # if (*curr == delimiter) break
 993     8a/copy-byte                    0/mod/indirect  0/rm32/EAX    .           .             .           3/r32/BL    .               .                 # copy byte at *EAX to BL
 994     39/compare                      3/mod/direct    3/rm32/EBX    .           .             .           2/r32/EDX   .               .                 # compare EBX and EDX
 995     74/jump-if-equal  $skip-chars-not-matching-in-slice:end/disp8
 996     # ++curr
 997     40/increment-EAX
 998     eb/jump  $skip-chars-not-matching-in-slice:loop/disp8
 999 $skip-chars-not-matching-in-slice:end:
1000     # . restore registers
1001     5b/pop-to-EBX
1002     5a/pop-to-EDX
1003     59/pop-to-ECX
1004     # . epilog
1005     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1006     5d/pop-to-EBP
1007     c3/return
1008 
1009 test-skip-chars-not-matching-in-slice:
1010     # (EAX..ECX) = "ab "
1011     b8/copy-to-EAX  "ab "/imm32
1012     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
1013     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
1014     05/add-to-EAX  4/imm32
1015     # EAX = skip-chars-not-matching-in-slice(EAX, ECX, 0x20/space)
1016     # . . push args
1017     68/push  0x20/imm32/space
1018     51/push-ECX
1019     50/push-EAX
1020     # . . call
1021     e8/call  skip-chars-not-matching-in-slice/disp32
1022     # . . discard args
1023     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1024     # check-ints-equal(ECX-EAX, 1, msg)
1025     # . . push args
1026     68/push  "F - test-skip-chars-not-matching-in-slice"/imm32
1027     68/push  1/imm32
1028     # . . push ECX-EAX
1029     29/subtract                     3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract EAX from ECX
1030     51/push-ECX
1031     # . . call
1032     e8/call  check-ints-equal/disp32
1033     # . . discard args
1034     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1035     # end
1036     c3/return
1037 
1038 test-skip-chars-not-matching-in-slice-none:
1039     # (EAX..ECX) = " ab"
1040     b8/copy-to-EAX  " ab"/imm32
1041     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
1042     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
1043     05/add-to-EAX  4/imm32
1044     # EAX = skip-chars-not-matching-in-slice(EAX, ECX, 0x20/space)
1045     # . . push args
1046     68/push  0x20/imm32/space
1047     51/push-ECX
1048     50/push-EAX
1049     # . . call
1050     e8/call  skip-chars-not-matching-in-slice/disp32
1051     # . . discard args
1052     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1053     # check-ints-equal(ECX-EAX, 3, msg)
1054     # . . push args
1055     68/push  "F - test-skip-chars-not-matching-in-slice-none"/imm32
1056     68/push  3/imm32
1057     # . . push ECX-EAX
1058     29/subtract                     3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract EAX from ECX
1059     51/push-ECX
1060     # . . call
1061     e8/call  check-ints-equal/disp32
1062     # . . discard args
1063     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1064     # end
1065     c3/return
1066 
1067 test-skip-chars-not-matching-in-slice-all:
1068     # (EAX..ECX) = "ab"
1069     b8/copy-to-EAX  "ab"/imm32
1070     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
1071     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
1072     05/add-to-EAX  4/imm32
1073     # EAX = skip-chars-not-matching-in-slice(EAX, ECX, 0x20/space)
1074     # . . push args
1075     68/push  0x20/imm32/space
1076     51/push-ECX
1077     50/push-EAX
1078     # . . call
1079     e8/call  skip-chars-not-matching-in-slice/disp32
1080     # . . discard args
1081     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1082     # check-ints-equal(ECX-EAX, 0, msg)
1083     # . . push args
1084     68/push  "F - test-skip-chars-not-matching-in-slice-all"/imm32
1085     68/push  0/imm32
1086     # . . push ECX-EAX
1087     29/subtract                     3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract EAX from ECX
1088     51/push-ECX
1089     # . . call
1090     e8/call  check-ints-equal/disp32
1091     # . . discard args
1092     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1093     # end
1094     c3/return
1095 
1096 skip-chars-not-matching-whitespace-in-slice:  # in : (address stream)
1097     # . prolog
1098     55/push-EBP
1099     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1100     # . save registers
1101     51/push-ECX
1102     53/push-EBX
1103     # EAX = curr
1104     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   8/disp8         .                 # copy *(EBP+8) to EAX
1105     # ECX = end
1106     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   0xc/disp8       .                 # copy *(EBP+12) to ECX
1107     # EBX = 0
1108     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
1109 $skip-chars-not-matching-whitespace-in-slice:loop:
1110     # if (curr >= end) break
1111     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # compare EAX with ECX
1112     0f 83/jump-if-greater-or-equal-unsigned  $skip-chars-not-matching-in-slice:end/disp32
1113     # EBX = *curr
1114     8a/copy-byte                    0/mod/indirect  0/rm32/EAX    .           .             .           3/r32/BL    .               .                 # copy byte at *EAX to BL
1115     # if (*curr == ' ') break
1116     81          7/subop/compare     3/mod/direct    3/rm32/EBX    .           .             .           .           .               0x20/imm32/space  # compare EBX
1117     74/jump-if-equal  $skip-chars-not-matching-whitespace-in-slice:end/disp8
1118     # if (*curr == '\n') break
1119     81          7/subop/compare     3/mod/direct    3/rm32/EBX    .           .             .           .           .               0x0a/imm32/newline  # compare EBX
1120     74/jump-if-equal  $skip-chars-not-matching-whitespace-in-slice:end/disp8
1121     # if (*curr == '\t') break
1122     81          7/subop/compare     3/mod/direct    3/rm32/EBX    .           .             .           .           .               0x09/imm32/tab    # compare EBX
1123     74/jump-if-equal  $skip-chars-not-matching-whitespace-in-slice:end/disp8
1124     # if (*curr == '\r') break
1125     81          7/subop/compare     3/mod/direct    3/rm32/EBX    .           .             .           .           .               0x0d/imm32/cr     # compare EBX
1126     74/jump-if-equal  $skip-chars-not-matching-whitespace-in-slice:end/disp8
1127     # ++curr
1128     40/increment-EAX
1129     eb/jump  $skip-chars-not-matching-whitespace-in-slice:loop/disp8
1130 $skip-chars-not-matching-whitespace-in-slice:end:
1131     # . restore registers
1132     5b/pop-to-EBX
1133     59/pop-to-ECX
1134     # . epilog
1135     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1136     5d/pop-to-EBP
1137     c3/return
1138 
1139 test-skip-chars-not-matching-whitespace-in-slice:
1140     # (EAX..ECX) = "ab\n"
1141     b8/copy-to-EAX  "ab\n"/imm32
1142     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
1143     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
1144     05/add-to-EAX  4/imm32
1145     # EAX = skip-chars-not-matching-whitespace-in-slice(EAX, ECX)
1146     # . . push args
1147     51/push-ECX
1148     50/push-EAX
1149     # . . call
1150     e8/call  skip-chars-not-matching-whitespace-in-slice/disp32
1151     # . . discard args
1152     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1153     # check-ints-equal(ECX-EAX, 1, msg)
1154     # . . push args
1155     68/push  "F - test-skip-chars-not-matching-whitespace-in-slice"/imm32
1156     68/push  1/imm32
1157     # . . push ECX-EAX
1158     29/subtract                     3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract EAX from ECX
1159     51/push-ECX
1160     # . . call
1161     e8/call  check-ints-equal/disp32
1162     # . . discard args
1163     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1164     # end
1165     c3/return
1166 
1167 # . . vim:nowrap:textwidth=0