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