about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik Agaram <vc@akkartik.com>2019-05-27 11:11:36 -0700
committerKartik Agaram <vc@akkartik.com>2019-05-27 11:11:36 -0700
commit965dd1bf56253d4b6104a2f57b48d06f6287fe31 (patch)
tree61a7989b83c26bf6cc18349f374fb1f3155b969e
parent7c575de40da307ccb676c6aafb433f40ecafde41 (diff)
downloadmu-965dd1bf56253d4b6104a2f57b48d06f6287fe31.tar.gz
.
'get-or-insert-stream' is now the more generic 'get-or-insert' that can
handle tables of any value type. But callers have to be careful to cast
values to the right type.
-rw-r--r--subx/062write-stream.subx2
-rwxr-xr-xsubx/apps/assortbin24100 -> 24081 bytes
-rw-r--r--subx/apps/assort.subx53
-rwxr-xr-xsubx/apps/crenshaw2-1bin21307 -> 21308 bytes
-rwxr-xr-xsubx/apps/crenshaw2-1bbin21866 -> 21867 bytes
-rwxr-xr-xsubx/apps/factorialbin20223 -> 20224 bytes
-rwxr-xr-xsubx/apps/handlebin21029 -> 21030 bytes
-rwxr-xr-xsubx/apps/hexbin24316 -> 24317 bytes
-rwxr-xr-xsubx/apps/packbin39782 -> 39732 bytes
-rw-r--r--subx/apps/subx-common.subx222
-rwxr-xr-xsubx/apps/surveybin21901 -> 21850 bytes
-rw-r--r--subx/apps/survey.subx2
12 files changed, 143 insertions, 136 deletions
diff --git a/subx/062write-stream.subx b/subx/062write-stream.subx
index 92c67dc2..c4ec8db2 100644
--- a/subx/062write-stream.subx
+++ b/subx/062write-stream.subx
@@ -122,7 +122,7 @@ _write-stream:  # fd : int, s : (address stream) -> <void>
 $_write-stream:abort:
     # . _write(2/stderr, error)
     # . . push args
-    68/push  "_write-stream: failed to write to file"/imm32
+    68/push  "_write-stream: failed to write to file\n"/imm32
     68/push  2/imm32/stderr
     # . . call
     e8/call  _write/disp32
diff --git a/subx/apps/assort b/subx/apps/assort
index e9228e59..32023675 100755
--- a/subx/apps/assort
+++ b/subx/apps/assort
Binary files differdiff --git a/subx/apps/assort.subx b/subx/apps/assort.subx
index 14956d82..8c5fd184 100644
--- a/subx/apps/assort.subx
+++ b/subx/apps/assort.subx
@@ -453,13 +453,14 @@ read-segments:  # in : (address buffered-file), table : (address stream {string,
     #       continue
     #     if slice-equal?(word-slice, "==")
     #       var segment-name = next-word(line)
-    #       curr-segment = get-or-insert-stream(table, segment-name, Segment-size)
-    #       if curr-segment->write == 0
-    #         rewind-stream(line)
-    #         write-stream(curr-segment, line)
-    #     else
-    #       rewind-stream(line)
-    #       write-stream(curr-segment, line)  # abort if curr-segment overflows
+    #       segment-slot = get-or-insert(table, segment-name, row-size=8)
+    #       curr-segment = *segment-slot
+    #       if curr-segment != 0
+    #         continue
+    #       curr-segment = new-stream(Segment-size)
+    #       *segment-slot = curr-segment
+    #     rewind-stream(line)
+    #     write-stream(curr-segment, line)  # abort if curr-segment overflows
     #
     # word-slice and segment-name are both slices with disjoint lifetimes, so
     # we'll use the same address for them.
@@ -609,10 +610,7 @@ $read-segments:check-for-segment-header:
 #?     # . . discard args
 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 #?     # }}}
-    # if slice-equal?(word-slice, "==")
-    #   segment-name = next-word(line)
-    #   curr-segment = get-or-insert-stream(table, segment-name)
-    #   if (curr-segment->write > 0) continue
+    # if !slice-equal?(word-slice, "==") goto next check
     # . EAX = slice-equal?(word-slice, "==")
     # . . push args
     68/push  "=="/imm32
@@ -624,7 +622,7 @@ $read-segments:check-for-segment-header:
     # . if (EAX == 0) goto check3
     3d/compare-EAX-and  0/imm32
     0f 84/jump-if-equal  $read-segments:regular-line/disp32
-    # . next-word(line, segment-name)
+    # segment-name = next-word(line)
     # . . push args
     52/push-EDX
     51/push-ECX
@@ -674,21 +672,38 @@ $read-segments:check-for-segment-header:
 #?     # . . discard args
 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 #?     # }}}
-    # . EAX = get-or-insert-stream(table, segment-name, Segment-size)
+    # segment-slot/EAX = get-or-insert(table, segment-name, value-size=8)
     # . . push args
-    ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Segment-size/disp32               # push *Segment-size
+    68/push  8/imm32/value-size
     52/push-EDX
     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
     # . . call
-    e8/call  get-or-insert-stream/disp32
+    e8/call  get-or-insert/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+    # curr-segment = *segment-slot
+    8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           3/r32/EBX   .               .                 # copy *EAX to EBX
+    # if (curr-segment != 0) continue
+    81          7/subop/compare     3/mod/direct    3/rm32/EBX    .           .             .           .           .               0/imm32           # compare EBX
+    0f 85/jump-if-not-equal  $read-segments:loop/disp32
+    # curr-segment = new-stream(Heap, Segment-size, 1)
+    # . save segment-slot
+    50/push-EAX
+    # . EAX = new-stream(Heap, Segment-size, 1)
+    # . . push args
+    68/push  1/imm32
+    ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Segment-size/disp32               # push *Segment-size
+    68/push  Heap/imm32
+    # . . call
+    e8/call  new-stream/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
     # . curr-segment = EAX
     89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EBX
-    # . if (curr-segment->write > 0) continue
-    8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # copy *EAX to EAX
-    3d/compare-EAX-and  0/imm32
-    0f 8f/jump-if-greater  $read-segments:loop/disp32
+    # . restore segment-slot
+    58/pop-to-EAX
+    # *segment-slot = curr-segment
+    89/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           3/r32/EBX   .               .                 # copy EBX to *EAX
     # fall through
 $read-segments:regular-line:
     # rewind-stream(line)
diff --git a/subx/apps/crenshaw2-1 b/subx/apps/crenshaw2-1
index 25fc3d05..def09e48 100755
--- a/subx/apps/crenshaw2-1
+++ b/subx/apps/crenshaw2-1
Binary files differdiff --git a/subx/apps/crenshaw2-1b b/subx/apps/crenshaw2-1b
index 4c24542b..705ac0ad 100755
--- a/subx/apps/crenshaw2-1b
+++ b/subx/apps/crenshaw2-1b
Binary files differdiff --git a/subx/apps/factorial b/subx/apps/factorial
index 26b881fd..2330d6ad 100755
--- a/subx/apps/factorial
+++ b/subx/apps/factorial
Binary files differdiff --git a/subx/apps/handle b/subx/apps/handle
index 916c6ec1..974e57a2 100755
--- a/subx/apps/handle
+++ b/subx/apps/handle
Binary files differdiff --git a/subx/apps/hex b/subx/apps/hex
index 13c58d02..4bce98eb 100755
--- a/subx/apps/hex
+++ b/subx/apps/hex
Binary files differdiff --git a/subx/apps/pack b/subx/apps/pack
index f7363a10..d1e98aa6 100755
--- a/subx/apps/pack
+++ b/subx/apps/pack
Binary files differdiff --git a/subx/apps/subx-common.subx b/subx/apps/subx-common.subx
index 86cf10fe..363ac24a 100644
--- a/subx/apps/subx-common.subx
+++ b/subx/apps/subx-common.subx
@@ -5,25 +5,26 @@
 # . op          subop               mod             rm32          base        index         scale       r32
 # . 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
 
-# scan a 'table' of (string, stream) rows for a row matching 's'
-# if it doesn't exist, add a new row with a stream of size 'n'
-# if there isn't room, abort
+# 'table' is a stream of (key, value) rows
+# keys are always strings
+# values may be any type but have size 'n'
+# scan 'table' for a row with a key 's' and return the address of the corresponding value
+# if no row is found, save 's' in the next available row
+# if there are no rows free, return null
 # TODO: pass in an allocation descriptor
-get-or-insert-stream:  # table : (address stream {string, (address stream byte)}), s : (address slice), n : int -> EAX : (address stream)
+get-or-insert:  # table : (address stream {string, _}), s : (address slice), n : int -> EAX : (address _)
     # pseudocode:
     #   curr = table->data
     #   max = &table->data[table->write]
     #   while curr < max
     #     if slice-equal?(s, *curr)
-    #       return *(curr+4)
-    #     curr += 8
-    #   if table->write < table->length
-    #     *max = slice-to-string(Heap, s)
-    #     result = new-stream(Heap, n, 1)
-    #     *(max+4) = result
-    #     table->write += 8
-    #     return result
-    #   return 0
+    #       return curr+4
+    #     curr += n
+    #   if table->write >= table->length
+    #     abort
+    #   *max = slice-to-string(Heap, s)
+    #   table->write += n
+    #   return max+4
     #
     # . prolog
     55/push-EBP
@@ -39,10 +40,10 @@ get-or-insert-stream:  # table : (address stream {string, (address stream byte)}
     # max/EDX = table->data + table->write
     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           2/r32/EDX   .               .                 # copy *ESI to EDX
     8d/copy-address                 0/mod/indirect  4/rm32/sib    1/base/ECX  2/index/EDX   .           2/r32/EDX   .               .                 # copy ECX+EDX to EDX
-$get-or-insert-stream:search-loop:
+$get-or-insert:search-loop:
     # if (curr >= max) break
     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
-    7d/jump-if-greater-or-equal  $get-or-insert-stream:not-found/disp8
+    7d/jump-if-greater-or-equal  $get-or-insert:not-found/disp8
     # if (slice-equal?(s, *curr)) return *(curr+4)
     # . EAX = slice-equal?(s, *curr)
     # . . push args
@@ -52,23 +53,23 @@ $get-or-insert-stream:search-loop:
     e8/call  slice-equal?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-    # . if (EAX != 0) return EAX = *(curr+4)
+    # . if (EAX != 0) return EAX = curr+4
     3d/compare-EAX-and  0/imm32
-    74/jump-if-equal  $get-or-insert-stream:mismatch/disp8
-    8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
-    eb/jump  $get-or-insert-stream:end/disp8
-$get-or-insert-stream:mismatch:
-    # curr += 8
-    81          0/subop/add         3/mod/direct    1/rm32/ECX    .           .             .           .           .               8/imm32           # add to ECX
+    74/jump-if-equal  $get-or-insert:mismatch/disp8
+    8d/copy-address                 1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy ECX+4 to EAX
+    eb/jump  $get-or-insert:end/disp8
+$get-or-insert:mismatch:
+    # curr += n
+    03/add                          1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   0x10/disp8      .                 # add *(EBP+16) to ECX
     # loop
-    eb/jump  $get-or-insert-stream:search-loop/disp8
-$get-or-insert-stream:not-found:
+    eb/jump  $get-or-insert:search-loop/disp8
+$get-or-insert:not-found:
     # result/EAX = 0
     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
     # if (table->write >= table->length) abort
     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # copy *ESI to ECX
     3b/compare                      1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   8/disp8         .                 # compare ECX with *(ESI+8)
-    7d/jump-if-greater-or-equal  $get-or-insert-stream:abort/disp8
+    7d/jump-if-greater-or-equal  $get-or-insert:abort/disp8
     # *max = slice-to-string(Heap, s)
     # . EAX = slice-to-string(Heap, s)
     # . . push args
@@ -80,20 +81,17 @@ $get-or-insert-stream:not-found:
     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     # . *max = EAX
     89/copy                         0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/EAX   .               .                 # copy EAX to *EDX
-    # result/EAX = new-stream(Heap, n, 1)
-    # . . push args
-    68/push  1/imm32
-    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
-    68/push  Heap/imm32
-    # . . call
-    e8/call  new-stream/disp32
-    # . . discard args
-    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-    # *(max+4) = result
-    89/copy                         1/mod/*+disp8   2/rm32/EDX    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDX+4)
-    # table->write += 8
-    81          0/subop/add         0/mod/indirect  6/rm32/ESI    .           .             .           .           .               8/imm32           # add to *ESI
-$get-or-insert-stream:end:
+    # table->write += n
+    # . EAX = n
+    8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   0x10/disp8      .                 # copy *(EBP+16) to EAX
+    # . table->write += EAX
+    01/add                          0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # add EAX to *ESI
+    # return max+4
+    # . EAX = max
+    89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           2/r32/EDX   .               .                 # copy EDX to EAX
+    # . EAX += 4
+    05/add-to-EAX  4/imm32
+$get-or-insert:end:
     # . restore registers
     5e/pop-to-ESI
     5a/pop-to-EDX
@@ -103,10 +101,10 @@ $get-or-insert-stream:end:
     5d/pop-to-EBP
     c3/return
 
-$get-or-insert-stream:abort:
+$get-or-insert:abort:
     # . _write(2/stderr, error)
     # . . push args
-    68/push  "get-or-insert-stream: too many segments"/imm32
+    68/push  "get-or-insert: too many segments"/imm32
     68/push  2/imm32/stderr
     # . . call
     e8/call  _write/disp32
@@ -118,11 +116,11 @@ $get-or-insert-stream:abort:
     cd/syscall  0x80/imm8
     # never gets here
 
-test-get-or-insert-stream:
+test-get-or-insert:
     # . prolog
     55/push-EBP
     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-    # var table/ECX : (address stream byte) = stream(2 * 8)
+    # var table/ECX : (address stream {string, number}) = stream(2 rows * 8 bytes)
     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # subtract from ESP
     68/push  0x10/imm32/length
     68/push  0/imm32/read
@@ -132,140 +130,134 @@ test-get-or-insert-stream:
     68/push  _test-code-segment-end/imm32/end
     68/push  _test-code-segment/imm32/start
     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
-$test-get-or-insert-stream:first-call:
-    # - start with an empty table, insert one segment, verify that it was inserted
-    # segment/EAX = get-or-insert-stream(table, "code" slice, 10)
+$test-get-or-insert:first-call:
+    # - start with an empty table, insert one key, verify that it was inserted
+    # EAX = get-or-insert(table, "code" slice, 8 bytes per row)
     # . . push args
-    68/push  0xa/imm32/segment-length
+    68/push  8/imm32/row-size
     52/push-EDX
     51/push-ECX
     # . . call
-    e8/call  get-or-insert-stream/disp32
+    e8/call  get-or-insert/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-    # save segment
-    50/push-EAX
-    # if (segment != 0) goto next check
-    3d/compare-EAX-and  0/imm32
-    75/jump-if-not-equal  $test-get-or-insert-stream:check1/disp8
-    # fail test
-    # . _write(2/stderr, msg)
-    # . . push args
-    68/push  "F - test-get-or-insert-stream/0\n"/imm32
-    68/push  2/imm32/stderr
-    # . . call
-    e8/call  _write/disp32
-    # . . discard args
-    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-    # . increment Num-test-failures
-    ff          0/subop/increment   0/mod/indirect  5/rm32/.disp32            .             .           .           Num-test-failures/disp32          # increment *Num-test-failures
-    e9/jump  $test-get-or-insert-stream:end/disp32
-$test-get-or-insert-stream:check1:
-    # check-ints-equal(segment->length, 10, msg)
+    # check-ints-equal(EAX - table->data, 4, msg)  # first row's value slot returned
+    # . check-ints-equal(EAX - table, 16, msg)
     # . . push args
-    68/push  "F - test-get-or-insert-stream/1"/imm32
-    68/push  0xa/imm32/segment-length
-    ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           8/disp8         .                 # push *(EAX+8)
+    68/push  "F - test-get-or-insert/0"/imm32
+    68/push  0x10/imm32
+    29/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # subtract ECX from EAX
+    50/push-EAX
     # . . call
     e8/call  check-ints-equal/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-$test-get-or-insert-stream:check2:
+$test-get-or-insert:check2:
     # check-ints-equal(table->write, row-size = 8, msg)
     # . . push args
-    68/push  "F - test-get-or-insert-stream/2"/imm32
+    68/push  "F - test-get-or-insert/1"/imm32
     68/push  8/imm32/row-size
     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
     # . . call
     e8/call  check-ints-equal/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-    # EAX = string-equal?(*table->data, "code")
+    # check-string-equal(*table->data, "code", msg)
     # . . push args
+    68/push  "F - test-get-or-insert/2"/imm32
     68/push  "code"/imm32
     ff          6/subop/push        1/mod/*+disp8   1/rm32/ECX    .           .             .           .           0xc/disp8       .                 # push *(ECX+12)
     # . . call
-    e8/call  string-equal?/disp32
-    # . . discard args
-    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-    # check-ints-equal(EAX, 1, msg)
-    # . . push args
-    68/push  "F - test-get-or-insert-stream/3"/imm32
-    68/push  1/imm32
-    50/push-EAX
-    # . . call
-    e8/call  check-ints-equal/disp32
+    e8/call  check-string-equal/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-$test-get-or-insert-stream:check3:
-    # stream/EAX = *(table->data+4)
-    8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   0x10/disp8      .                 # copy *(ECX+16) to EAX
-    # check-ints-equal(stream->length, 10, msg)
+$test-get-or-insert:second-call:
+    # - insert the same key again, verify that it was reused
+    # EAX = get-or-insert(table, "code" slice, 8 bytes per row)
     # . . push args
-    68/push  "F - test-get-or-insert-stream/4"/imm32
-    68/push  0xa/imm32/segment-size
-    ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           8/disp8         .                 # push *(EAX+8)
-    # . . call
-    e8/call  check-ints-equal/disp32
-    # . . discard args
-    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-$test-get-or-insert-stream:second-call:
-    # - insert the same segment name again, verify that it was reused
-    # segment2/EAX = get-or-insert-stream(table, "code" slice, 8)
-    # . . push args
-    68/push  8/imm32/segment-length
+    68/push  8/imm32/row-size
     52/push-EDX
     51/push-ECX
     # . . call
-    e8/call  get-or-insert-stream/disp32
+    e8/call  get-or-insert/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-    # restore old segment1
-    5a/pop-to-EDX
-    # check-ints-equal(segment2/EAX, segment1/EDX, msg)
+    # check-ints-equal(EAX - table->data, 4, msg)
+    # . check-ints-equal(EAX - table, 16, msg)
     # . . push args
-    68/push  "F - test-get-or-insert-stream/5"/imm32
-    52/push-EDX
+    68/push  "F - test-get-or-insert/3"/imm32
+    68/push  0x10/imm32
+    29/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # subtract ECX from EAX
     50/push-EAX
     # . . call
     e8/call  check-ints-equal/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-    # no change to table size
+    # no new row inserted
     # . check-ints-equal(table->write, row-size = 8, msg)
     # . . push args
-    68/push  "F - test-get-or-insert-stream/6"/imm32
+    68/push  "F - test-get-or-insert/4"/imm32
     68/push  8/imm32/row-size
     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
     # . . call
     e8/call  check-ints-equal/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-$test-get-or-insert-stream:third-call:
-    # - insert a new segment name, verify that it was inserted
+    # check-string-equal(*table->data, "code", msg)
+    # . . push args
+    68/push  "F - test-get-or-insert/5"/imm32
+    68/push  "code"/imm32
+    ff          6/subop/push        1/mod/*+disp8   1/rm32/ECX    .           .             .           .           0xc/disp8       .                 # push *(ECX+12)
+    # . . call
+    e8/call  check-string-equal/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+$test-get-or-insert:third-call:
+    # - insert a new key, verify that it was inserted
     # EDX : (address slice) = "data"
     c7          0/subop/copy        0/mod/indirect  2/rm32/EDX    .           .             .           .           .               _test-data-segment/imm32  # copy to *EDX
     c7          0/subop/copy        1/mod/*+disp8   2/rm32/EDX    .           .             .           .           4/disp8         _test-data-segment-end/imm32  # copy to *(EDX+4)
-    # segment2/EAX = get-or-insert-stream(table, "data" slice, 8)
+    # EAX = get-or-insert(table, "data" slice, 8 bytes per row)
     # . . push args
-    68/push  8/imm32/segment-length
+    68/push  8/imm32/row-size
     52/push-EDX
     51/push-ECX
     # . . call
-    e8/call  get-or-insert-stream/disp32
+    e8/call  get-or-insert/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
     # table gets a new row
-    # . check-ints-equal(table->write, 2 rows = 16, msg)
+    # check-ints-equal(EAX - table->data, 12, msg)  # second row's value slot returned
+    # . check-ints-equal(EAX - table, 24, msg)
     # . . push args
-    68/push  "F - test-get-or-insert-stream/7"/imm32
+    68/push  "F - test-get-or-insert/6"/imm32
+    68/push  0x18/imm32
+    29/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # subtract ECX from EAX
+    50/push-EAX
+    # . . call
+    e8/call  check-ints-equal/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+    # check-ints-equal(table->write, 2 rows = 16, msg)
+    # . . push args
+    68/push  "F - test-get-or-insert/7"/imm32
     68/push  0x10/imm32/two-rows
     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
     # . . call
     e8/call  check-ints-equal/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-$test-get-or-insert-stream:end:
+    # check-string-equal(*table->data+8, "data", msg)
+    # check-string-equal(*(table+20), "data", msg)
+    # . . push args
+    68/push  "F - test-get-or-insert/8"/imm32
+    68/push  "data"/imm32
+    ff          6/subop/push        1/mod/*+disp8   1/rm32/ECX    .           .             .           .           0x14/disp8      .                 # push *(ECX+20)
+    # . . call
+    e8/call  check-string-equal/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+$test-get-or-insert:end:
     # . epilog
     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
     5d/pop-to-EBP
diff --git a/subx/apps/survey b/subx/apps/survey
index fb72c78f..9578a1d6 100755
--- a/subx/apps/survey
+++ b/subx/apps/survey
Binary files differdiff --git a/subx/apps/survey.subx b/subx/apps/survey.subx
index e0f04e22..186dea3a 100644
--- a/subx/apps/survey.subx
+++ b/subx/apps/survey.subx
@@ -49,7 +49,7 @@ Entry:
     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 
     # for debugging: run a single test
-#?     e8/call test-convert/disp32
+#?     e8/call test-get-or-insert/disp32
 #?     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
 #?     eb/jump  $main:end/disp8