about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik Agaram <vc@akkartik.com>2020-11-01 22:02:13 -0800
committerKartik Agaram <vc@akkartik.com>2020-11-01 22:02:13 -0800
commit17623a628aee2429a5a8d8b65bd84235f6be0e3f (patch)
tree373ac0b29678e96d24faf1cb8d8c853ca6b06279
parentcc7dcdc3b8bb6c133ecdbc97d1c134d30f1500f5 (diff)
downloadmu-17623a628aee2429a5a8d8b65bd84235f6be0e3f.tar.gz
7154
-rw-r--r--apps/ex1.mu4
-rw-r--r--apps/ex2.mu10
-rw-r--r--apps/ex3.2.mu5
-rw-r--r--apps/ex3.mu5
-rwxr-xr-xapps/factorialbin44985 -> 44992 bytes
-rw-r--r--apps/factorial.mu56
-rw-r--r--apps/factorial.subx64
-rw-r--r--apps/factorial2.subx59
-rw-r--r--apps/factorial3.subx39
-rw-r--r--apps/factorial4.subx31
-rw-r--r--apps/hello.mu4
-rw-r--r--apps/mulisp.subx233
12 files changed, 141 insertions, 369 deletions
diff --git a/apps/ex1.mu b/apps/ex1.mu
index f6d67001..41609fac 100644
--- a/apps/ex1.mu
+++ b/apps/ex1.mu
@@ -8,6 +8,6 @@
 #   $ echo $?
 #   42
 
-fn main -> result/ebx: int {
-  result <- copy 0x2a  # Mu requires hexadecimal
+fn main -> _/ebx: int {
+  return 0x2a  # Mu requires hexadecimal
 }
diff --git a/apps/ex2.mu b/apps/ex2.mu
index c3a73874..4ecfde9f 100644
--- a/apps/ex2.mu
+++ b/apps/ex2.mu
@@ -7,11 +7,13 @@
 #   $ echo $?
 #   7
 
-fn main -> result/ebx: int {
-  result <- do-add 3 4
+fn main -> _/ebx: int {
+  var result/eax: int <- do-add 3 4
+  return result
 }
 
-fn do-add a: int, b: int -> result/ebx: int {
-  result <- copy a
+fn do-add a: int, b: int -> _/eax: int {
+  var result/ebx: int <- copy a
   result <- add b
+  return result
 }
diff --git a/apps/ex3.2.mu b/apps/ex3.2.mu
index dc97bf7a..d06928fe 100644
--- a/apps/ex3.2.mu
+++ b/apps/ex3.2.mu
@@ -6,7 +6,7 @@
 #   $ echo $?
 #   55
 
-fn main -> result/ebx: int {
+fn main -> _/ebx: int {
   # populate a
   var a: (array int 0xb)  # 11; we waste index 0
   var i/ecx: int <- copy 1
@@ -19,7 +19,7 @@ fn main -> result/ebx: int {
     loop
   }
   # sum
-  result <- copy 0
+  var result/edx: int <- copy 0
   i <- copy 1
   {
     compare i, 0xb
@@ -29,4 +29,5 @@ fn main -> result/ebx: int {
     i <- increment
     loop
   }
+  return result
 }
diff --git a/apps/ex3.mu b/apps/ex3.mu
index efe6a317..d7e73140 100644
--- a/apps/ex3.mu
+++ b/apps/ex3.mu
@@ -7,8 +7,8 @@
 #   $ echo $?
 #   55
 
-fn main -> result/ebx: int {
-  result <- copy 0
+fn main -> _/ebx: int {
+  var result/ebx: int <- copy 0
   var i/eax: int <- copy 1
   {
     compare i, 0xa
@@ -17,4 +17,5 @@ fn main -> result/ebx: int {
     i <- increment
     loop
   }
+  return result
 }
diff --git a/apps/factorial b/apps/factorial
index 5a567172..5eaecd80 100755
--- a/apps/factorial
+++ b/apps/factorial
Binary files differdiff --git a/apps/factorial.mu b/apps/factorial.mu
index 362b68c6..bb335b0a 100644
--- a/apps/factorial.mu
+++ b/apps/factorial.mu
@@ -16,21 +16,19 @@
 #
 # Compare apps/factorial4.subx
 
-fn factorial n: int -> result/eax: int {
+fn factorial n: int -> _/eax: int {
   compare n, 1
+  # if (n <= 1) return 1
   {
     break-if->
-    # n <= 1; return 1
-    result <- copy 1
-  }
-  {
-    break-if-<=
-    # n > 1; return n * factorial(n-1)
-    var tmp/ecx: int <- copy n
-    tmp <- decrement
-    result <- factorial tmp
-    result <- multiply n
+    return 1
   }
+  # n > 1; return n * factorial(n-1)
+  var tmp/ecx: int <- copy n
+  tmp <- decrement
+  var result/eax: int <- factorial tmp
+  result <- multiply n
+  return result
 }
 
 fn test-factorial {
@@ -38,27 +36,25 @@ fn test-factorial {
   check-ints-equal result, 0x78, "F - test-factorial"
 }
 
-fn main args-on-stack: (addr array (addr array byte)) -> exit-status/ebx: int {
+fn main args-on-stack: (addr array (addr array byte)) -> _/ebx: int {
   var args/eax: (addr array addr array byte) <- copy args-on-stack
   # len = length(args)
   var len/ecx: int <- length args
-  $main-body: {
-    # if (len <= 1) return factorial(5)
-    compare len, 1
-    {
-      break-if->
-      var tmp/eax: int <- factorial 5
-      exit-status <- copy tmp
-      break $main-body
-    }
-    # if (args[1] == "test") run-tests()
-    var tmp2/ecx: (addr addr array byte) <- index args, 1
-    var tmp3/eax: boolean <- string-equal? *tmp2, "test"
-    compare tmp3, 0
-    {
-      break-if-=
-      run-tests
-      exit-status <- copy 0  # TODO: get at Num-test-failures somehow
-    }
+  # if (len <= 1) return factorial(5)
+  compare len, 1
+  {
+    break-if->
+    var exit-status/eax: int <- factorial 5
+    return exit-status
+  }
+  # if (args[1] == "test") run-tests()
+  var tmp2/ecx: (addr addr array byte) <- index args, 1
+  var tmp3/eax: boolean <- string-equal? *tmp2, "test"
+  compare tmp3, 0
+  {
+    break-if-=
+    run-tests
+    # TODO: get at Num-test-failures somehow
   }
+  return 0
 }
diff --git a/apps/factorial.subx b/apps/factorial.subx
index b027696e..586592f5 100644
--- a/apps/factorial.subx
+++ b/apps/factorial.subx
@@ -18,21 +18,21 @@
 # . op          subop               mod             rm32          base        index         scale       r32
 # . 1-3 bytes   3 bits              2 bits          3 bits        3 bits      3 bits        2 bits      2 bits      0/1/2/4 bytes   0/1/2/4 bytes
 
-factorial:  # n: int -> int/eax
+factorial:  # n: int -> _/eax: int
     # . prologue
     55/push-ebp
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
-    53/push-ebx
+    51/push-ecx
     # if (n <= 1) return 1
     b8/copy-to-eax  1/imm32
     81          7/subop/compare     1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         1/imm32           # compare *(ebp+8)
     7e/jump-if-<=  $factorial:end/disp8
-    # var ebx: int = n-1
-    8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         3/r32/ebx   8/disp8         .                 # copy *(ebp+8) to ebx
-    4b/decrement-ebx
+    # var ecx: int = n-1
+    8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
+    49/decrement-ecx
     # var eax: int = factorial(n-1)
     # . . push args
-    53/push-ebx
+    51/push-ecx
     # . . call
     e8/call  factorial/disp32
     # . . discard args
@@ -41,8 +41,9 @@ factorial:  # n: int -> int/eax
     f7          4/subop/multiply    1/mod/*+disp8   5/rm32/ebp    .           .                                     8/disp8         .                 # multiply *(ebp+8) into eax
     # TODO: check for overflow
 $factorial:end:
+    # restore registers
+    59/pop-to-ecx
     # . epilogue
-    5b/pop-to-ebx
     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
     5d/pop-to-ebp
     c3/return
@@ -71,7 +72,7 @@ Entry:  # run tests if necessary, compute `factorial(5)` if not
     # . prologue
     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 
-    # initialize heap
+    # initialize heap (needed by tests elsewhere)
     # . Heap = new-segment(Heap-size)
     # . . push args
     68/push  Heap/imm32
@@ -81,29 +82,10 @@ Entry:  # run tests if necessary, compute `factorial(5)` if not
     # . . discard args
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 
-    # - if argc > 1 and argv[1] == "test", then return run_tests()
-    # if (argc <= 1) goto run-main
-    81          7/subop/compare     1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0/disp8         1/imm32           # compare *ebp
-    7e/jump-if-<=  $run-main/disp8
-    # if (!kernel-string-equal?(argv[1], "test")) goto run-main
-    # . eax = kernel-string-equal?(argv[1], "test")
-    # . . push args
-    68/push  "test"/imm32
-    ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
-    # . . call
-    e8/call  kernel-string-equal?/disp32
-    # . . discard args
-    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-    # . if (eax == false) goto run-main
-    3d/compare-eax-and  0/imm32/false
-    74/jump-if-=  $run-main/disp8
-    # run-tests()
-    e8/call  run-tests/disp32
-    # syscall(exit, *Num-test-failures)
-    8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/ebx   Num-test-failures/disp32          # copy *Num-test-failures to ebx
-    eb/jump  $main:end/disp8
+    # if (argc <= 1) return factorial(5)
 $run-main:
-    # - otherwise print factorial(5)
+    81          7/subop/compare     1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0/disp8         1/imm32           # compare *ebp
+    7f/jump-if->  $main:run-tests/disp8
     # eax = factorial(5)
     # . . push args
     68/push  5/imm32
@@ -143,6 +125,28 @@ $run-main:
     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     #
     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # copy eax to ebx
+    eb/jump  $main:end/disp8
+$main:run-tests:
+    # otherwise if first arg is "test", then return run_tests()
+    # if (!kernel-string-equal?(argv[1], "test")) goto do-nothing
+    # . eax = kernel-string-equal?(argv[1], "test")
+    # . . push args
+    68/push  "test"/imm32
+    ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
+    # . . call
+    e8/call  kernel-string-equal?/disp32
+    # . . discard args
+    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+    # . if (eax == false) goto do-nothing
+    3d/compare-eax-and  0/imm32/false
+    74/jump-if-=  $main:do-nothing/disp8
+    # run-tests()
+    e8/call  run-tests/disp32
+    # exit(*Num-test-failures)
+    8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/ebx   Num-test-failures/disp32          # copy *Num-test-failures to ebx
+    eb/jump  $main:end/disp8
+$main:do-nothing:
+    bb/copy-to-ebx  0/imm32
 $main:end:
     e8/call  syscall_exit/disp32
 
diff --git a/apps/factorial2.subx b/apps/factorial2.subx
index 7d303227..55723f75 100644
--- a/apps/factorial2.subx
+++ b/apps/factorial2.subx
@@ -20,31 +20,33 @@
 
 == code
 
-factorial:  # n: int -> int/eax
+factorial:  # n: int -> _/eax: int
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
-    53/push-ebx
+    # save registers
+    51/push-ecx
     # if (n <= 1) return 1
     b8/copy-to-eax 1/imm32
     81 7/subop/compare *(ebp+8) 1/imm32
     7e/jump-if-<= $factorial:end/disp8
-    # var ebx: int = n-1
-    8b/-> *(ebp+8) 3/r32/ebx
-    4b/decrement-ebx
-    # var eax: int = factorial(n-1)
+    # n > 1; return n * factorial(n-1)
+    8b/-> *(ebp+8) 1/r32/ecx
+    49/decrement-ecx
+    # var tmp/eax: int = factorial(n-1)
     # . . push args
-    53/push-ebx
+    51/push-ecx
     # . . call
     e8/call factorial/disp32
     # . . discard args
     81 0/subop/add %esp 4/imm32
-    # return n * factorial(n-1)
+    # return n * tmp
     f7 4/subop/multiply-into-eax *(ebp+8)
     # TODO: check for overflow
 $factorial:end:
+    # restore registers
+    59/pop-to-ecx
     # . epilogue
-    5b/pop-to-ebx
     89/<- %esp 5/r32/ebp
     5d/pop-to-ebp
     c3/return
@@ -73,7 +75,7 @@ Entry:  # run tests if necessary, compute `factorial(5)` if not
     # . prologue
     89/<- %ebp 4/r32/esp
 
-    # initialize heap
+    # initialize heap (needed by tests elsewhere)
     # . Heap = new-segment(Heap-size)
     # . . push args
     68/push Heap/imm32
@@ -83,11 +85,21 @@ Entry:  # run tests if necessary, compute `factorial(5)` if not
     # . . discard args
     81 0/subop/add %esp 8/imm32
 
-    # - if argc > 1 and argv[1] == "test", then return run_tests()
-    # if (argc <= 1) goto run-main
+    # if (argc <= 1) return factorial(5)
     81 7/subop/compare *ebp 1/imm32
-    7e/jump-if-<= $run-main/disp8
-    # if (!kernel-string-equal?(argv[1], "test")) goto run-main
+    7f/jump-if-> $main:run-tests/disp8
+    # . . push args
+    68/push 5/imm32
+    # . . call
+    e8/call factorial/disp32
+    # . . discard args
+    81 0/subop/add %esp 4/imm32
+    # .
+    89/<- %ebx 0/r32/eax
+    eb/jump $main:end/disp8
+$main:run-tests:
+    # otherwise if first arg is "test", then return run_tests()
+    # if (!kernel-string-equal?(argv[1], "test")) goto do-nothing
     # . eax = kernel-string-equal?(argv[1], "test")
     # . . push args
     68/push "test"/imm32
@@ -96,24 +108,15 @@ Entry:  # run tests if necessary, compute `factorial(5)` if not
     e8/call kernel-string-equal?/disp32
     # . . discard args
     81 0/subop/add %esp 8/imm32
-    # . if (eax == false) goto run-main
+    # . if (eax == false) goto do-nothing
     3d/compare-eax-and 0/imm32/false
-    74/jump-if-= $run-main/disp8
+    74/jump-if-= $main:do-nothing/disp8
     # run-tests()
     e8/call run-tests/disp32
-    # syscall(exit, *Num-test-failures)
+    # exit(*Num-test-failures)
     8b/-> *Num-test-failures 3/r32/ebx
     eb/jump $main:end/disp8
-$run-main:
-    # - otherwise return factorial(5)
-    # ebx = factorial(5)
-    # . . push args
-    68/push 5/imm32
-    # . . call
-    e8/call factorial/disp32
-    # . . discard args
-    81 0/subop/add %esp 4/imm32
-    #
-    89/<- %ebx 0/r32/eax
+$main:do-nothing:
+    bb/copy-to-ebx 0/imm32
 $main:end:
     e8/call  syscall_exit/disp32
diff --git a/apps/factorial3.subx b/apps/factorial3.subx
index eb07b5ca..b2624fe7 100644
--- a/apps/factorial3.subx
+++ b/apps/factorial3.subx
@@ -21,27 +21,25 @@
 
 == code
 
-factorial:  # n: int -> int/eax
+factorial:  # n: int -> _/eax: int
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
     # save registers
-    53/push-ebx
+    51/push-ecx
     # if (n <= 1) return 1
     b8/copy-to-eax 1/imm32
     81 7/subop/compare *(ebp+8) 1/imm32
     7e/jump-if-<= $factorial:end/disp8
-    # var ebx: int = n-1
-    8b/-> *(ebp+8) 3/r32/ebx
-    4b/decrement-ebx
-    #
-    (factorial %ebx)  # => eax
-    # return n * factorial(n-1)
+    # n > 1; return n * factorial(n-1)
+    8b/-> *(ebp+8) 1/r32/ecx
+    49/decrement-ecx
+    (factorial %ecx)  # => eax
     f7 4/subop/multiply-into-eax *(ebp+8)
     # TODO: check for overflow
 $factorial:end:
     # restore registers
-    5b/pop-to-ebx
+    59/pop-to-ecx
     # . epilogue
     89/<- %esp 5/r32/ebp
     5d/pop-to-ebp
@@ -59,23 +57,24 @@ Entry:  # run tests if necessary, compute `factorial(5)` if not
     # initialize heap (needed by tests elsewhere)
     (new-segment *Heap-size Heap)
 
-    # - if argc > 1 and argv[1] == "test", then return run_tests()
-    # if (argc <= 1) goto run-main
+    # if (argc <= 1) return factorial(5)
     81 7/subop/compare *ebp 1/imm32
-    7e/jump-if-<= $run-main/disp8
-    # if (!kernel-string-equal?(argv[1], "test")) goto run-main
+    7f/jump-if-> $main:run-tests/disp8
+    (factorial 5)  # => eax
+    89/<- %ebx 0/r32/eax
+    eb/jump $main:end/disp8
+$main:run-tests:
+    # otherwise if first arg is "test", then return run_tests()
+    # if (!kernel-string-equal?(argv[1], "test")) goto do-nothing
     (kernel-string-equal? *(ebp+8) "test")  # => eax
-    # . if (eax == false) goto run-main
     3d/compare-eax-and 0/imm32/false
-    74/jump-if-= $run-main/disp8
+    74/jump-if-= $main:do-nothing/disp8
     #
     (run-tests)
-    # syscall(exit, *Num-test-failures)
+    # exit(*Num-test-failures)
     8b/-> *Num-test-failures 3/r32/ebx
     eb/jump $main:end/disp8
-$run-main:
-    # - otherwise
-    (factorial 5)  # => eax
-    89/<- %ebx 0/r32/eax
+$main:do-nothing:
+    bb/copy-to-ebx 0/imm32
 $main:end:
     e8/call  syscall_exit/disp32
diff --git a/apps/factorial4.subx b/apps/factorial4.subx
index 6228f9ab..e39c2178 100644
--- a/apps/factorial4.subx
+++ b/apps/factorial4.subx
@@ -22,7 +22,7 @@
 
 == code
 
-factorial:  # n: int -> int/eax
+factorial:  # n: int -> _/eax: int
     # . prologue
     55/push-ebp
     89/<- %ebp 4/r32/esp
@@ -33,16 +33,15 @@ factorial:  # n: int -> int/eax
     {
       7f/jump-if-> break/disp8
       b8/copy-to-eax 1/imm32
+      eb/jump $factorial:end/disp8
     }
-    # if (n > 1) return n * factorial(n-1)
-    {
-      7e/jump-if-<= break/disp8
-      # var tmp/ecx: int = n-1
-      8b/-> *(ebp+8) 1/r32/ecx
-      49/decrement-ecx
-      (factorial %ecx)  # => eax
-      f7 4/subop/multiply-into-eax *(ebp+8)
-    }
+    # n > 1; return n * factorial(n-1)
+    8b/-> *(ebp+8) 1/r32/ecx
+    49/decrement-ecx
+    (factorial %ecx)  # => eax
+    f7 4/subop/multiply-into-eax *(ebp+8)
+    # TODO: check for overflow
+$factorial:end:
     # restore registers
     59/pop-to-ecx
     # . epilogue
@@ -70,20 +69,20 @@ Entry:  # run tests if necessary, compute `factorial(5)` if not
       # ebx = factorial(5)
       (factorial 5)  # => eax
       89/<- %ebx 0/r32/eax
+      eb/jump $main:end/disp8
     }
-    # otherwise if an arg exists and is "test", then return run_tests()
+    # otherwise if first arg is "test", then return run_tests()
     {
-      # if (argc <= 1) break
-      81 7/subop/compare *ebp 1/imm32
-      7e/jump-if-<= break/disp8
       # if (!kernel-string-equal?(argv[1], "test")) break
       (kernel-string-equal? *(ebp+8) "test")  # => eax
       3d/compare-eax-and 0/imm32/false
       74/jump-if-= break/disp8
       #
       (run-tests)
-      # ebx = *Num-test-failures
+      # exit(*Num-test-failures)
       8b/-> *Num-test-failures 3/r32/ebx
+      eb/jump $main:end/disp8
     }
-
+    bb/copy-to-ebx 0/imm32
+$main:end:
     e8/call  syscall_exit/disp32
diff --git a/apps/hello.mu b/apps/hello.mu
index a305edf0..5a656b96 100644
--- a/apps/hello.mu
+++ b/apps/hello.mu
@@ -4,7 +4,7 @@
 #   $ ./translate_mu apps/hello.mu
 #   $ ./a.elf
 
-fn main -> exit-status/ebx: int {
+fn main -> _/ebx: int {
   print-string 0, "Hello world!\n"
-  exit-status <- copy 0
+  return 0
 }
diff --git a/apps/mulisp.subx b/apps/mulisp.subx
deleted file mode 100644
index b433fa17..00000000
--- a/apps/mulisp.subx
+++ /dev/null
@@ -1,233 +0,0 @@
-# Toy lisp interpreter. Incomplete.
-#
-# To run:
-#   $ ./translate_subx init.linux 0*.subx apps/mulisp.subx
-#   $ ./a.elf
-#   42
-#   => 42
-#   ^D
-#   $
-
-== code
-
-Entry:  # run tests if necessary, a REPL if not
-    # . prologue
-    89/<- %ebp 4/r32/esp
-    # initialize heap
-    (new-segment *Heap-size Heap)
-    {
-      # if (argc <= 1) break
-      81 7/subop/compare *ebp 1/imm32
-      7e/jump-if-<= break/disp8
-      # if (argv[1] != "test")) break
-      (kernel-string-equal? *(ebp+8) "test")  # => eax
-      3d/compare-eax-and 0/imm32/false
-      74/jump-if-= break/disp8
-      #
-      (run-tests)
-      # syscall(exit, *Num-test-failures)
-      8b/-> *Num-test-failures 3/r32/ebx
-      eb/jump $main:end/disp8
-    }
-    (repl Stdin Stdout)
-    # syscall(exit, 0)
-    bb/copy-to-ebx 0/imm32
-$main:end:
-    e8/call  syscall_exit/disp32
-
-# Data structures
-#
-# Lisp is dynamically typed. Values always carry around knowledge of their
-# type.
-#
-# There's several types of types in the description below, so we need a
-# glossary and notational convention to disambiguate:
-#   lisp type: what Lisp code can see. Looks how you type it at the prompt.
-#     nil num char string symbol pair array
-#   type tag: the numeric code for a lisp type. All caps.
-#     NIL NUM CHAR STRING SYMBOL PAIR ARRAY
-#   memory type: a type specifying memory layout at the SubX level. Starts
-#   with a '$'.
-#     $int $array $(addr _)
-#
-# Lisp values are represented in memory by the _cell_ data structure. A cell
-# is 12 bytes long:
-#   tag: $int (4 bytes; we're not concerned about wasting space)
-#   data: 8 bytes whose contents and meaning depend on tag
-#
-# What values of the different Lisp types look like in memory:
-#   - nil: cell{ tag: 0/NIL, data: 0 0 }
-#   - num: cell{ tag: 1/NUM, data: $int 0 }
-#     data contains the number
-#   - char: cell{ tag: 2/CHAR, data: $int 0 }
-#     data contains the utf-8 code of the character (no compound glyphs, no
-#     modifiers, etc., etc.)
-#   - string: cell{ tag: 3/STRING, data: $(addr stream byte)
-#     data contains an (addr array byte) containing the string in utf-8
-#   - symbol: cell{ tag: 4/SYMBOL, data: $(addr array byte) 0 }
-#     data contains an (addr array byte) containing the name of the symbol in utf-8
-#     alternatively, data could contain an index into the table of interned symbols
-#   - pair: cell{ tag: 5/PAIR, data: $(addr cell) $(addr cell)  }
-#     data contains pointers to car and cdr
-#   - array: cell{ tag: 6/ARRAY, data: $tag $(addr stream data)
-#     data contains a pointer to an array of 8-byte data fields and the common
-#     tag for them all
-
-repl:  # in: (addr buffered-file), out: (addr buffered-file)
-    # . prologue
-    55/push-ebp
-    89/<- %ebp 4/r32/esp
-    # . save registers
-    50/push-eax
-    {
-      (lisp-read Stdin)  # => eax: (handle cell)
-      # if (eax == 0) break
-      3d/compare-eax-and 0/imm32
-      74/jump-if-= break/disp8
-      #
-      (lisp-eval %eax)  # => eax: (handle cell)
-      (lisp-print Stdout %eax)
-      eb/jump loop/disp8
-    }
-$repl:end:
-    # . restore registers
-    58/pop-to-eax
-    # . epilogue
-    89/<- %esp 5/r32/ebp
-    5d/pop-to-ebp
-    c3/return
-
-# numbers start with a digit and are always in hex
-# characters start with a backslash
-# pairs start with '('
-# arrays start with '['
-# symbols start with anything else but quote, backquote, unquote or splice
-# only one s-expression per line
-lisp-read:  # in: (addr buffered-file) -> eax: (handle cell)
-    # . prologue
-    55/push-ebp
-    89/<- %ebp 4/r32/esp
-    # . save registers
-    51/push-ecx
-    # var s/ecx: (stream byte 512)
-    81 5/subop/subtract %esp 0x200/imm32
-    68/push 0x200/imm32/size
-    68/push 0/imm32/read
-    68/push 0/imm32/write
-    89/<- %ecx 4/r32/esp
-    {
-      # read line into s
-      (clear-stream %ecx)
-      (read-line-buffered *(ebp+8) %ecx)
-      # if (s->write == 0) return null
-      {
-        81 7/subop/compare *ecx 0/imm32
-        75/jump-if-!= break/disp8
-        b8/copy-to-eax 0/imm32/eof
-        eb/jump $lisp-read:end/disp8
-      }
-      # ...
-#?       eb/jump loop/disp8
-    }
-    # return s
-    89/<- %eax 1/r32/ecx
-$lisp-read:end:
-    # . reclaim locals
-    81 0/subop/add %esp 0x20c/imm32
-    # . restore registers
-    59/pop-to-ecx
-    # . epilogue
-    89/<- %esp 5/r32/ebp
-    5d/pop-to-ebp
-    c3/return
-
-# lisp-read:  in: (addr buffered-file) -> (handle cell)
-#   token tmp = next-mulisp-token(in)
-#   if is-int(tmp) return cell(tmp)
-#   if is-string(tmp) return cell(tmp)
-#   if is-pair(tmp) ...
-#   if is-array(tmp) ...
-
-next-mulisp-token:  # in: (addr buffered-file), line: (addr stream byte), result: (addr slice)
-    # pseudocode:
-    #   if (line->read >= line->write)
-    #     read-line-buffered(in, line)
-    #     recurse
-    #   if (line->data[line->read] == ' ')
-    #     skip-chars-matching-whitespace(line)
-    #     recurse
-    #   if (line->data[line->read] == '#')
-    #     read-line-buffered(in, line)
-    #     recurse
-    #   eax = line->data[line->read]
-    #   if (eax == '"')
-    #     result->start = &line->data[line->read]
-    #     skip-string(in)
-    #     result->end = &line->data[line->read]
-    #     return
-    #   if (is-digit(eax))
-    #     result->start = &line->data[line->read]
-    #     skip-hex-int(in)
-    #     result->end = &line->data[line->read]
-    #     return
-    #   if (eax in '(' ')' '[' ']')
-    #     result->start = &line->data[line->read]
-    #     ++line->read
-    #     result->en = &line->data[line->read]
-    #     return
-    #   else
-    #     result->start = &line->data[line->read]
-    #     skip-lisp-word(line)
-    #     result->en = &line->data[line->read]
-    #     return
-    #
-    # . prologue
-    55/push-ebp
-    89/<- %ebp 4/r32/esp
-    # . save registers
-$next-mulisp-token:end:
-    # . reclaim locals
-    # . restore registers
-    # . epilogue
-    89/<- %esp 5/r32/ebp
-    5d/pop-to-ebp
-    c3/return
-
-new-int-cell:  # in: (addr slice) -> eax: (handle cell)
-
-new-string-cell:  # in: (addr slice) -> eax: (handle cell)
-
-lisp-eval:  # in: (addr cell) -> eax: (handle cell)
-    # . prologue
-    55/push-ebp
-    89/<- %ebp 4/r32/esp
-    # . save registers
-    8b/-> *(ebp+8) 0/r32/eax
-$lisp-eval:end:
-    # . restore registers
-    # . epilogue
-    89/<- %esp 5/r32/ebp
-    5d/pop-to-ebp
-    c3/return
-
-lisp-print:  # out: (addr buffered-file), x: (addr cell)
-    # . prologue
-    55/push-ebp
-    89/<- %ebp 4/r32/esp
-    # . save registers
-    # write(x)
-    (write-buffered Stdout "=> ")
-    (write-stream-data Stdout *(ebp+0xc))
-    (flush Stdout)
-$lisp-print:end:
-    # . restore registers
-    # . epilogue
-    89/<- %esp 5/r32/ebp
-    5d/pop-to-ebp
-    c3/return
-
-== data
-
-Nil:
-  0/imm32/tag
-  0/imm32/data