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