about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik Agaram <vc@akkartik.com>2020-11-11 22:58:29 -0800
committerKartik Agaram <vc@akkartik.com>2020-11-11 23:25:55 -0800
commit307745bcc2efbd4c5b8191b45d5e1885be1c0089 (patch)
treea0da43e2258fea744143b24e2560e042c481125a
parent7d15b0884eb57657b130b98f3701544fedaf4b7f (diff)
downloadmu-307745bcc2efbd4c5b8191b45d5e1885be1c0089.tar.gz
7225
Both manual tests described in commit 7222 now work.

To make them work I had to figure out how to copy a file. It
requires a dependency on a new syscall: lseek.
-rw-r--r--312copy.subx68
-rw-r--r--400.mu2
-rwxr-xr-xapps/assortbin46541 -> 46549 bytes
-rwxr-xr-xapps/bracesbin48596 -> 48604 bytes
-rwxr-xr-xapps/callsbin53447 -> 53455 bytes
-rwxr-xr-xapps/crenshaw2-1bin45882 -> 45890 bytes
-rwxr-xr-xapps/crenshaw2-1bbin46429 -> 46437 bytes
-rwxr-xr-xapps/dquotesbin50163 -> 50171 bytes
-rwxr-xr-xapps/ex1bin221 -> 229 bytes
-rwxr-xr-xapps/ex10bin288 -> 296 bytes
-rwxr-xr-xapps/ex11bin1202 -> 1210 bytes
-rwxr-xr-xapps/ex12bin258 -> 266 bytes
-rwxr-xr-xapps/ex13bin235 -> 243 bytes
-rwxr-xr-xapps/ex2bin227 -> 235 bytes
-rwxr-xr-xapps/ex3bin239 -> 247 bytes
-rwxr-xr-xapps/ex4bin260 -> 268 bytes
-rwxr-xr-xapps/ex5bin260 -> 268 bytes
-rwxr-xr-xapps/ex6bin256 -> 264 bytes
-rwxr-xr-xapps/ex7bin390 -> 398 bytes
-rwxr-xr-xapps/ex8bin258 -> 266 bytes
-rwxr-xr-xapps/ex9bin252 -> 260 bytes
-rwxr-xr-xapps/factorialbin44992 -> 45000 bytes
-rwxr-xr-xapps/hexbin48721 -> 48729 bytes
-rwxr-xr-xapps/mubin550700 -> 550708 bytes
-rwxr-xr-xapps/packbin59231 -> 59239 bytes
-rwxr-xr-xapps/sigilsbin60965 -> 60973 bytes
-rwxr-xr-xapps/surveybin56473 -> 56481 bytes
-rwxr-xr-xapps/testsbin45332 -> 45340 bytes
-rw-r--r--apps/tile/data.mu2
-rw-r--r--apps/tile/environment.mu57
-rw-r--r--apps/tile/main.mu1
-rw-r--r--apps/tile/rpn.mu14
-rw-r--r--apps/tile/table.mu54
-rw-r--r--apps/tile/value-stack.mu34
-rw-r--r--apps/tile/value.mu115
-rw-r--r--init.linux6
36 files changed, 293 insertions, 60 deletions
diff --git a/312copy.subx b/312copy.subx
new file mode 100644
index 00000000..11bfbc3e
--- /dev/null
+++ b/312copy.subx
@@ -0,0 +1,68 @@
+== code
+
+copy-array-object:  # src: (addr array T), dest-ah: (addr handle array T)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    #
+    (copy-array Heap *(ebp+8) *(ebp+0xc))
+$copy-array-object:end:
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+# create an independent copy of file src into dest
+# there's no way to do this without knowing the filename
+copy-file:  # src: (addr buffered-file), dest-ah: (addr handle buffered-file), filename: (addr array byte)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    56/push-esi
+    57/push-edi
+    # esi = src
+    8b/-> *(ebp+8) 6/r32/esi
+    # var n/ecx: int = src->buffer->size + 16
+    8b/-> *(esi+0xc) 0/r32/eax
+    05/add-to-eax 0x10/imm32  # buffered-file fields before buffer contents
+    89/<- %ecx 0/r32/eax
+    #
+    (allocate Heap %ecx *(ebp+0xc))
+    # var dest/edi: (addr buffered-file) = lookup(*dest-ah)
+    8b/-> *(ebp+0xc) 0/r32/eax
+    (lookup *eax *(eax+4))  # => eax
+    89/<- %edi 0/r32/eax
+    #
+    (copy-bytes %esi %edi %ecx)
+    # var offset/ecx: int = lseek(src->fd, 0, SEEK_CUR)
+    8b/-> *esi 3/r32/ebx
+    b9/copy-to-ecx 0/imm32/offset
+    ba/copy-to-edx 1/imm32/whence:SEEK_CUR
+    (syscall_lseek)
+    89/<- %ecx 0/r32/eax
+    # at this point dest is identical to src, including file descriptor. Now
+    # create an independent copy of the file descriptor
+    (open-fd *(ebp+0x10) 0)  # false => eax
+    89/<- *edi 0/r32/eax
+    # replicate offset in the new fd
+    89/<- %ebx 0/r32/eax  # fd
+    51/push-ecx  # offset
+    ba/copy-to-edx 0/imm32/whence:SEEK_SET
+    (syscall_lseek)
+$copy-file:end:
+    # . 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
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
diff --git a/400.mu b/400.mu
index 75d21c0b..d3442cdf 100644
--- a/400.mu
+++ b/400.mu
@@ -183,3 +183,5 @@ sig stream-first s: (addr stream byte) -> result/eax: byte
 sig stream-final s: (addr stream byte) -> result/eax: byte
 
 #sig copy-bytes src: (addr byte), dest: (addr byte), n: int
+sig copy-array-object src: (addr array _), dest-ah: (addr handle array _)
+sig copy-file src: (addr buffered-file), dest-ah: (addr handle buffered-file), filename: (addr array byte)
diff --git a/apps/assort b/apps/assort
index aaa63b08..3fa27b27 100755
--- a/apps/assort
+++ b/apps/assort
Binary files differdiff --git a/apps/braces b/apps/braces
index 0dc79c34..ae630053 100755
--- a/apps/braces
+++ b/apps/braces
Binary files differdiff --git a/apps/calls b/apps/calls
index 990bb060..c9568f8a 100755
--- a/apps/calls
+++ b/apps/calls
Binary files differdiff --git a/apps/crenshaw2-1 b/apps/crenshaw2-1
index c288bcb2..40497b3e 100755
--- a/apps/crenshaw2-1
+++ b/apps/crenshaw2-1
Binary files differdiff --git a/apps/crenshaw2-1b b/apps/crenshaw2-1b
index 0f1e8700..91686feb 100755
--- a/apps/crenshaw2-1b
+++ b/apps/crenshaw2-1b
Binary files differdiff --git a/apps/dquotes b/apps/dquotes
index 1e255bf8..a8fb2e13 100755
--- a/apps/dquotes
+++ b/apps/dquotes
Binary files differdiff --git a/apps/ex1 b/apps/ex1
index 2aad6427..4b7d86bf 100755
--- a/apps/ex1
+++ b/apps/ex1
Binary files differdiff --git a/apps/ex10 b/apps/ex10
index 7926e5db..3b7d66d9 100755
--- a/apps/ex10
+++ b/apps/ex10
Binary files differdiff --git a/apps/ex11 b/apps/ex11
index 697ef6d3..d4a63d54 100755
--- a/apps/ex11
+++ b/apps/ex11
Binary files differdiff --git a/apps/ex12 b/apps/ex12
index 92d006ff..bc1986ff 100755
--- a/apps/ex12
+++ b/apps/ex12
Binary files differdiff --git a/apps/ex13 b/apps/ex13
index 239481d4..4ee78bee 100755
--- a/apps/ex13
+++ b/apps/ex13
Binary files differdiff --git a/apps/ex2 b/apps/ex2
index 0b221cff..95e86040 100755
--- a/apps/ex2
+++ b/apps/ex2
Binary files differdiff --git a/apps/ex3 b/apps/ex3
index 9e3d3a5a..3dd52b65 100755
--- a/apps/ex3
+++ b/apps/ex3
Binary files differdiff --git a/apps/ex4 b/apps/ex4
index a0452d50..6adb1344 100755
--- a/apps/ex4
+++ b/apps/ex4
Binary files differdiff --git a/apps/ex5 b/apps/ex5
index ec351b61..0c55e531 100755
--- a/apps/ex5
+++ b/apps/ex5
Binary files differdiff --git a/apps/ex6 b/apps/ex6
index 58fb8b58..354bb936 100755
--- a/apps/ex6
+++ b/apps/ex6
Binary files differdiff --git a/apps/ex7 b/apps/ex7
index 444dc7be..32e9edce 100755
--- a/apps/ex7
+++ b/apps/ex7
Binary files differdiff --git a/apps/ex8 b/apps/ex8
index 4eeb3f07..49057a80 100755
--- a/apps/ex8
+++ b/apps/ex8
Binary files differdiff --git a/apps/ex9 b/apps/ex9
index 14dd22b2..4e2ec963 100755
--- a/apps/ex9
+++ b/apps/ex9
Binary files differdiff --git a/apps/factorial b/apps/factorial
index 5eaecd80..7b3d9a28 100755
--- a/apps/factorial
+++ b/apps/factorial
Binary files differdiff --git a/apps/hex b/apps/hex
index ef1178da..c5b05406 100755
--- a/apps/hex
+++ b/apps/hex
Binary files differdiff --git a/apps/mu b/apps/mu
index 4f1cfac4..1feacce4 100755
--- a/apps/mu
+++ b/apps/mu
Binary files differdiff --git a/apps/pack b/apps/pack
index fdc7714b..07884e8b 100755
--- a/apps/pack
+++ b/apps/pack
Binary files differdiff --git a/apps/sigils b/apps/sigils
index ca8f4f2b..640c4317 100755
--- a/apps/sigils
+++ b/apps/sigils
Binary files differdiff --git a/apps/survey b/apps/survey
index 5020e96e..27bde944 100755
--- a/apps/survey
+++ b/apps/survey
Binary files differdiff --git a/apps/tests b/apps/tests
index b692dd85..7d8e0c27 100755
--- a/apps/tests
+++ b/apps/tests
Binary files differdiff --git a/apps/tile/data.mu b/apps/tile/data.mu
index e2c1a47e..0464c54f 100644
--- a/apps/tile/data.mu
+++ b/apps/tile/data.mu
@@ -33,12 +33,14 @@ type word {
   prev: (handle word)
 }
 
+# todo: turn this into a sum type
 type value {
   type: int
   int-data: int  # if type = 0
   text-data: (handle array byte)  # if type = 1
   array-data: (handle array value)  # if type = 2
   file-data: (handle buffered-file)  # if type = 3
+  filename: (handle array byte)  # if type = 3
   screen-data: (handle screen)  # if type = 4
 }
 
diff --git a/apps/tile/environment.mu b/apps/tile/environment.mu
index a268291c..b802fc26 100644
--- a/apps/tile/environment.mu
+++ b/apps/tile/environment.mu
@@ -967,7 +967,7 @@ fn evaluate-environment _env: (addr environment), stack: (addr value-stack) {
 }
 
 fn render _env: (addr environment) {
-#?   print-string 0, "==\n"
+#?   print-string 0, "== render\n"
   var env/esi: (addr environment) <- copy _env
   clear-canvas env
   # screen
@@ -983,19 +983,13 @@ fn render _env: (addr environment) {
   # sandbox
   var sandbox-ah/eax: (addr handle sandbox) <- get env, sandboxes
   var sandbox/eax: (addr sandbox) <- lookup *sandbox-ah
-#?   {
-#?     var line-ah/eax: (addr handle line) <- get sandbox, data
-#?     var line/eax: (addr line) <- lookup *line-ah
-#?     var first-word-ah/eax: (addr handle word) <- get line, data
-#?     var curr-word/eax: (addr word) <- lookup *first-word-ah
-#?     print-word 0, curr-word
-#?     print-string 0, "\n"
-#?   }
   # bindings
   var bindings-storage: table
   var bindings/ebx: (addr table) <- address bindings-storage
   initialize-table bindings, 0x10
+#?   print-string 0, "render-sandbox {\n"
   render-sandbox screen, functions, bindings, sandbox, 3, repl-col
+#?   print-string 0, "render-sandbox }\n"
 }
 
 fn render-sandbox screen: (addr screen), functions: (addr handle function), bindings: (addr table), _sandbox: (addr sandbox), top-row: int, left-col: int {
@@ -1027,14 +1021,6 @@ fn render-sandbox screen: (addr screen), functions: (addr handle function), bind
       var cursor-call-path/eax: (addr call-path-element) <- lookup *cursor-call-path-ah
       var cursor-word-ah/eax: (addr handle word) <- get cursor-call-path, word
       var cursor-word/eax: (addr word) <- lookup *cursor-word-ah
-#?       print-string 0, "cursor 2: "
-#?       {
-#?         print-word 0, cursor-word
-#?         print-string 0, " -- "
-#?         var foo/eax: int <- copy cursor-word
-#?         print-int32-hex 0, foo
-#?         print-string 0, "\n"
-#?       }
       # it's enough to pass in the first word of the path, because if the path isn't a singleton the word is guaranteed to be unique
       render-line-without-stack screen, curr-line, curr-row, left-col, cursor-word, cursor-row-addr, cursor-col-addr
     }
@@ -1043,7 +1029,6 @@ fn render-sandbox screen: (addr screen), functions: (addr handle function), bind
     loop
   }
   #
-#?   print-string 0, "render final line\n"
   render-final-line-with-stack screen, functions, bindings, sandbox, curr-row, left-col, cursor-row-addr, cursor-col-addr
   # at most one of the following dialogs will be rendered
   render-rename-dialog screen, sandbox, cursor-row, cursor-col
@@ -1276,14 +1261,12 @@ fn call-path-element-length _x: (addr handle call-path-element) -> _/eax: int {
 #
 # Along the way, compute the column the cursor should be positioned at (cursor-col-addr).
 fn render-line screen: (addr screen), functions: (addr handle function), bindings: (addr table), first-line: (addr line), _line: (addr line), expanded-words: (addr handle call-path), top-row: int, left-col: int, curr-path: (addr handle call-path-element), cursor-word: (addr word), cursor-call-path: (addr handle call-path-element), cursor-row-addr: (addr int), cursor-col-addr: (addr int) -> _/ecx: int {
-#?   print-string 0, "## render-line: "
+#?   print-string 0, "render-line\n"
 #?   dump-table bindings
   # curr-word
   var line/esi: (addr line) <- copy _line
   var first-word-ah/eax: (addr handle word) <- get line, data
   var curr-word/eax: (addr word) <- lookup *first-word-ah
-  var debug-row: int
-  copy-to debug-row, 0x20
   #
   # loop-carried dependency
   var curr-col/ecx: int <- copy left-col
@@ -1334,7 +1317,7 @@ fn render-line screen: (addr screen), functions: (addr handle function), binding
         break-if-=
         var bindings2-storage: table
         var bindings2/ebx: (addr table) <- address bindings2-storage
-        shallow-copy-table-values bindings, bindings2
+        deep-copy-table bindings, bindings2
         evaluate functions, bindings2, first-line, prev-word, stack
       }
       # construct new bindings
@@ -1348,7 +1331,7 @@ fn render-line screen: (addr screen), functions: (addr handle function), binding
       var callee-body-first-word/edx: (addr handle word) <- get callee-body, data
       # - render subsidiary stack
       push-to-call-path-element curr-path, callee-body-first-word  # leak
-#?       print-string 0, "subsidiary { "
+#?       print-string 0, "subsidiary {\n"
 #?       dump-table callee-bindings
 #?       syscall_exit
       curr-col <- render-line screen, functions, callee-bindings, callee-body, callee-body, expanded-words, top-row, curr-col, curr-path, cursor-word, cursor-call-path, cursor-row-addr, cursor-col-addr
@@ -1363,33 +1346,21 @@ fn render-line screen: (addr screen), functions: (addr handle function), binding
     }
     # render main column
     var old-col/edx: int <- copy curr-col
-#?     move-cursor 0, debug-row, 1
-#?     increment debug-row
-#?     print-string 0, "rendering column from "
-#?     print-int32-decimal 0, curr-col
-#?     print-string 0, "\n"
     var bindings2-storage: table
     var bindings2/ebx: (addr table) <- address bindings2-storage
-    shallow-copy-table-values bindings, bindings2
+#?     print-string 0, "deep-copy {\n"
+    deep-copy-table bindings, bindings2
+#?     print-string 0, "}\n"
+#?     print-string 0, "render column {\n"
     curr-col <- render-column screen, functions, bindings2, first-line, line, curr-word, top-row, curr-col
+#?     print-string 0, "}\n"
     # cache cursor column if necessary
     $render-line:cache-cursor-column: {
-#?       print-string 0, "cache cursor? "
-#?       {
-#?         var foo/eax: int <- copy curr-word
-#?         print-int32-hex 0, foo
-#?       }
-#?       print-string 0, "\n"
       {
         var found?/eax: boolean <- call-path-element-match? curr-path, cursor-call-path
         compare found?, 0  # false
         break-if-= $render-line:cache-cursor-column
       }
-#?       print-string 0, "cursor at "
-#?       print-int32-decimal 0, top-row
-#?       print-string 0, ", "
-#?       print-int32-decimal 0, old-col
-#?       print-string 0, "\n"
       var dest/edi: (addr int) <- copy cursor-row-addr
       {
         var src/eax: int <- copy top-row
@@ -1428,7 +1399,7 @@ fn callee functions: (addr handle function), word: (addr word), out: (addr handl
 #
 # Return the farthest column written.
 fn render-column screen: (addr screen), functions: (addr handle function), bindings: (addr table), first-line: (addr line), line: (addr line), final-word: (addr word), top-row: int, left-col: int -> _/ecx: int {
-#?   print-string 0, "render-column: "
+#?   print-string 0, "render-column\n"
 #?   dump-table bindings
   var max-width/esi: int <- copy 0
   {
@@ -1439,7 +1410,9 @@ fn render-column screen: (addr screen), functions: (addr handle function), bindi
     # copy bindings
     var bindings2-storage: table
     var bindings2/ebx: (addr table) <- address bindings2-storage
-    shallow-copy-table-values bindings, bindings2
+#?     print-string 0, "deep copy table {\n"
+    deep-copy-table bindings, bindings2
+#?     print-string 0, "}\n"
     evaluate functions, bindings2, first-line, final-word, stack-addr
     # indent stack
     var indented-col/ebx: int <- copy left-col
diff --git a/apps/tile/main.mu b/apps/tile/main.mu
index 457c9816..c47bbe3d 100644
--- a/apps/tile/main.mu
+++ b/apps/tile/main.mu
@@ -77,6 +77,7 @@ fn test {
   process env, 0xa  # newline: define function
   process env, 0x435b1b  # right-arrow
 #?   process env, 5  # ctrl-e: end of line
+  print-string 0, "==\n"
   process env, 0xa  # newline: expand
   render env
 }
diff --git a/apps/tile/rpn.mu b/apps/tile/rpn.mu
index cc8e0163..935d7bdd 100644
--- a/apps/tile/rpn.mu
+++ b/apps/tile/rpn.mu
@@ -134,6 +134,8 @@ fn evaluate functions: (addr handle function), bindings: (addr table), scratch:
         var type-addr/eax: (addr int) <- get target-val, type
         copy-to *type-addr, 3  # file
         var target-string-ah/eax: (addr handle array byte) <- get target-val, text-data
+        var filename-ah/ecx: (addr handle array byte) <- get target-val, filename
+        copy-object target-string-ah, filename-ah
         var empty: (handle array byte)
         copy-handle empty, target-string-ah
         break $evaluate:process-word
@@ -607,6 +609,12 @@ fn evaluate functions: (addr handle function), bindings: (addr table), scratch:
         top <- decrement
         var dest-offset/edx: (offset value) <- compute-offset data, top
         var target-val/edx: (addr value) <- index data, dest-offset
+#?           {
+#?             print-string 0, "DD: "
+#?             var y0/eax: int <- copy target-val
+#?             print-int32-hex 0, y0
+#?             print-string 0, "\n"
+#?           }
         # create binding from curr-stream to target-val
         var key-h: (handle array byte)
         var key/ecx: (addr handle array byte) <- address key-h
@@ -640,6 +648,12 @@ fn evaluate functions: (addr handle function), bindings: (addr table), scratch:
         var val/eax: (addr value) <- lookup *val-ah
         compare val, 0
         break-if-=
+#?           {
+#?             print-string 0, "UU: "
+#?             var y0/eax: int <- copy val
+#?             print-int32-hex 0, y0
+#?             print-string 0, "\n"
+#?           }
         push-value-stack out, val
         break $evaluate:process-word
       }
diff --git a/apps/tile/table.mu b/apps/tile/table.mu
index 42b2e3c8..8b8b93f3 100644
--- a/apps/tile/table.mu
+++ b/apps/tile/table.mu
@@ -4,33 +4,67 @@ fn initialize-table _self: (addr table), n: int {
   populate data-ah, n
 }
 
-fn shallow-copy-table-values _src: (addr table), dest: (addr table) {
+fn deep-copy-table _src: (addr table), _dest: (addr table) {
+#?   print-string 0, "deep-copy-table\n"
   var src/eax: (addr table) <- copy _src
-#?   print-string 0, "before copy: "
-#?   dump-table src
   var src-data-ah/eax: (addr handle array bind) <- get src, data
   var _src-data/eax: (addr array bind) <- lookup *src-data-ah
   var src-data/esi: (addr array bind) <- copy _src-data
   var n/ecx: int <- length src-data
+  var dest/eax: (addr table) <- copy _dest
   initialize-table dest, n
+  var dest-data-ah/eax: (addr handle array bind) <- get dest, data
+  var _dest-data/eax: (addr array bind) <- lookup *dest-data-ah
+  var dest-data/edi: (addr array bind) <- copy _dest-data
   var i/eax: int <- copy 0
   {
     compare i, n
     break-if->=
-    {
+#?     print-string 0, "iter\n"
+    $deep-copy:element: {
       var offset/edx: (offset bind) <- compute-offset src-data, i
       var src-bind/ecx: (addr bind) <- index src-data, offset
-      var key-ah/ebx: (addr handle array byte) <- get src-bind, key
-      var key/eax: (addr array byte) <- lookup *key-ah
-      compare key, 0
+      var dest-bind/edx: (addr bind) <- index dest-data, offset
+      var src-key-ah/ebx: (addr handle array byte) <- get src-bind, key
+      var src-key/eax: (addr array byte) <- lookup *src-key-ah
+      compare src-key, 0
       break-if-=
-      var val-ah/eax: (addr handle value) <- get src-bind, value
-      var val/eax: (addr value) <- lookup *val-ah
-      bind-in-table dest, key-ah, val
+      # copy key
+      var dest-key-ah/eax: (addr handle array byte) <- get dest-bind, key
+      copy-object src-key-ah, dest-key-ah
+      # deep copy value
+      var src-val-ah/eax: (addr handle value) <- get src-bind, value
+      var _src-val/eax: (addr value) <- lookup *src-val-ah
+      var src-val/ecx: (addr value) <- copy _src-val
+#?       {
+#?         print-string 0, "src type: "
+#?         var foo/eax: (addr int) <- get src-val, type
+#?         print-int32-decimal 0, *foo
+#?         print-string 0, "\n"
+#?       }
+      var dest-val-ah/eax: (addr handle value) <- get dest-bind, value
+      allocate dest-val-ah
+      var dest-val/eax: (addr value) <- lookup *dest-val-ah
+#?       {
+#?         var foo/eax: int <- copy dest-val
+#?         print-string 0, "iter: "
+#?         print-int32-hex 0, foo
+#?         print-string 0, "\n"
+#?       }
+#?       print-string 0, "deep copy value {\n"
+      deep-copy-value src-val, dest-val
+#?       {
+#?         print-string 0, "dest: "
+#?         var foo/eax: (addr int) <- get dest-val, type
+#?         print-int32-decimal 0, *foo
+#?         print-string 0, "\n"
+#?       }
+#?       print-string 0, "}\n"
     }
     i <- increment
     loop
   }
+#?   print-string 0, "end deep-copy-table\n"
 }
 
 fn bind-in-table _self: (addr table), key: (addr handle array byte), val: (addr value) {
diff --git a/apps/tile/value-stack.mu b/apps/tile/value-stack.mu
index a3d05322..e8e05068 100644
--- a/apps/tile/value-stack.mu
+++ b/apps/tile/value-stack.mu
@@ -82,6 +82,40 @@ fn push-value-stack _self: (addr value-stack), val: (addr value) {
   var dest-offset/edx: (offset value) <- compute-offset data, top
   var dest-addr/edx: (addr value) <- index data, dest-offset
   copy-object val, dest-addr
+#?   {
+#?     print-string 0, "push-value-stack/src: "
+#?     var x5/eax: (addr value) <- copy val
+#?     {
+#?       var y0/eax: int <- copy x5
+#?       print-int32-hex 0, y0
+#?       print-string 0, " -- "
+#?     }
+#?     var x6/eax: (addr int) <- get x5, type
+#?     compare x6, 0
+#?     break-if-=
+#?     print-int32-hex 0, *x6
+#?     print-string 0, "\n"
+#?   }
+#?   {
+#?     print-string 0, "push-value-stack/dest: "
+#?     var x5/eax: (addr value) <- copy dest-addr
+#?     {
+#?       var y0/eax: int <- copy x5
+#?       print-int32-hex 0, y0
+#?       print-string 0, " -- "
+#?     }
+#?     compare x5, 0
+#?     break-if-=
+#?     var x6/eax: (addr handle screen) <- get x5, screen-data
+#?     var x7/eax: (addr screen) <- lookup *x6
+#?     compare x7, 0
+#?     break-if-=
+#?     {
+#?       var foo/eax: int <- copy x7
+#?       print-int32-hex 0, foo
+#?     }
+#?     print-string 0, "\n"
+#?   }
   increment *top-addr
 }
 
diff --git a/apps/tile/value.mu b/apps/tile/value.mu
index ad526901..68bd42e5 100644
--- a/apps/tile/value.mu
+++ b/apps/tile/value.mu
@@ -1,9 +1,12 @@
-
-## Rendering values
-
 fn render-value-at screen: (addr screen), row: int, col: int, _val: (addr value), max-width: int {
   move-cursor screen, row, col
   var val/esi: (addr value) <- copy _val
+#?           {
+#?             print-string 0, "val: "
+#?             var y0/eax: int <- copy val
+#?             print-int32-hex 0, y0
+#?             print-string 0, "\n"
+#?           }
   var val-type/ecx: (addr int) <- get val, type
   # per-type rendering logic goes here
   compare *val-type, 1  # string
@@ -18,11 +21,6 @@ fn render-value-at screen: (addr screen), row: int, col: int, _val: (addr value)
     var truncated-ah/esi: (addr handle array byte) <- address truncated
     substring val-string, 0, 0xc, truncated-ah
     var truncated-string/eax: (addr array byte) <- lookup *truncated-ah
-#?     {
-#?       var foo/eax: int <- copy truncated-string
-#?       print-int32-hex 0, foo
-#?       print-string 0, "\n"
-#?     }
     var len/edx: int <- length truncated-string
     start-color screen, 0xf2, 7
     print-code-point screen, 0x275d  # open-quote
@@ -57,9 +55,17 @@ fn render-value-at screen: (addr screen), row: int, col: int, _val: (addr value)
   compare *val-type, 4  # screen
   {
     break-if-!=
+#?     print-string 0, "render-screen"
     var val-ah/eax: (addr handle screen) <- get val, screen-data
     var val-screen/eax: (addr screen) <- lookup *val-ah
+#?     {
+#?       print-string 0, " -- "
+#?       var foo/eax: int <- copy val-screen
+#?       print-int32-hex 0, foo
+#?       print-string 0, " {\n"
+#?     }
     render-screen screen, row, col, val-screen
+#?     print-string 0, "}\n"
     return
   }
   # render ints by default for now
@@ -136,6 +142,11 @@ fn render-screen screen: (addr screen), row: int, col: int, _target-screen: (add
   start-color screen, 0xf2, 7
   move-cursor screen, row, col
   var target-screen/esi: (addr screen) <- copy _target-screen
+#?   {
+#?     var foo/eax: int <- copy target-screen
+#?     print-int32-hex 0, foo
+#?     print-string 0, "\n"
+#?   }
   var ncols-a/ecx: (addr int) <- get target-screen, num-cols
   print-upper-border screen, *ncols-a
   var r/edx: int <- copy 1
@@ -340,3 +351,91 @@ fn value-height _v: (addr value) -> _/eax: int {
   }
   return 1
 }
+
+fn deep-copy-value _src: (addr value), _dest: (addr value) {
+#?   print-string 0, "deep-copy-value\n"
+  var src/esi: (addr value) <- copy _src
+  var dest/edi: (addr value) <- copy _dest
+  var type/ebx: (addr int) <- get src, type
+  var y/ecx: (addr int) <- get dest, type
+  copy-object type, y
+  compare *type, 0   # int
+  {
+    break-if-!=
+#?     print-string 0, "int value\n"
+    var x/eax: (addr int) <- get src, int-data
+    y <- get dest, int-data
+    copy-object x, y
+    return
+  }
+  compare *type, 1  # string
+  {
+    break-if-!=
+#?     print-string 0, "string value\n"
+    var src-ah/eax: (addr handle array byte) <- get src, text-data
+    var src/eax: (addr array byte) <- lookup *src-ah
+    var dest-ah/edx: (addr handle array byte) <- get dest, text-data
+    copy-array-object src, dest-ah
+    return
+  }
+  compare *type, 2  # array
+  {
+    break-if-!=
+#?     print-string 0, "array value\n"
+    var src-ah/eax: (addr handle array value) <- get src, array-data
+    var _src/eax: (addr array value) <- lookup *src-ah
+    var src/esi: (addr array value) <- copy _src
+    var n/ecx: int <- length src
+    var dest-ah/edx: (addr handle array value) <- get dest, array-data
+    populate dest-ah, n
+    var _dest/eax: (addr array value) <- lookup *dest-ah
+    var dest/edi: (addr array value) <- copy _dest
+    var i/eax: int <- copy 0
+    {
+      compare i, n
+      break-if->=
+      {
+        var offset/edx: (offset value) <- compute-offset src, i
+        var src-element/eax: (addr value) <- index src, offset
+        var dest-element/ecx: (addr value) <- index dest, offset
+        deep-copy-value src-element, dest-element
+      }
+      i <- increment
+      loop
+    }
+    copy-array-object src, dest-ah
+    return
+  }
+  compare *type, 3  # file
+  {
+    break-if-!=
+#?     print-string 0, "file value\n"
+    var src-filename-ah/eax: (addr handle array byte) <- get src, filename
+    var _src-filename/eax: (addr array byte) <- lookup *src-filename-ah
+    var src-filename/ecx: (addr array byte) <- copy _src-filename
+    var dest-filename-ah/ebx: (addr handle array byte) <- get dest, filename
+    copy-array-object src-filename, dest-filename-ah
+    var src-file-ah/eax: (addr handle buffered-file) <- get src, file-data
+    var src-file/eax: (addr buffered-file) <- lookup *src-file-ah
+    var dest-file-ah/edx: (addr handle buffered-file) <- get dest, file-data
+    copy-file src-file, dest-file-ah, src-filename
+    return
+  }
+  compare *type, 4  # screen
+  {
+    break-if-!=
+#?     print-string 0, "screen value\n"
+    var src-screen-ah/eax: (addr handle screen) <- get src, screen-data
+    var _src-screen/eax: (addr screen) <- lookup *src-screen-ah
+    var src-screen/ecx: (addr screen) <- copy _src-screen
+    var dest-screen-ah/eax: (addr handle screen) <- get dest, screen-data
+    allocate dest-screen-ah
+    var dest-screen/eax: (addr screen) <- lookup *dest-screen-ah
+    copy-object src-screen, dest-screen
+    var dest-screen-data-ah/ebx: (addr handle array screen-cell) <- get dest-screen, data
+    var src-screen-data-ah/eax: (addr handle array screen-cell) <- get src-screen, data
+    var src-screen-data/eax: (addr array screen-cell) <- lookup *src-screen-data-ah
+    copy-array-object src-screen-data, dest-screen-data-ah
+    return
+  }
+}
diff --git a/init.linux b/init.linux
index 6e482b96..f151d80f 100644
--- a/init.linux
+++ b/init.linux
@@ -43,6 +43,12 @@ syscall_close:  # fd/ebx: int -> status/eax
     cd/syscall 0x80/imm8
     c3/return
 
+# http://man7.org/linux/man-pages/man2/lseek.2.html
+syscall_lseek:  # fd/ebx: int, offset/ecx: int, whence/edx: int
+    b8/copy-to-eax 0x13/imm32
+    cd/syscall 0x80/imm8
+    c3/return
+
 # http://man7.org/linux/man-pages/man2/creat.2.html
 syscall_creat:  # filename/ebx: (addr kernel-string) -> fd-or-error/eax: int
     b8/copy-to-eax 8/imm32