about summary refs log tree commit diff stats
path: root/subx
diff options
context:
space:
mode:
authorKartik Agaram <vc@akkartik.com>2019-04-03 00:46:32 -0700
committerKartik Agaram <vc@akkartik.com>2019-04-03 00:48:40 -0700
commit1c1889172ef8f7f14fe0c28daaddf4117cafd567 (patch)
tree0d43ff114995bdbe01048cbbf747419f7965b831 /subx
parent89672f8680f372ec05429770f54786597829d4ce (diff)
downloadmu-1c1889172ef8f7f14fe0c28daaddf4117cafd567.tar.gz
5053
write-stream-buffered isn't a clean abstraction. Ignoring the 'read' index
of a stream is a hack. It's just saving us the trouble of a rewind-stream.
So make it a helper of pack.subx rather than part of the standard library.
Diffstat (limited to 'subx')
-rw-r--r--subx/072slice.subx113
-rw-r--r--subx/Readme.md1
-rwxr-xr-xsubx/apps/crenshaw2-1bin17833 -> 17583 bytes
-rwxr-xr-xsubx/apps/crenshaw2-1bbin18392 -> 18142 bytes
-rwxr-xr-xsubx/apps/factorialbin16751 -> 16501 bytes
-rwxr-xr-xsubx/apps/handlebin17526 -> 17276 bytes
-rwxr-xr-xsubx/apps/hexbin20812 -> 20562 bytes
-rwxr-xr-xsubx/apps/packbin34859 -> 35117 bytes
-rw-r--r--subx/apps/pack.subx306
9 files changed, 266 insertions, 154 deletions
diff --git a/subx/072slice.subx b/subx/072slice.subx
index 0ebbf388..a62e87e8 100644
--- a/subx/072slice.subx
+++ b/subx/072slice.subx
@@ -795,119 +795,6 @@ test-write-slice:
     5d/pop-to-EBP
     c3/return
 
-# write an entire stream's contents to a buffered-file
-# ways to do this:
-#   - construct a 'maximal slice' and pass it to write-slice
-#   - 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-buffered:  # f : (address buffered-file), s : (address stream) -> <void>
-    # . 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
-    # 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
-    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
-    # . push s->data
-    8d/copy-address                 1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   0xc/disp8       .                 # copy ESI+12 to EAX
-    50/push-EAX
-    # . ECX = ESP
-    89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-    # write-slice(f, slice)
-    # . . push args
-    51/push-ECX
-    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-    # . . call
-    e8/call  write-slice/disp32
-    # . . discard args
-    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-$write-stream-buffered:end:
-    # . restore locals
-    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-    # . restore registers
-    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-write-stream-buffered:
-    # . 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
-    # . 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
-    # initialize input
-    # . write(_test-tmp-stream, "abcd")
-    # . . push args
-    68/push  "abcd"/imm32
-    68/push  _test-tmp-stream/imm32
-    # . . call
-    e8/call  write/disp32
-    # . . discard args
-    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-    # perform the write-stream-buffered
-    # . write-stream-buffered(_test-buffered-file, _test-tmp-stream)
-    # . . push args
-    68/push  _test-tmp-stream/imm32
-    68/push  _test-buffered-file/imm32
-    # . . call
-    e8/call  write-stream-buffered/disp32
-    # . . discard args
-    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-    # check that the write happened as expected
-    # . flush(_test-buffered-file)
-    # . . push args
-    68/push  _test-buffered-file/imm32
-    # . . call
-    e8/call  flush/disp32
-    # . . discard args
-    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-    # . check-stream-equal(_test-stream, "abcd", msg)
-    # . . push args
-    68/push  "F - test-write-stream-buffered"/imm32
-    68/push  "abcd"/imm32
-    68/push  _test-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
-    # . epilog
-    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-    5d/pop-to-EBP
-    c3/return
-
 == data
 
 _test-slice-data-0:
diff --git a/subx/Readme.md b/subx/Readme.md
index 8970e97b..612081dc 100644
--- a/subx/Readme.md
+++ b/subx/Readme.md
@@ -630,7 +630,6 @@ allocated memory for it.)_
 * `write-stream`: stream -> file
 * `write-buffered`: string -> buffered-file
 * `write-slice`: slice -> buffered-file
-* `write-stream-buffered`: stream -> buffered-file
 * `flush`: buffered-file
 * `print-byte`:  buffered-file, int
 
diff --git a/subx/apps/crenshaw2-1 b/subx/apps/crenshaw2-1
index 98669c9b..0d625f1d 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 cd7739b8..f693a204 100755
--- a/subx/apps/crenshaw2-1b
+++ b/subx/apps/crenshaw2-1b
Binary files differdiff --git a/subx/apps/factorial b/subx/apps/factorial
index 9e6f96f7..97014e36 100755
--- a/subx/apps/factorial
+++ b/subx/apps/factorial
Binary files differdiff --git a/subx/apps/handle b/subx/apps/handle
index 1726afaa..c484fb0b 100755
--- a/subx/apps/handle
+++ b/subx/apps/handle
Binary files differdiff --git a/subx/apps/hex b/subx/apps/hex
index 6d1e57bf..bfbdc771 100755
--- a/subx/apps/hex
+++ b/subx/apps/hex
Binary files differdiff --git a/subx/apps/pack b/subx/apps/pack
index b3a20354..d81654aa 100755
--- a/subx/apps/pack
+++ b/subx/apps/pack
Binary files differdiff --git a/subx/apps/pack.subx b/subx/apps/pack.subx
index 4fff835c..db0c9e7d 100644
--- a/subx/apps/pack.subx
+++ b/subx/apps/pack.subx
@@ -22,7 +22,7 @@
 Entry:  # run tests if necessary, convert stdin if not
 
     # for debugging: run a single test
-#?     e8/call test-convert-instruction-handles-disp8-name/disp32
+#?     e8/call test-convert-data-trailing-comment/disp32
 #?     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
 #?     eb/jump  $main:end/disp8
 
@@ -101,11 +101,11 @@ convert:  # in : (address buffered-file), out : (address buffered-file) -> <void
     #     if (line->write == 0) break             # end of file
     #     var word-slice = next-word(line)
     #     if slice-empty?(word-slice)             # whitespace
-    #       write-stream-buffered(out, line)
+    #       write-stream-data(out, line)
     #     else if (slice-equal?(word-slice, "=="))
     #       word-slice = next-word(line)
     #       in-code? = slice-equal?(word-slice, "code")
-    #       write-stream-buffered(out, line)
+    #       write-stream-data(out, line)
     #     else if (in-code?)
     #       rewind-stream(line)
     #       convert-instruction(line, out)
@@ -198,7 +198,7 @@ $convert:check0:
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 $convert:check1:
-    # if (slice-empty?(word-slice)) write-stream-buffered(out, line)
+    # if (slice-empty?(word-slice)) write-stream-data(out, line)
     # . EAX = slice-empty?(word-slice)
     # . . push args
     52/push-EDX
@@ -206,7 +206,7 @@ $convert: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-buffered(out, line)
+    # . if (EAX != 0) write-stream-data(out, line)
     3d/compare-EAX-and  0/imm32
     0f 85/jump-if-not-equal  $convert:pass-through/disp32
 $convert:check2:
@@ -263,7 +263,7 @@ $convert:check2:
     # if (slice-equal?(word-slice, "=="))
     #   word-slice = next-word(line)
     #   in-code? = slice-equal?(word-slice, "code")
-    #   write-stream-buffered(out, line)
+    #   write-stream-data(out, line)
     # . EAX = slice-equal?(word-slice, "==")
     # . . push args
     68/push  "=="/imm32
@@ -380,12 +380,12 @@ $convert:data:
     # . loop
     e9/jump  $convert:loop/disp32
 $convert:pass-through:
-    # write-stream-buffered(out, line)
+    # write-stream-data(out, line)
     # . . push args
     51/push-ECX
     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
     # . . call
-    e8/call  write-stream-buffered/disp32
+    e8/call  write-stream-data/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     # . loop
@@ -1043,10 +1043,10 @@ convert-data:  # line : (address stream byte), out : (address buffered-file) ->
     #     if slice-empty?(word-slice)                 # end of file (maybe including trailing whitespace)
     #       break  # skip emitting some whitespace
     #     if slice-starts-with?(word-slice, "#")      # comment
-    #       write-stream-buffered(out, line)
+    #       write-slice(out, word-slice)
     #       break
     #     if slice-ends-with?(word-slice, ":")        # label
-    #       write-stream-buffered(out, line)
+    #       write-stream-data(out, line)
     #       break
     #     if has-metadata?(word-slice, "imm32")
     #       emit(out, word-slice, 4)
@@ -1167,30 +1167,52 @@ $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) pass through
+    # . if (EAX != 0) break
     3d/compare-EAX-and  0/imm32
-    75/jump-if-not-equal  $convert-data:break/disp8
-$convert-data:check1:
-    # if (slice-starts-with?(word-slice, "#")) write-stream-buffered(out, line)
+    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
     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
     # . c/EAX = *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 (EAX != '#') goto next check
     3d/compare-EAX-and  0x23/imm32/hash
-    74/jump-if-equal  $convert-data:pass-line-through/disp8
-$convert-data:check2:
-    # if (slice-ends-with?(word-slice, ":")) write-stream-buffered(out, line)
+    75/jump-if-not-equal  $convert-data:check-for-label/disp8
+$convert-data:comment:
+    # write-slice(out, word-slice)
+    # . . push args
+    51/push-ECX
+    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+    # . . call
+    e8/call  write-slice/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+    # break
+    75/jump-if-not-equal  $convert-data:break/disp8
+$convert-data:check-for-label:
+    # if (slice-ends-with?(word-slice, ":"))
     # . end/EDX = 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)
     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 (EAX != ':') goto next check
     3d/compare-EAX-and  0x3a/imm32/colon
-    74/jump-if-equal  $convert-data:pass-line-through/disp8
-$convert-data:check3:
+    75/jump-if-not-equal  $convert-data:check-for-imm32/disp8
+$convert-data:label:
+    # write-stream-data(out, line)
+    # . . push args
+    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+    # . . call
+    e8/call  write-stream-data/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+    # break
+    75/jump-if-not-equal  $convert-data:break/disp8
+$convert-data:check-for-imm32:
     # if (has-metadata?(word-slice, "imm32"))
     # . EAX = has-metadata?(ECX, "imm32")
     # . . push args
@@ -1225,16 +1247,6 @@ $convert-data:single-byte:
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
     e9/jump  $convert-data:loop/disp32
-$convert-data:pass-line-through:
-    # write-stream-buffered(out, line)
-    # . . push args
-    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
-    # . . call
-    e8/call  write-stream-buffered/disp32
-    # . . discard args
-    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-    # break
 $convert-data:break:
 $convert-data:end:
     # . reclaim locals
@@ -1828,6 +1840,108 @@ test-convert-data-multiple-words:
     5d/pop-to-EBP
     c3/return
 
+test-convert-data-trailing-comment:
+    # Trailing comments in data segment get appropriately ignored.
+    # . prolog
+    55/push-EBP
+    89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+    # setup
+    # . clear-stream(_test-input-stream)
+    # . . push args
+    68/push  _test-input-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-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
+    # initialize input
+    # . write(_test-input-stream, "30/imm32 # comment")
+    # . . push args
+    68/push  "30/imm32 # comment"/imm32
+    68/push  _test-input-stream/imm32
+    # . . call
+    e8/call  write/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+    # convert-data(_test-input-stream, _test-output-buffered-file)
+    # . . push args
+    68/push  _test-output-buffered-file/imm32
+    68/push  _test-input-stream/imm32
+    # . . call
+    e8/call  convert-data/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+    # check output
+    # . flush(_test-output-buffered-file)
+    # . . push args
+    68/push  _test-output-buffered-file/imm32
+    # . . call
+    e8/call  flush/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+#?     # dump output {{{
+#?     # . write(2/stderr, "^")
+#?     # . . push args
+#?     68/push  "^"/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, _test-output-stream)
+#?     # . . push args
+#?     68/push  _test-output-stream/imm32
+#?     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, "$")
+#?     # . . push args
+#?     68/push  "$"/imm32
+#?     68/push  2/imm32/stderr
+#?     # . . call
+#?     e8/call  write/disp32
+#?     # . . discard args
+#?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+#?     # . write(2/stderr, "\n")
+#?     # . . push args
+#?     68/push  Newline/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
+#?     # }}}
+    # . check-stream-equal(_test-output-stream, "30 00 00 00 # comment", msg)
+    # . . push args
+    68/push  "F - test-convert-data-trailing-comment"/imm32
+    68/push  "30 00 00 00 # comment"/imm32
+    68/push  _test-output-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
+    # . epilog
+    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+    5d/pop-to-EBP
+    c3/return
+
 # pack an instruction, following the C++ version
 #
 # zero error handling at the moment (continuing to rely on the C++ version for that):
@@ -1843,13 +1957,13 @@ convert-instruction:  # line : (address stream byte), out : (address buffered-fi
     #   # some early exits
     #   var word-slice = next-word(line)
     #   if slice-empty?(word-slice)
-    #     write-stream-buffered(out, line)
+    #     write-stream-data(out, line)
     #     return
     #   if slice-starts-with?(word-slice, "#")
-    #     write-stream-buffered(out, line)
+    #     write-stream-data(out, line)
     #     return
     #   if slice-ends-with?(word-slice, ":")
-    #     write-stream-buffered(out, line)
+    #     write-stream-data(out, line)
     #     return
     #   # really convert
     #   emit-opcodes(line, out)
@@ -1891,7 +2005,7 @@ $convert-instruction:check0:
     3d/compare-EAX-and  0/imm32
     75/jump-if-not-equal  $convert-instruction:pass-through/disp8
 $convert-instruction:check1:
-    # if (slice-starts-with?(word-slice, "#")) write-stream-buffered(out, line)
+    # if (slice-starts-with?(word-slice, "#")) write-stream-data(out, line)
     # . start/EDX = word-slice->start
     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
     # . c/EAX = *start
@@ -1901,7 +2015,7 @@ $convert-instruction:check1:
     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-buffered(out, line)
+    # if (slice-ends-with?(word-slice, ":")) write-stream-data(out, line)
     # . end/EDX = 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)
@@ -1911,12 +2025,12 @@ $convert-instruction:check2:
     3d/compare-EAX-and  0x3a/imm32/colon
     75/jump-if-not-equal  $convert-instruction:really-convert/disp8
 $convert-instruction:pass-through:
-    # write-stream-buffered(out, line)
+    # write-stream-data(out, line)
     # . . push args
     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
     # . . call
-    e8/call  write-stream-buffered/disp32
+    e8/call  write-stream-data/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     # return
@@ -3391,12 +3505,12 @@ emit-line-in-comment:  # line : (address stream byte), out : (address buffered-f
     e8/call  write-buffered/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-    # write-stream-buffered(out, line)
+    # write-stream-data(out, line)
     # . . push args
     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
     # . . call
-    e8/call  write-stream-buffered/disp32
+    e8/call  write-stream-data/disp32
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 $emit-line-in-comment:end:
@@ -7523,6 +7637,118 @@ test-emit-hex-negative:
     # . end
     c3/return
 
+# write an entire stream's contents to a buffered-file
+# ways to do this:
+#   - construct a 'maximal slice' and pass it to write-slice
+#   - 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) -> <void>
+    # . 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
+    # 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
+    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
+    # . push s->data
+    8d/copy-address                 1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   0xc/disp8       .                 # copy ESI+12 to EAX
+    50/push-EAX
+    # . ECX = ESP
+    89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+    # write-slice(f, slice)
+    # . . push args
+    51/push-ECX
+    ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+    # . . call
+    e8/call  write-slice/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+$write-stream-data:end:
+    # . restore locals
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+    # . restore registers
+    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-write-stream-data:
+    # . 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
+    # . 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
+    # initialize input
+    # . write(_test-tmp-stream, "abcd")
+    # . . push args
+    68/push  "abcd"/imm32
+    68/push  _test-tmp-stream/imm32
+    # . . call
+    e8/call  write/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+    # write-stream-data(_test-buffered-file, _test-tmp-stream)
+    # . . push args
+    68/push  _test-tmp-stream/imm32
+    68/push  _test-buffered-file/imm32
+    # . . call
+    e8/call  write-stream-data/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+    # check that the write happened as expected
+    # . flush(_test-buffered-file)
+    # . . push args
+    68/push  _test-buffered-file/imm32
+    # . . call
+    e8/call  flush/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+    # . check-stream-equal(_test-stream, "abcd", msg)
+    # . . push args
+    68/push  "F - test-write-stream-data"/imm32
+    68/push  "abcd"/imm32
+    68/push  _test-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
+    # . epilog
+    89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+    5d/pop-to-EBP
+    c3/return
+
 # shortcut for parse-hex-int(next-token-from-slice(word->start, word->end, '/'))
 parse-datum-of-word:  # word : (address slice) -> value/EAX
     # . prolog