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