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