https://github.com/akkartik/mu/blob/main/linux/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         .                 # copy *(ebx+4) to eax
    1168     # skip payload->allocid
    1169     05/add-to-eax  4/imm32
    1170 +-- 26 lines: #?     # dump eax --------------------------------------------------------------------------------------------------------------------------------------------
    1196     # eax = string-equal?(eax, "Abc")
    1197     # . . push args
    1198     68/push  "Abc"/imm32
    1199     50/push-eax
    1200     # . . call
    1201     e8/call  string-equal?/disp32
    1202     # . . discard args
    1203     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1204     # check-ints-equal(eax, 1, msg)
    1205     # . . push args
    1206     68/push  "F - test-slice-to-string"/imm32
    1207     68/push  1/imm32/true
    1208     50/push-eax
    1209     # . . call
    1210     e8/call  check-ints-equal/disp32
    1211     # . . discard args
    1212     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1213     # . reclaim locals
    1214     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x18/imm32        # add to esp
    1215     # . epilogue
    1216     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1217     5d/pop-to-ebp
    1218     c3/return
    1219
    1220 # . . vim:nowrap:textwidth=0