From ec73ed1230d75deb0f913a32617c9f1e0a5ca640 Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Fri, 10 Jul 2020 23:44:10 -0700 Subject: 6631 --- html/131table.subx.html | 2212 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2212 insertions(+) create mode 100644 html/131table.subx.html (limited to 'html/131table.subx.html') diff --git a/html/131table.subx.html b/html/131table.subx.html new file mode 100644 index 00000000..719b91b3 --- /dev/null +++ b/html/131table.subx.html @@ -0,0 +1,2212 @@ + + + + +Mu - 131table.subx + + + + + + + + + + +https://github.com/akkartik/mu/blob/master/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
+
+ + + -- cgit 1.4.1-2-gfad0