https://github.com/akkartik/mu/blob/main/linux/131table.subx

       1 # A table is a stream of (key, value) rows.
       2 #
       3 # Each row consists of an 8-byte key -- a (handle array byte) -- and a variable-size value.
       4 #
       5 # Accessing the table performs a linear scan for a key string, and always
       6 # requires passing in the row size.
       7 #
       8 # Table primitives have the form <variant>(stream, <arg>, row-size, ...) -> address/eax
       9 #
      10 # The following table shows available options for <variant>:
      11 #   if not found:           | arg=string              arg=slice
      12 #   ------------------------+---------------------------------------------------
      13 #   abort                   | get                     get-slice
      14 #   insert key              | get-or-insert           get-or-insert-slice
      15 #                           | get-or-insert-handle
      16 #   stop                    | get-or-stop             get-slice-or-stop
      17 #   return null             | maybe-get               maybe-get-slice
      18 # Some variants may take extra args.
      19
      20 == code
      21 #   instruction                     effective address                                                   register    displacement    immediate
      22 # . op          subop               mod             rm32          base        index         scale       r32
      23 # . 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
      24
      25 # if no row is found, abort
      26 get:  # table: (addr stream {(handle array byte), T}), key: (addr array byte), row-size: int, abort-message-prefix: (addr array byte) -> result/eax: (addr T)
      27     # pseudocode:
      28     #   curr = table->data
      29     #   max = &table->data[table->write]
      30     #   while curr < max
      31     #     var c: (addr array byte) = lookup(*curr)
      32     #     if string-equal?(key, c)
      33     #       return curr+8
      34     #     curr += row-size
      35     #   abort
      36     #
      37     # . prologue
      38     55/push-ebp
      39     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
      40     # . save registers
      41     51/push-ecx
      42     52/push-edx
      43     56/push-esi
      44     # esi = table
      45     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
      46     # var curr/ecx: (addr handle array byte) = table->data
      47     8d/copy-address                 1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   0xc/disp8       .                 # copy esi+12 to ecx
      48     # var max/edx: (addr byte) = &table->data[table->write]
      49     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           2/r32/edx   .               .                 # copy *esi to edx
      50     8d/copy-address                 0/mod/indirect  4/rm32/sib    1/base/ecx  2/index/edx   .           2/r32/edx   .               .                 # copy ecx+edx to edx
      51 $get:search-loop:
      52     # if (curr >= max) abort
      53     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
      54     73/jump-if-addr>=  $get:abort/disp8
      55     # var c/eax: (addr array byte) = lookup(*curr)
      56     # . . push args
      57     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         .                 # push *(ecx+4)
      58     ff          6/subop/push        0/mod/indirect  1/rm32/ecx    .           .             .           .           .               .                 # push *ecx
      59     # . . call
      60     e8/call  lookup/disp32
      61     # . . discard args
      62     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
      63     # if (string-equal?(key, c)) return curr+8
      64     # . eax = string-equal?(key, c)
      65     # . . push args
      66     50/push-eax
      67     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
      68     # . . call
      69     e8/call  string-equal?/disp32
      70     # . . discard args
      71     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
      72     # . if (eax != false) return eax = curr+8
      73     3d/compare-eax-and  0/imm32/false
      74     74/jump-if-=  $get:mismatch/disp8
      75     8d/copy-address                 1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   8/disp8         .                 # copy ecx+8 to eax
      76     eb/jump  $get:end/disp8
      77 $get:mismatch:
      78     # curr += row-size
      79     03/add                          1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   0x10/disp8      .                 # add *(ebp+16) to ecx
      80     # loop
      81     eb/jump  $get:search-loop/disp8
      82 $get:end:
      83     # . restore registers
      84     5e/pop-to-esi
      85     5a/pop-to-edx
      86     59/pop-to-ecx
      87     # . epilogue
      88     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
      89     5d/pop-to-ebp
      90     c3/return
      91
      92 $get:abort:
      93     # . _write(2/stderr, abort-message-prefix)
      94     # . . push args
      95     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x14/disp8      .                 # push *(ebp+20)
      96     68/push  2/imm32/stderr
      97     # . . call
      98     e8/call  _write/disp32
      99     # . . discard args
     100     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     101     # . _write(2/stderr, error)
     102     # . . push args
     103     68/push  ": get: key not found: "/imm32
     104     68/push  2/imm32/stderr
     105     # . . call
     106     e8/call  _write/disp32
     107     # . . discard args
     108     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     109     # . _write(2/stderr, key)
     110     # . . push args
     111     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     112     68/push  2/imm32/stderr
     113     # . . call
     114     e8/call  _write/disp32
     115     # . . discard args
     116     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     117     # . _write(2/stderr, "\n")
     118     # . . push args
     119     68/push  Newline/imm32
     120     68/push  2/imm32/stderr
     121     # . . call
     122     e8/call  _write/disp32
     123     # . . discard args
     124     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     125     # . syscall(exit, 1)
     126     bb/copy-to-ebx  1/imm32
     127     e8/call  syscall_exit/disp32
     128     # never gets here
     129
     130 test-get:
     131     # . prologue
     132     55/push-ebp
     133     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     134     # - setup: create a table with a couple of keys
     135     # var table/ecx: (stream {(handle array byte), number} 24)  # 2 rows * 12 bytes/row
     136     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x18/imm32        # subtract from esp
     137     68/push  0x18/imm32/size
     138     68/push  0/imm32/read
     139     68/push  0/imm32/write
     140     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
     141     # insert(table, "code", 12 bytes/row, Heap)
     142     # . . push args
     143     68/push  Heap/imm32
     144     68/push  0xc/imm32/row-size
     145     68/push  "code"/imm32
     146     51/push-ecx
     147     # . . call
     148     e8/call  get-or-insert/disp32
     149     # . . discard args
     150     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
     151     # insert(table, "data", 12 bytes/row, Heap)
     152     # . . push args
     153     68/push  Heap/imm32
     154     68/push  0xc/imm32/row-size
     155     68/push  "data"/imm32
     156     51/push-ecx
     157     # . . call
     158     e8/call  get-or-insert/disp32
     159     # . . discard args
     160     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
     161 $test-get:check1:
     162     # eax = get(table, "code", 12 bytes/row)
     163     # . . push args
     164     68/push  0xc/imm32/row-size
     165     68/push  "code"/imm32
     166     51/push-ecx
     167     # . . call
     168     e8/call  get/disp32
     169     # . . discard args
     170     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     171     # check-ints-equal(eax - table->data, 8, msg)
     172     # . check-ints-equal(eax - table, 20, msg)
     173     # . . push args
     174     68/push  "F - test-get/0"/imm32
     175     68/push  0x14/imm32
     176     29/subtract                     3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # subtract ecx from eax
     177     50/push-eax
     178     # . . call
     179     e8/call  check-ints-equal/disp32
     180     # . . discard args
     181     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     182 $test-get:check2:
     183     # eax = get(table, "data", 12 bytes/row)
     184     # . . push args
     185     68/push  0xc/imm32/row-size
     186     68/push  "data"/imm32
     187     51/push-ecx
     188     # . . call
     189     e8/call  get/disp32
     190     # . . discard args
     191     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     192     # check-ints-equal(eax - table->data, 20, msg)
     193     # . check-ints-equal(eax - table, 32, msg)
     194     # . . push args
     195     68/push  "F - test-get/1"/imm32
     196     68/push  0x20/imm32
     197     29/subtract                     3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # subtract ecx from eax
     198     50/push-eax
     199     # . . call
     200     e8/call  check-ints-equal/disp32
     201     # . . discard args
     202     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     203 $test-get:end:
     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 # if no row is found, abort
     210 get-slice:  # table: (addr stream {(handle array byte), T}), key: (addr slice), row-size: int, abort-message-prefix: (addr array byte) -> result/eax: (addr T)
     211     # pseudocode:
     212     #   curr = table->data
     213     #   max = &table->data[table->write]
     214     #   while curr < max
     215     #     var c: (addr array byte) = lookup(*curr)
     216     #     if slice-equal?(key, c)
     217     #       return curr+8
     218     #     curr += row-size
     219     #   abort
     220     #
     221     # . prologue
     222     55/push-ebp
     223     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     224     # . save registers
     225     51/push-ecx
     226     52/push-edx
     227     56/push-esi
     228     # esi = table
     229     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
     230     # var curr/ecx: (addr handle array byte) = table->data
     231     8d/copy-address                 1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   0xc/disp8       .                 # copy esi+12 to ecx
     232     # var max/edx: (addr byte) = &table->data[table->write]
     233     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           2/r32/edx   .               .                 # copy *esi to edx
     234     8d/copy-address                 0/mod/indirect  4/rm32/sib    1/base/ecx  2/index/edx   .           2/r32/edx   .               .                 # copy ecx+edx to edx
     235 $get-slice:search-loop:
     236     # if (curr >= max) abort
     237     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
     238     73/jump-if-addr>=  $get-slice:abort/disp8
     239     # var c/eax: (addr array byte) = lookup(*curr)
     240     # . . push args
     241     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         .                 # push *(ecx+4)
     242     ff          6/subop/push        0/mod/indirect  1/rm32/ecx    .           .             .           .           .               .                 # push *ecx
     243     # . . call
     244     e8/call  lookup/disp32
     245     # . . discard args
     246     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     247     # if (slice-equal?(key, c)) return curr+8
     248     # . eax = slice-equal?(key, c)
     249     # . . push args
     250     50/push-eax
     251     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     252     # . . call
     253     e8/call  slice-equal?/disp32
     254     # . . discard args
     255     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     256     # . if (eax != false) return eax = curr+8
     257     3d/compare-eax-and  0/imm32/false
     258     74/jump-if-=  $get-slice:mismatch/disp8
     259     8d/copy-address                 1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   8/disp8         .                 # copy ecx+8 to eax
     260     eb/jump  $get-slice:end/disp8
     261 $get-slice:mismatch:
     262     # curr += row-size
     263     03/add                          1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   0x10/disp8      .                 # add *(ebp+16) to ecx
     264     # loop
     265     eb/jump  $get-slice:search-loop/disp8
     266 $get-slice:end:
     267     # . restore registers
     268     5e/pop-to-esi
     269     5a/pop-to-edx
     270     59/pop-to-ecx
     271     # . epilogue
     272     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
     273     5d/pop-to-ebp
     274     c3/return
     275
     276 $get-slice:abort:
     277     # . _write(2/stderr, abort-message-prefix)
     278     # . . push args
     279     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x14/disp8      .                 # push *(ebp+20)
     280     68/push  2/imm32/stderr
     281     # . . call
     282     e8/call  _write/disp32
     283     # . . discard args
     284     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     285     # . _write(2/stderr, error)
     286     # . . push args
     287     68/push  ": get-slice: key not found: "/imm32
     288     68/push  2/imm32/stderr
     289     # . . call
     290     e8/call  _write/disp32
     291     # . . discard args
     292     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     293     # . write-slice-buffered(Stderr, key)
     294     # . . push args
     295     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     296     68/push  Stderr/imm32
     297     # . . call
     298     e8/call  write-slice-buffered/disp32
     299     # . . discard args
     300     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     301     # . flush(Stderr)
     302     # . . push args
     303     68/push  Stderr/imm32
     304     # . . call
     305     e8/call  flush/disp32
     306     # . . discard args
     307     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     308     # . _write(2/stderr, "\n")
     309     # . . push args
     310     68/push  Newline/imm32
     311     68/push  2/imm32/stderr
     312     # . . call
     313     e8/call  _write/disp32
     314     # . . discard args
     315     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     316     # . syscall(exit, 1)
     317     bb/copy-to-ebx  1/imm32
     318     e8/call  syscall_exit/disp32
     319     # never gets here
     320
     321 test-get-slice:
     322     # . prologue
     323     55/push-ebp
     324     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     325     # - setup: create a table with a couple of keys
     326     # var table/ecx: (stream {string, number} 24)  # 2 rows * 12 bytes/row
     327     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x18/imm32        # subtract from esp
     328     68/push  0x18/imm32/size
     329     68/push  0/imm32/read
     330     68/push  0/imm32/write
     331     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
     332     # insert(table, "code", 12 bytes/row, Heap)
     333     # . . push args
     334     68/push  Heap/imm32
     335     68/push  0xc/imm32/row-size
     336     68/push  "code"/imm32
     337     51/push-ecx
     338     # . . call
     339     e8/call  get-or-insert/disp32
     340     # . . discard args
     341     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
     342     # insert(table, "data", 12 bytes/row, Heap)
     343     # . . push args
     344     68/push  Heap/imm32
     345     68/push  0xc/imm32/row-size
     346     68/push  "data"/imm32
     347     51/push-ecx
     348     # . . call
     349     e8/call  get-or-insert/disp32
     350     # . . discard args
     351     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
     352 $test-get-slice:check1:
     353     # (eax..edx) = "code"
     354     b8/copy-to-eax  "code"/imm32
     355     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           2/r32/edx   .               .                 # copy *eax to edx
     356     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  2/index/edx   .           2/r32/edx   4/disp8         .                 # copy eax+edx+4 to edx
     357     05/add-to-eax  4/imm32
     358     # var slice/edx: slice = {eax, edx}
     359     52/push-edx
     360     50/push-eax
     361     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
     362     # eax = get-slice(table, "code", 12 bytes/row)
     363     # . . push args
     364     68/push  0xc/imm32/row-size
     365     52/push-edx
     366     51/push-ecx
     367     # . . call
     368     e8/call  get-slice/disp32
     369     # . . discard args
     370     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     371     # check-ints-equal(eax - table->data, 8, msg)  # first row's value slot returned
     372     # . check-ints-equal(eax - table, 20, msg)
     373     # . . push args
     374     68/push  "F - test-get-slice/0"/imm32
     375     68/push  0x14/imm32
     376     29/subtract                     3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # subtract ecx from eax
     377     50/push-eax
     378     # . . call
     379     e8/call  check-ints-equal/disp32
     380     # . . discard args
     381     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     382 $test-get-slice:check2:
     383     # (eax..edx) = "data"
     384     b8/copy-to-eax  "data"/imm32
     385     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           2/r32/edx   .               .                 # copy *eax to edx
     386     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  2/index/edx   .           2/r32/edx   4/disp8         .                 # copy eax+edx+4 to edx
     387     05/add-to-eax  4/imm32
     388     # var slice/edx: slice = {eax, edx}
     389     52/push-edx
     390     50/push-eax
     391     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
     392     # eax = get-slice(table, "data" slice, 12 bytes/row)
     393     # . . push args
     394     68/push  0xc/imm32/row-size
     395     52/push-edx
     396     51/push-ecx
     397     # . . call
     398     e8/call  get-slice/disp32
     399     # . . discard args
     400     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     401     # check-ints-equal(eax - table->data, 20, msg)
     402     # . check-ints-equal(eax - table, 32, msg)
     403     # . . push args
     404     68/push  "F - test-get-slice/1"/imm32
     405     68/push  0x20/imm32
     406     29/subtract                     3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # subtract ecx from eax
     407     50/push-eax
     408     # . . call
     409     e8/call  check-ints-equal/disp32
     410     # . . discard args
     411     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     412 $test-get-slice:end:
     413     # . epilogue
     414     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
     415     5d/pop-to-ebp
     416     c3/return
     417
     418 # if no row is found, save 'key' to the next available row
     419 # if there are no rows free, abort
     420 # return the address of the value
     421 get-or-insert:  # table: (addr stream {(handle array byte), T}), key: (addr array byte), row-size: int, ad: (addr allocation-descriptor) -> result/eax: (addr T)
     422     # pseudocode:
     423     #   curr = table->data
     424     #   max = &table->data[table->write]
     425     #   while curr < max
     426     #     var c: (addr array byte) = lookup(*curr)
     427     #     if string-equal?(key, c)
     428     #       return curr+8
     429     #     curr += row-size
     430     #   if table->write >= table->size
     431     #     abort
     432     #   zero-out(max, row-size)
     433     #   copy-array(ad, key, max)
     434     #   table->write += row-size
     435     #   return max+8
     436     #
     437     # . prologue
     438     55/push-ebp
     439     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     440     # . save registers
     441     51/push-ecx
     442     52/push-edx
     443     56/push-esi
     444     # esi = table
     445     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
     446     # var curr/ecx: (addr handle array byte) = table->data
     447     8d/copy-address                 1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   0xc/disp8       .                 # copy esi+12 to ecx
     448     # var max/edx: (addr byte) = &table->data[table->write]
     449     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           2/r32/edx   .               .                 # copy *esi to edx
     450     8d/copy-address                 0/mod/indirect  4/rm32/sib    1/base/ecx  2/index/edx   .           2/r32/edx   .               .                 # copy ecx+edx to edx
     451 $get-or-insert:search-loop:
     452     # if (curr >= max) break
     453     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
     454     73/jump-if-addr>=  $get-or-insert:not-found/disp8
     455     # var c/eax: (addr array byte) = lookup(*curr)
     456     # . . push args
     457     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         .                 # push *(ecx+4)
     458     ff          6/subop/push        0/mod/indirect  1/rm32/ecx    .           .             .           .           .               .                 # push *ecx
     459     # . . call
     460     e8/call  lookup/disp32
     461     # . . discard args
     462     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     463     # if (string-equal?(key, c)) return curr+8
     464     # . eax = string-equal?(key, c)
     465     # . . push args
     466     50/push-eax
     467     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     468     # . . call
     469     e8/call  string-equal?/disp32
     470     # . . discard args
     471     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     472     # . if (eax != false) return eax = curr+8
     473     3d/compare-eax-and  0/imm32/false
     474     74/jump-if-=  $get-or-insert:mismatch/disp8
     475     8d/copy-address                 1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   8/disp8         .                 # copy ecx+8 to eax
     476     eb/jump  $get-or-insert:end/disp8
     477 $get-or-insert:mismatch:
     478     # curr += row-size
     479     03/add                          1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   0x10/disp8      .                 # add *(ebp+16) to ecx
     480     # loop
     481     eb/jump  $get-or-insert:search-loop/disp8
     482 $get-or-insert:not-found:
     483     # if (table->write >= table->size) abort
     484     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # copy *esi to ecx
     485     3b/compare                      1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   8/disp8         .                 # compare ecx with *(esi+8)
     486     73/jump-if-addr>=  $get-or-insert:abort/disp8
     487     # zero-out(max, row-size)
     488     # . . push args
     489     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
     490     52/push-edx
     491     # . . call
     492     e8/call  zero-out/disp32
     493     # . . discard args
     494     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     495     # copy-array(ad, key, max)
     496     # . . push args
     497     52/push-edx
     498     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     499     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x14/disp8      .                 # push *(ebp+20)
     500     # . . call
     501     e8/call  copy-array/disp32
     502     # . . discard args
     503     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     504     # table->write += row-size
     505     # . eax = row-size
     506     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   0x10/disp8      .                 # copy *(ebp+16) to eax
     507     # . table->write += eax
     508     01/add                          0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # add eax to *esi
     509     # return max+8
     510     # . eax = max
     511     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # copy edx to eax
     512     # . eax += 8
     513     05/add-to-eax  8/imm32
     514 $get-or-insert:end:
     515     # . restore registers
     516     5e/pop-to-esi
     517     5a/pop-to-edx
     518     59/pop-to-ecx
     519     # . epilogue
     520     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
     521     5d/pop-to-ebp
     522     c3/return
     523
     524 $get-or-insert:abort:
     525     # . _write(2/stderr, error)
     526     # . . push args
     527     68/push  "get-or-insert: table is full\n"/imm32
     528     68/push  2/imm32/stderr
     529     # . . call
     530     e8/call  _write/disp32
     531     # . . discard args
     532     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     533     # . syscall(exit, 1)
     534     bb/copy-to-ebx  1/imm32
     535     e8/call  syscall_exit/disp32
     536     # never gets here
     537
     538 test-get-or-insert:
     539     # . prologue
     540     55/push-ebp
     541     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     542     # var table/ecx: (stream {(handle array byte), number} 24)  # 2 rows * 12 bytes/row
     543     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x18/imm32        # subtract from esp
     544     68/push  0x18/imm32/size
     545     68/push  0/imm32/read
     546     68/push  0/imm32/write
     547     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
     548 $test-get-or-insert:first-call:
     549     # - start with an empty table, insert one key, verify that it was inserted
     550     # eax = get-or-insert(table, "code", 12 bytes/row, Heap)
     551     # . . push args
     552     68/push  Heap/imm32
     553     68/push  0xc/imm32/row-size
     554     68/push  "code"/imm32
     555     51/push-ecx
     556     # . . call
     557     e8/call  get-or-insert/disp32
     558     # . . discard args
     559     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
     560     # check-ints-equal(eax - table->data, 8, msg)  # first row's value slot returned
     561     # . check-ints-equal(eax - table, 20, msg)
     562     # . . push args
     563     68/push  "F - test-get-or-insert/0"/imm32
     564     68/push  0x14/imm32
     565     29/subtract                     3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # subtract ecx from eax
     566     50/push-eax
     567     # . . call
     568     e8/call  check-ints-equal/disp32
     569     # . . discard args
     570     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     571     # check-ints-equal(table->write, row-size = 12, msg)
     572     # . . push args
     573     68/push  "F - test-get-or-insert/1"/imm32
     574     68/push  0xc/imm32/row-size
     575     ff          6/subop/push        0/mod/indirect  1/rm32/ecx    .           .             .           .           .               .                 # push *ecx
     576     # . . call
     577     e8/call  check-ints-equal/disp32
     578     # . . discard args
     579     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     580     # var curr-addr/eax: (addr array byte) = lookup(table->data)
     581     # . . push args
     582     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           0x10/disp8      .                 # push *(ecx+16)
     583     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           0xc/disp8       .                 # push *(ecx+12)
     584     # . . call
     585     e8/call  lookup/disp32
     586     # . . discard args
     587     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     588     # check-strings-equal(curr-addr, "code", msg)
     589     # . . push args
     590     68/push  "F - test-get-or-insert/2"/imm32
     591     68/push  "code"/imm32
     592     50/push-eax
     593     # . . call
     594     e8/call  check-strings-equal/disp32
     595     # . . discard args
     596     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     597 $test-get-or-insert:second-call:
     598     # - insert the same key again, verify that it was reused
     599     # eax = get-or-insert(table, "code", 12 bytes/row, Heap)
     600     # . . push args
     601     68/push  Heap/imm32
     602     68/push  0xc/imm32/row-size
     603     68/push  "code"/imm32
     604     51/push-ecx
     605     # . . call
     606     e8/call  get-or-insert/disp32
     607     # . . discard args
     608     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
     609     # check-ints-equal(eax - table->data, 8, msg)
     610     # . check-ints-equal(eax - table, 20, msg)
     611     # . . push args
     612     68/push  "F - test-get-or-insert/3"/imm32
     613     68/push  0x14/imm32
     614     29/subtract                     3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # subtract ecx from eax
     615     50/push-eax
     616     # . . call
     617     e8/call  check-ints-equal/disp32
     618     # . . discard args
     619     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     620     # no new row inserted
     621     # . check-ints-equal(table->write, row-size = 12, msg)
     622     # . . push args
     623     68/push  "F - test-get-or-insert/4"/imm32
     624     68/push  0xc/imm32/row-size
     625     ff          6/subop/push        0/mod/indirect  1/rm32/ecx    .           .             .           .           .               .                 # push *ecx
     626     # . . call
     627     e8/call  check-ints-equal/disp32
     628     # . . discard args
     629     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     630     # curr-addr = lookup(table->data)
     631     # . . push args
     632     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           0x10/disp8      .                 # push *(ecx+16)
     633     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           0xc/disp8       .                 # push *(ecx+12)
     634     # . . call
     635     e8/call  lookup/disp32
     636     # . . discard args
     637     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     638     # check-strings-equal(curr-addr, "code", msg)
     639     # . . push args
     640     68/push  "F - test-get-or-insert/5"/imm32
     641     68/push  "code"/imm32
     642     50/push-eax
     643     # . . call
     644     e8/call  check-strings-equal/disp32
     645     # . . discard args
     646     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     647 $test-get-or-insert:third-call:
     648     # - insert a new key, verify that it was inserted
     649     # eax = get-or-insert(table, "data", 12 bytes/row, Heap)
     650     # . . push args
     651     68/push  Heap/imm32
     652     68/push  0xc/imm32/row-size
     653     68/push  "data"/imm32
     654     51/push-ecx
     655     # . . call
     656     e8/call  get-or-insert/disp32
     657     # . . discard args
     658     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
     659     # table gets a new row
     660     # check-ints-equal(eax - table->data, 20, msg)  # second row's value slot returned
     661     # . check-ints-equal(eax - table, 32, msg)
     662     # . . push args
     663     68/push  "F - test-get-or-insert/6"/imm32
     664     68/push  0x20/imm32
     665     29/subtract                     3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # subtract ecx from eax
     666     50/push-eax
     667     # . . call
     668     e8/call  check-ints-equal/disp32
     669     # . . discard args
     670     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     671     # check-ints-equal(table->write, 2 rows = 24, msg)
     672     # . . push args
     673     68/push  "F - test-get-or-insert/7"/imm32
     674     68/push  0x18/imm32/two-rows
     675     ff          6/subop/push        0/mod/indirect  1/rm32/ecx    .           .             .           .           .               .                 # push *ecx
     676     # . . call
     677     e8/call  check-ints-equal/disp32
     678     # . . discard args
     679     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     680     # curr-addr = lookup(table->data+12)
     681     # . . push args
     682     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           0x1c/disp8      .                 # push *(ecx+28)
     683     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           0x18/disp8      .                 # push *(ecx+24)
     684     # . . call
     685     e8/call  lookup/disp32
     686     # . . discard args
     687     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     688     # check-strings-equal(curr-addr, "data", msg)
     689     # . . push args
     690     68/push  "F - test-get-or-insert/8"/imm32
     691     68/push  "data"/imm32
     692     50/push-eax
     693     # . . call
     694     e8/call  check-strings-equal/disp32
     695     # . . discard args
     696     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     697 $test-get-or-insert:end:
     698     # . epilogue
     699     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
     700     5d/pop-to-ebp
     701     c3/return
     702
     703 # if no row is found, save 'key' to the next available row
     704 # if there are no rows free, abort
     705 # return the address of the value
     706 get-or-insert-handle:  # table: (addr stream {(handle array byte), T}), key: (handle array byte), row-size: int -> result/eax: (addr T)
     707     # pseudocode:
     708     #   var curr: (addr handle stream) = table->data
     709     #   var max: (addr byte) = &table->data[table->write]
     710     #   var k: (addr array byte) = lookup(key)
     711     #   while curr < max
     712     #     var c: (addr array byte) = lookup(*curr)
     713     #     if string-equal?(k, c)
     714     #       return curr+8
     715     #     curr += row-size
     716     #   if table->write >= table->size
     717     #     abort
     718     #   *max = key
     719     #   table->write += row-size
     720     #   return max+8
     721     #
     722     # . prologue
     723     55/push-ebp
     724     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     725     # . save registers
     726     51/push-ecx
     727     52/push-edx
     728     53/push-ebx
     729     56/push-esi
     730     # esi = table
     731     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
     732     # var k/ebx: (addr array byte) = lookup(key)
     733     # . eax = lookup(key)
     734     # . . push args
     735     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
     736     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     737     # . . call
     738     e8/call  lookup/disp32
     739     # . . discard args
     740     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     741     # . ebx = eax
     742     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # copy eax to ebx
     743     # var curr/ecx: (addr handle array byte) = table->data
     744     8d/copy-address                 1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   0xc/disp8       .                 # copy esi+12 to ecx
     745     # var max/edx: (addr byte) = &table->data[table->write]
     746     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           2/r32/edx   .               .                 # copy *esi to edx
     747     8d/copy-address                 0/mod/indirect  4/rm32/sib    1/base/ecx  2/index/edx   .           2/r32/edx   .               .                 # copy ecx+edx to edx
     748 $get-or-insert-handle:search-loop:
     749     # if (curr >= max) break
     750     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
     751     73/jump-if-addr>=  $get-or-insert-handle:not-found/disp8
     752     # var c/eax: (addr array byte) = lookup(*curr)
     753     # . . push args
     754     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         .                 # push *(ecx+4)
     755     ff          6/subop/push        0/mod/indirect  1/rm32/ecx    .           .             .           .           .               .                 # push *ecx
     756     # . . call
     757     e8/call  lookup/disp32
     758     # . . discard args
     759     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     760     # if (string-equal?(k, c)) return curr+8
     761     # . eax = string-equal?(k, c)
     762     # . . push args
     763     50/push-eax
     764     53/push-ebx
     765     # . . call
     766     e8/call  string-equal?/disp32
     767     # . . discard args
     768     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     769     # . if (eax != false) return eax = curr+8
     770     3d/compare-eax-and  0/imm32/false
     771     74/jump-if-=  $get-or-insert-handle:mismatch/disp8
     772     8d/copy-address                 1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   8/disp8         .                 # copy ecx+8 to eax
     773     eb/jump  $get-or-insert-handle:end/disp8
     774 $get-or-insert-handle:mismatch:
     775     # curr += row-size
     776     03/add                          1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   0x14/disp8      .                 # add *(ebp+20) to ecx
     777     # loop
     778     eb/jump  $get-or-insert-handle:search-loop/disp8
     779 $get-or-insert-handle:not-found:
     780     # if (table->write >= table->size) abort
     781     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # copy *esi to ecx
     782     3b/compare                      1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   8/disp8         .                 # compare ecx with *(esi+8)
     783     73/jump-if-addr>=  $get-or-insert-handle:abort/disp8
     784     # table->write += row-size
     785     # . eax = row-size
     786     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   0x14/disp8      .                 # copy *(ebp+20) to eax
     787     # . table->write += eax
     788     01/add                          0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # add eax to *esi
     789     # *max = key
     790     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   0xc/disp8       .                 # copy *(ebp+12) to eax
     791     89/copy                         0/mod/indirect  2/rm32/edx    .           .             .           0/r32/eax   .               .                 # copy eax to *edx
     792     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   0x10/disp8      .                 # copy *(ebp+16) to eax
     793     89/copy                         1/mod/*+disp8   2/rm32/edx    .           .             .           0/r32/eax   4/disp8         .                 # copy eax to *(edx+4)
     794     # return max+8
     795     # . eax = max
     796     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # copy edx to eax
     797     # . eax += 8
     798     05/add-to-eax  8/imm32
     799 $get-or-insert-handle:end:
     800     # . restore registers
     801     5e/pop-to-esi
     802     5b/pop-to-ebx
     803     5a/pop-to-edx
     804     59/pop-to-ecx
     805     # . epilogue
     806     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
     807     5d/pop-to-ebp
     808     c3/return
     809
     810 $get-or-insert-handle:abort:
     811     # . _write(2/stderr, error)
     812     # . . push args
     813     68/push  "get-or-insert-handle: table is full\n"/imm32
     814     68/push  2/imm32/stderr
     815     # . . call
     816     e8/call  _write/disp32
     817     # . . discard args
     818     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     819     # . syscall(exit, 1)
     820     bb/copy-to-ebx  1/imm32
     821     e8/call  syscall_exit/disp32
     822     # never gets here
     823
     824 test-get-or-insert-handle:
     825     # . prologue
     826     55/push-ebp
     827     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     828     # var table/ecx: (stream {(handle array byte), number} 24)  # 2 rows * 12 bytes/row
     829     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x18/imm32        # subtract from esp
     830     68/push  0x18/imm32/size
     831     68/push  0/imm32/read
     832     68/push  0/imm32/write
     833     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
     834     # var h/edx: (handle array byte)
     835     68/push  0/imm32
     836     68/push  0/imm32
     837     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
     838 $test-get-or-insert-handle:first-call:
     839     # - start with an empty table, insert one key, verify that it was inserted
     840     # copy-array(Heap, "code", h)
     841     # . . push args
     842     52/push-edx
     843     68/push  "code"/imm32
     844     68/push  Heap/imm32
     845     # . . call
     846     e8/call  copy-array/disp32
     847     # . . discard args
     848     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     849     # eax = get-or-insert-handle(table, h, 12 bytes/row)
     850     # . . push args
     851     68/push  0xc/imm32/row-size
     852     ff          6/subop/push        1/mod/*+disp8   2/rm32/edx    .           .             .           .           4/disp8         .                 # push *(edx+4)
     853     ff          6/subop/push        0/mod/indirect  2/rm32/edx    .           .             .           .           .               .                 # push *edx
     854     51/push-ecx
     855     # . . call
     856     e8/call  get-or-insert-handle/disp32
     857     # . . discard args
     858     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
     859     # check-ints-equal(eax - table->data, 8, msg)  # first row's value slot returned
     860     # . check-ints-equal(eax - table, 20, msg)
     861     # . . push args
     862     68/push  "F - test-get-or-insert-handle/0"/imm32
     863     68/push  0x14/imm32
     864     29/subtract                     3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # subtract ecx from eax
     865     50/push-eax
     866     # . . call
     867     e8/call  check-ints-equal/disp32
     868     # . . discard args
     869     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     870     # check-ints-equal(table->write, row-size = 12, msg)
     871     # . . push args
     872     68/push  "F - test-get-or-insert-handle/1"/imm32
     873     68/push  0xc/imm32/row-size
     874     ff          6/subop/push        0/mod/indirect  1/rm32/ecx    .           .             .           .           .               .                 # push *ecx
     875     # . . call
     876     e8/call  check-ints-equal/disp32
     877     # . . discard args
     878     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     879     # var curr-addr/eax: (addr array byte) = lookup(table->data)
     880     # . . push args
     881     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           0x10/disp8      .                 # push *(ecx+16)
     882     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           0xc/disp8       .                 # push *(ecx+12)
     883     # . . call
     884     e8/call  lookup/disp32
     885     # . . discard args
     886     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     887     # check-strings-equal(curr-addr, "code", msg)
     888     # . . push args
     889     68/push  "F - test-get-or-insert-handle/2"/imm32
     890     68/push  "code"/imm32
     891     50/push-eax
     892     # . . call
     893     e8/call  check-strings-equal/disp32
     894     # . . discard args
     895     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     896 $test-get-or-insert-handle:second-call:
     897     # - insert the same key again, verify that it was reused
     898     # copy-array(Heap, "code", h)
     899     # . . push args
     900     52/push-edx
     901     68/push  "code"/imm32
     902     68/push  Heap/imm32
     903     # . . call
     904     e8/call  copy-array/disp32
     905     # . . discard args
     906     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     907     # eax = get-or-insert-handle(table, h, 12 bytes/row)
     908     # . . push args
     909     68/push  0xc/imm32/row-size
     910     ff          6/subop/push        1/mod/*+disp8   2/rm32/edx    .           .             .           .           4/disp8         .                 # push *(edx+4)
     911     ff          6/subop/push        0/mod/indirect  2/rm32/edx    .           .             .           .           .               .                 # push *edx
     912     51/push-ecx
     913     # . . call
     914     e8/call  get-or-insert-handle/disp32
     915     # . . discard args
     916     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
     917     # check-ints-equal(eax - table->data, 8, msg)
     918     # . check-ints-equal(eax - table, 20, msg)
     919     # . . push args
     920     68/push  "F - test-get-or-insert-handle/3"/imm32
     921     68/push  0x14/imm32
     922     29/subtract                     3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # subtract ecx from eax
     923     50/push-eax
     924     # . . call
     925     e8/call  check-ints-equal/disp32
     926     # . . discard args
     927     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     928     # no new row inserted
     929     # . check-ints-equal(table->write, row-size = 12, msg)
     930     # . . push args
     931     68/push  "F - test-get-or-insert-handle/4"/imm32
     932     68/push  0xc/imm32/row-size
     933     ff          6/subop/push        0/mod/indirect  1/rm32/ecx    .           .             .           .           .               .                 # push *ecx
     934     # . . call
     935     e8/call  check-ints-equal/disp32
     936     # . . discard args
     937     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     938     # curr-addr = lookup(table->data)
     939     # . . push args
     940     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           0x10/disp8      .                 # push *(ecx+16)
     941     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           0xc/disp8       .                 # push *(ecx+12)
     942     # . . call
     943     e8/call  lookup/disp32
     944     # . . discard args
     945     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     946     # check-strings-equal(curr-addr, "code", msg)
     947     # . . push args
     948     68/push  "F - test-get-or-insert-handle/5"/imm32
     949     68/push  "code"/imm32
     950     50/push-eax
     951     # . . call
     952     e8/call  check-strings-equal/disp32
     953     # . . discard args
     954     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     955 $test-get-or-insert-handle:third-call:
     956     # - insert a new key, verify that it was inserted
     957     # copy-array(Heap, "data", h)
     958     # . . push args
     959     52/push-edx
     960     68/push  "data"/imm32
     961     68/push  Heap/imm32
     962     # . . call
     963     e8/call  copy-array/disp32
     964     # . . discard args
     965     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     966     # eax = get-or-insert-handle(table, h, 12 bytes/row)
     967     # . . push args
     968     68/push  0xc/imm32/row-size
     969     ff          6/subop/push        1/mod/*+disp8   2/rm32/edx    .           .             .           .           4/disp8         .                 # push *(edx+4)
     970     ff          6/subop/push        0/mod/indirect  2/rm32/edx    .           .             .           .           .               .                 # push *edx
     971     51/push-ecx
     972     # . . call
     973     e8/call  get-or-insert-handle/disp32
     974     # . . discard args
     975     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
     976     # table gets a new row
     977     # check-ints-equal(eax - table->data, 20, msg)  # second row's value slot returned
     978     # . check-ints-equal(eax - table, 32, msg)
     979     # . . push args
     980     68/push  "F - test-get-or-insert-handle/6"/imm32
     981     68/push  0x20/imm32
     982     29/subtract                     3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # subtract ecx from eax
     983     50/push-eax
     984     # . . call
     985     e8/call  check-ints-equal/disp32
     986     # . . discard args
     987     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     988     # check-ints-equal(table->write, 2 rows = 24, msg)
     989     # . . push args
     990     68/push  "F - test-get-or-insert-handle/7"/imm32
     991     68/push  0x18/imm32/two-rows
     992     ff          6/subop/push        0/mod/indirect  1/rm32/ecx    .           .             .           .           .               .                 # push *ecx
     993     # . . call
     994     e8/call  check-ints-equal/disp32
     995     # . . discard args
     996     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     997     # curr-addr = lookup(table->data+12)
     998     # . . push args
     999     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           0x1c/disp8      .                 # push *(ecx+28)
    1000     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           0x18/disp8      .                 # push *(ecx+24)
    1001     # . . call
    1002     e8/call  lookup/disp32
    1003     # . . discard args
    1004     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1005     # check-strings-equal(curr-addr, "data", msg)
    1006     # . . push args
    1007     68/push  "F - test-get-or-insert-handle/8"/imm32
    1008     68/push  "data"/imm32
    1009     50/push-eax
    1010     # . . call
    1011     e8/call  check-strings-equal/disp32
    1012     # . . discard args
    1013     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1014 $test-get-or-insert-handle:end:
    1015     # . epilogue
    1016     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1017     5d/pop-to-ebp
    1018     c3/return
    1019
    1020 # if no row is found, save 'key' in the next available row
    1021 # if there are no rows free, abort
    1022 get-or-insert-slice:  # table: (addr stream {(handle array byte), T}), key: (addr slice), row-size: int, ad: (addr allocation-descriptor) -> result/eax: (addr T)
    1023     # pseudocode:
    1024     #   curr = table->data
    1025     #   max = &table->data[table->write]
    1026     #   while curr < max
    1027     #     var c: (addr array byte) = lookup(*curr)
    1028     #     if slice-equal?(key, *curr)
    1029     #       return curr+8
    1030     #     curr += row-size
    1031     #   if table->write >= table->size
    1032     #     abort
    1033     #   zero-out(max, row-size)
    1034     #   slice-to-string(ad, key, max)
    1035     #   table->write += row-size
    1036     #   return max+8
    1037     #
    1038     # . prologue
    1039     55/push-ebp
    1040     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1041     # . save registers
    1042     51/push-ecx
    1043     52/push-edx
    1044     56/push-esi
    1045     # esi = table
    1046     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
    1047     # var curr/ecx: (addr handle array byte) = table->data
    1048     8d/copy-address                 1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   0xc/disp8       .                 # copy esi+12 to ecx
    1049     # var max/edx: (addr byte) = &table->data[table->write]
    1050     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           2/r32/edx   .               .                 # copy *esi to edx
    1051     8d/copy-address                 0/mod/indirect  4/rm32/sib    1/base/ecx  2/index/edx   .           2/r32/edx   .               .                 # copy ecx+edx to edx
    1052 $get-or-insert-slice:search-loop:
    1053     # if (curr >= max) break
    1054     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
    1055     73/jump-if-addr>=  $get-or-insert-slice:not-found/disp8
    1056     # var c/eax: (addr array byte) = lookup(*curr)
    1057     # . . push args
    1058     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         .                 # push *(ecx+4)
    1059     ff          6/subop/push        0/mod/indirect  1/rm32/ecx    .           .             .           .           .               .                 # push *ecx
    1060     # . . call
    1061     e8/call  lookup/disp32
    1062     # . . discard args
    1063     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1064     # if (slice-equal?(key, c)) return curr+4
    1065     # . eax = slice-equal?(key, c)
    1066     # . . push args
    1067     50/push-eax
    1068     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    1069     # . . call
    1070     e8/call  slice-equal?/disp32
    1071     # . . discard args
    1072     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1073     # . if (eax != false) return eax = curr+8
    1074     3d/compare-eax-and  0/imm32/false
    1075     74/jump-if-=  $get-or-insert-slice:mismatch/disp8
    1076     8d/copy-address                 1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   8/disp8         .                 # copy ecx+8 to eax
    1077     eb/jump  $get-or-insert-slice:end/disp8
    1078 $get-or-insert-slice:mismatch:
    1079     # curr += row-size
    1080     03/add                          1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   0x10/disp8      .                 # add *(ebp+16) to ecx
    1081     # loop
    1082     eb/jump  $get-or-insert-slice:search-loop/disp8
    1083 $get-or-insert-slice:not-found:
    1084     # result/eax = 0
    1085     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
    1086     # if (table->write >= table->size) abort
    1087     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # copy *esi to ecx
    1088     3b/compare                      1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   8/disp8         .                 # compare ecx with *(esi+8)
    1089     7d/jump-if->=  $get-or-insert-slice:abort/disp8
    1090     # zero-out(max, row-size)
    1091     # . . push args
    1092     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
    1093     52/push-edx
    1094     # . . call
    1095     e8/call  zero-out/disp32
    1096     # . . discard args
    1097     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1098     # slice-to-string(ad, key, max)
    1099     # . . push args
    1100     52/push-edx
    1101     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    1102     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x14/disp8      .                 # push *(ebp+20)
    1103     # . . call
    1104     e8/call  slice-to-string/disp32
    1105     # . . discard args
    1106     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1107     # table->write += row-size
    1108     # . eax = row-size
    1109     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   0x10/disp8      .                 # copy *(ebp+16) to eax
    1110     # . table->write += eax
    1111     01/add                          0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # add eax to *esi
    1112     # return max+8
    1113     # . eax = max
    1114     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # copy edx to eax
    1115     # . eax += 8
    1116     05/add-to-eax  8/imm32
    1117 $get-or-insert-slice:end:
    1118     # . restore registers
    1119     5e/pop-to-esi
    1120     5a/pop-to-edx
    1121     59/pop-to-ecx
    1122     # . epilogue
    1123     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1124     5d/pop-to-ebp
    1125     c3/return
    1126
    1127 $get-or-insert-slice:abort:
    1128     # . _write(2/stderr, error)
    1129     # . . push args
    1130     68/push  "get-or-insert-slice: table is full\n"/imm32
    1131     68/push  2/imm32/stderr
    1132     # . . call
    1133     e8/call  _write/disp32
    1134     # . . discard args
    1135     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1136     # . syscall(exit, 1)
    1137     bb/copy-to-ebx  1/imm32
    1138     e8/call  syscall_exit/disp32
    1139     # never gets here
    1140
    1141 test-get-or-insert-slice:
    1142     # . prologue
    1143     55/push-ebp
    1144     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1145     # var table/ecx: (stream {string, number} 24)  # 2 rows * 12 bytes/row
    1146     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x18/imm32        # subtract from esp
    1147     68/push  0x18/imm32/size
    1148     68/push  0/imm32/read
    1149     68/push  0/imm32/write
    1150     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
    1151     # (eax..edx) = "code"
    1152     b8/copy-to-eax  "code"/imm32
    1153     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           2/r32/edx   .               .                 # copy *eax to edx
    1154     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  2/index/edx   .           2/r32/edx   4/disp8         .                 # copy eax+edx+4 to edx
    1155     05/add-to-eax  4/imm32
    1156     # var slice/edx: slice = {eax, edx}
    1157     52/push-edx
    1158     50/push-eax
    1159     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
    1160 $test-get-or-insert-slice:first-call:
    1161     # - start with an empty table, insert one key, verify that it was inserted
    1162     # eax = get-or-insert-slice(table, "code" slice, 12 bytes/row, Heap)
    1163     # . . push args
    1164     68/push  Heap/imm32
    1165     68/push  0xc/imm32/row-size
    1166     52/push-edx
    1167     51/push-ecx
    1168     # . . call
    1169     e8/call  get-or-insert-slice/disp32
    1170     # . . discard args
    1171     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
    1172     # check-ints-equal(eax - table->data, 8, msg)  # first row's value slot returned
    1173     # . check-ints-equal(eax - table, 20, msg)
    1174     # . . push args
    1175     68/push  "F - test-get-or-insert-slice/0"/imm32
    1176     68/push  0x14/imm32
    1177     29/subtract                     3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # subtract ecx from eax
    1178     50/push-eax
    1179     # . . call
    1180     e8/call  check-ints-equal/disp32
    1181     # . . discard args
    1182     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1183     # check-ints-equal(table->write, row-size = 12, msg)
    1184     # . . push args
    1185     68/push  "F - test-get-or-insert-slice/1"/imm32
    1186     68/push  0xc/imm32/row-size
    1187     ff          6/subop/push        0/mod/indirect  1/rm32/ecx    .           .             .           .           .               .                 # push *ecx
    1188     # . . call
    1189     e8/call  check-ints-equal/disp32
    1190     # . . discard args
    1191     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1192     # var curr-addr/eax: (addr array byte) = lookup(table->data)
    1193     # . . push args
    1194     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           0x10/disp8      .                 # push *(ecx+16)
    1195     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           0xc/disp8       .                 # push *(ecx+12)
    1196     # . . call
    1197     e8/call  lookup/disp32
    1198     # . . discard args
    1199     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1200     # check-strings-equal(curr-addr, "code", msg)
    1201     # . . push args
    1202     68/push  "F - test-get-or-insert-slice/2"/imm32
    1203     68/push  "code"/imm32
    1204     50/push-eax
    1205     # . . call
    1206     e8/call  check-strings-equal/disp32
    1207     # . . discard args
    1208     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1209 $test-get-or-insert-slice:second-call:
    1210     # - insert the same key again, verify that it was reused
    1211     # eax = get-or-insert-slice(table, "code" slice, 12 bytes/row)
    1212     # . . push args
    1213     68/push  Heap/imm32
    1214     68/push  0xc/imm32/row-size
    1215     52/push-edx
    1216     51/push-ecx
    1217     # . . call
    1218     e8/call  get-or-insert-slice/disp32
    1219     # . . discard args
    1220     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
    1221     # check-ints-equal(eax - table->data, 8, msg)
    1222     # . check-ints-equal(eax - table, 20, msg)
    1223     # . . push args
    1224     68/push  "F - test-get-or-insert-slice/3"/imm32
    1225     68/push  0x14/imm32
    1226     29/subtract                     3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # subtract ecx from eax
    1227     50/push-eax
    1228     # . . call
    1229     e8/call  check-ints-equal/disp32
    1230     # . . discard args
    1231     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1232     # no new row inserted
    1233     # . check-ints-equal(table->write, row-size = 12, msg)
    1234     # . . push args
    1235     68/push  "F - test-get-or-insert-slice/4"/imm32
    1236     68/push  0xc/imm32/row-size
    1237     ff          6/subop/push        0/mod/indirect  1/rm32/ecx    .           .             .           .           .               .                 # push *ecx
    1238     # . . call
    1239     e8/call  check-ints-equal/disp32
    1240     # . . discard args
    1241     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1242     # curr-addr = lookup(table->data)
    1243     # . . push args
    1244     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           0x10/disp8      .                 # push *(ecx+16)
    1245     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           0xc/disp8       .                 # push *(ecx+12)
    1246     # . . call
    1247     e8/call  lookup/disp32
    1248     # . . discard args
    1249     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1250     # check-strings-equal(curr-addr, "code", msg)
    1251     # . . push args
    1252     68/push  "F - test-get-or-insert-slice/5"/imm32
    1253     68/push  "code"/imm32
    1254     50/push-eax
    1255     # . . call
    1256     e8/call  check-strings-equal/disp32
    1257     # . . discard args
    1258     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1259 $test-get-or-insert-slice:third-call:
    1260     # - insert a new key, verify that it was inserted
    1261     # (eax..edx) = "data"
    1262     b8/copy-to-eax  "data"/imm32
    1263     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           2/r32/edx   .               .                 # copy *eax to edx
    1264     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  2/index/edx   .           2/r32/edx   4/disp8         .                 # copy eax+edx+4 to edx
    1265     05/add-to-eax  4/imm32
    1266     # var slice/edx: slice = {eax, edx}
    1267     52/push-edx
    1268     50/push-eax
    1269     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
    1270     # eax = get-or-insert-slice(table, "data" slice, 12 bytes/row)
    1271     # . . push args
    1272     68/push  Heap/imm32
    1273     68/push  0xc/imm32/row-size
    1274     52/push-edx
    1275     51/push-ecx
    1276     # . . call
    1277     e8/call  get-or-insert-slice/disp32
    1278     # . . discard args
    1279     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
    1280     # table gets a new row
    1281     # check-ints-equal(eax - table->data, 20, msg)  # second row's value slot returned
    1282     # . check-ints-equal(eax - table, 32, msg)
    1283     # . . push args
    1284     68/push  "F - test-get-or-insert-slice/6"/imm32
    1285     68/push  0x20/imm32
    1286     29/subtract                     3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # subtract ecx from eax
    1287     50/push-eax
    1288     # . . call
    1289     e8/call  check-ints-equal/disp32
    1290     # . . discard args
    1291     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1292     # check-ints-equal(table->write, 2 rows = 24, msg)
    1293     # . . push args
    1294     68/push  "F - test-get-or-insert-slice/7"/imm32
    1295     68/push  0x18/imm32/two-rows
    1296     ff          6/subop/push        0/mod/indirect  1/rm32/ecx    .           .             .           .           .               .                 # push *ecx
    1297     # . . call
    1298     e8/call  check-ints-equal/disp32
    1299     # . . discard args
    1300     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1301     # curr-addr = lookup(table->data+12)
    1302     # . . push args
    1303     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           0x1c/disp8      .                 # push *(ecx+28)
    1304     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           0x18/disp8      .                 # push *(ecx+24)
    1305     # . . call
    1306     e8/call  lookup/disp32
    1307     # . . discard args
    1308     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1309     # check-strings-equal(curr-addr, "data", msg)
    1310     # . . push args
    1311     68/push  "F - test-get-or-insert-slice/8"/imm32
    1312     68/push  "data"/imm32
    1313     50/push-eax
    1314     # . . call
    1315     e8/call  check-strings-equal/disp32
    1316     # . . discard args
    1317     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1318 $test-get-or-insert-slice:end:
    1319     # . epilogue
    1320     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1321     5d/pop-to-ebp
    1322     c3/return
    1323
    1324 # if no row is found, stop(ed)
    1325 get-or-stop:  # table: (addr stream {(handle array byte), T}), key: (addr array byte), row-size: int,
    1326               # abort-message-prefix: (addr array byte), err: (addr buffered-file), ed: (addr exit-descriptor)
    1327               # -> result/eax: (addr T)
    1328     # pseudocode:
    1329     #   curr = table->data
    1330     #   max = &table->data[table->write]
    1331     #   while curr < max
    1332     #     var c: (addr array byte) = lookup(*curr)
    1333     #     if string-equal?(key, c)
    1334     #       return curr+8
    1335     #     curr += row-size
    1336     #   write-buffered(err, msg)
    1337     #   stop(ed)
    1338     #
    1339     # . prologue
    1340     55/push-ebp
    1341     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1342     # . save registers
    1343     51/push-ecx
    1344     52/push-edx
    1345     56/push-esi
    1346     # esi = table
    1347     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
    1348     # var curr/ecx: (addr handle array byte) = table->data
    1349     8d/copy-address                 1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   0xc/disp8       .                 # copy esi+12 to ecx
    1350     # var max/edx: (addr byte) = &table->data[table->write]
    1351     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           2/r32/edx   .               .                 # copy *esi to edx
    1352     8d/copy-address                 0/mod/indirect  4/rm32/sib    1/base/ecx  2/index/edx   .           2/r32/edx   .               .                 # copy ecx+edx to edx
    1353 $get-or-stop:search-loop:
    1354     # if (curr >= max) stop(ed)
    1355     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
    1356     73/jump-if-addr>=  $get-or-stop:stop/disp8
    1357     # var c/eax: (addr array byte) = lookup(*curr)
    1358     # . . push args
    1359     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         .                 # push *(ecx+4)
    1360     ff          6/subop/push        0/mod/indirect  1/rm32/ecx    .           .             .           .           .               .                 # push *ecx
    1361     # . . call
    1362     e8/call  lookup/disp32
    1363     # . . discard args
    1364     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1365     # if (string-equal?(key, c)) return curr+8
    1366     # . eax = string-equal?(key, c)
    1367     # . . push args
    1368     50/push-eax
    1369     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    1370     # . . call
    1371     e8/call  string-equal?/disp32
    1372     # . . discard args
    1373     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1374     # . if (eax != false) return eax = curr+8
    1375     3d/compare-eax-and  0/imm32/false
    1376     74/jump-if-=  $get-or-stop:mismatch/disp8
    1377     8d/copy-address                 1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   8/disp8         .                 # copy ecx+8 to eax
    1378     eb/jump  $get-or-stop:end/disp8
    1379 $get-or-stop:mismatch:
    1380     # curr += row-size
    1381     03/add                          1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   0x10/disp8      .                 # add *(ebp+16) to ecx
    1382     # loop
    1383     eb/jump  $get-or-stop:search-loop/disp8
    1384 $get-or-stop:end:
    1385     # . restore registers
    1386     5e/pop-to-esi
    1387     5a/pop-to-edx
    1388     59/pop-to-ecx
    1389     # . epilogue
    1390     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1391     5d/pop-to-ebp
    1392     c3/return
    1393
    1394 $get-or-stop:stop:
    1395     # . write-buffered(err, abort-message-prefix)
    1396     # . . push args
    1397     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x14/disp8      .                 # push *(ebp+20)
    1398     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x18/disp8      .                 # push *(ebp+24)
    1399     # . . call
    1400     e8/call  write-buffered/disp32
    1401     # . . discard args
    1402     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1403     # . write-buffered(err, error)
    1404     # . . push args
    1405     68/push  ": get-or-stop: key not found: "/imm32
    1406     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x18/disp8      .                 # push *(ebp+24)
    1407     # . . call
    1408     e8/call  write-buffered/disp32
    1409     # . . discard args
    1410     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1411     # . write-buffered(err, key)
    1412     # . . push args
    1413     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    1414     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x18/disp8      .                 # push *(ebp+24)
    1415     # . . call
    1416     e8/call  write-buffered/disp32
    1417     # . . discard args
    1418     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1419     # . write-buffered(err, "\n")
    1420     # . . push args
    1421     68/push  Newline/imm32
    1422     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x18/disp8      .                 # push *(ebp+24)
    1423     # . . call
    1424     e8/call  write-buffered/disp32
    1425     # . . discard args
    1426     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1427     # . stop(ed, 1)
    1428     # . . push args
    1429     68/push  1/imm32
    1430     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x1c/disp8      .                 # push *(ebp+28)
    1431     # . . call
    1432     e8/call  stop/disp32
    1433     # never gets here
    1434 $get-or-stop:terminus:
    1435     # . . discard args
    1436     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1437     # syscall(exit, 1)
    1438     bb/copy-to-ebx  1/imm32
    1439     e8/call  syscall_exit/disp32
    1440
    1441 test-get-or-stop:
    1442     # This test uses exit-descriptors. Use ebp for setting up local variables.
    1443     # . prologue
    1444     55/push-ebp
    1445     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1446     # setup
    1447     # . clear-stream(_test-error-stream)
    1448     # . . push args
    1449     68/push  _test-error-stream/imm32
    1450     # . . call
    1451     e8/call  clear-stream/disp32
    1452     # . . discard args
    1453     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1454     # . clear-stream($_test-error-buffered-file->buffer)
    1455     # . . push args
    1456     68/push  $_test-error-buffered-file->buffer/imm32
    1457     # . . call
    1458     e8/call  clear-stream/disp32
    1459     # . . discard args
    1460     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1461     # var table/ecx: (stream {string, number} 24)  # 2 rows * 12 bytes/row
    1462     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x18/imm32        # subtract from esp
    1463     68/push  0x18/imm32/size
    1464     68/push  0/imm32/read
    1465     68/push  0/imm32/write
    1466     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
    1467     # var ed/edx: exit-descriptor
    1468     68/push  0/imm32
    1469     68/push  0/imm32
    1470     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
    1471     # size 'ed' for the calls to 'get-or-stop'
    1472     # . tailor-exit-descriptor(ed, 24)
    1473     # . . push args
    1474     68/push  0x18/imm32/nbytes-of-args-for-get-or-stop
    1475     52/push-edx
    1476     # . . call
    1477     e8/call  tailor-exit-descriptor/disp32
    1478     # . . discard args
    1479     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1480     # insert(table, "code", 12 bytes/row, Heap)
    1481     # . . push args
    1482     68/push  Heap/imm32
    1483     68/push  0xc/imm32/row-size
    1484     68/push  "code"/imm32
    1485     51/push-ecx
    1486     # . . call
    1487     e8/call  get-or-insert/disp32
    1488     # . . discard args
    1489     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
    1490 $test-get-or-stop:success:
    1491     # eax = get-or-stop(table, "code", row-size=12, msg, _test-error-buffered-file, ed)
    1492     # . . push args
    1493     52/push-edx/ed
    1494     68/push  _test-error-buffered-file/imm32
    1495     68/push  "foo"/imm32/abort-prefix
    1496     68/push  0xc/imm32/row-size
    1497     68/push  "code"/imm32
    1498     51/push-ecx
    1499     # . . call
    1500     e8/call  get-or-stop/disp32
    1501     # . . discard args
    1502     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x18/imm32        # add to esp
    1503 $test-get-or-stop:success-assertion:
    1504     # check-ints-equal(eax - table->data, 8, msg)
    1505     # . check-ints-equal(eax - table, 20, msg)
    1506     # . . push args
    1507     68/push  "F - test-get-or-stop/0"/imm32
    1508     68/push  0x14/imm32
    1509     29/subtract                     3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # subtract ecx from eax
    1510     50/push-eax
    1511     # . . call
    1512     e8/call  check-ints-equal/disp32
    1513     # . . discard args
    1514     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1515 $test-get-or-stop:failure:
    1516     # eax = get-or-stop(table, "data", row-size=12, msg, _test-error-buffered-file, ed)
    1517     # . . push args
    1518     52/push-edx/ed
    1519     68/push  _test-error-buffered-file/imm32
    1520     68/push  "foo"/imm32/abort-prefix
    1521     68/push  0xc/imm32/row-size
    1522     68/push  "data"/imm32
    1523     51/push-ecx
    1524     # . . call
    1525     e8/call  get-or-stop/disp32
    1526     # registers except esp may be clobbered at this point
    1527     # restore register args, discard others
    1528     59/pop-to-ecx
    1529     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
    1530     5a/pop-to-edx
    1531 $test-get-or-stop:failure-assertion:
    1532     # check that get-or-stop tried to call stop(1)
    1533     # . check-ints-equal(ed->value, 2, msg)
    1534     # . . push args
    1535     68/push  "F - test-get-or-stop/1"/imm32
    1536     68/push  2/imm32
    1537     # . . push ed->value
    1538     ff          6/subop/push        1/mod/*+disp8   2/rm32/edx    .           .             .           .           4/disp8         .                 # push *(edx+4)
    1539     # . . call
    1540     e8/call  check-ints-equal/disp32
    1541     # . . discard args
    1542     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1543 $test-get-or-stop:end:
    1544     # . epilogue
    1545     # don't restore esp from ebp; manually reclaim locals
    1546     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x2c/imm32        # add to esp
    1547     5d/pop-to-ebp
    1548     c3/return
    1549
    1550 # if no row is found, stop(ed)
    1551 get-slice-or-stop:  # table: (addr stream {(handle array byte), _}), key: (addr slice), row-size: int,
    1552                     # abort-message-prefix: (addr string), err: (addr buffered-file), ed: (addr exit-descriptor)
    1553                     # -> result/eax: (addr _)
    1554     # pseudocode:
    1555     #   curr = table->data
    1556     #   max = &table->data[table->write]
    1557     #   while curr < max
    1558     #     var c: (addr array byte) = lookup(*curr)
    1559     #     if slice-equal?(key, c)
    1560     #       return curr+8
    1561     #     curr += row-size
    1562     #   write-buffered(err, msg)
    1563     #   stop(ed)
    1564     #
    1565     # . prologue
    1566     55/push-ebp
    1567     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1568     # . save registers
    1569     51/push-ecx
    1570     52/push-edx
    1571     56/push-esi
    1572     # esi = table
    1573     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
    1574     # var curr/ecx: (addr handle array byte) = table->data
    1575     8d/copy-address                 1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   0xc/disp8       .                 # copy esi+12 to ecx
    1576     # var max/edx: (addr byte) = &table->data[table->write]
    1577     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           2/r32/edx   .               .                 # copy *esi to edx
    1578     8d/copy-address                 0/mod/indirect  4/rm32/sib    1/base/ecx  2/index/edx   .           2/r32/edx   .               .                 # copy ecx+edx to edx
    1579 $get-slice-or-stop:search-loop:
    1580     # if (curr >= max) stop(ed)
    1581     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
    1582     73/jump-if-addr>=  $get-slice-or-stop:stop/disp8
    1583     # var c/eax: (addr array byte) = lookup(*curr)
    1584     # . . push args
    1585     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         .                 # push *(ecx+4)
    1586     ff          6/subop/push        0/mod/indirect  1/rm32/ecx    .           .             .           .           .               .                 # push *ecx
    1587     # . . call
    1588     e8/call  lookup/disp32
    1589     # . . discard args
    1590     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1591     # if (slice-equal?(key, c)) return curr+4
    1592     # . eax = slice-equal?(key, c)
    1593     # . . push args
    1594     50/push-eax
    1595     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    1596     # . . call
    1597     e8/call  slice-equal?/disp32
    1598     # . . discard args
    1599     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1600     # . if (eax != false) return eax = curr+8
    1601     3d/compare-eax-and  0/imm32/false
    1602     74/jump-if-=  $get-slice-or-stop:mismatch/disp8
    1603     8d/copy-address                 1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   8/disp8         .                 # copy ecx+8 to eax
    1604     eb/jump  $get-slice-or-stop:end/disp8
    1605 $get-slice-or-stop:mismatch:
    1606     # curr += row-size
    1607     03/add                          1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   0x10/disp8      .                 # add *(ebp+16) to ecx
    1608     # loop
    1609     eb/jump  $get-slice-or-stop:search-loop/disp8
    1610 $get-slice-or-stop:end:
    1611     # . restore registers
    1612     5e/pop-to-esi
    1613     5a/pop-to-edx
    1614     59/pop-to-ecx
    1615     # . epilogue
    1616     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1617     5d/pop-to-ebp
    1618     c3/return
    1619
    1620 $get-slice-or-stop:stop:
    1621     # . write-buffered(err, abort-message-prefix)
    1622     # . . push args
    1623     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x14/disp8      .                 # push *(ebp+20)
    1624     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x18/disp8      .                 # push *(ebp+24)
    1625     # . . call
    1626     e8/call  write-buffered/disp32
    1627     # . . discard args
    1628     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1629     # . write-buffered(err, error)
    1630     # . . push args
    1631     68/push  ": get-slice-or-stop: key not found: "/imm32
    1632     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x18/disp8      .                 # push *(ebp+24)
    1633     # . . call
    1634     e8/call  write-buffered/disp32
    1635     # . . discard args
    1636     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1637     # . write-slice-buffered(err, key)
    1638     # . . push args
    1639     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    1640     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x18/disp8      .                 # push *(ebp+24)
    1641     # . . call
    1642     e8/call  write-slice-buffered/disp32
    1643     # . . discard args
    1644     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1645     # . write-buffered(err, "\n")
    1646     # . . push args
    1647     68/push  Newline/imm32
    1648     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x18/disp8      .                 # push *(ebp+24)
    1649     # . . call
    1650     e8/call  write-buffered/disp32
    1651     # . . discard args
    1652     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1653     # . stop(ed, 1)
    1654     # . . push args
    1655     68/push  1/imm32
    1656     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x1c/disp8      .                 # push *(ebp+28)
    1657     # . . call
    1658     e8/call  stop/disp32
    1659     # never gets here
    1660 $get-slice-or-stop:terminus:
    1661     # . . discard args
    1662     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1663     # syscall(exit, 1)
    1664     bb/copy-to-ebx  1/imm32
    1665     e8/call  syscall_exit/disp32
    1666
    1667 test-get-slice-or-stop:
    1668     # This test uses exit-descriptors. Use ebp for setting up local variables.
    1669     # . prologue
    1670     55/push-ebp
    1671     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1672     # setup
    1673     # . clear-stream(_test-error-stream)
    1674     # . . push args
    1675     68/push  _test-error-stream/imm32
    1676     # . . call
    1677     e8/call  clear-stream/disp32
    1678     # . . discard args
    1679     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1680     # . clear-stream($_test-error-buffered-file->buffer)
    1681     # . . push args
    1682     68/push  $_test-error-buffered-file->buffer/imm32
    1683     # . . call
    1684     e8/call  clear-stream/disp32
    1685     # . . discard args
    1686     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1687     # var table/ecx: (stream {string, number} 24)  # 2 rows * 12 bytes/row
    1688     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x18/imm32        # subtract from esp
    1689     68/push  0x18/imm32/size
    1690     68/push  0/imm32/read
    1691     68/push  0/imm32/write
    1692     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
    1693     # var ed/edx: exit-descriptor
    1694     68/push  0/imm32
    1695     68/push  0/imm32
    1696     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
    1697     # var slice/ebx: slice = "code"
    1698     # . (eax..ebx) = "code"
    1699     b8/copy-to-eax  "code"/imm32
    1700     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           3/r32/ebx   .               .                 # copy *eax to ebx
    1701     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  3/index/ebx   .           3/r32/ebx   4/disp8         .                 # copy eax+ebx+4 to ebx
    1702     05/add-to-eax  4/imm32
    1703     # . ebx = {eax, ebx}
    1704     53/push-ebx
    1705     50/push-eax
    1706     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
    1707     # size 'ed' for the calls to 'get-or-stop' (define no locals past this point)
    1708     # . tailor-exit-descriptor(ed, 24)
    1709     # . . push args
    1710     68/push  0x18/imm32/nbytes-of-args-for-get-or-stop
    1711     52/push-edx
    1712     # . . call
    1713     e8/call  tailor-exit-descriptor/disp32
    1714     # . . discard args
    1715     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1716     # insert(table, "code", 12 bytes/row, Heap)
    1717     # . . push args
    1718     68/push  Heap/imm32
    1719     68/push  0xc/imm32/row-size
    1720     68/push  "code"/imm32
    1721     51/push-ecx
    1722     # . . call
    1723     e8/call  get-or-insert/disp32
    1724     # . . discard args
    1725     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
    1726 $test-get-slice-or-stop:success:
    1727     # eax = get-slice-or-stop(table, slice, row-size=12, msg, _test-error-buffered-file, ed)
    1728     # . . push args
    1729     52/push-edx/ed
    1730     68/push  _test-error-buffered-file/imm32
    1731     68/push  "foo"/imm32/abort-prefix
    1732     68/push  0xc/imm32/row-size
    1733     53/push-ebx/slice
    1734     51/push-ecx
    1735     # . . call
    1736     e8/call  get-slice-or-stop/disp32
    1737     # registers except esp may be clobbered at this point
    1738     # restore register args, discard others
    1739     59/pop-to-ecx
    1740     5b/pop-to-ebx
    1741     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1742     5a/pop-to-edx
    1743 $test-get-slice-or-stop:success-assertion:
    1744     # check-ints-equal(eax - table->data, 8, msg)  # first row's value slot returned
    1745     # . check-ints-equal(eax - table, 20, msg)
    1746     # . . push args
    1747     68/push  "F - test-get-slice-or-stop/0"/imm32
    1748     68/push  0x14/imm32
    1749     29/subtract                     3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # subtract ecx from eax
    1750     50/push-eax
    1751     # . . call
    1752     e8/call  check-ints-equal/disp32
    1753     # . . discard args
    1754     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1755 $test-get-slice-or-stop:failure:
    1756     # slice = "segment2"
    1757     # . *ebx = "segment2"->data
    1758     b8/copy-to-eax  "segment2"/imm32
    1759     05/add-to-eax  4/imm32
    1760     89/copy                         0/mod/indirect  3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # copy eax to *ebx
    1761     # . *(ebx+4) = "segment2"->data + len("segment2")
    1762     05/add-to-eax  8/imm32/strlen
    1763     89/copy                         1/mod/*+disp8   3/rm32/ebx    .           .             .           0/r32/eax   4/disp8         .                 # copy eax to *(ebx+4)
    1764     # eax = get-slice-or-stop(table, slice, row-size=12, msg, _test-error-buffered-file, ed)
    1765     # . . push args
    1766     52/push-edx/ed
    1767     68/push  _test-error-buffered-file/imm32
    1768     68/push  "foo"/imm32/abort-prefix
    1769     68/push  0xc/imm32/row-size
    1770     53/push-ebx/slice
    1771     51/push-ecx
    1772     # . . call
    1773     e8/call  get-slice-or-stop/disp32
    1774     # registers except esp may be clobbered at this point
    1775     # restore register args, discard others
    1776     59/pop-to-ecx
    1777     5b/pop-to-ebx
    1778     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1779     5a/pop-to-edx
    1780 $test-get-slice-or-stop:failure-assertion:
    1781     # check that get-or-stop tried to call stop(1)
    1782     # . check-ints-equal(ed->value, 2, msg)
    1783     # . . push args
    1784     68/push  "F - test-get-or-stop/1"/imm32
    1785     68/push  2/imm32
    1786     # . . push ed->value
    1787     ff          6/subop/push        1/mod/*+disp8   2/rm32/edx    .           .             .           .           4/disp8         .                 # push *(edx+4)
    1788     # . . call
    1789     e8/call  check-ints-equal/disp32
    1790     # . . discard args
    1791     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1792 $test-get-slice-or-stop:end:
    1793     # . epilogue
    1794     # don't restore esp from ebp; manually reclaim locals
    1795     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x34/imm32        # add to esp
    1796     5d/pop-to-ebp
    1797     c3/return
    1798
    1799 # if no row is found, return null (0)
    1800 maybe-get:  # table: (addr stream {(handle array byte), T}), key: (addr array byte), row-size: int -> result/eax: (addr T)
    1801     # pseudocode:
    1802     #   curr = table->data
    1803     #   max = &table->data[table->write]
    1804     #   while curr < max
    1805     #     var c: (addr array byte) = lookup(*curr)
    1806     #     if string-equal?(key, c)
    1807     #       return curr+8
    1808     #     curr += row-size
    1809     #   return 0
    1810     #
    1811     # . prologue
    1812     55/push-ebp
    1813     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1814     # . save registers
    1815     51/push-ecx
    1816     52/push-edx
    1817     56/push-esi
    1818     # esi = table
    1819     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
    1820     # var curr/ecx: (addr handle array byte) = table->data
    1821     8d/copy-address                 1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   0xc/disp8       .                 # copy esi+12 to ecx
    1822     # var max/edx: (addr byte) = &table->data[table->write]
    1823     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           2/r32/edx   .               .                 # copy *esi to edx
    1824     8d/copy-address                 0/mod/indirect  4/rm32/sib    1/base/ecx  2/index/edx   .           2/r32/edx   .               .                 # copy ecx+edx to edx
    1825 $maybe-get:search-loop:
    1826     # if (curr >= max) return null
    1827     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
    1828     73/jump-if-addr>=  $maybe-get:null/disp8
    1829     # var c/eax: (addr array byte) = lookup(*curr)
    1830     # . . push args
    1831     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         .                 # push *(ecx+4)
    1832     ff          6/subop/push        0/mod/indirect  1/rm32/ecx    .           .             .           .           .               .                 # push *ecx
    1833     # . . call
    1834     e8/call  lookup/disp32
    1835     # . . discard args
    1836     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1837     # if (string-equal?(key, c)) return curr+4
    1838     # . eax = string-equal?(key, c)
    1839     # . . push args
    1840     50/push-eax
    1841     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    1842     # . . call
    1843     e8/call  string-equal?/disp32
    1844     # . . discard args
    1845     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1846     # . if (eax != false) return eax = curr+8
    1847     3d/compare-eax-and  0/imm32/false
    1848     74/jump-if-=  $maybe-get:mismatch/disp8
    1849     8d/copy-address                 1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   8/disp8         .                 # copy ecx+8 to eax
    1850     eb/jump  $maybe-get:end/disp8
    1851 $maybe-get:mismatch:
    1852     # curr += row-size
    1853     03/add                          1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   0x10/disp8      .                 # add *(ebp+16) to ecx
    1854     # loop
    1855     eb/jump  $maybe-get:search-loop/disp8
    1856 $maybe-get:null:
    1857     b8/copy-to-eax  0/imm32
    1858 $maybe-get:end:
    1859     # . restore registers
    1860     5e/pop-to-esi
    1861     5a/pop-to-edx
    1862     59/pop-to-ecx
    1863     # . epilogue
    1864     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1865     5d/pop-to-ebp
    1866     c3/return
    1867
    1868 test-maybe-get:
    1869     # . prologue
    1870     55/push-ebp
    1871     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1872     # - setup: create a table with one row
    1873     # var table/ecx: (stream {string, number} 24)   # 2 rows * 12 bytes/row
    1874     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x18/imm32        # subtract from esp
    1875     68/push  0x18/imm32/size
    1876     68/push  0/imm32/read
    1877     68/push  0/imm32/write
    1878     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
    1879     # eax = get-or-insert(table, "code", 12 bytes/row, Heap)
    1880     # . . push args
    1881     68/push  Heap/imm32
    1882     68/push  0xc/imm32/row-size
    1883     68/push  "code"/imm32
    1884     51/push-ecx
    1885     # . . call
    1886     e8/call  get-or-insert/disp32
    1887     # . . discard args
    1888     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
    1889 $test-maybe-get:success:
    1890     # - check for the same key, verify that it was reused
    1891     # eax = maybe-get(table, "code", 12 bytes/row)
    1892     # . . push args
    1893     68/push  0xc/imm32/row-size
    1894     68/push  "code"/imm32
    1895     51/push-ecx
    1896     # . . call
    1897     e8/call  maybe-get/disp32
    1898     # . . discard args
    1899     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1900     # check-ints-equal(eax - table->data, 8, msg)
    1901     # . check-ints-equal(eax - table, 20, msg)
    1902     # . . push args
    1903     68/push  "F - test-maybe-get/0"/imm32
    1904     68/push  0x14/imm32
    1905     29/subtract                     3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # subtract ecx from eax
    1906     50/push-eax
    1907     # . . call
    1908     e8/call  check-ints-equal/disp32
    1909     # . . discard args
    1910     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1911     # no new row inserted
    1912     # . check-ints-equal(table->write, row-size = 12, msg)
    1913     # . . push args
    1914     68/push  "F - test-maybe-get/1"/imm32
    1915     68/push  0xc/imm32/row-size
    1916     ff          6/subop/push        0/mod/indirect  1/rm32/ecx    .           .             .           .           .               .                 # push *ecx
    1917     # . . call
    1918     e8/call  check-ints-equal/disp32
    1919     # . . discard args
    1920     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1921     # var curr-addr/eax: (addr array byte) = lookup(table->data)
    1922     # . . push args
    1923     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           0x10/disp8      .                 # push *(ecx+16)
    1924     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           0xc/disp8       .                 # push *(ecx+12)
    1925     # . . call
    1926     e8/call  lookup/disp32
    1927     # . . discard args
    1928     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1929     # check-strings-equal(curr-addr, "code", msg)
    1930     # . . push args
    1931     68/push  "F - test-maybe-get/2"/imm32
    1932     68/push  "code"/imm32
    1933     50/push-eax
    1934     # . . call
    1935     e8/call  check-strings-equal/disp32
    1936     # . . discard args
    1937     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1938 $test-maybe-get:failure:
    1939     # - search for a new key
    1940     # eax = maybe-get(table, "data", 12 bytes/row)
    1941     # . . push args
    1942     68/push  0xc/imm32/row-size
    1943     68/push  "data"/imm32
    1944     51/push-ecx
    1945     # . . call
    1946     e8/call  maybe-get/disp32
    1947     # . . discard args
    1948     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1949     # check-ints-equal(eax, 0, msg)
    1950     # . . push args
    1951     68/push  "F - test-maybe-get/3"/imm32
    1952     68/push  0/imm32
    1953     50/push-eax
    1954     # . . call
    1955     e8/call  check-ints-equal/disp32
    1956     # . . discard args
    1957     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1958 $test-maybe-get:end:
    1959     # . epilogue
    1960     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1961     5d/pop-to-ebp
    1962     c3/return
    1963
    1964 # if no row is found, return null (0)
    1965 maybe-get-slice:  # table: (addr stream {(handle array byte), T}), key: (addr slice), row-size: int -> result/eax: (addr T)
    1966     # pseudocode:
    1967     #   curr = table->data
    1968     #   max = &table->data[table->write]
    1969     #   while curr < max
    1970     #     var c: (addr array byte) = lookup(*curr)
    1971     #     if slice-equal?(key, c)
    1972     #       return curr+8
    1973     #     curr += row-size
    1974     #   return 0
    1975     #
    1976     # . prologue
    1977     55/push-ebp
    1978     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1979     # . save registers
    1980     51/push-ecx
    1981     52/push-edx
    1982     56/push-esi
    1983     # esi = table
    1984     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
    1985     # var curr/ecx: (addr handle array byte) = table->data
    1986     8d/copy-address                 1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   0xc/disp8       .                 # copy esi+12 to ecx
    1987     # var max/edx: (addr byte) = &table->data[table->write]
    1988     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           2/r32/edx   .               .                 # copy *esi to edx
    1989     8d/copy-address                 0/mod/indirect  4/rm32/sib    1/base/ecx  2/index/edx   .           2/r32/edx   .               .                 # copy ecx+edx to edx
    1990 $maybe-get-slice:search-loop:
    1991     # if (curr >= max) return null
    1992     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
    1993     73/jump-if-addr>=  $maybe-get-slice:null/disp8
    1994     # var c/eax: (addr array byte) = lookup(*curr)
    1995     # . . push args
    1996     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         .                 # push *(ecx+4)
    1997     ff          6/subop/push        0/mod/indirect  1/rm32/ecx    .           .             .           .           .               .                 # push *ecx
    1998     # . . call
    1999     e8/call  lookup/disp32
    2000     # . . discard args
    2001     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2002     # if (slice-equal?(key, c)) return curr+4
    2003     # . eax = slice-equal?(key, c)
    2004     # . . push args
    2005     50/push-eax
    2006     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    2007     # . . call
    2008     e8/call  slice-equal?/disp32
    2009     # . . discard args
    2010     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2011     # . if (eax != false) return eax = curr+8
    2012     3d/compare-eax-and  0/imm32/false
    2013     74/jump-if-=  $maybe-get-slice:mismatch/disp8
    2014     8d/copy-address                 1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   8/disp8         .                 # copy ecx+8 to eax
    2015     eb/jump  $maybe-get-slice:end/disp8
    2016 $maybe-get-slice:mismatch:
    2017     # curr += row-size
    2018     03/add                          1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   0x10/disp8      .                 # add *(ebp+16) to ecx
    2019     # loop
    2020     eb/jump  $maybe-get-slice:search-loop/disp8
    2021 $maybe-get-slice:null:
    2022     b8/copy-to-eax  0/imm32
    2023 $maybe-get-slice:end:
    2024     # . restore registers
    2025     5e/pop-to-esi
    2026     5a/pop-to-edx
    2027     59/pop-to-ecx
    2028     # . epilogue
    2029     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    2030     5d/pop-to-ebp
    2031     c3/return
    2032
    2033 test-maybe-get-slice:
    2034     # . prologue
    2035     55/push-ebp
    2036     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    2037     # - setup: create a table with one row
    2038     # var table/ecx: (stream {string, number} 24)   # 2 rows * 12 bytes/row
    2039     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x18/imm32        # subtract from esp
    2040     68/push  0x18/imm32/size
    2041     68/push  0/imm32/read
    2042     68/push  0/imm32/write
    2043     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
    2044     # insert(table, "code", 12 bytes/row, Heap)
    2045     # . . push args
    2046     68/push  Heap/imm32
    2047     68/push  0xc/imm32/row-size
    2048     68/push  "code"/imm32
    2049     51/push-ecx
    2050     # . . call
    2051     e8/call  get-or-insert/disp32
    2052     # . . discard args
    2053     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
    2054 $test-maybe-get-slice:success:
    2055     # - check for the same key, verify that it was reused
    2056     # (eax..edx) = "code"
    2057     b8/copy-to-eax  "code"/imm32
    2058     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           2/r32/edx   .               .                 # copy *eax to edx
    2059     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  2/index/edx   .           2/r32/edx   4/disp8         .                 # copy eax+edx+4 to edx
    2060     05/add-to-eax  4/imm32
    2061     # var slice/edx: slice = {eax, edx}
    2062     52/push-edx
    2063     50/push-eax
    2064     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
    2065     # eax = maybe-get-slice(table, "code" slice, 12 bytes/row)
    2066     # . . push args
    2067     68/push  0xc/imm32/row-size
    2068     52/push-edx
    2069     51/push-ecx
    2070     # . . call
    2071     e8/call  maybe-get-slice/disp32
    2072     # . . discard args
    2073     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2074     # check-ints-equal(eax - table->data, 8, msg)
    2075     # . check-ints-equal(eax - table, 20, msg)
    2076     # . . push args
    2077     68/push  "F - test-maybe-get-slice/0"/imm32
    2078     68/push  0x14/imm32
    2079     29/subtract                     3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # subtract ecx from eax
    2080     50/push-eax
    2081     # . . call
    2082     e8/call  check-ints-equal/disp32
    2083     # . . discard args
    2084     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2085     # no new row inserted
    2086     # . check-ints-equal(table->write, row-size = 12, msg)
    2087     # . . push args
    2088     68/push  "F - test-maybe-get-slice/1"/imm32
    2089     68/push  0xc/imm32/row-size
    2090     ff          6/subop/push        0/mod/indirect  1/rm32/ecx    .           .             .           .           .               .                 # push *ecx
    2091     # . . call
    2092     e8/call  check-ints-equal/disp32
    2093     # . . discard args
    2094     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2095     # var curr-addr/eax: (addr array byte) = lookup(table->data)
    2096     # . . push args
    2097     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           0x10/disp8      .                 # push *(ecx+16)
    2098     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           0xc/disp8       .                 # push *(ecx+12)
    2099     # . . call
    2100     e8/call  lookup/disp32
    2101     # . . discard args
    2102     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2103     # check-strings-equal(curr-addr, "code", msg)
    2104     # . . push args
    2105     68/push  "F - test-maybe-get-slice/2"/imm32
    2106     68/push  "code"/imm32
    2107     50/push-eax
    2108     # . . call
    2109     e8/call  check-strings-equal/disp32
    2110     # . . discard args
    2111     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2112 $test-maybe-get-slice:failure:
    2113     # - search for a new key
    2114     # (eax..edx) = "data"
    2115     b8/copy-to-eax  "data"/imm32
    2116     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           2/r32/edx   .               .                 # copy *eax to edx
    2117     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  2/index/edx   .           2/r32/edx   4/disp8         .                 # copy eax+edx+4 to edx
    2118     05/add-to-eax  4/imm32
    2119     # var slice/edx: slice = {eax, edx}
    2120     52/push-edx
    2121     50/push-eax
    2122     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
    2123     # eax = maybe-get-slice(table, "data" slice, 12 bytes/row)
    2124     # . . push args
    2125     68/push  0xc/imm32/row-size
    2126     52/push-edx
    2127     51/push-ecx
    2128     # . . call
    2129     e8/call  maybe-get-slice/disp32
    2130     # . . discard args
    2131     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2132     # check-ints-equal(eax, 0, msg)
    2133     # . . push args
    2134     68/push  "F - test-maybe-get-slice/3"/imm32
    2135     68/push  0/imm32
    2136     50/push-eax
    2137     # . . call
    2138     e8/call  check-ints-equal/disp32
    2139     # . . discard args
    2140     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2141 $test-maybe-get-slice:end:
    2142     # . epilogue
    2143     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    2144     5d/pop-to-ebp
    2145     c3/return
    2146
    2147 # . . vim:nowrap:textwidth=0