about summary refs log tree commit diff stats
path: root/subx
diff options
context:
space:
mode:
authorKartik Agaram <vc@akkartik.com>2019-07-16 23:34:33 -0700
committerKartik Agaram <vc@akkartik.com>2019-07-16 23:40:25 -0700
commitc2a74205d690862d4df31682aa5bbe992227fda1 (patch)
treeb570f38927e21af0ab05d4864cfd3676bb2b0880 /subx
parent70a999aaebbd05873be56b0460e0d4182397cfea (diff)
downloadmu-c2a74205d690862d4df31682aa5bbe992227fda1.tar.gz
5408
Bugfix ten: type error in `convert`. I was calling `rewind-stream` on a
`buffered-file`.

examples/ex1 is now just one nibble off the canonical.

I *have* found one missing feature in the self-hosted translator,
though: dquotes doesn't support newlines in strings, even though the C++
version does. dquotes parses them right, but the value initialized in
the data segment is wrong.
Diffstat (limited to 'subx')
-rw-r--r--subx/010---vm.cc6
-rw-r--r--subx/071read-line.subx171
-rw-r--r--subx/077slurp.subx161
-rwxr-xr-xsubx/apps/assortbin33376 -> 34142 bytes
-rwxr-xr-xsubx/apps/crenshaw2-1bin23839 -> 24605 bytes
-rwxr-xr-xsubx/apps/crenshaw2-1bbin24398 -> 25164 bytes
-rwxr-xr-xsubx/apps/dquotesbin39729 -> 40495 bytes
-rwxr-xr-xsubx/apps/factorialbin22755 -> 23521 bytes
-rwxr-xr-xsubx/apps/handlebin23614 -> 24380 bytes
-rwxr-xr-xsubx/apps/hexbin26848 -> 27614 bytes
-rwxr-xr-xsubx/apps/packbin45742 -> 46508 bytes
-rwxr-xr-xsubx/apps/surveybin40474 -> 41172 bytes
-rw-r--r--subx/apps/survey.subx144
13 files changed, 418 insertions, 64 deletions
diff --git a/subx/010---vm.cc b/subx/010---vm.cc
index 6ee41c9b..b54dd9d5 100644
--- a/subx/010---vm.cc
+++ b/subx/010---vm.cc
@@ -202,7 +202,9 @@ inline uint8_t* mem_addr_u8(uint32_t addr) {
   }
   if (result == NULL) {
     if (Trace_file) Trace_file.flush();
-    raise << "Tried to access uninitialized memory at address 0x" << HEXWORD << addr << '\n' << die();
+    raise << "Tried to access uninitialized memory at address 0x" << HEXWORD << addr << '\n' << end();
+    DUMP("");
+    exit(1);
   }
   return result;
 }
@@ -222,6 +224,8 @@ inline uint32_t* mem_addr_u32(uint32_t addr) {
     if (Trace_file) Trace_file.flush();
     raise << "Tried to access uninitialized memory at address 0x" << HEXWORD << addr << '\n' << end();
     raise << "The entire 4-byte word should be initialized and lie in a single segment.\n" << end();
+    DUMP("");
+    exit(1);
   }
   return result;
 }
diff --git a/subx/071read-line.subx b/subx/071read-line.subx
index 9189773c..0add5568 100644
--- a/subx/071read-line.subx
+++ b/subx/071read-line.subx
@@ -217,4 +217,175 @@ test-read-line-buffered-reads-final-line-until-Eof:
     # end
     c3/return
 
+# 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) -> <void>
+    # pseudocode:
+    #   while true
+    #     if (s->write >= s->length) abort
+    #     if (f->read >= f->write) break
+    #     AL = f->data[f->read]
+    #     s->data[s->write] = AL
+    #     ++f->read
+    #     ++s->write
+    #     if (AL == '\n') break
+    # . 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
+    52/push-EDX
+    56/push-ESI
+    57/push-EDI
+    # ESI = f
+    8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
+    # ECX = 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
+    # EDX = s->write
+    8b/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           2/r32/EDX   .               .                 # copy *EDI to EDX
+$read-line:loop:
+    # if (s->write >= s->length) abort
+    3b/compare                      1/mod/*+disp8   7/rm32/EDI    .           .             .           2/r32/EDX   8/disp8         .                 # compare EDX with *(EDI+8)
+    0f 8d/jump-if-greater-or-equal  $read-line:abort/disp32
+    # if (f->read >= f->write) break
+    3b/compare                      0/mod/indirect  6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # compare ECX with *ESI
+    7d/jump-if-greater-or-equal  $read-line:end/disp8
+    # AL = 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    0xc/disp8       .                 # copy byte at *(ESI+ECX+12) to AL
+    # s->data[s->write] = AL
+    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
+    # ++s->write
+    42/increment-EDX
+    # if (AL == '\n') return
+    3d/compare-EAX-and  0xa/imm32
+    0f 85/jump-if-not-equal  $read-line:loop/disp32
+$read-line:end:
+    # save f->read
+    89/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy ECX to *(ESI+4)
+    # save s->write
+    89/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           2/r32/EDX   .               .                 # copy EDX to *EDI
+    # . restore registers
+    5f/pop-to-EDI
+    5e/pop-to-ESI
+    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
+    c3/return
+
+$read-line:abort:
+    # . _write(2/stderr, error)
+    # . . push args
+    68/push  "read-line: line too long\n"/imm32
+    68/push  2/imm32/stderr
+    # . . call
+    e8/call  _write/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+    # . syscall(exit, 1)
+    bb/copy-to-EBX  1/imm32
+    b8/copy-to-EAX  1/imm32/exit
+    cd/syscall  0x80/imm8
+    # never gets here
+
+test-read-line:
+    # - check that read-line stops at a newline
+    # 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
+    # . clear-stream(_test-tmp-stream)
+    # . . push args
+    68/push  _test-tmp-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
+    # write(_test-stream, "ab\ncd")
+    # . . push args
+    68/push  "ab\ncd"/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
+    # read a line from _test-stream into _test-tmp-stream
+    # . EAX = read-line(_test-stream, _test-tmp-stream)
+    # . . push args
+    68/push  _test-tmp-stream/imm32
+    68/push  _test-stream/imm32
+    # . . call
+    e8/call  read-line/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+    # check-next-stream-line-equal(_test-tmp-stream, "ab", msg)
+    # . . push args
+    68/push  "F - test-read-line"/imm32
+    68/push  "ab"/imm32
+    68/push  _test-tmp-stream/imm32
+    # . . call
+    e8/call  check-next-stream-line-equal/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+    # end
+    c3/return
+
+test-read-line-reads-final-line-until-Eof:
+    # 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
+    # . clear-stream(_test-tmp-stream)
+    # . . push args
+    68/push  _test-tmp-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
+    # write(_test-stream, "cd")
+    # . . push args
+    68/push  "cd"/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
+    # read a line from _test-stream into _test-tmp-stream
+    # . EAX = read-line(_test-stream, _test-tmp-stream)
+    # . . push args
+    68/push  _test-tmp-stream/imm32
+    68/push  _test-stream/imm32
+    # . . call
+    e8/call  read-line/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+    # check-stream-equal(_test-tmp-stream, "cd", msg)
+    # . . push args
+    68/push  "F - test-read-line-reads-final-line-until-Eof"/imm32
+    68/push  "cd"/imm32
+    68/push  _test-tmp-stream/imm32
+    # . . call
+    e8/call  check-stream-equal/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+    # end
+    c3/return
+
 # . . vim:nowrap:textwidth=0
diff --git a/subx/077slurp.subx b/subx/077slurp.subx
new file mode 100644
index 00000000..2bf203da
--- /dev/null
+++ b/subx/077slurp.subx
@@ -0,0 +1,161 @@
+== code
+#   instruction                     effective address                                                   register    displacement    immediate
+# . 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 all bytes from 'f' and store them into 's'
+# abort if 's' is too small
+slurp:  # f : (address buffered-file), s : (address stream byte) -> <void>
+    # pseudocode:
+    #   while true
+    #     if (s->write >= s->length) abort
+    #     if (f->read >= f->write) populate stream from file
+    #     if (f->write == 0) break
+    #     AL = f->data[f->read]
+    #     s->data[s->write] = AL
+    #     ++f->read
+    #     ++s->write
+    # . 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
+    52/push-EDX
+    56/push-ESI
+    57/push-EDI
+    # ESI = f
+    8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
+    # ECX = f->read
+    8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   8/disp8         .                 # copy *(ESI+8) to ECX
+    # EDI = s
+    8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   0xc/disp8       .                 # copy *(EBP+12) to EDI
+    # EDX = s->write
+    8b/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           2/r32/EDX   .               .                 # copy *EDI to EDX
+$slurp:loop:
+    # if (s->write >= s->length) abort
+    3b/compare                      1/mod/*+disp8   7/rm32/EDI    .           .             .           2/r32/EDX   8/disp8         .                 # compare EDX with *(EDI+8)
+    7d/jump-if-greater-or-equal  $slurp:abort/disp8
+    # if (f->read >= f->write) populate stream from file
+    3b/compare                      1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # compare ECX with *(ESI+4)
+    7c/jump-if-lesser  $slurp:from-stream/disp8
+    # . clear-stream(stream = f+4)
+    # . . push args
+    8d/copy-address                 1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy ESI+4 to EAX
+    50/push-EAX
+    # . . call
+    e8/call  clear-stream/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+    # . f->read must now be 0; update its cache at ECX
+    31/xor                          3/mod/direct    1/rm32/ECX    .           .             .           1/r32/ECX   .               .                 # clear ECX
+    # . EAX = read(f->fd, stream = f+4)
+    # . . push args
+    50/push-EAX
+    ff          6/subop/push        0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # push *ESI
+    # . . call
+    e8/call  read/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+    # if (f->write == 0) break
+    # since f->read was initially 0, EAX is the same as f->write
+    # . if (EAX == 0) return true
+    3d/compare-EAX-and  0/imm32
+    74/jump-if-equal  $slurp:end/disp8
+$slurp:from-stream:
+    # AL = 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
+    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
+    # ++s->write
+    42/increment-EDX
+    eb/jump  $slurp:loop/disp8
+$slurp:end:
+    # save f->read
+    89/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   8/disp8         .                 # copy ECX to *(ESI+8)
+    # save s->write
+    89/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           2/r32/EDX   .               .                 # copy EDX to *EDI
+    # . restore registers
+    5f/pop-to-EDI
+    5e/pop-to-ESI
+    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
+    c3/return
+
+$slurp:abort:
+    # . _write(2/stderr, error)
+    # . . push args
+    68/push  "slurp: destination too small\n"/imm32
+    68/push  2/imm32/stderr
+    # . . call
+    e8/call  _write/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+    # . syscall(exit, 1)
+    bb/copy-to-EBX  1/imm32
+    b8/copy-to-EAX  1/imm32/exit
+    cd/syscall  0x80/imm8
+    # never gets here
+
+test-slurp:
+    # 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
+    # . clear-stream(_test-buffered-file+4)
+    # . . push args
+    b8/copy-to-EAX  _test-buffered-file/imm32
+    05/add-to-EAX  4/imm32
+    50/push-EAX
+    # . . call
+    e8/call  clear-stream/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+    # . clear-stream(_test-tmp-stream)
+    # . . push args
+    68/push  _test-tmp-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
+    # write(_test-stream, "ab\ncd")
+    # . . push args
+    68/push  "ab\ncd"/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
+    # read a line from _test-stream (buffered by _test-buffered-file) into _test-tmp-stream
+    # . EAX = slurp(_test-buffered-file, _test-tmp-stream)
+    # . . push args
+    68/push  _test-tmp-stream/imm32
+    68/push  _test-buffered-file/imm32
+    # . . call
+    e8/call  slurp/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+    # check-stream-equal(_test-tmp-stream, "ab\ncd", msg)
+    # . . push args
+    68/push  "F - test-slurp"/imm32
+    68/push  "ab\ncd"/imm32
+    68/push  _test-tmp-stream/imm32
+    # . . call
+    e8/call  check-stream-equal/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+    # end
+    c3/return
+
+# . . vim:nowrap:textwidth=0
diff --git a/subx/apps/assort b/subx/apps/assort
index c903ec47..b8cfbe5c 100755
--- a/subx/apps/assort
+++ b/subx/apps/assort
Binary files differdiff --git a/subx/apps/crenshaw2-1 b/subx/apps/crenshaw2-1
index e5e6560c..cf275a97 100755
--- a/subx/apps/crenshaw2-1
+++ b/subx/apps/crenshaw2-1
Binary files differdiff --git a/subx/apps/crenshaw2-1b b/subx/apps/crenshaw2-1b
index eebd667a..f26acdb6 100755
--- a/subx/apps/crenshaw2-1b
+++ b/subx/apps/crenshaw2-1b
Binary files differdiff --git a/subx/apps/dquotes b/subx/apps/dquotes
index 5e277311..68dac8fc 100755
--- a/subx/apps/dquotes
+++ b/subx/apps/dquotes
Binary files differdiff --git a/subx/apps/factorial b/subx/apps/factorial
index 55a59123..7364a397 100755
--- a/subx/apps/factorial
+++ b/subx/apps/factorial
Binary files differdiff --git a/subx/apps/handle b/subx/apps/handle
index 3ea729c6..7861645c 100755
--- a/subx/apps/handle
+++ b/subx/apps/handle
Binary files differdiff --git a/subx/apps/hex b/subx/apps/hex
index 315e855c..bc838186 100755
--- a/subx/apps/hex
+++ b/subx/apps/hex
Binary files differdiff --git a/subx/apps/pack b/subx/apps/pack
index 9c3ed916..705006e5 100755
--- a/subx/apps/pack
+++ b/subx/apps/pack
Binary files differdiff --git a/subx/apps/survey b/subx/apps/survey
index c413a9f4..ea50364a 100755
--- a/subx/apps/survey
+++ b/subx/apps/survey
Binary files differdiff --git a/subx/apps/survey.subx b/subx/apps/survey.subx
index b7ce08dd..da01407b 100644
--- a/subx/apps/survey.subx
+++ b/subx/apps/survey.subx
@@ -79,22 +79,14 @@ Entry:
     eb/jump  $main:end/disp8
 $run-main:
     # - otherwise convert stdin
-    # var ed/EAX : 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()
-    # . ed->target = 0
-    c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # copy to *EAX
-    # return convert(Stdin, 1/stdout, 2/stderr, ed)
-    # . . push args
-    50/push-EAX/ed
-    68/push  Stderr/imm32
+    # return convert(Stdin, Stdout)
+    # . . push args
     68/push  Stdout/imm32
     68/push  Stdin/imm32
     # . . call
     e8/call  convert/disp32
     # . . discard args
-    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     # . syscall(exit, 0)
     bb/copy-to-EBX  0/imm32
 $main:end:
@@ -108,8 +100,10 @@ $main:end:
 #   labels: (address stream {string, label-info})         (16 bytes per row)
 # these are all inefficient; use sequential scans for lookups
 
-convert:  # in : (address buffered-file), out : (address buffered-file) -> <void>
+convert:  # infile : (address buffered-file), out : (address buffered-file) -> <void>
     # pseudocode
+    #   var in : (address stream byte) = stream(4096)
+    #   slurp(infile, in)
     #   var segments = new-stream(10 rows, 16 bytes each)
     #   var labels = new-stream(512 rows, 16 bytes each)
     #   compute-offsets(in, segments, labels)
@@ -123,6 +117,7 @@ convert:  # in : (address buffered-file), out : (address buffered-file) -> <void
     # . save registers
     51/push-ECX
     52/push-EDX
+    56/push-ESI
     # var segments/ECX = stream(10 * 16)
     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xa0/imm32        # subtract from ESP
     68/push  0xa0/imm32/length
@@ -135,6 +130,62 @@ convert:  # in : (address buffered-file), out : (address buffered-file) -> <void
     68/push  0/imm32/read
     68/push  0/imm32/write
     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
+    # var in/ESI = stream(4096 * 1)
+    81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x1000/imm32      # subtract from ESP
+    68/push  0x1000/imm32/length
+    68/push  0/imm32/read
+    68/push  0/imm32/write
+    89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           4/r32/ESP   .               .                 # copy ESP to ESI
+#?     # write(2/stderr, "slurp in\n") {{{
+#?     # . . push args
+#?     68/push  "compute-offsets\n"/imm32
+#?     68/push  2/imm32/stderr
+#?     # . . call
+#?     e8/call  write/disp32
+#?     # . . discard args
+#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+#?     # }}}
+    # slurp(infile, in)
+    # . . push args
+    56/push-ESI
+    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+    # . . call
+    e8/call  slurp/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+#?     # dump in {{{
+#?     # . write(2/stderr, "in: ")
+#?     # . . push args
+#?     68/push  "in: "/imm32
+#?     68/push  2/imm32/stderr
+#?     # . . call
+#?     e8/call  write/disp32
+#?     # . . discard args
+#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+#?     # write-stream(2/stderr, in)
+#?     # . . push args
+#?     56/push-ESI
+#?     68/push  2/imm32/stderr
+#?     # . . call
+#?     e8/call  write-stream/disp32
+#?     # . . discard args
+#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+#?     # . write(2/stderr, "$\n")
+#?     # . . push args
+#?     68/push  "$\n"/imm32
+#?     68/push  2/imm32/stderr
+#?     # . . call
+#?     e8/call  write/disp32
+#?     # . . discard args
+#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+#?     # . rewind-stream(in)
+#?     # . . push args
+#?     56/push-ESI
+#?     # . . call
+#?     e8/call  rewind-stream/disp32
+#?     # . . discard args
+#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+#?     # }}}
 #?     # write(2/stderr, "compute-offsets\n") {{{
 #?     # . . push args
 #?     68/push  "compute-offsets\n"/imm32
@@ -148,7 +199,7 @@ convert:  # in : (address buffered-file), out : (address buffered-file) -> <void
     # . . push args
     52/push-EDX
     51/push-ECX
-    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+    56/push-ESI
     # . . call
     e8/call  compute-offsets/disp32
     # . . discard args
@@ -172,7 +223,7 @@ convert:  # in : (address buffered-file), out : (address buffered-file) -> <void
     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x8/imm32         # add to ESP
     # rewind-stream(in)
     # . . push args
-    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+    56/push-ESI
     # . . call
     e8/call  rewind-stream/disp32
     # . . discard args
@@ -263,7 +314,7 @@ convert:  # in : (address buffered-file), out : (address buffered-file) -> <void
     52/push-EDX
     51/push-ECX
     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)
+    56/push-ESI
     # . . call
     e8/call  emit-output/disp32
     # . . discard args
@@ -277,8 +328,9 @@ convert:  # in : (address buffered-file), out : (address buffered-file) -> <void
     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 $convert:end:
     # . reclaim locals
-    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x214/imm32       # add to ESP
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x30a0/imm32      # add to ESP
     # . restore registers
+    5e/pop-to-ESI
     5a/pop-to-EDX
     59/pop-to-ECX
     # . epilog
@@ -474,7 +526,7 @@ compute-offsets:segment-tmp:  # slice
 
 == code
 
-compute-offsets:  # in : (address buffered-file), segments : (address stream {string, segment-info}), labels : (address stream {string, label-info})
+compute-offsets:  # in : (address stream), segments : (address stream {string, segment-info}), labels : (address stream {string, label-info})
     # skeleton:
     #   for lines in 'in'
     #     for words in line
@@ -489,7 +541,7 @@ compute-offsets:  # in : (address buffered-file), segments : (address stream {st
     #   var line = new-stream(512, 1)
     #   while true                                  # line loop
     #     clear-stream(line)
-    #     read-line-buffered(in, line)
+    #     read-line(in, line)
     #     if (line->write == 0) break               # end of file
     #     while true                                # word loop
     #       word-slice = next-word(line)
@@ -562,10 +614,10 @@ $compute-offsets:line-loop:
     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)
+    # read-line(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
+    e8/call  read-line/disp32
     # . discard args
     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     # if (line->write == 0) break
@@ -1093,31 +1145,6 @@ 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
-    # . clear-stream(_test-input-buffered-file+4)
-    # . . push args
-    b8/copy-to-EAX  _test-input-buffered-file/imm32
-    05/add-to-EAX  4/imm32
-    50/push-EAX
-    # . . call
-    e8/call  clear-stream/disp32
-    # . . discard args
-    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-    # . clear-stream(_test-output-stream)
-    # . . push args
-    68/push  _test-output-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
-    # . clear-stream(_test-output-buffered-file+4)
-    # . . push args
-    b8/copy-to-EAX  _test-output-buffered-file/imm32
-    05/add-to-EAX  4/imm32
-    50/push-EAX
-    # . . call
-    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)
     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x20/imm32        # subtract from ESP
     68/push  0x20/imm32/length
@@ -1179,11 +1206,11 @@ test-compute-offsets:
     e8/call  write/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-    # compute-offsets(_test-input-buffered-file, segments, labels)
+    # compute-offsets(_test-input-stream, segments, labels)
     # . . push args
     52/push-EDX
     51/push-ECX
-    68/push  _test-input-buffered-file/imm32
+    68/push  _test-input-stream/imm32
     # . . call
     e8/call  compute-offsets/disp32
     # . . discard args
@@ -1671,7 +1698,7 @@ test-compute-addresses:
     5d/pop-to-EBP
     c3/return
 
-emit-output:  # in : (address buffered-file), out : (address buffered-file), segments : (address stream {string, segment-info}), labels : (address stream {string, label-info})
+emit-output:  # in : (address stream), 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)
@@ -1722,14 +1749,14 @@ $emit-output:end:
     5d/pop-to-EBP
     c3/return
 
-emit-segments:  # in : (address buffered-file), out : (address buffered-file), segments : (address stream {string, segment-info}), labels : (address stream {string, label-info})
+emit-segments:  # in : (address stream), 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 = new-stream(512, 1)
     #   line-loop:
     #   while true
     #     clear-stream(line)
-    #     read-line-buffered(in, line)
+    #     read-line(in, line)
     #     if (line->write == 0) break               # end of file
     #     offset-of-next-instruction += num-bytes(line)
     #     while true
@@ -1804,12 +1831,12 @@ $emit-segments:line-loop:
     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)
+    # read-line(in, line)
     # . . push args
     51/push-ECX
     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
     # . . call
-    e8/call  read-line-buffered/disp32
+    e8/call  read-line/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 #?     # dump line {{{
@@ -2259,15 +2286,6 @@ test-emit-segments:
     e8/call  clear-stream/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-    # . clear-stream(_test-input-buffered-file+4)
-    # . . push args
-    b8/copy-to-EAX  _test-input-buffered-file/imm32
-    05/add-to-EAX  4/imm32
-    50/push-EAX
-    # . . call
-    e8/call  clear-stream/disp32
-    # . . discard args
-    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     # . clear-stream(_test-output-stream)
     # . . push args
     68/push  _test-output-stream/imm32
@@ -2394,12 +2412,12 @@ test-emit-segments:
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
     # component under test
-    # . emit-segments(_test-input-buffered-file, _test-output-buffered-file, segments, labels)
+    # . emit-segments(_test-input-stream, _test-output-buffered-file, segments, labels)
     # . . push args
     52/push-EDX
     51/push-ECX
     68/push  _test-output-buffered-file/imm32
-    68/push  _test-input-buffered-file/imm32
+    68/push  _test-input-stream/imm32
     # . . call
     e8/call  emit-segments/disp32
     # . . discard args