about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--050_write.subx20
-rw-r--r--051test.subx14
-rw-r--r--052kernel-string-equal.subx26
-rw-r--r--053new-segment.subx8
-rw-r--r--054string-equal.subx17
-rw-r--r--055stream.subx18
-rw-r--r--056trace.subx96
-rw-r--r--057write.subx4
-rw-r--r--058stream-equal.subx32
-rw-r--r--059stop.subx2
-rw-r--r--060read.subx7
-rw-r--r--061read-byte.subx17
-rw-r--r--062write-stream.subx6
-rw-r--r--063error.subx2
-rw-r--r--064write-byte.subx18
-rw-r--r--065write-buffered.subx8
-rw-r--r--066print-int.subx7
-rw-r--r--067parse-hex.subx52
-rw-r--r--068error-byte.subx4
-rw-r--r--069allocate.subx16
-rw-r--r--070new-stream.subx10
-rw-r--r--071read-line.subx2
-rw-r--r--072slice.subx106
-rw-r--r--073next-token.subx78
-rw-r--r--074write-stream-data.subx6
-rw-r--r--075print-int-decimal.subx12
-rw-r--r--076next-word.subx8
-rw-r--r--077subx-words.subx70
-rw-r--r--078emit-hex.subx4
-rw-r--r--079emit.subx26
-rw-r--r--080zero-out.subx4
-rw-r--r--081table.subx123
-rw-r--r--082slurp.subx4
-rw-r--r--083subx-widths.subx25
-rw-r--r--084emit-hex-array.subx12
-rw-r--r--092write-int.subx2
-rw-r--r--093array-equal.subx60
-rw-r--r--094next-word-or-string.subx12
-rw-r--r--095stack.subx12
-rwxr-xr-xapps/assortbin42810 -> 42800 bytes
-rw-r--r--apps/assort.subx44
-rwxr-xr-xapps/bracesbin42826 -> 42816 bytes
-rw-r--r--apps/braces.subx20
-rwxr-xr-xapps/callsbin47536 -> 47526 bytes
-rw-r--r--apps/calls.subx56
-rwxr-xr-xapps/crenshaw2-1bin42218 -> 42208 bytes
-rw-r--r--apps/crenshaw2-1.subx32
-rwxr-xr-xapps/crenshaw2-1bbin42765 -> 42755 bytes
-rw-r--r--apps/crenshaw2-1b.subx30
-rwxr-xr-xapps/dquotesbin46460 -> 46450 bytes
-rw-r--r--apps/dquotes.subx74
-rwxr-xr-xapps/factorialbin41237 -> 41227 bytes
-rw-r--r--apps/factorial.subx8
-rwxr-xr-xapps/handlebin42135 -> 42125 bytes
-rw-r--r--apps/handle.subx22
-rwxr-xr-xapps/hexbin45057 -> 45047 bytes
-rw-r--r--apps/hex.subx28
-rwxr-xr-xapps/mubin63193 -> 63183 bytes
-rw-r--r--apps/mu.subx451
-rw-r--r--apps/mulisp.subx18
-rwxr-xr-xapps/packbin55202 -> 55192 bytes
-rw-r--r--apps/pack.subx260
-rwxr-xr-xapps/sigilsbin55211 -> 55201 bytes
-rw-r--r--apps/sigils.subx47
-rwxr-xr-xapps/surveybin52051 -> 52041 bytes
-rw-r--r--apps/survey.subx142
-rwxr-xr-xapps/testsbin41608 -> 41598 bytes
-rw-r--r--apps/tests.subx18
68 files changed, 1122 insertions, 1078 deletions
diff --git a/050_write.subx b/050_write.subx
index 4a48e825..13f3cddf 100644
--- a/050_write.subx
+++ b/050_write.subx
@@ -11,6 +11,26 @@ Entry:  # just exit; can't test _write just yet
     b8/copy-to-eax  1/imm32/exit
     cd/syscall  0x80/imm8
 
+# Since this is the first file of SubX code, a note about type comments.
+# Eventually we'll build a slightly higher-level safe language atop SubX.
+# Even though we don't have the safe language yet, we'll start thinking in
+# terms of the higher-level types in comments.
+#
+# Mu will have two kinds of addresses:
+#   - 'ref' which is used to point to a unique element, because machine
+#     code can't store large types in registers.
+#   - 'handle' which can point to a heap allocation, different heap allocations
+#     at different times, or even at times nothing at all.
+#
+# The type 'address' can be obtained from either a ref or handle, but it can
+# only be stored on the stack (say to pass objects by reference).
+# Conversely, a ref can't be copied into another ref, only to an address (which
+# by construction has a shorter lifetime).
+#
+# Beginnings of a lattice of types:
+#   You can convert a ref or handle to an address, but not the other way around.
+#   You can convert addresses to ints, but not the other way around.
+
 _write:  # fd : int, s : (address array byte)
     # . prologue
     55/push-ebp
diff --git a/051test.subx b/051test.subx
index 846e6cee..ac0f32a5 100644
--- a/051test.subx
+++ b/051test.subx
@@ -80,26 +80,26 @@ $check-ints-equal:end:
 
 # length-prefixed string containing just a single newline
 # convenient to have when printing messages and so on
-Newline:
-    # size
+Newline:  # (ref array byte)
+    # size : int
     1/imm32
     # data
     0a/newline
 
 # every test failure increments this counter
-Num-test-failures:
+Num-test-failures:  # int
     0/imm32
 
 # length-prefixed string containing just a single space
-Space:
-    # size
+Space:  # (ref array byte)
+    # size : int
     1/imm32
     # data
     20/space
 
 # length-prefixed string containing just a single slash
-Slash:
-    # size
+Slash:  # (ref array byte)
+    # size : int
     1/imm32
     # data
     2f/slash
diff --git a/052kernel-string-equal.subx b/052kernel-string-equal.subx
index 357591e1..57b5b1ec 100644
--- a/052kernel-string-equal.subx
+++ b/052kernel-string-equal.subx
@@ -1,9 +1,11 @@
 # Checking null-terminated ascii strings.
 #
-# By default we create strings with a 4-byte length prefix rather than a null suffix.
-# However we still need null-prefixed strings when interacting with the Linux
-# kernel in a few places. This layer implements a function for comparing
-# a null-terminated 'kernel string' with a length-prefixed 'SubX string'.
+# By default we create strings as arrays of bytes, and all arrays have a 4-byte
+# length prefix.
+#
+# However, we sometimes need to deal with null-prefixed strings when interacting
+# with the Linux kernel. This layer implements a function for comparing a
+# null-terminated 'kernel string' with a length-prefixed 'SubX string'.
 #
 # To run (from the subx directory):
 #   $ ./subx translate 05[0-2]*.subx -o /tmp/tmp52
@@ -28,7 +30,7 @@ Entry:  # run all tests
 
 # compare a null-terminated ascii string with a more idiomatic length-prefixed byte array
 # reason for the name: the only place we should have null-terminated ascii strings is from commandline args
-kernel-string-equal?:  # s : null-terminated ascii string, benchmark : length-prefixed ascii string -> eax : boolean
+kernel-string-equal?:  # s : (address kernel-string), benchmark : (address array byte) -> eax : boolean
     # pseudocode:
     #   n = benchmark->length
     #   s1 = s
@@ -59,17 +61,19 @@ kernel-string-equal?:  # s : null-terminated ascii string, benchmark : length-pr
     53/push-ebx
     56/push-esi
     57/push-edi
-    # s1/edi = s
+    # var s1/edi : (address byte) = s
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           7/r32/edi   8/disp8         .                 # copy *(ebp+8) to edi
-    # n/edx = benchmark->length
+    # var n/edx : int = benchmark->length
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           2/r32/edx   0xc/disp8       .                 # copy *(ebp+12) to edx
     8b/copy                         0/mod/indirect  2/rm32/edx    .           .             .           2/r32/edx   .               .                 # copy *edx to edx
-    # s2/esi = benchmark->data
+    # var s2/esi : (address byte) = benchmark->data
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   0xc/disp8       .                 # copy *(ebp+12) to esi
     81          0/subop/add         3/mod/direct    6/rm32/esi    .           .             .           .           .               4/imm32           # add to esi
-    # i/ecx = c1/eax = c2/ebx = 0
+    # var i/ecx : int = 0
     b9/copy-to-ecx  0/imm32/exit
+    # var c1/eax : byte = 0
     b8/copy-to-eax  0/imm32
+    # var c2/ebx : byte = 0
     bb/copy-to-ebx  0/imm32
 $kernel-string-equal?:loop:
     # if (i >= n) break
@@ -258,10 +262,10 @@ test-compare-kernel-string-with-longer-array:
 
 == data
 
-Null-kernel-string:
+Null-kernel-string:  # (address kernel-string)
     00/null
 
-_test-Abc-kernel-string:
+_test-Abc-kernel-string:  # (address kernel-string)
     41/A 62/b 63/c 00/null
 
 # . . vim:nowrap:textwidth=0
diff --git a/053new-segment.subx b/053new-segment.subx
index 719413ab..ba2487d5 100644
--- a/053new-segment.subx
+++ b/053new-segment.subx
@@ -17,7 +17,7 @@
 # . 1-3 bytes   3 bits              2 bits          3 bits        3 bits      3 bits        2 bits      2 bits      0/1/2/4 bytes   0/1/2/4 bytes
 
 Entry:   # manual test
-    # var ad/ecx : (address allocation-descriptor) = {0, 0}
+    # var ad/ecx : (ref 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
@@ -29,7 +29,7 @@ Entry:   # manual test
     e8/call  new-segment/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-    # eax = ad->curr
+    # var eax : (address _) = 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
@@ -38,7 +38,7 @@ Entry:   # manual test
     b8/copy-to-eax  1/imm32/exit
     cd/syscall  0x80/imm8
 
-new-segment:  # len : int, ad : (address allocation-descriptor)
+new-segment:  # len : int, ad : (ref allocation-descriptor)
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
@@ -72,7 +72,7 @@ $new-segment:end:
 == data
 
 # various constants used here were found in the Linux sources (search for file mman-common.h)
-_mmap-new-segment:  # type mmap_arg_struct
+_mmap-new-segment:  # (ref mmap_arg_struct)
     # addr
     0/imm32
 $_mmap-new-segment:len:
diff --git a/054string-equal.subx b/054string-equal.subx
index 484844d9..c63688a1 100644
--- a/054string-equal.subx
+++ b/054string-equal.subx
@@ -13,12 +13,12 @@ Entry:  # run all tests
     b8/copy-to-eax  1/imm32/exit
     cd/syscall  0x80/imm8
 
-string-equal?:  # s : (address string), benchmark : (address string) -> eax : boolean
+string-equal?:  # s : (address array byte), benchmark : (address array byte) -> eax : boolean
     # pseudocode:
     #   if (s->length != benchmark->length) return false
     #   currs = s->data
     #   currb = benchmark->data
-    #   maxs = s->data + s->length
+    #   maxs = &s->data[s->length]
     #   while currs < maxs
     #     c1 = *currs
     #     c2 = *currb
@@ -51,14 +51,15 @@ $string-equal?:lengths:
     # if (ecx != benchmark->length) return false
     39/compare                      0/mod/indirect  7/rm32/edi    .           .             .           1/r32/ecx   .               .                 # compare *edi and ecx
     75/jump-if-not-equal  $string-equal?:false/disp8
-    # currs/esi = s->data
+    # var currs/esi : (address byte) = s->data
     81          0/subop/add         3/mod/direct    6/rm32/esi    .           .             .           .           .               4/imm32           # add to esi
-    # maxs/ecx = s->data + s->length
+    # var maxs/ecx : (address byte) = &s->data[s->length]
     01/add                          3/mod/direct    1/rm32/ecx    .           .             .           6/r32/esi   .               .                 # add esi to ecx
-    # currb/edi = benchmark->data
+    # var currb/edi : (address byte) = benchmark->data
     81          0/subop/add         3/mod/direct    7/rm32/edi    .           .             .           .           .               4/imm32           # add to edi
-    # c1/eax = c2/edx = 0
+    # var c1/eax : byte = 0
     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
+    # var c2/edx : byte = 0
     31/xor                          3/mod/direct    2/rm32/edx    .           .             .           2/r32/edx   .               .                 # clear edx
 $string-equal?:loop:
     # if (currs >= maxs) return true
@@ -175,13 +176,13 @@ test-compare-inequal-strings-equal-lengths:
     c3/return
 
 # helper for later tests
-check-strings-equal:  # s : (address string), expected : (address string), msg : (address string)
+check-strings-equal:  # s : (address array byte), expected : (address array byte), msg : (address array byte)
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     # . save registers
     50/push-eax
-    # eax = string-equal?(s, expected)
+    # var eax : boolean = string-equal?(s, expected)
     # . . push args
     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    .           .             .           .           8/disp8         .                 # push *(ebp+8)
diff --git a/055stream.subx b/055stream.subx
index c60be1d5..6568602a 100644
--- a/055stream.subx
+++ b/055stream.subx
@@ -3,7 +3,7 @@
 # A stream looks like this:
 #   write : int  # index at which writes go
 #   read : int  # index that we've read until
-#   data : (array byte)  # prefixed by length as usual
+#   data : (ref array byte)  # prefixed by length as usual
 #
 # some primitives for operating on streams:
 #   - clear-stream (clears everything but the data length)
@@ -14,7 +14,7 @@
 # . 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
 
-clear-stream:  # f : (address stream)
+clear-stream:  # f : (address stream byte)
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
@@ -23,23 +23,23 @@ clear-stream:  # f : (address stream)
     51/push-ecx
     # eax = f
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         0/r32/eax   8/disp8         .                 # copy *(ebp+8) to eax
-    # ecx = f->length
+    # var count/ecx : int = f->length
     8b/copy                         1/mod/*+disp8   0/rm32/eax    .           .             .           1/r32/ecx   8/disp8         .                 # copy *(eax+8) to ecx
-    # ecx = &f->data[f->length]
+    # var max/ecx : (address byte) = &f->data[f->length]
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   0xc/disp8       .                 # copy eax+ecx+12 to ecx
     # f->write = 0
     c7          0/subop/copy        0/mod/direct    0/rm32/eax    .           .             .           .           .               0/imm32           # copy to *eax
     # f->read = 0
     c7          0/subop/copy        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         0/imm32           # copy to *(eax+4)
-    # eax = f->data
+    # var curr/eax : (address byte) = f->data
     81          0/subop/add         3/mod/direct    0/rm32/eax    .           .             .           .           .               0xc/imm32         # add to eax
 $clear-stream:loop:
-    # if (eax >= ecx) break
+    # if (curr >= max) break
     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # compare eax with ecx
     73/jump-if-greater-or-equal-unsigned  $clear-stream:end/disp8
-    # *eax = 0
+    # *curr = 0
     c6          0/subop/copy-byte   0/mod/direct    0/rm32/eax    .           .             .           .           .               0/imm8            # copy byte to *eax
-    # ++eax
+    # ++curr
     40/increment-eax
     eb/jump  $clear-stream:loop/disp8
 $clear-stream:end:
@@ -51,7 +51,7 @@ $clear-stream:end:
     5d/pop-to-ebp
     c3/return
 
-rewind-stream:  # f : (address stream)
+rewind-stream:  # f : (address stream byte)
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
diff --git a/056trace.subx b/056trace.subx
index 2bcdde1e..0a09bd4f 100644
--- a/056trace.subx
+++ b/056trace.subx
@@ -14,9 +14,9 @@
 
 == data
 
-# We'll save the address of the trace segment here.
-Trace-stream:
+Trace-stream:  # (handle stream byte)
     0/imm32
+    # we don't have safe handles (fat pointers) yet
 
 Trace-segment:
     0/imm32/curr
@@ -24,7 +24,7 @@ Trace-segment:
 
 # Fake trace-stream for tests.
 # Also illustrates the layout of the real trace-stream (segment).
-_test-trace-stream:
+_test-trace-stream:  # (ref stream byte)
     # current write index
     0/imm32
     # current read index
@@ -60,7 +60,7 @@ initialize-trace-stream:  # n : int
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     # copy Trace-segment->curr to *Trace-stream
     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   Trace-segment/disp32              # copy *Trace-segment to eax
-    # watch point to catch Trace-stream leaks
+#?     # watch point to catch Trace-stream leaks
 #? $watch-1:
     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   Trace-stream/disp32               # copy eax to *Trace-stream
     # Trace-stream->length = n - 12
@@ -79,7 +79,7 @@ $initialize-trace-stream:end:
 
 # Append a string to the given trace stream.
 # Silently give up if it's already full. Or truncate the string if there isn't enough room.
-trace:  # line : (address string)
+trace:  # line : (address array byte)
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
@@ -90,13 +90,13 @@ trace:  # line : (address string)
     53/push-ebx
     56/push-esi
     57/push-edi
-    # edi = *Trace-stream
+    # var edi : (address stream byte) = *Trace-stream
     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           7/r32/edi   Trace-stream/disp32               # copy *Trace-stream to edi
     # esi = line
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
-    # ecx = t->write
+    # var ecx : int = t->write
     8b/copy                         0/mod/indirect  7/rm32/edi    .           .             .           1/r32/ecx   .               .                 # copy *edi to ecx
-    # edx = t->length
+    # var edx : int = t->length
     8b/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           2/r32/edx   8/disp8         .                 # copy *(edi+8) to edx
     # eax = _append-3(&t->data[t->write], &t->data[t->length], line)
     # . . push line
@@ -302,7 +302,7 @@ $check-trace-scans-to:end:
     c3/return
 
 # Start scanning from Trace-stream->read for 'line'. If found, update Trace-stream->read and return true.
-trace-scan:  # line : (address string) -> result/eax : boolean
+trace-scan:  # line : (address array byte) -> result/eax : boolean
     # pseudocode:
     #   push Trace-stream->read
     #   while true:
@@ -340,8 +340,8 @@ $trace-scan:loop:
     e8/call  next-line-matches?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-    # if (eax == 0) continue
-    3d/compare-eax-and  0/imm32
+    # if (eax == false) continue
+    3d/compare-eax-and  0/imm32/false
     74/jump-if-equal  $trace-scan:continue/disp8
 $trace-scan:true:
     # skip-next-line(Trace-stream)
@@ -554,7 +554,7 @@ test-trace-scan-failure-leaves-read-index-untouched:
     # . end
     c3/return
 
-next-line-matches?:  # t : (address stream), line : (address string) -> result/eax : boolean
+next-line-matches?:  # t : (address stream byte), line : (address array byte) -> result/eax : boolean
     # pseudocode:
     #   while true:
     #     if (currl >= maxl) break
@@ -575,58 +575,58 @@ next-line-matches?:  # t : (address stream), line : (address string) -> result/e
     57/push-edi
     # edx = line
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         2/r32/edx   0xc/disp8       .                 # copy *(ebp+12) to edx
-    # currl/esi = line->data
+    # var currl/esi : (address byte) = line->data
     # . esi = line/edx->data
     8d/copy-address                 1/mod/*+disp8   2/rm32/edx    .           .             .           6/r32/esi   4/disp8         .                 # copy edx+4 to esi
-    # maxl/ecx = line->data + line->size
+    # var maxl/ecx : (address byte) = &line->data[line->size]
     # . eax = line/edx->size
     8b/copy                         0/mod/indirect  2/rm32/edx    .           .                         0/r32/eax   .               .                 # copy *edx to eax
-    # . maxl/ecx = line->data/esi + line->size/eax
+    # . maxl = &line->data[line->size]
     8d/copy-address                 0/mod/indirect  4/rm32/sib    6/base/esi  0/index/eax   .           1/r32/ecx   .               .                 # copy edx+eax to ecx
     # edi = t
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         7/r32/edi   8/disp8         .                 # copy *(ebp+8) to edi
-    # ebx = t->data
+    # var ebx : (address byte) = t->data
     8d/copy-address                 1/mod/*+disp8   7/rm32/edi    .           .             .           3/r32/ebx   0xc/disp8       .                 # copy edi+12 to ebx
-    # maxt/edx = t->data + t->write
+    # var maxt/edx : (address byte) = &t->data[t->write]
     # . eax = t->write
     8b/copy                         0/mod/indirect  7/rm32/edi    .           .                         0/r32/eax   .               .                 # copy *edi to eax
-    # . maxt/edx = t->data/ebx + t->write/eax
+    # . maxt = &t->data[t->write]
     8d/copy-address                 0/mod/indirect  4/rm32/sib    3/base/ebx  0/index/eax   .           2/r32/edx   .               .                 # copy ebx+eax to edx
-    # currt/edi = t->data + t->read
+    # var currt/edi : (address byte) = &t->data[t->read]
     # . eax = t/edi->read
     8b/copy                         1/mod/*+disp8   7/rm32/edi    .           .                         0/r32/eax   4/disp8         .                 # copy *(edi+4) to eax
-    # . currt/edi = t->data/ebx + t->read/eax
+    # . currt = &t->data[t->read]
     8d/copy-address                 0/mod/indirect  4/rm32/sib    3/base/ebx  0/index/eax   .           7/r32/edi   .               .                 # copy ebx+eax to edi
 $next-line-matches?:loop:
-    # if (currl/esi >= maxl/ecx) break
+    # if (currl >= maxl) break
     39/compare                      3/mod/direct    6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # compare esi and ecx
     73/jump-if-greater-or-equal-unsigned  $next-line-matches?:break/disp8
-    # if (currt/edi >= maxt/edx) return false
+    # if (currt >= maxt) return false
     # . eax = false
     b8/copy-to-eax  0/imm32/false
     39/compare                      3/mod/direct    7/rm32/edi    .           .             .           2/r32/edx   .               .                 # compare edi and edx
     73/jump-if-greater-or-equal-unsigned  $next-line-matches?:end/disp8
-    # if (*currt/edi != *currl/esi) return false
+    # if (*currt != *currl) return false
     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
     31/xor                          3/mod/direct    3/rm32/eax    .           .             .           3/r32/eax   .               .                 # clear ebx
-    # . eax = (char) *currt/edi
+    # . eax : byte = *currt
     8a/copy-byte                    0/mod/indirect  7/rm32/edi    .           .                         0/r32/eax   .               .                 # copy *edi to eax
-    # . ebx = (char) *currl/esi
+    # . ebx : byte = *currl
     8a/copy-byte                    0/mod/indirect  6/rm32/esi    .           .                         3/r32/ebx   .               .                 # copy *esi to ebx
     # . eax >= ebx
     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           3/r32/ebx   .               .                 # compare eax and ebx
     # . eax = false
     b8/copy-to-eax  0/imm32/false
     75/jump-if-not-equal  $next-line-matches?:end/disp8
-    # ++currt/edi
+    # ++currt
     47/increment-edi
-    # ++currl/esi
+    # ++currl
     46/increment-esi
     eb/jump  $next-line-matches?:loop/disp8
 $next-line-matches?:break:
     # return *currt == '\n'
     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
-    # . eax = (char) *currt
+    # . eax : byte = *currt
     8a/copy-byte                    0/mod/indirect  7/rm32/edi    .           .                         0/r32/eax   .               .                 # copy *edi to eax
     3d/compare-eax-and  0xa/imm32/newline
     # . eax = false
@@ -730,11 +730,11 @@ test-next-line-matches?-match:
     c3/return
 
 # move t->read to _after_ next newline
-skip-next-line:  # t : (address stream)
+skip-next-line:  # t : (address stream byte)
     # pseudocode:
-    #   max = t->data + t->write
+    #   max = &t->data[t->write]
     #   i = t->read
-    #   curr = t->data + t->read
+    #   curr = &t->data[t->read]
     #   while true
     #     if (curr >= max) break
     #     ++i
@@ -752,37 +752,37 @@ skip-next-line:  # t : (address stream)
     53/push-ebx
     # ecx = t
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
-    # edx = t/ecx->data
+    # edx = t->data
     8d/copy-address                 1/mod/*+disp8   1/rm32/ecx    .           .             .           2/r32/edx   0xc/disp8       .                 # copy ecx+12 to edx
-    # eax = t/ecx->write
+    # eax = t->write
     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
-    # max/ebx = t->data/edx + t->write/eax
+    # var max/ebx : (address byte) = &t->data[t->write]
     8d/copy-address                 0/mod/indirect  4/rm32/sib    2/base/edx  0/index/eax   .           3/r32/ebx   .               .                 # copy edx+eax to ebx
-    # eax = t/ecx->read
+    # eax = t->read
     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to edx
-    # curr/ecx = t->data/edx + t->read/eax
+    # var curr/ecx : (address byte) = &t->data[t->read]
     8d/copy-address                 0/mod/indirect  4/rm32/sib    2/base/edx  0/index/eax   .           1/r32/ecx   .               .                 # copy edx+eax to ecx
-    # i/edx = eax
+    # var i/edx : int = t->read
     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           0/r32/eax   .               .                 # copy eax to edx
 $skip-next-line:loop:
-    # if (curr/ecx >= max/ebx) break
+    # if (curr >= max) break
     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           3/r32/ebx   .               .                 # compare ecx and ebx
     73/jump-if-greater-or-equal-unsigned  $skip-next-line:end/disp8
-    # ++i/edx
+    # ++i
     42/increment-edx
-    # if (*curr/ecx == '\n') break
+    # if (*curr == '\n') break
     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
     3d/compare-eax-and  0a/imm32/newline
     74/jump-if-equal  $skip-next-line:end/disp8
-    # ++curr/ecx
+    # ++curr
     41/increment-ecx
     # loop
     eb/jump  $skip-next-line:loop/disp8
 $skip-next-line:end:
     # ecx = t
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
-    # t/ecx->read = i/edx
+    # t->read = i
     89/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           2/r32/edx   4/disp8         .                 # copy edx to *(ecx+4)
     # . restore registers
     5b/pop-to-ebx
@@ -858,7 +858,7 @@ $clear-trace-stream:end:
 # - helpers
 
 # 3-argument variant of _append
-_append-3:  # out : address, outend : address, s : (array byte) -> num_bytes_appended/eax
+_append-3:  # out : (address byte), outend : (address byte), s : (address array byte) -> num_bytes_appended/eax
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
@@ -890,7 +890,7 @@ $_append-3:end:
     c3/return
 
 # 4-argument variant of _append
-_append-4:  # out : address, outend : address, in : address, inend : address -> num_bytes_appended/eax
+_append-4:  # out : (address byte), outend : (address byte), in : (address byte), inend : (address byte) -> num_bytes_appended/eax : int
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
@@ -900,7 +900,7 @@ _append-4:  # out : address, outend : address, in : address, inend : address ->
     53/push-ebx
     56/push-esi
     57/push-edi
-    # eax/num_bytes_appended = 0
+    # num_bytes_appended = 0
     b8/copy-to-eax  0/imm32
     # edi = out
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           7/r32/edi   8/disp8         .                 # copy *(ebp+8) to edi
@@ -956,7 +956,7 @@ $_append-4:abort:
 
 == data
 
-_test-stream-line-ABABA:
+_test-stream-line-ABABA:  # (ref stream byte)
     # write
     8/imm32
     # read
@@ -966,7 +966,7 @@ _test-stream-line-ABABA:
     # data
     41 42 41 42 41 0a 00 00  # 8 bytes
 
-_test-stream-empty:
+_test-stream-empty:  # (ref stream byte)
     # write
     0/imm32
     # read
@@ -976,7 +976,7 @@ _test-stream-empty:
     # data
     00 00 00 00 00 00 00 00  # 8 bytes
 
-_test-stream-filled:
+_test-stream-filled:  # (ref stream byte)
     # write
     8/imm32
     # read
diff --git a/057write.subx b/057write.subx
index b1ec1688..db3cb5cc 100644
--- a/057write.subx
+++ b/057write.subx
@@ -21,7 +21,7 @@
 # . 1-3 bytes   3 bits              2 bits          3 bits        3 bits      3 bits        2 bits      2 bits      0/1/2/4 bytes   0/1/2/4 bytes
 
 # TODO: come up with a way to signal when a write to disk fails
-write:  # f : fd or (address stream), s : (address array byte)
+write:  # f : fd or (address stream byte), s : (address array byte)
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
@@ -145,7 +145,7 @@ test-write-appends:
 
 == data
 
-_test-stream:
+_test-stream:  # (ref stream byte)
     # current write index
     0/imm32
     # current read index
diff --git a/058stream-equal.subx b/058stream-equal.subx
index 25330a52..bc3bc4d0 100644
--- a/058stream-equal.subx
+++ b/058stream-equal.subx
@@ -6,7 +6,7 @@
 # . 1-3 bytes   3 bits              2 bits          3 bits        3 bits      3 bits        2 bits      2 bits      0/1/2/4 bytes   0/1/2/4 bytes
 
 # compare all the data in a stream (ignoring the read pointer)
-stream-data-equal?:  # f : (address stream), s : (address string) -> eax : boolean
+stream-data-equal?:  # f : (address stream byte), s : (address array byte) -> eax : boolean
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
@@ -19,20 +19,21 @@ stream-data-equal?:  # f : (address stream), s : (address string) -> eax : boole
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
     # eax = f->write
     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
-    # maxf/edx = f->data + f->write
+    # var maxf/edx : (address byte) = &f->data[f->write]
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/esi  0/index/eax   .           2/r32/edx   0xc/disp8       .                 # copy esi+eax+12 to edx
-    # currf/esi = f->data
+    # var currf/esi : (address byte) = f->data
     81          0/subop/add         3/mod/direct    6/rm32/esi    .           .             .           .           .               0xc/imm32         # add to esi
     # edi = s
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           7/r32/edi   0xc/disp8       .                 # copy *(ebp+12) to edi
-    # if (f->write != s->length) return false
 $stream-data-equal?:compare-lengths:
+    # if (f->write != s->length) return false
     39/compare                      0/mod/indirect  7/rm32/edi    .           .             .           0/r32/eax   .               .                 # compare *edi and eax
     75/jump-if-not-equal  $stream-data-equal?:false/disp8
-    # currs/edi = s->data
+    # var currs/edi : (address byte) = s->data
     81          0/subop/add         3/mod/direct    7/rm32/edi    .           .             .           .           .               4/imm32           # add to edi
-    # eax = ecx = 0
+    # var eax : byte = 0
     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
+    # var ecx : byte = 0
     31/xor                          3/mod/direct    1/rm32/ecx    .           .             .           1/r32/ecx   .               .                 # clear ecx
 $stream-data-equal?:loop:
     # if (currf >= maxf) return true
@@ -190,7 +191,7 @@ test-stream-data-equal-length-check:
     c3/return
 
 # helper for later tests
-check-stream-equal:  # f : (address stream), s : (address string), msg : (address string)
+check-stream-equal:  # f : (address stream byte), s : (address array byte), msg : (address array byte)
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
@@ -226,7 +227,7 @@ $check-stream-equal:end:
 # on success, set f->read to after the next newline
 # on failure, leave f->read unmodified
 # this function is usually used only in tests, so we repeatedly write f->read
-next-stream-line-equal?:  # f : (address stream), s : (address string) -> eax : boolean
+next-stream-line-equal?:  # f : (address stream byte), s : (address array byte) -> eax : boolean
     # pseudocode:
     #   currf = f->read  # bound: f->write
     #   currs = 0  # bound : s->length
@@ -273,30 +274,31 @@ next-stream-line-equal?:  # f : (address stream), s : (address string) -> eax :
     57/push-edi
     # esi = f
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
-    # currf/ecx = f->read
+    # var currf/ecx : int = f->read
     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(esi+4) to ecx
     # edi = s
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           7/r32/edi   0xc/disp8       .                 # copy *(ebp+12) to edi
-    # currs/edx = 0
+    # var currs/edx : int = 0
     31/xor                          3/mod/direct    2/rm32/edx    .           .             .           2/r32/edx   .               .                 # clear edx
-    # eax = ebx = 0
+    # var c1/eax : byte = 0
     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
+    # var c2/ebx : byte = 0
     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
 $next-stream-line-equal?:loop:
     # if (currf >= f->write) break
     3b/compare                      0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # compare ecx with *esi
     7d/jump-if-greater-or-equal  $next-stream-line-equal?:break/disp8
-    # AL = *(f->data + f->read)
+    # c1 = f->data[f->read]
     8a/copy-byte                    1/mod/*+disp8   4/rm32/sib    6/base/esi  1/index/ecx   .           0/r32/AL    0xc/disp8       .                 # copy byte at *(esi+ecx+12) to AL
-    # if (eax == '\n') break
+    # if (c1 == '\n') break
     3d/compare-eax-and  0xa/imm32/newline
     74/jump-if-equal  $next-stream-line-equal?:break/disp8
     # if (currs >= s->length) return false
     3b/compare                      0/mod/indirect  7/rm32/edi    .           .             .           2/r32/edx   .               .                 # compare edx with *edi
     7d/jump-if-greater-or-equal  $next-stream-line-equal?:false/disp8
-    # BL = *(s->data + currs)
+    # c2 = s->data[currs]
     8a/copy-byte                    1/mod/*+disp8   4/rm32/sib    7/base/edi  2/index/edx   .           3/r32/BL    4/disp8         .                 # copy byte at *(edi+edx+4) to BL
-    # if (eax != ebx) return false
+    # if (c1 != c2) return false
     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           3/r32/ebx   .               .                 # compare eax and ebx
     75/jump-if-not-equal  $next-stream-line-equal?:false/disp8
     # ++currf
diff --git a/059stop.subx b/059stop.subx
index 628c813d..7c1c776b 100644
--- a/059stop.subx
+++ b/059stop.subx
@@ -123,7 +123,7 @@ test-stop-skips-returns-on-exit:
     # Make room for an exit descriptor on the stack. That's almost always the
     # right place for it, available only as long as it's legal to use. Once this
     # containing function returns we'll need a new exit descriptor.
-    # var ed/eax : (address exit-descriptor)
+    # var ed/eax : (ref exit-descriptor)
     68/push  0/imm32
     68/push  0/imm32
     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           4/r32/esp   .               .                 # copy esp to eax
diff --git a/060read.subx b/060read.subx
index 1fe37da7..34658b7b 100644
--- a/060read.subx
+++ b/060read.subx
@@ -45,7 +45,7 @@
 # . 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
 
-read:  # f : fd or (address stream), s : (address stream) -> num-bytes-read/eax
+read:  # f : fd or (address stream byte), s : (address stream byte) -> num-bytes-read/eax : int
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
@@ -191,12 +191,11 @@ $_buffer-4:end:
     5d/pop-to-ebp
     c3/return
 
-
 # idea: a clear-if-empty method on streams that clears only if f->read == f->write
 # Unclear how I'd use it, though. Callers seem to need the check anyway.
 # Maybe a better helper would be 'empty-stream?'
 
-_read:  # fd : int, s : (address stream) -> num-bytes-read/eax
+_read:  # fd : int, s : (address stream byte) -> num-bytes-read/eax : int
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
@@ -427,7 +426,7 @@ test-read-returns-0-on-end-of-file:
 
 == data
 
-_test-tmp-stream:
+_test-tmp-stream:  # (ref stream byte)
     # current write index
     0/imm32
     # current read index
diff --git a/061read-byte.subx b/061read-byte.subx
index 4806a9f5..a90c05da 100644
--- a/061read-byte.subx
+++ b/061read-byte.subx
@@ -11,8 +11,8 @@
 
 # The buffered file for standard input. Also illustrates the layout for
 # buffered-file: a pointer to the backing store, followed by a 'buffer' stream
-Stdin:
-    # file descriptor or (address stream)
+Stdin:  # (ref buffered-file)
+    # file descriptor or (address stream byte)
     0/imm32  # standard input
 $Stdin->buffer:
     # inlined fields for a stream
@@ -73,8 +73,7 @@ read-byte-buffered:  # f : (address buffered-file) -> byte-or-Eof/eax
     b8/copy-to-eax  0xffffffff/imm32/Eof
     eb/jump  $read-byte-buffered:end/disp8
 $read-byte-buffered:from-stream:
-    # read byte from stream
-    # AL = f->data[f->read]
+    # byte-or-Eof = f->data[f->read]
     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
     8a/copy-byte                    1/mod/*+disp8   4/rm32/sib    6/base/esi  1/index/ecx   .           0/r32/AL    0x10/disp8      .                 # copy byte at *(esi+ecx+16) to AL
     # ++f->read
@@ -272,8 +271,8 @@ test-read-byte-buffered-refills-buffer:
 == data
 
 # a test buffered file for _test-stream
-_test-buffered-file:
-    # file descriptor or (address stream)
+_test-buffered-file:  # (ref buffered-file)
+    # file descriptor or (address stream byte)
     _test-stream/imm32
 $_test-buffered-file->buffer:
     # current write index
@@ -285,7 +284,7 @@ $_test-buffered-file->buffer:
     # data
     00 00 00 00 00 00  # 6 bytes
 
-_test-input-stream:
+_test-input-stream:  # (ref stream byte)
     # current write index
     0/imm32
     # current read index
@@ -311,8 +310,8 @@ _test-input-stream:
     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 
 # a test buffered file for _test-input-stream
-_test-input-buffered-file:
-    # file descriptor or (address stream)
+_test-input-buffered-file:  # (ref buffered-file)
+    # file descriptor or (address stream byte)
     _test-input-stream/imm32
 $_test-input-buffered-file->buffer:
     # current write index
diff --git a/062write-stream.subx b/062write-stream.subx
index 12d3c7e2..2dab3fe8 100644
--- a/062write-stream.subx
+++ b/062write-stream.subx
@@ -15,7 +15,7 @@
 #?     b8/copy-to-eax  1/imm32/exit
 #?     cd/syscall  0x80/imm8
 
-write-stream:  # f : fd or (address stream), s : (address stream)
+write-stream:  # f : fd or (address stream byte), s : (address stream byte)
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
@@ -75,7 +75,7 @@ $write-stream:end:
     5d/pop-to-ebp
     c3/return
 
-_write-stream:  # fd : int, s : (address stream)
+_write-stream:  # fd : int, s : (address stream byte)
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
@@ -242,7 +242,7 @@ test-write-stream-appends:
 
 == data
 
-_test-stream2:
+_test-stream2:  # (ref stream byte)
     # current write index
     4/imm32
     # current read index
diff --git a/063error.subx b/063error.subx
index 1d77b3f0..bb1a8b71 100644
--- a/063error.subx
+++ b/063error.subx
@@ -6,7 +6,7 @@
 # . 1-3 bytes   3 bits              2 bits          3 bits        3 bits      3 bits        2 bits      2 bits      0/1/2/4 bytes   0/1/2/4 bytes
 
 # write(out, "Error: "+msg+"\n") then stop(ed, 1)
-error:  # ed : (address exit-descriptor), out : fd or (address stream), msg : (address array byte)
+error:  # ed : (address exit-descriptor), out : fd or (address stream byte), msg : (address array byte)
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
diff --git a/064write-byte.subx b/064write-byte.subx
index ec849c6d..2e7a36f3 100644
--- a/064write-byte.subx
+++ b/064write-byte.subx
@@ -7,8 +7,8 @@
 == data
 
 # The buffered file for standard output.
-Stdout:
-    # file descriptor or (address stream)
+Stdout:  # (ref buffered-file)
+    # file descriptor or (address stream byte)
     1/imm32  # standard output
 $Stdout->buffer:
     # inlined fields for a stream
@@ -205,7 +205,7 @@ test-write-byte-buffered-multiple-flushes:
 # - variant without buffering
 
 # Write lower byte of 'n' to 'f'.
-append-byte:  # f : (address stream), n : int
+append-byte:  # f : (address stream byte), n : int
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
@@ -283,7 +283,7 @@ test-append-byte-single:
 
 == data
 
-_test-output-stream:
+_test-output-stream:  # (ref stream byte)
     # current write index
     0/imm32
     # current read index
@@ -325,8 +325,8 @@ _test-output-stream:
     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 
 # a test buffered file for _test-output-stream
-_test-output-buffered-file:
-    # file descriptor or (address stream)
+_test-output-buffered-file:  # (ref buffered-file)
+    # file descriptor or (address stream byte)
     _test-output-stream/imm32
 $_test-output-buffered-file->buffer:
     # current write index
@@ -338,7 +338,7 @@ $_test-output-buffered-file->buffer:
     # data
     00 00 00 00 00 00  # 6 bytes
 
-_test-error-stream:
+_test-error-stream:  # (ref stream byte)
     # current write index
     0/imm32
     # current read index
@@ -356,8 +356,8 @@ _test-error-stream:
     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 
 # a test buffered file for _test-error-stream
-_test-error-buffered-file:
-    # file descriptor or (address stream)
+_test-error-buffered-file:  # (ref buffered-file)
+    # file descriptor or (address stream byte)
     _test-error-stream/imm32
 $_test-error-buffered-file->buffer:
     # current write index
diff --git a/065write-buffered.subx b/065write-buffered.subx
index bc829a51..8c7ae9c5 100644
--- a/065write-buffered.subx
+++ b/065write-buffered.subx
@@ -38,9 +38,9 @@ write-buffered:  # f : (address buffered-file), msg : (address array byte)
     57/push-edi
     # eax = msg
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         0/r32/eax   0xc/disp8       .                 # copy *(ebp+12) to eax
-    # in/esi = msg->data
+    # var in/esi : (address byte) = msg->data
     8d/copy-address                 1/mod/*+disp8   0/rm32/eax    .           .             .           6/r32/esi   4/disp8         .                 # copy eax+4 to esi
-    # inend/ecx = &msg->data[msg->length]
+    # var inend/ecx : (address byte) = &msg->data[msg->length]
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 0/mod/indirect  4/rm32/sib    6/base/esi  1/index/ecx   .           1/r32/ecx   .               .                 # copy esi+ecx to ecx
     # edi = f
@@ -206,8 +206,8 @@ test-write-buffered-with-intermediate-flush:
 == data
 
 # The buffered file for standard error.
-Stderr:
-    # file descriptor or (address stream)
+Stderr:  # (ref buffered-file)
+    # file descriptor or (address stream byte)
     2/imm32  # standard error
 $Stderr->buffer:
     # inlined fields for a stream
diff --git a/066print-int.subx b/066print-int.subx
index cf043635..a1f968f7 100644
--- a/066print-int.subx
+++ b/066print-int.subx
@@ -5,7 +5,8 @@
 # . 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
 
-to-hex-char:  # in/eax : nibble -> out/eax : byte
+# convert the lowest nibble of eax to ascii and return it in the lowest byte of eax
+to-hex-char:  # in/eax : int -> out/eax : int
     # no error checking; accepts argument in eax
     # if (eax <= 9) return eax + '0'
     3d/compare-eax-with  0x9/imm32/9
@@ -17,7 +18,7 @@ $to-hex-char:else:
     05/add-to-eax  0x57/imm32/a-10
     c3/return
 
-append-byte-hex:  # f : (address stream), n : int
+append-byte-hex:  # f : (address stream byte), n : int
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
@@ -174,7 +175,7 @@ test-print-byte-buffered:
     # . end
     c3/return
 
-print-int32:  # f : (address stream), n : int
+print-int32:  # f : (address stream byte), n : int
     # pseudocode:
     #  write(f, "0x")
     #  ecx = 28
diff --git a/067parse-hex.subx b/067parse-hex.subx
index b5694b8c..105943ca 100644
--- a/067parse-hex.subx
+++ b/067parse-hex.subx
@@ -18,7 +18,7 @@ is-hex-int?:  # in : (address slice) -> eax : boolean
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
     # edx = s->end
     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           2/r32/edx   4/disp8         .                 # copy *(ecx+4) to edx
-    # curr/ecx = s->start
+    # var curr/ecx : (address byte) = s->start
     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           1/r32/ecx   .               .                 # copy *ecx to ecx
     # if s is empty return false
     b8/copy-to-eax  0/imm32/false
@@ -56,7 +56,7 @@ $is-hex-int?:loop:
     # if (curr >= in->end) return true
     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
     73/jump-if-greater-or-equal-unsigned  $is-hex-int?:true/disp8
-    # eax = is-hex-digit?(*curr)
+    # var eax : boolean = is-hex-digit?(*curr)
     # . . push args
     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
     50/push-eax
@@ -93,7 +93,7 @@ test-is-hex-int:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -127,7 +127,7 @@ test-is-hex-int-handles-letters:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -161,7 +161,7 @@ test-is-hex-int-with-trailing-char:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -195,7 +195,7 @@ test-is-hex-int-with-leading-char:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -224,7 +224,7 @@ test-is-hex-int-empty:
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
-    # var slice/ecx = ""
+    # var slice/ecx : (ref slice) = ""
     68/push  0/imm32
     68/push  0/imm32
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -258,7 +258,7 @@ test-is-hex-int-handles-0x-prefix:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -292,7 +292,7 @@ test-is-hex-int-handles-negative:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -326,7 +326,7 @@ test-is-hex-int-handles-negative-0x-prefix:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -351,7 +351,7 @@ test-is-hex-int-handles-negative-0x-prefix:
     5d/pop-to-ebp
     c3/return
 
-parse-hex-int:  # in : (address slice) -> result/eax
+parse-hex-int:  # in : (address slice) -> result/eax : int
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
@@ -360,18 +360,18 @@ parse-hex-int:  # in : (address slice) -> result/eax
     52/push-edx
     53/push-ebx
     56/push-esi
-    # result/ebx = 0
+    # var result/ebx : int = 0
     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
-    # ecx = s
+    # ecx = in
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
-    # edx = s->end
+    # edx = in->end
     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           2/r32/edx   4/disp8         .                 # copy *(ecx+4) to edx
-    # curr/ecx = s->start
+    # var curr/ecx : (address byte) = in->start
     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           1/r32/ecx   .               .                 # copy *ecx to ecx
-    # negate?/esi = false
+    # var negate?/esi : boolean = false
     31/xor                          3/mod/direct    6/rm32/esi    .           .             .           6/r32/esi   .               .                 # clear esi
 $parse-hex-int:negative:
-    # . if (*curr == '-') negate = true
+    # if (*curr == '-') ++curr, negate = true
     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
     3d/compare-eax-and  0x2d/imm32/-
@@ -403,7 +403,7 @@ $parse-hex-int:loop:
     # if (curr >= in->end) break
     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
     73/jump-if-greater-or-equal-unsigned  $parse-hex-int:negate/disp8
-    # eax = from-hex-char(*curr)
+    # var eax : int = from-hex-char(*curr)
     # . . copy arg to eax
     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
     # . . call
@@ -416,10 +416,12 @@ $parse-hex-int:loop:
     # loop
     eb/jump  $parse-hex-int:loop/disp8
 $parse-hex-int:negate:
-    81          7/subop/compare     3/mod/direct    6/rm32/esi    .           .             .           .           .               0/imm32           # compare esi
+    # if (negate?) result = -result
+    81          7/subop/compare     3/mod/direct    6/rm32/esi    .           .             .           .           .               0/imm32/false     # compare esi
     74/jump-if-equal  $parse-hex-int:end/disp8
     f7          3/subop/negate      3/mod/direct    3/rm32/ebx    .           .             .           .           .               .                 # negate ebx
 $parse-hex-int:end:
+    # return result
     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           3/r32/ebx   .               .                 # copy ebx to eax
     # . restore registers
     5e/pop-to-esi
@@ -440,7 +442,7 @@ test-parse-hex-int-single-digit:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -474,7 +476,7 @@ test-parse-hex-int-multi-digit:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -508,7 +510,7 @@ test-parse-hex-int-0x-prefix:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -542,7 +544,7 @@ test-parse-hex-int-zero:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -576,7 +578,7 @@ test-parse-hex-int-0-prefix:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -610,7 +612,7 @@ test-parse-hex-int-negative:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
diff --git a/068error-byte.subx b/068error-byte.subx
index 5284c034..5490f890 100644
--- a/068error-byte.subx
+++ b/068error-byte.subx
@@ -6,7 +6,7 @@
 # . 1-3 bytes   3 bits              2 bits          3 bits        3 bits      3 bits        2 bits      2 bits      0/1/2/4 bytes   0/1/2/4 bytes
 
 #? Entry:  # manual test
-#?     # . var ed/eax : exit-descriptor
+#?     # . var ed/eax : (ref exit-descriptor)
 #?     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
 #?     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           4/r32/esp   .               .                 # copy esp to eax
 #?     # . configure ed to really exit()
@@ -68,7 +68,7 @@ error-byte:  # ed : (address exit-descriptor), out : (address buffered-file), ms
     e8/call  write-buffered/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-    # . flush(out)
+    # flush(out)
     # . . push args
     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     # . . call
diff --git a/069allocate.subx b/069allocate.subx
index df3c05db..9b4dff57 100644
--- a/069allocate.subx
+++ b/069allocate.subx
@@ -19,14 +19,14 @@
 == data
 
 # A default allocation descriptor for programs to use.
-Heap:
+Heap:  # (ref allocation-descriptor)
   # curr
   0/imm32
   # limit
   0/imm32
 
 # a reasonable default
-Heap-size:
+Heap-size:  # (ref int)
   0x200000/imm32/2MB
 
 == code
@@ -56,7 +56,7 @@ $array-equal-main:end:
 
 # Claim the next 'n' bytes of memory starting at ad->curr and update ad->curr.
 # Abort if there isn't enough memory in 'ad'.
-allocate:  # ad : (address allocation-descriptor), n : int -> address-or-null/eax
+allocate:  # ad : (address allocation-descriptor), n : int -> address-or-null/eax : (address _)
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
@@ -104,11 +104,11 @@ test-allocate-success:
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
-    # var ad/ecx : (address allocation-descriptor) = {11, 15}
+    # var ad/ecx : (ref allocation-descriptor) = {11, 15}
     68/push  0xf/imm32/limit
     68/push  0xb/imm32/curr
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
-    # eax = allocate(ad, 3)
+    # var eax : (handle byte) = allocate(ad, 3)
     # . . push args
     68/push  3/imm32
     51/push-ecx
@@ -143,11 +143,11 @@ _pending-test-allocate-failure:
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
-    # var ad/ecx : (address allocation-descriptor) = {11, 15}
+    # var ad/ecx : (ref allocation-descriptor) = {11, 15}
     68/push  0xf/imm32/limit
     68/push  0xb/imm32/curr
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
-    # eax = allocate(ad, 6)
+    # var eax : (handle byte) = allocate(ad, 6)
     # . . push args
     68/push  6/imm32
     51/push-ecx
@@ -180,7 +180,7 @@ _pending-test-allocate-failure:
     c3/return
 
 # helper: create a nested allocation descriptor (useful for tests)
-allocate-region:  # ad : (address allocation-descriptor), n : int -> new-ad : (address allocation-descriptor)
+allocate-region:  # ad : (address allocation-descriptor), n : int -> new-ad : (handle allocation-descriptor)
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
diff --git a/070new-stream.subx b/070new-stream.subx
index e1bea314..45076c91 100644
--- a/070new-stream.subx
+++ b/070new-stream.subx
@@ -5,13 +5,13 @@
 # . 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
 
-new-stream:  # ad : (address allocation-descriptor), length : int, elemsize : int -> address/eax
+new-stream:  # ad : (address allocation-descriptor), length : int, elemsize : int -> address/eax : (handle stream _)
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     # . save registers
     52/push-edx
-    # n = elemsize * length + 12 (for read, write and length)
+    # var n/eax : int = elemsize * length + 12 (for read, write and length)
     # . eax = elemsize
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   0x10/disp8      .                 # copy *(ebp+16) to eax
     # . eax *= length
@@ -24,7 +24,7 @@ new-stream:  # ad : (address allocation-descriptor), length : int, elemsize : in
     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           0/r32/eax   .               .                 # copy eax to edx
     # . eax += 12
     05/add-to-eax  0xc/imm32
-    # allocate(ad, n)
+    # var eax : (handle stream _) = allocate(ad, n)
     # . . push args
     50/push-eax
     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
@@ -68,7 +68,7 @@ test-new-stream:
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
-    # var heap/ecx : (address allocation-descriptor) = {0, 0}
+    # var heap/ecx : (ref 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
@@ -82,7 +82,7 @@ test-new-stream:
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     # var start/edx = ad->curr
     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # copy *ecx to edx
-    # eax = new-stream(heap, 3, 2)
+    # var eax : (handle stream byte) = new-stream(heap, 3, 2)
     # . . push args
     68/push  2/imm32
     68/push  3/imm32
diff --git a/071read-line.subx b/071read-line.subx
index 6fe8deb7..ab336c52 100644
--- a/071read-line.subx
+++ b/071read-line.subx
@@ -216,7 +216,7 @@ test-read-line-buffered-reads-final-line-until-Eof:
 # read bytes from 'f' until (and including) a newline and store them into 's'
 # 's' fails to grow if and only if no data found
 # just abort if 's' is too small
-read-line:  # f : (address stream), s : (address stream byte)
+read-line:  # f : (address stream byte), s : (address stream byte)
     # pseudocode:
     #   while true
     #     if (s->write >= s->length) abort
diff --git a/072slice.subx b/072slice.subx
index 6ecdc003..30c1b356 100644
--- a/072slice.subx
+++ b/072slice.subx
@@ -34,7 +34,7 @@ test-slice-empty-true:
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
-    # var slice/ecx = {34, 34}
+    # var slice/ecx : (ref slice) = {34, 34}
     68/push  34/imm32/end
     68/push  34/imm32/start
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -63,7 +63,7 @@ test-slice-empty-false:
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
-    # var slice/ecx = {34, 23}
+    # var slice/ecx : (ref slice) = {34, 23}
     68/push  23/imm32/end
     68/push  34/imm32/start
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -88,7 +88,7 @@ test-slice-empty-false:
     5d/pop-to-ebp
     c3/return
 
-slice-equal?:  # s : (address slice), p : (address string) -> eax : boolean
+slice-equal?:  # s : (address slice), p : (address array byte) -> eax : boolean
     # pseudocode:
     #   if (p == 0) return (s == 0)
     #   currs = s->start
@@ -118,11 +118,11 @@ slice-equal?:  # s : (address slice), p : (address string) -> eax : boolean
     56/push-esi
     # esi = s
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
-    # currs/edx = s->start
+    # var currs/edx : (address byte) = s->start
     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           2/r32/edx   .               .                 # copy *esi to edx
-    # maxs/esi = s->end
+    # var maxs/esi : (address byte) = s->end
     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           6/r32/esi   4/disp8         .                 # copy *(esi+4) to esi
-    # eax = maxs - currs
+    # var slen/eax : int = maxs - currs
     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           6/r32/esi   .               .                 # copy esi to eax
     29/subtract                     3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # subtract edx from eax
     # ebx = p
@@ -136,23 +136,24 @@ $slice-equal?:null-string:
     74/jump-if-equal  $slice-equal?:true/disp8
     eb/jump  $slice-equal?:false/disp8
 $slice-equal?:nonnull-string:
-    # if (eax != p->length) return false
+    # if (slen != p->length) return false
     39/compare                      0/mod/indirect  3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # compare *ebx and eax
     75/jump-if-not-equal  $slice-equal?:false/disp8
-    # currp/ebx = p->data
+    # var currp/ebx : (address byte) = p->data
     81          0/subop/add         3/mod/direct    3/rm32/ebx    .           .             .           .           .               4/imm32           # add to ebx
-    # eax = ecx = 0
+    # var c1/eax : byte = 0
     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
+    # var c2/ecx : byte = 0
     31/xor                          3/mod/direct    1/rm32/ecx    .           .             .           1/r32/ecx   .               .                 # clear ecx
 $slice-equal?:loop:
     # if (currs >= maxs) return true
     39/compare                      3/mod/direct    2/rm32/edx    .           .             .           6/r32/esi   .               .                 # compare edx with esi
     73/jump-if-greater-or-equal-unsigned  $slice-equal?:true/disp8
-    # AL = *currp
+    # c1 = *currp
     8a/copy-byte                    0/mod/indirect  3/rm32/ebx    .           .             .           0/r32/AL    .               .                 # copy byte at *ebx to AL
-    # CL = *currs
+    # c2 = *currs
     8a/copy-byte                    0/mod/indirect  2/rm32/edx    .           .             .           1/r32/CL    .               .                 # copy byte at *edx to CL
-    # if (eax != ecx) return false
+    # if (c1 != c2) return false
     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # compare eax and ecx
     75/jump-if-not-equal  $slice-equal?:false/disp8
     # ++currp
@@ -186,7 +187,7 @@ test-slice-equal:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -222,7 +223,7 @@ test-slice-equal-false:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -258,7 +259,7 @@ test-slice-equal-too-long:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -294,7 +295,7 @@ test-slice-equal-too-short:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -325,7 +326,7 @@ test-slice-equal-empty:
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
-    # var slice/ecx
+    # var slice/ecx : (ref slice)
     68/push  0/imm32/end
     68/push  0/imm32/start
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -361,7 +362,7 @@ test-slice-equal-with-empty:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -392,7 +393,7 @@ test-slice-equal-empty-with-empty:
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
-    # var slice/ecx
+    # var slice/ecx : (ref slice)
     68/push  0/imm32/end
     68/push  0/imm32/start
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -428,7 +429,7 @@ test-slice-equal-with-null:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -454,7 +455,7 @@ test-slice-equal-with-null:
     5d/pop-to-ebp
     c3/return
 
-slice-starts-with?:  # s : (address slice), head : (address string) -> eax : boolean
+slice-starts-with?:  # s : (address slice), head : (address array byte) -> eax : boolean
     # pseudocode
     #   lenh = head->length
     #   if (lenh > s->end - s->start) return false
@@ -487,34 +488,35 @@ slice-starts-with?:  # s : (address slice), head : (address string) -> eax : boo
     57/push-edi
     # esi = s
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
-    # ecx = s->end - s->start
+    # var lens/ecx : int = s->end - s->start
     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(esi+4) to ecx
     2b/subtract                     0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # subtract *esi from ecx
     # edi = head
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           7/r32/edi   0xc/disp8       .                 # copy *(ebp+12) to edi
-    # lenh/edx = head->length
+    # var lenh/edx : int = head->length
     8b/copy                         0/mod/indirect  7/rm32/edi    .           .             .           2/r32/edx   .               .                 # copy *edi to edx
-    # if (lenh > s->end - s->start) return false
+    # if (lenh > lens) return false
     39/compare                      3/mod/direct    2/rm32/edx    .           .             .           1/r32/ecx   .               .                 # compare edx with ecx
     7f/jump-if-greater  $slice-starts-with?:false/disp8
-    # currs/esi = s->start
+    # var currs/esi : (address byte) = s->start
     8b/subtract                     0/mod/indirect  6/rm32/esi    .           .             .           6/r32/esi   .               .                 # copy *esi to esi
-    # currh/edi = head->data
+    # var currh/edi : (address byte) = head->data
     81          0/subop/add         3/mod/direct    7/rm32/edi    .           .             .           .           .               4/imm32           # add to edi
-    # i/ecx = 0
+    # var i/ecx : int = 0
     31/xor                          3/mod/direct    1/rm32/ecx    .           .             .           1/r32/ecx   .               .                 # clear ecx
-    # eax = ebx = 0
+    # var c1/eax : byte = 0
     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
+    # var c2/ebx : byte = 0
     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
 $slice-starts-with?:loop:
     # if (i >= lenh) return true
     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
     7d/jump-if-greater-or-equal  $slice-starts-with?:true/disp8
-    # AL = *currs
+    # c1 = *currs
     8a/copy-byte                    0/mod/indirect  6/rm32/esi    .           .             .           0/r32/AL    .               .                 # copy byte at *esi to AL
-    # BL = *currh
+    # c2 = *currh
     8a/copy-byte                    0/mod/indirect  7/rm32/edi    .           .             .           3/r32/BL    .               .                 # copy byte at *edi to BL
-    # if (*currs != *currh) return false
+    # if (c1 != c2) return false
     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           3/r32/ebx   .               .                 # compare eax and ebx
     75/jump-if-not-equal  $slice-starts-with?:false/disp8
     # ++i
@@ -551,7 +553,7 @@ test-slice-starts-with-single-character:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -587,7 +589,7 @@ test-slice-starts-with-empty-string:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -623,7 +625,7 @@ test-slice-starts-with-multiple-characters:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -659,7 +661,7 @@ test-slice-starts-with-entire-string:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -695,7 +697,7 @@ test-slice-starts-with-fails:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -731,7 +733,7 @@ test-slice-starts-with-fails-2:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -759,7 +761,7 @@ test-slice-starts-with-fails-2:
 
 # write a slice to a stream
 # abort if the stream doesn't have enough space
-write-slice:  # out : (address stream), s : (address slice)
+write-slice:  # out : (address stream byte), s : (address slice)
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
@@ -772,9 +774,9 @@ write-slice:  # out : (address stream), s : (address slice)
     57/push-edi
     # esi = s
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   0xc/disp8       .                 # copy *(ebp+12) to esi
-    # curr/ecx = s->start
+    # var curr/ecx : (address byte) = s->start
     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # copy *esi to ecx
-    # max/esi = s->end
+    # var max/esi : (address byte) = s->end
     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           6/r32/esi   4/disp8         .                 # copy *(esi+4) to esi
     # edi = out
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         7/r32/edi   8/disp8         .                 # copy *(ebp+8) to edi
@@ -848,7 +850,7 @@ test-write-slice:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -888,9 +890,9 @@ write-slice-buffered:  # out : (address buffered-file), s : (address slice)
     57/push-edi
     # esi = s
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   0xc/disp8       .                 # copy *(ebp+12) to esi
-    # curr/ecx = s->start
+    # var curr/ecx : (address byte) = s->start
     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # copy *esi to ecx
-    # max/esi = s->end
+    # var max/esi : (address byte) = s->end
     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           6/r32/esi   4/disp8         .                 # copy *(esi+4) to esi
     # edi = out
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         7/r32/edi   8/disp8         .                 # copy *(ebp+8) to edi
@@ -976,7 +978,7 @@ test-write-slice-buffered:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -1010,7 +1012,7 @@ test-write-slice-buffered:
     c3/return
 
 # copy a slice into a new (dynamically allocated) string
-slice-to-string:  # ad : (address allocation-descriptor), in : (address slice) -> out/eax : (address array)
+slice-to-string:  # ad : (address allocation-descriptor), in : (address slice) -> out/eax : (address array byte)
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
@@ -1021,15 +1023,15 @@ slice-to-string:  # ad : (address allocation-descriptor), in : (address slice) -
     56/push-esi
     # esi = in
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   0xc/disp8       .                 # copy *(ebp+12) to esi
-    # curr/edx = in->start
+    # var curr/edx : (address byte) = in->start
     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           2/r32/edx   .               .                 # copy *esi to edx
-    # max/ebx = in->end
+    # var max/ebx : (address byte) = in->end
     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           3/r32/ebx   4/disp8         .                 # copy *(esi+4) to ebx
-    # size/ecx = max - curr + 4  # total size of output string (including the initial length)
+    # var size/ecx : int = max - curr + 4  # total size of output string (including the initial length)
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           3/r32/ebx   .               .                 # copy ebx to ecx
     29/subtract                     3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # subtract edx from ecx
     81          0/subop/add         3/mod/direct    1/rm32/ecx    .           .             .           .           .               4/imm32           # add to ecx
-    # out/eax = allocate(ad, size)
+    # var out/eax : (handle array byte) = allocate(ad, size)
     # . . push args
     51/push-ecx
     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
@@ -1040,7 +1042,7 @@ slice-to-string:  # ad : (address allocation-descriptor), in : (address slice) -
     # if (eax == 0) abort
     3d/compare-eax-and  0/imm32
     74/jump-if-equal  $slice-to-string:abort/disp8
-    # *out = size-4
+    # out->length = size-4
     89/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy ecx to *eax
     81          5/subop/subtract    0/mod/indirect  0/rm32/eax    .           .             .           .           .               4/imm32           # subtract 4 from *eax
     # save out
@@ -1092,7 +1094,7 @@ test-slice-to-string:
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
-    # var heap/edx : (address allocation-descriptor) = {0, 0}
+    # var heap/edx : (ref allocation-descriptor)
     68/push  0/imm32/limit
     68/push  0/imm32/curr
     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
@@ -1109,7 +1111,7 @@ test-slice-to-string:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
diff --git a/073next-token.subx b/073next-token.subx
index 35414883..567f0cce 100644
--- a/073next-token.subx
+++ b/073next-token.subx
@@ -7,7 +7,7 @@
 
 # extract the next run of characters that are different from a given 'delimiter' (skipping multiple delimiters if necessary)
 # on reaching end of file, return an empty interval
-next-token:  # in : (address stream), delimiter : byte, out : (address slice)
+next-token:  # in : (address stream byte), delimiter : byte, out : (address slice)
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
@@ -66,7 +66,7 @@ test-next-token:
     e8/call  clear-stream/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # var slice/ecx = {0, 0}
+    # var slice/ecx : (ref slice)
     68/push  0/imm32/end
     68/push  0/imm32/start
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -130,7 +130,7 @@ test-next-token-Eof:
     e8/call  clear-stream/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # var slice/ecx = {0, 0}
+    # var slice/ecx : (ref slice)
     68/push  0/imm32/end
     68/push  0/imm32/start
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -216,7 +216,7 @@ test-next-token-from-slice:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var out/edi : (address slice) = {0, 0}
+    # var out/edi : (ref slice)
     68/push  0/imm32/end
     68/push  0/imm32/start
     89/copy                         3/mod/direct    7/rm32/edi    .           .             .           4/r32/esp   .               .                 # copy esp to edi
@@ -265,7 +265,7 @@ test-next-token-from-slice-Eof:
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
-    # var out/edi : (address slice) = {0, 0}
+    # var out/edi : (ref slice)
     68/push  0/imm32/end
     68/push  0/imm32/start
     89/copy                         3/mod/direct    7/rm32/edi    .           .             .           4/r32/esp   .               .                 # copy esp to edi
@@ -306,7 +306,7 @@ test-next-token-from-slice-nothing:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var out/edi : (address slice) = {0, 0}
+    # var out/edi : (ref slice)
     68/push  0/imm32/end
     68/push  0/imm32/start
     89/copy                         3/mod/direct    7/rm32/edi    .           .             .           4/r32/esp   .               .                 # copy esp to edi
@@ -338,7 +338,7 @@ test-next-token-from-slice-nothing:
     5d/pop-to-ebp
     c3/return
 
-skip-chars-matching:  # in : (address stream), delimiter : byte
+skip-chars-matching:  # in : (address stream byte), delimiter : byte
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
@@ -461,7 +461,7 @@ test-skip-chars-matching-none:
     # end
     c3/return
 
-skip-chars-matching-whitespace:  # in : (address stream)
+skip-chars-matching-whitespace:  # in : (address stream byte)
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
@@ -551,7 +551,7 @@ test-skip-chars-matching-whitespace:
     c3/return
 
 # minor fork of 'skip-chars-matching'
-skip-chars-not-matching:  # in : (address stream), delimiter : byte
+skip-chars-not-matching:  # in : (address stream byte), delimiter : byte
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
@@ -713,7 +713,7 @@ test-skip-chars-not-matching-all:
     # end
     c3/return
 
-skip-chars-not-matching-whitespace:  # in : (address stream)
+skip-chars-not-matching-whitespace:  # in : (address stream byte)
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
@@ -815,14 +815,15 @@ skip-chars-matching-in-slice:  # curr : (address byte), end : (address byte), de
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   0xc/disp8       .                 # copy *(ebp+12) to ecx
     # edx = delimiter
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           2/r32/edx   0x10/disp8       .                 # copy *(ebp+16) to edx
-    # ebx = 0
+    # var c/ebx : byte = 0
     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
 $skip-chars-matching-in-slice:loop:
     # if (curr >= end) break
     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # compare eax with ecx
     73/jump-if-greater-or-equal-unsigned  $skip-chars-matching-in-slice:end/disp8
-    # if (*curr != delimiter) break
+    # c = *curr
     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           3/r32/BL    .               .                 # copy byte at *eax to BL
+    # if (c != delimiter) break
     39/compare                      3/mod/direct    3/rm32/ebx    .           .             .           2/r32/edx   .               .                 # compare ebx and edx
     75/jump-if-not-equal  $skip-chars-matching-in-slice:end/disp8
     # ++curr
@@ -896,7 +897,7 @@ test-skip-chars-matching-in-slice-none:
     # end
     c3/return
 
-skip-chars-matching-whitespace-in-slice:  # in : (address stream)
+skip-chars-matching-whitespace-in-slice:  # curr : (address byte), end : (address byte) -> curr/eax
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
@@ -907,24 +908,24 @@ skip-chars-matching-whitespace-in-slice:  # in : (address stream)
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   8/disp8         .                 # copy *(ebp+8) to eax
     # ecx = end
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   0xc/disp8       .                 # copy *(ebp+12) to ecx
-    # ebx = 0
+    # var c/ebx : byte = 0
     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
 $skip-chars-matching-whitespace-in-slice:loop:
     # if (curr >= end) break
     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # compare eax with ecx
     0f 83/jump-if-greater-or-equal-unsigned  $skip-chars-matching-in-slice:end/disp32
-    # ebx = *curr
+    # c = *curr
     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           3/r32/BL    .               .                 # copy byte at *eax to BL
-    # if (*curr == ' ') goto body
+    # if (c == ' ') goto body
     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x20/imm32/space  # compare ebx
     74/jump-if-equal  $skip-chars-matching-whitespace-in-slice:body/disp8
-    # if (*curr == '\n') goto body
+    # if (c == '\n') goto body
     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x0a/imm32/newline  # compare ebx
     74/jump-if-equal  $skip-chars-matching-whitespace-in-slice:body/disp8
-    # if (*curr == '\t') goto body
+    # if (c == '\t') goto body
     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x09/imm32/tab    # compare ebx
     74/jump-if-equal  $skip-chars-matching-whitespace-in-slice:body/disp8
-    # if (*curr != '\r') break
+    # if (c != '\r') break
     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x0d/imm32/cr     # compare ebx
     75/jump-if-not-equal  $skip-chars-matching-whitespace-in-slice:end/disp8
 $skip-chars-matching-whitespace-in-slice:body:
@@ -983,14 +984,15 @@ skip-chars-not-matching-in-slice:  # curr : (address byte), end : (address byte)
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   0xc/disp8       .                 # copy *(ebp+12) to ecx
     # edx = delimiter
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           2/r32/edx   0x10/disp8       .                 # copy *(ebp+16) to edx
-    # ebx = 0
+    # var c/ebx : byte = 0
     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
 $skip-chars-not-matching-in-slice:loop:
     # if (curr >= end) break
     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # compare eax with ecx
     73/jump-if-greater-or-equal-unsigned  $skip-chars-not-matching-in-slice:end/disp8
-    # if (*curr == delimiter) break
+    # c = *curr
     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           3/r32/BL    .               .                 # copy byte at *eax to BL
+    # if (c == delimiter) break
     39/compare                      3/mod/direct    3/rm32/ebx    .           .             .           2/r32/edx   .               .                 # compare ebx and edx
     74/jump-if-equal  $skip-chars-not-matching-in-slice:end/disp8
     # ++curr
@@ -1093,7 +1095,7 @@ test-skip-chars-not-matching-in-slice-all:
     # end
     c3/return
 
-skip-chars-not-matching-whitespace-in-slice:  # in : (address stream)
+skip-chars-not-matching-whitespace-in-slice:  # curr : (address byte), end : (address byte) -> curr/eax
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
@@ -1104,24 +1106,24 @@ skip-chars-not-matching-whitespace-in-slice:  # in : (address stream)
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   8/disp8         .                 # copy *(ebp+8) to eax
     # ecx = end
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   0xc/disp8       .                 # copy *(ebp+12) to ecx
-    # ebx = 0
+    # var c/ebx : byte = 0
     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
 $skip-chars-not-matching-whitespace-in-slice:loop:
     # if (curr >= end) break
     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # compare eax with ecx
     0f 83/jump-if-greater-or-equal-unsigned  $skip-chars-not-matching-in-slice:end/disp32
-    # ebx = *curr
+    # c = *curr
     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           3/r32/BL    .               .                 # copy byte at *eax to BL
-    # if (*curr == ' ') break
+    # if (c == ' ') break
     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x20/imm32/space  # compare ebx
     74/jump-if-equal  $skip-chars-not-matching-whitespace-in-slice:end/disp8
-    # if (*curr == '\n') break
+    # if (c == '\n') break
     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x0a/imm32/newline  # compare ebx
     74/jump-if-equal  $skip-chars-not-matching-whitespace-in-slice:end/disp8
-    # if (*curr == '\t') break
+    # if (c == '\t') break
     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x09/imm32/tab    # compare ebx
     74/jump-if-equal  $skip-chars-not-matching-whitespace-in-slice:end/disp8
-    # if (*curr == '\r') break
+    # if (c == '\r') break
     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x0d/imm32/cr     # compare ebx
     74/jump-if-equal  $skip-chars-not-matching-whitespace-in-slice:end/disp8
     # ++curr
@@ -1166,7 +1168,7 @@ test-skip-chars-not-matching-whitespace-in-slice:
 
 # update line->read to end of string literal surrounded by double quotes
 # line->read must start out at a double-quote
-skip-string:  # line : (address stream)
+skip-string:  # line : (address stream byte)
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
@@ -1420,7 +1422,7 @@ skip-string-in-slice:  # curr : (address byte), end : (address byte) -> new_curr
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
     # edx = end
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         2/r32/edx   0xc/disp8         .               # copy *(ebp+12) to edx
-    # eax = 0
+    # var c/eax : byte = 0
     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
     # skip initial dquote
     41/increment-ecx
@@ -1428,14 +1430,14 @@ $skip-string-in-slice:loop:
     # if (curr >= end) return curr
     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
     73/jump-if-greater-unsigned-or-equal  $skip-string-in-slice:return-curr/disp8
-    # AL = *curr
+    # c = *curr
     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
 $skip-string-in-slice:dquote:
-    # if (eax == '"') break
+    # if (c == '"') break
     3d/compare-eax-and  0x22/imm32/double-quote
     74/jump-if-equal  $skip-string-in-slice:break/disp8
 $skip-string-in-slice:check-for-escape:
-    # if (eax == '\') escape next char
+    # if (c == '\') escape next char
     3d/compare-eax-and  0x5c/imm32/backslash
     75/jump-if-not-equal  $skip-string-in-slice:continue/disp8
 $skip-string-in-slice:escape:
@@ -1594,7 +1596,7 @@ test-skip-string-in-slice-stops-at-end:
 
 # update line->read to ')'
 # line->read ends at ')'
-skip-until-close-paren:  # line : (address stream)
+skip-until-close-paren:  # line : (address stream byte)
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
@@ -1781,7 +1783,7 @@ test-skip-until-close-paren-works-from-mid-stream:
     5d/pop-to-ebp
     c3/return
 
-skip-until-close-paren-in-slice:  # curr : (address byte), end : (address byte) -> new_curr/eax
+skip-until-close-paren-in-slice:  # curr : (address byte), end : (address byte) -> new_curr/eax : (address byte)
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
@@ -1792,7 +1794,7 @@ skip-until-close-paren-in-slice:  # curr : (address byte), end : (address byte)
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
     # edx = end
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         2/r32/edx   0xc/disp8         .               # copy *(ebp+12) to edx
-    # eax = 0
+    # var c/eax : byte = 0
     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
     # skip initial dquote
     41/increment-ecx
@@ -1800,10 +1802,10 @@ $skip-until-close-paren-in-slice:loop:
     # if (curr >= end) break
     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
     73/jump-if-greater-unsigned-or-equal  $skip-until-close-paren-in-slice:break/disp8
-    # AL = *curr
+    # c = *curr
     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
 $skip-until-close-paren-in-slice:check-close:
-    # if (eax == ')') break
+    # if (c == ')') break
     3d/compare-eax-and  0x29/imm32/close-paren
     74/jump-if-equal  $skip-until-close-paren-in-slice:break/disp8
     # ++curr
diff --git a/074write-stream-data.subx b/074write-stream-data.subx
index 25a8f87c..074e9d29 100644
--- a/074write-stream-data.subx
+++ b/074write-stream-data.subx
@@ -8,7 +8,7 @@
 #   - construct a 'maximal slice' and pass it to write-slice-buffered
 #   - flush the buffered-file and pass the stream directly to its fd (disabling buffering)
 # we'll go with the first way for now
-write-stream-data:  # f : (address buffered-file), s : (address stream)
+write-stream-data:  # f : (address buffered-file), s : (address stream byte)
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
@@ -18,8 +18,8 @@ write-stream-data:  # f : (address buffered-file), s : (address stream)
     56/push-esi
     # esi = s
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   0xc/disp8       .                 # copy *(ebp+12) to esi
-    # var slice/ecx = {s->data, s->data + s->write}
-    # . push s->data + s->write
+    # var slice/ecx : (ref slice) = {s->data, &s->data[s->write]}
+    # . push &s->data[s->write]
     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/esi  0/index/eax   .           0/r32/eax   0xc/disp8       .                 # copy esi+eax+12 to eax
     50/push-eax
diff --git a/075print-int-decimal.subx b/075print-int-decimal.subx
index a9cfa5fc..8e8c7da4 100644
--- a/075print-int-decimal.subx
+++ b/075print-int-decimal.subx
@@ -5,7 +5,7 @@
 # . 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
 
-print-int32-decimal:  # out : (address stream), n : int32
+print-int32-decimal:  # out : (address stream byte), n : int32
     # works by generating characters from lowest to highest and pushing them
     # to the stack, before popping them one by one into the stream
     #
@@ -44,11 +44,11 @@ print-int32-decimal:  # out : (address stream), n : int32
     52/push-edx
     53/push-ebx
     57/push-edi
-    # ten/ecx = 10
+    # const ten/ecx = 10
     b9/copy-to-ecx  0xa/imm32
     # push sentinel
     68/push  0/imm32/sentinel
-    # eax = abs(n)
+    # var eax : int = abs(n)
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   0xc/disp8       .                 # copy *(ebp+12) to eax
     3d/compare-eax-with  0/imm32
     7d/jump-if-greater-or-equal  $print-int32-decimal:read-loop/disp8
@@ -74,11 +74,11 @@ $print-int32-decimal:push-negative:
 $print-int32-decimal:write:
     # edi = out
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           7/r32/edi   8/disp8         .                 # copy *(ebp+8) to edi
-    # w/edx = out->write
+    # var w/edx : int = out->write
     8b/copy                         0/mod/indirect  7/rm32/edi    .           .             .           2/r32/edx   .               .                 # copy *edi to edx
-    # curr/ecx = &out->data[out->write]
+    # var curr/ecx : (address byte) = &out->data[out->write]
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    7/base/edi  2/index/edx   .           1/r32/ecx   0xc/disp8       .                 # copy ebx+edx+12 to ecx
-    # max/ebx = &out->data[out->length]
+    # var max/ebx : (address byte) = &out->data[out->length]
     8b/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           3/r32/ebx   8/disp8         .                 # copy *(edi+8) to ebx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    7/base/edi  3/index/ebx   .           3/r32/ebx   0xc/disp8       .                 # copy edi+ebx+12 to ebx
 $print-int32-decimal:write-loop:
diff --git a/076next-word.subx b/076next-word.subx
index 054dfb47..34dd9c4d 100644
--- a/076next-word.subx
+++ b/076next-word.subx
@@ -35,7 +35,7 @@ $next-word:check0:
     # . if (eax < line->write) goto next check
     3b/compare                      0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # compare eax with *esi
     7c/jump-if-lesser  $next-word:check-for-comment/disp8
-    # . return out = {0, 0}
+    # . return out
     c7          0/subop/copy        0/mod/direct    7/rm32/edi    .           .             .           .           .               0/imm32           # copy to *edi
     c7          0/subop/copy        1/mod/*+disp8   7/rm32/edi    .           .             .           .           4/disp8         0/imm32           # copy to *(edi+4)
     eb/jump  $next-word:end/disp8
@@ -95,7 +95,7 @@ test-next-word:
     e8/call  clear-stream/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # var slice/ecx = {0, 0}
+    # var slice/ecx : (ref slice)
     68/push  0/imm32/end
     68/push  0/imm32/start
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -158,7 +158,7 @@ test-next-word-returns-whole-comment:
     e8/call  clear-stream/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # var slice/ecx = {0, 0}
+    # var slice/ecx : (ref slice)
     68/push  0/imm32/end
     68/push  0/imm32/start
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -221,7 +221,7 @@ test-next-word-returns-empty-string-on-eof:
     e8/call  clear-stream/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # var slice/ecx = {0, 0}
+    # var slice/ecx : (ref slice)
     68/push  0/imm32/end
     68/push  0/imm32/start
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
diff --git a/077subx-words.subx b/077subx-words.subx
index 2b53ebb1..a166217a 100644
--- a/077subx-words.subx
+++ b/077subx-words.subx
@@ -25,9 +25,9 @@ has-metadata?:  # word : (address slice), s : (address string) -> eax : boolean
     57/push-edi
     # esi = word
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
-    # edx = word->end
+    # var edx : (address byte) = word->end
     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           2/r32/edx   4/disp8         .                 # copy *(esi+4) to edx
-    # var twig/edi : (address slice) = {0, 0}
+    # var twig/edi : (ref slice)
     68/push  0/imm32/end
     68/push  0/imm32/start
     89/copy                         3/mod/direct    7/rm32/edi    .           .             .           4/r32/esp   .               .                 # copy esp to edi
@@ -41,15 +41,13 @@ has-metadata?:  # word : (address slice), s : (address string) -> eax : boolean
     e8/call  next-token-from-slice/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
-    # curr/ecx = twig->end
-    8b/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(edi+4) to ecx
 $has-metadata?:loop:
     # next-token-from-slice(curr, word->end, '/', twig)
     # . . push args
     57/push-edi
     68/push  0x2f/imm32/slash
     52/push-edx
-    51/push-ecx
+    ff          6/subop/push        1/mod/*+disp8   7/rm32/edi    .           .             .           .           4/disp8         .                 # push *(edi+4)
     # . . call
     e8/call  next-token-from-slice/disp32
     # . . discard args
@@ -62,8 +60,8 @@ $has-metadata?:loop:
     e8/call  slice-empty?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # . if (eax != 0) return false
-    3d/compare-eax-and  0/imm32
+    # . if (eax != false) return false
+    3d/compare-eax-and  0/imm32/false
     75/jump-if-not-equal  $has-metadata?:false/disp8
     # if (slice-equal?(twig, s)) return true
     # . eax = slice-equal?(twig, s)
@@ -74,15 +72,11 @@ $has-metadata?: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 true
-    3d/compare-eax-and  0/imm32
-    75/jump-if-not-equal  $has-metadata?:true/disp8
-    # curr = twig->end
-    8b/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(edi+4) to ecx
+    # . if (eax != false) return true
+    3d/compare-eax-and  0/imm32/false
+    # eax already contains true
+    75/jump-if-not-equal  $has-metadata?:end/disp8
     eb/jump  $has-metadata?:loop/disp8
-$has-metadata?:true:
-    b8/copy-to-eax  1/imm32/true
-    eb/jump  $has-metadata?:end/disp8
 $has-metadata?:false:
     b8/copy-to-eax  0/imm32/false
 $has-metadata?:end:
@@ -107,7 +101,7 @@ test-has-metadata-true:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var in/esi : (address slice) = {eax, ecx}
+    # var in/esi : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           4/r32/esp   .               .                 # copy esp to esi
@@ -142,7 +136,7 @@ test-has-metadata-false:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var in/esi : (address slice) = {eax, ecx}
+    # var in/esi : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           4/r32/esp   .               .                 # copy esp to esi
@@ -177,7 +171,7 @@ test-has-metadata-ignore-name:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var in/esi : (address slice) = {eax, ecx}
+    # var in/esi : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           4/r32/esp   .               .                 # copy esp to esi
@@ -212,7 +206,7 @@ test-has-metadata-multiple-true:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var in/esi : (address slice) = {eax, ecx}
+    # var in/esi : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           4/r32/esp   .               .                 # copy esp to esi
@@ -247,7 +241,7 @@ test-has-metadata-multiple-false:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var in/esi : (address slice) = {eax, ecx}
+    # var in/esi : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           4/r32/esp   .               .                 # copy esp to esi
@@ -290,22 +284,21 @@ is-valid-name?:  # in : (address slice) -> eax : boolean
     56/push-esi
     # esi = in
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
-    # start/ecx = in->start
+    # var start/ecx : (address byte) = in->start
     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # copy *esi to ecx
-    # end/eax = in->end
-    8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           0/r32/eax   4/disp8         .                 # copy *(esi+4) to eax
 $is-valid-name?:check0:
-    # if (start >= end) return false
-    39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # compare ecx with eax
+    # if (start >= in->end) return false
+    3b/compare                      1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # compare ecx with *(esi+4)
     73/jump-if-greater-or-equal-unsigned  $is-valid-name?:false/disp8
 $is-valid-name?:check1:
-    # eax -= ecx
+    # var len/eax : int = in->end - start
+    8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           0/r32/eax   4/disp8         .                 # copy *(esi+4) to eax
     29/subtract                     3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # subtract ecx from eax
     # if (eax == 2) return false
     3d/compare-eax-and  2/imm32
     74/jump-if-equal  $is-valid-name?:false/disp8
 $is-valid-name?:check2:
-    # c/eax = *ecx
+    # var c/eax : (address byte) = *start
     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
     # if (c == "-") return false
@@ -344,7 +337,7 @@ test-is-valid-name-digit-prefix:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -378,7 +371,7 @@ test-is-valid-name-negative-prefix:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -412,7 +405,7 @@ test-is-valid-name-0x-prefix:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -446,7 +439,7 @@ test-is-valid-name-starts-with-pre-digit:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -480,7 +473,7 @@ test-is-valid-name-starts-with-post-digit:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -514,7 +507,7 @@ test-is-valid-name-starts-with-digit:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -547,12 +540,11 @@ is-label?: # word : (address slice) -> eax : boolean
     51/push-ecx
     # ecx = word
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
-    # ecx = word->end
+    # var end/ecx : (address byte) = word->end
     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(ecx+4) to ecx
-    # return *(word->end - 1) == ':'
-    # . eax = 0
+    # return *(end - 1) == ':'
+    # . eax = *(end-1)
     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
-    # . eax = *((char *) word->end - 1)
     8a/copy-byte                    1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/AL    -1/disp8         .                 # copy byte at *(ecx-1) to AL
     # . return (eax == ':')
     3d/compare-eax-and  0x3a/imm32/colon
@@ -577,7 +569,7 @@ $test-is-label?:true:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -603,7 +595,7 @@ $test-is-label?:false:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
diff --git a/078emit-hex.subx b/078emit-hex.subx
index 2c642481..caea7800 100644
--- a/078emit-hex.subx
+++ b/078emit-hex.subx
@@ -20,13 +20,13 @@ emit-hex:  # out : (address buffered-file), n : int, width : int
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           3/r32/ebx   0xc/disp8       .                 # copy *(ebp+12) to ebx
     # edx = width
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           2/r32/edx   0x10/disp8      .                 # copy *(ebp+16) to edx
-    # var curr/ecx = 0
+    # var curr/ecx : int = 0
     31/xor                          3/mod/direct    1/rm32/ecx    .           .             .           1/r32/ecx   .               .                 # clear ecx
 $emit-hex:loop:
     # if (curr >= width) break
     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
     7d/jump-if-greater-or-equal  $emit-hex:end/disp8
-    # print-byte-buffered(out, ebx)
+    # print-byte-buffered(out, ebx)  # only BL used
     # . . push args
     53/push-ebx
     57/push-edi
diff --git a/079emit.subx b/079emit.subx
index 61ed425e..abf11dce 100644
--- a/079emit.subx
+++ b/079emit.subx
@@ -17,7 +17,7 @@ emit:  # out : (address buffered-file), word : (address slice), width : int
     57/push-edi
     # esi = word
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   0xc/disp8       .                 # copy *(ebp+12) to esi
-    # var name/edi : (address slice) = {0, 0}
+    # var datum/edi : (ref slice)
     68/push  0/imm32/end
     68/push  0/imm32/start
     89/copy                         3/mod/direct    7/rm32/edi    .           .             .           4/r32/esp   .               .                 # copy esp to edi
@@ -39,8 +39,8 @@ emit:  # out : (address buffered-file), word : (address slice), width : int
     e8/call  is-valid-name?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # . if (eax != 0)
-    3d/compare-eax-and  0/imm32
+    # . if (eax != false)
+    3d/compare-eax-and  0/imm32/false
     74/jump-if-equal  $emit:hex-int/disp8
 $emit:name:
     # . write-slice-buffered(out, word)
@@ -63,11 +63,11 @@ $emit:name:
     eb/jump  $emit:end/disp8
     # otherwise emit-hex(out, parse-hex-int(datum), width)
     #   (Weird shit can happen here if the datum of 'word' isn't either a valid
-    #   name or a hex number, but we're only going to be passing in real legal
-    #   programs. We just want to make sure that valid names aren't treated as
-    #   (valid) hex numbers.)
+    #   name or a hex number. `emit` is mostly used by ntranslate, which is
+    #   currently designed to only receive legal SubX programs. We just want to
+    #   make sure that valid names aren't treated as (valid) hex numbers.)
 $emit:hex-int:
-    # . value/eax = parse-hex-int(datum)
+    # . var value/eax : int = parse-hex-int(datum)
     # . . push args
     57/push-edi
     # . . call
@@ -119,7 +119,7 @@ test-emit-number:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -178,7 +178,7 @@ test-emit-negative-number:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -236,7 +236,7 @@ test-emit-number-with-metadata:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -295,7 +295,7 @@ test-emit-non-number:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -353,7 +353,7 @@ test-emit-non-number-with-metadata:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -411,7 +411,7 @@ test-emit-non-number-with-all-hex-digits-and-metadata:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
diff --git a/080zero-out.subx b/080zero-out.subx
index 2dee2665..19e8128f 100644
--- a/080zero-out.subx
+++ b/080zero-out.subx
@@ -5,7 +5,7 @@
 # . 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
 
-zero-out:  # start : address, len : int
+zero-out:  # start : (address byte), len : int
     # pseudocode:
     #   curr/esi = start
     #   i/ecx = 0
@@ -25,7 +25,7 @@ zero-out:  # start : address, len : int
     56/push-esi
     # curr/esi = start
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
-    # i/ecx = 0
+    # var i/ecx : int = 0
     31/xor                          3/mod/direct    1/rm32/ecx    .           .             .           1/r32/ecx   .               .                 # clear ecx
     # edx = len
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           2/r32/edx   0xc/disp8       .                 # copy *(ebp+12) to edx
diff --git a/081table.subx b/081table.subx
index 83cd9102..46329ca0 100644
--- a/081table.subx
+++ b/081table.subx
@@ -1,7 +1,7 @@
 # A table is a stream of (key, value) rows.
 #
-# Each row consists of a 4-byte key (address to a string) and a variable-size
-# value.
+# Each row consists of a 4-byte key -- a 'string_key' which is (address array
+# byte) -- and a variable-size value.
 #
 # Accessing the table performs a linear scan for a key string, and always
 # requires passing in the row size.
@@ -23,7 +23,8 @@
 # . 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
 
 # if no row is found, abort
-get:  # table : (address stream {string, _}), key : (address string), row-size : int, abort-message-prefix : (address string) -> eax : (address _)
+# type string_key = (address array byte)
+get:  # table : (address stream {string_key, T}), key : string_key, row-size : int, abort-message-prefix : (address array byte) -> eax : (address T)
     # pseudocode:
     #   curr = table->data
     #   max = &table->data[table->write]
@@ -42,9 +43,9 @@ get:  # table : (address stream {string, _}), key : (address string), row-size :
     56/push-esi
     # esi = table
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
-    # curr/ecx = table->data
+    # var curr/ecx : (address string_key) = table->data
     8d/copy-address                 1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   0xc/disp8       .                 # copy esi+12 to ecx
-    # max/edx = table->data + table->write
+    # var max/edx : (address 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
 $get:search-loop:
@@ -60,8 +61,8 @@ $get:search-loop:
     e8/call  string-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
-    3d/compare-eax-and  0/imm32
+    # . if (eax != false) return eax = curr+4
+    3d/compare-eax-and  0/imm32/false
     74/jump-if-equal  $get:mismatch/disp8
     8d/copy-address                 1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy ecx+4 to eax
     eb/jump  $get:end/disp8
@@ -124,7 +125,7 @@ test-get:
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     # - setup: create a table with a couple of keys
-    # var table/ecx : (address stream {string, number}) = stream(2 rows * 8 bytes)
+    # var table/ecx : (ref stream {string, number} 16)  # 2 rows * 8 bytes/row
     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
@@ -197,7 +198,7 @@ $test-get:end:
     c3/return
 
 # if no row is found, abort
-get-slice:  # table : (address stream {string, _}), key : (address slice), row-size : int, abort-message-prefix : (address string) -> eax : (address _)
+get-slice:  # table : (address stream {string_key, T}), key : (address slice), row-size : int, abort-message-prefix : (address array byte) -> eax : (address T)
     # pseudocode:
     #   curr = table->data
     #   max = &table->data[table->write]
@@ -216,9 +217,9 @@ get-slice:  # table : (address stream {string, _}), key : (address slice), row-s
     56/push-esi
     # esi = table
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
-    # curr/ecx = table->data
+    # var curr/ecx : (address string_key) = table->data
     8d/copy-address                 1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   0xc/disp8       .                 # copy esi+12 to ecx
-    # max/edx = table->data + table->write
+    # var max/edx : (address 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
 $get-slice:search-loop:
@@ -234,8 +235,8 @@ $get-slice: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
-    3d/compare-eax-and  0/imm32
+    # . if (eax != false) return eax = curr+4
+    3d/compare-eax-and  0/imm32/false
     74/jump-if-equal  $get-slice:mismatch/disp8
     8d/copy-address                 1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy ecx+4 to eax
     eb/jump  $get-slice:end/disp8
@@ -305,7 +306,7 @@ test-get-slice:
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     # - setup: create a table with a couple of keys
-    # var table/ecx : (address stream {string, number}) = stream(2 rows * 8 bytes)
+    # var table/ecx : (ref stream {string, number} 16)  # 2 rows * 8 bytes/row
     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
@@ -335,7 +336,7 @@ $test-get-slice:check1:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           2/r32/edx   .               .                 # copy *eax to edx
     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
     05/add-to-eax  4/imm32
-    # var slice/edx = {eax, edx}
+    # var slice/edx : (ref slice) = {eax, edx}
     52/push-edx
     50/push-eax
     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
@@ -365,7 +366,7 @@ $test-get-slice:check2:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           2/r32/edx   .               .                 # copy *eax to edx
     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
     05/add-to-eax  4/imm32
-    # var slice/edx = {eax, edx}
+    # var slice/edx : (ref slice) = {eax, edx}
     52/push-edx
     50/push-eax
     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
@@ -400,7 +401,7 @@ $test-get-slice:end:
 # return the address of the value
 # Beware: assume keys are immutable; they're inserted by reference
 # TODO: pass in an allocation descriptor
-get-or-insert:  # table : (address stream {string, _}), key : (address string), row-size : int -> eax : (address _)
+get-or-insert:  # table : (address stream {string_key, T}), key : string_key, row-size : int -> eax : (address T)
     # pseudocode:
     #   curr = table->data
     #   max = &table->data[table->write]
@@ -424,9 +425,9 @@ get-or-insert:  # table : (address stream {string, _}), key : (address string),
     56/push-esi
     # esi = table
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
-    # curr/ecx = table->data
+    # var curr/ecx : (address string_key) = table->data
     8d/copy-address                 1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   0xc/disp8       .                 # copy esi+12 to ecx
-    # max/edx = table->data + table->write
+    # var max/edx : (address string_key) = &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:search-loop:
@@ -442,8 +443,8 @@ $get-or-insert:search-loop:
     e8/call  string-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
-    3d/compare-eax-and  0/imm32
+    # . if (eax != false) return eax = curr+4
+    3d/compare-eax-and  0/imm32/false
     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
@@ -511,7 +512,7 @@ test-get-or-insert:
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
-    # var table/ecx : (address stream {string, number}) = stream(2 rows * 8 bytes)
+    # var table/ecx : (ref stream {string, number} 16)  # 2 rows * 8 bytes/row
     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
@@ -651,7 +652,7 @@ $test-get-or-insert:end:
 # if there are no rows free, abort
 # WARNING: leaks memory
 # TODO: pass in an allocation descriptor
-leaky-get-or-insert-slice:  # table : (address stream {string, _}), key : (address slice), row-size : int -> eax : (address _)
+leaky-get-or-insert-slice:  # table : (address stream {string_key, T}), key : (address slice), row-size : int -> eax : (address T)
     # pseudocode:
     #   curr = table->data
     #   max = &table->data[table->write]
@@ -675,9 +676,9 @@ leaky-get-or-insert-slice:  # table : (address stream {string, _}), key : (addre
     56/push-esi
     # esi = table
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
-    # curr/ecx = table->data
+    # var curr/ecx : (address string_key) = table->data
     8d/copy-address                 1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   0xc/disp8       .                 # copy esi+12 to ecx
-    # max/edx = table->data + table->write
+    # var max/edx : (address string_key) = &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
 $leaky-get-or-insert-slice:search-loop:
@@ -693,8 +694,8 @@ $leaky-get-or-insert-slice: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
-    3d/compare-eax-and  0/imm32
+    # . if (eax != false) return eax = curr+4
+    3d/compare-eax-and  0/imm32/false
     74/jump-if-equal  $leaky-get-or-insert-slice:mismatch/disp8
     8d/copy-address                 1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy ecx+4 to eax
     eb/jump  $leaky-get-or-insert-slice:end/disp8
@@ -768,7 +769,7 @@ test-leaky-get-or-insert-slice:
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
-    # var table/ecx : (address stream {string, number}) = stream(2 rows * 8 bytes)
+    # var table/ecx : (ref stream {string, number} 16)  # 2 rows * 8 bytes/row
     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
@@ -779,7 +780,7 @@ test-leaky-get-or-insert-slice:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           2/r32/edx   .               .                 # copy *eax to edx
     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
     05/add-to-eax  4/imm32
-    # var slice/edx = {eax, edx}
+    # var slice/edx : (ref slice) = {eax, edx}
     52/push-edx
     50/push-eax
     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
@@ -872,7 +873,7 @@ $test-leaky-get-or-insert-slice:third-call:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           2/r32/edx   .               .                 # copy *eax to edx
     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
     05/add-to-eax  4/imm32
-    # var slice/edx = {eax, edx}
+    # var slice/edx : (ref slice) = {eax, edx}
     52/push-edx
     50/push-eax
     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
@@ -923,9 +924,9 @@ $test-leaky-get-or-insert-slice:end:
     c3/return
 
 # if no row is found, stop(ed)
-get-or-stop:  # table : (address stream {string, _}), key : (address string), row-size : int,
-              # abort-message-prefix : (address string), err : (address buffered-file), ed : (address exit-descriptor)
-              # -> eax : (address _)
+get-or-stop:  # table : (address stream {string_key, T}), key : string_key, row-size : int,
+              # abort-message-prefix : (address array byte), err : (address buffered-file), ed : (address exit-descriptor)
+              # -> eax : (address T)
     # pseudocode:
     #   curr = table->data
     #   max = &table->data[table->write]
@@ -945,9 +946,9 @@ get-or-stop:  # table : (address stream {string, _}), key : (address string), ro
     56/push-esi
     # esi = table
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
-    # curr/ecx = table->data
+    # var curr/ecx : (address string_key) = table->data
     8d/copy-address                 1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   0xc/disp8       .                 # copy esi+12 to ecx
-    # max/edx = table->data + table->write
+    # var max/edx : (address 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
 $get-or-stop:search-loop:
@@ -963,8 +964,8 @@ $get-or-stop:search-loop:
     e8/call  string-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
-    3d/compare-eax-and  0/imm32
+    # . if (eax != false) return eax = curr+4
+    3d/compare-eax-and  0/imm32/false
     74/jump-if-equal  $get-or-stop: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-stop:end/disp8
@@ -1050,13 +1051,13 @@ test-get-or-stop:
     e8/call  clear-stream/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # var table/ecx : (address stream {string, number}) = stream(2 rows * 8 bytes)
+    # var table/ecx : (ref stream {string, number} 16)  # 2 rows * 8 bytes/row
     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
     68/push  0/imm32/write
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
-    # var ed/edx : (address exit-descriptor)
+    # var ed/edx : (ref exit-descriptor)
     68/push  0/imm32
     68/push  0/imm32
     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
@@ -1139,7 +1140,7 @@ $test-get-or-stop:end:
     c3/return
 
 # if no row is found, stop(ed)
-get-slice-or-stop:  # table : (address stream {string, _}), key : (address slice), row-size : int,
+get-slice-or-stop:  # table : (address stream {string_key, _}), key : (address slice), row-size : int,
                     # abort-message-prefix : (address string), err : (address buffered-file), ed : (address exit-descriptor)
                     # -> eax : (address _)
     # pseudocode:
@@ -1161,9 +1162,9 @@ get-slice-or-stop:  # table : (address stream {string, _}), key : (address slice
     56/push-esi
     # esi = table
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
-    # curr/ecx = table->data
+    # var curr/ecx : (address string_key) = table->data
     8d/copy-address                 1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   0xc/disp8       .                 # copy esi+12 to ecx
-    # max/edx = table->data + table->write
+    # var max/edx : (address 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
 $get-slice-or-stop:search-loop:
@@ -1179,8 +1180,8 @@ $get-slice-or-stop: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
-    3d/compare-eax-and  0/imm32
+    # . if (eax != false) return eax = curr+4
+    3d/compare-eax-and  0/imm32/false
     74/jump-if-equal  $get-slice-or-stop:mismatch/disp8
     8d/copy-address                 1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy ecx+4 to eax
     eb/jump  $get-slice-or-stop:end/disp8
@@ -1266,17 +1267,17 @@ test-get-slice-or-stop:
     e8/call  clear-stream/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # var table/ecx : (address stream {string, number}) = stream(2 rows * 8 bytes)
+    # var table/ecx : (ref stream {string, number} 16)  # 2 rows * 8 bytes/row
     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
     68/push  0/imm32/write
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
-    # var ed/edx : (address exit-descriptor)
+    # var ed/edx : (ref exit-descriptor)
     68/push  0/imm32
     68/push  0/imm32
     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
-    # var slice/ebx = "code"
+    # var slice/ebx : (ref slice) = "code"
     # . (eax..ebx) = "code"
     b8/copy-to-eax  "code"/imm32
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           3/r32/ebx   .               .                 # copy *eax to ebx
@@ -1378,7 +1379,7 @@ $test-get-slice-or-stop:end:
     c3/return
 
 # if no row is found, return null (0)
-maybe-get:  # table : (address stream {string, _}), key : (address string), row-size : int -> eax : (address _)
+maybe-get:  # table : (address stream {string_key, T}), key : string_key, row-size : int -> eax : (address T)
     # pseudocode:
     #   curr = table->data
     #   max = &table->data[table->write]
@@ -1397,9 +1398,9 @@ maybe-get:  # table : (address stream {string, _}), key : (address string), row-
     56/push-esi
     # esi = table
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
-    # curr/ecx = table->data
+    # var curr/ecx : (address string_key) = table->data
     8d/copy-address                 1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   0xc/disp8       .                 # copy esi+12 to ecx
-    # max/edx = table->data + table->write
+    # var max/edx : (address 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
 $maybe-get:search-loop:
@@ -1415,8 +1416,8 @@ $maybe-get:search-loop:
     e8/call  string-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
-    3d/compare-eax-and  0/imm32
+    # . if (eax != false) return eax = curr+4
+    3d/compare-eax-and  0/imm32/false
     74/jump-if-equal  $maybe-get:mismatch/disp8
     8d/copy-address                 1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy ecx+4 to eax
     eb/jump  $maybe-get:end/disp8
@@ -1442,7 +1443,7 @@ test-maybe-get:
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     # - setup: create a table with one row
-    # var table/ecx : (address stream {string, number}) = stream(2 rows * 8 bytes)
+    # var table/ecx : (ref stream {string, number} 16)   # 2 rows * 8 bytes/row
     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
@@ -1525,7 +1526,7 @@ $test-maybe-get:end:
     c3/return
 
 # if no row is found, return null (0)
-maybe-get-slice:  # table : (address stream {string, _}), key : (address slice), row-size : int -> eax : (address _)
+maybe-get-slice:  # table : (address stream {string_key, T}), key : (address slice), row-size : int -> eax : (address T)
     # pseudocode:
     #   curr = table->data
     #   max = &table->data[table->write]
@@ -1544,9 +1545,9 @@ maybe-get-slice:  # table : (address stream {string, _}), key : (address slice),
     56/push-esi
     # esi = table
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
-    # curr/ecx = table->data
+    # var curr/ecx : (address string_key) = table->data
     8d/copy-address                 1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   0xc/disp8       .                 # copy esi+12 to ecx
-    # max/edx = table->data + table->write
+    # var max/edx : (address 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
 $maybe-get-slice:search-loop:
@@ -1562,8 +1563,8 @@ $maybe-get-slice: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
-    3d/compare-eax-and  0/imm32
+    # . if (eax != false) return eax = curr+4
+    3d/compare-eax-and  0/imm32/false
     74/jump-if-equal  $maybe-get-slice:mismatch/disp8
     8d/copy-address                 1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy ecx+4 to eax
     eb/jump  $maybe-get-slice:end/disp8
@@ -1589,7 +1590,7 @@ test-maybe-get-slice:
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     # - setup: create a table with one row
-    # var table/ecx : (address stream {string, number}) = stream(2 rows * 8 bytes)
+    # var table/ecx : (ref stream {string, number} 16)   # 2 rows * 8 bytes/row
     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
@@ -1611,7 +1612,7 @@ $test-maybe-get-slice:success:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           2/r32/edx   .               .                 # copy *eax to edx
     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
     05/add-to-eax  4/imm32
-    # var slice/edx = {eax, edx}
+    # var slice/edx : (ref slice) = {eax, edx}
     52/push-edx
     50/push-eax
     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
@@ -1661,7 +1662,7 @@ $test-maybe-get-slice:failure:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           2/r32/edx   .               .                 # copy *eax to edx
     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
     05/add-to-eax  4/imm32
-    # var slice/edx = {eax, edx}
+    # var slice/edx : (ref slice) = {eax, edx}
     52/push-edx
     50/push-eax
     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
diff --git a/082slurp.subx b/082slurp.subx
index a00f252b..51fba1e2 100644
--- a/082slurp.subx
+++ b/082slurp.subx
@@ -63,10 +63,10 @@ $slurp:loop:
     3d/compare-eax-and  0/imm32
     74/jump-if-equal  $slurp:end/disp8
 $slurp:from-stream:
-    # AL = f->data[f->read]
+    # var c/eax : byte = f->data[f->read]
     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
     8a/copy-byte                    1/mod/*+disp8   4/rm32/sib    6/base/esi  1/index/ecx   .           0/r32/AL    0x10/disp8      .                 # copy byte at *(esi+ecx+16) to AL
-    # s->data[s->write] = AL
+    # s->data[s->write] = c
     88/copy-byte                    1/mod/*+disp8   4/rm32/sib    7/base/edi  2/index/edx   .           0/r32/AL    0xc/disp8       .                 # copy AL to *(edi+edx+12)
     # ++f->read
     41/increment-ecx
diff --git a/083subx-widths.subx b/083subx-widths.subx
index 7bd7141f..db5b84ba 100644
--- a/083subx-widths.subx
+++ b/083subx-widths.subx
@@ -1,3 +1,8 @@
+# Helpers to compute widths of SubX arguments based on their metadata.
+#   /imm8 => 1 byte,
+#   /disp32 => 4 bytes,
+#   and so on.
+
 == code
 #   instruction                     effective address                                                   register    displacement    immediate
 # . op          subop               mod             rm32          base        index         scale       r32
@@ -11,12 +16,12 @@ compute-width: # word : (address array byte) -> eax : int
     51/push-ecx
     # eax = word
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   8/disp8         .                 # copy *(ebp+8) to ecx
-    # ecx = word + word->length
+    # var ecx : (address byte) = &word[word->length]
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     # eax = word->data
     05/add-to-eax  4/imm32
-    # var in/ecx : (address slice) = {eax, ecx}
+    # var in/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -54,8 +59,8 @@ compute-width-of-slice: # s : (address slice) -> eax : int
     e8/call  has-metadata?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-    # . if (eax != 0) return 4
-    3d/compare-eax-and  0/imm32
+    # . if (eax != false) return 4
+    3d/compare-eax-and  0/imm32/false
     b8/copy-to-eax  4/imm32         # ZF is set, so we can overwrite eax now
     75/jump-if-not-equal  $compute-width-of-slice:end/disp8
     # if (has-metadata?(word, "disp32")) return 4
@@ -67,8 +72,8 @@ compute-width-of-slice: # s : (address slice) -> eax : int
     e8/call  has-metadata?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-    # . if (eax != 0) return 4
-    3d/compare-eax-and  0/imm32
+    # . if (eax != false) return 4
+    3d/compare-eax-and  0/imm32/false
     b8/copy-to-eax  4/imm32         # ZF is set, so we can overwrite eax now
     75/jump-if-not-equal  $compute-width-of-slice:end/disp8
     # if (has-metadata?(word, "imm16")) return 2
@@ -80,8 +85,8 @@ compute-width-of-slice: # s : (address slice) -> eax : int
     e8/call  has-metadata?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-    # . if (eax != 0) return 2
-    3d/compare-eax-and  0/imm32
+    # . if (eax != false) return 2
+    3d/compare-eax-and  0/imm32/false
     b8/copy-to-eax  2/imm32         # ZF is set, so we can overwrite eax now
     75/jump-if-not-equal  $compute-width-of-slice:end/disp8
     # if (has-metadata?(word, "disp16")) return 2
@@ -93,8 +98,8 @@ compute-width-of-slice: # s : (address slice) -> eax : int
     e8/call  has-metadata?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-    # . if (eax != 0) return 2
-    3d/compare-eax-and  0/imm32
+    # . if (eax != false) return 2
+    3d/compare-eax-and  0/imm32/false
     b8/copy-to-eax  2/imm32         # ZF is set, so we can overwrite eax now
     75/jump-if-not-equal  $compute-width-of-slice:end/disp8
     # otherwise return 1
diff --git a/084emit-hex-array.subx b/084emit-hex-array.subx
index e284b2c8..29f685f9 100644
--- a/084emit-hex-array.subx
+++ b/084emit-hex-array.subx
@@ -15,20 +15,20 @@ emit-hex-array:  # out : (address buffered-file), arr : (address array byte)
     57/push-edi
     # edi = out
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           7/r32/edi   8/disp8         .                 # copy *(ebp+8) to edi
-    # edx = arr  # <== 0xbdffffe4
+    # edx = arr
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           2/r32/edx   0xc/disp8       .                 # copy *(ebp+12) to edx
-    # curr/ecx = arr->data
+    # var curr/ecx : (address byte) = arr->data
     8d/copy-address                 1/mod/*+disp8   2/rm32/edx    .           .             .           1/r32/ecx   4/disp8         .                 # copy edx+4 to ecx
-    # max/edx = arr->data + arr->length
+    # var max/edx : (address byte) = &arr->data[arr->length]
     8b/copy                         0/mod/indirect  2/rm32/edx    .           .             .           2/r32/edx   .               .                 # copy *edx to edx
     01/add                          3/mod/direct    2/rm32/edx    .           .             .           1/r32/ecx   .               .                 # add ecx to edx
-    # eax = 0
+    # var c/eax : byte = 0
     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
 $emit-hex-array:loop:
     # if (curr >= width) break
     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
     73/jump-if-greater-or-equal-unsigned  $emit-hex-array:end/disp8
-    # emit-hex(out, *curr, width=1)
+    # emit-hex(out, c = *curr, width=1)
     # . . push args
     68/push  1/imm32/width
     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
@@ -71,7 +71,7 @@ test-emit-hex-array:
     e8/call  clear-stream/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # var arr/ecx (address array byte) = [01, 02, 03]
+    # var arr/ecx (ref array byte) = [01, 02, 03]
     68/push  0x00030201/imm32  # bytes 01 02 03
     68/push  3/imm32/length
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
diff --git a/092write-int.subx b/092write-int.subx
index 7ebd473a..5fe1c4d3 100644
--- a/092write-int.subx
+++ b/092write-int.subx
@@ -5,7 +5,7 @@
 # . 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
 
-write-int:  # out : (address stream), n : int
+write-int:  # out : (address stream byte), n : int
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
diff --git a/093array-equal.subx b/093array-equal.subx
index 95cff9dd..f10a9463 100644
--- a/093array-equal.subx
+++ b/093array-equal.subx
@@ -40,27 +40,29 @@ array-equal?:  # a : (address array int), b : (address array int) -> eax : boole
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
     # edi = b
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           7/r32/edi   0xc/disp8       .                 # copy *(ebp+12) to edi
-    # lena/edx = a->length
+    # var lena/edx : int = a->length
     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           2/r32/edx   .               .                 # copy *esi to edx
 $array-equal?:lengths:
     # if (lena != b->length) return false
     39/compare                      0/mod/indirect  7/rm32/edi    .           .             .           2/r32/edx   .               .                 # compare *edi and edx
     75/jump-if-not-equal  $array-equal?:false/disp8
-    # curra/esi = a->data
+    # var curra/esi : (address byte) = a->data
     81          0/subop/add         3/mod/direct    6/rm32/esi    .           .             .           .           .               4/imm32           # add to esi
-    # currb/edi = b->data
+    # var currb/edi : (address byte) = b->data
     81          0/subop/add         3/mod/direct    7/rm32/edi    .           .             .           .           .               4/imm32           # add to edi
-    # i/ecx = i1/eax = i2/ebx = 0
+    # var i/ecx : int = 0
     31/xor                          3/mod/direct    1/rm32/ecx    .           .             .           1/r32/ecx   .               .                 # clear ecx
+    # var vala/eax : int
+    # var valb/ebx : int
 $array-equal?:loop:
     # if (i >= lena) return true
     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
     7d/jump-if-greater-or-equal  $array-equal?:true/disp8
-    # i1 = *curra
+    # vala = *curra
     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
-    # i2 = *currb
+    # valb = *currb
     8b/copy                         0/mod/indirect  7/rm32/edi    .           .             .           3/r32/ebx   .               .                 # copy *edi to ebx
-    # if (i1 != i2) return false
+    # if (vala != valb) return false
     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           3/r32/ebx   .               .                 # compare eax and ebx
     75/jump-if-not-equal  $array-equal?:false/disp8
     # i += 4
@@ -91,10 +93,10 @@ test-compare-empty-with-empty-array:
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
-    # var ecx = []
+    # var ecx : (ref array _) = []
     68/push  0/imm32/size
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
-    # var edx = []
+    # var edx : (ref array _) = []
     68/push  0/imm32/size
     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
     # eax = array-equal?(ecx, edx)
@@ -123,11 +125,11 @@ test-compare-empty-with-non-empty-array:  # also checks length-mismatch code pat
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
-    # var ecx = [1]
+    # var ecx : (ref array int) = [1]
     68/push  1/imm32
     68/push  4/imm32/size
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
-    # var edx = []
+    # var edx : (ref array int) = []
     68/push  0/imm32/size
     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
     # eax = array-equal?(ecx, edx)
@@ -156,13 +158,13 @@ test-compare-equal-arrays:
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
-    # var ecx = [1, 2, 3]
+    # var ecx : (ref array int) = [1, 2, 3]
     68/push  3/imm32
     68/push  2/imm32
     68/push  1/imm32
     68/push  0xc/imm32/size
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
-    # var edx = [1, 2, 3]
+    # var edx : (ref array int) = [1, 2, 3]
     68/push  3/imm32
     68/push  2/imm32
     68/push  1/imm32
@@ -194,13 +196,13 @@ test-compare-inequal-arrays-equal-lengths:
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
-    # var ecx = [1, 4, 3]
+    # var ecx : (ref array int) = [1, 4, 3]
     68/push  3/imm32
     68/push  4/imm32
     68/push  1/imm32
     68/push  0xc/imm32/size
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
-    # var edx = [1, 2, 3]
+    # var edx : (ref array int) = [1, 2, 3]
     68/push  3/imm32
     68/push  2/imm32
     68/push  1/imm32
@@ -228,9 +230,9 @@ test-compare-inequal-arrays-equal-lengths:
     5d/pop-to-ebp
     c3/return
 
-parse-array-of-ints:  # ad : (address allocation-descriptor), s : (address string) -> result/eax : (address array int)
+parse-array-of-ints:  # ad : (address allocation-descriptor), s : (address string) -> result/eax : (handle array int)
     # pseudocode
-    #   end = s->data + s->length
+    #   end = &s->data[s->length]
     #   curr = s->data
     #   size = 0
     #   while true
@@ -241,7 +243,7 @@ parse-array-of-ints:  # ad : (address allocation-descriptor), s : (address strin
     #     ++size
     #   result = allocate(ad, (size+1)*4)
     #   result->size = (size+1)*4
-    #   var slice = {s->data, 0}
+    #   var slice : (ref slice) = {s->data, 0}
     #   out = result->data
     #   while true
     #     if (slice->start >= end) break
@@ -264,14 +266,14 @@ parse-array-of-ints:  # ad : (address allocation-descriptor), s : (address strin
     57/push-edi
     # esi = s
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   0xc/disp8       .                 # copy *(ebp+12) to esi
-    # curr/ecx = s->data
+    # var curr/ecx : (address byte) = s->data
     8d/copy-address                 1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy esi+4 to ecx
-    # end/edx = s->data + s->length
+    # var end/edx : (address byte) = &s->data[s->length]
     # . edx = s->length
     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           2/r32/edx   .               .                 # copy *esi to edx
     # . edx += curr
     01/add                          3/mod/direct    2/rm32/edx    .           .             .           1/r32/ecx   .               .                 # add ecx to edx
-    # size/ebx = 0
+    # var size/ebx : int = 0
     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
 $parse-array-of-ints:loop1:
     # if (curr >= end) break
@@ -308,7 +310,7 @@ $parse-array-of-ints:loop1:
     81          0/subop/add         3/mod/direct    3/rm32/ebx    .           .             .           .           .               4/imm32           # add to ebx
     eb/jump  $parse-array-of-ints:loop1/disp8
 $parse-array-of-ints:break1:
-    # result/edi = allocate(ad, size+4)
+    # var result/edi : (handle array int) = allocate(ad, size+4)
     # . eax = allocate(ad, size+4)
     # . . push args
     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           3/r32/ebx   .               .                 # copy ebx to eax
@@ -324,7 +326,7 @@ $parse-array-of-ints:break1:
     # result->size = size
     89/copy                         0/mod/indirect  0/rm32/eax    .           .             .           3/r32/ebx   .               .                 # copy ebx to *eax
 $parse-array-of-ints:pass2:
-    # var slice/ecx = {s->data, 0}
+    # var slice/ecx : (ref slice) = {s->data, 0}
     # . push 0
     68/push  0/imm32/end
     # . push s->data
@@ -332,7 +334,7 @@ $parse-array-of-ints:pass2:
     51/push-ecx
     # . bookmark
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
-    # out/ebx = result->data
+    # var out/ebx : (address byte) = result->data
     8d/copy-address                 1/mod/*+disp8   0/rm32/eax    .           .             .           3/r32/ebx   4/disp8         .                 # copy eax+4 to ebx
 $parse-array-of-ints:loop2:
     # if (slice->start >= end) break
@@ -373,7 +375,7 @@ $parse-array-of-ints:loop2:
     e8/call  parse-hex-int/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # *out = eax
+    # . *out = eax
     89/copy                         0/mod/indirect  3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # copy eax to *ebx
     # out += 4
     81          0/subop/add         3/mod/direct    3/rm32/ebx    .           .             .           .           .               4/imm32           # add to ebx
@@ -402,7 +404,7 @@ test-parse-array-of-ints:
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
-    # var ecx = [1, 2, 3]
+    # var ecx : (ref array int) = [1, 2, 3]
     68/push  3/imm32
     68/push  2/imm32
     68/push  1/imm32
@@ -496,7 +498,7 @@ test-parse-array-of-ints-extra-whitespace:
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
-    # var ecx = [1, 2, 3]
+    # var ecx : (ref array int) = [1, 2, 3]
     68/push  3/imm32
     68/push  2/imm32
     68/push  1/imm32
@@ -540,7 +542,7 @@ check-array-equal:  # a : (address array int), expected : (address string), msg
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     # . save registers
     50/push-eax
-    # var b/ecx = parse-array-of-ints(Heap, expected)
+    # var b/ecx : (handle array int) = parse-array-of-ints(Heap, expected)
     # . eax = parse-array-of-ints(Heap, expected)
     # . . push args
     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
@@ -580,7 +582,7 @@ test-check-array-equal:
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
-    # var ecx = [1, 2, 3]
+    # var ecx : (ref array int) = [1, 2, 3]
     68/push  3/imm32
     68/push  2/imm32
     68/push  1/imm32
diff --git a/094next-word-or-string.subx b/094next-word-or-string.subx
index 0fe24c6a..d3734fc9 100644
--- a/094next-word-or-string.subx
+++ b/094next-word-or-string.subx
@@ -33,7 +33,7 @@ $next-word-or-string:check0:
     # . if (eax < line->write) goto next check
     3b/compare                      0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # compare eax with *esi
     7c/jump-if-lesser  $next-word-or-string:check-for-comment/disp8
-    # . return out = {0, 0}
+    # . return out
     c7          0/subop/copy        0/mod/direct    7/rm32/edi    .           .             .           .           .               0/imm32           # copy to *edi
     c7          0/subop/copy        1/mod/*+disp8   7/rm32/edi    .           .             .           .           4/disp8         0/imm32           # copy to *(edi+4)
     eb/jump  $next-word-or-string:end/disp8
@@ -111,7 +111,7 @@ test-next-word-or-string:
     e8/call  clear-stream/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # var slice/ecx = {0, 0}
+    # var slice/ecx : (ref slice)
     68/push  0/imm32/end
     68/push  0/imm32/start
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -184,7 +184,7 @@ test-next-word-or-string-returns-whole-comment:
     e8/call  clear-stream/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # var slice/ecx = {0, 0}
+    # var slice/ecx : (ref slice)
     68/push  0/imm32/end
     68/push  0/imm32/start
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -257,7 +257,7 @@ test-next-word-or-string-returns-empty-slice-on-eof:
     e8/call  clear-stream/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # var slice/ecx = {0, 0}
+    # var slice/ecx : (ref slice)
     68/push  0/imm32/end
     68/push  0/imm32/start
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -299,7 +299,7 @@ test-next-word-or-string-returns-string-literal:
     e8/call  clear-stream/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # var slice/ecx = {0, 0}
+    # var slice/ecx : (ref slice)
     68/push  0/imm32/end
     68/push  0/imm32/start
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -362,7 +362,7 @@ test-next-word-or-string-returns-string-with-escapes:
     e8/call  clear-stream/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # var slice/ecx = {0, 0}
+    # var slice/ecx : (ref slice)
     68/push  0/imm32/end
     68/push  0/imm32/start
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
diff --git a/095stack.subx b/095stack.subx
index 2e2f182e..692dece2 100644
--- a/095stack.subx
+++ b/095stack.subx
@@ -16,20 +16,20 @@ clear-stack:  # s : (address stack)
     51/push-ecx
     # eax = s
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   8/disp8         .                 # copy *(ebp+8) to eax
-    # ecx = &s->data[s->length]
+    # var max/ecx : (address byte) = &s->data[s->length]
     8b/copy                         1/mod/*+disp8   0/rm32/eax    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(eax+4) to eax
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   8/disp8         .                 # copy eax+ecx+8 to ecx
     # s->top = 0
     c7          0/subop/copy        0/mod/direct    0/rm32/eax    .           .             .           .           .               0/imm32           # copy to *eax
-    # eax = s->data
+    # var curr/eax : (address byte) = s->data
     81          0/subop/add         3/mod/direct    0/rm32/eax    .           .             .           .           .               8/imm32           # add to eax
 $clear-stack:loop:
-    # if (eax >= ecx) break
+    # if (curr >= max) break
     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # compare eax with ecx
     73/jump-if-greater-or-equal-unsigned  $clear-stack:end/disp8
-    # *eax = 0
+    # *curr = 0
     c6          0/subop/copy        0/mod/direct    0/rm32/eax    .           .             .           .           .               0/imm8            # copy byte to *eax
-    # ++eax
+    # ++curr
     40/increment-eax
     eb/jump $clear-stack:loop/disp8
 $clear-stack:end:
@@ -343,7 +343,7 @@ top:  # s : (address stack) -> n/eax : int
     # if (s->top <= 0) abort
     81          7/subop/compare     0/mod/indirect  6/rm32/esi    .           .             .           .           .               0/imm32           # compare *esi
     7e/jump-if-lesser-or-equal  $top:abort/disp8
-    # eax = s->data[s->top - 4]
+    # n = s->data[s->top - 4]
     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # copy *esi to ecx
     81          5/subop/subtract    3/mod/direct    1/rm32/ecx    .           .             .           .           .               4/imm32           # subtract from ecx
     8b/copy                         1/mod/*+disp8   4/rm32/sib    6/base/esi  1/index/ecx   .           0/r32/eax   8/disp8         .                 # copy *(esi+ecx+8) to eax
diff --git a/apps/assort b/apps/assort
index 05e615a7..b8cba438 100755
--- a/apps/assort
+++ b/apps/assort
Binary files differdiff --git a/apps/assort.subx b/apps/assort.subx
index 123ad64e..0aa18555 100644
--- a/apps/assort.subx
+++ b/apps/assort.subx
@@ -50,8 +50,8 @@ Entry:  # run tests if necessary, convert stdin if not
     e8/call  kernel-string-equal?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-    # . if (eax == 0) goto interactive
-    3d/compare-eax-and  0/imm32
+    # . if (eax == false) goto interactive
+    3d/compare-eax-and  0/imm32/false
     74/jump-if-equal  $subx-assort-main:interactive/disp8
     # run-tests()
     e8/call  run-tests/disp32
@@ -60,7 +60,7 @@ Entry:  # run tests if necessary, convert stdin if not
     eb/jump  $subx-assort-main:end/disp8
 $subx-assort-main:interactive:
     # - otherwise convert stdin
-    # var ed/eax : exit-descriptor
+    # var ed/eax : (ref exit-descriptor)
     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           4/r32/esp   .               .                 # copy esp to eax
     # configure ed to really exit()
@@ -97,7 +97,7 @@ subx-assort:  # in : (address buffered-file), out : (address buffered-file)
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     # . save registers
     51/push-ecx
-    # var table/ecx : (address stream byte 10*8)
+    # var table/ecx : (ref stream {string, (address stream byte)} 80)  # 10 rows * 8 bytes/row
     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x50/imm32        # subtract from esp
     68/push  0x50/imm32/length
     68/push  0/imm32/read
@@ -450,8 +450,10 @@ test-subx-assort:
     5d/pop-to-ebp
     c3/return
 
+# type string_key = (address array byte)
+
 # beware: leaks memory (one name per segment read)
-read-segments:  # in : (address buffered-file), table : (address stream {string, (address stream byte)})
+read-segments:  # in : (address buffered-file), table : (address stream {string_key, (handle stream byte)})
     # pseudocode:
     #   var curr-segment : (handle stream byte) = 0
     #   var line : (stream byte 512)
@@ -494,13 +496,13 @@ read-segments:  # in : (address buffered-file), table : (address stream {string,
     52/push-edx
     53/push-ebx
     56/push-esi
-    # var line/ecx : (stream byte 512)
+    # var line/ecx : (ref stream byte 512)
     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x200/imm32       # subtract from esp
     68/push  0x200/imm32/length
     68/push  0/imm32/read
     68/push  0/imm32/write
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
-    # var word-slice/edx = {0, 0}
+    # var word-slice/edx : (ref slice)
     68/push  0/imm32/end
     68/push  0/imm32/start
     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
@@ -583,8 +585,8 @@ $read-segments:check1:
     e8/call  slice-empty?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # . if (eax != 0) continue
-    3d/compare-eax-and  0/imm32
+    # . if (eax != false) continue
+    3d/compare-eax-and  0/imm32/false
     0f 85/jump-if-not-equal  $read-segments:loop/disp32
 $read-segments:check-for-comment:
 #?     # print("check for comment\n") {{{
@@ -597,12 +599,12 @@ $read-segments:check-for-comment:
 #?     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 #?     # }}}
     # if (slice-starts-with?(word-slice, "#")) continue
-    # . start/esi = word-slice->start
+    # . var start/esi : (address byte) = word-slice->start
     8b/copy                         0/mod/indirect  2/rm32/edx    .           .             .           6/r32/esi   .               .                 # copy *ecx to esi
-    # . c/eax = *start
+    # . var c/eax : byte = *start
     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
     8a/copy-byte                    0/mod/indirect  6/rm32/esi    .           .             .           0/r32/AL    .               .                 # copy byte at *esi to AL
-    # . if (eax == '#') continue
+    # . if (c == '#') continue
     3d/compare-eax-and  0x23/imm32/hash
     0f 84/jump-if-equal  $read-segments:loop/disp32
 $read-segments:check-for-segment-header:
@@ -664,8 +666,8 @@ $read-segments:check-for-segment-header:
     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) goto check3
-    3d/compare-eax-and  0/imm32
+    # . if (eax == false) goto check3
+    3d/compare-eax-and  0/imm32/false
     0f 84/jump-if-equal  $read-segments:regular-line/disp32
     # segment-name = next-word-or-string(line)
     # . . push args
@@ -715,7 +717,7 @@ $read-segments:check-for-segment-header:
 #?     # . . discard args
 #?     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 #?     # }}}
-    # segment-slot/eax = leaky-get-or-insert-slice(table, segment-name, row-size=8)
+    # var segment-slot/eax : (address handle stream byte) = leaky-get-or-insert-slice(table, segment-name, row-size=8)
     # . . push args
     68/push  8/imm32/row-size
     52/push-edx
@@ -841,10 +843,10 @@ $read-segments:end:
     5d/pop-to-ebp
     c3/return
 
-write-segments:  # out : (address buffered-file), table : (address stream {string, (address stream byte)})
+write-segments:  # out : (address buffered-file), table : (address stream {string_key, (handle stream byte)})
     # pseudocode:
     #   var curr = table->data
-    #   var max = table->data + table->write
+    #   var max = &table->data[table->write]
     #   while curr < max
     #     stream = table[i].stream
     #     write-stream-data(out, stream)
@@ -860,17 +862,17 @@ write-segments:  # out : (address buffered-file), table : (address stream {strin
     56/push-esi
     # esi = table
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   0xc/disp8       .                 # copy *(ebp+12) to esi
-    # write/edx = table->write
+    # var write/edx : int = table->write
     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           2/r32/edx   .               .                 # copy *esi to edx
-    # curr/esi = table->data
+    # var curr/esi : (address byte) = table->data
     81          0/subop/add         3/mod/direct    6/rm32/esi    .           .             .           .           .               0xc/imm32         # add to eax
-    # max/edx = curr + write
+    # var max/edx : (address byte) = curr + write
     01/add                          3/mod/direct    2/rm32/edx    .           .             .           6/r32/esi   .               .                 # add esi to edx
 $write-segments:loop:
     # if (curr >= max) break
     39/compare                      3/mod/direct    6/rm32/esi    .           .             .           2/r32/edx   .               .                 # compare esi with edx
     73/jump-if-greater-or-equal-unsigned  $write-segments:break/disp8
-    # stream/eax = table[i].stream
+    # var stream/eax : (address stream byte) = table[i].stream
     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           0/r32/eax   4/disp8         .                 # copy *(esi+4) to eax
     # write-stream-data(out, stream)
     # . . push args
diff --git a/apps/braces b/apps/braces
index 7307be11..ef7eb54a 100755
--- a/apps/braces
+++ b/apps/braces
Binary files differdiff --git a/apps/braces.subx b/apps/braces.subx
index 27810a86..95b6a7ea 100644
--- a/apps/braces.subx
+++ b/apps/braces.subx
@@ -77,8 +77,8 @@ $subx-braces-main:end:
 
 subx-braces:  # in : (address buffered-file), out : (address buffered-file)
     # pseudocode:
-    #   var line : (stream byte 512)
-    #   var label-stack : (stack address 32)  # at most 32 levels of nesting
+    #   var line : (ref stream byte 512)
+    #   var label-stack : (stack int 32)  # at most 32 levels of nesting
     #   var next-label-id : int = 1
     #   while true
     #     clear-stream(line)
@@ -123,20 +123,20 @@ subx-braces:  # in : (address buffered-file), out : (address buffered-file)
     57/push-edi
     # esi = in
     8b/-> *(ebp+8) 6/r32/esi
-    # var line/ecx : (stream byte 512)
+    # var line/ecx : (ref stream byte 512)
     81 5/subop/subtract %esp 0x200/imm32
     68/push 0x200/imm32/length
     68/push 0/imm32/read
     68/push 0/imm32/write
     89/<- %ecx 4/r32/esp
-    # var label-stack/edx : (stack int 32)
+    # var label-stack/edx : (ref stack int 32)
     81 5/subop/subtract %esp 0x80/imm32
     68/push 0x80/imm32/length
     68/push 0/imm32/top
     89/<- %edx 4/r32/esp
-    # next-label-id/ebx = 1
+    # var next-label-id/ebx : int = 1
     c7 0/subop/copy %ebx 1/imm32
-    # var word-slice/edi = {0, 0}
+    # var word-slice/edi : (ref slice)
     68/push 0/imm32/end
     68/push 0/imm32/start
     89/<- %edi 4/r32/esp
@@ -201,8 +201,8 @@ $subx-braces:check-for-break:
     # if (!slice-starts-with?(word-slice, "break/")) goto next check
     # . eax = slice-starts-with?(word-slice, "break/")
     (slice-starts-with? %edi "break/")
-    # . if (eax == 0) goto next check
-    3d/compare-eax-and 0/imm32
+    # . if (eax == false) goto next check
+    3d/compare-eax-and 0/imm32/false
     74/jump-if-equal $subx-braces:check-for-loop/disp8
 $subx-braces:emit-break:
     (top %edx)
@@ -217,8 +217,8 @@ $subx-braces:check-for-loop:
     # if (!slice-starts-with?(word-slice, "loop/")) emit word
     # . eax = slice-starts-with?(word-slice, "loop/")
     (slice-starts-with? %edi "loop/")
-    # . if (eax == 0) goto next check
-    3d/compare-eax-and 0/imm32
+    # . if (eax == false) goto next check
+    3d/compare-eax-and 0/imm32/false
     74/jump-if-equal $subx-braces:emit-word-slice/disp8
 $subx-braces:emit-loop:
     (top %edx)
diff --git a/apps/calls b/apps/calls
index 418d230e..15bbc5db 100755
--- a/apps/calls
+++ b/apps/calls
Binary files differdiff --git a/apps/calls.subx b/apps/calls.subx
index 28e2ff63..030509ed 100644
--- a/apps/calls.subx
+++ b/apps/calls.subx
@@ -51,8 +51,8 @@ Entry:  # run tests if necessary, convert stdin if not
     e8/call kernel-string-equal?/disp32
     # . . discard args
     81 0/subop/add %esp 8/imm32
-    # . if (eax == 0) goto run-main
-    3d/compare-eax-and 0/imm32
+    # . if (eax == false) goto run-main
+    3d/compare-eax-and 0/imm32/false
     74/jump-if-equal $subx-calls-main:interactive/disp8
     # run-tests()
     e8/call run-tests/disp32
@@ -77,8 +77,8 @@ $subx-calls-main:end:
 
 subx-calls:  # in : (address buffered-file), out : (address buffered-file)
     # pseudocode:
-    #   var line : (stream byte 512)
-    #   var words : (stream slice 16)  # at most function name and 15 args
+    #   var line : (ref stream byte 512)
+    #   var words : (ref stream slice 16)  # at most function name and 15 args
     #   while true
     #     clear-stream(line)
     #     read-line-buffered(in, line)
@@ -105,13 +105,13 @@ subx-calls:  # in : (address buffered-file), out : (address buffered-file)
     51/push-ecx
     52/push-edx
     56/push-esi
-    # var line/esi : (address stream byte) = stream(512)
+    # var line/esi : (ref stream byte 512)
     81 5/subop/subtract %esp 0x200/imm32
     68/push 0x200/imm32/length
     68/push 0/imm32/read
     68/push 0/imm32/write
     89/<- %esi 4/r32/esp
-    # var words/edx : (address stream slice) = stream(16, 8)
+    # var words/edx : (ref stream slice 128)  # 16 rows * 8 bytes/row
     81 5/subop/subtract %esp 0x80/imm32
     68/push 0x80/imm32/length
     68/push 0/imm32/read
@@ -233,7 +233,7 @@ $subx-calls:end:
 
 parse-line:  # line : (address stream byte), words : (address stream slice)
     # pseudocode:
-    #   var word-slice : (address slice)
+    #   var word-slice : (ref slice)
     #   while true
     #     word-slice = next-word-string-or-expression-without-metadata(line)
     #     if slice-empty?(word-slice)
@@ -246,7 +246,7 @@ parse-line:  # line : (address stream byte), words : (address stream slice)
     89/<- %ebp 4/r32/esp
     # . save registers
     51/push-ecx
-    # var word-slice/ecx : (address slice) = {0, 0}
+    # var word-slice/ecx : (ref slice)
     68/push 0/imm32/end
     68/push 0/imm32/start
     89/<- %ecx 4/r32/esp
@@ -268,8 +268,8 @@ $parse-line:check1:
     e8/call slice-empty?/disp32
     # . . discard args
     81 0/subop/add %esp 4/imm32
-    # . if (eax != 0) break
-    3d/compare-eax-and 0/imm32
+    # . if (eax != false) break
+    3d/compare-eax-and 0/imm32/false
     0f 85/jump-if-not-equal $parse-line:end/disp32
 #?     # dump word-slice {{{
 #?     # . write(2/stderr, "w: ")
@@ -381,9 +381,9 @@ emit-call:  # out : (address buffered-file), words : (address stream slice)
     8b/-> *esi 1/r32/ecx
     81 5/subop/subtract %ecx 8/imm32
     0f 8c/jump-if-lesser $emit-call:error1/disp32
-    # curr/ecx = &words->data[words->write-8]
+    # var curr/ecx : (address slice) = &words->data[words->write-8]
     8d/copy-address *(esi+ecx+0xc) 1/r32/ecx
-    # min/edx = words->data
+    # var min/edx : (address byte) = words->data
     8d/copy-address *(esi+0xc) 2/r32/edx
     # - emit pushes
 $emit-call:push-loop:
@@ -391,15 +391,15 @@ $emit-call:push-loop:
     39/compare %ecx 2/r32/edx
     0f 8e/jump-if-lesser-or-equal $emit-call:call-instruction/disp32
     # if (*curr->start in '%' '*') goto push-rm32
-    # . eax = curr->start
+    # . var start/eax : (address byte) = curr->start
     8b/-> *ecx 0/r32/eax
-    # . eax = (byte)*eax
+    # . var c/eax : byte = *eax
     8b/-> *eax 0/r32/eax
     81 4/subop/and %eax 0xff/imm32
-    # . if (eax == '%') goto push-rm32
+    # . if (c == '%') goto push-rm32
     3d/compare-eax-and 0x25/imm32/percent
     74/jump-if-equal $emit-call:push-rm32/disp8
-    # . if (eax == '*') goto push-rm32
+    # . if (c == '*') goto push-rm32
     3d/compare-eax-and 0x2a/imm32/asterisk
     74/jump-if-equal $emit-call:push-rm32/disp8
 $emit-call:push-imm32:
@@ -741,7 +741,7 @@ test-subx-calls-processes-calls:
     5d/pop-to-ebp
     c3/return
 
-next-word-string-or-expression-without-metadata:  # line : (address stream), out : (address slice)
+next-word-string-or-expression-without-metadata:  # line : (address stream byte), out : (address slice)
     # pseudocode:
     #   skip-chars-matching(line, ' ')
     #   if line->read >= line->write              # end of line
@@ -830,7 +830,7 @@ $next-word-string-or-expression-without-metadata:check-for-comment:
     8d/copy-address *(esi+ecx+0xc) 0/r32/eax
     89/<- *edi 0/r32/eax
     # if (line->data[line->read] != '#') goto next check
-    # . eax = line->data[line->read]
+    # . var eax : byte = line->data[line->read]
     31/xor %eax 0/r32/eax
     8a/copy-byte *(esi+ecx+0xc) 0/r32/AL
     # . if (eax != '#') goto next check
@@ -1191,7 +1191,7 @@ test-next-word-string-or-expression-without-metadata:
     e8/call clear-stream/disp32
     # . . discard args
     81 0/subop/add %esp 4/imm32
-    # var slice/ecx = {0, 0}
+    # var slice/ecx : (ref slice)
     68/push 0/imm32/end
     68/push 0/imm32/start
     89/<- %ecx 4/r32/esp
@@ -1264,7 +1264,7 @@ test-next-word-string-or-expression-without-metadata-returns-whole-comment:
     e8/call clear-stream/disp32
     # . . discard args
     81 0/subop/add %esp 4/imm32
-    # var slice/ecx = {0, 0}
+    # var slice/ecx : (ref slice)
     68/push 0/imm32/end
     68/push 0/imm32/start
     89/<- %ecx 4/r32/esp
@@ -1337,7 +1337,7 @@ test-next-word-string-or-expression-without-metadata-returns-empty-slice-on-eof:
     e8/call clear-stream/disp32
     # . . discard args
     81 0/subop/add %esp 4/imm32
-    # var slice/ecx = {0, 0}
+    # var slice/ecx : (ref slice)
     68/push 0/imm32/end
     68/push 0/imm32/start
     89/<- %ecx 4/r32/esp
@@ -1379,7 +1379,7 @@ test-next-word-string-or-expression-without-metadata-returns-string-literal:
     e8/call clear-stream/disp32
     # . . discard args
     81 0/subop/add %esp 4/imm32
-    # var slice/ecx = {0, 0}
+    # var slice/ecx : (ref slice)
     68/push 0/imm32/end
     68/push 0/imm32/start
     89/<- %ecx 4/r32/esp
@@ -1442,7 +1442,7 @@ test-next-word-string-or-expression-without-metadata-returns-string-with-escapes
     e8/call clear-stream/disp32
     # . . discard args
     81 0/subop/add %esp 4/imm32
-    # var slice/ecx = {0, 0}
+    # var slice/ecx : (ref slice)
     68/push 0/imm32/end
     68/push 0/imm32/start
     89/<- %ecx 4/r32/esp
@@ -1505,7 +1505,7 @@ test-next-word-string-or-expression-without-metadata-returns-whole-expression:
     e8/call clear-stream/disp32
     # . . discard args
     81 0/subop/add %esp 4/imm32
-    # var slice/ecx = {0, 0}
+    # var slice/ecx : (ref slice)
     68/push 0/imm32/end
     68/push 0/imm32/start
     89/<- %ecx 4/r32/esp
@@ -1568,7 +1568,7 @@ test-next-word-string-or-expression-without-metadata-returns-eol-on-trailing-clo
     e8/call clear-stream/disp32
     # . . discard args
     81 0/subop/add %esp 4/imm32
-    # var slice/ecx = {0, 0}
+    # var slice/ecx : (ref slice)
     68/push 0/imm32/end
     68/push 0/imm32/start
     89/<- %ecx 4/r32/esp
@@ -1623,7 +1623,7 @@ test-next-word-string-or-expression-without-metadata-handles-comment-after-trail
     e8/call clear-stream/disp32
     # . . discard args
     81 0/subop/add %esp 4/imm32
-    # var slice/ecx = {0, 0}
+    # var slice/ecx : (ref slice)
     68/push 0/imm32/end
     68/push 0/imm32/start
     89/<- %ecx 4/r32/esp
@@ -1678,7 +1678,7 @@ test-next-word-string-or-expression-without-metadata-handles-newline-after-trail
     e8/call clear-stream/disp32
     # . . discard args
     81 0/subop/add %esp 4/imm32
-    # var slice/ecx = {0, 0}
+    # var slice/ecx : (ref slice)
     68/push 0/imm32/end
     68/push 0/imm32/start
     89/<- %ecx 4/r32/esp
@@ -1733,7 +1733,7 @@ test-next-word-string-or-expression-without-metadata-stops-at-close-paren:
     e8/call clear-stream/disp32
     # . . discard args
     81 0/subop/add %esp 4/imm32
-    # var slice/ecx = {0, 0}
+    # var slice/ecx : (ref slice)
     68/push 0/imm32/end
     68/push 0/imm32/start
     89/<- %ecx 4/r32/esp
diff --git a/apps/crenshaw2-1 b/apps/crenshaw2-1
index 7103908f..a579ee45 100755
--- a/apps/crenshaw2-1
+++ b/apps/crenshaw2-1
Binary files differdiff --git a/apps/crenshaw2-1.subx b/apps/crenshaw2-1.subx
index 2217e430..228f4696 100644
--- a/apps/crenshaw2-1.subx
+++ b/apps/crenshaw2-1.subx
@@ -57,8 +57,8 @@ Entry:  # run tests if necessary, call 'compile' if not
     e8/call  kernel-string-equal?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-    # . if (eax == 0) goto run-main
-    3d/compare-eax-and  0/imm32
+    # . if (eax == false) goto run-main
+    3d/compare-eax-and  0/imm32/false
     74/jump-if-equal  $run-main/disp8
     # run-tests()
     e8/call  run-tests/disp32
@@ -67,7 +67,7 @@ Entry:  # run tests if necessary, call 'compile' if not
     eb/jump  $main:end/disp8
 $run-main:
     # - otherwise read a program from stdin and emit its translation to stdout
-    # var ed/eax : exit-descriptor
+    # var ed/eax : (ref exit-descriptor)
     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           4/r32/esp   .               .                 # copy esp to eax
     # configure ed to really exit()
@@ -90,7 +90,7 @@ $main:end:
     cd/syscall  0x80/imm8
 
 # the main entry point
-compile:  # in : (address buffered-file), out : fd or (address stream), err : fd or (address stream), ed : (address exit-descriptor)
+compile:  # in : (address buffered-file), out : fd or (address stream byte), err : fd or (address stream byte), ed : (address exit-descriptor)
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
@@ -105,7 +105,7 @@ compile:  # in : (address buffered-file), out : fd or (address stream), err : fd
     e8/call  get-char/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # var num/ecx : (address stream) on the stack
+    # var num/ecx : (ref stream byte 7)
     # Numbers can be 32 bits or 8 hex bytes long. One of them will be in 'Look', so we need space for 7 bytes.
     # Sizing the stream just right buys us overflow-handling for free inside 'get-num'.
     # Add 12 bytes for 'read', 'write' and 'length' fields, for a total of 19 bytes, or 0x13 in hex.
@@ -187,10 +187,11 @@ $compile:end:
     5d/pop-to-ebp
     c3/return
 
-# Read a single digit into 'out'. Abort if there are none, or if there is no space in 'out'.
-# Input comes from the global variable 'Look', and we leave the next byte from
-# 'in' into it on exit.
-get-num:  # in : (address buffered-file), out : (address stream), err : fd or (address stream), ed : (address exit-descriptor)
+# Read a single digit into 'out'. Abort if there are none, or if there is no
+# space in 'out'.
+# Input comes from the global variable 'Look' (first byte) and the argument
+# 'in' (rest). We leave the next byte from 'in' into 'Look' on exit.
+get-num:  # in : (address buffered-file), out : (address stream byte), err : fd or (address stream byte), ed : (address exit-descriptor)
     # pseudocode:
     #   if (!is-digit?(Look)) expected(ed, err, "integer")
     #   if out->write >= out->length
@@ -221,8 +222,8 @@ get-num:  # in : (address buffered-file), out : (address stream), err : fd or (a
     e8/call  is-digit?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # . if (eax == 0)
-    3d/compare-eax-and  0/imm32
+    # . if (eax == false)
+    3d/compare-eax-and  0/imm32/false
     75/jump-if-not-equal  $get-num:main/disp8
     # . expected(ed, err, "integer")
     # . . push args
@@ -251,6 +252,7 @@ $get-num:main:
     8b/copy                         0/mod/indirect  7/rm32/edi    .           .             .           1/r32/ecx   .               .                 # copy *edi to ecx
     # edx = out->length
     8b/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           2/r32/edx   8/disp8         .                 # copy *(edi+8) to edx
+$get-num:loop:
     # if (out->write >= out->length) error
     39/compare                      3/mod/direct    2/rm32/edx    .           .             .           1/r32/ecx   .               .                 # compare edx with ecx
     7d/jump-if-lesser  $get-num:stage2/disp8
@@ -337,7 +339,7 @@ test-get-num-reads-single-digit:
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     # initialize exit-descriptor 'ed' for the call to 'get-num' below
-    # . var ed/eax : exit-descriptor
+    # . var ed/eax : (ref exit-descriptor)
     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           4/r32/esp   .               .                 # copy esp to eax
     # . tailor-exit-descriptor(ed, 16)
@@ -426,7 +428,7 @@ test-get-num-aborts-on-non-digit-in-Look:
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     # initialize exit-descriptor 'ed' for the call to 'get-num' below
-    # . var ed/eax : (address exit-descriptor)
+    # . var ed/eax : (ref exit-descriptor)
     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           4/r32/esp   .               .                 # copy esp to eax
     # . tailor-exit-descriptor(ed, 16)
@@ -468,7 +470,7 @@ test-get-num-aborts-on-non-digit-in-Look:
 ## helpers
 
 # write(f, "Error: "+s+" expected\n") then stop(ed, 1)
-expected:  # ed : (address exit-descriptor), f : fd or (address stream), s : (address array byte)
+expected:  # ed : (address exit-descriptor), f : fd or (address stream byte), s : (address array byte)
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
@@ -488,7 +490,7 @@ expected:  # ed : (address exit-descriptor), f : fd or (address stream), s : (ad
     e8/call  write/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-    # write(f, " expected")
+    # write(f, " expected\n")
     # . . push args
     68/push  " expected\n"/imm32
     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
diff --git a/apps/crenshaw2-1b b/apps/crenshaw2-1b
index 31b9cac1..a398a0bc 100755
--- a/apps/crenshaw2-1b
+++ b/apps/crenshaw2-1b
Binary files differdiff --git a/apps/crenshaw2-1b.subx b/apps/crenshaw2-1b.subx
index 58467aa9..374625e9 100644
--- a/apps/crenshaw2-1b.subx
+++ b/apps/crenshaw2-1b.subx
@@ -57,8 +57,8 @@ Entry:  # run tests if necessary, call 'compile' if not
     e8/call  kernel-string-equal?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-    # . if (eax == 0) goto run-main
-    3d/compare-eax-and  0/imm32
+    # . if (eax == false) goto run-main
+    3d/compare-eax-and  0/imm32/false
     74/jump-if-equal  $run-main/disp8
     # run-tests()
     e8/call  run-tests/disp32
@@ -67,7 +67,7 @@ Entry:  # run tests if necessary, call 'compile' if not
     eb/jump  $main:end/disp8
 $run-main:
     # - otherwise read a program from stdin and emit its translation to stdout
-    # var ed/eax : exit-descriptor
+    # var ed/eax : (ref exit-descriptor)
     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           4/r32/esp   .               .                 # copy esp to eax
     # configure ed to really exit()
@@ -90,7 +90,7 @@ $main:end:
     cd/syscall  0x80/imm8
 
 # the main entry point
-compile:  # in : (address buffered-file), out : fd or (address stream), err : fd or (address stream), ed : (address exit-descriptor)
+compile:  # in : (address buffered-file), out : fd or (address stream byte), err : fd or (address stream byte), ed : (address exit-descriptor)
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
@@ -105,7 +105,7 @@ compile:  # in : (address buffered-file), out : fd or (address stream), err : fd
     e8/call  get-char/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # var num/ecx : (address stream) on the stack
+    # var num/ecx : (ref stream byte 7)
     # Numbers can be 32 bits or 8 hex bytes long. One of them will be in 'Look', so we need space for 7 bytes.
     # Sizing the stream just right buys us overflow-handling for free inside 'get-num'.
     # Add 12 bytes for 'read', 'write' and 'length' fields, for a total of 19 bytes, or 0x13 in hex.
@@ -191,7 +191,7 @@ $compile:end:
 # no space in 'out'.
 # Input comes from the global variable 'Look' (first byte) and the argument
 # 'in' (rest). We leave the next byte from 'in' into 'Look' on exit.
-get-num:  # in : (address buffered-file), out : (address stream), err : fd or (address stream), ed : (address exit-descriptor)
+get-num:  # in : (address buffered-file), out : (address stream byte), err : fd or (address stream byte), ed : (address exit-descriptor)
     # pseudocode:
     #   if (!is-digit?(Look)) expected(ed, err, "integer")
     #   do
@@ -227,8 +227,8 @@ get-num:  # in : (address buffered-file), out : (address stream), err : fd or (a
     e8/call  is-digit?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # . if (eax == 0)
-    3d/compare-eax-and  0/imm32
+    # . if (eax == false)
+    3d/compare-eax-and  0/imm32/false
     75/jump-if-not-equal  $get-num:main/disp8
     # . expected(ed, err, "integer")
     # . . push args
@@ -292,8 +292,8 @@ $get-num:loop-stage2:
     e8/call  is-digit?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # . if (eax != 0) loop
-    3d/compare-eax-and  0/imm32
+    # . if (eax != false) loop
+    3d/compare-eax-and  0/imm32/false
     0f 85/jump-if-not-equal  $get-num:loop/disp32
 $get-num:loop-end:
     # persist necessary variables from registers
@@ -355,7 +355,7 @@ test-get-num-reads-single-digit:
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     # initialize exit-descriptor 'ed' for the call to 'get-num' below
-    # . var ed/eax : exit-descriptor
+    # . var ed/eax : (ref exit-descriptor)
     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           4/r32/esp   .               .                 # copy esp to eax
     # . tailor-exit-descriptor(ed, 16)
@@ -444,7 +444,7 @@ test-get-num-aborts-on-non-digit-in-Look:
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     # initialize exit-descriptor 'ed' for the call to 'get-num' below
-    # . var ed/eax : (address exit-descriptor)
+    # . var ed/eax : (ref exit-descriptor)
     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           4/r32/esp   .               .                 # copy esp to eax
     # . tailor-exit-descriptor(ed, 16)
@@ -527,7 +527,7 @@ test-get-num-reads-multiple-digits:
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     # initialize exit-descriptor 'ed' for the call to 'get-num' below
-    # . var ed/eax : (address exit-descriptor)
+    # . var ed/eax : (ref exit-descriptor)
     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           4/r32/esp   .               .                 # copy esp to eax
     # . tailor-exit-descriptor(ed, 16)
@@ -616,7 +616,7 @@ test-get-num-reads-multiple-digits-followed-by-nondigit:
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     # initialize exit-descriptor 'ed' for the call to 'get-num' below
-    # . var ed/eax : (address exit-descriptor)
+    # . var ed/eax : (ref exit-descriptor)
     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           4/r32/esp   .               .                 # copy esp to eax
     # . tailor-exit-descriptor(ed, 16)
@@ -664,7 +664,7 @@ test-get-num-reads-multiple-digits-followed-by-nondigit:
 ## helpers
 
 # write(f, "Error: "+s+" expected\n") then stop(ed, 1)
-expected:  # ed : (address exit-descriptor), f : fd or (address stream), s : (address array byte)
+expected:  # ed : (address exit-descriptor), f : fd or (address stream byte), s : (address array byte)
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
diff --git a/apps/dquotes b/apps/dquotes
index ec6808ec..ae526a6c 100755
--- a/apps/dquotes
+++ b/apps/dquotes
Binary files differdiff --git a/apps/dquotes.subx b/apps/dquotes.subx
index 5186eda6..4b985fbb 100644
--- a/apps/dquotes.subx
+++ b/apps/dquotes.subx
@@ -46,8 +46,8 @@ Entry:  # run tests if necessary, convert stdin if not
     e8/call  kernel-string-equal?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-    # . if (eax == 0) goto interactive
-    3d/compare-eax-and  0/imm32
+    # . if (eax == false) goto interactive
+    3d/compare-eax-and  0/imm32/false
     74/jump-if-equal  $subx-dquotes-main:interactive/disp8
     # run-tests()
     e8/call  run-tests/disp32
@@ -56,7 +56,7 @@ Entry:  # run tests if necessary, convert stdin if not
     eb/jump  $subx-dquotes-main:end/disp8
 $subx-dquotes-main:interactive:
     # - otherwise convert stdin
-    # var ed/eax : exit-descriptor
+    # var ed/eax : (ref exit-descriptor)
     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           4/r32/esp   .               .                 # copy esp to eax
     # configure ed to really exit()
@@ -84,7 +84,7 @@ $subx-dquotes-main:end:
 
 subx-dquotes:  # in : (address buffered-file), out : (address buffered-file)
     # pseudocode:
-    #   var line : (stream byte 512)
+    #   var line : (ref stream byte 512)
     #   var new-data-segment : (handle stream byte) = new-stream(Heap, Segment-size, 1)
     #
     #   write(new-data-segment, "== data\n")
@@ -125,13 +125,13 @@ subx-dquotes:  # in : (address buffered-file), out : (address buffered-file)
     53/push-ebx
     56/push-esi
     57/push-edi
-    # var line/ecx : (address stream byte) = stream(512)
+    # var line/ecx : (ref stream byte 512)
     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x200/imm32       # subtract from esp
     68/push  0x200/imm32/length
     68/push  0/imm32/read
     68/push  0/imm32/write
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
-    # var word-slice/edx = {0, 0}
+    # var word-slice/edx : (ref slice)
     68/push  0/imm32/end
     68/push  0/imm32/start
     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
@@ -193,17 +193,17 @@ $subx-dquotes:check1:
     e8/call  slice-empty?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # . if (eax != 0) break
-    3d/compare-eax-and  0/imm32
+    # . if (eax != false) break
+    3d/compare-eax-and  0/imm32/false
     0f 85/jump-if-not-equal  $subx-dquotes:next-line/disp32
 $subx-dquotes:check-for-comment:
     # if (slice-starts-with?(word-slice, "#")) continue
-    # . start/esi = word-slice->start
+    # . var start/esi : (address byte) = word-slice->start
     8b/copy                         0/mod/indirect  2/rm32/edx    .           .             .           6/r32/esi   .               .                 # copy *edx to esi
-    # . c/eax = *start
+    # . var c/eax : byte = *start
     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
     8a/copy-byte                    0/mod/indirect  6/rm32/esi    .           .             .           0/r32/AL    .               .                 # copy byte at *esi to AL
-    # . if (eax == '#') continue
+    # . if (c == '#') continue
     3d/compare-eax-and  0x23/imm32/hash
     74/jump-if-equal  $subx-dquotes:word-loop/disp8
 $subx-dquotes:check-for-string-literal:
@@ -287,7 +287,7 @@ $subx-dquotes:end:
 
 # Write out 'string-literal' in a new format to 'out-segment', assign it a new
 # label, and write the new label out to 'out'.
-process-string-literal:  # string-literal : (address slice), out : (address buffered-file), out-segment : (address stream)
+process-string-literal:  # string-literal : (address slice), out : (address buffered-file), out-segment : (address stream byte)
     # pseudocode:
     #   print(out-segment, "_string#{Next-string-literal}:\n")
     #   emit-string-literal-data(out-segment, string-literal)
@@ -300,7 +300,7 @@ process-string-literal:  # string-literal : (address slice), out : (address buff
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     # . save registers
     51/push-ecx
-    # var int32-stream/ecx = stream(10)  # number of decimal digits a 32-bit number can have
+    # var int32-stream/ecx : (ref stream byte 10)  # number of decimal digits a 32-bit number can have
     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0xa/imm32         # subtract from esp
     68/push  0xa/imm32/decimal-digits-in-32bit-number
     68/push  0/imm32/read
@@ -846,7 +846,7 @@ test-subx-dquotes-processes-string-literals:
     c3/return
 
 # generate the data segment contents byte by byte for a given slice
-emit-string-literal-data:  # out : (address stream), word : (address slice)
+emit-string-literal-data:  # out : (address stream byte), word : (address slice)
     # pseudocode
     #   len = string-length-at-start-of-slice(word->start, word->end)
     #   print(out, "#{len}/imm32 ")
@@ -885,14 +885,14 @@ emit-string-literal-data:  # out : (address stream), word : (address slice)
     56/push-esi
     # esi = word
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   0xc/disp8       .                 # copy *(ebp+12) to esi
-    # idx/ebx = 0
+    # var idx/ebx : int = 0
     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
-    # curr/edx = word->start
+    # var curr/edx : (address byte) = word->start
     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           2/r32/edx   .               .                 # copy *esi to edx
-    # max/esi = word->end
+    # var max/esi : (address byte) = word->end
     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           6/r32/esi   4/disp8         .                 # copy *(esi+4) to esi
 $emit-string-literal-data:emit-length:
-    # len/eax = string-length-at-start-of-slice(word->start, word->end)
+    # var len/eax : int = string-length-at-start-of-slice(word->start, word->end)
     # . . push args
     56/push-esi
     52/push-edx
@@ -920,7 +920,7 @@ $emit-string-literal-data:emit-length:
 $emit-string-literal-data:loop-init:
     # ++curr  # skip initial '"'
     42/increment-edx
-    # c/ecx = 0
+    # var c/ecx : byte = 0
     31/xor                          3/mod/direct    1/rm32/ecx    .           .             .           1/r32/ecx   .               .                 # clear ecx
 $emit-string-literal-data:loop:
     # if (curr >= max) break
@@ -955,15 +955,15 @@ $emit-string-literal-data:emit:
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     # if (is-alphanumeric?(*curr)) print(out, "/#{*curr}")
-    # . eax = is-alphanumeric?(CL)
+    # . var eax : boolean = is-alphanumeric?(CL)
     # . . push args
     51/push-ecx
     # . . call
     e8/call  is-alphanumeric?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # . if (eax == 0) goto char-done
-    3d/compare-eax-and  0/imm32
+    # . if (eax == false) goto char-done
+    3d/compare-eax-and  0/imm32/false
     74/jump-if-equal  $emit-string-literal-data:char-done/disp8
     # . write(out, "/")
     # . . push args
@@ -1027,22 +1027,22 @@ is-alphanumeric?:  # c : int -> eax : boolean
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     # eax = c
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   8/disp8         .                 # copy *(ebp+8) to eax
-    # if (eax < '0') return false
+    # if (c < '0') return false
     3d/compare-eax-with  0x30/imm32/0
     7c/jump-if-lesser  $is-alphanumeric?:false/disp8
-    # if (eax <= '9') return true
+    # if (c <= '9') return true
     3d/compare-eax-with  0x39/imm32/9
     7e/jump-if-lesser-or-equal  $is-alphanumeric?:true/disp8
-    # if (eax < 'A') return false
+    # if (c < 'A') return false
     3d/compare-eax-with  0x41/imm32/A
     7c/jump-if-lesser  $is-alphanumeric?:false/disp8
-    # if (eax <= 'Z') return true
+    # if (c <= 'Z') return true
     3d/compare-eax-with  0x5a/imm32/Z
     7e/jump-if-lesser-or-equal  $is-alphanumeric?:true/disp8
-    # if (eax < 'a') return false
+    # if (c < 'a') return false
     3d/compare-eax-with  0x61/imm32/a
     7c/jump-if-lesser  $is-alphanumeric?:false/disp8
-    # if (eax <= 'z') return true
+    # if (c <= 'z') return true
     3d/compare-eax-with  0x7a/imm32/z
     7e/jump-if-lesser-or-equal  $is-alphanumeric?:true/disp8
     # return false
@@ -1381,7 +1381,7 @@ test-emit-string-literal-data-handles-newline-escape:
 # emit everything from a word except the initial datum
 emit-metadata:  # out : (address buffered-file), word : (address slice)
     # pseudocode
-    #   var slice = {0, word->end}
+    #   var slice : (ref slice) = {0, word->end}
     #   curr = word->start
     #   if *curr == '"'
     #     curr = skip-string-in-slice(curr, word->end)
@@ -1406,11 +1406,11 @@ emit-metadata:  # out : (address buffered-file), word : (address slice)
     56/push-esi
     # esi = word
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   0xc/disp8       .                 # copy *(ebp+12) to esi
-    # curr/ecx = word->start
+    # var curr/ecx : (address byte) = word->start
     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # copy *esi to ecx
-    # end/edx = word->end
+    # var end/edx : (address byte) = word->end
     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           2/r32/edx   4/disp8         .                 # copy *(esi+4) to edx
-    # var slice/ebx = {0, end}
+    # var slice/ebx : (ref slice) = {0, end}
     52/push-edx
     68/push  0/imm32
     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
@@ -1790,9 +1790,9 @@ string-length-at-start-of-slice:  # curr : (address byte), end : (address byte)
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
     # edx = end
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         2/r32/edx   0xc/disp8         .               # copy *(ebp+12) to edx
-    # length/eax = 0
+    # var length/eax : int = 0
     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
-    # ebx = 0
+    # var c/ebx : byte = 0
     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
     # skip initial dquote
     41/increment-ecx
@@ -1800,14 +1800,14 @@ $string-length-at-start-of-slice:loop:
     # if (curr >= end) return length
     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
     73/jump-if-greater-unsigned-or-equal  $string-length-at-start-of-slice:end/disp8
-    # BL = *curr
+    # c = *curr
     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           3/r32/BL    .               .                 # copy byte at *ecx to BL
 $string-length-at-start-of-slice:dquote:
-    # if (ebx == '"') break
+    # if (c == '"') break
     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x22/imm32/dquote # compare ebx
     74/jump-if-equal  $string-length-at-start-of-slice:end/disp8
 $string-length-at-start-of-slice:check-for-escape:
-    # if (ebx == '\') escape next char
+    # if (c == '\') escape next char
     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x5c/imm32/backslash # compare ebx
     75/jump-if-not-equal  $string-length-at-start-of-slice:continue/disp8
 $string-length-at-start-of-slice:escape:
diff --git a/apps/factorial b/apps/factorial
index d3cee9da..dd981d0d 100755
--- a/apps/factorial
+++ b/apps/factorial
Binary files differdiff --git a/apps/factorial.subx b/apps/factorial.subx
index 26cf3f9d..019329e3 100644
--- a/apps/factorial.subx
+++ b/apps/factorial.subx
@@ -45,8 +45,8 @@ Entry:  # run tests if necessary, compute `factorial(5)` if not
     e8/call  kernel-string-equal?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-    # . if (eax == 0) goto run-main
-    3d/compare-eax-and  0/imm32
+    # . if (eax == false) goto run-main
+    3d/compare-eax-and  0/imm32/false
     74/jump-if-equal  $run-main/disp8
     # run-tests()
     e8/call  run-tests/disp32
@@ -77,10 +77,10 @@ factorial:  # n : int -> int/eax
     b8/copy-to-eax  1/imm32
     81          7/subop/compare     1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         1/imm32           # compare *(ebp+8)
     7e/jump-if-<=  $factorial:end/disp8
-    # ebx = n-1
+    # var ebx : int = n-1
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         3/r32/ebx   8/disp8         .                 # copy *(ebp+8) to ebx
     4b/decrement-ebx
-    # eax = factorial(n-1)
+    # var eax : int = factorial(n-1)
     # . . push args
     53/push-ebx
     # . . call
diff --git a/apps/handle b/apps/handle
index fdc96dcf..23993ca6 100755
--- a/apps/handle
+++ b/apps/handle
Binary files differdiff --git a/apps/handle.subx b/apps/handle.subx
index 7c4657ec..07611596 100644
--- a/apps/handle.subx
+++ b/apps/handle.subx
@@ -44,7 +44,7 @@ $handle-main:end:
     b8/copy-to-eax  1/imm32/exit
     cd/syscall  0x80/imm8
 
-new:  # ad : (address allocation-descriptor), n : int, out : (address handle)
+new:  # ad : (address allocation-descriptor), n : int, out : (handle _)
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
@@ -55,7 +55,7 @@ new:  # ad : (address allocation-descriptor), n : int, out : (address handle)
     # ecx = n+4
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   0xc/disp8       .                 # copy *(ebp+12) to ecx
     81          0/subop/add         3/mod/direct    1/rm32/ecx    .           .             .           .           .               4/imm32           # add to ecx
-    # eax = allocate(ad, ecx)
+    # var eax : (handle _) = allocate(ad, ecx)
     # . . push args
     51/push-ecx
     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
@@ -96,7 +96,7 @@ test-new:
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
-    # var heap/edx : (address allocation-descriptor) = {0, 0}
+    # var heap/edx : (ref allocation-descriptor)
     68/push  0/imm32/limit
     68/push  0/imm32/curr
     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
@@ -110,7 +110,7 @@ test-new:
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     # *Next-alloc-id = 0x34
     c7          0/subop/copy        0/mod/indirect  5/rm32/.disp32            .             .           .     Next-alloc-id/disp32  0x34/imm32        # copy to *Next-alloc-id
-    # var handle/ecx = {0, 0}
+    # var handle/ecx : (ref handle)
     68/push  0/imm32/address
     68/push  0/imm32/alloc-id
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -166,7 +166,7 @@ _pending-test-new-failure:
     # . *Next-alloc-id = 0x34
     c7          0/subop/copy        0/mod/indirect  5/rm32/.disp32            .             .           .           Next-alloc-id/disp32  0x34/imm32  # copy to *Next-alloc-id
     # define an allocation-descriptor with no space left
-    # . var ad/eax : (address allocation-descriptor) = {0x10, 0x10}
+    # . var ad/eax : (ref allocation-descriptor) = {0x10, 0x10}
     68/push  0x10/imm32/limit
     68/push  0x10/imm32/curr
     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           4/r32/esp   .               .                 # copy esp to eax
@@ -282,7 +282,7 @@ test-lookup-success:
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     # . save registers
-    # var heap/ebx : (address allocation-descriptor) = {0, 0}
+    # var heap/ebx : (ref allocation-descriptor)
     68/push  0/imm32/limit
     68/push  0/imm32/curr
     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
@@ -294,7 +294,7 @@ test-lookup-success:
     e8/call  new-segment/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-    # var handle/ecx = {0, 0}
+    # var handle/ecx : (ref handle)
     68/push  0/imm32/address
     68/push  0/imm32/alloc-id
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -350,7 +350,7 @@ test-lookup-failure:
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
-    # var heap/esi : (address allocation-descriptor) = {0, 0}
+    # var heap/esi : (ref allocation-descriptor)
     68/push  0/imm32/limit
     68/push  0/imm32/curr
     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           4/r32/esp   .               .                 # copy esp to esi
@@ -362,7 +362,7 @@ test-lookup-failure:
     e8/call  new-segment/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-    # var h1/ecx = {0, 0}
+    # var h1/ecx : (ref handle)
     68/push  0/imm32/address
     68/push  0/imm32/alloc-id
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -381,7 +381,7 @@ test-lookup-failure:
     # reset heap->curr to mimic reclamation
     89/copy                         0/mod/indirect  6/rm32/esi    .           .             .           3/r32/ebx   .               .                 # copy ebx to *esi
     # second allocation that returns the same address as the first
-    # var h2/edx = {0, 0}
+    # var h2/edx : (ref handle)
     68/push  0/imm32/address
     68/push  0/imm32/alloc-id
     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
@@ -422,7 +422,7 @@ test-lookup-failure:
 == data
 
 # Monotonically increasing counter for calls to 'new'
-Next-alloc-id:
+Next-alloc-id:  # int
     1/imm32
 
 # . . vim:nowrap:textwidth=0
diff --git a/apps/hex b/apps/hex
index 785d81f8..d349dea8 100755
--- a/apps/hex
+++ b/apps/hex
Binary files differdiff --git a/apps/hex.subx b/apps/hex.subx
index 80469961..09e394bd 100644
--- a/apps/hex.subx
+++ b/apps/hex.subx
@@ -44,8 +44,8 @@ Entry:  # run tests if necessary, convert stdin if not
     e8/call  kernel-string-equal?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-    # . if (eax == 0) goto interactive
-    3d/compare-eax-and  0/imm32
+    # . if (eax == false) goto interactive
+    3d/compare-eax-and  0/imm32/false
     74/jump-if-equal  $subx-hex-main:interactive/disp8
     # run-tests()
     e8/call  run-tests/disp32
@@ -54,7 +54,7 @@ Entry:  # run tests if necessary, convert stdin if not
     eb/jump  $subx-hex-main:end/disp8
 $subx-hex-main:interactive:
     # - otherwise convert stdin
-    # var ed/eax : exit-descriptor
+    # var ed/eax : (ref exit-descriptor)
     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           4/r32/esp   .               .                 # copy esp to eax
     # configure ed to really exit()
@@ -247,7 +247,7 @@ test-convert-next-octet:
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     # initialize exit-descriptor 'ed' for the call to 'convert-next-octet' below
-    # . var ed/ecx : exit-descriptor
+    # . var ed/ecx : (ref exit-descriptor)
     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
     # . tailor-exit-descriptor(ed, 12)
@@ -337,7 +337,7 @@ test-convert-next-octet-handles-Eof:
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     # don't initialize '_test-stream'
     # initialize exit-descriptor 'ed' for the call to 'convert-next-octet' below
-    # . var ed/ecx : exit-descriptor
+    # . var ed/ecx : (ref exit-descriptor)
     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
     # . tailor-exit-descriptor(ed, 12)
@@ -435,7 +435,7 @@ test-convert-next-octet-aborts-on-single-hex-byte:
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     # initialize exit-descriptor 'ed' for the call to 'convert-next-octet' below
-    # . var ed/ecx : exit-descriptor
+    # . var ed/ecx : (ref exit-descriptor)
     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
     # . tailor-exit-descriptor(ed, 12)
@@ -604,7 +604,7 @@ test-scan-next-byte:
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below
-    # . var ed/ecx : exit-descriptor
+    # . var ed/ecx : (ref exit-descriptor)
     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
     # . tailor-exit-descriptor(ed, 12)
@@ -702,7 +702,7 @@ test-scan-next-byte-skips-whitespace:
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below
-    # . var ed/ecx : exit-descriptor
+    # . var ed/ecx : (ref exit-descriptor)
     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
     # . tailor-exit-descriptor(ed, 12)
@@ -808,7 +808,7 @@ test-scan-next-byte-skips-comment:
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below
-    # . var ed/ecx : exit-descriptor
+    # . var ed/ecx : (ref exit-descriptor)
     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
     # . tailor-exit-descriptor(ed, 12)
@@ -914,7 +914,7 @@ test-scan-next-byte-skips-comment-and-whitespace:
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below
-    # . var ed/ecx : exit-descriptor
+    # . var ed/ecx : (ref exit-descriptor)
     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
     # . tailor-exit-descriptor(ed, 12)
@@ -1022,7 +1022,7 @@ test-scan-next-byte-skips-whitespace-and-comment:
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below
-    # . var ed/ecx : exit-descriptor
+    # . var ed/ecx : (ref exit-descriptor)
     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
     # . tailor-exit-descriptor(ed, 12)
@@ -1120,7 +1120,7 @@ test-scan-next-byte-reads-final-byte:
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below
-    # . var ed/ecx : exit-descriptor
+    # . var ed/ecx : (ref exit-descriptor)
     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
     # . tailor-exit-descriptor(ed, 12)
@@ -1210,7 +1210,7 @@ test-scan-next-byte-handles-Eof:
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     # leave '_test-stream' empty
     # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below
-    # . var ed/ecx : exit-descriptor
+    # . var ed/ecx : (ref exit-descriptor)
     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
     # . tailor-exit-descriptor(ed, 12)
@@ -1308,7 +1308,7 @@ test-scan-next-byte-aborts-on-invalid-byte:
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     # initialize exit-descriptor 'ed' for the call to 'scan-next-byte' below
-    # . var ed/ecx : exit-descriptor
+    # . var ed/ecx : (ref exit-descriptor)
     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
     # . tailor-exit-descriptor(ed, 12)
diff --git a/apps/mu b/apps/mu
index e95a43f1..0e2717c7 100755
--- a/apps/mu
+++ b/apps/mu
Binary files differdiff --git a/apps/mu.subx b/apps/mu.subx
index b0cf03c1..e4b8604b 100644
--- a/apps/mu.subx
+++ b/apps/mu.subx
@@ -7,7 +7,8 @@
 # == Goals
 # 1. Be memory safe. It should be impossible to corrupt the heap, or to create
 # a bad pointer. (Requires strong type safety.)
-# 2. Do as little as possible to achieve goal 1.
+# 2. Do as little as possible to achieve goal 1. The translator should be
+# implementable in machine code.
 #   - minimize impedance mismatch between source language and SubX target
 #     (e.g. programmer manages registers manually)
 #   - checks over syntax
@@ -63,9 +64,10 @@
 #         }
 #
 #   - variable definitions on the stack. E.g.:
-#       - var foo: int
-#       - var bar: (array int 3)
+#       - var foo: (ref int)
+#       - var bar: (ref array int 3)
 #     There's no initializer; variables are automatically initialized.
+#     The type of a local variable is either word-length (4 bytes) or starts with 'ref'.
 #
 #   - variables definitions in a register. E.g.:
 #       - var foo/eax : int <- add bar 1
@@ -81,20 +83,24 @@
 #   user-defined types: 'type' for structs, 'choice' for unions
 #   short-lived 'address' type for efficiently writing inside nested structs
 #
+# We don't have 'handle' types yet, but we try to distinguish 'ref', 'handle'
+# and 'address' in comments. Their definitions are in layer 50, but really you
+# can ignore the distinctions on a first reading of this program.
+#
 # Formal types:
 #   A program is a linked list of functions
 #   A function contains:
-#     name: string
+#     name: (handle array byte)
 #     inouts: linked list of vars  <-- 'inouts' is more precise than 'inputs'
-#       data: (address var)
-#       next: (address list)
+#       data: (handle var)
+#       next: (handle list)
 #     outputs: linked list of vars
-#       data: (address var)
-#       next: (address list)
-#     body: block
+#       data: (handle var)
+#       next: (handle list)
+#     body: (handle block)
 #   A var-type contains:
-#     name: string
-#     type: s-expression of type ids
+#     name: (handle array byte)
+#     type: (handle s-expression type-id)
 #
 #   A statement can be:
 #     tag 0: a block
@@ -105,29 +111,29 @@
 #
 #   A block contains:
 #     tag: 0
-#     statements: (address list statement)
+#     statements: (handle list statement)
 #
 #   A regular statement contains:
 #     tag: 1
-#     operation: string
-#     inouts: (address list operand)
-#     outputs: (address list var)
+#     operation: (handle array byte)
+#     inouts: (handle list operand)
+#     outputs: (handle list var)
 #
 #   A variable defined on the stack contains:
 #     tag: 2
-#     name: string
-#     type: type-tree
+#     name: (handle array byte)
+#     type: (handle s-expression type-id)
 #
 #   A variable defined in a register contains:
 #     tag: 3
-#     name: string
-#     type: type-tree
-#     reg: string
+#     name: (handle array byte)
+#     type: (handle s-expression type-id)
+#     reg: (handle array byte)
 #
 #   A named block contains:
 #     tag: 4
-#     name: string
-#     statements: (address list statement)
+#     name: (handle array byte)
+#     statements: (handle list statement)
 
 # == Translation: managing the stack
 # Now that we know what the language looks like in the large, let's think
@@ -151,11 +157,11 @@
 # Formal types:
 #   live-vars: stack of vars
 #   var:
-#     name: string
+#     name: (handle array byte)
 #     type: s-expression? Just a type id for now.
 #     block: int
 #     stack-offset: int  (added to ebp)
-#     register: string
+#     register: (handle array byte)
 #       either usual register names
 #       or '*' to indicate any register
 #   At most one of stack-offset or register-index must be non-zero.
@@ -172,11 +178,11 @@
 #
 # Formal types:
 #   functions: linked list of info
-#     name: string
+#     name: (handle array byte)
 #     inouts: linked list of vars
 #     outputs: linked list of vars
 #     body: block (singleton linked list)
-#     subx-name: string
+#     subx-name: (handle array byte)
 
 # == Translating a single primitive instruction
 # A second crucial piece of the puzzle is how Mu converts fairly regular
@@ -208,10 +214,10 @@
 #
 # Accordingly, the formal data structure for a primitive looks like this:
 #   primitives: linked list of info
-#     name: string
+#     name: (handle array byte)
 #     mu-inouts: linked list of vars to check
 #     mu-outputs: linked list of vars to check
-#     subx-name: string
+#     subx-name: (handle array byte)
 #     subx-rm32: enum arg-location
 #     subx-r32: enum arg-location
 #     subx-imm32: enum arg-location
@@ -239,31 +245,31 @@
 
 == data
 
-Program:  # (address function)
+Program:  # (handle function)
   0/imm32
 
 Function-name:
   0/imm32
 Function-subx-name:
   4/imm32
-Function-inouts:  # (address list var)
+Function-inouts:  # (handle list var)
   8/imm32
-Function-outputs:  # (address list var)
+Function-outputs:  # (handle list var)
   0xc/imm32
-Function-body:  # (address block)
+Function-body:  # (handle block)
   0x10/imm32
-Function-next:  # (address function)
+Function-next:  # (handle function)
   0x14/imm32
 Function-size:
   0x18/imm32/24
 
 Primitive-name:
   0/imm32
-Primitive-inouts:  # (address list var)
+Primitive-inouts:  # (handle list var)
   4/imm32
-Primitive-outputs:  # (address list var)
+Primitive-outputs:  # (handle list var)
   8/imm32
-Primitive-subx-name:  # (address string)
+Primitive-subx-name:  # (handle array byte)
   0xc/imm32
 Primitive-subx-rm32:  # enum arg-location
   0x10/imm32
@@ -271,7 +277,7 @@ Primitive-subx-r32:  # enum arg-location
   0x14/imm32
 Primitive-subx-imm32:  # enum arg-location
   0x18/imm32
-Primitive-next:  # (address function)
+Primitive-next:  # (handle function)
   0x1c/imm32
 Primitive-size:
   0x20/imm32/24
@@ -279,31 +285,31 @@ Primitive-size:
 Stmt-tag:
   0/imm32
 
-Block-statements:  # (address list statement)
+Block-statements:  # (handle list statement)
   4/imm32
 
-Stmt1-operation:  # string
+Stmt1-operation:  # (handle array byte)
   4/imm32
-Stmt1-inouts:  # (address list var)
+Stmt1-inouts:  # (handle list var)
   8/imm32
-Stmt1-outputs:  # (address list var)
+Stmt1-outputs:  # (handle list var)
   0xc/imm32
 
-Vardef-name:  # string
+Vardef-name:  # (handle array byte)
   4/imm32
-Vardef-type:  # (address tree type-id)
+Vardef-type:  # (handle tree type-id)
   8/imm32
 
-Regvardef-name:  # string
+Regvardef-name:  # (handle array byte)
   4/imm32
-Regvardef-type:  # (address tree type-id)
+Regvardef-type:  # (handle tree type-id)
   8/imm32
-Regvardef-register:  # string
+Regvardef-register:  # (handle array byte)
   0xc/imm32
 
 Named-block-name:
   4/imm32
-Named-block-statements:  # (address list statement)
+Named-block-statements:  # (handle list statement)
   8/imm32
 
 Stmt-size:
@@ -621,9 +627,9 @@ test-convert-function-with-arg-and-body:
 
 parse-mu:  # in : (address buffered-file)
     # pseudocode
-    #   var curr-function = Program
-    #   var line : (stream byte 512)
-    #   var word-slice : slice
+    #   var curr-function : (handle function) = Program
+    #   var line : (ref stream byte 512)
+    #   var word-slice : (ref slice)
     #   while true                                  # line loop
     #     clear-stream(line)
     #     read-line-buffered(in, line)
@@ -634,7 +640,7 @@ parse-mu:  # in : (address buffered-file)
     #     else if slice-starts-with?(word-slice, "#")  # comment
     #       continue                                # end of line
     #     else if slice-equal(word-slice, "fn")
-    #       var new-function : (address function) = new function
+    #       var new-function : (handle function) = new function
     #       populate-mu-function-header(in, new-function)
     #       populate-mu-function-body(in, new-function)
     #       *curr-function = new-function
@@ -650,17 +656,17 @@ parse-mu:  # in : (address buffered-file)
     51/push-ecx
     52/push-edx
     57/push-edi
-    # var line/ecx : (stream byte 512)
+    # var line/ecx : (ref stream byte 512)
     81 5/subop/subtract %esp 0x200/imm32
     68/push 0x200/imm32/length
     68/push 0/imm32/read
     68/push 0/imm32/write
     89/<- %ecx 4/r32/esp
-    # var word-slice/edx : slice
+    # var word-slice/edx : (ref slice)
     68/push 0/imm32/end
     68/push 0/imm32/start
     89/<- %edx 4/r32/esp
-    # var curr-function/edi : (address function) = Program
+    # var curr-function/edi : (handle function) = Program
     bf/copy-to-edi Program/imm32
     {
 $parse-mu:line-loop:
@@ -694,7 +700,7 @@ $parse-mu:fn:
         (slice-equal? %edx "fn")
         3d/compare-eax-and 0/imm32
         0f 84/jump-if-equal break/disp32
-        # var new-function/eax : (address function) = populate-mu-function()
+        # var new-function/eax : (handle function) = populate-mu-function()
         (allocate Heap *Function-size)  # => eax
         (populate-mu-function-header %ecx %eax)
         (populate-mu-function-body *(ebp+8) %eax)
@@ -743,9 +749,10 @@ $parse-mu:abort:
 # ✓ fn foo x : int {
 # ✓ fn foo x: int {
 # ✓ fn foo x: int -> y/eax: int {
-populate-mu-function-header:  # first-line : (address stream byte), out : (address function)
+populate-mu-function-header:  # first-line : (address stream byte), out : (handle function)
     # pseudocode:
-    #   var name : slice = next-word(first-line)
+    #   var name : (ref slice)
+    #   next-word(first-line, name)
     #   assert(name not in '{' '}' '->')
     #   out->name = slice-to-string(name)
     #   ## inouts
@@ -755,14 +762,14 @@ populate-mu-function-header:  # first-line : (address stream byte), out : (addre
     #     if (name == '{') goto done
     #     if (name == '->') break
     #     assert(name != '}')
-    #     var v : (address var) = parse-var-with-type(name, first-line)
+    #     var v : (handle var) = parse-var-with-type(name, first-line)
     #     out->inouts = append(out->inouts, v)
     #   ## outputs
     #   while true
     #     ## name
     #     name = next-word(first-line)
     #     assert(name not in '{' '}' '->')
-    #     var v : (address var) = parse-var-with-type(name, first-line)
+    #     var v : (handle var) = parse-var-with-type(name, first-line)
     #     out->outputs = append(out->outputs, v)
     #   done:
     #
@@ -775,7 +782,7 @@ populate-mu-function-header:  # first-line : (address stream byte), out : (addre
     57/push-edi
     # edi = out
     8b/-> *(ebp+0xc) 7/r32/edi
-    # var word-slice/ecx : slice
+    # var word-slice/ecx : (ref slice)
     68/push 0/imm32/end
     68/push 0/imm32/start
     89/<- %ecx 4/r32/esp
@@ -875,7 +882,7 @@ test-function-header-with-arg:
     # setup
     (clear-stream _test-input-stream)
     (write _test-input-stream "foo n : int {\n")
-    # result/ecx : (address function)
+    # result/ecx : (ref function)
     2b/subtract-> *Function-size 4/r32/esp
     89/<- %ecx 4/r32/esp
     (zero-out %ecx *Function-size)
@@ -883,9 +890,9 @@ test-function-header-with-arg:
     (populate-mu-function-header _test-input-stream %ecx)
     # check result
     (check-strings-equal *ecx "foo" "F - test-function-header-with-arg/name")  # Function-name
-    # edx : (address list var) = result->inouts
+    # edx : (handle list var) = result->inouts
     8b/-> *(ecx+8) 2/r32/edx  # Function-inouts
-    # ebx : (address var) = result->inouts->value
+    # ebx : (handle var) = result->inouts->value
     8b/-> *edx 3/r32/ebx  # List-value
     (check-strings-equal *ebx "n" "F - test-function-header-with-arg/inout:0")  # Var-name
     (check-ints-equal *(ebx+4) 1 "F - test-function-header-with-arg/inout:0/type")  # Var-type
@@ -902,7 +909,7 @@ test-function-header-with-multiple-args:
     # setup
     (clear-stream _test-input-stream)
     (write _test-input-stream "foo a: int, b: int c: int {\n")
-    # result/ecx : (address function)
+    # result/ecx : (handle function)
     2b/subtract-> *Function-size 4/r32/esp
     89/<- %ecx 4/r32/esp
     (zero-out %ecx *Function-size)
@@ -910,9 +917,9 @@ test-function-header-with-multiple-args:
     (populate-mu-function-header _test-input-stream %ecx)
     # check result
     (check-strings-equal *ecx "foo")  # Function-name
-    # edx : (address list var) = result->inouts
+    # edx : (handle list var) = result->inouts
     8b/-> *(ecx+8) 2/r32/edx  # Function-inouts
-    # ebx : (address var) = result->inouts->value
+    # ebx : (handle var) = result->inouts->value
     8b/-> *edx 3/r32/ebx  # List-value
     (check-strings-equal *ebx "a" "F - test-function-header-with-multiple-args/inout:0")  # Var-name
     (check-ints-equal *(ebx+4) 1 "F - test-function-header-with-arg/inout:0/type")  # Var-type
@@ -941,7 +948,7 @@ test-function-with-multiple-args-and-outputs:
     # setup
     (clear-stream _test-input-stream)
     (write _test-input-stream "foo a: int, b: int, c: int -> x/ecx: int y/edx : int {\n")
-    # result/ecx : (address function)
+    # result/ecx : (handle function)
     2b/subtract-> *Function-size 4/r32/esp
     89/<- %ecx 4/r32/esp
     (zero-out %ecx *Function-size)
@@ -949,9 +956,9 @@ test-function-with-multiple-args-and-outputs:
     (populate-mu-function-header _test-input-stream %ecx)
     # check result
     (check-strings-equal *ecx "foo")  # Function-name
-    # edx : (address list var) = result->inouts
+    # edx : (handle list var) = result->inouts
     8b/-> *(ecx+8) 2/r32/edx  # Function-inouts
-    # ebx : (address var) = result->inouts->value
+    # ebx : (handle var) = result->inouts->value
     8b/-> *edx 3/r32/ebx  # List-value
     (check-strings-equal *ebx "a" "F - test-function-header-with-multiple-args/inout:0")  # Var-name
     (check-ints-equal *(ebx+4) 1 "F - test-function-header-with-arg/inout:0/type")  # Var-type
@@ -967,9 +974,9 @@ test-function-with-multiple-args-and-outputs:
     8b/-> *edx 3/r32/ebx  # List-value
     (check-strings-equal *ebx "c" "F - test-function-header-with-multiple-args/inout:2")  # Var-name
     (check-ints-equal *(ebx+4) 1 "F - test-function-header-with-arg/inout:2/type")  # Var-type
-    # edx : (address list var) = result->outputs
+    # edx : (handle list var) = result->outputs
     8b/-> *(ecx+0xc) 2/r32/edx  # Function-outputs
-    # ebx : (address var) = result->outputs->value
+    # ebx : (handle var) = result->outputs->value
     8b/-> *edx 3/r32/ebx  # List-value
     (check-strings-equal *ebx "x" "F - test-function-header-with-multiple-args/output:0")  # Var-name
     (check-ints-equal *(ebx+4) 1 "F - test-function-header-with-arg/output:0/type")  # Var-type
@@ -991,12 +998,12 @@ test-function-with-multiple-args-and-outputs:
 #   x: int
 #   x: int,
 # ignores at most one trailing colon or comma
-parse-var-with-type:  # name: slice, first-line: (address stream) -> result/eax: (address var)
+parse-var-with-type:  # name: (address slice), first-line: (address stream byte) -> result/eax: (handle var)
     # pseudocode:
-    #   var v : (address var) = allocate(Heap, Var-size)
-    #   var s : slice
+    #   var v : (handle var) = allocate(Heap, Var-size)
+    #   var s : (ref slice)
     #   next-token-from-slice(name->start, name->end, '/', s)
-    #   var end : address = s->end
+    #   var end : (address byte) = s->end
     #   if (slice-ends-with(s, ":"))
     #     decrement s->end
     #   if (slice-ends-with(s, ","))
@@ -1030,12 +1037,12 @@ parse-var-with-type:  # name: slice, first-line: (address stream) -> result/eax:
     53/push-ebx
     56/push-esi
     57/push-edi
-    # var result/edi : (address var) = allocate(Heap, Var-size)
+    # var result/edi : (handle var) = allocate(Heap, Var-size)
     (allocate Heap *Var-size)
     89/<- %edi 0/r32/eax
     # esi = name
     8b/-> *(ebp+8) 6/r32/esi
-    # var s/ecx : slice
+    # var s/ecx : (ref slice)
     68/push 0/imm32/end
     68/push 0/imm32/start
     89/<- %ecx 4/r32/esp
@@ -1160,7 +1167,7 @@ $parse-var-with-type:abort:
     cd/syscall  0x80/imm8
     # never gets here
 
-next-mu-token:  # in: (address stream), out: (address slice)
+next-mu-token:  # in: (address stream byte), out: (address slice)
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
@@ -1201,7 +1208,7 @@ $next-mu-token:end:
     5d/pop-to-ebp
     c3/return
 
-type-for:  # name: (address slice) -> result/eax: type-tree
+type-for:  # name: (address slice) -> result/eax: (handle s-expression type-id)
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
@@ -1227,7 +1234,7 @@ test-parse-var-with-type:
     8b/-> *eax 1/r32/ecx
     8d/copy-address *(eax+ecx+4) 1/r32/ecx
     05/add-to-eax 4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/<- %ecx 4/r32/esp
@@ -1254,7 +1261,7 @@ test-parse-var-with-type-and-register:
     8b/-> *eax 1/r32/ecx
     8d/copy-address *(eax+ecx+4) 1/r32/ecx
     05/add-to-eax 4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/<- %ecx 4/r32/esp
@@ -1283,7 +1290,7 @@ test-parse-var-with-trailing-characters:
     8b/-> *eax 1/r32/ecx
     8d/copy-address *(eax+ecx+4) 1/r32/ecx
     05/add-to-eax 4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/<- %ecx 4/r32/esp
@@ -1314,7 +1321,7 @@ is-identifier?:  # in : (address slice) -> result/eax : boolean
     (slice-empty? *(ebp+8))  # => eax
     3d/compare-eax-and 0/imm32
     75/jump-if-not-equal $is-identifier?:false/disp8
-    # var c/eax : char = *in->start
+    # var c/eax : byte = *in->start
     8b/-> *(ebp+8) 0/r32/eax
     8b/-> *eax 0/r32/eax
     8a/copy-byte *eax 0/r32/AL
@@ -1354,7 +1361,7 @@ test-is-identifier-dollar:
     8b/-> *eax 1/r32/ecx
     8d/copy-address *(eax+ecx+4) 1/r32/ecx
     05/add-to-eax 4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/<- %ecx 4/r32/esp
@@ -1375,7 +1382,7 @@ test-is-identifier-underscore:
     8b/-> *eax 1/r32/ecx
     8d/copy-address *(eax+ecx+4) 1/r32/ecx
     05/add-to-eax 4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/<- %ecx 4/r32/esp
@@ -1396,7 +1403,7 @@ test-is-identifier-a:
     8b/-> *eax 1/r32/ecx
     8d/copy-address *(eax+ecx+4) 1/r32/ecx
     05/add-to-eax 4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/<- %ecx 4/r32/esp
@@ -1417,7 +1424,7 @@ test-is-identifier-z:
     8b/-> *eax 1/r32/ecx
     8d/copy-address *(eax+ecx+4) 1/r32/ecx
     05/add-to-eax 4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/<- %ecx 4/r32/esp
@@ -1438,7 +1445,7 @@ test-is-identifier-A:
     8b/-> *eax 1/r32/ecx
     8d/copy-address *(eax+ecx+4) 1/r32/ecx
     05/add-to-eax 4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/<- %ecx 4/r32/esp
@@ -1459,7 +1466,7 @@ test-is-identifier-Z:
     8b/-> *eax 1/r32/ecx
     8d/copy-address *(eax+ecx+4) 1/r32/ecx
     05/add-to-eax 4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/<- %ecx 4/r32/esp
@@ -1481,7 +1488,7 @@ test-is-identifier-@:
     8b/-> *eax 1/r32/ecx
     8d/copy-address *(eax+ecx+4) 1/r32/ecx
     05/add-to-eax 4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/<- %ecx 4/r32/esp
@@ -1503,7 +1510,7 @@ test-is-identifier-square-bracket:
     8b/-> *eax 1/r32/ecx
     8d/copy-address *(eax+ecx+4) 1/r32/ecx
     05/add-to-eax 4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/<- %ecx 4/r32/esp
@@ -1525,7 +1532,7 @@ test-is-identifier-backtick:
     8b/-> *eax 1/r32/ecx
     8d/copy-address *(eax+ecx+4) 1/r32/ecx
     05/add-to-eax 4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/<- %ecx 4/r32/esp
@@ -1547,7 +1554,7 @@ test-is-identifier-curly-brace-open:
     8b/-> *eax 1/r32/ecx
     8d/copy-address *(eax+ecx+4) 1/r32/ecx
     05/add-to-eax 4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/<- %ecx 4/r32/esp
@@ -1568,7 +1575,7 @@ test-is-identifier-curly-brace-close:
     8b/-> *eax 1/r32/ecx
     8d/copy-address *(eax+ecx+4) 1/r32/ecx
     05/add-to-eax 4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/<- %ecx 4/r32/esp
@@ -1590,7 +1597,7 @@ test-is-identifier-hyphen:
     8b/-> *eax 1/r32/ecx
     8d/copy-address *(eax+ecx+4) 1/r32/ecx
     05/add-to-eax 4/imm32
-    # var slice/ecx = {eax, ecx}
+    # var slice/ecx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/<- %ecx 4/r32/esp
@@ -1602,7 +1609,7 @@ test-is-identifier-hyphen:
     5d/pop-to-ebp
     c3/return
 
-populate-mu-function-body:  # in : (address buffered-file), out : (address function)
+populate-mu-function-body:  # in : (address buffered-file), out : (handle function)
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
@@ -1614,7 +1621,7 @@ populate-mu-function-body:  # in : (address buffered-file), out : (address funct
     8b/-> *(ebp+8) 6/r32/esi
     # edi = out
     8b/-> *(ebp+0xc) 7/r32/edi
-    # var eax : (address block) = parse-mu-block(in)
+    # var eax : (handle block) = parse-mu-block(in)
     (parse-mu-block %esi)  # => eax
     # out->body = eax
     89/<- *(edi+0x10) 0/r32/eax  # Function-body
@@ -1629,10 +1636,10 @@ $populate-mu-function-body:end:
     c3/return
 
 # parses a block, assuming that the leading '{' has already been read by the caller
-parse-mu-block:  # in : (address buffered-file) -> result/eax : (address block)
+parse-mu-block:  # in : (address buffered-file) -> result/eax : (handle block)
     # pseudocode:
-    #   var line : (stream byte 512)
-    #   var word-slice : slice
+    #   var line : (ref stream byte 512)
+    #   var word-slice : (ref slice)
     #   result/eax = allocate(Heap, Stmt-size)
     #   result->tag = 0/Block
     #   while true                                  # line loop
@@ -1670,13 +1677,13 @@ parse-mu-block:  # in : (address buffered-file) -> result/eax : (address block)
     53/push-ebx
     56/push-esi
     57/push-edi
-    # var line/ecx : (stream byte 512)
+    # var line/ecx : (ref stream byte 512)
     81 5/subop/subtract %esp 0x200/imm32
     68/push 0x200/imm32/length
     68/push 0/imm32/read
     68/push 0/imm32/write
     89/<- %ecx 4/r32/esp
-    # var word-slice/edx : slice
+    # var word-slice/edx : (ref slice)
     68/push 0/imm32/end
     68/push 0/imm32/start
     89/<- %edx 4/r32/esp
@@ -1791,14 +1798,14 @@ $parse-mu-block:abort:
     cd/syscall  0x80/imm8
     # never gets here
 
-check-no-tokens-left:  # line : (address stream)
+check-no-tokens-left:  # line : (address stream byte)
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
     # . save registers
     50/push-eax
     51/push-ecx
-    # var s/ecx : slice = next-word(line)
+    # var s/ecx : (ref slice)
     68/push 0/imm32/end
     68/push 0/imm32/start
     89/<- %ecx 4/r32/esp
@@ -1838,10 +1845,10 @@ $check-no-tokens-left:end:
     5d/pop-to-ebp
     c3/return
 
-parse-mu-named-block:  # name : (address slice), first-line : (address stream), in : (address buffered-file) -> result/eax : (address stmt)
+parse-mu-named-block:  # name : (address slice), first-line : (address stream byte), in : (address buffered-file) -> result/eax : (handle stmt)
     # pseudocode:
-    #   var line : (stream byte 512)
-    #   var word-slice : slice
+    #   var line : (ref stream byte 512)
+    #   var word-slice : (ref slice)
     #   result/eax = allocate(Heap, Stmt-size)
     #   result->tag = 4/Named-block
     #   result->name = name
@@ -1882,7 +1889,7 @@ $parse-mu-named-block:end:
     5d/pop-to-ebp
     c3/return
 
-parse-mu-var-def:  # line : (address stream) -> result/eax : (address stmt)
+parse-mu-var-def:  # line : (address stream byte) -> result/eax : (handle stmt)
     # pseudocode:
     #
     # . prologue
@@ -1897,10 +1904,10 @@ $parse-mu-var-def:end:
     5d/pop-to-ebp
     c3/return
 
-parse-mu-stmt:  # line : (address stream) -> result/eax : (address stmt)
+parse-mu-stmt:  # line : (address stream byte) -> result/eax : (handle stmt)
     # pseudocode:
-    #   var name : slice
-    #   var v : (address var)
+    #   var name : (ref slice)
+    #   var v : (ref var)
     #   result = allocate(Heap, Stmt-size)
     #   if stmt-has-outputs?(line)
     #     while true
@@ -1921,11 +1928,11 @@ parse-mu-stmt:  # line : (address stream) -> result/eax : (address stmt)
     # . save registers
     51/push-ecx
     57/push-edi
-    # var name/ecx : (address slice)
+    # var name/ecx : (ref slice)
     68/push 0/imm32/end
     68/push 0/imm32/start
     89/<- %ecx 4/r32/esp
-    # var result/edi : (address stmt)
+    # result/edi : (handle stmt)
     (allocate Heap *Stmt-size)
     89/<- %edi 0/r32/eax
     # result->tag = 1/stmt
@@ -2017,13 +2024,13 @@ $parse-mu-stmt:abort2:
     cd/syscall  0x80/imm8
     # never gets here
 
-stmt-has-outputs?:  # line : (address stream) -> result/eax : boolean
+stmt-has-outputs?:  # line : (address stream byte) -> result/eax : boolean
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
     # . save registers
     51/push-ecx
-    # var word-slice/ecx : slice
+    # var word-slice/ecx : (ref slice)
     68/push 0/imm32/end
     68/push 0/imm32/start
     89/<- %ecx 4/r32/esp
@@ -2063,7 +2070,7 @@ $stmt-has-outputs:end:
     5d/pop-to-ebp
     c3/return
 
-parse-var:  # ad: allocation-descriptor, name: (address slice) -> result/eax: (address var)
+parse-var:  # ad: allocation-descriptor, name: (address slice) -> result/eax: (handle var)
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
@@ -2099,9 +2106,9 @@ test-parse-mu-stmt:
     (parse-mu-stmt _test-input-stream)
     # check result
     (check-strings-equal *(eax+4) "increment" "F - test-parse-mu-stmt/name")  # Stmt1-operation
-    # edx : (address list var) = result->inouts
+    # edx : (handle list var) = result->inouts
     8b/-> *(eax+8) 2/r32/edx  # Stmt1-inouts
-    # ebx : (address var) = result->inouts->value
+    # ebx : (handle var) = result->inouts->value
     8b/-> *edx 3/r32/ebx  # List-value
     (check-strings-equal *ebx "n" "F - test-parse-mu-stmt/inout:0")  # Var-name
     # . epilogue
@@ -2109,7 +2116,7 @@ test-parse-mu-stmt:
     5d/pop-to-ebp
     c3/return
 
-new-function:  # ad: allocation-descriptor, name: string, subx-name: string, inouts: (address list var), outputs: (address list var), body: (address block), next: (address function) -> result/eax: (address function)
+new-function:  # ad: allocation-descriptor, name: string, subx-name: string, inouts: (handle list var), outputs: (handle list var), body: (handle block), next: (handle function) -> result/eax: (handle function)
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
@@ -2137,7 +2144,7 @@ $new-function:end:
     5d/pop-to-ebp
     c3/return
 
-new-var:  # ad: allocation-descriptor, name: string, type: int, block: int, stack-offset: int, register: string -> result/eax: (address var)
+new-var:  # ad: allocation-descriptor, name: string, type: int, block: int, stack-offset: int, register: string -> result/eax: (handle var)
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
@@ -2163,7 +2170,7 @@ $new-var:end:
     5d/pop-to-ebp
     c3/return
 
-new-block:  # ad: allocation-descriptor, data: (address list statement) -> result/eax: (address statement)
+new-block:  # ad: allocation-descriptor, data: (handle list statement) -> result/eax: (handle statement)
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
@@ -2182,7 +2189,7 @@ $new-block:end:
     5d/pop-to-ebp
     c3/return
 
-new-stmt:  # ad: allocation-descriptor, operation: string, inouts: (address list var), outputs: (address list var) -> result/eax: (address statement)
+new-stmt:  # ad: allocation-descriptor, operation: string, inouts: (handle list var), outputs: (handle list var) -> result/eax: (handle statement)
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
@@ -2205,7 +2212,7 @@ $new-stmt:end:
     5d/pop-to-ebp
     c3/return
 
-new-vardef:  # ad: allocation-descriptor, name: string, type: int -> result/eax: (address statement)
+new-vardef:  # ad: allocation-descriptor, name: string, type: int -> result/eax: (handle statement)
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
@@ -2226,7 +2233,7 @@ $new-vardef:end:
     5d/pop-to-ebp
     c3/return
 
-new-regvardef:  # ad: allocation-descriptor, name: string, type: int, register: string -> result/eax: (address statement)
+new-regvardef:  # ad: allocation-descriptor, name: string, type: int, register: string -> result/eax: (handle statement)
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
@@ -2249,7 +2256,7 @@ $new-regvardef:end:
     5d/pop-to-ebp
     c3/return
 
-new-named-block:  # ad: allocation-descriptor, name: string, data: (address list statement) -> result/eax: (address statement)
+new-named-block:  # ad: allocation-descriptor, name: string, data: (handle list statement) -> result/eax: (handle statement)
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
@@ -2270,7 +2277,7 @@ $new-named-block:end:
     5d/pop-to-ebp
     c3/return
 
-new-list:  # ad: allocation-descriptor, value: _type, next: (address list _type) -> result/eax : (address list _type)
+new-list:  # ad: allocation-descriptor, value: _type, next: (handle list _type) -> result/eax : (handle list _type)
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
@@ -2290,7 +2297,7 @@ $new-list:end:
     5d/pop-to-ebp
     c3/return
 
-append-list:  # ad: allocation-descriptor, value: _type, list: (address list _type) -> result/eax : (address list _type)
+append-list:  # ad: allocation-descriptor, value: _type, list: (handle list _type) -> result/eax : (handle list _type)
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
@@ -2326,7 +2333,7 @@ $append-list:end:
     5d/pop-to-ebp
     c3/return
 
-append-to-block:  # ad: allocation-descriptor, block: (address block), x: (address stmt)
+append-to-block:  # ad: allocation-descriptor, block: (handle block), x: (handle stmt)
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
@@ -2373,10 +2380,10 @@ emit-subx:  # out : (address buffered-file)
     57/push-edi
     # edi = out
     8b/-> *(ebp+8) 7/r32/edi
-    # var curr/ecx : (address function) = Program
+    # var curr/ecx : (handle function) = Program
     8b/-> *Program 1/r32/ecx
     {
-      # if (curr == NULL) break
+      # if (curr == null) break
       81 7/subop/compare %ecx 0/imm32
       0f 84/jump-if-equal break/disp32
       (emit-subx-function %edi %ecx)
@@ -2394,7 +2401,7 @@ $emit-subx:end:
     5d/pop-to-ebp
     c3/return
 
-emit-subx-function:  # out : (address buffered-file), f : (address function)
+emit-subx-function:  # out : (address buffered-file), f : (handle function)
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
@@ -2422,11 +2429,11 @@ $emit-subx-function:end:
     5d/pop-to-ebp
     c3/return
 
-emit-subx-block:  # out : (address buffered-file), block : (address block)
+emit-subx-block:  # out : (address buffered-file), block : (handle block)
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
-    # curr/esi : (address list statement) = block->statements
+    # curr/esi : (handle list statement) = block->statements
     8b/-> *(ebp+0xc) 6/r32/esi
     8b/-> *(esi+4) 6/r32/esi  # Block-statements
     #
@@ -2451,7 +2458,7 @@ $emit-subx-block:end:
     5d/pop-to-ebp
     c3/return
 
-emit-subx-statement:  # out : (address buffered-file), stmt : (address statement), vars : (stack var), primitives : (address primitive), functions : (address function)
+emit-subx-statement:  # out : (address buffered-file), stmt : (handle statement), vars : (handle stack var), primitives : (handle primitive), functions : (handle function)
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
@@ -2614,7 +2621,7 @@ Lit-var:
     0/imm32/no-register
 
 == code
-emit-subx-primitive:  # out : (address buffered-file), stmt : (address statement), vars : (address variable), primitive : (address function)
+emit-subx-primitive:  # out : (address buffered-file), stmt : (handle statement), vars : (handle variable), primitive : (handle function)
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
@@ -2640,7 +2647,7 @@ $emit-subx-primitive:end:
     5d/pop-to-ebp
     c3/return
 
-emit-subx-rm32:  # out : (address buffered-file), l : arg-location, stmt : (address statement)
+emit-subx-rm32:  # out : (address buffered-file), l : arg-location, stmt : (handle statement)
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
@@ -2660,7 +2667,7 @@ $emit-subx-rm32:end:
     5d/pop-to-ebp
     c3/return
 
-get-stmt-operand-from-arg-location:  # stmt : (address statement), l : arg-location -> var/eax : (address variable)
+get-stmt-operand-from-arg-location:  # stmt : (handle statement), l : arg-location -> var/eax : (handle variable)
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
@@ -2720,7 +2727,7 @@ $get-stmt-operand-from-arg-location:abort:
     cd/syscall  0x80/imm8
     # never gets here
 
-emit-subx-r32:  # out : (address buffered-file), l : arg-location, stmt : (address statement)
+emit-subx-r32:  # out : (address buffered-file), l : arg-location, stmt : (handle statement)
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
@@ -2745,7 +2752,7 @@ $emit-subx-r32:end:
     5d/pop-to-ebp
     c3/return
 
-emit-subx-imm32:  # out : (address buffered-file), l : arg-location, stmt : (address statement)
+emit-subx-imm32:  # out : (address buffered-file), l : arg-location, stmt : (handle statement)
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
@@ -2769,7 +2776,7 @@ $emit-subx-imm32:end:
     5d/pop-to-ebp
     c3/return
 
-emit-subx-call:  # out : (address buffered-file), stmt : (address statement), vars : (address variable), callee : (address function)
+emit-subx-call:  # out : (address buffered-file), stmt : (handle statement), vars : (handle variable), callee : (handle function)
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
@@ -2782,7 +2789,7 @@ emit-subx-call:  # out : (address buffered-file), stmt : (address statement), va
     8b/-> *(ebp+0x14) 1/r32/ecx
     (write-buffered *(ebp+8) *(ecx+4))  # Function-subx-name
     # - emit arguments
-    # var curr/ecx : (list var) = stmt->inouts
+    # var curr/ecx : (handle list var) = stmt->inouts
     8b/-> *(ebp+0xc) 1/r32/ecx
     8b/-> *(ecx+8) 1/r32/ecx  # Stmt1-inouts
     {
@@ -2805,7 +2812,7 @@ $emit-subx-call:end:
     5d/pop-to-ebp
     c3/return
 
-emit-subx-call-operand:  # out : (address buffered-file), operand : (address variable)
+emit-subx-call-operand:  # out : (address buffered-file), operand : (handle variable)
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
@@ -2831,7 +2838,7 @@ $emit-subx-call-operand:end:
     5d/pop-to-ebp
     c3/return
 
-emit-subx-var-as-rm32:  # out : (address buffered-file), operand : (address variable)
+emit-subx-var-as-rm32:  # out : (address buffered-file), operand : (handle variable)
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
@@ -2866,19 +2873,19 @@ $emit-subx-var-as-rm32:end:
     5d/pop-to-ebp
     c3/return
 
-find-matching-function:  # functions : (address function), stmt : (address statement) -> result/eax : (address function)
+find-matching-function:  # functions : (address function), stmt : (handle statement) -> result/eax : (handle function)
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
     # . save registers
     51/push-ecx
-    # var curr/ecx : (address function) = functions
+    # var curr/ecx : (handle function) = functions
     8b/-> *(ebp+8) 1/r32/ecx
     {
       # if (curr == null) break
       81 7/subop/compare %ecx 0/imm32
       74/jump-if-equal break/disp8
-      # if match(curr, stmt) return curr
+      # if match(stmt, curr) return curr
       {
         (mu-stmt-matches-function? *(ebp+0xc) %ecx)  # => eax
         3d/compare-eax-and 0/imm32
@@ -2900,13 +2907,13 @@ $find-matching-function:end:
     5d/pop-to-ebp
     c3/return
 
-find-matching-primitive:  # primitives : (address primitive), stmt : (address statement) -> result/eax : (address primitive)
+find-matching-primitive:  # primitives : (handle primitive), stmt : (handle statement) -> result/eax : (handle primitive)
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
     # . save registers
     51/push-ecx
-    # var curr/ecx : (address primitive) = primitives
+    # var curr/ecx : (handle primitive) = primitives
     8b/-> *(ebp+8) 1/r32/ecx
     {
 $find-matching-primitive:loop:
@@ -2936,7 +2943,7 @@ $find-matching-primitive:end:
     5d/pop-to-ebp
     c3/return
 
-mu-stmt-matches-function?:  # stmt : (address statement), function : (address opcode-info) => result/eax : boolean
+mu-stmt-matches-function?:  # stmt : (handle statement), function : (handle function) => result/eax : boolean
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
@@ -2954,7 +2961,7 @@ $mu-stmt-matches-function?:end:
     5d/pop-to-ebp
     c3/return
 
-mu-stmt-matches-primitive?:  # stmt : (address statement), primitive : (address primitive) => result/eax : boolean
+mu-stmt-matches-primitive?:  # stmt : (handle statement), primitive : (handle primitive) => result/eax : boolean
     # A mu stmt matches a primitive if the name matches, all the inout vars
     # match, and all the output vars match.
     # Vars match if types match and registers match.
@@ -3080,7 +3087,7 @@ $mu-stmt-matches-primitive?:end:
     5d/pop-to-ebp
     c3/return
 
-operand-matches-primitive?:  # var : (address var), primout-var : (address var) => result/eax : boolean
+operand-matches-primitive?:  # var : (handle var), primout-var : (handle var) => result/eax : boolean
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
@@ -3153,30 +3160,30 @@ test-emit-subx-statement-primitive:
     # setup
     (clear-stream _test-output-stream)
     (clear-stream $_test-output-buffered-file->buffer)
-    # var-foo/ecx : var
+    # var var-foo/ecx : (ref var)
     68/push 0/imm32/no-register
     68/push -8/imm32/stack-offset
     68/push 1/imm32/block-depth
     68/push 1/imm32/type-int
     68/push "foo"/imm32
     89/<- %ecx 4/r32/esp
-    # vars/edx : (stack 1)
+    # var vars/edx : (ref stack 1)
     51/push-ecx/var-foo
     68/push 1/imm32/data-length
     68/push 1/imm32/top
     89/<- %edx 4/r32/esp
-    # operand/ebx : (list var)
+    # var operand/ebx : (ref list var)
     68/push 0/imm32/next
     51/push-ecx/var-foo
     89/<- %ebx 4/r32/esp
-    # stmt/esi : statement
+    # var stmt/esi : (ref statement)
     68/push 0/imm32/next
     68/push 0/imm32/outputs
     53/push-ebx/operands
     68/push "increment"/imm32/operation
     68/push 1/imm32
     89/<- %esi 4/r32/esp
-    # primitives/ebx : primitive
+    # var primitives/ebx : (ref primitive)
     68/push 0/imm32/next
     68/push 0/imm32/no-imm32
     68/push 0/imm32/no-r32
@@ -3226,41 +3233,41 @@ test-emit-subx-statement-primitive-register:
     # setup
     (clear-stream _test-output-stream)
     (clear-stream $_test-output-buffered-file->buffer)
-    # var-foo/ecx : var in eax
+    # var var-foo/ecx : (ref var) in eax
     68/push "eax"/imm32/register
     68/push 0/imm32/no-stack-offset
     68/push 1/imm32/block-depth
     68/push 1/imm32/type-int
     68/push "foo"/imm32
     89/<- %ecx 4/r32/esp
-    # vars/edx : (stack 1)
+    # var vars/edx : (ref stack 1)
     51/push-ecx/var-foo
     68/push 1/imm32/data-length
     68/push 1/imm32/top
     89/<- %edx 4/r32/esp
-    # operand/ebx : (list var)
+    # var operand/ebx : (ref list var)
     68/push 0/imm32/next
     51/push-ecx/var-foo
     89/<- %ebx 4/r32/esp
-    # stmt/esi : statement
+    # var stmt/esi : (ref statement)
     68/push 0/imm32/next
     53/push-ebx/outputs
     68/push 0/imm32/inouts
     68/push "increment"/imm32/operation
     68/push 1/imm32
     89/<- %esi 4/r32/esp
-    # formal-var/ebx : var in any register
+    # var formal-var/ebx : (ref var) in any register
     68/push Any-register/imm32
     68/push 0/imm32/no-stack-offset
     68/push 1/imm32/block-depth
     68/push 1/imm32/type-int
     68/push "dummy"/imm32
     89/<- %ebx 4/r32/esp
-    # operand/ebx : (list var)
+    # var operand/ebx : (ref list var)
     68/push 0/imm32/next
     53/push-ebx/formal-var
     89/<- %ebx 4/r32/esp
-    # primitives/ebx : primitive
+    # var primitives/ebx : (ref primitive)
     68/push 0/imm32/next
     68/push 0/imm32/no-imm32
     68/push 0/imm32/no-r32
@@ -3313,41 +3320,41 @@ test-emit-subx-statement-select-primitive:
     # setup
     (clear-stream _test-output-stream)
     (clear-stream $_test-output-buffered-file->buffer)
-    # var-foo/ecx : var in eax
+    # var var-foo/ecx : (ref var) in eax
     68/push "eax"/imm32/register
     68/push 0/imm32/no-stack-offset
     68/push 1/imm32/block-depth
     68/push 1/imm32/type-int
     68/push "foo"/imm32
     89/<- %ecx 4/r32/esp
-    # vars/edx : (stack 1)
+    # var vars/edx : (ref stack 1)
     51/push-ecx/var-foo
     68/push 1/imm32/data-length
     68/push 1/imm32/top
     89/<- %edx 4/r32/esp
-    # real-outputs/edi : (list var)
+    # var real-outputs/edi : (ref list var)
     68/push 0/imm32/next
     51/push-ecx/var-foo
     89/<- %edi 4/r32/esp
-    # stmt/esi : statement
+    # var stmt/esi : (ref statement)
     68/push 0/imm32/next
     57/push-edi/outputs
     68/push 0/imm32/inouts
     68/push "increment"/imm32/operation
     68/push 1/imm32
     89/<- %esi 4/r32/esp
-    # formal-var/ebx : var in any register
+    # var formal-var/ebx : (ref var) in any register
     68/push Any-register/imm32
     68/push 0/imm32/no-stack-offset
     68/push 1/imm32/block-depth
     68/push 1/imm32/type-int
     68/push "dummy"/imm32
     89/<- %ebx 4/r32/esp
-    # formal-outputs/ebx : (list var)
+    # var formal-outputs/ebx : (ref list var) = {formal-var, 0}
     68/push 0/imm32/next
     53/push-ebx/formal-var
     89/<- %ebx 4/r32/esp
-    # primitive1/ebx : primitive
+    # var primitive1/ebx : (ref primitive)
     68/push 0/imm32/next
     68/push 0/imm32/no-imm32
     68/push 0/imm32/no-r32
@@ -3357,7 +3364,7 @@ test-emit-subx-statement-select-primitive:
     68/push 0/imm32/inouts
     68/push "increment"/imm32/name
     89/<- %ebx 4/r32/esp
-    # primitives/ebx : primitive
+    # var primitives/ebx : (ref primitive)
     53/push-ebx/next
     68/push 0/imm32/no-imm32
     68/push 0/imm32/no-r32
@@ -3410,41 +3417,41 @@ test-emit-subx-statement-select-primitive-2:
     # setup
     (clear-stream _test-output-stream)
     (clear-stream $_test-output-buffered-file->buffer)
-    # var-foo/ecx : var in eax
+    # var var-foo/ecx : (ref var) in eax
     68/push "eax"/imm32/register
     68/push 0/imm32/no-stack-offset
     68/push 1/imm32/block-depth
     68/push 1/imm32/type-int
     68/push "foo"/imm32
     89/<- %ecx 4/r32/esp
-    # vars/edx : (stack 1)
+    # var vars/edx : (ref stack 1)
     51/push-ecx/var-foo
     68/push 1/imm32/data-length
     68/push 1/imm32/top
     89/<- %edx 4/r32/esp
-    # inouts/edi : (list var)
+    # var inouts/edi : (ref list var)
     68/push 0/imm32/next
     51/push-ecx/var-foo
     89/<- %edi 4/r32/esp
-    # stmt/esi : statement
+    # var stmt/esi : (ref statement)
     68/push 0/imm32/next
     68/push 0/imm32/outputs
     57/push-edi/inouts
     68/push "increment"/imm32/operation
     68/push 1/imm32
     89/<- %esi 4/r32/esp
-    # formal-var/ebx : var in any register
+    # var formal-var/ebx : (ref var) in any register
     68/push Any-register/imm32
     68/push 0/imm32/no-stack-offset
     68/push 1/imm32/block-depth
     68/push 1/imm32/type-int
     68/push "dummy"/imm32
     89/<- %ebx 4/r32/esp
-    # operand/ebx : (list var)
+    # var operand/ebx : (ref list var)
     68/push 0/imm32/next
     53/push-ebx/formal-var
     89/<- %ebx 4/r32/esp
-    # primitive1/ebx : primitive
+    # var primitive1/ebx : primitive
     68/push 0/imm32/next
     68/push 0/imm32/no-imm32
     68/push 0/imm32/no-r32
@@ -3454,7 +3461,7 @@ test-emit-subx-statement-select-primitive-2:
     68/push 0/imm32/inouts
     68/push "increment"/imm32/name
     89/<- %ebx 4/r32/esp
-    # primitives/ebx : primitive
+    # var primitives/ebx : (ref primitive)
     53/push-ebx/next
     68/push 0/imm32/no-imm32
     68/push 0/imm32/no-r32
@@ -3501,23 +3508,23 @@ test-increment-register:
     # setup
     (clear-stream _test-output-stream)
     (clear-stream $_test-output-buffered-file->buffer)
-    # var-foo/ecx : var in eax
+    # var var-foo/ecx : (ref var) in eax
     68/push "eax"/imm32/register
     68/push 0/imm32/no-stack-offset
     68/push 1/imm32/block-depth
     68/push 1/imm32/type-int
     68/push "foo"/imm32
     89/<- %ecx 4/r32/esp
-    # vars/edx : (stack 1)
+    # var vars/edx : (ref stack 1)
     51/push-ecx/var-foo
     68/push 1/imm32/data-length
     68/push 1/imm32/top
     89/<- %edx 4/r32/esp
-    # real-outputs/edi : (list var)
+    # var real-outputs/edi : (ref list var)
     68/push 0/imm32/next
     51/push-ecx/var-foo
     89/<- %edi 4/r32/esp
-    # stmt/esi : statement
+    # var stmt/esi : (ref statement)
     68/push 0/imm32/next
     57/push-edi/outputs
     68/push 0/imm32/inouts
@@ -3561,23 +3568,23 @@ test-increment-var:
     # setup
     (clear-stream _test-output-stream)
     (clear-stream $_test-output-buffered-file->buffer)
-    # var-foo/ecx : var in eax
+    # var var-foo/ecx : (ref var) in eax
     68/push "eax"/imm32/register
     68/push 0/imm32/no-stack-offset
     68/push 1/imm32/block-depth
     68/push 1/imm32/type-int
     68/push "foo"/imm32
     89/<- %ecx 4/r32/esp
-    # vars/edx : (stack 1)
+    # var vars/edx : (ref stack 1)
     51/push-ecx/var-foo
     68/push 1/imm32/data-length
     68/push 1/imm32/top
     89/<- %edx 4/r32/esp
-    # inouts/edi : (list var)
+    # var inouts/edi : (ref list var)
     68/push 0/imm32/next
     51/push-ecx/var-foo
     89/<- %edi 4/r32/esp
-    # stmt/esi : statement
+    # var stmt/esi : (ref statement)
     68/push 0/imm32/next
     68/push 0/imm32/outputs
     57/push-edi/inouts
@@ -3611,29 +3618,29 @@ test-add-reg-to-reg:
     # setup
     (clear-stream _test-output-stream)
     (clear-stream $_test-output-buffered-file->buffer)
-    # var-var1/ecx : var in eax
+    # var var-var1/ecx : (ref var) in eax
     68/push "eax"/imm32/register
     68/push 0/imm32/no-stack-offset
     68/push 1/imm32/block-depth
     68/push 1/imm32/type-int
     68/push "var1"/imm32
     89/<- %ecx 4/r32/esp
-    # var-var2/edx : var in ecx
+    # var var-var2/edx : (ref var) in ecx
     68/push "ecx"/imm32/register
     68/push 0/imm32/no-stack-offset
     68/push 1/imm32/block-depth
     68/push 1/imm32/type-int
     68/push "var2"/imm32
     89/<- %edx 4/r32/esp
-    # inouts/esi : (list var2)
+    # var inouts/esi : (ref list var2)
     68/push 0/imm32/next
     52/push-edx/var-var2
     89/<- %esi 4/r32/esp
-    # outputs/edi : (list var1)
+    # var outputs/edi : (ref list var1)
     68/push 0/imm32/next
     51/push-ecx/var-var1
     89/<- %edi 4/r32/esp
-    # stmt/esi : statement
+    # var stmt/esi : (ref statement)
     68/push 0/imm32/next
     57/push-edi/outputs
     56/push-esi/inouts
@@ -3667,29 +3674,29 @@ test-add-reg-to-mem:
     # setup
     (clear-stream _test-output-stream)
     (clear-stream $_test-output-buffered-file->buffer)
-    # var-var1/ecx : var
+    # var var-var1/ecx : (ref var)
     68/push 0/imm32/no-register
     68/push 8/imm32/stack-offset
     68/push 1/imm32/block-depth
     68/push 1/imm32/type-int
     68/push "var1"/imm32
     89/<- %ecx 4/r32/esp
-    # var-var2/edx : var in ecx
+    # var var-var2/edx : (ref var) in ecx
     68/push "ecx"/imm32/register
     68/push 0/imm32/no-stack-offset
     68/push 1/imm32/block-depth
     68/push 1/imm32/type-int
     68/push "var2"/imm32
     89/<- %edx 4/r32/esp
-    # inouts/esi : (list var2)
+    # var inouts/esi : (ref list var2)
     68/push 0/imm32/next
     52/push-edx/var-var2
     89/<- %esi 4/r32/esp
-    # inouts = (list var1 var2)
+    # var inouts = (ref list var1 var2)
     56/push-esi/next
     51/push-ecx/var-var1
     89/<- %esi 4/r32/esp
-    # stmt/esi : statement
+    # var stmt/esi : (ref statement)
     68/push 0/imm32/next
     68/push 0/imm32/outputs
     56/push-esi/inouts
@@ -3723,29 +3730,29 @@ test-add-mem-to-reg:
     # setup
     (clear-stream _test-output-stream)
     (clear-stream $_test-output-buffered-file->buffer)
-    # var-var1/ecx : var in eax
+    # var var-var1/ecx : (ref var) in eax
     68/push "eax"/imm32/register
     68/push 0/imm32/no-stack-offset
     68/push 1/imm32/block-depth
     68/push 1/imm32/type-int
     68/push "var1"/imm32
     89/<- %ecx 4/r32/esp
-    # var-var2/edx : var
+    # var var-var2/edx : (ref var)
     68/push 0/imm32/no-register
     68/push 8/imm32/stack-offset
     68/push 1/imm32/block-depth
     68/push 1/imm32/type-int
     68/push "var2"/imm32
     89/<- %edx 4/r32/esp
-    # inouts/esi : (list var2)
+    # var inouts/esi : (ref list var2)
     68/push 0/imm32/next
     52/push-edx/var-var2
     89/<- %esi 4/r32/esp
-    # outputs/edi : (list var1)
+    # var outputs/edi : (ref list var1)
     68/push 0/imm32/next
     51/push-ecx/var-var1
     89/<- %edi 4/r32/esp
-    # stmt/esi : statement
+    # var stmt/esi : (ref statement)
     68/push 0/imm32/next
     57/push-edi/outputs
     56/push-esi/inouts
@@ -3779,29 +3786,29 @@ test-add-literal-to-reg:
     # setup
     (clear-stream _test-output-stream)
     (clear-stream $_test-output-buffered-file->buffer)
-    # var-var1/ecx : var in eax
+    # var var-var1/ecx : (ref var) in eax
     68/push "eax"/imm32/register
     68/push 0/imm32/no-stack-offset
     68/push 1/imm32/block-depth
     68/push 1/imm32/type-int
     68/push "var1"/imm32
     89/<- %ecx 4/r32/esp
-    # var-var2/edx : var literal
+    # var var-var2/edx : (ref var) literal
     68/push 0/imm32/no-register
     68/push 0/imm32/no-stack-offset
     68/push 1/imm32/block-depth
     68/push 0/imm32/type-literal
     68/push "0x34"/imm32
     89/<- %edx 4/r32/esp
-    # inouts/esi : (list var2)
+    # var inouts/esi : (ref list var2)
     68/push 0/imm32/next
     52/push-edx/var-var2
     89/<- %esi 4/r32/esp
-    # outputs/edi : (list var1)
+    # var outputs/edi : (ref list var1)
     68/push 0/imm32/next
     51/push-ecx/var-var1
     89/<- %edi 4/r32/esp
-    # stmt/esi : statement
+    # var stmt/esi : (ref statement)
     68/push 0/imm32/next
     57/push-edi/outputs
     56/push-esi/inouts
@@ -3835,29 +3842,29 @@ test-add-literal-to-mem:
     # setup
     (clear-stream _test-output-stream)
     (clear-stream $_test-output-buffered-file->buffer)
-    # var-var1/ecx : var
+    # var var-var1/ecx : (ref var)
     68/push 0/imm32/no-register
     68/push 8/imm32/stack-offset
     68/push 1/imm32/block-depth
     68/push 1/imm32/type-int
     68/push "var1"/imm32
     89/<- %ecx 4/r32/esp
-    # var-var2/edx : var literal
+    # var var-var2/edx : (ref var) literal
     68/push 0/imm32/no-register
     68/push 0/imm32/no-stack-offset
     68/push 1/imm32/block-depth
     68/push 0/imm32/type-literal
     68/push "0x34"/imm32
     89/<- %edx 4/r32/esp
-    # inouts/esi : (list var2)
+    # var inouts/esi : (ref list var2)
     68/push 0/imm32/next
     52/push-edx/var-var2
     89/<- %esi 4/r32/esp
-    # inouts = (list var1 inouts)
+    # var inouts = (ref list var1 inouts)
     56/push-esi/next
     51/push-ecx/var-var1
     89/<- %esi 4/r32/esp
-    # stmt/esi : statement
+    # var stmt/esi : (ref statement)
     68/push 0/imm32/next
     68/push 0/imm32/outputs
     56/push-esi/inouts
@@ -3906,30 +3913,30 @@ test-emit-subx-statement-function-call:
     # setup
     (clear-stream _test-output-stream)
     (clear-stream $_test-output-buffered-file->buffer)
-    # var-foo/ecx : var
+    # var var-foo/ecx : (ref var)
     68/push 0/imm32/no-register
     68/push -8/imm32/stack-offset
     68/push 0/imm32/block-depth
     68/push 1/imm32/type-int
     68/push "foo"/imm32
     89/<- %ecx 4/r32/esp
-    # vars/edx = (stack 1)
+    # var vars/edx = (ref stack 1)
     51/push-ecx/var-foo
     68/push 1/imm32/data-length
     68/push 1/imm32/top
     89/<- %edx 4/r32/esp
-    # operands/esi : (list var)
+    # var operands/esi : (ref list var)
     68/push 0/imm32/next
     51/push-ecx/var-foo
     89/<- %esi 4/r32/esp
-    # stmt/esi : statement
+    # var stmt/esi : (ref statement)
     68/push 0/imm32/next
     68/push 0/imm32/outputs
     56/push-esi/inouts
     68/push "f"/imm32/operation
     68/push 1/imm32
     89/<- %esi 4/r32/esp
-    # functions/ebx : function
+    # var functions/ebx : (ref function)
     68/push 0/imm32/next
     68/push 0/imm32/body
     68/push 0/imm32/outputs
@@ -3965,30 +3972,30 @@ test-emit-subx-statement-function-call-with-literal-arg:
     # setup
     (clear-stream _test-output-stream)
     (clear-stream $_test-output-buffered-file->buffer)
-    # var-foo/ecx : literal
+    # var var-foo/ecx : (ref var) literal
     68/push 0/imm32/no-register
     68/push 0/imm32/no-stack-offset
     68/push 0/imm32/block-depth
     68/push 0/imm32/type-literal
     68/push "34"/imm32
     89/<- %ecx 4/r32/esp
-    # vars/edx = (stack 1)
+    # var vars/edx = (ref stack 1)
     51/push-ecx/var-foo
     68/push 1/imm32/data-length
     68/push 1/imm32/top
     89/<- %edx 4/r32/esp
-    # operands/esi : (list var)
+    # var operands/esi : (ref list var)
     68/push 0/imm32/next
     51/push-ecx/var-foo
     89/<- %esi 4/r32/esp
-    # stmt/esi : statement
+    # var stmt/esi : (ref statement)
     68/push 0/imm32/next
     68/push 0/imm32/outputs
     56/push-esi/inouts
     68/push "f"/imm32/operation
     68/push 1/imm32
     89/<- %esi 4/r32/esp
-    # functions/ebx : function
+    # var functions/ebx : (ref function)
     68/push 0/imm32/next
     68/push 0/imm32/body
     68/push 0/imm32/outputs
diff --git a/apps/mulisp.subx b/apps/mulisp.subx
index 71ad170c..1cfdeb03 100644
--- a/apps/mulisp.subx
+++ b/apps/mulisp.subx
@@ -81,12 +81,12 @@ repl:  # in : (address buffered-file), out : (address buffered-file)
     # . save registers
     50/push-eax
     {
-      (lisp-read Stdin)  # => eax : (address cell)
+      (lisp-read Stdin)  # => eax : (handle cell)
       # if (eax == 0) break
       3d/compare-eax-and 0/imm32
       74/jump-if-equal break/disp8
       #
-      (lisp-eval %eax)  # => eax : (address cell)
+      (lisp-eval %eax)  # => eax : (handle cell)
       (lisp-print Stdout %eax)
       eb/jump loop/disp8
     }
@@ -104,13 +104,13 @@ $repl:end:
 # arrays start with '['
 # symbols start with anything else but quote, backquote, unquote or splice
 # only one s-expression per line
-lisp-read:  # in : (address buffered-file) -> eax : (address cell)
+lisp-read:  # in : (address buffered-file) -> eax : (handle cell)
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
     # . save registers
     51/push-ecx
-    # var s/ecx : (stream byte 512)
+    # var s/ecx : (ref stream byte 512)
     81 5/subop/subtract %esp 0x200/imm32
     68/push 0x200/imm32/size
     68/push 0/imm32/read
@@ -142,14 +142,14 @@ $lisp-read:end:
     5d/pop-to-ebp
     c3/return
 
-# lisp-read:  in : (address buffered-file) -> (address cell)
+# lisp-read:  in : (address buffered-file) -> (handle cell)
 #   token tmp = next-mulisp-token(in)
 #   if is-int(tmp) return cell(tmp)
 #   if is-string(tmp) return cell(tmp)
 #   if is-pair(tmp) ...
 #   if is-array(tmp) ...
 
-next-mulisp-token:  # in : (address buffered-file), line : (address stream), result : (address slice)
+next-mulisp-token:  # in : (address buffered-file), line : (address stream byte), result : (address slice)
     # pseudocode:
     #   if (line->read >= line->write)
     #     read-line-buffered(in, line)
@@ -194,11 +194,11 @@ $next-mulisp-token:end:
     5d/pop-to-ebp
     c3/return
 
-new-int-cell:  # in : (address slice) -> eax : (address cell)
+new-int-cell:  # in : (address slice) -> eax : (handle cell)
 
-new-string-cell:  # in : (address slice) -> eax : (address cell)
+new-string-cell:  # in : (address slice) -> eax : (handle cell)
 
-lisp-eval:  # in : (address cell) -> eax : (address cell)
+lisp-eval:  # in : (address cell) -> eax : (handle cell)
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
diff --git a/apps/pack b/apps/pack
index 74d4ed32..005c42f8 100755
--- a/apps/pack
+++ b/apps/pack
Binary files differdiff --git a/apps/pack.subx b/apps/pack.subx
index 6079a896..df683f84 100644
--- a/apps/pack.subx
+++ b/apps/pack.subx
@@ -45,8 +45,8 @@ Entry:  # run tests if necessary, convert stdin if not
     e8/call  kernel-string-equal?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-    # . if (eax == 0) goto interactive
-    3d/compare-eax-and  0/imm32
+    # . if (eax == false) goto interactive
+    3d/compare-eax-and  0/imm32/false
     74/jump-if-equal  $subx-pack-main:interactive/disp8
     # run-tests()
     e8/call  run-tests/disp32
@@ -55,7 +55,7 @@ Entry:  # run tests if necessary, convert stdin if not
     eb/jump  $subx-pack-main:end/disp8
 $subx-pack-main:interactive:
     # - otherwise convert stdin
-    # var ed/eax : exit-descriptor
+    # var ed/eax : (ref exit-descriptor)
     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           4/r32/esp   .               .                 # copy esp to eax
     # configure ed to really exit()
@@ -99,7 +99,7 @@ $subx-pack-main:end:
 
 subx-pack:  # in : (address buffered-file), out : (address buffered-file)
     # pseudocode:
-    #   var line : (stream byte 512)
+    #   var line : (ref stream byte 512)
     #   var in-code? = false
     #   while true
     #     clear-stream(line)
@@ -128,17 +128,17 @@ subx-pack:  # in : (address buffered-file), out : (address buffered-file)
     51/push-ecx
     52/push-edx
     53/push-ebx
-    # var line/ecx : (address stream byte) = stream(512)
+    # var line/ecx : (ref stream byte 512)
     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x200/imm32       # subtract from esp
     68/push  0x200/imm32/length
     68/push  0/imm32/read
     68/push  0/imm32/write
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
-    # var word-slice/edx = {0, 0}
+    # var word-slice/edx : (ref slice)
     68/push  0/imm32/end
     68/push  0/imm32/start
     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
-    # var in-code?/ebx = false
+    # var in-code?/ebx : boolean = false
     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
 $subx-pack:loop:
     # clear-stream(line)
@@ -203,8 +203,8 @@ $subx-pack:check1:
     e8/call  slice-empty?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # . if (eax != 0) write-stream-data(out, line)
-    3d/compare-eax-and  0/imm32
+    # . if (eax != false) write-stream-data(out, line)
+    3d/compare-eax-and  0/imm32/false
     0f 85/jump-if-not-equal  $subx-pack:pass-through/disp32
 $subx-pack:check2:
 #?     # dump word-slice {{{
@@ -256,8 +256,8 @@ $subx-pack:check2:
     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) goto check3
-    3d/compare-eax-and  0/imm32
+    # . if (eax == false) goto check3
+    3d/compare-eax-and  0/imm32/false
     0f 84/jump-if-equal  $subx-pack:check3/disp32
     # word-slice = next-word(line)
     # . . push args
@@ -328,8 +328,8 @@ $subx-pack:check3:
     e8/call  rewind-stream/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # if (in-code? != 0) convert-instruction(line, out)
-    81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0/imm32           # compare ebx
+    # if (in-code? != false) convert-instruction(line, out)
+    81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0/imm32/false     # compare ebx
     74/jump-if-equal  $subx-pack:data/disp8
 $subx-pack:code:
     # . convert-instruction(line, out)
@@ -911,7 +911,7 @@ test-subx-pack-code-and-data-segments:
 
 convert-data:  # line : (address stream byte), out : (address buffered-file)
     # pseudocode:
-    #   var word-slice = {0, 0}
+    #   var word-slice : (ref slice)
     #   while true
     #     word-slice = next-word(line)
     #     if slice-empty?(word-slice)                 # end of file (maybe including trailing whitespace)
@@ -936,7 +936,7 @@ convert-data:  # line : (address stream byte), out : (address buffered-file)
     50/push-eax
     51/push-ecx
     52/push-edx
-    # var word-slice/ecx = {0, 0}
+    # var word-slice/ecx : (ref slice)
     68/push  0/imm32/end
     68/push  0/imm32/start
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -1024,17 +1024,17 @@ $convert-data:check0:
     e8/call  slice-empty?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # . if (eax != 0) break
-    3d/compare-eax-and  0/imm32
+    # . if (eax != false) break
+    3d/compare-eax-and  0/imm32/false
     0f 85/jump-if-not-equal  $convert-data:break/disp32
 $convert-data:check-for-comment:
     # if (slice-starts-with?(word-slice, "#"))
-    # . start/edx = word-slice->start
+    # . var start/edx : (address byte) = word-slice->start
     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # copy *ecx to edx
-    # . c/eax = *start
+    # . var c/eax : byte = *start
     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
     8a/copy-byte                    0/mod/indirect  2/rm32/edx    .           .             .           0/r32/AL    .               .                 # copy byte at *edx to AL
-    # . if (eax != '#') goto next check
+    # . if (c != '#') goto next check
     3d/compare-eax-and  0x23/imm32/hash
     75/jump-if-not-equal  $convert-data:check-for-label/disp8
 $convert-data:comment:
@@ -1050,12 +1050,12 @@ $convert-data:comment:
     0f 85/jump-if-not-equal  $convert-data:end/disp32
 $convert-data:check-for-label:
     # if (slice-ends-with?(word-slice, ":"))
-    # . end/edx = word-slice->end
+    # . var end/edx : (address byte) = word-slice->end
     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           2/r32/edx   4/disp8         .                 # copy *(ecx+4) to edx
-    # . c/eax = *(end-1)
+    # . var c/eax : byte = *(end-1)
     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
     8a/copy-byte                    1/mod/*+disp8   2/rm32/edx    .           .             .           0/r32/AL    -1/disp8        .                 # copy byte at *ecx to AL
-    # . if (eax != ':') goto next check
+    # . if (c != ':') goto next check
     3d/compare-eax-and  0x3a/imm32/colon
     75/jump-if-not-equal  $convert-data:check-for-imm32/disp8
 $convert-data:label:
@@ -1079,8 +1079,8 @@ $convert-data:check-for-imm32:
     e8/call  has-metadata?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-    # . if (eax == 0) process as a single byte
-    3d/compare-eax-and  0/imm32
+    # . if (eax == false) process as a single byte
+    3d/compare-eax-and  0/imm32/false
     74/jump-if-equal  $convert-data:single-byte/disp8
 $convert-data:imm32:
     # emit(out, word-slice, 4)
@@ -1837,7 +1837,7 @@ convert-instruction:  # line : (address stream byte), out : (address buffered-fi
     50/push-eax
     51/push-ecx
     52/push-edx
-    # var word-slice/ecx = {0, 0}
+    # var word-slice/ecx : (ref slice)
     68/push  0/imm32/end
     68/push  0/imm32/start
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -1858,27 +1858,27 @@ $convert-instruction:check0:
     e8/call  slice-empty?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # . if (eax != 0) pass through
-    3d/compare-eax-and  0/imm32
+    # . if (eax != false) pass through
+    3d/compare-eax-and  0/imm32/false
     75/jump-if-not-equal  $convert-instruction:pass-through/disp8
 $convert-instruction:check1:
     # if (slice-starts-with?(word-slice, "#")) write-stream-data(out, line)
-    # . start/edx = word-slice->start
+    # . var start/edx : (address byte) = word-slice->start
     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # copy *ecx to edx
-    # . c/eax = *start
+    # . var c/eax : byte = *start
     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
     8a/copy-byte                    0/mod/indirect  2/rm32/edx    .           .             .           0/r32/AL    .               .                 # copy byte at *edx to AL
-    # . if (eax == '#') pass through
+    # . if (c == '#') pass through
     3d/compare-eax-and  0x23/imm32/hash
     74/jump-if-equal  $convert-instruction:pass-through/disp8
 $convert-instruction:check2:
     # if (slice-ends-with?(word-slice, ":")) write-stream-data(out, line)
-    # . end/edx = word-slice->end
+    # . var end/edx : (address byte) = word-slice->end
     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           2/r32/edx   4/disp8         .                 # copy *(ecx+4) to edx
-    # . c/eax = *(end-1)
+    # . var c/eax : byte = *(end-1)
     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
     8a/copy-byte                    1/mod/*+disp8   2/rm32/edx    .           .             .           0/r32/AL    -1/disp8        .                 # copy byte at *ecx to AL
-    # . if (eax == ':') pass through
+    # . if (c == ':') pass through
     3d/compare-eax-and  0x3a/imm32/colon
     75/jump-if-not-equal  $convert-instruction:really-convert/disp8
 $convert-instruction:pass-through:
@@ -1994,11 +1994,11 @@ emit-opcodes:  # line : (address stream byte), out : (address buffered-file)
     51/push-ecx
     52/push-edx
     53/push-ebx
-    # var op1/ecx = {0, 0}
+    # var op1/ecx : (ref slice)
     68/push  0/imm32/end
     68/push  0/imm32/start
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
-    # var op2/edx = {0, 0}
+    # var op2/edx : (ref slice)
     68/push  0/imm32/end
     68/push  0/imm32/start
     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
@@ -2026,16 +2026,16 @@ $emit-opcodes:op1:
     e8/call  slice-empty?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # . if (eax != 0) return
-    3d/compare-eax-and  0/imm32
+    # . if (eax != false) return
+    3d/compare-eax-and  0/imm32/false
     0f 85/jump-if-not-equal  $emit-opcodes:end/disp32
     # if (slice-starts-with?(op1, "#")) return
-    # . start/ebx = op1->start
+    # . var start/ebx : (address byte) = op1->start
     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           3/r32/ebx   .               .                 # copy *ecx to ebx
-    # . c/eax = *start
+    # . var c/eax : byte = *start
     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
     8a/copy-byte                    0/mod/indirect  3/rm32/ebx    .           .             .           0/r32/AL    .               .                 # copy byte at *ebx to AL
-    # . if (eax == '#') return
+    # . if (c == '#') return
     3d/compare-eax-and  0x23/imm32/hash
     0f 84/jump-if-equal  $emit-opcodes:end/disp32
     # op1 = next-token-from-slice(op1->start, op1->end, '/')
@@ -2073,8 +2073,8 @@ $emit-opcodes:op1:
     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) goto op2
-    3d/compare-eax-and  0/imm32
+    # . if (eax != false) goto op2
+    3d/compare-eax-and  0/imm32/false
     75/jump-if-not-equal  $emit-opcodes:op2/disp8
     # if (slice-equal?(op1, "f2")) goto op2
     # . eax = slice-equal?(op1, "f2")
@@ -2085,8 +2085,8 @@ $emit-opcodes:op1:
     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) goto op2
-    3d/compare-eax-and  0/imm32
+    # . if (eax != false) goto op2
+    3d/compare-eax-and  0/imm32/false
     75/jump-if-not-equal  $emit-opcodes:op2/disp8
     # if (slice-equal?(op1, "f3")) goto op2
     # . eax = slice-equal?(op1, "f3")
@@ -2097,8 +2097,8 @@ $emit-opcodes:op1:
     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) goto op2
-    3d/compare-eax-and  0/imm32
+    # . if (eax != false) goto op2
+    3d/compare-eax-and  0/imm32/false
     75/jump-if-not-equal  $emit-opcodes:op2/disp8
     # otherwise return
     e9/jump  $emit-opcodes:end/disp32
@@ -2119,16 +2119,16 @@ $emit-opcodes:op2:
     e8/call  slice-empty?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # . if (eax != 0) return
-    3d/compare-eax-and  0/imm32
+    # . if (eax != false) return
+    3d/compare-eax-and  0/imm32/false
     0f 85/jump-if-not-equal  $emit-opcodes:end/disp32
     # if (slice-starts-with?(op2, "#")) return
-    # . start/ebx = op2->start
+    # . var start/ebx : (address byte) = op2->start
     8b/copy                         0/mod/indirect  2/rm32/edx    .           .             .           3/r32/ebx   .               .                 # copy *edx to ebx
-    # . c/eax = *start
+    # . var c/eax : byte = *start
     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
     8a/copy-byte                    0/mod/indirect  3/rm32/ebx    .           .             .           0/r32/AL    .               .                 # copy byte at *ebx to AL
-    # . if (eax == '#') return
+    # . if (c == '#') return
     3d/compare-eax-and  0x23/imm32/hash
     0f 84/jump-if-equal  $emit-opcodes:end/disp32
     # op2 = next-token-from-slice(op2->start, op2->end, '/')
@@ -2166,8 +2166,8 @@ $emit-opcodes:op2:
     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
-    3d/compare-eax-and  0/imm32
+    # . if (eax != false) return
+    3d/compare-eax-and  0/imm32/false
     0f 85/jump-if-not-equal  $emit-opcodes:end/disp32
     # if (!slice-equal?(op2, "0f")) return
     # . eax = slice-equal?(op2, "0f")
@@ -2178,8 +2178,8 @@ $emit-opcodes:op2:
     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
-    3d/compare-eax-and  0/imm32
+    # . if (eax == false) return
+    3d/compare-eax-and  0/imm32/false
     0f 84/jump-if-equal  $emit-opcodes:end/disp32
 $emit-opcodes:op3:
     # next-word(line, op3)  # reuse op2/edx
@@ -2198,16 +2198,16 @@ $emit-opcodes:op3:
     e8/call  slice-empty?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # . if (eax != 0) return
-    3d/compare-eax-and  0/imm32
+    # . if (eax != false) return
+    3d/compare-eax-and  0/imm32/false
     0f 85/jump-if-not-equal  $emit-opcodes:end/disp32
     # if (slice-starts-with?(op3, "#")) return
-    # . start/ebx = op2->start
+    # . var start/ebx : (address byte) = op2->start
     8b/copy                         0/mod/indirect  2/rm32/edx    .           .             .           3/r32/ebx   .               .                 # copy *edx to ebx
-    # . c/eax = *start
+    # . var c/eax : byte = *start
     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
     8a/copy-byte                    0/mod/indirect  3/rm32/ebx    .           .             .           0/r32/AL    .               .                 # copy byte at *ebx to AL
-    # . if (eax == '#') return
+    # . if (c == '#') return
     3d/compare-eax-and  0x23/imm32/hash
     0f 84/jump-if-equal  $emit-opcodes:end/disp32
     # op3 = next-token-from-slice(op3->start, op3->end, '/')
@@ -2253,7 +2253,7 @@ emit-modrm:  # line : (address stream byte), out : (address buffered-file)
     # pseudocode:
     #   rewind-stream(line)
     #   var has-modrm? = false, mod = 0, rm32 = 0, r32 = 0
-    #   var word-slice = {0, 0}
+    #   var word-slice : (ref slice)
     #   while true
     #     word-slice = next-word(line)
     #     if (slice-empty?(word-slice)) break
@@ -2285,17 +2285,17 @@ emit-modrm:  # line : (address stream byte), out : (address buffered-file)
     53/push-ebx
     56/push-esi
     57/push-edi
-    # var word-slice/ecx = {0, 0}
+    # var word-slice/ecx : (ref slice)
     68/push  0/imm32/end
     68/push  0/imm32/start
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
-    # var has-modrm?/edx = false
+    # var has-modrm?/edx : boolean = false
     31/xor                          3/mod/direct    2/rm32/edx    .           .             .           2/r32/edx   .               .                 # clear edx
-    # var mod/ebx = 0
+    # var mod/ebx : byte = 0
     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
-    # var rm32/esi = 0
+    # var rm32/esi : byte = 0
     31/xor                          3/mod/direct    6/rm32/esi    .           .             .           6/r32/esi   .               .                 # clear esi
-    # var r32/edi = 0
+    # var r32/edi : byte = 0
     31/xor                          3/mod/direct    7/rm32/edi    .           .             .           7/r32/edi   .               .                 # clear edi
     # rewind-stream(line)
     # . . push args
@@ -2395,21 +2395,21 @@ $emit-modrm:check0:
     e8/call  slice-empty?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # . if (eax != 0) pass through
-    3d/compare-eax-and  0/imm32
+    # . if (eax != false) pass through
+    3d/compare-eax-and  0/imm32/false
     0f 85/jump-if-not-equal  $emit-modrm:break/disp32
 $emit-modrm:check1:
     # if (slice-starts-with?(word-slice, "#")) break
     # . spill edx
     52/push-edx
-    # . start/edx = word-slice->start
+    # . var start/edx : (address byte) = word-slice->start
     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # copy *ecx to edx
-    # . c/eax = *start
+    # . var c/eax : byte = *start
     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
     8a/copy-byte                    0/mod/indirect  2/rm32/edx    .           .             .           0/r32/AL    .               .                 # copy byte at *edx to AL
     # . restore edx
     5a/pop-to-edx
-    # . if (eax == '#') pass through
+    # . if (c == '#') pass through
     3d/compare-eax-and  0x23/imm32/hash
     0f 84/jump-if-equal  $emit-modrm:break/disp32
 $emit-modrm:check-for-mod:
@@ -2422,8 +2422,8 @@ $emit-modrm:check-for-mod:
     e8/call  has-metadata?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-    # . if (eax == 0) goto next check
-    3d/compare-eax-and  0/imm32
+    # . if (eax == false) goto next check
+    3d/compare-eax-and  0/imm32/false
     74/jump-if-equal  $emit-modrm:check-for-rm32/disp8
 $emit-modrm:mod:
     # mod = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
@@ -2450,8 +2450,8 @@ $emit-modrm:check-for-rm32:
     e8/call  has-metadata?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-    # . if (eax == 0) goto next check
-    3d/compare-eax-and  0/imm32
+    # . if (eax == false) goto next check
+    3d/compare-eax-and  0/imm32/false
     74/jump-if-equal  $emit-modrm:check-for-r32/disp8
 $emit-modrm:rm32:
     # rm32 = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
@@ -2478,8 +2478,8 @@ $emit-modrm:check-for-r32:
     e8/call  has-metadata?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-    # . if (eax == 0) goto next check
-    3d/compare-eax-and  0/imm32
+    # . if (eax == false) goto next check
+    3d/compare-eax-and  0/imm32/false
     74/jump-if-equal  $emit-modrm:check-for-subop/disp8
 $emit-modrm:r32:
     # r32 = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
@@ -2506,8 +2506,8 @@ $emit-modrm:check-for-subop:
     e8/call  has-metadata?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-    # . if (eax == 0) loop
-    3d/compare-eax-and  0/imm32
+    # . if (eax == false) loop
+    3d/compare-eax-and  0/imm32/false
     0f 84/jump-if-equal  $emit-modrm:loop/disp32
 $emit-modrm:subop:
     # r32 = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
@@ -2526,10 +2526,10 @@ $emit-modrm:subop:
     e9/jump  $emit-modrm:loop/disp32
 $emit-modrm:break:
     # if (!has-modrm?) return
-    81          7/subop/compare     3/mod/direct    2/rm32/edx    .           .             .           .           .               0/imm32           # compare edx
+    81          7/subop/compare     3/mod/direct    2/rm32/edx    .           .             .           .           .               0/imm32/false     # compare edx
     74/jump-if-equal  $emit-modrm:end/disp8
 $emit-modrm:calculate:
-    # modrm/ebx = mod & 0b11
+    # var modrm/ebx : byte = mod & 0b11
     81          4/subop/and         3/mod/direct    3/rm32/ebx    .           .             .           .           .               3/imm32/0b11      # bitwise and of ebx
     # modrm <<= 3
     c1/shift    4/subop/left        3/mod/direct    3/rm32/ebx    .           .             .           .           .               3/imm8            # shift ebx left by 3 bits
@@ -2569,7 +2569,7 @@ $emit-modrm:end:
 emit-sib:  # line : (address stream byte), out : (address buffered-file)
     # pseudocode:
     #   var has-sib? = false, base = 0, index = 0, scale = 0
-    #   var word-slice = {0, 0}
+    #   var word-slice : (ref slice)
     #   while true
     #     word-slice = next-word(line)
     #     if (slice-empty?(word-slice)) break
@@ -2601,17 +2601,17 @@ emit-sib:  # line : (address stream byte), out : (address buffered-file)
     53/push-ebx
     56/push-esi
     57/push-edi
-    # var word-slice/ecx = {0, 0}
+    # var word-slice/ecx : (ref slice)
     68/push  0/imm32/end
     68/push  0/imm32/start
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
-    # var has-sib?/edx = false
+    # var has-sib?/edx : boolean = false
     31/xor                          3/mod/direct    2/rm32/edx    .           .             .           2/r32/edx   .               .                 # clear edx
-    # var scale/ebx = 0
+    # var scale/ebx : byte = 0
     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
-    # var base/esi = 0
+    # var base/esi : byte = 0
     31/xor                          3/mod/direct    6/rm32/esi    .           .             .           6/r32/esi   .               .                 # clear esi
-    # var index/edi = 0
+    # var index/edi : byte = 0
     31/xor                          3/mod/direct    7/rm32/edi    .           .             .           7/r32/edi   .               .                 # clear edi
     # rewind-stream(line)
     # . . push args
@@ -2704,21 +2704,21 @@ $emit-sib:check0:
     e8/call  slice-empty?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # . if (eax != 0) pass through
-    3d/compare-eax-and  0/imm32
+    # . if (eax != false) pass through
+    3d/compare-eax-and  0/imm32/false
     0f 85/jump-if-not-equal  $emit-sib:break/disp32
 $emit-sib:check1:
     # if (slice-starts-with?(word-slice, "#")) break
     # . spill edx
     52/push-edx
-    # . start/edx = word-slice->start
+    # . var start/edx : (address byte) = word-slice->start
     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # copy *ecx to edx
-    # . c/eax = *start
+    # . var c/eax : byte = *start
     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
     8a/copy-byte                    0/mod/indirect  2/rm32/edx    .           .             .           0/r32/AL    .               .                 # copy byte at *edx to AL
     # . restore edx
     5a/pop-to-edx
-    # . if (eax == '#') pass through
+    # . if (c == '#') pass through
     3d/compare-eax-and  0x23/imm32/hash
     0f 84/jump-if-equal  $emit-sib:break/disp32
 $emit-sib:check-for-scale:
@@ -2731,8 +2731,8 @@ $emit-sib:check-for-scale:
     e8/call  has-metadata?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-    # . if (eax == 0) goto next check
-    3d/compare-eax-and  0/imm32
+    # . if (eax == false) goto next check
+    3d/compare-eax-and  0/imm32/false
     74/jump-if-equal  $emit-sib:check-for-base/disp8
 $emit-sib:scale:
     # scale = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
@@ -2759,8 +2759,8 @@ $emit-sib:check-for-base:
     e8/call  has-metadata?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-    # . if (eax == 0) goto next check
-    3d/compare-eax-and  0/imm32
+    # . if (eax == false) goto next check
+    3d/compare-eax-and  0/imm32/false
     74/jump-if-equal  $emit-sib:check-for-index/disp8
 $emit-sib:base:
     # base = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
@@ -2787,8 +2787,8 @@ $emit-sib:check-for-index:
     e8/call  has-metadata?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-    # . if (eax == 0) loop
-    3d/compare-eax-and  0/imm32
+    # . if (eax == false) loop
+    3d/compare-eax-and  0/imm32/false
     0f 84/jump-if-equal  $emit-sib:loop/disp32
 $emit-sib:index:
     # index = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
@@ -2807,10 +2807,10 @@ $emit-sib:index:
     e9/jump  $emit-sib:loop/disp32
 $emit-sib:break:
     # if (!has-sib?) return
-    81          7/subop/compare     3/mod/direct    2/rm32/edx    .           .             .           .           .               0/imm32           # compare edx
+    81          7/subop/compare     3/mod/direct    2/rm32/edx    .           .             .           .           .               0/imm32/false     # compare edx
     74/jump-if-equal  $emit-sib:end/disp8
 $emit-sib:calculate:
-    # sib/ebx = scale & 0b11
+    # var sib/ebx : byte = scale & 0b11
     81          4/subop/and         3/mod/direct    3/rm32/ebx    .           .             .           .           .               3/imm32/0b11      # bitwise and of ebx
     # sib <<= 2
     c1/shift    4/subop/left        3/mod/direct    3/rm32/ebx    .           .             .           .           .               2/imm8            # shift ebx left by 2 bits
@@ -2850,7 +2850,7 @@ $emit-sib:end:
 emit-disp:  # line : (address stream byte), out : (address buffered-file)
     # pseudocode:
     #   rewind-stream(line)
-    #   var word-slice = {0, 0}
+    #   var word-slice : (ref slice)
     #   while true
     #     word-slice = next-word(line)
     #     if (slice-empty?(word-slice)) break
@@ -2872,7 +2872,7 @@ emit-disp:  # line : (address stream byte), out : (address buffered-file)
     50/push-eax
     51/push-ecx
     52/push-edx
-    # var word-slice/ecx = {0, 0}
+    # var word-slice/ecx : (ref slice)
     68/push  0/imm32/end
     68/push  0/imm32/start
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -2967,17 +2967,17 @@ $emit-disp:check0:
     e8/call  slice-empty?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # . if (eax != 0) pass through
-    3d/compare-eax-and  0/imm32
+    # . if (eax != false) pass through
+    3d/compare-eax-and  0/imm32/false
     0f 85/jump-if-not-equal  $emit-disp:break/disp32
 $emit-disp:check1:
     # if (slice-starts-with?(word-slice, "#")) break
-    # . start/edx = word-slice->start
+    # . var start/edx : (address byte) = word-slice->start
     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # copy *ecx to edx
-    # . c/eax = *start
+    # . var c/eax : byte = *start
     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
     8a/copy-byte                    0/mod/indirect  2/rm32/edx    .           .             .           0/r32/AL    .               .                 # copy byte at *edx to AL
-    # . if (eax == '#') break
+    # . if (c == '#') break
     3d/compare-eax-and  0x23/imm32/hash
     0f 84/jump-if-equal  $emit-disp:break/disp32
 $emit-disp:check-for-disp32:
@@ -2990,8 +2990,8 @@ $emit-disp:check-for-disp32:
     e8/call  has-metadata?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-    # . if (eax == 0) goto next check
-    3d/compare-eax-and  0/imm32
+    # . if (eax == false) goto next check
+    3d/compare-eax-and  0/imm32/false
     74/jump-if-equal  $emit-disp:check-for-disp16/disp8
 $emit-disp:disp32:
     # emit(out, word-slice, 4)
@@ -3015,8 +3015,8 @@ $emit-disp:check-for-disp16:
     e8/call  has-metadata?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-    # . if (eax == 0) goto next check
-    3d/compare-eax-and  0/imm32
+    # . if (eax == false) goto next check
+    3d/compare-eax-and  0/imm32/false
     74/jump-if-equal  $emit-disp:check-for-disp8/disp8
 $emit-disp:disp16:
     # emit(out, word-slice, 2)
@@ -3040,8 +3040,8 @@ $emit-disp:check-for-disp8:
     e8/call  has-metadata?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-    # . if (eax == 0) loop
-    3d/compare-eax-and  0/imm32
+    # . if (eax == false) loop
+    3d/compare-eax-and  0/imm32/false
     0f 84/jump-if-equal  $emit-disp:loop/disp32
 $emit-disp:disp8:
     # emit(out, word-slice, 1)
@@ -3069,7 +3069,7 @@ $emit-disp:break:
 emit-imm:  # line : (address stream byte), out : (address buffered-file)
     # pseudocode:
     #   rewind-stream(line)
-    #   var word-slice = {0, 0}
+    #   var word-slice : (ref slice)
     #   while true
     #     word-slice = next-word(line)
     #     if (slice-empty?(word-slice)) break
@@ -3091,7 +3091,7 @@ emit-imm:  # line : (address stream byte), out : (address buffered-file)
     50/push-eax
     51/push-ecx
     52/push-edx
-    # var word-slice/ecx = {0, 0}
+    # var word-slice/ecx : (ref slice)
     68/push  0/imm32/end
     68/push  0/imm32/start
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -3186,17 +3186,17 @@ $emit-imm:check0:
     e8/call  slice-empty?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # . if (eax != 0) pass through
-    3d/compare-eax-and  0/imm32
+    # . if (eax != false) pass through
+    3d/compare-eax-and  0/imm32/false
     0f 85/jump-if-not-equal  $emit-imm:break/disp32
 $emit-imm:check1:
     # if (slice-starts-with?(word-slice, "#")) break
-    # . start/edx = slice->start
+    # . var start/edx : (address byte) = slice->start
     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # copy *ecx to edx
-    # . c/eax = *start
+    # . var c/eax : byte = *start
     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
     8a/copy-byte                    0/mod/indirect  2/rm32/edx    .           .             .           0/r32/AL    .               .                 # copy byte at *edx to AL
-    # . if (eax == '#') break
+    # . if (c == '#') break
     3d/compare-eax-and  0x23/imm32/hash
     0f 84/jump-if-equal  $emit-imm:break/disp32
 $emit-imm:check-for-imm32:
@@ -3209,8 +3209,8 @@ $emit-imm:check-for-imm32:
     e8/call  has-metadata?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-    # . if (eax == 0) goto next check
-    3d/compare-eax-and  0/imm32
+    # . if (eax == false) goto next check
+    3d/compare-eax-and  0/imm32/false
     74/jump-if-equal  $emit-imm:check-for-imm16/disp8
 $emit-imm:imm32:
     # emit(out, word-slice, 4)
@@ -3234,8 +3234,8 @@ $emit-imm:check-for-imm16:
     e8/call  has-metadata?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-    # . if (eax == 0) goto next check
-    3d/compare-eax-and  0/imm32
+    # . if (eax == false) goto next check
+    3d/compare-eax-and  0/imm32/false
     74/jump-if-equal  $emit-imm:check-for-imm8/disp8
 $emit-imm:imm16:
     # emit(out, word-slice, 2)
@@ -3259,8 +3259,8 @@ $emit-imm:check-for-imm8:
     e8/call  has-metadata?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-    # . if (eax == 0) loop
-    3d/compare-eax-and  0/imm32
+    # . if (eax == false) loop
+    3d/compare-eax-and  0/imm32/false
     0f 84/jump-if-equal  $emit-imm:loop/disp32
 $emit-imm:imm8:
     # emit(out, word-slice, 1)
@@ -5837,7 +5837,7 @@ test-convert-instruction-handles-imm8-operand:
     c3/return
 
 # shortcut for parse-hex-int(next-token-from-slice(word->start, word->end, '/'))
-parse-datum-of-word:  # word : (address slice) -> value/eax
+parse-datum-of-word:  # word : (address slice) -> value/eax : int
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
@@ -5846,7 +5846,7 @@ parse-datum-of-word:  # word : (address slice) -> value/eax
     56/push-esi
     # esi = word
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
-    # var slice/ecx = {0, 0}
+    # var slice/ecx : (ref slice)
     68/push  0/imm32/end
     68/push  0/imm32/start
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -5860,7 +5860,7 @@ parse-datum-of-word:  # word : (address slice) -> value/eax
     e8/call  next-token-from-slice/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
-    # value/eax = parse-hex-int(slice)
+    # return parse-hex-int(slice)
     # . . push args
     51/push-ecx
     # . . call
diff --git a/apps/sigils b/apps/sigils
index a792c7e0..a1f33ada 100755
--- a/apps/sigils
+++ b/apps/sigils
Binary files differdiff --git a/apps/sigils.subx b/apps/sigils.subx
index e6aba87a..a22aee44 100644
--- a/apps/sigils.subx
+++ b/apps/sigils.subx
@@ -78,8 +78,8 @@ Entry:  # run tests if necessary, convert stdin if not
     e8/call  kernel-string-equal?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-    # . if (eax == 0) goto interactive
-    3d/compare-eax-and  0/imm32
+    # . if (eax == false) goto interactive
+    3d/compare-eax-and  0/imm32/false
     74/jump-if-equal  $subx-sigils-main:interactive/disp8
     # run-tests()
     e8/call  run-tests/disp32
@@ -147,7 +147,7 @@ subx-sigils:  # in : (address buffered-file), out : (address buffered-file)
     68/push  0/imm32/read
     68/push  0/imm32/write
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
-    # var word-slice/edx = {0, 0}
+    # var word-slice/edx : (ref slice)
     68/push  0/imm32/end
     68/push  0/imm32/start
     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
@@ -189,8 +189,8 @@ $subx-sigils:check1:
     e8/call  slice-empty?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # . if (eax != 0) break
-    3d/compare-eax-and  0/imm32
+    # . if (eax != false) break
+    3d/compare-eax-and  0/imm32/false
     0f 85/jump-if-not-equal  $subx-sigils:next-line/disp32
 $subx-sigils:check-for-comment:
     # if (slice-starts-with?(word-slice, "#")) continue
@@ -269,8 +269,8 @@ $subx-sigils:check-for-indirect-mode:
     e8/call  disp32-mode?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # . if (eax == 0) goto indirect mode
-    3d/compare-eax-and  0/imm32
+    # . if (eax == false) goto indirect mode
+    3d/compare-eax-and  0/imm32/false
     74/jump-if-equal  $subx-sigils:indirect-mode/disp8
 $subx-sigils:disp32-mode:
     # emit-indirect-mode(out, word-slice)
@@ -1365,7 +1365,7 @@ emit-direct-mode:  # out : (address buffered-file), word-slice : (address slice)
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     # . save registers
     50/push-eax
-    # var local-slice/eax : (address slice) = {word-slice->start, word-slice->end}
+    # var local-slice/eax : (ref slice) = {word-slice->start, word-slice->end}
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   0xc/disp8       .                 # copy *(ebp+12) to eax
     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
     ff          6/subop/push        0/mod/indirect  0/rm32/eax    .           .             .           .           .               .                 # push *eax
@@ -1834,7 +1834,7 @@ test-next-word-or-expression:
     e8/call  clear-stream/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # var slice/ecx = {0, 0}
+    # var slice/ecx : (ref slice)
     68/push  0/imm32/end
     68/push  0/imm32/start
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -1907,7 +1907,7 @@ test-next-word-or-expression-returns-whole-comment:
     e8/call  clear-stream/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # var slice/ecx = {0, 0}
+    # var slice/ecx : (ref slice)
     68/push  0/imm32/end
     68/push  0/imm32/start
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -1980,7 +1980,7 @@ test-next-word-or-expression-returns-empty-slice-on-eof:
     e8/call  clear-stream/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # var slice/ecx = {0, 0}
+    # var slice/ecx : (ref slice)
     68/push  0/imm32/end
     68/push  0/imm32/start
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -2022,7 +2022,7 @@ test-next-word-or-expression-returns-string-literal:
     e8/call  clear-stream/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # var slice/ecx = {0, 0}
+    # var slice/ecx : (ref slice)
     68/push  0/imm32/end
     68/push  0/imm32/start
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -2085,7 +2085,7 @@ test-next-word-or-expression-returns-string-with-escapes:
     e8/call  clear-stream/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # var slice/ecx = {0, 0}
+    # var slice/ecx : (ref slice)
     68/push  0/imm32/end
     68/push  0/imm32/start
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -2148,7 +2148,7 @@ test-next-word-or-expression-returns-whole-expression:
     e8/call  clear-stream/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # var slice/ecx = {0, 0}
+    # var slice/ecx : (ref slice)
     68/push  0/imm32/end
     68/push  0/imm32/start
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -2250,7 +2250,7 @@ parse-effective-address:  # word-slice : (address slice) -> base/eax, index/ecx,
     # . save registers
     56/push-esi
     57/push-edi
-    # var local-slice/esi : (address slice) = {word-slice->start, word-slice->end}
+    # var local-slice/esi : (ref slice) = {word-slice->start, word-slice->end}
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
@@ -2679,7 +2679,7 @@ next-register:  # in : (address slice) -> reg/eax : int
     56/push-esi
     # esi = in
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
-    # var reg-slice/ecx : (address slice) = {in->start, in->start + 3}
+    # var reg-slice/ecx : (ref slice) = {in->start, in->start + 3}
     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
     05/add-to-eax  3/imm32
     50/push-eax
@@ -3865,7 +3865,7 @@ disp32-mode?:  # in : (address slice) -> reg/eax : boolean
     # . save registers
     56/push-esi
     57/push-edi
-    # var local-slice/esi : (address slice) = {in->start, in->end}
+    # var local-slice/esi : (ref slice) = {in->start, in->end}
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
@@ -3923,7 +3923,7 @@ emit-indirect-disp32:  # out : (address buffered-file), word-slice : (address sl
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     # . save registers
     56/push-esi
-    # var local-slice/esi : (address slice) = {in->start, in->end}
+    # var local-slice/esi : (ref slice) = {in->start, in->end}
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   0xc/disp8       .                 # copy *(ebp+12) to esi
     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
@@ -4055,8 +4055,8 @@ $next-hex-int:loop:
     e8/call  is-hex-digit?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # . if (eax == 0) break
-    3d/compare-eax-and  0/imm32
+    # . if (eax == false) break
+    3d/compare-eax-and  0/imm32/false
     74/jump-if-equal  $next-hex-int:break/disp8
     # eax = from-hex-char(*curr)
     # . . copy arg to eax
@@ -4071,7 +4071,8 @@ $next-hex-int:loop:
     # loop
     eb/jump  $next-hex-int:loop/disp8
 $next-hex-int:break:
-    81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0/imm32           # compare ebx
+    # if (negate?) result = -result
+    81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0/imm32/false     # compare ebx
     74/jump-if-equal  $next-hex-int:end/disp8
 $next-hex-int:negate:
     f7          3/subop/negate      3/mod/direct    7/rm32/edi    .           .             .           .           .               .                 # negate edi
@@ -4437,8 +4438,8 @@ $next-positive-hex-int:loop:
     e8/call  is-hex-digit?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # . if (eax == 0) break
-    3d/compare-eax-and  0/imm32
+    # . if (eax == false) break
+    3d/compare-eax-and  0/imm32/false
     74/jump-if-equal  $next-positive-hex-int:end/disp8
     # eax = from-hex-char(*curr)
     # . . copy arg to eax
diff --git a/apps/survey b/apps/survey
index 23eb9e3b..2ec38e5d 100755
--- a/apps/survey
+++ b/apps/survey
Binary files differdiff --git a/apps/survey.subx b/apps/survey.subx
index 8563116f..e365ac1b 100644
--- a/apps/survey.subx
+++ b/apps/survey.subx
@@ -74,8 +74,8 @@ Entry:  # run tests if necessary, convert stdin if not
     e8/call  kernel-string-equal?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-    # . if (eax == 0) goto interactive
-    3d/compare-eax-and  0/imm32
+    # . if (eax == false) goto interactive
+    3d/compare-eax-and  0/imm32/false
     74/jump-if-equal  $subx-survey-main:interactive/disp8
     # run-tests()
     e8/call  run-tests/disp32
@@ -115,9 +115,9 @@ $subx-survey-main:end:
 
 subx-survey:  # infile : (address buffered-file), out : (address buffered-file)
     # pseudocode
-    #   var in : (address stream byte) = stream(4096)
+    #   var in : (ref stream byte 4096)
     #   slurp(infile, in)
-    #   var segments : (stream segment-info 10)
+    #   var segments : (stream segment-info)
     #   var labels : (stream label-info Max-labels)
     #   compute-offsets(in, segments, labels)
     #   compute-addresses(segments, labels)
@@ -131,13 +131,13 @@ subx-survey:  # infile : (address buffered-file), out : (address buffered-file)
     51/push-ecx
     52/push-edx
     56/push-esi
-    # var segments/ecx = stream(10 * 16)
+    # var segments/ecx : (ref stream {string, segment-info} 160)   # 10 rows * 16 bytes/row
     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0xa0/imm32        # subtract from esp
     68/push  0xa0/imm32/length
     68/push  0/imm32/read
     68/push  0/imm32/write
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
-    # var labels/edx = stream(Max-labels * 16)
+    # var labels/edx : (ref stream label-info Max-labels*16)
     # . data
     2b/subtract                     0/mod/indirect  5/rm32/.disp32            .             .           4/r32/esp   Max-labels/disp32                 # subtract *Max-labels from esp
     # . length
@@ -147,7 +147,7 @@ subx-survey:  # infile : (address buffered-file), out : (address buffered-file)
     # . write
     68/push  0/imm32/write
     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
-    # var in/esi = stream(Input-size * 1)
+    # var in/esi : (ref stream byte Input-size)
     # . data
     2b/subtract                     0/mod/indirect  5/rm32/.disp32            .             .           4/r32/esp   Input-size/disp32                 # subtract *Input-size from esp
     # . length
@@ -618,7 +618,7 @@ compute-offsets:segment-tmp:  # slice
 
 == code
 
-compute-offsets:  # in : (address stream), segments : (address stream {string, segment-info}), labels : (address stream {string, label-info})
+compute-offsets:  # in : (address stream byte), segments : (address stream {string, segment-info}), labels : (address stream {string, label-info})
     # skeleton:
     #   for lines in 'in'
     #     for words in line
@@ -833,8 +833,8 @@ $compute-offsets:case-empty:
     e8/call  slice-empty?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # . if (eax != 0) break
-    3d/compare-eax-and  0/imm32
+    # . if (eax != false) break
+    3d/compare-eax-and  0/imm32/false
     0f 85/jump-if-not-equal  $compute-offsets:line-loop/disp32
 $compute-offsets:case-comment:
     # if slice-starts-with?(word-slice, "#") continue
@@ -843,8 +843,8 @@ $compute-offsets:case-comment:
     e8/call  slice-starts-with?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-    # . if (eax != 0) break
-    3d/compare-eax-and  0/imm32
+    # . if (eax != false) break
+    3d/compare-eax-and  0/imm32/false
     0f 85/jump-if-not-equal  $compute-offsets:line-loop/disp32
 $compute-offsets:case-segment-header:
     # if (!slice-equal?(word-slice/edx, "==")) goto next case
@@ -854,8 +854,8 @@ $compute-offsets:case-segment-header:
     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) goto next case
-    3d/compare-eax-and  0/imm32
+    # . if (eax == false) goto next case
+    3d/compare-eax-and  0/imm32/false
     0f 84/jump-if-equal  $compute-offsets:case-label/disp32
     # if (curr-segment-name == 0) goto construct-next-segment
     81          7/subop/compare     3/mod/direct    6/rm32/esi    .           .             .           .           .               0/imm32           # compare esi
@@ -973,8 +973,8 @@ $compute-offsets:update-curr-segment-name:
     e8/call  slice-empty?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # . if (eax != 0) abort
-    3d/compare-eax-and  0/imm32
+    # . if (eax != false) abort
+    3d/compare-eax-and  0/imm32/false
     0f 85/jump-if-not-equal  $compute-offsets:abort/disp32
     # seg/ebx = get-or-insert(segments, curr-segment-name, row-size=16)
     # . . push args
@@ -1022,8 +1022,8 @@ $compute-offsets:case-label:
     e8/call  is-label?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # . if (eax == 0) goto next case
-    3d/compare-eax-and  0/imm32
+    # . if (eax == false) goto next case
+    3d/compare-eax-and  0/imm32/false
     74/jump-if-equal  $compute-offsets:case-default/disp8
     # strip trailing ':' from word-slice
     ff          1/subop/decrement   1/mod/*+disp8   2/rm32/edx    .           .             .           .           4/disp8         .                 # decrement *(edx+4)
@@ -1219,13 +1219,13 @@ test-compute-offsets:
     e8/call  clear-stream/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # var segments/ecx = stream(2 * 16)
+    # var segments/ecx : (ref stream byte 2*16)
     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x20/imm32        # subtract from esp
     68/push  0x20/imm32/length
     68/push  0/imm32/read
     68/push  0/imm32/write
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
-    # var labels/edx = stream(2 * 16)
+    # var labels/edx : (ref stream byte 2*16)
     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x20/imm32        # subtract from esp
     68/push  0x20/imm32/length
     68/push  0/imm32/read
@@ -1381,7 +1381,7 @@ test-compute-offsets:
 compute-addresses:  # segments : (address stream {string, segment-info}), labels : (address stream {string, label-info})
     # pseudocode:
     #   srow : (address segment-info) = segments->data
-    #   max = segments->data + segments->write
+    #   max = &segments->data[segments->write]
     #   num-segments = segments->write / 16
     #   starting-offset = 0x34 + (num-segments * 0x20)
     #   while true
@@ -1392,7 +1392,7 @@ compute-addresses:  # segments : (address stream {string, segment-info}), labels
     #     trace-sssns("segment " s->key " starts at address " s->address)
     #     srow += 16  # row-size
     #   lrow : (address label-info) = labels->data
-    #   max = labels->data + labels->write
+    #   max = &labels->data[labels->write]
     #   while true
     #     if (lrow >= max) break
     #     seg-name : (address string) = lrow->segment-name
@@ -1422,7 +1422,7 @@ compute-addresses:  # segments : (address stream {string, segment-info}), labels
     81          0/subop/add         3/mod/direct    7/rm32/edi    .           .             .           .           .               0x34/imm32        # add to edi
     # srow/eax = segments->data
     8d/copy-address                 1/mod/*+disp8   6/rm32/esi    .           .             .           0/r32/eax   0xc/disp8       .                 # copy esi+12 to eax
-    # max/ecx = segments->data + segments->write
+    # max/ecx = &segments->data[segments->write]
     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # copy *esi to ecx
     01/add                          3/mod/direct    1/rm32/ecx    .           .             .           6/r32/esi   .               .                 # add esi to ecx
 $compute-addresses:segment-loop:
@@ -1489,7 +1489,7 @@ $compute-addresses:segment-break:
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   0xc/disp8       .                 # copy *(ebp+12) to esi
     # lrow/eax = labels->data
     8d/copy-address                 1/mod/*+disp8   6/rm32/esi    .           .             .           0/r32/eax   0xc/disp8       .                 # copy esi+12 to eax
-    # max/ecx = labels->data + labels->write
+    # max/ecx = &labels->data[labels->write]
     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # copy *esi to ecx
     01/add                          3/mod/direct    1/rm32/ecx    .           .             .           6/r32/esi   .               .                 # add esi to ecx
 $compute-addresses:label-loop:
@@ -1621,13 +1621,13 @@ test-compute-addresses:
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     # setup
-    # . var segments/ecx = stream(10 * 16)
+    # . var segments/ecx : (ref stream byte 10*16)
     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0xa0/imm32        # subtract from esp
     68/push  0xa0/imm32/length
     68/push  0/imm32/read
     68/push  0/imm32/write
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
-    # . var labels/edx = stream(512 * 16)
+    # . var labels/edx : (ref stream byte 512*16)
     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x2000/imm32      # subtract from esp
     68/push  0x2000/imm32/length
     68/push  0/imm32/read
@@ -1790,13 +1790,13 @@ test-compute-addresses-large-segments:
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     # setup
-    # . var segments/ecx = stream(10 * 16)
+    # . var segments/ecx : (ref stream byte 10*16)
     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0xa0/imm32        # subtract from esp
     68/push  0xa0/imm32/length
     68/push  0/imm32/read
     68/push  0/imm32/write
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
-    # . var labels/edx = stream(512 * 16)
+    # . var labels/edx : (ref stream byte 512*16)
     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x2000/imm32      # subtract from esp
     68/push  0x2000/imm32/length
     68/push  0/imm32/read
@@ -1871,7 +1871,7 @@ test-compute-addresses-large-segments:
     5d/pop-to-ebp
     c3/return
 
-emit-output:  # in : (address stream), out : (address buffered-file), segments : (address stream {string, segment-info}), labels : (address stream {string, label-info})
+emit-output:  # in : (address stream byte), out : (address buffered-file), segments : (address stream {string, segment-info}), labels : (address stream {string, label-info})
     # pseudocode:
     #   emit-headers(out, segments, labels)
     #   emit-segments(in, out, segments, labels)
@@ -1922,7 +1922,7 @@ $emit-output:end:
     5d/pop-to-ebp
     c3/return
 
-emit-segments:  # in : (address stream), out : (address buffered-file), segments : (address stream {string, segment-info}), labels : (address stream {string, label-info})
+emit-segments:  # in : (address stream byte), out : (address buffered-file), segments : (address stream {string, segment-info}), labels : (address stream {string, label-info})
     # pseudocode:
     #   var offset-of-next-instruction = 0
     #   var line : (stream byte 512)
@@ -1993,11 +1993,11 @@ emit-segments:  # in : (address stream), out : (address buffered-file), segments
     68/push  0/imm32/read
     68/push  0/imm32/write
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
-    # var word-slice/edx = {0, 0}
+    # var word-slice/edx : (ref slice)
     68/push  0/imm32/end
     68/push  0/imm32/start
     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
-    # var datum/edi = {0, 0}
+    # var datum/edi : (ref slice)
     68/push  0/imm32/end
     68/push  0/imm32/start
     89/copy                         3/mod/direct    7/rm32/edi    .           .             .           4/r32/esp   .               .                 # copy esp to edi
@@ -2139,8 +2139,8 @@ $emit-segments:check-for-label:
     e8/call  is-label?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # . if (eax != 0) break
-    3d/compare-eax-and  0/imm32
+    # . if (eax != false) break
+    3d/compare-eax-and  0/imm32/false
     0f 85/jump-if-not-equal  $emit-segments:line-loop/disp32
 $emit-segments:check-for-segment-header:
     # if (slice-equal?(word-slice, "==")) break
@@ -2152,8 +2152,8 @@ $emit-segments:check-for-segment-header:
     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) break
-    3d/compare-eax-and  0/imm32
+    # . if (eax != false) break
+    3d/compare-eax-and  0/imm32/false
     0f 85/jump-if-not-equal  $emit-segments:line-loop/disp32
 $emit-segments:2-character:
     # if (length(word-slice) != 2) goto next check
@@ -2275,8 +2275,8 @@ $emit-segments:check-global-variable:
     e8/call  string-equal?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-    # . if (eax != 0) goto code label checks
-    3d/compare-eax-and  0/imm32
+    # . if (eax != false) goto code label checks
+    3d/compare-eax-and  0/imm32/false
     0f 85/jump-if-not-equal  $emit-segments:check-code-label-for-imm8/disp32
 $emit-segments:check-global-variable-for-disp8:
     # if has-metadata?(word-slice, "disp8") abort
@@ -2288,8 +2288,8 @@ $emit-segments:check-global-variable-for-disp8:
     e8/call  has-metadata?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-    # . if (eax != 0) abort
-    3d/compare-eax-and  0/imm32
+    # . if (eax != false) abort
+    3d/compare-eax-and  0/imm32/false
     0f 85/jump-if-not-equal  $emit-segments:global-variable-abort/disp32
 $emit-segments:check-global-variable-for-imm8:
     # if has-metadata?(word-slice, "imm8") abort
@@ -2301,8 +2301,8 @@ $emit-segments:check-global-variable-for-imm8:
     e8/call  has-metadata?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-    # . if (eax != 0) abort
-    3d/compare-eax-and  0/imm32
+    # . if (eax != false) abort
+    3d/compare-eax-and  0/imm32/false
     0f 85/jump-if-not-equal  $emit-segments:global-variable-abort/disp32
 $emit-segments:emit-global-variable:
     # emit-hex(out, info->address, 4)
@@ -2326,8 +2326,8 @@ $emit-segments:check-code-label-for-imm8:
     e8/call  has-metadata?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-    # . if (eax != 0) abort
-    3d/compare-eax-and  0/imm32
+    # . if (eax != false) abort
+    3d/compare-eax-and  0/imm32/false
     0f 85/jump-if-not-equal  $emit-segments:imm8-abort/disp32
 $emit-segments:check-code-label-for-imm32:
     # if (!has-metadata?(word-slice, "imm32")) goto next check
@@ -2339,7 +2339,7 @@ $emit-segments:check-code-label-for-imm32:
     e8/call  has-metadata?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-    # . if (eax == 0) goto next check
+    # . if (eax == false) goto next check
     3d/compare-eax-and  0/imm32
     74/jump-if-equal  $emit-segments:check-code-label-for-disp8/disp8
 #?     # dump info->address {{{
@@ -2397,8 +2397,8 @@ $emit-segments:check-code-label-for-disp8:
     e8/call  has-metadata?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-    # . if (eax == 0) goto next check
-    3d/compare-eax-and  0/imm32
+    # . if (eax == false) goto next check
+    3d/compare-eax-and  0/imm32/false
     74/jump-if-equal  $emit-segments:check-code-label-for-disp32/disp8
 $emit-segments:emit-code-label-disp8:
     # emit-hex(out, info->offset - offset-of-next-instruction, 1)
@@ -2424,8 +2424,8 @@ $emit-segments:check-code-label-for-disp32:
     e8/call  has-metadata?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-    # . if (eax == 0) abort
-    3d/compare-eax-and  0/imm32
+    # . if (eax == false) abort
+    3d/compare-eax-and  0/imm32/false
     0f 84/jump-if-equal  $emit-segments:abort/disp32
 $emit-segments:emit-code-label-disp32:
     # emit-hex(out, info->offset - offset-of-next-instruction, 4)
@@ -2577,13 +2577,13 @@ test-emit-segments-global-variable:
     e8/call  clear-stream/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # . var segments/ecx = stream(10 * 16)
+    # . var segments/ecx : (ref stream byte 10*16)
     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0xa0/imm32        # subtract from esp
     68/push  0xa0/imm32/length
     68/push  0/imm32/read
     68/push  0/imm32/write
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
-    # . var labels/edx = stream(512 * 16)
+    # . var labels/edx : (ref stream byte 512*16)
     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x2000/imm32      # subtract from esp
     68/push  0x2000/imm32/length
     68/push  0/imm32/read
@@ -2814,13 +2814,13 @@ test-emit-segments-code-label:
     e8/call  clear-stream/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # . var segments/ecx = stream(10 * 16)
+    # . var segments/ecx : (ref stream byte 10*16)
     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0xa0/imm32        # subtract from esp
     68/push  0xa0/imm32/length
     68/push  0/imm32/read
     68/push  0/imm32/write
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
-    # . var labels/edx = stream(512 * 16)
+    # . var labels/edx : (ref stream byte 512*16)
     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x2000/imm32      # subtract from esp
     68/push  0x2000/imm32/length
     68/push  0/imm32/read
@@ -3016,13 +3016,13 @@ test-emit-segments-code-label-absolute:
     e8/call  clear-stream/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # . var segments/ecx = stream(10 * 16)
+    # . var segments/ecx : (ref stream byte 10*16)
     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0xa0/imm32        # subtract from esp
     68/push  0xa0/imm32/length
     68/push  0/imm32/read
     68/push  0/imm32/write
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
-    # . var labels/edx = stream(512 * 16)
+    # . var labels/edx : (ref stream byte 512*16)
     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x2000/imm32      # subtract from esp
     68/push  0x2000/imm32/length
     68/push  0/imm32/read
@@ -3177,7 +3177,7 @@ emit-headers:  # out : (address buffered-file), segments : (address stream {stri
     # pseudocode:
     #   emit-elf-header(out, segments, labels)
     #   curr-segment = segments->data
-    #   max = segments->data + segments->write
+    #   max = &segments->data[segments->write]
     #   while true
     #     if (curr-segment >= max) break
     #     emit-elf-program-header-entry(out, curr-segment)
@@ -3213,7 +3213,7 @@ emit-headers:  # out : (address buffered-file), segments : (address stream {stri
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     # curr-segment/eax = segments->data
     8d/copy-address                 1/mod/*+disp8   0/rm32/eax    .           .             .           0/r32/eax   0xc/disp8       .                 # copy eax+12 to eax
-    # max/ecx = segments->data + segments->write
+    # max/ecx = &segments->data[segments->write]
     01/add                          3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # add eax to ecx
 $emit-headers:loop:
     # if (curr-segment >= max) break
@@ -3431,8 +3431,8 @@ emit-elf-program-header-entry:  # out : (address buffered-file), curr-segment :
     e8/call  string-equal?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-    # . if (eax == 0) goto next check
-    3d/compare-eax-and  0/imm32
+    # . if (eax == false) goto next check
+    3d/compare-eax-and  0/imm32/false
     74/jump-if-equal  $emit-elf-program-header-entry:data/disp8
     # *$Elf_p_flags = r-x
     c7          0/subop/copy        0/mod/indirect  5/rm32/.disp32            .             .           .           $Elf_p_flags/disp32  5/imm32      # copy to *$Elf_p_flags
@@ -3479,12 +3479,12 @@ stream-add4:  # in : (address stream byte), key : address, val1 : address, val2
     56/push-esi
     # esi = in
     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
-    # curr/eax = in->data + in->write
+    # curr/eax = &in->data[in->write]
     # . eax = in->write
     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
     # . eax = esi+eax+12
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/esi  0/index/eax   .           0/r32/eax   0xc/disp8       .                 # copy esi+eax+12 to eax
-    # max/edx = in->data + in->length
+    # max/edx = &in->data[in->length]
     # . edx = in->length
     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           2/r32/edx   8/disp8         .                 # copy *(esi+8) to edx
     # . edx = esi+edx+12
@@ -3830,7 +3830,7 @@ test-trace-slsls:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var b/ebx : (address slice) = {eax, ecx}
+    # var b/ebx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
@@ -3839,7 +3839,7 @@ test-trace-slsls:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var d/edx : (address slice) = {eax, ecx}
+    # var d/edx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
@@ -3955,7 +3955,7 @@ test-trace-slsns:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var b/ebx : (address slice) = {eax, ecx}
+    # var b/ebx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
@@ -4071,7 +4071,7 @@ test-trace-slsss:
     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
     05/add-to-eax  4/imm32
-    # var b/ebx : (address slice) = {eax, ecx}
+    # var b/ebx : (ref slice) = {eax, ecx}
     51/push-ecx
     50/push-eax
     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
@@ -4125,7 +4125,7 @@ test-trace-slsss:
     5d/pop-to-ebp
     c3/return
 
-num-bytes:  # line : (address stream) -> eax : int
+num-bytes:  # line : (address stream byte) -> eax : int
     # pseudocode:
     #   result = 0
     #   while true
@@ -4150,7 +4150,7 @@ num-bytes:  # line : (address stream) -> eax : int
     53/push-ebx
     # var result/eax = 0
     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
-    # var word-slice/ecx = {0, 0}
+    # var word-slice/ecx : (ref slice)
     68/push  0/imm32/end
     68/push  0/imm32/start
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
@@ -4247,8 +4247,8 @@ $num-bytes:check0:
     e8/call  slice-empty?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # . if (eax != 0) break
-    3d/compare-eax-and  0/imm32
+    # . if (eax != false) break
+    3d/compare-eax-and  0/imm32/false
     # . restore result now that ZF is set
     58/pop-to-eax
     75/jump-if-not-equal  $num-bytes:end/disp8
@@ -4284,8 +4284,8 @@ $num-bytes:check-for-segment-header:
     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) break
-    3d/compare-eax-and  0/imm32
+    # . if (eax != false) break
+    3d/compare-eax-and  0/imm32/false
     # . restore result now that ZF is set
     58/pop-to-eax
     75/jump-if-not-equal  $num-bytes:end/disp8
diff --git a/apps/tests b/apps/tests
index dd75aac1..b292f93b 100755
--- a/apps/tests
+++ b/apps/tests
Binary files differdiff --git a/apps/tests.subx b/apps/tests.subx
index f31f4d16..1d00cf30 100644
--- a/apps/tests.subx
+++ b/apps/tests.subx
@@ -43,8 +43,8 @@ Entry:  # run tests if necessary, convert stdin if not
     e8/call  kernel-string-equal?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-    # . if (eax == 0) goto run-main
-    3d/compare-eax-and  0/imm32
+    # . if (eax == false) goto run-main
+    3d/compare-eax-and  0/imm32/false
     74/jump-if-equal  $subx-tests-main:interactive/disp8
     # run-tests()
     e8/call  run-tests/disp32
@@ -101,13 +101,13 @@ subx-gen-run-tests:  # in : (address buffered-file), out : (address buffered-fil
     52/push-edx
     53/push-ebx
     57/push-edi
-    # var line/ecx : (address stream byte) = stream(512)
+    # var line/ecx : (ref stream byte 512)
     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x200/imm32       # subtract from esp
     68/push  0x200/imm32/length
     68/push  0/imm32/read
     68/push  0/imm32/write
     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
-    # var word-slice/edx = {0, 0}
+    # var word-slice/edx : (ref slice)
     68/push  0/imm32/end
     68/push  0/imm32/start
     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
@@ -178,8 +178,8 @@ $subx-gen-run-tests:check-for-label:
     e8/call  is-label?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-    # . if (eax == 0) continue
-    3d/compare-eax-and  0/imm32
+    # . if (eax == false) continue
+    3d/compare-eax-and  0/imm32/false
     74/jump-if-equal  $subx-gen-run-tests:continue/disp8
 $subx-gen-run-tests:check-label-prefix:
     # strip trailing ':' from word-slice
@@ -192,8 +192,8 @@ $subx-gen-run-tests:check-label-prefix:
     e8/call  slice-starts-with?/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-    # . if (eax == 0) break
-    3d/compare-eax-and  0/imm32
+    # . if (eax == false) break
+    3d/compare-eax-and  0/imm32/false
     74/jump-if-equal  $subx-gen-run-tests:continue/disp8
 $subx-gen-run-tests:call-test-function:
     # tests-found? = true
@@ -242,7 +242,7 @@ $subx-gen-run-tests:continue:
     e9/jump  $subx-gen-run-tests:loop/disp32
 $subx-gen-run-tests:break:
     # if (!tests-found?) goto end
-    81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0/imm32           # compare ebx
+    81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0/imm32/false     # compare ebx
     74/jump-if-equal  $subx-gen-run-tests:end/disp8
     # write(new-code-segment, "  c3/return\n")
     # . . push args