about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--subx/apps/pack.subx246
-rw-r--r--subx/apps/subx-common.subx246
-rw-r--r--subx/apps/survey.subx137
-rwxr-xr-xsubx/run_one_test.sh18
4 files changed, 393 insertions, 254 deletions
diff --git a/subx/apps/pack.subx b/subx/apps/pack.subx
index 68d936f9..1e6c1fbc 100644
--- a/subx/apps/pack.subx
+++ b/subx/apps/pack.subx
@@ -5835,252 +5835,6 @@ test-convert-instruction-handles-imm8-operand:
     5d/pop-to-EBP
     c3/return
 
-# (re)compute the bounds of the next word in the line
-# return empty string on reaching end of file
-next-word:  # line : (address stream byte), out : (address slice)
-    # . prolog
-    55/push-EBP
-    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-    # . save registers
-    50/push-EAX
-    51/push-ECX
-    56/push-ESI
-    57/push-EDI
-    # ESI = line
-    8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
-    # EDI = out
-    8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   0xc/disp8       .                 # copy *(EBP+12) to EDI
-    # skip-chars-matching(line, ' ')
-    # . . push args
-    68/push  0x20/imm32/space
-    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-    # . . call
-    e8/call  skip-chars-matching/disp32
-    # . . discard args
-    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-$next-word:check0:
-    # if (line->read >= line->write) clear out and return
-    # . EAX = line->read
-    8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ESI+4) to EAX
-    # . 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}
-    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
-$next-word:check-for-comment:
-    # out->start = &line->data[line->read]
-    8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
-    8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/EAX   0xc/disp8       .                 # copy ESI+ECX+12 to EAX
-    89/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *EDI
-    # if (line->data[line->read] == '#') out->end = &line->data[line->write]), skip rest of stream and return
-    # . EAX = line->data[line->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    0xc/disp8       .                 # copy byte at *(ESI+ECX+12) to AL
-    # . compare
-    3d/compare-EAX-and  0x23/imm32/pound
-    75/jump-if-not-equal  $next-word:regular-word/disp8
-$next-word:comment:
-    # . out->end = &line->data[line->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
-    89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
-    # . line->read = line->write
-    89/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(ESI+4)
-    # . return
-    eb/jump  $next-word:end/disp8
-$next-word:regular-word:
-    # otherwise skip-chars-not-matching-whitespace(line)  # including trailing newline
-    # . . push args
-    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-    # . . call
-    e8/call  skip-chars-not-matching-whitespace/disp32
-    # . . discard args
-    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-    # out->end = &line->data[line->read]
-    8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
-    8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/EAX   0xc/disp8       .                 # copy ESI+ECX+12 to EAX
-    89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
-$next-word:end:
-    # . restore registers
-    5f/pop-to-EDI
-    5e/pop-to-ESI
-    59/pop-to-ECX
-    58/pop-to-EAX
-    # . epilog
-    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-    5d/pop-to-EBP
-    c3/return
-
-test-next-word:
-    # . prolog
-    55/push-EBP
-    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-    # setup
-    # . clear-stream(_test-stream)
-    # . . push args
-    68/push  _test-stream/imm32
-    # . . call
-    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}
-    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
-    # write(_test-stream, "  ab")
-    # . . push args
-    68/push  "  ab"/imm32
-    68/push  _test-stream/imm32
-    # . . call
-    e8/call  write/disp32
-    # . . discard args
-    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-    # next-word(_test-stream, slice)
-    # . . push args
-    51/push-ECX
-    68/push  _test-stream/imm32
-    # . . call
-    e8/call  next-word/disp32
-    # . . discard args
-    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-    # check-ints-equal(slice->start - _test-stream->data, 2, msg)
-    # . check-ints-equal(slice->start - _test-stream, 14, msg)
-    # . . push args
-    68/push  "F - test-next-word: start"/imm32
-    68/push  0xe/imm32
-    # . . push slice->start - _test-stream
-    8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
-    81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
-    50/push-EAX
-    # . . call
-    e8/call  check-ints-equal/disp32
-    # . . discard args
-    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-    # check-ints-equal(slice->end - _test-stream->data, 4, msg)
-    # . check-ints-equal(slice->end - _test-stream, 16, msg)
-    # . . push args
-    68/push  "F - test-next-word: end"/imm32
-    68/push  0x10/imm32
-    # . . push slice->end - _test-stream
-    8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
-    81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
-    50/push-EAX
-    # . . call
-    e8/call  check-ints-equal/disp32
-    # . . discard args
-    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-    # . epilog
-    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-    5d/pop-to-EBP
-    c3/return
-
-test-next-word-returns-whole-comment:
-    # . prolog
-    55/push-EBP
-    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-    # setup
-    # . clear-stream(_test-stream)
-    # . . push args
-    68/push  _test-stream/imm32
-    # . . call
-    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}
-    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
-    # write(_test-stream, "  # a")
-    # . . push args
-    68/push  "  # a"/imm32
-    68/push  _test-stream/imm32
-    # . . call
-    e8/call  write/disp32
-    # . . discard args
-    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-    # next-word(_test-stream, slice)
-    # . . push args
-    51/push-ECX
-    68/push  _test-stream/imm32
-    # . . call
-    e8/call  next-word/disp32
-    # . . discard args
-    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-    # check-ints-equal(slice->start - _test-stream->data, 2, msg)
-    # . check-ints-equal(slice->start - _test-stream, 14, msg)
-    # . . push args
-    68/push  "F - test-next-word-returns-whole-comment: start"/imm32
-    68/push  0xe/imm32
-    # . . push slice->start - _test-stream
-    8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
-    81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
-    50/push-EAX
-    # . . call
-    e8/call  check-ints-equal/disp32
-    # . . discard args
-    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-    # check-ints-equal(slice->end - _test-stream->data, 5, msg)
-    # . check-ints-equal(slice->end - _test-stream, 17, msg)
-    # . . push args
-    68/push  "F - test-next-word-returns-whole-comment: end"/imm32
-    68/push  0x11/imm32
-    # . . push slice->end - _test-stream
-    8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
-    81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
-    50/push-EAX
-    # . . call
-    e8/call  check-ints-equal/disp32
-    # . . discard args
-    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-    # . epilog
-    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-    5d/pop-to-EBP
-    c3/return
-
-test-next-word-returns-empty-string-on-eof:
-    # . prolog
-    55/push-EBP
-    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-    # setup
-    # . clear-stream(_test-stream)
-    # . . push args
-    68/push  _test-stream/imm32
-    # . . call
-    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}
-    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
-    # write nothing to _test-stream
-    # next-word(_test-stream, slice)
-    # . . push args
-    51/push-ECX
-    68/push  _test-stream/imm32
-    # . . call
-    e8/call  next-word/disp32
-    # . . discard args
-    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-    # check-ints-equal(slice->end - slice->start, 0, msg)
-    # . . push args
-    68/push  "F - test-next-word-returns-empty-string-on-eof"/imm32
-    68/push  0/imm32
-    # . . push slice->end - slice->start
-    8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
-    2b/subtract                     0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract *ECX from EAX
-    50/push-EAX
-    # . . call
-    e8/call  check-ints-equal/disp32
-    # . . discard args
-    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-    # . epilog
-    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-    5d/pop-to-EBP
-    c3/return
-
 # If datum of 'word' is not a valid name, it must be a hex int. Parse and print
 # it in 'width' bytes of hex, least significant first.
 # Otherwise just print the entire word including metadata.
diff --git a/subx/apps/subx-common.subx b/subx/apps/subx-common.subx
index f4034e84..0ac143f3 100644
--- a/subx/apps/subx-common.subx
+++ b/subx/apps/subx-common.subx
@@ -252,6 +252,252 @@ $test-get-or-insert:end:
     5d/pop-to-EBP
     c3/return
 
+# (re)compute the bounds of the next word in the line
+# return empty string on reaching end of file
+next-word:  # line : (address stream byte), out : (address slice)
+    # . prolog
+    55/push-EBP
+    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+    # . save registers
+    50/push-EAX
+    51/push-ECX
+    56/push-ESI
+    57/push-EDI
+    # ESI = line
+    8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
+    # EDI = out
+    8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   0xc/disp8       .                 # copy *(EBP+12) to EDI
+    # skip-chars-matching(line, ' ')
+    # . . push args
+    68/push  0x20/imm32/space
+    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+    # . . call
+    e8/call  skip-chars-matching/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+$next-word:check0:
+    # if (line->read >= line->write) clear out and return
+    # . EAX = line->read
+    8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ESI+4) to EAX
+    # . 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}
+    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
+$next-word:check-for-comment:
+    # out->start = &line->data[line->read]
+    8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
+    8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/EAX   0xc/disp8       .                 # copy ESI+ECX+12 to EAX
+    89/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *EDI
+    # if (line->data[line->read] == '#') out->end = &line->data[line->write]), skip rest of stream and return
+    # . EAX = line->data[line->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    0xc/disp8       .                 # copy byte at *(ESI+ECX+12) to AL
+    # . compare
+    3d/compare-EAX-and  0x23/imm32/pound
+    75/jump-if-not-equal  $next-word:regular-word/disp8
+$next-word:comment:
+    # . out->end = &line->data[line->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
+    89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
+    # . line->read = line->write
+    89/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(ESI+4)
+    # . return
+    eb/jump  $next-word:end/disp8
+$next-word:regular-word:
+    # otherwise skip-chars-not-matching-whitespace(line)  # including trailing newline
+    # . . push args
+    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+    # . . call
+    e8/call  skip-chars-not-matching-whitespace/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+    # out->end = &line->data[line->read]
+    8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
+    8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/EAX   0xc/disp8       .                 # copy ESI+ECX+12 to EAX
+    89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
+$next-word:end:
+    # . restore registers
+    5f/pop-to-EDI
+    5e/pop-to-ESI
+    59/pop-to-ECX
+    58/pop-to-EAX
+    # . epilog
+    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+    5d/pop-to-EBP
+    c3/return
+
+test-next-word:
+    # . prolog
+    55/push-EBP
+    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+    # setup
+    # . clear-stream(_test-stream)
+    # . . push args
+    68/push  _test-stream/imm32
+    # . . call
+    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}
+    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
+    # write(_test-stream, "  ab")
+    # . . push args
+    68/push  "  ab"/imm32
+    68/push  _test-stream/imm32
+    # . . call
+    e8/call  write/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+    # next-word(_test-stream, slice)
+    # . . push args
+    51/push-ECX
+    68/push  _test-stream/imm32
+    # . . call
+    e8/call  next-word/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+    # check-ints-equal(slice->start - _test-stream->data, 2, msg)
+    # . check-ints-equal(slice->start - _test-stream, 14, msg)
+    # . . push args
+    68/push  "F - test-next-word: start"/imm32
+    68/push  0xe/imm32
+    # . . push slice->start - _test-stream
+    8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
+    81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
+    50/push-EAX
+    # . . call
+    e8/call  check-ints-equal/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+    # check-ints-equal(slice->end - _test-stream->data, 4, msg)
+    # . check-ints-equal(slice->end - _test-stream, 16, msg)
+    # . . push args
+    68/push  "F - test-next-word: end"/imm32
+    68/push  0x10/imm32
+    # . . push slice->end - _test-stream
+    8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
+    81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
+    50/push-EAX
+    # . . call
+    e8/call  check-ints-equal/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+    # . epilog
+    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+    5d/pop-to-EBP
+    c3/return
+
+test-next-word-returns-whole-comment:
+    # . prolog
+    55/push-EBP
+    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+    # setup
+    # . clear-stream(_test-stream)
+    # . . push args
+    68/push  _test-stream/imm32
+    # . . call
+    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}
+    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
+    # write(_test-stream, "  # a")
+    # . . push args
+    68/push  "  # a"/imm32
+    68/push  _test-stream/imm32
+    # . . call
+    e8/call  write/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+    # next-word(_test-stream, slice)
+    # . . push args
+    51/push-ECX
+    68/push  _test-stream/imm32
+    # . . call
+    e8/call  next-word/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+    # check-ints-equal(slice->start - _test-stream->data, 2, msg)
+    # . check-ints-equal(slice->start - _test-stream, 14, msg)
+    # . . push args
+    68/push  "F - test-next-word-returns-whole-comment: start"/imm32
+    68/push  0xe/imm32
+    # . . push slice->start - _test-stream
+    8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
+    81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
+    50/push-EAX
+    # . . call
+    e8/call  check-ints-equal/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+    # check-ints-equal(slice->end - _test-stream->data, 5, msg)
+    # . check-ints-equal(slice->end - _test-stream, 17, msg)
+    # . . push args
+    68/push  "F - test-next-word-returns-whole-comment: end"/imm32
+    68/push  0x11/imm32
+    # . . push slice->end - _test-stream
+    8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
+    81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
+    50/push-EAX
+    # . . call
+    e8/call  check-ints-equal/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+    # . epilog
+    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+    5d/pop-to-EBP
+    c3/return
+
+test-next-word-returns-empty-string-on-eof:
+    # . prolog
+    55/push-EBP
+    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+    # setup
+    # . clear-stream(_test-stream)
+    # . . push args
+    68/push  _test-stream/imm32
+    # . . call
+    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}
+    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
+    # write nothing to _test-stream
+    # next-word(_test-stream, slice)
+    # . . push args
+    51/push-ECX
+    68/push  _test-stream/imm32
+    # . . call
+    e8/call  next-word/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+    # check-ints-equal(slice->end - slice->start, 0, msg)
+    # . . push args
+    68/push  "F - test-next-word-returns-empty-string-on-eof"/imm32
+    68/push  0/imm32
+    # . . push slice->end - slice->start
+    8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
+    2b/subtract                     0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract *ECX from EAX
+    50/push-EAX
+    # . . call
+    e8/call  check-ints-equal/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+    # . epilog
+    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+    5d/pop-to-EBP
+    c3/return
+
 # 'table' is a stream of (key, value) rows
 # keys are always strings (addresses; size 4 bytes)
 # values may be any type, but rows (key+value) always occupy 'row-size' bytes
diff --git a/subx/apps/survey.subx b/subx/apps/survey.subx
index 5a796b73..f036ed85 100644
--- a/subx/apps/survey.subx
+++ b/subx/apps/survey.subx
@@ -438,9 +438,140 @@ compute-offsets:  # in : (address buffered-file), segments : (address stream {st
     55/push-EBP
     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
     # . save registers
+    50/push-EAX
+    51/push-ECX
+    52/push-EDX
+    53/push-EBX
+    56/push-ESI
+    57/push-EDI
+    # cur-segment-name = {0, 0}
+    #c7          0/subop/copy        0/mod/indirect  5/rm32/.disp32            .             .           .           compute-offsets:cur-segment-name/disp32  0/imm32        # copy to *compute-offsets:word-slice
+    #c7          0/subop/copy        1/mod/*+disp8  5/rm32/.disp32             .             .           . 4/disp8   compute-offsets:cur-segment-name/disp32  0/imm32        # copy to *(compute-offsets:word-slice+4)
+    # file-offset = 0
+    b8/copy-to-EAX  0/imm32
+    89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   compute-offsets:file-offset/disp32      # copy EAX to *cur-segment-name
+    # segment-offset = 0
+    b8/copy-to-EAX  0/imm32
+    89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   compute-offsets:segment-offset/disp32      # copy EAX to *cur-segment-name
+    # var heap/ECX : (address allocation-descriptor) = {0, 0}
+    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
+    # heap = new-segment(512)
+    # . . push args
+    51/push-ECX
+    68/push  0x200/imm32
+    # . . call
+    e8/call  new-segment/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+    # EAX = new-stream(512, 1)
+    68/push  1/imm32
+    68/push  0x200/imm32
+    51/push-ECX
+    e8/call  new-stream/disp32
+    # . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+    # line/ECX = EAX
+    8b/copy                         3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
+$compute-offsets:line-loop:
+    # clear-stream(line/ECX)
+    51/push-ECX
+    e8/call  clear-stream/disp32
+    # . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+    # read-line-buffered(in, line/ECX)
+    51/push-ECX
+    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+    e8/call  read-line-buffered/disp32
+    # . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+    # if(line->write == 0)
+    # . EAX = line/ECX->write
+    8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *EAX to EAX
+    # . line->write/EAX == 0 ?
+    3d/compare-EAX-and  0/imm32
+    # . if so, break
+    0f 84/jump-if-equal  $compute-offsets:end/disp32
+$compute-offsets:word-loop:
+    # var word-slice/EDX = {0, 0}
+    #c7          0/subop/copy        0/mod/indirect  5/rm32/.disp32            .             .           .           compute-offsets:word-slice/disp32  0/imm32        # copy to *compute-offsets:word-slice
+    #c7          0/subop/copy        1/mod/*+disp8  5/rm32/.disp32             .             .           . 4/disp8   compute-offsets:word-slice/disp32  0/imm32        # copy to *(compute-offsets:word-slice+4)
+    ba/copy-to-EDX  compute-offsets:word-slice/imm32
+    # next-word(line/ECX, word-slice/EDX)
+    52/push-EDX
+    51/push-ECX
+    e8/call  next-word/disp32
+    # . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+    # if slice-empty?(word/EDX) break
+    # . EAX = slice-empty?(word/EDX)
+    52/push-EDX
+    e8/call  slice-empty?/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+    # . EAX == 1 ?
+    3d/compare-EAX-and  1/imm32
+    # . if so, break
+    #74/jump-if-equal  $compute-offsets:word-loop:end/disp8
+    # if slice-starts-with?(word-slice, "#") continue
+    68/push  "#"/imm32
+    52/push-EDX
+    e8/call  slice-starts-with?/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+    # . EAX == 1 ?
+    3d/compare-EAX-and  1/imm32
+    # . if so, continue
+    74/jump-if-equal  $compute-offsets:word-loop/disp8
+#TODO: implement is-label? and it's block
+    # if slice-equal?(word-slice/EDX, "==")
+    # . EAX = slice-equal?(word-slice/EDX, "==")
+    68/push  "=="/imm32
+    52/push-EDX
+    e8/call  slice-equal?/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+    # . EAX == 0 ?
+    3d/compare-EAX-and  0/imm32
+    # . if so, goto else
+    74/jump-if-equal  $compute-offsets:else/disp8
+    # . or fallthrough
+    # next-word(line/ECX, curr-segment-name)
+    68/push  compute-offsets:curr-segment-name/imm32
+    51/push-ECX
+    e8/call  next-word/disp32
+    # . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+$compute-offsets:else:
+    # width/EAX = compute-width(word-slice/EDX)
+    52/push-EDX
+    e8/call compute-width/disp32
+    # . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+    # segment-offset += width
+    # . EBX = *segment-offset
+    8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   compute-offsets:segment-offset/disp32 # copy *segment-offset to EBX
+    # . *segment-offset/EBX += EAX/width
+    03/add                          3/mod/direct    0/rm32/EAX    .           .             .           3/r32/EBX   .               .                 # add EAX to EBX
+    89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   compute-offsets:segment-offset/disp32 # copy EBX to *compute-offsets:segment-offset
+    # file-offset += width
+    # . EBX = *file-offset
+    8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   compute-offsets:file-offset/disp32 # copy *file-offset to EBX
+    # . *file-offset/EBX += EAX/width
+    03/add                          3/mod/direct    0/rm32/EAX    .           .             .           3/r32/EBX   .               .                 # add EAX to EBX
+    89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   compute-offsets:file-offset/disp32 # copy EBX to *compute-offsets:file-offset
+    e9/jump  $compute-offsets:line-loop/disp32
 $compute-offsets:end:
     # . reclaim locals
     # . restore registers
+
+    5f/pop-to-EDI
+    5e/pop-to-ESI
+    5b/pop-to-EBX
+    5a/pop-to-EDX
+    59/pop-to-ECX
+    58/pop-to-EAX
     # . epilog
     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
     5d/pop-to-EBP
@@ -1678,6 +1809,11 @@ $p_align:
   # compute the starting address for each segment
   0x1000/imm32
 
+compute-offsets:curr-segment-name: 0/imm32/start 0/imm32/end
+compute-offsets:file-offset: 0/imm32
+compute-offsets:segment-offset: 0/imm32
+compute-offsets:word-slice: 0/imm32/start 0/imm32/end
+
 _test-label-slice-start:
   41/A
   41/A
@@ -1686,5 +1822,4 @@ _test-label-slice-end2:
   3A/:
 _test-label-slice-end1:
 
-
 # . . vim:nowrap:textwidth=0
diff --git a/subx/run_one_test.sh b/subx/run_one_test.sh
index c81bd0c4..7ac3e5d0 100755
--- a/subx/run_one_test.sh
+++ b/subx/run_one_test.sh
@@ -4,10 +4,9 @@
 
 if [[ $2 == 'test-'* ]]
 then
-  TEST_NAME=$2 envsubst '$TEST_NAME' < run_one_test.subx > /tmp/run_one_test.subx
-  FILES=$(ls [0-9]*.subx apps/subx-common.subx $1 |sort |uniq)
-  echo $FILES > /tmp/last_run_files
-elif [[ -e /tmp/last_run_files ]]
+  export TEST_NAME=$2
+  echo $TEST_NAME > /tmp/last_test_run
+elif [[ -e /tmp/last_test_run ]]
 then
   FILES=`cat /tmp/last_run_files`
 else
@@ -16,7 +15,12 @@ else
 fi
 
 set -e
-                                      # turn newlines into spaces
-CFLAGS=$CFLAGS subx --debug translate $(echo $FILES) /tmp/run_one_test.subx -o /tmp/a.elf
+if [[ $1 == */* ]]
+then
+  CFLAGS=$CFLAGS ./subx --debug translate [0-9]*.subx  apps/subx-common.subx $1 /tmp/run_one_test.subx -o /tmp/a.elf
+else
+  # don't mention files twice
+  CFLAGS=$CFLAGS ./subx --debug translate [0-9]*.subx apps/subx-common.subx    /tmp/run_one_test.subx -o /tmp/a.elf
+fi
 
-subx --debug --trace run /tmp/a.elf
+./subx --debug --trace run /tmp/a.elf