about summary refs log tree commit diff stats
path: root/shell
diff options
context:
space:
mode:
Diffstat (limited to 'shell')
-rw-r--r--shell/evaluate.mu240
-rw-r--r--shell/macroexpand.mu114
-rw-r--r--shell/main.mu19
-rw-r--r--shell/parse.mu10
-rw-r--r--shell/print.mu74
-rw-r--r--shell/sandbox.mu17
-rw-r--r--shell/tokenize.mu35
-rw-r--r--shell/trace.mu69
8 files changed, 425 insertions, 153 deletions
diff --git a/shell/evaluate.mu b/shell/evaluate.mu
index e8d1b616..714e4255 100644
--- a/shell/evaluate.mu
+++ b/shell/evaluate.mu
@@ -17,7 +17,8 @@ fn evaluate _in-ah: (addr handle cell), _out-ah: (addr handle cell), env-h: (han
   }
   # errors? skip
   {
-    compare trace, 0
+    var should-trace?/eax: boolean <- should-trace? trace
+    compare should-trace?, 0/false
     break-if-=
     var error?/eax: boolean <- has-errors? trace
     compare error?, 0/false
@@ -51,15 +52,20 @@ fn evaluate _in-ah: (addr handle cell), _out-ah: (addr handle cell), env-h: (han
 #?   }
   # trace "evaluate " in " in environment " env {{{
   {
-    compare trace, 0
+    var should-trace?/eax: boolean <- should-trace? trace
+    compare should-trace?, 0/false
     break-if-=
     var stream-storage: (stream byte 0x300)
     var stream/ecx: (addr stream byte) <- address stream-storage
     write stream, "evaluate "
-    print-cell in-ah, stream, 0/no-trace
+    var nested-trace-storage: trace
+    var nested-trace/edi: (addr trace) <- address nested-trace-storage
+    initialize-trace nested-trace, 1/only-errors, 0x10/capacity, 0/visible
+    print-cell in-ah, stream, nested-trace
     write stream, " in environment "
     var env-ah/eax: (addr handle cell) <- address env-h
-    print-cell env-ah, stream, 0/no-trace
+    clear-trace nested-trace
+    print-cell env-ah, stream, nested-trace
     trace trace, "eval", stream
   }
   # }}}
@@ -239,7 +245,8 @@ fn evaluate _in-ah: (addr handle cell), _out-ah: (addr handle cell), env-h: (han
     debug-print "Q", 4/fg, 0/bg
     # errors? skip
     {
-      compare trace, 0
+      var should-trace?/eax: boolean <- should-trace? trace
+      compare should-trace?, 0/false
       break-if-=
       var error?/eax: boolean <- has-errors? trace
       compare error?, 0/false
@@ -294,7 +301,8 @@ fn evaluate _in-ah: (addr handle cell), _out-ah: (addr handle cell), env-h: (han
     debug-print "Q", 4/fg, 0/bg
     # errors? skip
     {
-      compare trace, 0
+      var should-trace?/eax: boolean <- should-trace? trace
+      compare should-trace?, 0/false
       break-if-=
       var error?/eax: boolean <- has-errors? trace
       compare error?, 0/false
@@ -330,7 +338,8 @@ fn evaluate _in-ah: (addr handle cell), _out-ah: (addr handle cell), env-h: (han
     debug-print "S2", 4/fg, 0/bg
     # errors? skip
     {
-      compare trace, 0
+      var should-trace?/eax: boolean <- should-trace? trace
+      compare should-trace?, 0/false
       break-if-=
       var error?/eax: boolean <- has-errors? trace
       compare error?, 0/false
@@ -378,7 +387,8 @@ fn evaluate _in-ah: (addr handle cell), _out-ah: (addr handle cell), env-h: (han
     debug-print "S2", 4/fg, 0/bg
     # errors? skip
     {
-      compare trace, 0
+      var should-trace?/eax: boolean <- should-trace? trace
+      compare should-trace?, 0/false
       break-if-=
       var error?/eax: boolean <- has-errors? trace
       compare error?, 0/false
@@ -405,7 +415,8 @@ fn evaluate _in-ah: (addr handle cell), _out-ah: (addr handle cell), env-h: (han
     debug-print "U2", 4/fg, 0/bg
     # errors? skip
     {
-      compare trace, 0
+      var should-trace?/eax: boolean <- should-trace? trace
+      compare should-trace?, 0/false
       break-if-=
       var error?/eax: boolean <- has-errors? trace
       compare error?, 0/false
@@ -439,7 +450,8 @@ fn evaluate _in-ah: (addr handle cell), _out-ah: (addr handle cell), env-h: (han
     debug-print "S", 4/fg, 0/bg
     # errors? skip
     {
-      compare trace, 0
+      var should-trace?/eax: boolean <- should-trace? trace
+      compare should-trace?, 0/false
       break-if-=
       var error?/eax: boolean <- has-errors? trace
       compare error?, 0/false
@@ -492,7 +504,8 @@ fn evaluate _in-ah: (addr handle cell), _out-ah: (addr handle cell), env-h: (han
     var guard-ah/esi: (addr handle cell) <- address guard-h
     $evaluate:while:loop-execution: {
       {
-        compare trace, 0
+        var should-trace?/eax: boolean <- should-trace? trace
+        compare should-trace?, 0/false
         break-if-=
         var error?/eax: boolean <- has-errors? trace
         compare error?, 0/false
@@ -505,7 +518,8 @@ fn evaluate _in-ah: (addr handle cell), _out-ah: (addr handle cell), env-h: (han
       debug-print "W", 4/fg, 0/bg
       # errors? skip
       {
-        compare trace, 0
+        var should-trace?/eax: boolean <- should-trace? trace
+        compare should-trace?, 0/false
         break-if-=
         var error?/eax: boolean <- has-errors? trace
         compare error?, 0/false
@@ -520,7 +534,8 @@ fn evaluate _in-ah: (addr handle cell), _out-ah: (addr handle cell), env-h: (han
       evaluate-exprs rest-ah, _out-ah, env-h, globals, trace, screen-cell, keyboard-cell, call-number
       # errors? skip
       {
-        compare trace, 0
+        var should-trace?/eax: boolean <- should-trace? trace
+        compare should-trace?, 0/false
         break-if-=
         var error?/eax: boolean <- has-errors? trace
         compare error?, 0/false
@@ -556,7 +571,8 @@ fn evaluate _in-ah: (addr handle cell), _out-ah: (addr handle cell), env-h: (han
     debug-print "B", 4/fg, 0/bg
     # errors? skip
     {
-      compare trace, 0
+      var should-trace?/eax: boolean <- should-trace? trace
+      compare should-trace?, 0/false
       break-if-=
       var error?/eax: boolean <- has-errors? trace
       compare error?, 0/false
@@ -566,7 +582,8 @@ fn evaluate _in-ah: (addr handle cell), _out-ah: (addr handle cell), env-h: (han
     }
     # a trip wire in case we're running without a trace (e.g. when loading the initial state from disk)
     {
-      compare trace, 0
+      var should-trace?/eax: boolean <- should-trace? trace
+      compare should-trace?, 0/false
       break-if-!=
       var left-out/eax: (addr cell) <- lookup *left-out-ah
       compare left-out, 0
@@ -592,12 +609,16 @@ fn evaluate _in-ah: (addr handle cell), _out-ah: (addr handle cell), env-h: (han
   trace-higher trace
   # trace "=> " _out-ah {{{
   {
-    compare trace, 0
+    var should-trace?/eax: boolean <- should-trace? trace
+    compare should-trace?, 0/false
     break-if-=
     var stream-storage: (stream byte 0x200)
     var stream/ecx: (addr stream byte) <- address stream-storage
     write stream, "=> "
-    print-cell _out-ah, stream, 0/no-trace
+    var nested-trace-storage: trace
+    var nested-trace/edi: (addr trace) <- address nested-trace-storage
+    initialize-trace nested-trace, 1/only-errors, 0x10/capacity, 0/visible
+    print-cell _out-ah, stream, nested-trace
     trace trace, "eval", stream
   }
   # }}}
@@ -619,15 +640,20 @@ fn apply _f-ah: (addr handle cell), args-ah: (addr handle cell), out: (addr hand
   # if it's not a primitive function it must be an anonymous function
   # trace "apply anonymous function " f " in environment " env {{{
   {
-    compare trace, 0
+    var should-trace?/eax: boolean <- should-trace? trace
+    compare should-trace?, 0/false
     break-if-=
     var stream-storage: (stream byte 0x200)
     var stream/ecx: (addr stream byte) <- address stream-storage
     write stream, "apply anonymous function "
-    print-cell _f-ah, stream, 0/no-trace
+    var nested-trace-storage: trace
+    var nested-trace/edi: (addr trace) <- address nested-trace-storage
+    initialize-trace nested-trace, 1/only-errors, 0x10/capacity, 0/visible
+    print-cell _f-ah, stream, nested-trace
 #?     write stream, " in environment "
 #?     var callee-env-ah/eax: (addr handle cell) <- address callee-env-h
-#?     print-cell callee-env-ah, stream, 0/no-trace
+#?     clear-trace nested-trace
+#?     print-cell callee-env-ah, stream, nested-trace
     trace trace, "eval", stream
   }
   # }}}
@@ -664,7 +690,8 @@ fn apply-function params-ah: (addr handle cell), args-ah: (addr handle cell), bo
   push-bindings params-ah, args-ah, env-h, new-env-ah, trace
   # errors? skip
   {
-    compare trace, 0
+    var should-trace?/eax: boolean <- should-trace? trace
+    compare should-trace?, 0/false
     break-if-=
     var error?/eax: boolean <- has-errors? trace
     compare error?, 0/false
@@ -695,7 +722,8 @@ fn evaluate-exprs _exprs-ah: (addr handle cell), out: (addr handle cell), env-h:
       debug-print "X", 7/fg, 0/bg
       # errors? skip
       {
-        compare trace, 0
+        var should-trace?/eax: boolean <- should-trace? trace
+        compare should-trace?, 0/false
         break-if-=
         var error?/eax: boolean <- has-errors? trace
         compare error?, 0/false
@@ -738,17 +766,23 @@ fn push-bindings _params-ah: (addr handle cell), _args-ah: (addr handle cell), o
   # Params can only be symbols or pairs. Args can be anything.
   # trace "pushing bindings from " params " to " args {{{
   {
-    compare trace, 0
+    var should-trace?/eax: boolean <- should-trace? trace
+    compare should-trace?, 0/false
     break-if-=
     var stream-storage: (stream byte 0x200)
     var stream/ecx: (addr stream byte) <- address stream-storage
     write stream, "pushing bindings from "
-    print-cell params-ah, stream, 0/no-trace
+    var nested-trace-storage: trace
+    var nested-trace/edi: (addr trace) <- address nested-trace-storage
+    initialize-trace nested-trace, 1/only-errors, 0x10/capacity, 0/visible
+    print-cell params-ah, stream, nested-trace
     write stream, " to "
-    print-cell args-ah, stream, 0/no-trace
+    clear-trace nested-trace
+    print-cell args-ah, stream, nested-trace
     write stream, " onto "
     var old-env-ah/eax: (addr handle cell) <- address old-env-h
-    print-cell old-env-ah, stream, 0/no-trace
+    clear-trace nested-trace
+    print-cell old-env-ah, stream, nested-trace
     trace trace, "eval", stream
   }
   # }}}
@@ -799,7 +833,8 @@ fn push-bindings _params-ah: (addr handle cell), _args-ah: (addr handle cell), o
   push-bindings first-param-ah, first-arg-ah, old-env-h, intermediate-env-ah, trace
   # errors? skip
   {
-    compare trace, 0
+    var should-trace?/eax: boolean <- should-trace? trace
+    compare should-trace?, 0/false
     break-if-=
     var error?/eax: boolean <- has-errors? trace
     compare error?, 0/false
@@ -816,7 +851,8 @@ fn push-bindings _params-ah: (addr handle cell), _args-ah: (addr handle cell), o
 fn lookup-symbol sym: (addr cell), out: (addr handle cell), env-h: (handle cell), globals: (addr global-table), trace: (addr trace), screen-cell: (addr handle cell), keyboard-cell: (addr handle cell) {
   # trace sym
   {
-    compare trace, 0
+    var should-trace?/eax: boolean <- should-trace? trace
+    compare should-trace?, 0/false
     break-if-=
     var stream-storage: (stream byte 0x800)  # pessimistically sized just for the large alist loaded from disk in `main`
     var stream/ecx: (addr stream byte) <- address stream-storage
@@ -828,7 +864,10 @@ fn lookup-symbol sym: (addr cell), out: (addr handle cell), env-h: (handle cell)
     write-stream stream, sym-data
     write stream, " in "
     var env-ah/eax: (addr handle cell) <- address env-h
-    print-cell env-ah, stream, 0/no-trace
+    var nested-trace-storage: trace
+    var nested-trace/edi: (addr trace) <- address nested-trace-storage
+    initialize-trace nested-trace, 1/only-errors, 0x10/capacity, 0/visible
+    print-cell env-ah, stream, nested-trace
     trace trace, "eval", stream
   }
   trace-lower trace
@@ -854,7 +893,8 @@ fn lookup-symbol sym: (addr cell), out: (addr handle cell), env-h: (handle cell)
     trace-higher trace
     # trace "=> " out " (global)" {{{
     {
-      compare trace, 0
+      var should-trace?/eax: boolean <- should-trace? trace
+      compare should-trace?, 0/false
       break-if-=
       var error?/eax: boolean <- has-errors? trace
       compare error?, 0/false
@@ -862,7 +902,10 @@ fn lookup-symbol sym: (addr cell), out: (addr handle cell), env-h: (handle cell)
       var stream-storage: (stream byte 0x200)
       var stream/ecx: (addr stream byte) <- address stream-storage
       write stream, "=> "
-      print-cell out, stream, 0/no-trace
+      var nested-trace-storage: trace
+      var nested-trace/edi: (addr trace) <- address nested-trace-storage
+      initialize-trace nested-trace, 1/only-errors, 0x10/capacity, 0/visible
+      print-cell out, stream, nested-trace
       write stream, " (global)"
       trace trace, "eval", stream
     }
@@ -873,7 +916,12 @@ fn lookup-symbol sym: (addr cell), out: (addr handle cell), env-h: (handle cell)
   # check car
   var env-head-storage: (handle cell)
   var env-head-ah/eax: (addr handle cell) <- address env-head-storage
-  car env, env-head-ah, 0/no-trace
+  {
+    var nested-trace-storage: trace
+    var nested-trace/edi: (addr trace) <- address nested-trace-storage
+    initialize-trace nested-trace, 1/only-errors, 0x10/capacity, 0/visible
+    car env, env-head-ah, nested-trace
+  }
   var _env-head/eax: (addr cell) <- lookup *env-head-ah
   var env-head/ecx: (addr cell) <- copy _env-head
   # if car is not a list, abort
@@ -904,10 +952,14 @@ fn lookup-symbol sym: (addr cell), out: (addr handle cell), env-h: (handle cell)
   compare match?, 0/false
   {
     break-if-=
-    cdr env-head, out, 0/no-trace
+    var nested-trace-storage: trace
+    var nested-trace/edi: (addr trace) <- address nested-trace-storage
+    initialize-trace nested-trace, 1/only-errors, 0x10/capacity, 0/visible
+    cdr env-head, out, nested-trace
     # trace "=> " out " (match)" {{{
     {
-      compare trace, 0
+      var should-trace?/eax: boolean <- should-trace? trace
+      compare should-trace?, 0/false
       break-if-=
       var error?/eax: boolean <- has-errors? trace
       compare error?, 0/false
@@ -915,7 +967,8 @@ fn lookup-symbol sym: (addr cell), out: (addr handle cell), env-h: (handle cell)
       var stream-storage: (stream byte 0x800)
       var stream/ecx: (addr stream byte) <- address stream-storage
       write stream, "=> "
-      print-cell out, stream, 0/no-trace
+      clear-trace nested-trace
+      print-cell out, stream, nested-trace
       write stream, " (match)"
       trace trace, "eval", stream
     }
@@ -929,21 +982,25 @@ fn lookup-symbol sym: (addr cell), out: (addr handle cell), env-h: (handle cell)
   cdr env, env-tail-ah, trace
   lookup-symbol sym, out, *env-tail-ah, globals, trace, screen-cell, keyboard-cell
   trace-higher trace
-    # trace "=> " out " (recurse)" {{{
-    {
-      compare trace, 0
-      break-if-=
-      var error?/eax: boolean <- has-errors? trace
-      compare error?, 0/false
-      break-if-!=
-      var stream-storage: (stream byte 0x200)
-      var stream/ecx: (addr stream byte) <- address stream-storage
-      write stream, "=> "
-      print-cell out, stream, 0/no-trace
-      write stream, " (recurse)"
-      trace trace, "eval", stream
-    }
-    # }}}
+  # trace "=> " out " (recurse)" {{{
+  {
+    var should-trace?/eax: boolean <- should-trace? trace
+    compare should-trace?, 0/false
+    break-if-=
+    var error?/eax: boolean <- has-errors? trace
+    compare error?, 0/false
+    break-if-!=
+    var stream-storage: (stream byte 0x200)
+    var stream/ecx: (addr stream byte) <- address stream-storage
+    write stream, "=> "
+    var nested-trace-storage: trace
+    var nested-trace/edi: (addr trace) <- address nested-trace-storage
+    initialize-trace nested-trace, 1/only-errors, 0x10/capacity, 0/visible
+    print-cell out, stream, nested-trace
+    write stream, " (recurse)"
+    trace trace, "eval", stream
+  }
+  # }}}
 }
 
 fn test-lookup-symbol-in-env {
@@ -967,7 +1024,10 @@ fn test-lookup-symbol-in-env {
   var tmp-ah/edx: (addr handle cell) <- address tmp-storage
   new-symbol tmp-ah, "a"
   var in/eax: (addr cell) <- lookup *tmp-ah
-  lookup-symbol in, tmp-ah, *env-ah, 0/no-globals, 0/no-trace, 0/no-screen, 0/no-keyboard
+  var trace-storage: trace
+  var trace/edi: (addr trace) <- address trace-storage
+  initialize-trace trace, 1/only-errors, 0x10/capacity, 0/visible
+  lookup-symbol in, tmp-ah, *env-ah, 0/no-globals, trace, 0/no-screen, 0/no-keyboard
   var result/eax: (addr cell) <- lookup *tmp-ah
   var result-type/edx: (addr int) <- get result, type
   check-ints-equal *result-type, 1/number, "F - test-lookup-symbol-in-env/0"
@@ -989,7 +1049,10 @@ fn test-lookup-symbol-in-globals {
   var tmp-ah/ebx: (addr handle cell) <- address tmp-storage
   new-symbol tmp-ah, "+"
   var in/eax: (addr cell) <- lookup *tmp-ah
-  lookup-symbol in, tmp-ah, *nil-ah, globals, 0/no-trace, 0/no-screen, 0/no-keyboard
+  var trace-storage: trace
+  var trace/esi: (addr trace) <- address trace-storage
+  initialize-trace trace, 1/only-errors, 0x10/capacity, 0/visible
+  lookup-symbol in, tmp-ah, *nil-ah, globals, trace, 0/no-screen, 0/no-keyboard
   var result/eax: (addr cell) <- lookup *tmp-ah
   var result-type/edx: (addr int) <- get result, type
   check-ints-equal *result-type, 4/primitive-function, "F - test-lookup-symbol-in-globals/0"
@@ -1000,7 +1063,8 @@ fn test-lookup-symbol-in-globals {
 fn mutate-binding name: (addr stream byte), val: (addr handle cell), env-h: (handle cell), globals: (addr global-table), trace: (addr trace) {
   # trace name
   {
-    compare trace, 0
+    var should-trace?/eax: boolean <- should-trace? trace
+    compare should-trace?, 0/false
     break-if-=
     var stream-storage: (stream byte 0x800)  # pessimistically sized just for the large alist loaded from disk in `main`
     var stream/ecx: (addr stream byte) <- address stream-storage
@@ -1008,10 +1072,14 @@ fn mutate-binding name: (addr stream byte), val: (addr handle cell), env-h: (han
     rewind-stream name
     write-stream stream, name
     write stream, " to "
-    print-cell val, stream, 0/no-trace
+    var nested-trace-storage: trace
+    var nested-trace/edi: (addr trace) <- address nested-trace-storage
+    initialize-trace nested-trace, 1/only-errors, 0x10/capacity, 0/visible
+    print-cell val, stream, nested-trace
     write stream, " in "
     var env-ah/eax: (addr handle cell) <- address env-h
-    print-cell env-ah, stream, 0/no-trace
+    clear-trace nested-trace
+    print-cell env-ah, stream, nested-trace
     trace trace, "eval", stream
   }
   trace-lower trace
@@ -1037,7 +1105,8 @@ fn mutate-binding name: (addr stream byte), val: (addr handle cell), env-h: (han
     trace-higher trace
     # trace "=> " val " (global)" {{{
     {
-      compare trace, 0
+      var should-trace?/eax: boolean <- should-trace? trace
+      compare should-trace?, 0/false
       break-if-=
       var error?/eax: boolean <- has-errors? trace
       compare error?, 0/false
@@ -1045,7 +1114,10 @@ fn mutate-binding name: (addr stream byte), val: (addr handle cell), env-h: (han
       var stream-storage: (stream byte 0x200)
       var stream/ecx: (addr stream byte) <- address stream-storage
       write stream, "=> "
-      print-cell val, stream, 0/no-trace
+      var nested-trace-storage: trace
+      var nested-trace/edi: (addr trace) <- address nested-trace-storage
+      initialize-trace nested-trace, 1/only-errors, 0x10/capacity, 0/visible
+      print-cell val, stream, nested-trace
       write stream, " (global)"
       trace trace, "eval", stream
     }
@@ -1056,7 +1128,10 @@ fn mutate-binding name: (addr stream byte), val: (addr handle cell), env-h: (han
   # check car
   var env-head-storage: (handle cell)
   var env-head-ah/eax: (addr handle cell) <- address env-head-storage
-  car env, env-head-ah, 0/no-trace
+  var nested-trace-storage: trace
+  var nested-trace/edi: (addr trace) <- address nested-trace-storage
+  initialize-trace nested-trace, 1/only-errors, 0x10/capacity, 0/visible
+  car env, env-head-ah, nested-trace
   var _env-head/eax: (addr cell) <- lookup *env-head-ah
   var env-head/ecx: (addr cell) <- copy _env-head
   # if car is not a list, abort
@@ -1397,7 +1472,10 @@ fn test-evaluate-number {
   var tmp-storage: (handle cell)
   var tmp-ah/edx: (addr handle cell) <- address tmp-storage
   new-integer tmp-ah, 3
-  evaluate tmp-ah, tmp-ah, *env-ah, 0/no-globals, 0/no-trace, 0/no-screen, 0/no-keyboard, 0/call-number
+  var trace-storage: trace
+  var trace/edi: (addr trace) <- address trace-storage
+  initialize-trace trace, 1/only-errors, 0x10/capacity, 0/visible
+  evaluate tmp-ah, tmp-ah, *env-ah, 0/no-globals, trace, 0/no-screen, 0/no-keyboard, 0/call-number
   #
   var result/eax: (addr cell) <- lookup *tmp-ah
   var result-type/edx: (addr int) <- get result, type
@@ -1427,7 +1505,10 @@ fn test-evaluate-symbol {
   var tmp-storage: (handle cell)
   var tmp-ah/edx: (addr handle cell) <- address tmp-storage
   new-symbol tmp-ah, "a"
-  evaluate tmp-ah, tmp-ah, *env-ah, 0/no-globals, 0/no-trace, 0/no-screen, 0/no-keyboard, 0/call-number
+  var trace-storage: trace
+  var trace/edi: (addr trace) <- address trace-storage
+  initialize-trace trace, 1/only-errors, 0x10/capacity, 0/visible
+  evaluate tmp-ah, tmp-ah, *env-ah, 0/no-globals, trace, 0/no-screen, 0/no-keyboard, 0/call-number
   var result/eax: (addr cell) <- lookup *tmp-ah
   var result-type/edx: (addr int) <- get result, type
   check-ints-equal *result-type, 1/number, "F - test-evaluate-symbol/0"
@@ -1449,7 +1530,10 @@ fn test-evaluate-quote {
   var tmp2-ah/ebx: (addr handle cell) <- address tmp2-storage
   new-symbol tmp2-ah, "a"
   new-pair tmp-ah, *tmp-ah, *tmp2-ah
-  evaluate tmp-ah, tmp-ah, *nil-ah, 0/no-globals, 0/no-trace, 0/no-screen, 0/no-keyboard, 0/call-number
+  var trace-storage: trace
+  var trace/edi: (addr trace) <- address trace-storage
+  initialize-trace trace, 1/only-errors, 0x10/capacity, 0/visible
+  evaluate tmp-ah, tmp-ah, *nil-ah, 0/no-globals, trace, 0/no-screen, 0/no-keyboard, 0/call-number
   var result/eax: (addr cell) <- lookup *tmp-ah
   var result-type/edx: (addr int) <- get result, type
   check-ints-equal *result-type, 2/symbol, "F - test-evaluate-quote/0"
@@ -1470,7 +1554,10 @@ fn test-evaluate-primitive-function {
   # eval +, nil env
   var tmp-storage: (handle cell)
   var tmp-ah/esi: (addr handle cell) <- address tmp-storage
-  evaluate add-ah, tmp-ah, *nil-ah, globals, 0/no-trace, 0/no-screen, 0/no-keyboard, 0/call-number
+  var trace-storage: trace
+  var trace/edx: (addr trace) <- address trace-storage
+  initialize-trace trace, 1/only-errors, 0x10/capacity, 0/visible
+  evaluate add-ah, tmp-ah, *nil-ah, globals, trace, 0/no-screen, 0/no-keyboard, 0/call-number
   #
   var result/eax: (addr cell) <- lookup *tmp-ah
   var result-type/edx: (addr int) <- get result, type
@@ -1530,7 +1617,10 @@ fn test-evaluate-backquote {
   new-symbol tmp2-ah, "a"
   new-pair tmp-ah, *tmp-ah, *tmp2-ah
   clear-object tmp2-ah
-  evaluate tmp-ah, tmp2-ah, *nil-ah, 0/no-globals, 0/no-trace, 0/no-screen, 0/no-keyboard, 0/call-number
+  var trace-storage: trace
+  var trace/edi: (addr trace) <- address trace-storage
+  initialize-trace trace, 1/only-errors, 0x10/capacity, 0/visible
+  evaluate tmp-ah, tmp2-ah, *nil-ah, 0/no-globals, trace, 0/no-screen, 0/no-keyboard, 0/call-number
   var result/eax: (addr cell) <- lookup *tmp2-ah
   var result-type/edx: (addr int) <- get result, type
   check-ints-equal *result-type, 2/symbol, "F - test-evaluate-backquote/0"
@@ -1554,7 +1644,8 @@ fn evaluate-backquote _in-ah: (addr handle cell), _out-ah: (addr handle cell), e
   }
   # errors? skip
   {
-    compare trace, 0
+    var should-trace?/eax: boolean <- should-trace? trace
+    compare should-trace?, 0/false
     break-if-=
     var error?/eax: boolean <- has-errors? trace
     compare error?, 0/false
@@ -1641,7 +1732,8 @@ fn evaluate-backquote _in-ah: (addr handle cell), _out-ah: (addr handle cell), e
     evaluate in-unquote-payload-ah, out-ah, env-h, globals, trace, screen-cell, keyboard-cell, call-number
     # errors? skip
     {
-      compare trace, 0
+      var should-trace?/eax: boolean <- should-trace? trace
+      compare should-trace?, 0/false
       break-if-=
       var error?/eax: boolean <- has-errors? trace
       compare error?, 0/false
@@ -1680,7 +1772,8 @@ fn evaluate-backquote _in-ah: (addr handle cell), _out-ah: (addr handle cell), e
   debug-print "`r)", 3/fg, 0/bg
   # errors? skip
   {
-    compare trace, 0
+    var should-trace?/eax: boolean <- should-trace? trace
+    compare should-trace?, 0/false
     break-if-=
     var error?/eax: boolean <- has-errors? trace
     compare error?, 0/false
@@ -1716,7 +1809,10 @@ fn test-evaluate-backquote-list {
   new-pair tmp-ah, *a-ah, *tmp-ah
   new-pair tmp-ah, *backquote-ah, *tmp-ah
   #
-  evaluate tmp-ah, tmp-ah, *nil-ah, 0/no-globals, 0/no-trace, 0/no-screen, 0/no-keyboard, 0/call-number
+  var trace-storage: trace
+  var trace/edi: (addr trace) <- address trace-storage
+  initialize-trace trace, 1/only-errors, 0x10/capacity, 0/visible
+  evaluate tmp-ah, tmp-ah, *nil-ah, 0/no-globals, trace, 0/no-screen, 0/no-keyboard, 0/call-number
   # result is (a b)
   var result/eax: (addr cell) <- lookup *tmp-ah
   {
@@ -1779,7 +1875,10 @@ fn test-evaluate-backquote-list-with-unquote {
   # tmp = cons(backquote, tmp)
   new-pair tmp-ah, backquote-h, tmp-h
   #
-  evaluate tmp-ah, tmp-ah, env-h, 0/no-globals, 0/no-trace, 0/no-screen, 0/no-keyboard, 0/call-number
+  var trace-storage: trace
+  var trace/edi: (addr trace) <- address trace-storage
+  initialize-trace trace, 1/only-errors, 0x10/capacity, 0/visible
+  evaluate tmp-ah, tmp-ah, env-h, 0/no-globals, trace, 0/no-screen, 0/no-keyboard, 0/call-number
   # result is (a 3)
   var result/eax: (addr cell) <- lookup *tmp-ah
   {
@@ -1850,7 +1949,10 @@ fn test-evaluate-backquote-list-with-unquote-splice {
   new-pair tmp-ah, backquote-h, tmp-h
 #?   dump-cell-from-cursor-over-full-screen tmp-ah
   #
-  evaluate tmp-ah, tmp-ah, env-h, 0/no-globals, 0/no-trace, 0/no-screen, 0/no-keyboard, 0/call-number
+  var trace-storage: trace
+  var trace/edi: (addr trace) <- address trace-storage
+  initialize-trace trace, 1/only-errors, 0x10/capacity, 0/visible
+  evaluate tmp-ah, tmp-ah, env-h, 0/no-globals, trace, 0/no-screen, 0/no-keyboard, 0/call-number
   # result is (a a 3 b)
 #?   dump-cell-from-cursor-over-full-screen tmp-ah
   var result/eax: (addr cell) <- lookup *tmp-ah
diff --git a/shell/macroexpand.mu b/shell/macroexpand.mu
index 70de1091..83c3c025 100644
--- a/shell/macroexpand.mu
+++ b/shell/macroexpand.mu
@@ -1,12 +1,16 @@
 fn macroexpand expr-ah: (addr handle cell), globals: (addr global-table), trace: (addr trace) {
   # trace "macroexpand " expr-ah {{{
   {
-    compare trace, 0
+    var should-trace?/eax: boolean <- should-trace? trace
+    compare should-trace?, 0/false
     break-if-=
     var stream-storage: (stream byte 0x200)
     var stream/ecx: (addr stream byte) <- address stream-storage
     write stream, "macroexpand "
-    print-cell expr-ah, stream, 0/no-trace
+    var nested-trace-storage: trace
+    var nested-trace/edi: (addr trace) <- address nested-trace-storage
+    initialize-trace nested-trace, 1/only-errors, 0x10/capacity, 0/visible
+    print-cell expr-ah, stream, nested-trace
     trace trace, "mac", stream
   }
   # }}}
@@ -18,12 +22,16 @@ fn macroexpand expr-ah: (addr handle cell), globals: (addr global-table), trace:
   }
   # trace "=> " expr-ah {{{
   {
-    compare trace, 0
+    var should-trace?/eax: boolean <- should-trace? trace
+    compare should-trace?, 0/false
     break-if-=
     var stream-storage: (stream byte 0x200)
     var stream/ecx: (addr stream byte) <- address stream-storage
     write stream, "=> "
-    print-cell expr-ah, stream, 0/no-trace
+    var nested-trace-storage: trace
+    var nested-trace/edi: (addr trace) <- address nested-trace-storage
+    initialize-trace nested-trace, 1/only-errors, 0x10/capacity, 0/visible
+    print-cell expr-ah, stream, nested-trace
     trace trace, "mac", stream
   }
   # }}}
@@ -34,12 +42,16 @@ fn macroexpand-iter _expr-ah: (addr handle cell), globals: (addr global-table),
   var expr-ah/esi: (addr handle cell) <- copy _expr-ah
   # trace "macroexpand-iter " expr {{{
   {
-    compare trace, 0
+    var should-trace?/eax: boolean <- should-trace? trace
+    compare should-trace?, 0/false
     break-if-=
     var stream-storage: (stream byte 0x200)
     var stream/ecx: (addr stream byte) <- address stream-storage
     write stream, "macroexpand-iter "
-    print-cell expr-ah, stream, 0/no-trace
+    var nested-trace-storage: trace
+    var nested-trace/edi: (addr trace) <- address nested-trace-storage
+    initialize-trace nested-trace, 1/only-errors, 0x10/capacity, 0/visible
+    print-cell expr-ah, stream, nested-trace
     trace trace, "mac", stream
   }
   # }}}
@@ -236,22 +248,28 @@ fn test-macroexpand {
   var gap/eax: (addr gap-buffer) <- lookup *gap-ah
   var result-h: (handle cell)
   var result-ah/ebx: (addr handle cell) <- address result-h
-  read-cell gap, result-ah, 0/no-trace
-  var dummy/eax: boolean <- macroexpand-iter result-ah, globals, 0/no-trace
+  var nested-trace-storage: trace
+  var nested-trace/ecx: (addr trace) <- address nested-trace-storage
+  initialize-trace nested-trace, 1/only-errors, 0x10/capacity, 0/visible
+  read-cell gap, result-ah, nested-trace
+  clear-trace nested-trace
+  var dummy/eax: boolean <- macroexpand-iter result-ah, globals, nested-trace
 #?   dump-cell-from-cursor-over-full-screen result-ah
   var _result/eax: (addr cell) <- lookup *result-ah
   var result/edi: (addr cell) <- copy _result
   # expected
   initialize-sandbox-with sandbox, "(+ 3 4)"
-  var expected-gap-ah/ecx: (addr handle gap-buffer) <- get sandbox, data
+  var expected-gap-ah/edx: (addr handle gap-buffer) <- get sandbox, data
   var expected-gap/eax: (addr gap-buffer) <- lookup *expected-gap-ah
   var expected-h: (handle cell)
-  var expected-ah/ecx: (addr handle cell) <- address expected-h
-  read-cell expected-gap, expected-ah, 0/no-trace
+  var expected-ah/edx: (addr handle cell) <- address expected-h
+  clear-trace nested-trace
+  read-cell expected-gap, expected-ah, nested-trace
 #?   dump-cell-from-cursor-over-full-screen expected-ah
   var expected/eax: (addr cell) <- lookup *expected-ah
   #
-  var assertion/eax: boolean <- cell-isomorphic? result, expected, 0/no-trace
+  clear-trace nested-trace
+  var assertion/eax: boolean <- cell-isomorphic? result, expected, nested-trace
   check assertion, "F - test-macroexpand"
 }
 
@@ -272,22 +290,28 @@ fn test-macroexpand-inside-anonymous-fn {
   var gap/eax: (addr gap-buffer) <- lookup *gap-ah
   var result-h: (handle cell)
   var result-ah/ebx: (addr handle cell) <- address result-h
-  read-cell gap, result-ah, 0/no-trace
-  var dummy/eax: boolean <- macroexpand-iter result-ah, globals, 0/no-trace
+  var nested-trace-storage: trace
+  var nested-trace/ecx: (addr trace) <- address nested-trace-storage
+  initialize-trace nested-trace, 1/only-errors, 0x10/capacity, 0/visible
+  read-cell gap, result-ah, nested-trace
+  clear-trace nested-trace
+  var dummy/eax: boolean <- macroexpand-iter result-ah, globals, nested-trace
 #?   dump-cell-from-cursor-over-full-screen result-ah
   var _result/eax: (addr cell) <- lookup *result-ah
   var result/edi: (addr cell) <- copy _result
   # expected
   initialize-sandbox-with sandbox, "(fn() (+ 3 4))"
-  var expected-gap-ah/ecx: (addr handle gap-buffer) <- get sandbox, data
+  var expected-gap-ah/edx: (addr handle gap-buffer) <- get sandbox, data
   var expected-gap/eax: (addr gap-buffer) <- lookup *expected-gap-ah
   var expected-h: (handle cell)
-  var expected-ah/ecx: (addr handle cell) <- address expected-h
-  read-cell expected-gap, expected-ah, 0/no-trace
+  var expected-ah/edx: (addr handle cell) <- address expected-h
+  clear-trace nested-trace
+  read-cell expected-gap, expected-ah, nested-trace
 #?   dump-cell-from-cursor-over-full-screen expected-ah
   var expected/eax: (addr cell) <- lookup *expected-ah
   #
-  var assertion/eax: boolean <- cell-isomorphic? result, expected, 0/no-trace
+  clear-trace nested-trace
+  var assertion/eax: boolean <- cell-isomorphic? result, expected, nested-trace
   check assertion, "F - test-macroexpand-inside-anonymous-fn"
 }
 
@@ -306,22 +330,28 @@ fn test-macroexpand-inside-fn-call {
   var gap/eax: (addr gap-buffer) <- lookup *gap-ah
   var result-h: (handle cell)
   var result-ah/ebx: (addr handle cell) <- address result-h
-  read-cell gap, result-ah, 0/no-trace
-  var dummy/eax: boolean <- macroexpand-iter result-ah, globals, 0/no-trace
+  var nested-trace-storage: trace
+  var nested-trace/ecx: (addr trace) <- address nested-trace-storage
+  initialize-trace nested-trace, 1/only-errors, 0x10/capacity, 0/visible
+  read-cell gap, result-ah, nested-trace
+  clear-trace nested-trace
+  var dummy/eax: boolean <- macroexpand-iter result-ah, globals, nested-trace
 #?   dump-cell-from-cursor-over-full-screen result-ah
   var _result/eax: (addr cell) <- lookup *result-ah
   var result/edi: (addr cell) <- copy _result
   # expected
   initialize-sandbox-with sandbox, "((fn() (+ 3 4)))"
-  var expected-gap-ah/ecx: (addr handle gap-buffer) <- get sandbox, data
+  var expected-gap-ah/edx: (addr handle gap-buffer) <- get sandbox, data
   var expected-gap/eax: (addr gap-buffer) <- lookup *expected-gap-ah
   var expected-h: (handle cell)
-  var expected-ah/ecx: (addr handle cell) <- address expected-h
-  read-cell expected-gap, expected-ah, 0/no-trace
+  var expected-ah/edx: (addr handle cell) <- address expected-h
+  clear-trace nested-trace
+  read-cell expected-gap, expected-ah, nested-trace
 #?   dump-cell-from-cursor-over-full-screen expected-ah
   var expected/eax: (addr cell) <- lookup *expected-ah
   #
-  var assertion/eax: boolean <- cell-isomorphic? result, expected, 0/no-trace
+  clear-trace nested-trace
+  var assertion/eax: boolean <- cell-isomorphic? result, expected, nested-trace
   check assertion, "F - test-macroexpand-inside-fn-call"
 }
 
@@ -340,22 +370,28 @@ fn pending-test-macroexpand-inside-backquote-unquote {
   var gap/eax: (addr gap-buffer) <- lookup *gap-ah
   var result-h: (handle cell)
   var result-ah/ebx: (addr handle cell) <- address result-h
-  read-cell gap, result-ah, 0/no-trace
-  var dummy/eax: boolean <- macroexpand-iter result-ah, globals, 0/no-trace
+  var nested-trace-storage: trace
+  var nested-trace/ecx: (addr trace) <- address nested-trace-storage
+  initialize-trace nested-trace, 1/only-errors, 0x10/capacity, 0/visible
+  read-cell gap, result-ah, nested-trace
+  clear-trace nested-trace
+  var dummy/eax: boolean <- macroexpand-iter result-ah, globals, nested-trace
   dump-cell-from-cursor-over-full-screen result-ah
   var _result/eax: (addr cell) <- lookup *result-ah
   var result/edi: (addr cell) <- copy _result
   # expected
   initialize-sandbox-with sandbox, "`(print [result is ] ,(+ 3 4)))"
-  var expected-gap-ah/ecx: (addr handle gap-buffer) <- get sandbox, data
+  var expected-gap-ah/edx: (addr handle gap-buffer) <- get sandbox, data
   var expected-gap/eax: (addr gap-buffer) <- lookup *expected-gap-ah
   var expected-h: (handle cell)
-  var expected-ah/ecx: (addr handle cell) <- address expected-h
-  read-cell expected-gap, expected-ah, 0/no-trace
+  var expected-ah/edx: (addr handle cell) <- address expected-h
+  clear-trace nested-trace
+  read-cell expected-gap, expected-ah, nested-trace
   dump-cell-from-cursor-over-full-screen expected-ah
   var expected/eax: (addr cell) <- lookup *expected-ah
   #
-  var assertion/eax: boolean <- cell-isomorphic? result, expected, 0/no-trace
+  clear-trace nested-trace
+  var assertion/eax: boolean <- cell-isomorphic? result, expected, nested-trace
   check assertion, "F - test-macroexpand-inside-backquote-unquote"
 }
 
@@ -374,22 +410,28 @@ fn pending-test-macroexpand-inside-nested-backquote-unquote {
   var gap/eax: (addr gap-buffer) <- lookup *gap-ah
   var result-h: (handle cell)
   var result-ah/ebx: (addr handle cell) <- address result-h
-  read-cell gap, result-ah, 0/no-trace
-  var dummy/eax: boolean <- macroexpand-iter result-ah, globals, 0/no-trace
+  var nested-trace-storage: trace
+  var nested-trace/ecx: (addr trace) <- address nested-trace-storage
+  initialize-trace nested-trace, 1/only-errors, 0x10/capacity, 0/visible
+  read-cell gap, result-ah, nested-trace
+  clear-trace nested-trace
+  var dummy/eax: boolean <- macroexpand-iter result-ah, globals, nested-trace
   dump-cell-from-cursor-over-full-screen result-ah
   var _result/eax: (addr cell) <- lookup *result-ah
   var result/edi: (addr cell) <- copy _result
   # expected
   initialize-sandbox-with sandbox, "`(a ,(+ 3 4) `(b ,(m 3 4) ,,(+ 3 4)))"
-  var expected-gap-ah/ecx: (addr handle gap-buffer) <- get sandbox, data
+  var expected-gap-ah/edx: (addr handle gap-buffer) <- get sandbox, data
   var expected-gap/eax: (addr gap-buffer) <- lookup *expected-gap-ah
   var expected-h: (handle cell)
-  var expected-ah/ecx: (addr handle cell) <- address expected-h
-  read-cell expected-gap, expected-ah, 0/no-trace
+  var expected-ah/edx: (addr handle cell) <- address expected-h
+  clear-trace nested-trace
+  read-cell expected-gap, expected-ah, nested-trace
   dump-cell-from-cursor-over-full-screen expected-ah
   var expected/eax: (addr cell) <- lookup *expected-ah
   #
-  var assertion/eax: boolean <- cell-isomorphic? result, expected, 0/no-trace
+  clear-trace nested-trace
+  var assertion/eax: boolean <- cell-isomorphic? result, expected, nested-trace
   check assertion, "F - test-macroexpand-inside-nested-backquote-unquote"
 }
 
diff --git a/shell/main.mu b/shell/main.mu
index 0e202fa5..f8f6a838 100644
--- a/shell/main.mu
+++ b/shell/main.mu
@@ -56,7 +56,10 @@ fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk)
         # run
         var out: (handle cell)
         var out-ah/ecx: (addr handle cell) <- address out
-        evaluate tmp, out-ah, nil, globals, 0/no-trace, 0/no-fake-screen, 0/no-fake-keyboard, 0/call-number
+        var trace-storage: trace
+        var trace/ebx: (addr trace) <- address trace-storage
+        initialize-trace trace, 1/only-errors, 0x10/capacity, 0/visible
+        evaluate tmp, out-ah, nil, globals, trace, 0/no-fake-screen, 0/no-fake-keyboard, 0/call-number
         {
           var tmp/eax: byte <- read-key keyboard
           compare tmp, 0
@@ -95,7 +98,10 @@ fn load-state data-disk: (addr disk), _sandbox: (addr sandbox), globals: (addr g
   # read: gap-buffer -> cell
   var initial-root-storage: (handle cell)
   var initial-root/ecx: (addr handle cell) <- address initial-root-storage
-  read-cell data, initial-root, 0/no-trace
+  var trace-storage: trace
+  var trace/edi: (addr trace) <- address trace-storage
+  initialize-trace trace, 1/only-errors, 0x10/capacity, 0/visible
+  read-cell data, initial-root, trace
   draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "  into s-expressions", 3/fg, 0/bg
   move-cursor-to-left-margin-of-next-line 0/screen
   clear-gap-buffer data
@@ -113,7 +119,8 @@ fn load-state data-disk: (addr disk), _sandbox: (addr sandbox), globals: (addr g
   var globals-literal/eax: (addr cell) <- lookup *globals-literal-ah
   var globals-cell-storage: (handle cell)
   var globals-cell-ah/edx: (addr handle cell) <- address globals-cell-storage
-  lookup-symbol globals-literal, globals-cell-ah, *initial-root, 0/no-globals, 0/no-trace, 0/no-screen, 0/no-keyboard
+  clear-trace trace
+  lookup-symbol globals-literal, globals-cell-ah, *initial-root, 0/no-globals, trace, 0/no-screen, 0/no-keyboard
   var globals-cell/eax: (addr cell) <- lookup *globals-cell-ah
   {
     compare globals-cell, 0
@@ -128,13 +135,15 @@ fn load-state data-disk: (addr disk), _sandbox: (addr sandbox), globals: (addr g
   var sandbox-literal/eax: (addr cell) <- lookup *sandbox-literal-ah
   var sandbox-cell-storage: (handle cell)
   var sandbox-cell-ah/edx: (addr handle cell) <- address sandbox-cell-storage
-  lookup-symbol sandbox-literal, sandbox-cell-ah, *initial-root, 0/no-globals, 0/no-trace, 0/no-screen, 0/no-keyboard
+  clear-trace trace
+  lookup-symbol sandbox-literal, sandbox-cell-ah, *initial-root, 0/no-globals, trace, 0/no-screen, 0/no-keyboard
   var sandbox-cell/eax: (addr cell) <- lookup *sandbox-cell-ah
   {
     compare sandbox-cell, 0
     break-if-=
     # print: cell -> stream
-    print-cell sandbox-cell-ah, s, 0/no-trace
+    clear-trace trace
+    print-cell sandbox-cell-ah, s, trace
     # stream -> gap-buffer
     load-gap-buffer-from-stream data, s
   }
diff --git a/shell/parse.mu b/shell/parse.mu
index eceebd7d..a977216c 100644
--- a/shell/parse.mu
+++ b/shell/parse.mu
@@ -218,7 +218,10 @@ fn parse-atom _curr-token: (addr cell), _out: (addr handle cell), trace: (addr t
       var stream-storage: (stream byte 0x400)
       var stream/ecx: (addr stream byte) <- address stream-storage
       write stream, "=> number "
-      print-number out-addr, stream, 0/no-trace
+      var nested-trace-storage: trace
+      var nested-trace/edi: (addr trace) <- address nested-trace-storage
+      initialize-trace nested-trace, 1/only-errors, 0x10/capacity, 0/visible
+      print-number out-addr, stream, nested-trace
       trace trace, "parse", stream
     }
     return
@@ -246,7 +249,10 @@ fn parse-atom _curr-token: (addr cell), _out: (addr handle cell), trace: (addr t
     var stream-storage: (stream byte 0x400)
     var stream/ecx: (addr stream byte) <- address stream-storage
     write stream, "=> symbol "
-    print-symbol out-addr, stream, 0/no-trace
+    var nested-trace-storage: trace
+    var nested-trace/edi: (addr trace) <- address nested-trace-storage
+    initialize-trace nested-trace, 1/only-errors, 0x10/capacity, 0/visible
+    print-symbol out-addr, stream, nested-trace
     trace trace, "parse", stream
   }
 }
diff --git a/shell/print.mu b/shell/print.mu
index 1658ec07..2fab999f 100644
--- a/shell/print.mu
+++ b/shell/print.mu
@@ -85,7 +85,10 @@ fn print-cell _in: (addr handle cell), out: (addr stream byte), trace: (addr tra
 fn dump-cell-at-top-right in-ah: (addr handle cell) {
   var stream-storage: (stream byte 0x1000)
   var stream/edx: (addr stream byte) <- address stream-storage
-  print-cell in-ah, stream, 0/no-trace
+  var trace-storage: trace
+  var trace/edi: (addr trace) <- address trace-storage
+  initialize-trace trace, 1/only-errors, 0x10/capacity, 0/visible
+  print-cell in-ah, stream, trace
   var d1/eax: int <- copy 0
   var d2/ecx: int <- copy 0
   d1, d2 <- draw-stream-wrapping-right-then-down 0/screen, stream, 0/xmin, 0/ymin, 0x80/xmax, 0x30/ymax, 0/x, 0/y, 7/fg, 0xc5/bg=blue-bg
@@ -94,7 +97,10 @@ fn dump-cell-at-top-right in-ah: (addr handle cell) {
 fn dump-cell-from-cursor-over-full-screen in-ah: (addr handle cell) {
   var stream-storage: (stream byte 0x200)
   var stream/edx: (addr stream byte) <- address stream-storage
-  print-cell in-ah, stream, 0/no-trace
+  var trace-storage: trace
+  var trace/edi: (addr trace) <- address trace-storage
+  initialize-trace trace, 1/only-errors, 0x10/capacity, 0/visible
+  print-cell in-ah, stream, trace
   draw-stream-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, stream, 7/fg, 0/bg
 }
 
@@ -107,7 +113,8 @@ fn print-symbol _in: (addr cell), out: (addr stream byte), trace: (addr trace) {
   rewind-stream data
   write-stream out, data
   # trace
-  compare trace, 0
+  var should-trace?/eax: boolean <- should-trace? trace
+  compare should-trace?, 0/false
   break-if-=
   rewind-stream data
   var stream-storage: (stream byte 0x40)
@@ -128,7 +135,8 @@ fn print-stream _in: (addr cell), out: (addr stream byte), trace: (addr trace) {
   write-stream out, data
   write out, "]"
   # trace
-  compare trace, 0
+  var should-trace?/eax: boolean <- should-trace? trace
+  compare should-trace?, 0/false
   break-if-=
   rewind-stream data
   var stream-storage: (stream byte 0x40)
@@ -143,8 +151,12 @@ fn print-number _in: (addr cell), out: (addr stream byte), trace: (addr trace) {
   var val/eax: (addr float) <- get in, number-data
   write-float-decimal-approximate out, *val, 3/precision
   # trace
-  compare trace, 0
-  break-if-=
+  {
+    var should-trace?/eax: boolean <- should-trace? trace
+    compare should-trace?, 0/false
+    break-if-!=
+    return
+  }
   var stream-storage: (stream byte 0x40)
   var stream/ecx: (addr stream byte) <- address stream-storage
   write stream, "=> number "
@@ -273,7 +285,10 @@ fn test-print-cell-zero {
   new-integer num, 0
   var out-storage: (stream byte 0x40)
   var out/edi: (addr stream byte) <- address out-storage
-  print-cell num, out, 0/no-trace
+  var trace-storage: trace
+  var trace/edx: (addr trace) <- address trace-storage
+  initialize-trace trace, 1/only-errors, 0x10/capacity, 0/visible
+  print-cell num, out, trace
   check-stream-equal out, "0", "F - test-print-cell-zero"
 }
 
@@ -283,7 +298,10 @@ fn test-print-cell-integer {
   new-integer num, 1
   var out-storage: (stream byte 0x40)
   var out/edi: (addr stream byte) <- address out-storage
-  print-cell num, out, 0/no-trace
+  var trace-storage: trace
+  var trace/edx: (addr trace) <- address trace-storage
+  initialize-trace trace, 1/only-errors, 0x10/capacity, 0/visible
+  print-cell num, out, trace
   check-stream-equal out, "1", "F - test-print-cell-integer"
 }
 
@@ -293,7 +311,10 @@ fn test-print-cell-integer-2 {
   new-integer num, 0x30
   var out-storage: (stream byte 0x40)
   var out/edi: (addr stream byte) <- address out-storage
-  print-cell num, out, 0/no-trace
+  var trace-storage: trace
+  var trace/edx: (addr trace) <- address trace-storage
+  initialize-trace trace, 1/only-errors, 0x10/capacity, 0/visible
+  print-cell num, out, trace
   check-stream-equal out, "48", "F - test-print-cell-integer-2"
 }
 
@@ -304,7 +325,10 @@ fn test-print-cell-fraction {
   new-float num, val
   var out-storage: (stream byte 0x40)
   var out/edi: (addr stream byte) <- address out-storage
-  print-cell num, out, 0/no-trace
+  var trace-storage: trace
+  var trace/edx: (addr trace) <- address trace-storage
+  initialize-trace trace, 1/only-errors, 0x10/capacity, 0/visible
+  print-cell num, out, trace
   check-stream-equal out, "0.5", "F - test-print-cell-fraction"
 }
 
@@ -314,7 +338,10 @@ fn test-print-cell-symbol {
   new-symbol sym, "abc"
   var out-storage: (stream byte 0x40)
   var out/edi: (addr stream byte) <- address out-storage
-  print-cell sym, out, 0/no-trace
+  var trace-storage: trace
+  var trace/edx: (addr trace) <- address trace-storage
+  initialize-trace trace, 1/only-errors, 0x10/capacity, 0/visible
+  print-cell sym, out, trace
   check-stream-equal out, "abc", "F - test-print-cell-symbol"
 }
 
@@ -324,7 +351,10 @@ fn test-print-cell-nil-list {
   allocate-pair nil
   var out-storage: (stream byte 0x40)
   var out/edi: (addr stream byte) <- address out-storage
-  print-cell nil, out, 0/no-trace
+  var trace-storage: trace
+  var trace/edx: (addr trace) <- address trace-storage
+  initialize-trace trace, 1/only-errors, 0x10/capacity, 0/visible
+  print-cell nil, out, trace
   check-stream-equal out, "()", "F - test-print-cell-nil-list"
 }
 
@@ -342,7 +372,10 @@ fn test-print-cell-singleton-list {
   #
   var out-storage: (stream byte 0x40)
   var out/edi: (addr stream byte) <- address out-storage
-  print-cell list, out, 0/no-trace
+  var trace-storage: trace
+  var trace/edx: (addr trace) <- address trace-storage
+  initialize-trace trace, 1/only-errors, 0x10/capacity, 0/visible
+  print-cell list, out, trace
   check-stream-equal out, "(abc)", "F - test-print-cell-singleton-list"
 }
 
@@ -363,7 +396,10 @@ fn test-print-cell-list {
   #
   var out-storage: (stream byte 0x40)
   var out/edi: (addr stream byte) <- address out-storage
-  print-cell list, out, 0/no-trace
+  var trace-storage: trace
+  var trace/edx: (addr trace) <- address trace-storage
+  initialize-trace trace, 1/only-errors, 0x10/capacity, 0/visible
+  print-cell list, out, trace
   check-stream-equal out, "(64 abc)", "F - test-print-cell-list"
 }
 
@@ -384,7 +420,10 @@ fn test-print-cell-list-of-nil {
   #
   var out-storage: (stream byte 0x40)
   var out/edi: (addr stream byte) <- address out-storage
-  print-cell list, out, 0/no-trace
+  var trace-storage: trace
+  var trace/edx: (addr trace) <- address trace-storage
+  initialize-trace trace, 1/only-errors, 0x10/capacity, 0/visible
+  print-cell list, out, trace
   check-stream-equal out, "(64 ())", "F - test-print-cell-list-nil"
 }
 
@@ -402,6 +441,9 @@ fn test-print-dotted-list {
   #
   var out-storage: (stream byte 0x40)
   var out/edi: (addr stream byte) <- address out-storage
-  print-cell list, out, 0/no-trace
+  var trace-storage: trace
+  var trace/edx: (addr trace) <- address trace-storage
+  initialize-trace trace, 1/only-errors, 0x10/capacity, 0/visible
+  print-cell list, out, trace
   check-stream-equal out, "(abc . 64)", "F - test-print-dotted-list"
 }
diff --git a/shell/sandbox.mu b/shell/sandbox.mu
index 5b94ce9a..d5cf6c8f 100644
--- a/shell/sandbox.mu
+++ b/shell/sandbox.mu
@@ -31,7 +31,7 @@ fn initialize-sandbox _self: (addr sandbox), fake-screen-and-keyboard?: boolean
   var trace-ah/eax: (addr handle trace) <- get self, trace
   allocate trace-ah
   var trace/eax: (addr trace) <- lookup *trace-ah
-  initialize-trace trace, 0x100/max-depth, 0x8000/lines, 0x80/visible-lines
+  initialize-trace trace, 0x100/max-depth, 0x8000/lines, 0x80/visible
   var cursor-in-data?/eax: (addr boolean) <- get self, cursor-in-data?
   copy-to *cursor-in-data?, 1/true
 }
@@ -49,7 +49,7 @@ fn initialize-sandbox-with _self: (addr sandbox), s: (addr array byte) {
   var trace-ah/eax: (addr handle trace) <- get self, trace
   allocate trace-ah
   var trace/eax: (addr trace) <- lookup *trace-ah
-  initialize-trace trace, 0x100/max-depth, 0x8000/lines, 0x80/visible-lines
+  initialize-trace trace, 0x100/max-depth, 0x8000/lines, 0x80/visible
   var cursor-in-data?/eax: (addr boolean) <- get self, cursor-in-data?
   copy-to *cursor-in-data?, 1/true
 }
@@ -646,15 +646,20 @@ fn read-evaluate-and-move-to-globals _in-ah: (addr handle gap-buffer), globals:
   var in/eax: (addr gap-buffer) <- lookup *in-ah
   var read-result-h: (handle cell)
   var read-result-ah/esi: (addr handle cell) <- address read-result-h
-  read-cell in, read-result-ah, 0/no-trace
-  macroexpand read-result-ah, globals, 0/no-trace
+  var trace-storage: trace
+  var trace/edx: (addr trace) <- address trace-storage
+  initialize-trace trace, 1/only-errors, 0x10/capacity, 0/visible
+  read-cell in, read-result-ah, trace
+  clear-trace trace
+  macroexpand read-result-ah, globals, trace
   var nil-storage: (handle cell)
   var nil-ah/eax: (addr handle cell) <- address nil-storage
   allocate-pair nil-ah
   var eval-result-storage: (handle cell)
   var eval-result/edi: (addr handle cell) <- address eval-result-storage
   debug-print "^", 4/fg, 0/bg
-  evaluate read-result-ah, eval-result, *nil-ah, globals, 0/no-trace, 0/no-screen-cell, 0/no-keyboard-cell, 1/call-number
+  clear-trace trace
+  evaluate read-result-ah, eval-result, *nil-ah, globals, trace, 0/no-screen-cell, 0/no-keyboard-cell, 1/call-number
   debug-print "$", 4/fg, 0/bg
   move-gap-buffer-to-global globals, read-result-ah, _in-ah
 }
@@ -894,7 +899,7 @@ fn has-trace? _self: (addr sandbox) -> _/eax: boolean {
   compare trace, 0
   {
     break-if-!=
-    return 0/false
+    abort "null trace"
   }
   var first-free/ebx: (addr int) <- get trace, first-free
   compare *first-free, 0
diff --git a/shell/tokenize.mu b/shell/tokenize.mu
index ed05b2de..03c04910 100644
--- a/shell/tokenize.mu
+++ b/shell/tokenize.mu
@@ -42,7 +42,10 @@ fn test-tokenize-quote {
   var stream-storage: (stream cell 0x10)
   var stream/edi: (addr stream cell) <- address stream-storage
   #
-  tokenize in, stream, 0/no-trace
+  var trace-storage: trace
+  var trace/edx: (addr trace) <- address trace-storage
+  initialize-trace trace, 1/only-errors, 0x10/capacity, 0/visible
+  tokenize in, stream, trace
   #
   var curr-token-storage: cell
   var curr-token/ebx: (addr cell) <- address curr-token-storage
@@ -66,7 +69,10 @@ fn test-tokenize-backquote {
   var stream-storage: (stream cell 0x10)
   var stream/edi: (addr stream cell) <- address stream-storage
   #
-  tokenize in, stream, 0/no-trace
+  var trace-storage: trace
+  var trace/edx: (addr trace) <- address trace-storage
+  initialize-trace trace, 1/only-errors, 0x10/capacity, 0/visible
+  tokenize in, stream, trace
   #
   var curr-token-storage: cell
   var curr-token/ebx: (addr cell) <- address curr-token-storage
@@ -90,7 +96,10 @@ fn test-tokenize-unquote {
   var stream-storage: (stream cell 0x10)
   var stream/edi: (addr stream cell) <- address stream-storage
   #
-  tokenize in, stream, 0/no-trace
+  var trace-storage: trace
+  var trace/edx: (addr trace) <- address trace-storage
+  initialize-trace trace, 1/only-errors, 0x10/capacity, 0/visible
+  tokenize in, stream, trace
   #
   var curr-token-storage: cell
   var curr-token/ebx: (addr cell) <- address curr-token-storage
@@ -114,7 +123,10 @@ fn test-tokenize-unquote-splice {
   var stream-storage: (stream cell 0x10)
   var stream/edi: (addr stream cell) <- address stream-storage
   #
-  tokenize in, stream, 0/no-trace
+  var trace-storage: trace
+  var trace/edx: (addr trace) <- address trace-storage
+  initialize-trace trace, 1/only-errors, 0x10/capacity, 0/visible
+  tokenize in, stream, trace
   #
   var curr-token-storage: cell
   var curr-token/ebx: (addr cell) <- address curr-token-storage
@@ -131,7 +143,10 @@ fn test-tokenize-dotted-list {
   var stream-storage: (stream cell 0x10)
   var stream/edi: (addr stream cell) <- address stream-storage
   #
-  tokenize in, stream, 0/no-trace
+  var trace-storage: trace
+  var trace/edx: (addr trace) <- address trace-storage
+  initialize-trace trace, 1/only-errors, 0x10/capacity, 0/visible
+  tokenize in, stream, trace
   #
   var curr-token-storage: cell
   var curr-token/ebx: (addr cell) <- address curr-token-storage
@@ -156,7 +171,10 @@ fn test-tokenize-stream-literal {
   var stream-storage: (stream cell 0x10)
   var stream/edi: (addr stream cell) <- address stream-storage
   #
-  tokenize in, stream, 0/no-trace
+  var trace-storage: trace
+  var trace/edx: (addr trace) <- address trace-storage
+  initialize-trace trace, 1/only-errors, 0x10/capacity, 0/visible
+  tokenize in, stream, trace
   #
   var curr-token-storage: cell
   var curr-token/ebx: (addr cell) <- address curr-token-storage
@@ -179,7 +197,10 @@ fn test-tokenize-stream-literal-in-tree {
   var stream-storage: (stream cell 0x10)
   var stream/edi: (addr stream cell) <- address stream-storage
   #
-  tokenize in, stream, 0/no-trace
+  var trace-storage: trace
+  var trace/edx: (addr trace) <- address trace-storage
+  initialize-trace trace, 1/only-errors, 0x10/capacity, 0/visible
+  tokenize in, stream, trace
   #
   var curr-token-storage: cell
   var curr-token/ebx: (addr cell) <- address curr-token-storage
diff --git a/shell/trace.mu b/shell/trace.mu
index d3cf1a3f..0398a099 100644
--- a/shell/trace.mu
+++ b/shell/trace.mu
@@ -43,7 +43,10 @@ type trace-line {
 fn initialize-trace _self: (addr trace), max-depth: int, capacity: int, visible-capacity: int {
   var self/esi: (addr trace) <- copy _self
   compare self, 0
-  break-if-=
+  {
+    break-if-!=
+    abort "null trace"
+  }
   var src/ecx: int <- copy max-depth
   var dest/eax: (addr int) <- get self, max-depth
   copy-to *dest, src
@@ -58,7 +61,10 @@ fn initialize-trace _self: (addr trace), max-depth: int, capacity: int, visible-
 fn clear-trace _self: (addr trace) {
   var self/eax: (addr trace) <- copy _self
   compare self, 0
-  break-if-=
+  {
+    break-if-!=
+    abort "null trace"
+  }
   var len/edx: (addr int) <- get self, first-free
   copy-to *len, 0
   # might leak memory; existing elements won't be used anymore
@@ -66,10 +72,10 @@ fn clear-trace _self: (addr trace) {
 
 fn has-errors? _self: (addr trace) -> _/eax: boolean {
   var self/eax: (addr trace) <- copy _self
+  compare self, 0
   {
-    compare self, 0
     break-if-!=
-    return 0/false
+    abort "null trace"
   }
   var max/edx: (addr int) <- get self, first-free
   var trace-ah/eax: (addr handle array trace-line) <- get self, data
@@ -93,10 +99,37 @@ fn has-errors? _self: (addr trace) -> _/eax: boolean {
   return 0/false
 }
 
+fn should-trace? _self: (addr trace) -> _/eax: boolean {
+  var self/esi: (addr trace) <- copy _self
+  compare self, 0
+  {
+    break-if-!=
+    abort "null trace"
+  }
+  var depth-a/ecx: (addr int) <- get self, curr-depth
+  var depth/ecx: int <- copy *depth-a
+  var max-depth-a/eax: (addr int) <- get self, max-depth
+  compare depth, *max-depth-a
+  {
+    break-if->=
+    return 1/true
+  }
+  return 0/false
+}
+
 fn trace _self: (addr trace), label: (addr array byte), message: (addr stream byte) {
   var self/esi: (addr trace) <- copy _self
   compare self, 0
-  break-if-=
+  {
+    break-if-!=
+    abort "null trace"
+  }
+  var should-trace?/eax: boolean <- should-trace? self
+  compare should-trace?, 0/false
+  {
+    break-if-!=
+    return
+  }
   var data-ah/eax: (addr handle array trace-line) <- get self, data
   var data/eax: (addr array trace-line) <- lookup *data-ah
   var index-addr/edi: (addr int) <- get self, first-free
@@ -130,7 +163,10 @@ fn trace _self: (addr trace), label: (addr array byte), message: (addr stream by
 
 fn trace-text self: (addr trace), label: (addr array byte), s: (addr array byte) {
   compare self, 0
-  break-if-=
+  {
+    break-if-!=
+    abort "null trace"
+  }
   var data-storage: (stream byte 0x100)
   var data/eax: (addr stream byte) <- address data-storage
   write data, s
@@ -140,7 +176,10 @@ fn trace-text self: (addr trace), label: (addr array byte), s: (addr array byte)
 fn error _self: (addr trace), message: (addr array byte) {
   var self/esi: (addr trace) <- copy _self
   compare self, 0
-  break-if-=
+  {
+    break-if-!=
+    abort "null trace"
+  }
   var curr-depth-a/eax: (addr int) <- get self, curr-depth
   var save-depth/ecx: int <- copy *curr-depth-a
   copy-to *curr-depth-a, 0/error
@@ -165,7 +204,10 @@ fn initialize-trace-line depth: int, label: (addr array byte), data: (addr strea
 fn trace-lower _self: (addr trace) {
   var self/esi: (addr trace) <- copy _self
   compare self, 0
-  break-if-=
+  {
+    break-if-!=
+    abort "null trace"
+  }
   var depth/eax: (addr int) <- get self, curr-depth
   increment *depth
 }
@@ -173,7 +215,10 @@ fn trace-lower _self: (addr trace) {
 fn trace-higher _self: (addr trace) {
   var self/esi: (addr trace) <- copy _self
   compare self, 0
-  break-if-=
+  {
+    break-if-!=
+    abort "null trace"
+  }
   var depth/eax: (addr int) <- get self, curr-depth
   decrement *depth
 }
@@ -304,7 +349,7 @@ fn dump-trace _self: (addr trace) {
   compare self, 0
   {
     break-if-!=
-    return
+    abort "null trace"
   }
   var trace-ah/eax: (addr handle array trace-line) <- get self, data
   var _trace/eax: (addr array trace-line) <- lookup *trace-ah
@@ -332,7 +377,7 @@ fn dump-trace-with-label _self: (addr trace), label: (addr array byte) {
   compare self, 0
   {
     break-if-!=
-    return
+    abort "null trace"
   }
   var trace-ah/eax: (addr handle array trace-line) <- get self, data
   var _trace/eax: (addr array trace-line) <- lookup *trace-ah
@@ -379,7 +424,7 @@ fn render-trace screen: (addr screen), _self: (addr trace), xmin: int, ymin: int
   compare self, 0
   {
     break-if-!=
-    return ymin
+    abort "null trace"
   }
   clamp-cursor-to-top self, y
   var trace-ah/eax: (addr handle array trace-line) <- get self, data