about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--linux/101_write.subx6
-rw-r--r--linux/102test.subx26
-rw-r--r--linux/103kernel-string-equal.subx10
-rw-r--r--linux/104new-segment.subx40
-rw-r--r--linux/105string-equal.subx12
-rw-r--r--linux/120allocate.subx24
-rw-r--r--linux/131table.subx311
-rwxr-xr-xlinux/assortbin46801 -> 47302 bytes
-rwxr-xr-xlinux/bracesbin48854 -> 49355 bytes
-rwxr-xr-xlinux/callsbin53837 -> 54338 bytes
-rwxr-xr-xlinux/dquotesbin50423 -> 50924 bytes
-rwxr-xr-xlinux/hexbin48981 -> 49482 bytes
-rwxr-xr-xlinux/labels_baremetalbin51541 -> 52042 bytes
-rw-r--r--linux/labels_baremetal.subx8
-rwxr-xr-xlinux/mubin608888 -> 609389 bytes
-rwxr-xr-xlinux/packbin59811 -> 60312 bytes
-rwxr-xr-xlinux/sigilsbin61208 -> 61709 bytes
-rwxr-xr-xlinux/survey_baremetalbin47482 -> 47983 bytes
-rw-r--r--linux/survey_baremetal.subx6
-rwxr-xr-xlinux/survey_elfbin56375 -> 56876 bytes
-rw-r--r--linux/survey_elf.subx8
-rwxr-xr-xlinux/test_apps6
-rwxr-xr-xlinux/testsbin45592 -> 46093 bytes
-rw-r--r--subx.md4
24 files changed, 385 insertions, 76 deletions
diff --git a/linux/101_write.subx b/linux/101_write.subx
index e1d64756..84718265 100644
--- a/linux/101_write.subx
+++ b/linux/101_write.subx
@@ -5,9 +5,9 @@
 # . 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
 
-Entry:  # just exit; can't test _write just yet
-    bb/copy-to-ebx  0/imm32
-    e8/call  syscall_exit/disp32
+#? Entry:  # just exit; can't test _write just yet
+#?     bb/copy-to-ebx  0/imm32
+#?     e8/call  syscall_exit/disp32
 
 _write:  # fd: int, s: (addr array byte)
     # . prologue
diff --git a/linux/102test.subx b/linux/102test.subx
index 90207e29..51ca2e12 100644
--- a/linux/102test.subx
+++ b/linux/102test.subx
@@ -5,19 +5,19 @@
 # . 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
 
-Entry:  # manual test
-    # check-ints-equal(34, 34)
-    # . . push args
-    68/push  "error in check-ints-equal"/imm32
-    68/push  34/imm32
-    68/push  34/imm32
-    # . . call
-    e8/call  check-ints-equal/disp32
-    # . . discard args
-    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-    # syscall_exit(0)
-    bb/copy-to-ebx  0/imm32
-    e8/call  syscall_exit/disp32
+#? Entry:  # manual test
+#?     # check-ints-equal(34, 34)
+#?     # . . push args
+#?     68/push  "error in check-ints-equal"/imm32
+#?     68/push  34/imm32
+#?     68/push  34/imm32
+#?     # . . call
+#?     e8/call  check-ints-equal/disp32
+#?     # . . discard args
+#?     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+#?     # syscall_exit(0)
+#?     bb/copy-to-ebx  0/imm32
+#?     e8/call  syscall_exit/disp32
 
 # print msg to stderr if a != b, otherwise print "."
 check-ints-equal:  # a: int, b: int, msg: (addr array byte)
diff --git a/linux/103kernel-string-equal.subx b/linux/103kernel-string-equal.subx
index 29dbb6e2..a5da1747 100644
--- a/linux/103kernel-string-equal.subx
+++ b/linux/103kernel-string-equal.subx
@@ -22,11 +22,11 @@
 # . 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
 
-Entry:  # run all tests
-    e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
-    # syscall_exit(Num-test-failures)
-    8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/ebx   Num-test-failures/disp32          # copy *Num-test-failures to ebx
-    e8/call  syscall_exit/disp32
+#? Entry:  # run all tests
+#?     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
+#?     # syscall_exit(Num-test-failures)
+#?     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/ebx   Num-test-failures/disp32          # copy *Num-test-failures to ebx
+#?     e8/call  syscall_exit/disp32
 
 kernel-string-equal?:  # s: (addr kernel-string), benchmark: (addr array byte) -> result/eax: boolean
     # pseudocode:
diff --git a/linux/104new-segment.subx b/linux/104new-segment.subx
index 723e1afb..5a79ff19 100644
--- a/linux/104new-segment.subx
+++ b/linux/104new-segment.subx
@@ -16,26 +16,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
 
-Entry:   # manual test
-    # var ad/ecx: allocation-descriptor
-    68/push  0/imm32/limit
-    68/push  0/imm32/curr
-    89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
-    # new-segment(0x1000, ad)
-    # . . push args
-    51/push-ecx
-    68/push  0x1000/imm32
-    # . . call
-    e8/call  new-segment/disp32
-    # . . discard args
-    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-    # var eax: (addr _) = ad->curr
-    8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
-    # write to *eax to check that we have access to the newly-allocated segment
-    c7          0/subop/copy        0/mod/direct    0/rm32/eax    .           .             .           .           .               0x34/imm32        # copy to *eax
-    # syscall_exit(eax)
-    89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # copy eax to ebx
-    e8/call  syscall_exit/disp32
+#? Entry:   # manual test
+#?     # var ad/ecx: allocation-descriptor
+#?     68/push  0/imm32/limit
+#?     68/push  0/imm32/curr
+#?     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
+#?     # new-segment(0x1000, ad)
+#?     # . . push args
+#?     51/push-ecx
+#?     68/push  0x1000/imm32
+#?     # . . call
+#?     e8/call  new-segment/disp32
+#?     # . . discard args
+#?     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+#?     # var eax: (addr _) = ad->curr
+#?     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
+#?     # write to *eax to check that we have access to the newly-allocated segment
+#?     c7          0/subop/copy        0/mod/direct    0/rm32/eax    .           .             .           .           .               0x34/imm32        # copy to *eax
+#?     # syscall_exit(eax)
+#?     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # copy eax to ebx
+#?     e8/call  syscall_exit/disp32
 
 new-segment:  # len: int, ad: (addr allocation-descriptor)
     # . prologue
diff --git a/linux/105string-equal.subx b/linux/105string-equal.subx
index ae753855..38e88f76 100644
--- a/linux/105string-equal.subx
+++ b/linux/105string-equal.subx
@@ -5,12 +5,12 @@
 # . 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
 
-Entry:  # run all tests
-#?     e8/call test-compare-equal-strings/disp32
-    e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
-    # syscall_exit(Num-test-failures)
-    8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/ebx   Num-test-failures/disp32          # copy *Num-test-failures to ebx
-    e8/call  syscall_exit/disp32
+#? Entry:  # run all tests
+#? #?     e8/call test-compare-equal-strings/disp32
+#?     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
+#?     # syscall_exit(Num-test-failures)
+#?     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/ebx   Num-test-failures/disp32          # copy *Num-test-failures to ebx
+#?     e8/call  syscall_exit/disp32
 
 string-equal?:  # s: (addr array byte), benchmark: (addr array byte) -> result/eax: boolean
     # pseudocode:
diff --git a/linux/120allocate.subx b/linux/120allocate.subx
index 254c5f33..58c6c26d 100644
--- a/linux/120allocate.subx
+++ b/linux/120allocate.subx
@@ -42,18 +42,18 @@ Next-alloc-id:  # int
 # . 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
 
-# Let's start initializing the default allocation descriptor.
-
-Entry:
-    # initialize heap
-    # . Heap = new-segment(Heap-size)
-    # . . push args
-    68/push  Heap/imm32
-    ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Heap-size/disp32                  # push *Heap-size
-    # . . call
-    e8/call  new-segment/disp32
-    # . . discard args
-    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+#? # Let's start initializing the default allocation descriptor.
+#? 
+#? Entry:
+#?     # initialize heap
+#?     # . Heap = new-segment(Heap-size)
+#?     # . . push args
+#?     68/push  Heap/imm32
+#?     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Heap-size/disp32                  # push *Heap-size
+#?     # . . call
+#?     e8/call  new-segment/disp32
+#?     # . . discard args
+#?     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 
     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
 $array-equal-main:end:
diff --git a/linux/131table.subx b/linux/131table.subx
index aa47204b..4df7a7a9 100644
--- a/linux/131table.subx
+++ b/linux/131table.subx
@@ -700,6 +700,154 @@ $test-get-or-insert:end:
     5d/pop-to-ebp
     c3/return
 
+# save 'key' to the next available row
+# if key already exists, abort
+# if there are no rows free, abort
+# return the address of the value
+insert-or-abort:  # table: (addr stream {(handle array byte), T}), key: (addr array byte), row-size: int, ad: (addr allocation-descriptor) -> result/eax: (addr T)
+    # pseudocode:
+    #   curr = table->data
+    #   max = &table->data[table->write]
+    #   while curr < max
+    #     var c: (addr array byte) = lookup(*curr)
+    #     if string-equal?(key, c)
+    #       abort
+    #     curr += row-size
+    #   if table->write >= table->size
+    #     abort
+    #   zero-out(max, row-size)
+    #   copy-array(ad, key, max)
+    #   table->write += row-size
+    #   return max+8
+    #
+    # . prologue
+    55/push-ebp
+    89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
+    # . save registers
+    51/push-ecx
+    52/push-edx
+    56/push-esi
+    # esi = table
+    8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
+    # var curr/ecx: (addr handle array byte) = table->data
+    8d/copy-address                 1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   0xc/disp8       .                 # copy esi+12 to ecx
+    # var max/edx: (addr byte) = &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
+$insert-or-abort:search-loop:
+    # if (curr >= max) break
+    39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
+    73/jump-if-addr>=  $insert-or-abort:not-found/disp8
+    # var c/eax: (addr array byte) = lookup(*curr)
+    # . . push args
+    ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         .                 # push *(ecx+4)
+    ff          6/subop/push        0/mod/indirect  1/rm32/ecx    .           .             .           .           .               .                 # push *ecx
+    # . . call
+    e8/call  lookup/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # if (string-equal?(key, c)) abort
+    # . eax = string-equal?(key, c)
+    # . . push args
+    50/push-eax
+    ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
+    # . . call
+    e8/call  string-equal?/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # . if (eax != false) abort
+    3d/compare-eax-and  0/imm32/false
+    0f 85/jump-if-!=  $insert-or-abort:error-duplicate/disp32
+    # curr += row-size
+    03/add                          1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   0x10/disp8      .                 # add *(ebp+16) to ecx
+    # loop
+    eb/jump  $insert-or-abort:search-loop/disp8
+$insert-or-abort:not-found:
+    # if (table->write >= table->size) 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)
+    0f 83/jump-if-addr>=  $insert-or-abort:error-full/disp32
+    # zero-out(max, row-size)
+    # . . push args
+    ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
+    52/push-edx
+    # . . call
+    e8/call  zero-out/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # copy-array(ad, key, max)
+    # . . push args
+    52/push-edx
+    ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
+    ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x14/disp8      .                 # push *(ebp+20)
+    # . . call
+    e8/call  copy-array/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+    # table->write += row-size
+    # . eax = row-size
+    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+8
+    # . eax = max
+    89/copy                         3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # copy edx to eax
+    # . eax += 8
+    05/add-to-eax  8/imm32
+$insert-or-abort:end:
+    # . restore registers
+    5e/pop-to-esi
+    5a/pop-to-edx
+    59/pop-to-ecx
+    # . epilogue
+    89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
+    5d/pop-to-ebp
+    c3/return
+
+$insert-or-abort:error-duplicate:
+    # . _write(2/stderr, error)
+    # . . push args
+    68/push  "insert-or-abort: key already exists: "/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
+    # . _write(2/stderr, key)
+    # . . push args
+    ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
+    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
+    # . _write(2/stderr, "\n")
+    # . . push args
+    68/push  "\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
+    # . syscall_exit(1)
+    bb/copy-to-ebx  1/imm32
+    e8/call  syscall_exit/disp32
+    # never gets here
+
+$insert-or-abort:error-full:
+    # . _write(2/stderr, error)
+    # . . push args
+    68/push  "insert-or-abort: table is full\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
+    # . syscall_exit(1)
+    bb/copy-to-ebx  1/imm32
+    e8/call  syscall_exit/disp32
+    # never gets here
+
 # if no row is found, save 'key' to the next available row
 # if there are no rows free, abort
 # return the address of the value
@@ -1321,6 +1469,169 @@ $test-get-or-insert-slice:end:
     5d/pop-to-ebp
     c3/return
 
+# if no row is found, save 'key' in the next available row
+# if there are no rows free, abort
+insert-slice-or-abort:  # table: (addr stream {(handle array byte), T}), key: (addr slice), row-size: int, ad: (addr allocation-descriptor) -> result/eax: (addr T)
+    # pseudocode:
+    #   curr = table->data
+    #   max = &table->data[table->write]
+    #   while curr < max
+    #     var c: (addr array byte) = lookup(*curr)
+    #     if slice-equal?(key, *curr)
+    #       return curr+8
+    #     curr += row-size
+    #   if table->write >= table->size
+    #     abort
+    #   zero-out(max, row-size)
+    #   slice-to-string(ad, key, max)
+    #   table->write += row-size
+    #   return max+8
+    #
+    # . prologue
+    55/push-ebp
+    89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
+    # . save registers
+    51/push-ecx
+    52/push-edx
+    56/push-esi
+    # esi = table
+    8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
+    # var curr/ecx: (addr handle array byte) = table->data
+    8d/copy-address                 1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   0xc/disp8       .                 # copy esi+12 to ecx
+    # var max/edx: (addr byte) = &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
+$insert-slice-or-abort:search-loop:
+    # if (curr >= max) break
+    39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
+    73/jump-if-addr>=  $insert-slice-or-abort:not-found/disp8
+    # var c/eax: (addr array byte) = lookup(*curr)
+    # . . push args
+    ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         .                 # push *(ecx+4)
+    ff          6/subop/push        0/mod/indirect  1/rm32/ecx    .           .             .           .           .               .                 # push *ecx
+    # . . call
+    e8/call  lookup/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # if (slice-equal?(key, c)) abort
+    # . eax = slice-equal?(key, c)
+    # . . push args
+    50/push-eax
+    ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
+    # . . call
+    e8/call  slice-equal?/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # . if (eax != false) abort
+    3d/compare-eax-and  0/imm32/false
+    0f 85/jump-if-!=  $insert-slice-or-abort:error-duplicate/disp32
+    # curr += row-size
+    03/add                          1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   0x10/disp8      .                 # add *(ebp+16) to ecx
+    # loop
+    eb/jump  $insert-slice-or-abort:search-loop/disp8
+$insert-slice-or-abort:not-found:
+    # result/eax = 0
+    31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
+    # if (table->write >= table->size) 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)
+    0f 8d/jump-if->=  $insert-slice-or-abort:error-full/disp32
+    # zero-out(max, row-size)
+    # . . push args
+    ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
+    52/push-edx
+    # . . call
+    e8/call  zero-out/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # slice-to-string(ad, key, max)
+    # . . push args
+    52/push-edx
+    ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
+    ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x14/disp8      .                 # push *(ebp+20)
+    # . . call
+    e8/call  slice-to-string/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+    # table->write += row-size
+    # . eax = row-size
+    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+8
+    # . eax = max
+    89/copy                         3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # copy edx to eax
+    # . eax += 8
+    05/add-to-eax  8/imm32
+$insert-slice-or-abort:end:
+    # . restore registers
+    5e/pop-to-esi
+    5a/pop-to-edx
+    59/pop-to-ecx
+    # . epilogue
+    89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
+    5d/pop-to-ebp
+    c3/return
+
+$insert-slice-or-abort:error-duplicate:
+    # . flush(Stderr)
+    # . . push args
+    68/push  Stderr/imm32
+    # . . call
+    e8/call  flush/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+    # . _write(2/stderr, error)
+    # . . push args
+    68/push  "insert-slice-or-abort: key already exists: "/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
+    # . write-slice-buffered(Stderr, key)
+    # . . push args
+    ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
+    68/push  Stderr/imm32
+    # . . call
+    e8/call  write-slice-buffered/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # . flush(Stderr)
+    # . . push args
+    68/push  Stderr/imm32
+    # . . call
+    e8/call  flush/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+    # . _write(2/stderr, "\n")
+    # . . push args
+    68/push  "\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
+    # . syscall_exit(1)
+    bb/copy-to-ebx  1/imm32
+    e8/call  syscall_exit/disp32
+    # never gets here
+
+$insert-slice-or-abort:error-full:
+    # . _write(2/stderr, error)
+    # . . push args
+    68/push  "insert-slice-or-abort: table is full\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
+    # . syscall_exit(1)
+    bb/copy-to-ebx  1/imm32
+    e8/call  syscall_exit/disp32
+    # never gets here
+
+
 # if no row is found, stop(ed)
 get-or-stop:  # table: (addr stream {(handle array byte), T}), key: (addr array byte), row-size: int,
               # abort-message-prefix: (addr array byte), err: (addr buffered-file), ed: (addr exit-descriptor)
diff --git a/linux/assort b/linux/assort
index f4f73847..960b45df 100755
--- a/linux/assort
+++ b/linux/assort
Binary files differdiff --git a/linux/braces b/linux/braces
index 0930284c..bb79fe27 100755
--- a/linux/braces
+++ b/linux/braces
Binary files differdiff --git a/linux/calls b/linux/calls
index e2c8b5bb..a4b5c875 100755
--- a/linux/calls
+++ b/linux/calls
Binary files differdiff --git a/linux/dquotes b/linux/dquotes
index 0339ae16..e43323f3 100755
--- a/linux/dquotes
+++ b/linux/dquotes
Binary files differdiff --git a/linux/hex b/linux/hex
index e6c63c83..e242824f 100755
--- a/linux/hex
+++ b/linux/hex
Binary files differdiff --git a/linux/labels_baremetal b/linux/labels_baremetal
index 12a545ca..66fcbd97 100755
--- a/linux/labels_baremetal
+++ b/linux/labels_baremetal
Binary files differdiff --git a/linux/labels_baremetal.subx b/linux/labels_baremetal.subx
index e1743c0b..6e1d559b 100644
--- a/linux/labels_baremetal.subx
+++ b/linux/labels_baremetal.subx
@@ -216,7 +216,7 @@ load-labels:  # in: (addr buffered-file), labels: (stream {label-name, address}
     #     read-line-buffered(in, line)
     #     if (line->write == 0) break               # end of file
     #     var word-slice/ecx: (addr slice) = next-word(line)
-    #     var dest/edi: (addr int) = get-or-insert-slice(labels, word-slice, 12 bytes/row)
+    #     var dest/edi: (addr int) = insert-slice-or-abort(labels, word-slice, 12 bytes/row)
     #     word-slice = next-word(line)
     #     var address/eax: int = parse-hex-int-from-slice(word-slice)
     #     *dest = address
@@ -333,15 +333,15 @@ $load-labels:loop:
 #?     # . . discard args
 #?     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 #?     # }}}
-    # var dest/edi: (addr int) = get-or-insert-slice(labels, word-slice, 12 bytes/row, Heap)
-    # . eax = get-or-insert-slice(labels, word-slice, 12 bytes/row, Heap)
+    # var dest/edi: (addr int) = insert-slice-or-abort(labels, word-slice, 12 bytes/row, Heap)
+    # . eax = insert-slice-or-abort(labels, word-slice, 12 bytes/row, Heap)
     # . . push args
     68/push  Heap/imm32
     68/push  0xc/imm32/row-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-slice/disp32
+    e8/call  insert-slice-or-abort/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
     # . edi = eax
diff --git a/linux/mu b/linux/mu
index ecf884a2..ff127f40 100755
--- a/linux/mu
+++ b/linux/mu
Binary files differdiff --git a/linux/pack b/linux/pack
index 32db6ad3..659fafe7 100755
--- a/linux/pack
+++ b/linux/pack
Binary files differdiff --git a/linux/sigils b/linux/sigils
index 3bcd2d9c..f390b17e 100755
--- a/linux/sigils
+++ b/linux/sigils
Binary files differdiff --git a/linux/survey_baremetal b/linux/survey_baremetal
index b5429495..d794d9b0 100755
--- a/linux/survey_baremetal
+++ b/linux/survey_baremetal
Binary files differdiff --git a/linux/survey_baremetal.subx b/linux/survey_baremetal.subx
index 40115cd8..e7d12b7c 100644
--- a/linux/survey_baremetal.subx
+++ b/linux/survey_baremetal.subx
@@ -468,6 +468,8 @@ compute-addresses:  # in: (addr stream byte), labels: (addr stream {(handle arra
     #         current-address = new-address
     #       else if label?(word-slice)
     #         strip trailing ':' from word-slice
+    #         var tmp/eax: (addr int) = insert-slice-or-abort(labels, word-slice)
+    #         *tmp = current-address
     #         trace("label '" word-slice "' is at address " current-address ".")
     #         # labels occupy no space, so no need to increment offsets
     #       else
@@ -661,14 +663,14 @@ $compute-addresses:case-label:
     0f 84/jump-if-=  $compute-addresses:case-default/disp32
     # strip trailing ':' from word-slice
     ff          1/subop/decrement   1/mod/*+disp8   2/rm32/edx    .           .             .           .           4/disp8         .                 # decrement *(edx+4)
-    # var tmp/eax: (addr int) = get-or-insert-slice(labels, word-slice, row-size=12)
+    # var tmp/eax: (addr int) = insert-slice-or-abort(labels, word-slice, row-size=12)
     # . . push args
     68/push  Heap/imm32
     68/push  0xc/imm32/row-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-slice/disp32
+    e8/call  insert-slice-or-abort/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
     # *tmp = current-address
diff --git a/linux/survey_elf b/linux/survey_elf
index 9d1a715c..c5f59cca 100755
--- a/linux/survey_elf
+++ b/linux/survey_elf
Binary files differdiff --git a/linux/survey_elf.subx b/linux/survey_elf.subx
index 838feb88..59fb5af4 100644
--- a/linux/survey_elf.subx
+++ b/linux/survey_elf.subx
@@ -451,7 +451,7 @@ compute-offsets:  # in: (addr stream byte), segments: (addr stream {(handle arra
     #         break  (next line)
     #       else if label?(word-slice)
     #         strip trailing ':' from word-slice
-    #         linfo: (addr label-info) = get-or-insert-slice(labels, word-slice)
+    #         linfo: (addr label-info) = insert-slice-or-abort(labels, word-slice)
     #         linfo->segment-name = curr-segment-name
     #         trace("label '", word-slice, "' is in segment '", curr-segment-name, "'.")
     #         linfo->segment-offset = segment-offset
@@ -753,15 +753,15 @@ $compute-offsets:case-label:
     0f 84/jump-if-=  $compute-offsets:case-default/disp32
     # strip trailing ':' from word-slice
     ff          1/subop/decrement   1/mod/*+disp8   2/rm32/edx    .           .             .           .           4/disp8         .                 # decrement *(edx+4)
-    # linfo/edi = get-or-insert-slice(labels, word-slice, row-size=24)
-    # . eax = get-or-insert-slice(labels, word-slice, row-size=24)
+    # linfo/edi = insert-slice-or-abort(labels, word-slice, row-size=24)
+    # . eax = insert-slice-or-abort(labels, word-slice, row-size=24)
     # . . push args
     68/push  Heap/imm32
     68/push  0x18/imm32/row-size
     52/push-edx
     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
     # . . call
-    e8/call  get-or-insert-slice/disp32
+    e8/call  insert-slice-or-abort/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
     # . edi = eax
diff --git a/linux/test_apps b/linux/test_apps
index 9dab3e41..a72b1ce5 100755
--- a/linux/test_apps
+++ b/linux/test_apps
@@ -227,7 +227,7 @@ test "$1" = 'record'  ||  git diff --exit-code sigils
 }
 
 echo calls
-cat [012]*.subx subx-params.subx calls.subx  |  sigils  > a.sigils
+cat [012]*.subx subx-params.subx calls.subx  |  ./sigils  > a.sigils
 bootstrap/bootstrap translate a.sigils -o calls
 test "$1" = 'record'  ||  git diff --exit-code calls
 {
@@ -241,7 +241,7 @@ test "$1" = 'record'  ||  git diff --exit-code calls
 }
 
 echo braces
-cat [012]*.subx subx-params.subx braces.subx  |  calls  |  sigils  > a.sigils
+cat [012]*.subx subx-params.subx braces.subx  |  ./calls  |  ./sigils  > a.sigils
 bootstrap/bootstrap translate a.sigils -o braces
 test "$1" = 'record'  ||  git diff --exit-code braces
 {
@@ -255,7 +255,7 @@ test "$1" = 'record'  ||  git diff --exit-code braces
 }
 
 echo mu
-cat [0-2]*.subx mu.subx  |  braces  |  calls  |  sigils  > a.sigils
+cat [0-2]*.subx mu.subx  |  ./braces  |  ./calls  |  ./sigils  > a.sigils
 bootstrap/bootstrap translate a.sigils -o mu
 test "$1" = 'record'  ||  git diff --exit-code mu
 {
diff --git a/linux/tests b/linux/tests
index 04a112ef..e9b31bb9 100755
--- a/linux/tests
+++ b/linux/tests
Binary files differdiff --git a/subx.md b/subx.md
index d2b9ad50..1d281ced 100644
--- a/subx.md
+++ b/subx.md
@@ -138,10 +138,6 @@ Functions are called using the following syntax:
 Function arguments must be either literals (integers or strings) or a reg/mem
 operand using the syntax in the previous section.
 
-A special label on Linux is `Entry`, which can be used to specify/override the
-entry point of the program. It doesn't have to be unique, and the latest
-definition will override earlier ones.
-
 Another special pair of labels are the block delimiters `{` and `}`. They can
 be nested, and jump instructions can take arguments `loop` or `break` that
 jump to the enclosing `{` and `}` respectively.