about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik Agaram <vc@akkartik.com>2020-12-25 12:26:05 -0800
committerKartik Agaram <vc@akkartik.com>2020-12-25 12:28:10 -0800
commit47287fbf1af21207d8f847842fb32db9ee95e4c8 (patch)
tree432e6a6e6087efaf1b23cbb19465aa21407db055
parent56bfd52643b469687beb6a91a2a6277cea7b75a0 (diff)
downloadmu-47287fbf1af21207d8f847842fb32db9ee95e4c8.tar.gz
7401 - clean up support for non-Linux platforms
-rw-r--r--README.md21
-rw-r--r--subx_debugging.md36
-rwxr-xr-xtest_apps244
-rwxr-xr-xtest_apps_emulated286
-rwxr-xr-xtranslate_mu3
-rwxr-xr-xtranslate_mu_debug5
-rwxr-xr-xtranslate_mu_emulated10
-rwxr-xr-xtranslate_subx17
-rwxr-xr-xtranslate_subx_debug36
-rwxr-xr-xtranslate_subx_emulated25
10 files changed, 478 insertions, 205 deletions
diff --git a/README.md b/README.md
index f39cd10b..cd9d4195 100644
--- a/README.md
+++ b/README.md
@@ -24,7 +24,8 @@ to _some_ safe and clear syntax with as few layers of translation as possible.
 The emphasis is on internal consistency at any point in time rather than
 compatibility with the past. ([More details.](http://akkartik.name/akkartik-convivial-20200607.pdf))
 
-Currently Mu requires a 32-bit x86 processor and Linux kernel.
+Currently Mu requires a 32-bit x86 processor. Generated programs require just
+a Linux kernel and nothing else.
 
 ## Goals
 
@@ -67,12 +68,20 @@ The Mu stack consists of:
 - _bare_ SubX, a more rudimentary form of SubX without certain syntax sugar.
 
 All Mu programs get translated through these layers into tiny zero-dependency
-ELF binaries. The translators for most levels are built out of lower levels.
-The translator from Mu to SubX is written in SubX, and the translator from
-SubX to bare SubX is built in bare SubX.
+ELF binaries that run natively on Linux. The translators for most levels are
+built out of lower levels. The translator from Mu to SubX is written in SubX,
+and the translator from SubX to bare SubX is built in bare SubX.
 
-Mu programs can be run in emulated mode to emit traces, which permit time-travel
-debugging. ([More details.](subx_debugging.md))
+There's an emulator for running Mu binaries (more slowly) on other Unix-like
+systems.
+
+```sh
+$ ./translate_mu_emulated apps/ex2.mu  # emit a.elf using the emulator
+$ ./bootstrap run ./a.elf  # run in the emulator
+$ echo $?
+```
+
+The emulator is also useful for [debugging](subx_debugging.md).
 
 ### incomplete tools
 
diff --git a/subx_debugging.md b/subx_debugging.md
index 27b98774..acd6f88d 100644
--- a/subx_debugging.md
+++ b/subx_debugging.md
@@ -27,28 +27,16 @@ rudimentary but hopefully still workable toolkit:
   mode (`bootstrap run`):
 
   ```
-  $ ./bootstrap translate input.subx -o binary
-  $ ./bootstrap --trace run binary arg1 arg2  2>trace
+  $ ./translate_subx_debug file1.subx file2.subx ...  # generating a.elf
+  $ ./bootstrap --trace run a.elf arg1 arg2
+  saving trace to 'last_run'
   ```
 
   The ability to generate a trace is the essential reason for the existence of
   `bootstrap run` mode. It gives far better visibility into program internals than
   running natively.
 
-- As a further refinement, it is possible to render label names in the trace
-  by adding a second flag to the `bootstrap translate` command:
-
-  ```
-  $ ./bootstrap --debug translate input.subx -o binary
-  $ ./bootstrap --trace run binary arg1 arg2  2>trace
-  ```
-
-  `bootstrap --debug translate` emits a mapping from label to address in a file
-  called `labels`. `bootstrap --trace run` reads in the `labels` file if
-  it exists and prints out any matching label name as it traces each instruction
-  executed.
-
-  Here's a sample of what a trace looks like, with a few boxes highlighted:
+  Here's a sample of the contents of `last_run`, with a few boxes highlighted:
 
   <img alt='trace example' src='html/trace.png'>
 
@@ -60,10 +48,10 @@ rudimentary but hopefully still workable toolkit:
   address `0x0900005e` maps to label `$loop` and presumably marks the start of
   some loop. Function names get similar `run: == label` lines.
 
-- One trick when emitting traces with labels:
+- One quick trick when scanning a trace for the first time:
 
   ```
-  $ grep label trace
+  $ grep label last_run
   ```
 
   This is useful for quickly showing you the control flow for the run, and the
@@ -115,11 +103,21 @@ rudimentary but hopefully still workable toolkit:
   `./translate_subx_debug`, and then running:
 
   ```
-  ./bootstrap --trace --dump run a.elf test 2>&1 |grep 'label test'
+  grep 'label test-' |tail
   ```
 
   Just read out the last test printed out before the segfault.
 
+  Even outside of tests, I can often quickly debug an error just by scanning
+  the end of a trace for labels:
+
+  ```
+  $ grep label last_run |tail
+  ```
+
+  Knowing _where_ the error occurred is often enough to put me on the right
+  track to debugging an error.
+
 Hopefully these hints are enough to get you started. The main thing to
 remember is to not be afraid of modifying the sources. A good debugging
 session gets into a nice rhythm of generating a trace, staring at it for a
diff --git a/test_apps b/test_apps
index 129e429c..8f6253ba 100755
--- a/test_apps
+++ b/test_apps
@@ -1,180 +1,171 @@
 #!/bin/sh
-# Build and test all included SubX programs:
+# Build and test all included SubX programs on Linux:
 #   translate them into ELF binaries
 #   compare the generated binaries with what's already in git
-#   run/test the ELF binaries in emulated mode (unless $NO_EMULATION)
-#   run/test the ELF binaries in native mode (if on Linux)
+#   run/test the ELF binaries in emulated mode
+#   run/test the ELF binaries in native mode
 #
 # Example usage:
-#   test_apps  # compare generated binaries, run them in emulated and native mode
-#   test_apps record  # run binaries in emulated and native mode
-#   NO_EMULATION=1 test_apps  # compare generated binaries, run them in native mode
-#   NO_EMULATION=1 test_apps record  # run binaries just in native mode
+#   test_apps
+#   test_apps record  # don't compare with what's in git
 
 set -e
 cd `dirname $0`
 
-test $NO_EMULATION  ||  EMULATED=1
-test $EMULATED  &&  echo 'testing emulated runs'
-test `uname` = 'Linux'  &&  NATIVE=1
-test $NATIVE  &&  echo 'testing native runs'
-
 ./build
 
-export OS=${OS:-linux}
-
-echo "== translating and running using C++"
+echo "== translating using the bootstrap C++ translator"
 
 # example programs
 
 echo ex1
-./bootstrap translate init.$OS apps/ex1.subx  -o apps/ex1
+./bootstrap_bin translate init.linux apps/ex1.subx  -o apps/ex1
 test "$1" = 'record'  ||  git diff --exit-code apps/ex1
-test $EMULATED  &&  {
-  ./bootstrap run apps/ex1  ||  ret=$?
+{
+  ./bootstrap_bin run apps/ex1  ||  ret=$?
   test $ret -eq 42  # life, the universe and everything
 }
-test $NATIVE  &&  {
+{
   apps/ex1  ||  ret=$?
   test $ret -eq 42  # life, the universe and everything
 }
 
 echo ex2
-./bootstrap translate init.$OS apps/ex2.subx  -o apps/ex2
+./bootstrap_bin translate init.linux apps/ex2.subx  -o apps/ex2
 test "$1" = 'record'  ||  git diff --exit-code apps/ex2
-test $EMULATED  &&  {
-  ./bootstrap run apps/ex2  ||  ret=$?
+{
+  ./bootstrap_bin run apps/ex2  ||  ret=$?
   test $ret -eq 7  # 3 + 4
 }
-test $NATIVE  &&  {
+{
   apps/ex2  ||  ret=$?
   test $ret -eq 7  # 3 + 4
 }
 
 echo ex3
-./bootstrap translate init.$OS apps/ex3.subx  -o apps/ex3
+./bootstrap_bin translate init.linux apps/ex3.subx  -o apps/ex3
 test "$1" = 'record'  ||  git diff --exit-code apps/ex3
-test $EMULATED  &&  {
-  ./bootstrap run apps/ex3  ||  ret=$?
+{
+  ./bootstrap_bin run apps/ex3  ||  ret=$?
   test $ret -eq 55  # 1 + 2 + ... + 10
 }
-test $NATIVE  &&  {
+{
   apps/ex3  ||  ret=$?
   test $ret -eq 55  # 1 + 2 + ... + 10
 }
 
 echo ex4
-./bootstrap translate init.$OS apps/ex4.subx  -o apps/ex4
+./bootstrap_bin translate init.linux apps/ex4.subx  -o apps/ex4
 test "$1" = 'record'  ||  git diff --exit-code apps/ex4
-test $EMULATED  &&  {
-  echo a | ./bootstrap run apps/ex4 >ex4.out  ||  true
+{
+  echo a | ./bootstrap_bin run apps/ex4 >ex4.out  ||  true
   test `cat ex4.out` = 'a'
 }
-test $NATIVE  &&  {
+{
   echo a | apps/ex4 >ex4.out  ||  true
   test `cat ex4.out` = 'a'
 }
 
 echo ex5
-./bootstrap translate init.$OS apps/ex5.subx  -o apps/ex5
+./bootstrap_bin translate init.linux apps/ex5.subx  -o apps/ex5
 test "$1" = 'record'  ||  git diff --exit-code apps/ex5
-test $EMULATED  &&  {
-  echo a | ./bootstrap run apps/ex5 >ex5.out  ||  true
+{
+  echo a | ./bootstrap_bin run apps/ex5 >ex5.out  ||  true
   test `cat ex5.out` = 'a'
 }
-test $NATIVE  &&  {
+{
   echo a | apps/ex5 >ex5.out  ||  true
   test `cat ex5.out` = 'a'
 }
 
 echo ex6
-./bootstrap translate init.$OS apps/ex6.subx  -o apps/ex6
+./bootstrap_bin translate init.linux apps/ex6.subx  -o apps/ex6
 test "$1" = 'record'  ||  git diff --exit-code apps/ex6
-test $EMULATED  &&  {
-  ./bootstrap run apps/ex6 >ex6.out  ||  true
+{
+  ./bootstrap_bin run apps/ex6 >ex6.out  ||  true
   test "`cat ex6.out`" = 'Hello, world!'
 }
-test $NATIVE  &&  {
+{
   apps/ex6 >ex6.out  ||  true
   test "`cat ex6.out`" = 'Hello, world!'
 }
 
 echo ex7
-./bootstrap translate init.$OS apps/ex7.subx  -o apps/ex7
+./bootstrap_bin translate init.linux apps/ex7.subx  -o apps/ex7
 test "$1" = 'record'  ||  git diff --exit-code apps/ex7
-test $EMULATED  &&  {
-  ./bootstrap run apps/ex7  ||  ret=$?
+{
+  ./bootstrap_bin run apps/ex7  ||  ret=$?
   test $ret -eq 97  # 'a'
 }
-test $NATIVE  &&  {
+{
   apps/ex7  ||  ret=$?
   test $ret -eq 97  # 'a'
 }
 
 echo ex8
-./bootstrap translate init.$OS apps/ex8.subx  -o apps/ex8
+./bootstrap_bin translate init.linux apps/ex8.subx  -o apps/ex8
 test "$1" = 'record'  || git diff --exit-code apps/ex8
-test $EMULATED  &&  {
-  ./bootstrap run apps/ex8 abcd  ||  ret=$?
+{
+  ./bootstrap_bin run apps/ex8 abcd  ||  ret=$?
   test $ret -eq 4  # length('abcd')
 }
-test $NATIVE  &&  {
+{
   apps/ex8 abcd  ||  ret=$?
   test $ret -eq 4  # length('abcd')
 }
 
 echo ex9
-./bootstrap translate init.$OS apps/ex9.subx  -o apps/ex9
+./bootstrap_bin translate init.linux apps/ex9.subx  -o apps/ex9
 test "$1" = 'record'  || git diff --exit-code apps/ex9
-test $EMULATED  &&  {
-  ./bootstrap run apps/ex9 z x  ||  ret=$?
+{
+  ./bootstrap_bin run apps/ex9 z x  ||  ret=$?
   test $ret -eq 2  # 'z' - 'x'
 }
-test $NATIVE  &&  {
+{
   apps/ex9 z x  ||  ret=$?
   test $ret -eq 2  # 'z' - 'x'
 }
 
 echo ex10
-./bootstrap translate init.$OS apps/ex10.subx  -o apps/ex10
+./bootstrap_bin translate init.linux apps/ex10.subx  -o apps/ex10
 test "$1" = 'record'  || git diff --exit-code apps/ex10
-test $EMULATED  &&  {
-  ./bootstrap run apps/ex10 abc abc  ||  ret=$?
+{
+  ./bootstrap_bin run apps/ex10 abc abc  ||  ret=$?
   test $ret -eq 1  # equal
-  ./bootstrap run apps/ex10 abc abcd  # 0; not equal
+  ./bootstrap_bin run apps/ex10 abc abcd  # 0; not equal
 }
-test $NATIVE  &&  {
+{
   apps/ex10 abc abc  ||  ret=$?
   test $ret -eq 1  # equal
   apps/ex10 abc abcd  # 0; not equal
 }
 
 echo ex11
-./bootstrap translate init.$OS apps/ex11.subx  -o apps/ex11
+./bootstrap_bin translate init.linux apps/ex11.subx  -o apps/ex11
 test "$1" = 'record'  || git diff --exit-code apps/ex11
-test $EMULATED  &&  {
-  ./bootstrap run apps/ex11
+{
+  ./bootstrap_bin run apps/ex11
   echo
 }
-test $NATIVE  &&  {
+{
   apps/ex11
   echo
 }
 
 echo ex12
-./bootstrap translate init.$OS apps/ex12.subx  -o apps/ex12
+./bootstrap_bin translate init.linux apps/ex12.subx  -o apps/ex12
 test "$1" = 'record'  || git diff --exit-code apps/ex12
-test $EMULATED  &&  ./bootstrap run apps/ex12  # final byte of mmap'd address is well-nigh guaranteed to be 0
+test $EMULATED  &&  ./bootstrap_bin run apps/ex12  # final byte of mmap'd address is well-nigh guaranteed to be 0
 test $NATIVE  &&  apps/ex12
 
 echo ex13
-./bootstrap translate init.$OS apps/ex13.subx  -o apps/ex13
+./bootstrap_bin translate init.linux apps/ex13.subx  -o apps/ex13
 test "$1" = 'record'  ||  git diff --exit-code apps/ex13
-test $EMULATED  &&  {
-  ./bootstrap run apps/ex13  ||  ret=$?
+{
+  ./bootstrap_bin run apps/ex13  ||  ret=$?
   test $ret -eq 1  # 3 == 3
 }
-test $NATIVE  &&  {
+{
   apps/ex13  ||  ret=$?
   test $ret -eq 1  # 3 == 3
 }
@@ -182,15 +173,15 @@ test $NATIVE  &&  {
 # Larger apps that use the standard library.
 
 echo factorial
-./bootstrap translate init.$OS [01]*.subx apps/factorial.subx  -o apps/factorial
+./bootstrap_bin translate init.linux [01]*.subx apps/factorial.subx  -o apps/factorial
 test "$1" = 'record'  ||  git diff --exit-code apps/factorial
-test $EMULATED  &&  {
-  ./bootstrap run apps/factorial  ||  ret=$?
+{
+  ./bootstrap_bin run apps/factorial  ||  ret=$?
   test $ret -eq 120  # factorial(5)
-  ./bootstrap run apps/factorial test
+  ./bootstrap_bin run apps/factorial test
   echo
 }
-test $NATIVE  &&  {
+{
   apps/factorial  ||  ret=$?
   test $ret -eq 120  # factorial(5)
   apps/factorial test
@@ -198,25 +189,25 @@ test $NATIVE  &&  {
 }
 
 echo crenshaw2-1
-./bootstrap translate init.$OS [01]*.subx apps/crenshaw2-1.subx  -o apps/crenshaw2-1
+./bootstrap_bin translate init.linux [01]*.subx apps/crenshaw2-1.subx  -o apps/crenshaw2-1
 test "$1" = 'record'  ||  git diff --exit-code apps/crenshaw2-1
-test $EMULATED  &&  {
-  ./bootstrap run apps/crenshaw2-1 test
+{
+  ./bootstrap_bin run apps/crenshaw2-1 test
   echo
 }
-test $NATIVE  &&  {
+{
   apps/crenshaw2-1 test
   echo
 }
 
 echo crenshaw2-1b
-./bootstrap translate init.$OS [01]*.subx apps/crenshaw2-1b.subx  -o apps/crenshaw2-1b
+./bootstrap_bin translate init.linux [01]*.subx apps/crenshaw2-1b.subx  -o apps/crenshaw2-1b
 test "$1" = 'record'  ||  git diff --exit-code apps/crenshaw2-1b
-test $EMULATED  &&  {
-  ./bootstrap run apps/crenshaw2-1b test
+{
+  ./bootstrap_bin run apps/crenshaw2-1b test
   echo
 }
-test $NATIVE  &&  {
+{
   apps/crenshaw2-1b test
   echo
 }
@@ -226,13 +217,13 @@ test $NATIVE  &&  {
 for phase in hex survey pack assort dquotes tests
 do
   echo $phase
-  ./bootstrap translate init.$OS [01]*.subx apps/subx-params.subx apps/$phase.subx -o apps/$phase
+  ./bootstrap_bin translate init.linux [01]*.subx apps/subx-params.subx apps/$phase.subx -o apps/$phase
   test "$1" = 'record'  ||  git diff --exit-code apps/$phase
-  test $EMULATED  &&  {
-    ./bootstrap run apps/$phase test
+  {
+    ./bootstrap_bin run apps/$phase test
     echo
   }
-  test $NATIVE  &&  {
+  {
     apps/$phase test
     echo
   }
@@ -241,70 +232,67 @@ done
 # Higher-level syntax.
 
 # Certain phases of translation run native beyond this point. We're starting
-# to go beyond functionality of the C++ bootstrap.
+# to go beyond functionality of the C++ bootstrap_bin.
 
 echo sigils
-./bootstrap translate init.$OS [012]*.subx apps/subx-params.subx apps/sigils.subx  -o apps/sigils
+./bootstrap_bin translate init.linux [012]*.subx apps/subx-params.subx apps/sigils.subx  -o apps/sigils
 test "$1" = 'record'  ||  git diff --exit-code apps/sigils
-test $EMULATED  &&  {
-  ./bootstrap run apps/sigils test
+{
+  ./bootstrap_bin run apps/sigils test
   echo
 }
-test $NATIVE  &&  {
+{
   apps/sigils test
   echo
 }
 
-test $NATIVE  ||  { echo 'there are more tests, but you need Linux to run them'; exit 0; }
-
 echo calls
-cat init.$OS [012]*.subx apps/subx-params.subx apps/calls.subx  |  apps/sigils  > a.sigils
-./bootstrap translate a.sigils -o apps/calls
+cat init.linux [012]*.subx apps/subx-params.subx apps/calls.subx  |  apps/sigils  > a.sigils
+./bootstrap_bin translate a.sigils -o apps/calls
 test "$1" = 'record'  ||  git diff --exit-code apps/calls
-test $EMULATED  &&  {
-  ./bootstrap run apps/calls test
+{
+  ./bootstrap_bin run apps/calls test
   echo
 }
-test $NATIVE  &&  {
+{
   apps/calls test
   echo
 }
 
 echo braces
-cat init.$OS [012]*.subx apps/subx-params.subx apps/braces.subx  |  apps/calls  |  apps/sigils  > a.sigils
-./bootstrap translate a.sigils -o apps/braces
+cat init.linux [012]*.subx apps/subx-params.subx apps/braces.subx  |  apps/calls  |  apps/sigils  > a.sigils
+./bootstrap_bin translate a.sigils -o apps/braces
 test "$1" = 'record'  ||  git diff --exit-code apps/braces
-test $EMULATED  &&  {
-  ./bootstrap run apps/braces test
+{
+  ./bootstrap_bin run apps/braces test
   echo
 }
-test $NATIVE  &&  {
+{
   apps/braces test
   echo
 }
 
 echo mu
-cat init.$OS [0-2]*.subx apps/mu.subx  |  apps/braces  |  apps/calls  |  apps/sigils  > a.sigils
-./bootstrap translate a.sigils -o apps/mu
+cat init.linux [0-2]*.subx apps/mu.subx  |  apps/braces  |  apps/calls  |  apps/sigils  > a.sigils
+./bootstrap_bin translate a.sigils -o apps/mu
 test "$1" = 'record'  ||  git diff --exit-code apps/mu
-test $EMULATED  &&  {
-  ./bootstrap run apps/mu test
+{
+  ./bootstrap_bin run apps/mu test
   echo
 }
-test $NATIVE  &&  {
+{
   apps/mu test
   echo
 }
 
-test $NATIVE  ||  exit 0
-echo "== translating using SubX (native only)"
+echo "== translating using the self-hosted translator"
 
 # example programs
 
 for n in `seq 1 12`
 do
   echo ex$n
-  ./translate_subx init.$OS apps/ex$n.subx
+  ./translate_subx init.linux apps/ex$n.subx
   diff apps/ex$n a.elf
 done
 
@@ -313,7 +301,7 @@ done
 for app in factorial crenshaw2-1 crenshaw2-1b
 do
   echo $app
-  ./translate_subx init.$OS [01]*.subx apps/$app.subx
+  ./translate_subx init.linux [01]*.subx apps/$app.subx
   diff apps/$app a.elf
 done
 
@@ -322,77 +310,77 @@ done
 for app in hex survey pack assort dquotes tests
 do
   echo $app
-  ./translate_subx init.$OS [01]*.subx apps/subx-params.subx apps/$app.subx
+  ./translate_subx init.linux [01]*.subx apps/subx-params.subx apps/$app.subx
   diff apps/$app a.elf
 done
 
 for app in sigils calls braces
 do
   echo $app
-  ./translate_subx init.$OS [012]*.subx apps/subx-params.subx apps/$app.subx
+  ./translate_subx init.linux [012]*.subx apps/subx-params.subx apps/$app.subx
   diff apps/$app a.elf
 done
 
 # Mu translator
 echo mu
-./translate_subx init.$OS [0-2]*.subx apps/mu.subx
+./translate_subx init.linux [0-2]*.subx apps/mu.subx
 diff apps/mu a.elf
 
 # Mu programs
 
 echo ex1.mu
 ./translate_mu apps/ex1.mu
-test $EMULATED  &&  {
-  ./bootstrap run a.elf  ||  ret=$?
+{
+  ./bootstrap_bin run a.elf  ||  ret=$?
   test $ret -eq 42  # life, the universe and everything
 }
-test $NATIVE  &&  {
+{
   ./a.elf  ||  ret=$?
   test $ret -eq 42  # life, the universe and everything
 }
 
 echo ex2.mu
 ./translate_mu apps/ex2.mu
-test $EMULATED  &&  {
-  ./bootstrap run a.elf  ||  ret=$?
+{
+  ./bootstrap_bin run a.elf  ||  ret=$?
   test $ret -eq 7
 }
-test $NATIVE  &&  {
+{
   ./a.elf  ||  ret=$?
   test $ret -eq 7
 }
 
 echo ex3.mu
 ./translate_mu apps/ex3.mu
-test $EMULATED  &&  {
-  ./bootstrap run a.elf  ||  ret=$?
+{
+  ./bootstrap_bin run a.elf  ||  ret=$?
   test $ret -eq 55
 }
-test $NATIVE  &&  {
+{
   ./a.elf  ||  ret=$?
   test $ret -eq 55
 }
 
 echo ex3.2.mu
 ./translate_mu apps/ex3.2.mu
-test $EMULATED  &&  {
-  ./bootstrap run a.elf  ||  ret=$?
+{
+  ./bootstrap_bin run a.elf  ||  ret=$?
   test $ret -eq 55
 }
-test $NATIVE  &&  {
+{
   ./a.elf  ||  ret=$?
   test $ret -eq 55
 }
 
 echo factorial.mu
 ./translate_mu apps/factorial.mu
-test $EMULATED  &&  {
-  ./bootstrap run a.elf  ||  ret=$?
+{
+  ./bootstrap_bin run a.elf  ||  ret=$?
   test $ret -eq 120
-  ./bootstrap run a.elf test
+  ./bootstrap_bin run a.elf test
   echo
 }
-test $NATIVE  &&  {
+{
   ./a.elf  ||  ret=$?
   test $ret -eq 120
   ./a.elf test
diff --git a/test_apps_emulated b/test_apps_emulated
new file mode 100755
index 00000000..d70c8341
--- /dev/null
+++ b/test_apps_emulated
@@ -0,0 +1,286 @@
+#!/bin/sh
+# Build and test all included SubX programs on non-Linux platforms:
+#   translate them into ELF binaries
+#   compare the generated binaries with what's already in git
+#   run/test the ELF binaries in emulated mode
+#
+# Example usage:
+#   test_apps_emulated
+#   test_apps_emulated record  # don't compare with what's in git
+#
+# This script is slow. 20+ times slower than running natively on Linux.
+
+set -e
+cd `dirname $0`
+
+./build
+
+echo "== translating using the bootstrap C++ translator"
+
+# example programs
+
+echo ex1
+./bootstrap_bin translate init.linux apps/ex1.subx  -o apps/ex1
+test "$1" = 'record'  ||  git diff --exit-code apps/ex1
+{
+  ./bootstrap_bin run apps/ex1  ||  ret=$?
+  test $ret -eq 42  # life, the universe and everything
+}
+
+echo ex2
+./bootstrap_bin translate init.linux apps/ex2.subx  -o apps/ex2
+test "$1" = 'record'  ||  git diff --exit-code apps/ex2
+{
+  ./bootstrap_bin run apps/ex2  ||  ret=$?
+  test $ret -eq 7  # 3 + 4
+}
+
+echo ex3
+./bootstrap_bin translate init.linux apps/ex3.subx  -o apps/ex3
+test "$1" = 'record'  ||  git diff --exit-code apps/ex3
+{
+  ./bootstrap_bin run apps/ex3  ||  ret=$?
+  test $ret -eq 55  # 1 + 2 + ... + 10
+}
+
+echo ex4
+./bootstrap_bin translate init.linux apps/ex4.subx  -o apps/ex4
+test "$1" = 'record'  ||  git diff --exit-code apps/ex4
+{
+  echo a | ./bootstrap_bin run apps/ex4 >ex4.out  ||  true
+  test `cat ex4.out` = 'a'
+}
+
+echo ex5
+./bootstrap_bin translate init.linux apps/ex5.subx  -o apps/ex5
+test "$1" = 'record'  ||  git diff --exit-code apps/ex5
+{
+  echo a | ./bootstrap_bin run apps/ex5 >ex5.out  ||  true
+  test `cat ex5.out` = 'a'
+}
+
+echo ex6
+./bootstrap_bin translate init.linux apps/ex6.subx  -o apps/ex6
+test "$1" = 'record'  ||  git diff --exit-code apps/ex6
+{
+  ./bootstrap_bin run apps/ex6 >ex6.out  ||  true
+  test "`cat ex6.out`" = 'Hello, world!'
+}
+
+echo ex7
+./bootstrap_bin translate init.linux apps/ex7.subx  -o apps/ex7
+test "$1" = 'record'  ||  git diff --exit-code apps/ex7
+{
+  ./bootstrap_bin run apps/ex7  ||  ret=$?
+  test $ret -eq 97  # 'a'
+}
+
+echo ex8
+./bootstrap_bin translate init.linux apps/ex8.subx  -o apps/ex8
+test "$1" = 'record'  || git diff --exit-code apps/ex8
+{
+  ./bootstrap_bin run apps/ex8 abcd  ||  ret=$?
+  test $ret -eq 4  # length('abcd')
+}
+
+echo ex9
+./bootstrap_bin translate init.linux apps/ex9.subx  -o apps/ex9
+test "$1" = 'record'  || git diff --exit-code apps/ex9
+{
+  ./bootstrap_bin run apps/ex9 z x  ||  ret=$?
+  test $ret -eq 2  # 'z' - 'x'
+}
+
+echo ex10
+./bootstrap_bin translate init.linux apps/ex10.subx  -o apps/ex10
+test "$1" = 'record'  || git diff --exit-code apps/ex10
+{
+  ./bootstrap_bin run apps/ex10 abc abc  ||  ret=$?
+  test $ret -eq 1  # equal
+  ./bootstrap_bin run apps/ex10 abc abcd  # 0; not equal
+}
+
+echo ex11
+./bootstrap_bin translate init.linux apps/ex11.subx  -o apps/ex11
+test "$1" = 'record'  || git diff --exit-code apps/ex11
+{
+  ./bootstrap_bin run apps/ex11
+  echo
+}
+
+echo ex12
+./bootstrap_bin translate init.linux apps/ex12.subx  -o apps/ex12
+test "$1" = 'record'  || git diff --exit-code apps/ex12
+test $EMULATED  &&  ./bootstrap_bin run apps/ex12  # final byte of mmap'd address is well-nigh guaranteed to be 0
+test $NATIVE  &&  apps/ex12
+
+echo ex13
+./bootstrap_bin translate init.linux apps/ex13.subx  -o apps/ex13
+test "$1" = 'record'  ||  git diff --exit-code apps/ex13
+{
+  ./bootstrap_bin run apps/ex13  ||  ret=$?
+  test $ret -eq 1  # 3 == 3
+}
+
+# Larger apps that use the standard library.
+
+echo factorial
+./bootstrap_bin translate init.linux [01]*.subx apps/factorial.subx  -o apps/factorial
+test "$1" = 'record'  ||  git diff --exit-code apps/factorial
+{
+  ./bootstrap_bin run apps/factorial  ||  ret=$?
+  test $ret -eq 120  # factorial(5)
+  ./bootstrap_bin run apps/factorial test
+  echo
+}
+
+echo crenshaw2-1
+./bootstrap_bin translate init.linux [01]*.subx apps/crenshaw2-1.subx  -o apps/crenshaw2-1
+test "$1" = 'record'  ||  git diff --exit-code apps/crenshaw2-1
+{
+  ./bootstrap_bin run apps/crenshaw2-1 test
+  echo
+}
+
+echo crenshaw2-1b
+./bootstrap_bin translate init.linux [01]*.subx apps/crenshaw2-1b.subx  -o apps/crenshaw2-1b
+test "$1" = 'record'  ||  git diff --exit-code apps/crenshaw2-1b
+{
+  ./bootstrap_bin run apps/crenshaw2-1b test
+  echo
+}
+
+# Phases of the self-hosted SubX translator.
+
+for phase in hex survey pack assort dquotes tests
+do
+  echo $phase
+  ./bootstrap_bin translate init.linux [01]*.subx apps/subx-params.subx apps/$phase.subx -o apps/$phase
+  test "$1" = 'record'  ||  git diff --exit-code apps/$phase
+  {
+    ./bootstrap_bin run apps/$phase test
+    echo
+  }
+done
+
+# Higher-level syntax.
+
+# Certain phases of translation run native beyond this point. We're starting
+# to go beyond functionality of the C++ bootstrap_bin.
+
+echo sigils
+./bootstrap_bin translate init.linux [012]*.subx apps/subx-params.subx apps/sigils.subx  -o apps/sigils
+test "$1" = 'record'  ||  git diff --exit-code apps/sigils
+{
+  ./bootstrap_bin run apps/sigils test
+  echo
+}
+
+echo calls
+cat init.linux [012]*.subx apps/subx-params.subx apps/calls.subx  |  ./bootstrap_bin run apps/sigils  > a.sigils
+./bootstrap_bin translate a.sigils -o apps/calls
+test "$1" = 'record'  ||  git diff --exit-code apps/calls
+{
+  ./bootstrap_bin run apps/calls test
+  echo
+}
+
+echo braces
+cat init.linux [012]*.subx apps/subx-params.subx apps/braces.subx  |  ./bootstrap_bin run apps/calls  |  ./bootstrap_bin run apps/sigils  > a.sigils
+./bootstrap_bin translate a.sigils -o apps/braces
+test "$1" = 'record'  ||  git diff --exit-code apps/braces
+{
+  ./bootstrap_bin run apps/braces test
+  echo
+}
+
+echo mu
+cat init.linux [0-2]*.subx apps/mu.subx  |  ./bootstrap_bin run apps/braces  |  ./bootstrap_bin run apps/calls  |  ./bootstrap_bin run apps/sigils  > a.sigils
+./bootstrap_bin translate a.sigils -o apps/mu
+test "$1" = 'record'  ||  git diff --exit-code apps/mu
+{
+  ./bootstrap_bin run apps/mu test
+  echo
+}
+
+echo "== translating using the self-hosted translator"
+
+# example programs
+
+for n in `seq 1 12`
+do
+  echo ex$n
+  ./translate_subx_emulated init.linux apps/ex$n.subx
+  diff apps/ex$n a.elf
+done
+
+# Larger apps that use the standard library.
+
+for app in factorial crenshaw2-1 crenshaw2-1b
+do
+  echo $app
+  ./translate_subx_emulated init.linux [01]*.subx apps/$app.subx
+  diff apps/$app a.elf
+done
+
+# Phases of the self-hosted SubX translator.
+
+for app in hex survey pack assort dquotes tests
+do
+  echo $app
+  ./translate_subx_emulated init.linux [01]*.subx apps/subx-params.subx apps/$app.subx
+  diff apps/$app a.elf
+done
+
+for app in sigils calls braces
+do
+  echo $app
+  ./translate_subx_emulated init.linux [012]*.subx apps/subx-params.subx apps/$app.subx
+  diff apps/$app a.elf
+done
+
+# Mu translator
+echo mu
+./translate_subx_emulated init.linux [0-2]*.subx apps/mu.subx
+diff apps/mu a.elf
+
+# Mu programs
+
+echo ex1.mu
+./translate_mu_emulated apps/ex1.mu
+{
+  ./bootstrap_bin run a.elf  ||  ret=$?
+  test $ret -eq 42  # life, the universe and everything
+}
+
+echo ex2.mu
+./translate_mu_emulated apps/ex2.mu
+{
+  ./bootstrap_bin run a.elf  ||  ret=$?
+  test $ret -eq 7
+}
+
+echo ex3.mu
+./translate_mu_emulated apps/ex3.mu
+{
+  ./bootstrap_bin run a.elf  ||  ret=$?
+  test $ret -eq 55
+}
+
+echo ex3.2.mu
+./translate_mu_emulated apps/ex3.2.mu
+{
+  ./bootstrap_bin run a.elf  ||  ret=$?
+  test $ret -eq 55
+}
+
+echo factorial.mu
+./translate_mu_emulated apps/factorial.mu
+{
+  ./bootstrap_bin run a.elf  ||  ret=$?
+  test $ret -eq 120
+  ./bootstrap_bin run a.elf test
+  echo
+}
+
+exit 0
diff --git a/translate_mu b/translate_mu
index 77098093..b4806bc5 100755
--- a/translate_mu
+++ b/translate_mu
@@ -1,6 +1,5 @@
 #!/bin/sh
-# Translate given Mu programs into ELF binaries.
-# Linux only for now.
+# Translate a given Mu program into an ELF binary on Linux.
 
 set -e
 
diff --git a/translate_mu_debug b/translate_mu_debug
index b6176a82..8bee3906 100755
--- a/translate_mu_debug
+++ b/translate_mu_debug
@@ -1,5 +1,8 @@
 #!/bin/sh
-# Translate Mu programs with debug information on Linux.
+# Translate given Mu files with debug information on Linux.
+#
+# (You _could_ do something similar on other platforms using emulation. But I
+# often find that to be too slow.)
 
 set -e
 
diff --git a/translate_mu_emulated b/translate_mu_emulated
new file mode 100755
index 00000000..7a305b96
--- /dev/null
+++ b/translate_mu_emulated
@@ -0,0 +1,10 @@
+#!/bin/sh
+# Translate a Mu program using emulated mode on Linux or BSD or Mac.
+
+set -e
+
+./build
+
+cat $* [0-9]*.mu    |./bootstrap_bin run apps/mu    > a.subx
+
+./translate_subx_emulated init.linux [0-9]*.subx mu-init.subx a.subx
diff --git a/translate_subx b/translate_subx
index ba36986a..ebde020a 100755
--- a/translate_subx
+++ b/translate_subx
@@ -1,19 +1,6 @@
 #!/bin/sh
-# Translate SubX by running the self-hosted translator natively on Linux.
-#
-# Possible knobs:
-#   Whether to run a phase natively or in emulated mode.
-#     This script is for running natively.
-#   Whether to stop after a phase.
-#     This script assumes inputs are already working so doesn't easily show
-#     which phase an error happens in.
-#   Whether to trace a phase. Whether to always trace or rerun with tracing
-#   enabled after an error.
-#     Leave tracing to other scripts. We save intermediate files so it's easy
-#     to rerun a single phase afterwards.
-#   Whether to run a phase with debug information. (Need to juggle multiple
-#   sets of debug files.)
-#     Again, that's for subsequent scripts.
+# Translate given SubX files by running the self-hosted translator natively on
+# Linux.
 
 set -e
 
diff --git a/translate_subx_debug b/translate_subx_debug
index 021de0a4..7f558fc9 100755
--- a/translate_subx_debug
+++ b/translate_subx_debug
@@ -1,28 +1,32 @@
 #!/bin/sh
-# Translate SubX files with debug information on Linux.
+# Translate given SubX files with debug information on Linux.
 #
-# Mu's core tooling has a gap:
-#   0. The C++ translator 'subx translate' can generate debug information on
-#   Linux or BSD or Mac, but doesn't support any syntax sugar.
-#   1. The self-hosted translator 'translate' runs in emulated mode and can
-#   run on Linux or BSD or Mac. However, syntax sugar passes (sigils and
-#   calls) can be very slow to run emulated.
-#   2. The self-hosted translator 'translate_subx' runs natively on Linux. It is
-#   fast, but you get no trace for runs and zero error-checking on the code
-#   emitted by sigils and calls. Which could still be buggy.
+# Mu provides 3 canonical ways to translate unsafe SubX programs:
+#   0. The C++ translator 'bootstrap translate' can generate traces for
+#   debugging on Linux or BSD or Mac, but doesn't support any syntax sugar.
+#   1. The self-hosted translator can be run natively on Linux using
+#   'translate_subx'. It is fast and supports all syntax sugar, but you get no
+#   trace for debugging.
+#   2. The self-hosted translator can be run emulated on Linux or BSD or Mac
+#   using 'translate_subx_emulated'. It supports all syntax sugar. However, it
+#   can be slow if you generate traces.
 #
-# This script is a hack to get the best of all worlds. We run natively what we
-# must, and leverage as much debug information as possible. This arrangement
-# is snappy but requires Linux just like 'translate_subx'. You also are on your
-# own to mentally map desugared instructions in traces and error messages back
-# to the original sources.
+# This script fills in the gap above by stitching together aspects from
+# multiple approaches. It translates syntax sugar natively, but emulates lower
+# levels using the C++ translator. The result is complete and relatively fast
+# even when generating traces.
+#
+# The cost: it needs Linux. There is no script to generate traces while
+# running emulated on BSD or Mac. That's often impractically slow.
 
 set -e
 
+./build
+
 cat $*          |apps/braces          > a.braces
 cat a.braces    |apps/calls           > a.calls
 cat a.calls     |apps/sigils          > a.sigils
 
-./bootstrap --debug translate a.sigils -o a.elf
+./bootstrap_bin --debug translate a.sigils -o a.elf
 
 chmod +x a.elf
diff --git a/translate_subx_emulated b/translate_subx_emulated
index 0f013dfc..4a75c03b 100755
--- a/translate_subx_emulated
+++ b/translate_subx_emulated
@@ -1,20 +1,8 @@
 #!/bin/sh
-# Translate SubX by running the self-hosted translator in emulated mode on
-# Linux or BSD or Mac.
+# Translate given SubX files by running the self-hosted translator in emulated
+# mode on Linux or BSD or Mac.
 #
-# Possible knobs:
-#   Whether to run a phase natively or in emulated mode.
-#     Just always emulate for now since we debug on non-Linux.
-#   Whether to stop after a phase.
-#     Just always run all phases, but print out phases so it's clear where an
-#     error happens.
-#   Whether to trace a phase. Whether to always trace or rerun with tracing
-#   enabled after an error.
-#     Leave tracing to other scripts. We save intermediate files so it's easy
-#     to rerun a single phase afterwards.
-#   Whether to run a phase with debug information. (Need to juggle multiple
-#   sets of debug files.)
-#     Again, that's for subsequent scripts.
+# We _could_ generate traces here, but that's often extremely slow.
 
 set -e
 
@@ -24,9 +12,10 @@ cat $*          |./bootstrap_bin run apps/braces   > a.braces
 cat a.braces    |./bootstrap_bin run apps/calls    > a.calls
 cat a.calls     |./bootstrap_bin run apps/sigils   > a.sigils
 cat a.sigils    |./bootstrap_bin run apps/tests    > a.tests
-cat a.tests     |./bootstrap_bin run apps/dquotes  > a.dquotes
-cat a.dquotes   |./bootstrap_bin run apps/assort   > a.assort
-cat a.assort    |./bootstrap_bin run apps/pack     > a.pack
+cat a.tests     |./bootstrap_bin run apps/assort   > a.assort
+cat a.assort    |./bootstrap_bin run apps/dquotes  > a.dquotes
+cat a.dquotes   |./bootstrap_bin run apps/assort   > a.assort2
+cat a.assort2   |./bootstrap_bin run apps/pack     > a.pack
 cat a.pack      |./bootstrap_bin run apps/survey   > a.survey
 cat a.survey    |./bootstrap_bin run apps/hex      > a.elf