From 14a380525ed2df295463b1906b718cb26871df3b Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Fri, 30 Nov 2018 16:54:01 -0800 Subject: 4809 - subx: html with 5 colors for comments --- html/subx/apps/crenshaw2-1b.subx.html | 1642 +++++++++++++++++---------------- 1 file changed, 822 insertions(+), 820 deletions(-) (limited to 'html/subx/apps/crenshaw2-1b.subx.html') diff --git a/html/subx/apps/crenshaw2-1b.subx.html b/html/subx/apps/crenshaw2-1b.subx.html index 38cddfdb..2d5015b9 100644 --- a/html/subx/apps/crenshaw2-1b.subx.html +++ b/html/subx/apps/crenshaw2-1b.subx.html @@ -11,15 +11,21 @@ @@ -54,13 +60,13 @@ if ('onhashchange' in window) {
-  1 ## Port of https://github.com/akkartik/crenshaw/blob/master/tutor2.1.pas
-  2 # Corresponds to the section "single digits" in https://compilers.iecc.com/crenshaw/tutor2.txt
-  3 # except that we support numbers of multiple digits.
+  1 # Port of https://github.com/akkartik/crenshaw/blob/master/tutor2.1.pas
+  2 # which corresponds to the section "single digits" in https://compilers.iecc.com/crenshaw/tutor2.txt
+  3 # except that we support hex numbers of multiple digits.
   4 #
-  5 # To run (from the subx directory):
-  6 #   $ ./subx translate *.subx apps/crenshaw2-1.subx -o crenshaw2-1
-  7 #   $ echo '1a'  |./subx run apps/crenshaw2-1
+  5 # To run (from the subx/ directory):
+  6 #   $ ./subx translate *.subx apps/crenshaw2-1b.subx -o crenshaw2-1b
+  7 #   $ echo '1a'  |./subx run apps/crenshaw2-1b
   8 # Expected output:
   9 #   # syscall(exit, 1a)
  10 #   bb/copy-to-EBX  3/imm32
@@ -68,828 +74,824 @@ if ('onhashchange' in window) {
  12 #   cd/syscall  0x80/imm8
  13 #
  14 # To run the generated output:
- 15 #   $ echo '1a'  |./subx run apps/crenshaw2-1 > z1.subx
+ 15 #   $ echo '1a'  |./subx run apps/crenshaw2-1b > z1.subx
  16 #   $ ./subx translate z1.subx -o z1
  17 #   $ ./subx run z1
  18 #   $ echo $?
  19 #   26  # 0x1a in decimal
  20 #
  21 # Stdin must contain just a single hex digit. Other input will print an error:
- 22 #   $ echo 'xyz'  |./subx run apps/crenshaw2-1
+ 22 #   $ echo 'xyz'  |./subx run apps/crenshaw2-1b
  23 #   Error: integer expected
  24 #
  25 # Names in this file sometimes follow Crenshaw's original rather than my usual
  26 # naming conventions.
  27 
  28 == code
- 29 # instruction                     effective address                                                   operand     displacement    immediate
- 30 # op          subop               mod             rm32          base        index         scale       r32
- 31 # 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
+ 29 #   instruction                     effective address                                                   register    displacement    immediate
+ 30 # . op          subop               mod             rm32          base        index         scale       r32
+ 31 # . 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
  32 
  33 # main: run tests if necessary, call 'compile' if not
- 34   # prolog
- 35   89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 36   # if (argc > 1)
- 37   81          7/subop/compare     1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0/disp8         1/imm32           # compare *EBP
- 38   7e/jump-if-lesser-or-equal  $run-main/disp8
- 39   # and if (argv[1] == "test")
- 40     # push args
- 41   68/push  "test"/imm32
- 42   ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0x8/disp8       .                 # push *(EBP+8)
- 43     # call
- 44   e8/call  kernel-string-equal/disp32
- 45     # discard args
- 46   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 47     # check result
- 48   3d/compare-EAX  1/imm32
- 49   75/jump-if-not-equal  $run-main/disp8
- 50   # then return run-tests()
- 51   e8/call  run-tests/disp32
- 52 #?   e8/call test-get-num-reads-multiple-digits/disp32
- 53   8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
- 54   eb/jump  $main:end/disp8
- 55 $run-main:
- 56   # allocate space for an exit-descriptor
- 57     # var ed/EAX : (address exit-descriptor)
- 58   81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
- 59   8d/copy-address                 0/mod/indirect  4/rm32/sib    4/base/ESP  4/index/none  .           0/r32/EAX   .               .                 # copy ESP to EAX
- 60     # clear ed->target (so we really exit)
- 61   c7/copy                         0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # copy to *EAX
- 62   # compile(Stdin, 1/stdout, 2/stderr, ed)
- 63     # push args
- 64   50/push-EAX/ed
- 65   68/push  2/imm32/stderr
- 66   68/push  1/imm32/stdout
- 67   68/push  Stdin/imm32
- 68     # call
- 69   e8/call  compile/disp32
- 70     # discard args
- 71   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
- 72   # syscall(exit, 0)
- 73   bb/copy-to-EBX  0/imm32
- 74 $main:end:
- 75   b8/copy-to-EAX  1/imm32/exit
- 76   cd/syscall  0x80/imm8
- 77 
- 78 # the main entry point
- 79 compile:  # in : fd or (address stream), out : fd or (address stream), err : fd or (address stream), ed : (address exit-descriptor) -> <void>
- 80   # prolog
- 81   55/push-EBP
- 82   89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 83   # save registers
- 84   50/push-EAX
- 85   51/push-ECX
- 86   # Look = get-char(in)
- 87     # push args
- 88   ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           8/disp8      .                    # push *(EBP+8)
- 89     # call
- 90   e8/call  get-char/disp32
- 91     # discard args
- 92   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 93   # var num/ECX : (address stream) on the stack
- 94   # Numbers can be 32 bits or 8 hex bytes long. One of them will be in 'Look', so we need space for 7 bytes.
- 95   # We won't add more, so that we can get overflow-handling for free.
- 96   # Add 12 bytes for 'read', 'write' and 'length' fields, for a total of 19 bytes, or 0x13 in hex.
- 97   81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x13/imm32        # subtract from ESP
- 98   8d/copy-address                 0/mod/indirect  4/rm32/sib    4/base/ESP  4/index/none  .           1/r32/ECX   .               .                 # copy ESP to ECX
- 99   # num->length = 7
-100   c7/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           .           8/disp8         7/imm32           # copy to *(ECX+8)
-101   # clear-stream(num)
-102     # push args
-103   51/push-ECX
-104     # call
-105   e8/call  clear-stream/disp32
-106     # discard args
-107   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-108   # get-num(in, num, err, ed)
-109     # push args
-110   ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0x14/disp8      .                 # push *(EBP+20)
-111   ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0x10/disp8      .                 # push *(EBP+16)
-112   51/push-ECX/num
-113   ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           8/disp8      .                    # push *(EBP+8)
-114     # call
-115   e8/call  get-num/disp32
-116     # discard args
-117   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
-118   # EAX = write(_test-stream, "Ab")
-119     # push args
-120   68/push  "Ab"/imm32
-121   68/push  _test-stream/imm32
-122     # call
-123   e8/call  write/disp32
-124     # discard args
-125   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-126   # EAX = write(out, "bb/copy-to-EBX  ")
-127     # push args
-128   68/push  "bb/copy-to-EBX  "/imm32
-129   ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0xc/disp8       .                 # push *(EBP+12)
-130     # call
-131   e8/call  write/disp32
-132     # discard args
-133   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-134   # write-stream(out, num)
-135     # push args
-136   51/push-ECX/num
-137   ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0xc/disp8       .                 # push *(EBP+12)
-138     # call
-139   e8/call  write-stream/disp32
-140     # discard args
-141   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-142   # write(out, Newline)
-143     # push args
-144   68/push  Newline/imm32
-145   ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0xc/disp8       .                 # push *(EBP+12)
-146     # call
-147   e8/call  write/disp32
-148     # discard args
-149   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-150   # EAX = write(out, "b8/copy-to-EAX  1/imm32/exit")
-151     # push args
-152   68/push  "b8/copy-to-EAX  1/imm32/exit"/imm32
-153   ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0xc/disp8       .                 # push *(EBP+12)
-154     # call
-155   e8/call  write/disp32
-156     # discard args
-157   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-158   # EAX = write(out, Newline)
-159     # push args
-160   68/push  Newline/imm32
-161   ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0xc/disp8       .                 # push *(EBP+12)
-162     # call
-163   e8/call  write/disp32
-164     # discard args
-165   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-166   # EAX = write(out, "cd/syscall  0x80/imm8")
-167     # push args
-168   68/push  "cd/syscall  0x80/imm8"/imm32
-169   ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0xc/disp8       .                 # push *(EBP+12)
-170     # call
-171   e8/call  write/disp32
-172     # discard args
-173   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-174   # EAX = write(out, Newline)
-175     # push args
-176   68/push  Newline/imm32
-177   ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0xc/disp8       .                 # push *(EBP+12)
-178     # call
-179   e8/call  write/disp32
-180     # discard args
-181   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-182   # restore registers
-183   59/pop-to-ECX
-184   58/pop-to-EAX
-185   # epilog
-186   89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-187   5d/pop-to-EBP
-188   c3/return
-189 
-190 # Read a sequence of digits into 'out'. Abort if there are none, or if there is
-191 # no space in 'out'.
-192 # Input comes from the global variable 'Look' (first byte) and the argument
-193 # 'in' (rest). We leave the next byte from 'in' into 'Look' on exit.
-194 get-num:  # in : (address buffered-file), out : (address stream), err : fd or (address stream), ed : (address exit-descriptor) -> <void>
-195   # pseudocode:
-196   #   if !is-digit?(Look) expected(ed, err, "integer")
-197   #   do
-198   #     if out.write >= out.length
-199   #       write(err, "Error: too many digits in number\n")
-200   #       stop(ed, 1)
-201   #     out.data[out.write] = LSB(Look)
-202   #     ++out.write
-203   #     Look = get-char(in)
-204   #   while is-digit?(Look)
-205   # This is complicated because I don't want to hard-code the error strategy in
-206   # a general helper like write-byte. Maybe I should just create a local helper.
-207   #
-208   # within the loop we'll try to keep things in registers:
-209   #   ESI : in
-210   #   EDI : out
-211   #   EAX : temp
-212   #   ECX : out->write
-213   #   EDX : out->length
-214   #   EBX : temp2
-215   # We can't allocate Look to a register because it gets written implicitly in
-216   # get-char in each iteration of the loop. (Thereby demonstrating that it's
-217   # not the right interface for us. But we'll keep it just to follow Crenshaw.)
-218   #
-219   # prolog
-220   55/push-EBP
-221   89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-222   # EAX = is-digit?(Look)
-223     # push args
-224   ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Look/disp32     .                 # push *Look
-225     # call
-226   e8/call  is-digit?/disp32
-227     # discard args
-228   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-229   # if EAX == 0 error
-230   3d/compare-EAX  0/imm32
-231   75/jump-if-not-equal  $get-num:main/disp8
-232     # expected(ed, err, "integer")
-233       # push args
-234   68/push  "integer"/imm32
-235   ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0x10/disp8      .                 # push *(EBP+16)
-236   ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0x14/disp8      .                 # push *(EBP+20)
-237       # call
-238   e8/call  expected/disp32  # never returns
-239       # discard args
-240   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-241 $get-num:main:
-242   # save registers
-243   50/push-EAX
-244   51/push-ECX
-245   52/push-EDX
-246   53/push-EBX
-247   56/push-ESI
-248   57/push-EDI
-249   # read necessary variables to registers
-250     # ESI = in
-251   8b/copy                         1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
-252     # EDI = out
-253   8b/copy                         1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           7/r32/EDI   0xc/disp8       .                 # copy *(EBP+12) to EDI
-254     # ECX = out->write
-255   8b/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           1/r32/ECX   .               .                 # copy *EDI to ECX
-256     # EDX = out->length
-257   8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           2/r32/EDX   8/disp8         .                 # copy *(EDI+8) to EDX
-258 $get-num:loop:
-259   # if out->write >= out->length error
-260   3b/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare EDX with ECX
-261   7d/jump-if-lesser  $get-num:loop-stage2/disp8
-262     # error(ed, err, "get-num: too many digits in number")  # TODO: show full number
-263       # push args
-264   68/push  "get-num: too many digits in number"/imm32
-265   ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0x10/disp8      .                 # push *(EBP+16)
-266   ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0x14/disp8      .                 # push *(EBP+20)
-267       # call
-268   e8/call  error/disp32  # never returns
-269       # discard args
-270   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-271 $get-num:loop-stage2:
-272   # out->data[out->write] = LSB(Look)
-273   8d/copy-address                 1/mod/*+disp8   4/rm32/sib    7/base/EDI  1/index/ECX   .           3/r32/EBX   0xc/disp8       .                 # copy EDI+ECX+12 to EBX
-274   8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Look/disp32     .                 # copy *Look to EAX
-275   88/copy-byte                    0/mod/indirect  3/rm32/EBX    .           .             .           0/r32/AL    .               .                 # copy byte at AL to *EBX
-276   # ++out->write
-277   41/increment-ECX
-278   # Look = get-char(in)
-279     # push args
-280   56/push-ESI
-281     # call
-282   e8/call  get-char/disp32
-283     # discard args
-284   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-285   # EAX = is-digit?(Look)
-286     # push args
-287   ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Look/disp32     .                 # push *Look
-288     # call
-289   e8/call  is-digit?/disp32
-290     # discard args
-291   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-292   # if EAX loop
-293   3d/compare-EAX  0/imm32
-294   0f 85/jump-if-not-equal  $get-num:loop/disp32
-295 $get-num:loop-end:
-296   # persist necessary variables from registers
-297   89/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           1/r32/ECX   .               .                 # copy ECX to *EDI
-298   # restore registers
-299   5f/pop-to-EDI
-300   5e/pop-to-ESI
-301   5b/pop-to-EBX
-302   5a/pop-to-EDX
-303   59/pop-to-ECX
-304   58/pop-to-EAX
-305   # epilog
-306   89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-307   5d/pop-to-EBP
-308   c3/return
-309 
-310 test-get-num-reads-single-digit:
-311   ## check that get-num returns first character if it's a digit
-312   # This test uses exit-descriptors. Use EBP for setting up local variables.
-313   55/push-EBP
-314   89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-315   ## clear all streams
-316   # clear-stream(_test-stream)
-317     # push args
-318   68/push  _test-stream/imm32
-319     # call
-320   e8/call  clear-stream/disp32
-321     # discard args
-322   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-323   # clear-stream(_test-buffered-file+4)
-324     # push args
-325   b8/copy-to-EAX  _test-buffered-file/imm32
-326   05/add-to-EAX  4/imm32
-327   50/push-EAX
-328     # call
-329   e8/call  clear-stream/disp32
-330     # discard args
-331   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-332   # clear-stream(_test-output-stream)
-333     # push args
-334   68/push  _test-output-stream/imm32
-335     # call
-336   e8/call  clear-stream/disp32
-337     # discard args
-338   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-339   # clear-stream(_test-error-stream)
-340     # push args
-341   68/push  _test-error-stream/imm32
-342     # call
-343   e8/call  clear-stream/disp32
-344     # discard args
-345   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-346   ## initialize 'in'
-347   # write(_test-stream, "3")
-348     # push args
-349   68/push  "3"/imm32
-350   68/push  _test-stream/imm32
-351     # call
-352   e8/call  write/disp32
-353     # discard args
-354   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-355   ## initialize exit-descriptor 'ed'
-356   # allocate on stack
-357   # var ed/EAX : (address exit-descriptor)
-358   81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
-359   8d/copy-address                 0/mod/indirect  4/rm32/sib    4/base/ESP  4/index/none  .           0/r32/EAX   .               .                 # copy ESP to EAX
-360   # size the exit-descriptor for the call to get-num below
-361   # tailor-exit-descriptor(ed, 16)
-362     # push args
-363   68/push  0x10/imm32/nbytes-of-args-for-get-num
-364   50/push-EAX/ed
-365     # call
-366   e8/call  tailor-exit-descriptor/disp32
-367     # discard args
-368   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-369   ## prime the pump
-370   # get-char(_test-buffered-file)
-371     # push args
-372   68/push  _test-buffered-file/imm32
-373     # call
-374   e8/call  get-char/disp32
-375     # discard args
-376   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-377   ## get-num(in, out, err, ed)
-378     # push args
-379   50/push-EAX/ed
-380   68/push  _test-error-stream/imm32
-381   68/push  _test-output-stream/imm32
-382   68/push  _test-buffered-file/imm32
-383     # call
-384   e8/call  get-num/disp32
-385   ## registers except ESP may be clobbered at this point
-386     # discard args
-387   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
-388   # check-ints-equal(*_test-output-stream.data, '3')
-389     # push args
-390   68/push  "F - test-get-num-reads-single-digit"/imm32
-391   68/push  0x33/imm32
-392   b8/copy-to-EAX  _test-output-stream/imm32
-393   ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
-394     # call
-395   e8/call  check-ints-equal/disp32
-396     # discard args
-397   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-398   # reclaim locals
-399   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-400   5d/pop-to-EBP
-401   c3/return
-402 
-403 test-get-num-aborts-on-non-digit-in-Look:
-404   ## check that get-num returns first character if it's a digit
-405   # This test uses exit-descriptors. Use EBP for setting up local variables.
-406   55/push-EBP
-407   89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-408   ## clear all streams
-409   # clear-stream(_test-stream)
-410     # push args
-411   68/push  _test-stream/imm32
-412     # call
-413   e8/call  clear-stream/disp32
-414     # discard args
-415   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-416   # clear-stream(_test-buffered-file+4)
-417     # push args
-418   b8/copy-to-EAX  _test-buffered-file/imm32
-419   05/add-to-EAX  4/imm32
-420   50/push-EAX
-421     # call
-422   e8/call  clear-stream/disp32
-423     # discard args
-424   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-425   # clear-stream(_test-output-stream)
-426     # push args
-427   68/push  _test-output-stream/imm32
-428     # call
-429   e8/call  clear-stream/disp32
-430     # discard args
-431   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-432   # clear-stream(_test-error-stream)
-433     # push args
-434   68/push  _test-error-stream/imm32
-435     # call
-436   e8/call  clear-stream/disp32
-437     # discard args
-438   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-439   ## initialize 'in'
-440   # write(_test-stream, "3")
-441     # push args
-442   68/push  "3"/imm32
-443   68/push  _test-stream/imm32
-444     # call
-445   e8/call  write/disp32
-446     # discard args
-447   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-448   ## initialize exit-descriptor 'ed'
-449   # allocate on stack
-450   # var ed/EAX : (address exit-descriptor)
-451   81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
-452   8d/copy-address                 0/mod/indirect  4/rm32/sib    4/base/ESP  4/index/none  .           0/r32/EAX   .               .                 # copy ESP to EAX
-453   # size the exit-descriptor for the call to get-num below
-454   # tailor-exit-descriptor(ed, 16)
-455     # push args
-456   68/push  0x10/imm32/nbytes-of-args-for-get-num
-457   50/push-EAX/ed
-458     # call
-459   e8/call  tailor-exit-descriptor/disp32
-460     # discard args
-461   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-462   ## don't initialize Look
-463   ## get-num(in, out, err, ed)
-464     # push args
-465   50/push-EAX/ed
-466   68/push  _test-error-stream/imm32
-467   68/push  _test-output-stream/imm32
-468   68/push  _test-buffered-file/imm32
-469     # call
-470   e8/call  get-num/disp32
-471   ## registers except ESP may be clobbered at this point
-472     # discard args
-473   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
-474   ## check that get-num tried to call exit(1)
-475   # check-ints-equal(ed->value, 2, msg)  # i.e. stop was called with value 1
-476     # push args
-477   68/push  "F - test-get-num-aborts-on-non-digit-in-Look"/imm32
-478   68/push  2/imm32
-479     # push ed->value
-480   ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
-481     # call
-482   e8/call  check-ints-equal/disp32
-483     # discard args
-484   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-485   # reclaim locals
-486   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-487   5d/pop-to-EBP
-488   c3/return
+ 34     # . prolog
+ 35     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 36     # - if argc > 1 and argv[1] == "test" then return run_tests()
+ 37     # . argc > 1
+ 38     81          7/subop/compare     1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0/disp8         1/imm32           # compare *EBP
+ 39     7e/jump-if-lesser-or-equal  $run-main/disp8
+ 40     # . argv[1] == "test"
+ 41     # . . push args
+ 42     68/push  "test"/imm32
+ 43     ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0x8/disp8       .                 # push *(EBP+8)
+ 44     # . . call
+ 45     e8/call  kernel-string-equal/disp32
+ 46     # . . discard args
+ 47     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 48     # . check result
+ 49     3d/compare-EAX  1/imm32
+ 50     75/jump-if-not-equal  $run-main/disp8
+ 51     # . run-tests()
+ 52     e8/call  run-tests/disp32
+ 53 #?     e8/call test-get-num-reads-multiple-digits/disp32
+ 54     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
+ 55     eb/jump  $main:end/disp8
+ 56 $run-main:
+ 57     # - otherwise read a program from stdin and emit its translation to stdout
+ 58     # var ed/EAX : exit-descriptor
+ 59     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
+ 60     8d/copy-address                 0/mod/indirect  4/rm32/sib    4/base/ESP  4/index/none  .           0/r32/EAX   .               .                 # copy ESP to EAX
+ 61     # configure ed to really exit()
+ 62     # . ed->target = 0
+ 63     c7/copy                         0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # copy to *EAX
+ 64     # return compile(Stdin, 1/stdout, 2/stderr, ed)
+ 65     # . . push args
+ 66     50/push-EAX/ed
+ 67     68/push  2/imm32/stderr
+ 68     68/push  1/imm32/stdout
+ 69     68/push  Stdin/imm32
+ 70     # . . call
+ 71     e8/call  compile/disp32
+ 72     # . . discard args
+ 73     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
+ 74     # . syscall(exit, 0)
+ 75     bb/copy-to-EBX  0/imm32
+ 76 $main:end:
+ 77     b8/copy-to-EAX  1/imm32/exit
+ 78     cd/syscall  0x80/imm8
+ 79 
+ 80 # the main entry point
+ 81 compile:  # in : fd or (address stream), out : fd or (address stream), err : fd or (address stream), ed : (address exit-descriptor) -> <void>
+ 82     # . prolog
+ 83     55/push-EBP
+ 84     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 85     # . save registers
+ 86     50/push-EAX
+ 87     51/push-ECX
+ 88     # prime the pump
+ 89     # . Look = get-char(in)
+ 90     # . . push args
+ 91     ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           8/disp8      .                    # push *(EBP+8)
+ 92     # . . call
+ 93     e8/call  get-char/disp32
+ 94     # . . discard args
+ 95     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 96     # var num/ECX : (address stream) on the stack
+ 97     # Numbers can be 32 bits or 8 hex bytes long. One of them will be in 'Look', so we need space for 7 bytes.
+ 98     # We won't add more, so that we can get overflow-handling for free.
+ 99     # Add 12 bytes for 'read', 'write' and 'length' fields, for a total of 19 bytes, or 0x13 in hex.
+100     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x13/imm32        # subtract from ESP
+101     8d/copy-address                 0/mod/indirect  4/rm32/sib    4/base/ESP  4/index/none  .           1/r32/ECX   .               .                 # copy ESP to ECX
+102     # num->length = 7
+103     c7/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           .           8/disp8         7/imm32           # copy to *(ECX+8)
+104     # read a digit from 'in' into 'num'
+105     # . clear-stream(num)
+106     # . . push args
+107     51/push-ECX
+108     # . . call
+109     e8/call  clear-stream/disp32
+110     # . . discard args
+111     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+112     # . get-num(in, num, err, ed)
+113     # . . push args
+114     ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0x14/disp8      .                 # push *(EBP+20)
+115     ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0x10/disp8      .                 # push *(EBP+16)
+116     51/push-ECX/num
+117     ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           8/disp8      .                    # push *(EBP+8)
+118     # . . call
+119     e8/call  get-num/disp32
+120     # . . discard args
+121     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
+122     # render 'num' into the following template on 'out':
+123     #   bb/copy-to-EBX  _num_
+124     #   b8/copy-to-EAX  1/imm32/exit
+125     #   cd/syscall  0x80/imm8
+126     #
+127     # . EAX = write(out, "bb/copy-to-EBX  ")
+128     # . . push args
+129     68/push  "bb/copy-to-EBX  "/imm32
+130     ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0xc/disp8       .                 # push *(EBP+12)
+131     # . . call
+132     e8/call  write/disp32
+133     # . . discard args
+134     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+135     # . write-stream(out, num)
+136     # . . push args
+137     51/push-ECX/num
+138     ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0xc/disp8       .                 # push *(EBP+12)
+139     # . . call
+140     e8/call  write-stream/disp32
+141     # . . discard args
+142     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+143     # . write(out, Newline)
+144     # . . push args
+145     68/push  Newline/imm32
+146     ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0xc/disp8       .                 # push *(EBP+12)
+147     # . . call
+148     e8/call  write/disp32
+149     # . . discard args
+150     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+151     # . write(out, "b8/copy-to-EAX  1/imm32/exit")
+152     # . . push args
+153     68/push  "b8/copy-to-EAX  1/imm32/exit"/imm32
+154     ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0xc/disp8       .                 # push *(EBP+12)
+155     # . . call
+156     e8/call  write/disp32
+157     # . . discard args
+158     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+159     # . write(out, Newline)
+160     # . . push args
+161     68/push  Newline/imm32
+162     ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0xc/disp8       .                 # push *(EBP+12)
+163     # . . call
+164     e8/call  write/disp32
+165     # . . discard args
+166     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+167     # . write(out, "cd/syscall  0x80/imm8")
+168     # . . push args
+169     68/push  "cd/syscall  0x80/imm8"/imm32
+170     ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0xc/disp8       .                 # push *(EBP+12)
+171     # . . call
+172     e8/call  write/disp32
+173     # . . discard args
+174     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+175     # . write(out, Newline)
+176     # . . push args
+177     68/push  Newline/imm32
+178     ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0xc/disp8       .                 # push *(EBP+12)
+179     # . . call
+180     e8/call  write/disp32
+181     # . . discard args
+182     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+183     # . restore registers
+184     59/pop-to-ECX
+185     58/pop-to-EAX
+186     # . epilog
+187     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+188     5d/pop-to-EBP
+189     c3/return
+190 
+191 # Read a sequence of digits into 'out'. Abort if there are none, or if there is
+192 # no space in 'out'.
+193 # Input comes from the global variable 'Look' (first byte) and the argument
+194 # 'in' (rest). We leave the next byte from 'in' into 'Look' on exit.
+195 get-num:  # in : (address buffered-file), out : (address stream), err : fd or (address stream), ed : (address exit-descriptor) -> <void>
+196     # pseudocode:
+197     #   if !is-digit?(Look) expected(ed, err, "integer")
+198     #   do
+199     #     if out.write >= out.length
+200     #       write(err, "Error: too many digits in number\n")
+201     #       stop(ed, 1)
+202     #     out.data[out.write] = LSB(Look)
+203     #     ++out.write
+204     #     Look = get-char(in)
+205     #   while is-digit?(Look)
+206     # This is complicated because I don't want to hard-code the error strategy in
+207     # a general helper like write-byte. Maybe I should just create a local helper.
+208     #
+209     # within the loop we'll try to keep things in registers:
+210     #   ESI : in
+211     #   EDI : out
+212     #   EAX : temp
+213     #   ECX : out->write
+214     #   EDX : out->length
+215     #   EBX : temp2
+216     # We can't allocate Look to a register because it gets written implicitly in
+217     # get-char in each iteration of the loop. (Thereby demonstrating that it's
+218     # not the right interface for us. But we'll keep it just to follow Crenshaw.)
+219     #
+220     # . prolog
+221     55/push-EBP
+222     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+223     # - if is-digit?(Look) expected(ed, err, "integer")
+224     # . EAX = is-digit?(Look)
+225     # . . push args
+226     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Look/disp32     .                 # push *Look
+227     # . . call
+228     e8/call  is-digit?/disp32
+229     # . . discard args
+230     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+231     # . if EAX == 0
+232     3d/compare-EAX  0/imm32
+233     75/jump-if-not-equal  $get-num:main/disp8
+234     # . expected(ed, err, "integer")
+235     # . . push args
+236     68/push  "integer"/imm32
+237     ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0x10/disp8      .                 # push *(EBP+16)
+238     ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0x14/disp8      .                 # push *(EBP+20)
+239     # . . call
+240     e8/call  expected/disp32  # never returns
+241     # . . discard args
+242     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+243 $get-num:main:
+244     # - otherwise read a digit
+245     # . save registers
+246     50/push-EAX
+247     51/push-ECX
+248     52/push-EDX
+249     53/push-EBX
+250     56/push-ESI
+251     57/push-EDI
+252     # read necessary variables to registers
+253     # ESI = in
+254     8b/copy                         1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
+255     # EDI = out
+256     8b/copy                         1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           7/r32/EDI   0xc/disp8       .                 # copy *(EBP+12) to EDI
+257     # ECX = out->write
+258     8b/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           1/r32/ECX   .               .                 # copy *EDI to ECX
+259     # EDX = out->length
+260     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           2/r32/EDX   8/disp8         .                 # copy *(EDI+8) to EDX
+261 $get-num:loop:
+262     # if out->write >= out->length error
+263     3b/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare EDX with ECX
+264     7d/jump-if-lesser  $get-num:loop-stage2/disp8
+265     # . error(ed, err, msg)  # TODO: show full number
+266     # . . push args
+267     68/push  "get-num: too many digits in number"/imm32
+268     ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0x10/disp8      .                 # push *(EBP+16)
+269     ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0x14/disp8      .                 # push *(EBP+20)
+270     # . . call
+271     e8/call  error/disp32  # never returns
+272     # . . discard args
+273     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+274 $get-num:loop-stage2:
+275     # out->data[out->write] = LSB(Look)
+276     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    7/base/EDI  1/index/ECX   .           3/r32/EBX   0xc/disp8       .                 # copy EDI+ECX+12 to EBX
+277     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Look/disp32     .                 # copy *Look to EAX
+278     88/copy-byte                    0/mod/indirect  3/rm32/EBX    .           .             .           0/r32/AL    .               .                 # copy byte at AL to *EBX
+279     # ++out->write
+280     41/increment-ECX
+281     # Look = get-char(in)
+282     # . . push args
+283     56/push-ESI
+284     # . . call
+285     e8/call  get-char/disp32
+286     # . . discard args
+287     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+288     # if is-digit?(Look) loop
+289     # . EAX = is-digit?(Look)
+290     # . . push args
+291     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Look/disp32     .                 # push *Look
+292     # . . call
+293     e8/call  is-digit?/disp32
+294     # . . discard args
+295     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+296     # . if EAX loop
+297     3d/compare-EAX  0/imm32
+298     0f 85/jump-if-not-equal  $get-num:loop/disp32
+299 $get-num:loop-end:
+300     # persist necessary variables from registers
+301     89/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           1/r32/ECX   .               .                 # copy ECX to *EDI
+302     # . restore registers
+303     5f/pop-to-EDI
+304     5e/pop-to-ESI
+305     5b/pop-to-EBX
+306     5a/pop-to-EDX
+307     59/pop-to-ECX
+308     58/pop-to-EAX
+309     # . epilog
+310     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+311     5d/pop-to-EBP
+312     c3/return
+313 
+314 test-get-num-reads-single-digit:
+315     # - check that get-num returns first character if it's a digit
+316     # This test uses exit-descriptors. Use EBP for setting up local variables.
+317     55/push-EBP
+318     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+319     # clear all streams
+320     # . clear-stream(_test-stream)
+321     # . . push args
+322     68/push  _test-stream/imm32
+323     # . . call
+324     e8/call  clear-stream/disp32
+325     # . . discard args
+326     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+327     # . clear-stream(_test-buffered-file+4)
+328     # . . push args
+329     b8/copy-to-EAX  _test-buffered-file/imm32
+330     05/add-to-EAX  4/imm32
+331     50/push-EAX
+332     # . . call
+333     e8/call  clear-stream/disp32
+334     # . . discard args
+335     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+336     # . clear-stream(_test-output-stream)
+337     # . . push args
+338     68/push  _test-output-stream/imm32
+339     # . . call
+340     e8/call  clear-stream/disp32
+341     # . . discard args
+342     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+343     # . clear-stream(_test-error-stream)
+344     # . . push args
+345     68/push  _test-error-stream/imm32
+346     # . . call
+347     e8/call  clear-stream/disp32
+348     # . . discard args
+349     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+350     # initialize 'in'
+351     # . write(_test-stream, "3")
+352     # . . push args
+353     68/push  "3"/imm32
+354     68/push  _test-stream/imm32
+355     # . . call
+356     e8/call  write/disp32
+357     # . . discard args
+358     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+359     # initialize exit-descriptor 'ed' for the call to 'get-num' below
+360     # . var ed/EAX : exit-descriptor
+361     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
+362     8d/copy-address                 0/mod/indirect  4/rm32/sib    4/base/ESP  4/index/none  .           0/r32/EAX   .               .                 # copy ESP to EAX
+363     # . tailor-exit-descriptor(ed, 16)
+364     # . . push args
+365     68/push  0x10/imm32/nbytes-of-args-for-get-num
+366     50/push-EAX/ed
+367     # . . call
+368     e8/call  tailor-exit-descriptor/disp32
+369     # . . discard args
+370     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+371     # prime the pump
+372     # . get-char(_test-buffered-file)
+373     # . . push args
+374     68/push  _test-buffered-file/imm32
+375     # . . call
+376     e8/call  get-char/disp32
+377     # . . discard args
+378     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+379     # get-num(in, out, err, ed)
+380     # . . push args
+381     50/push-EAX/ed
+382     68/push  _test-error-stream/imm32
+383     68/push  _test-output-stream/imm32
+384     68/push  _test-buffered-file/imm32
+385     # . . call
+386     e8/call  get-num/disp32
+387     # registers except ESP may be clobbered at this point
+388     # . . discard args
+389     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
+390     # check-ints-equal(*_test-output-stream.data, '3')
+391     # . . push args
+392     68/push  "F - test-get-num-reads-single-digit"/imm32
+393     68/push  0x33/imm32
+394     b8/copy-to-EAX  _test-output-stream/imm32
+395     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
+396     # . . call
+397     e8/call  check-ints-equal/disp32
+398     # . . discard args
+399     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+400     # . reclaim locals
+401     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+402     5d/pop-to-EBP
+403     c3/return
+404 
+405 test-get-num-aborts-on-non-digit-in-Look:
+406     # - check that get-num returns first character if it's a digit
+407     # This test uses exit-descriptors. Use EBP for setting up local variables.
+408     55/push-EBP
+409     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+410     # clear all streams
+411     # . clear-stream(_test-stream)
+412     # . . push args
+413     68/push  _test-stream/imm32
+414     # . . call
+415     e8/call  clear-stream/disp32
+416     # . . discard args
+417     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+418     # . clear-stream(_test-buffered-file+4)
+419     # . . push args
+420     b8/copy-to-EAX  _test-buffered-file/imm32
+421     05/add-to-EAX  4/imm32
+422     50/push-EAX
+423     # . . call
+424     e8/call  clear-stream/disp32
+425     # . . discard args
+426     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+427     # . clear-stream(_test-output-stream)
+428     # . . push args
+429     68/push  _test-output-stream/imm32
+430     # . . call
+431     e8/call  clear-stream/disp32
+432     # . . discard args
+433     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+434     # . clear-stream(_test-error-stream)
+435     # . . push args
+436     68/push  _test-error-stream/imm32
+437     # . . call
+438     e8/call  clear-stream/disp32
+439     # . . discard args
+440     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+441     # initialize 'in'
+442     # . write(_test-stream, "3")
+443     # . . push args
+444     68/push  "3"/imm32
+445     68/push  _test-stream/imm32
+446     # . . call
+447     e8/call  write/disp32
+448     # . . discard args
+449     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+450     # initialize exit-descriptor 'ed' for the call to 'get-num' below
+451     # . var ed/EAX : (address exit-descriptor)
+452     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
+453     8d/copy-address                 0/mod/indirect  4/rm32/sib    4/base/ESP  4/index/none  .           0/r32/EAX   .               .                 # copy ESP to EAX
+454     # . tailor-exit-descriptor(ed, 16)
+455     # . . push args
+456     68/push  0x10/imm32/nbytes-of-args-for-get-num
+457     50/push-EAX/ed
+458     # . . call
+459     e8/call  tailor-exit-descriptor/disp32
+460     # . . discard args
+461     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+462     # *don't* prime the pump
+463     # get-num(in, out, err, ed)
+464     # . . push args
+465     50/push-EAX/ed
+466     68/push  _test-error-stream/imm32
+467     68/push  _test-output-stream/imm32
+468     68/push  _test-buffered-file/imm32
+469     # . . call
+470     e8/call  get-num/disp32
+471     # registers except ESP may be clobbered at this point
+472     # . . discard args
+473     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
+474     # check that get-num tried to call exit(1)
+475     # . check-ints-equal(ed->value, 2, msg)  # i.e. stop was called with value 1
+476     # . . push args
+477     68/push  "F - test-get-num-aborts-on-non-digit-in-Look"/imm32
+478     68/push  2/imm32
+479     # . . push ed->value
+480     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
+481     # . . call
+482     e8/call  check-ints-equal/disp32
+483     # . . discard args
+484     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+485     # . reclaim locals
+486     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+487     5d/pop-to-EBP
+488     c3/return
 489 
 490 test-get-num-reads-multiple-digits:
-491   ## check that get-num returns all initial digits until it encounters a non-digit
-492   # This test uses exit-descriptors. Use EBP for setting up local variables.
-493   55/push-EBP
-494   89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-495   ## clear all streams
-496   # clear-stream(_test-stream)
-497     # push args
-498   68/push  _test-stream/imm32
-499     # call
-500   e8/call  clear-stream/disp32
-501     # discard args
-502   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-503   # clear-stream(_test-buffered-file+4)
-504     # push args
-505   b8/copy-to-EAX  _test-buffered-file/imm32
-506   05/add-to-EAX  4/imm32
-507   50/push-EAX
-508     # call
-509   e8/call  clear-stream/disp32
-510     # discard args
-511   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-512   # clear-stream(_test-output-stream)
-513     # push args
-514   68/push  _test-output-stream/imm32
-515     # call
-516   e8/call  clear-stream/disp32
-517     # discard args
-518   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-519   # clear-stream(_test-error-stream)
-520     # push args
-521   68/push  _test-error-stream/imm32
-522     # call
-523   e8/call  clear-stream/disp32
-524     # discard args
-525   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-526   ## initialize 'in'
-527   # write(_test-stream, "3456 x")
-528     # push args
-529   68/push  "3456"/imm32
-530   68/push  _test-stream/imm32
-531     # call
-532   e8/call  write/disp32
-533     # discard args
-534   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-535   ## initialize exit-descriptor 'ed'
-536   # allocate on stack
-537   # var ed/EAX : (address exit-descriptor)
-538   81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
-539   8d/copy-address                 0/mod/indirect  4/rm32/sib    4/base/ESP  4/index/none  .           0/r32/EAX   .               .                 # copy ESP to EAX
-540   # size the exit-descriptor for the call to get-num below
-541   # tailor-exit-descriptor(ed, 16)
-542     # push args
-543   68/push  0x10/imm32/nbytes-of-args-for-get-num
-544   50/push-EAX/ed
-545     # call
-546   e8/call  tailor-exit-descriptor/disp32
-547     # discard args
-548   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-549   ## prime the pump
-550   # get-char(_test-buffered-file)
-551     # push args
-552   68/push  _test-buffered-file/imm32
-553     # call
-554   e8/call  get-char/disp32
-555     # discard args
-556   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-557   ## get-num(in, out, err, ed)
-558     # push args
-559   50/push-EAX/ed
-560   68/push  _test-error-stream/imm32
-561   68/push  _test-output-stream/imm32
-562   68/push  _test-buffered-file/imm32
-563     # call
-564   e8/call  get-num/disp32
-565   ## registers except ESP may be clobbered at this point
-566     # discard args
-567   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
-568   # check-ints-equal(*_test-output-stream.data, '3456')
-569     # push args
-570   68/push  "F - test-get-num-reads-multiple-digits"/imm32
-571   68/push  0x36353433/imm32
-572   b8/copy-to-EAX  _test-output-stream/imm32
-573   ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
-574     # call
-575   e8/call  check-ints-equal/disp32
-576     # discard args
-577   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-578   # reclaim locals
-579   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-580   5d/pop-to-EBP
-581   c3/return
-582 
-583 test-get-num-reads-multiple-digits-followed-by-nondigit:
-584   ## check that get-num returns all initial digits until it encounters a non-digit
-585   # This test uses exit-descriptors. Use EBP for setting up local variables.
-586   55/push-EBP
-587   89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-588   ## clear all streams
-589   # clear-stream(_test-stream)
-590     # push args
-591   68/push  _test-stream/imm32
-592     # call
-593   e8/call  clear-stream/disp32
-594     # discard args
-595   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-596   # clear-stream(_test-buffered-file+4)
-597     # push args
-598   b8/copy-to-EAX  _test-buffered-file/imm32
-599   05/add-to-EAX  4/imm32
-600   50/push-EAX
-601     # call
-602   e8/call  clear-stream/disp32
-603     # discard args
-604   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-605   # clear-stream(_test-output-stream)
-606     # push args
-607   68/push  _test-output-stream/imm32
-608     # call
-609   e8/call  clear-stream/disp32
-610     # discard args
-611   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-612   # clear-stream(_test-error-stream)
-613     # push args
-614   68/push  _test-error-stream/imm32
-615     # call
-616   e8/call  clear-stream/disp32
-617     # discard args
-618   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-619   ## initialize 'in'
-620   # write(_test-stream, "3456 x")
-621     # push args
-622   68/push  "3456 x"/imm32
-623   68/push  _test-stream/imm32
-624     # call
-625   e8/call  write/disp32
-626     # discard args
-627   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-628   ## initialize exit-descriptor 'ed'
-629   # allocate on stack
-630   # var ed/EAX : (address exit-descriptor)
-631   81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
-632   8d/copy-address                 0/mod/indirect  4/rm32/sib    4/base/ESP  4/index/none  .           0/r32/EAX   .               .                 # copy ESP to EAX
-633   # size the exit-descriptor for the call to get-num below
-634   # tailor-exit-descriptor(ed, 16)
-635     # push args
-636   68/push  0x10/imm32/nbytes-of-args-for-get-num
-637   50/push-EAX/ed
-638     # call
-639   e8/call  tailor-exit-descriptor/disp32
-640     # discard args
-641   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-642   ## prime the pump
-643   # get-char(_test-buffered-file)
-644     # push args
-645   68/push  _test-buffered-file/imm32
-646     # call
-647   e8/call  get-char/disp32
-648     # discard args
-649   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-650   ## get-num(in, out, err, ed)
-651     # push args
-652   50/push-EAX/ed
-653   68/push  _test-error-stream/imm32
-654   68/push  _test-output-stream/imm32
-655   68/push  _test-buffered-file/imm32
-656     # call
-657   e8/call  get-num/disp32
-658   ## registers except ESP may be clobbered at this point
-659     # discard args
-660   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
-661   # check-ints-equal(*_test-output-stream.data, '3456')
-662     # push args
-663   68/push  "F - test-get-num-reads-multiple-digits-followed-by-nondigit"/imm32
-664   68/push  0x36353433/imm32
-665   b8/copy-to-EAX  _test-output-stream/imm32
-666   ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
-667     # call
-668   e8/call  check-ints-equal/disp32
-669     # discard args
-670   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-671   # reclaim locals
-672   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-673   5d/pop-to-EBP
-674   c3/return
-675 
-676 ## helpers
-677 
-678 # write(f, "Error: "+s+" expected\n") then stop(ed, 1)
-679 expected:  # ed : (address exit-descriptor), f : fd or (address stream), s : (address array byte) -> <void>
-680   # prolog
-681   55/push-EBP
-682   89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-683   # write(f, "Error: ")
-684     # push args
-685   68/push  "Error: "/imm32
-686   ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0xc/disp8       .                 # push *(EBP+12)
-687     # call
-688   e8/call  write/disp32
-689     # discard args
-690   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-691   # write(f, s)
-692     # push args
-693   ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0x10/disp8      .                 # push *(EBP+16)
-694   ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0xc/disp8       .                 # push *(EBP+12)
-695     # call
-696   e8/call  write/disp32
-697     # discard args
-698   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-699   # write(f, " expected")
-700     # push args
-701   68/push  " expected"/imm32
-702   ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0xc/disp8       .                 # push *(EBP+12)
-703     # call
-704   e8/call  write/disp32
-705     # discard args
-706   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-707   # write(f, Newline)
-708     # push args
-709   68/push  Newline/imm32
-710   ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0xc/disp8       .                 # push *(EBP+12)
-711     # call
-712   e8/call  write/disp32
-713     # discard args
-714   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-715   # stop(ed, 1)
-716     # push args
-717   68/push  1/imm32
-718   ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           8/disp8         .                 # push *(EBP+8)
-719     # call
-720   e8/call  stop/disp32
-721   ## should never get past this point
-722   # epilog
-723   89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-724   5d/pop-to-EBP
-725   c3/return
-726 
-727 # write(f, "Error: "+s+"\n") then stop(ed, 1)
-728 error:  # ed : (address exit-descriptor), f : fd or (address stream), s : (address array byte) -> <void>
-729   # prolog
-730   55/push-EBP
-731   89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-732   # write(f, "Error: ")
-733     # push args
-734   68/push  "Error: "/imm32
-735   ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0xc/disp8       .                 # push *(EBP+12)
-736     # call
-737   e8/call  write/disp32
-738     # discard args
-739   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-740   # write(f, s)
-741     # push args
-742   ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0x10/disp8      .                 # push *(EBP+16)
-743   ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0xc/disp8       .                 # push *(EBP+12)
-744     # call
-745   e8/call  write/disp32
-746     # discard args
-747   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-748   # write(f, Newline)
-749     # push args
-750   68/push  Newline/imm32
-751   ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0xc/disp8       .                 # push *(EBP+12)
-752     # call
-753   e8/call  write/disp32
-754     # discard args
-755   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-756   # stop(ed, 1)
-757     # push args
-758   68/push  1/imm32
-759   ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           8/disp8         .                 # push *(EBP+8)
-760     # call
-761   e8/call  stop/disp32
-762   ## should never get past this point
-763   # epilog
-764   89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-765   5d/pop-to-EBP
-766   c3/return
-767 
-768 # read a byte from 'f', and store it in 'Look'
-769 get-char:  # f : (address buffered-file) -> <void>
-770   # prolog
-771   55/push-EBP
-772   89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-773   # save registers
-774   50/push-EAX
-775   # read-byte(f)
-776     # push args
-777   ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0x8/disp8       .                 # push *(EBP+8)
-778     # call
-779   e8/call  read-byte/disp32
-780     # discard args
-781   81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-782   # save EAX to Look
-783   89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Look/disp32     .                 # copy EAX to *Look
-784   # restore registers
-785   58/pop-to-EAX
-786   # epilog
-787   89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-788   5d/pop-to-EBP
-789   c3/return
-790 
-791 is-digit?:  # c : int -> bool/EAX
-792   # prolog
-793   55/push-EBP
-794   89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-795   # EAX = false
-796   b8/copy-to-EAX  0/imm32
-797   # if c < '0' return false
-798   81          7/subop/compare     1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0x8/disp8       0x30/imm32        # compare *(EBP+8)
-799   7c/jump-if-lesser  $is-digit?:end/disp8
-800   # if c > '9' return false
-801   81          7/subop/compare     1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0x8/disp8       0x39/imm32        # compare *(EBP+8)
-802   7f/jump-if-greater  $is-digit?:end/disp8
-803   # otherwise return true
-804   b8/copy-to-EAX  1/imm32
-805 $is-digit?:end:
-806   # epilog
-807   89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-808   5d/pop-to-EBP
-809   c3/return
-810 
-811 == data
-812 
-813 Look:
-814   00 00 00 00
-815 
-816 _test-output-stream:
-817   # current write index
-818   00 00 00 00
-819   # current read index
-820   00 00 00 00
-821   # length (= 8)
-822   08 00 00 00
-823   # data
-824   00 00 00 00 00 00 00 00  # 8 bytes
-825 
-826 _test-error-stream:
-827   # current write index
-828   00 00 00 00
-829   # current read index
-830   00 00 00 00
-831   # length (= 8)
-832   08 00 00 00
-833   # data
-834   00 00 00 00 00 00 00 00  # 8 bytes
-835 
-836 # vim:nowrap:textwidth=0
+491     # - check that get-num returns all initial digits until it encounters a non-digit
+492     # This test uses exit-descriptors. Use EBP for setting up local variables.
+493     55/push-EBP
+494     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+495     # clear all streams
+496     # . clear-stream(_test-stream)
+497     # . . push args
+498     68/push  _test-stream/imm32
+499     # . . call
+500     e8/call  clear-stream/disp32
+501     # . . discard args
+502     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+503     # . clear-stream(_test-buffered-file+4)
+504     # . . push args
+505     b8/copy-to-EAX  _test-buffered-file/imm32
+506     05/add-to-EAX  4/imm32
+507     50/push-EAX
+508     # . . call
+509     e8/call  clear-stream/disp32
+510     # . . discard args
+511     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+512     # . clear-stream(_test-output-stream)
+513     # . . push args
+514     68/push  _test-output-stream/imm32
+515     # . . call
+516     e8/call  clear-stream/disp32
+517     # . . discard args
+518     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+519     # . clear-stream(_test-error-stream)
+520     # . . push args
+521     68/push  _test-error-stream/imm32
+522     # . . call
+523     e8/call  clear-stream/disp32
+524     # . . discard args
+525     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+526     # initialize 'in'
+527     # . write(_test-stream, "3456 x")
+528     # . . push args
+529     68/push  "3456"/imm32
+530     68/push  _test-stream/imm32
+531     # . . call
+532     e8/call  write/disp32
+533     # . . discard args
+534     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+535     # initialize exit-descriptor 'ed' for the call to 'get-num' below
+536     # . var ed/EAX : (address exit-descriptor)
+537     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
+538     8d/copy-address                 0/mod/indirect  4/rm32/sib    4/base/ESP  4/index/none  .           0/r32/EAX   .               .                 # copy ESP to EAX
+539     # . tailor-exit-descriptor(ed, 16)
+540     # . . push args
+541     68/push  0x10/imm32/nbytes-of-args-for-get-num
+542     50/push-EAX/ed
+543     # . . call
+544     e8/call  tailor-exit-descriptor/disp32
+545     # . . discard args
+546     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+547     # prime the pump
+548     # . get-char(_test-buffered-file)
+549     # . . push args
+550     68/push  _test-buffered-file/imm32
+551     # . . call
+552     e8/call  get-char/disp32
+553     # . . discard args
+554     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+555     # get-num(in, out, err, ed)
+556     # . . push args
+557     50/push-EAX/ed
+558     68/push  _test-error-stream/imm32
+559     68/push  _test-output-stream/imm32
+560     68/push  _test-buffered-file/imm32
+561     # . . call
+562     e8/call  get-num/disp32
+563     # registers except ESP may be clobbered at this point
+564     # . . discard args
+565     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
+566     # check-ints-equal(*_test-output-stream.data, '3456')
+567     # . . push args
+568     68/push  "F - test-get-num-reads-multiple-digits"/imm32
+569     68/push  0x36353433/imm32
+570     b8/copy-to-EAX  _test-output-stream/imm32
+571     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
+572     # . . call
+573     e8/call  check-ints-equal/disp32
+574     # . . discard args
+575     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+576     # . reclaim locals
+577     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+578     5d/pop-to-EBP
+579     c3/return
+580 
+581 test-get-num-reads-multiple-digits-followed-by-nondigit:
+582     # - check that get-num returns all initial digits until it encounters a non-digit
+583     # This test uses exit-descriptors. Use EBP for setting up local variables.
+584     55/push-EBP
+585     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+586     # clear all streams
+587     # . clear-stream(_test-stream)
+588     # . . push args
+589     68/push  _test-stream/imm32
+590     # . . call
+591     e8/call  clear-stream/disp32
+592     # . . discard args
+593     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+594     # . clear-stream(_test-buffered-file+4)
+595     # . . push args
+596     b8/copy-to-EAX  _test-buffered-file/imm32
+597     05/add-to-EAX  4/imm32
+598     50/push-EAX
+599     # . . call
+600     e8/call  clear-stream/disp32
+601     # . . discard args
+602     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+603     # . clear-stream(_test-output-stream)
+604     # . . push args
+605     68/push  _test-output-stream/imm32
+606     # . . call
+607     e8/call  clear-stream/disp32
+608     # . . discard args
+609     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+610     # . clear-stream(_test-error-stream)
+611     # . . push args
+612     68/push  _test-error-stream/imm32
+613     # . . call
+614     e8/call  clear-stream/disp32
+615     # . . discard args
+616     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+617     # initialize 'in'
+618     # . write(_test-stream, "3456 x")
+619     # . . push args
+620     68/push  "3456 x"/imm32
+621     68/push  _test-stream/imm32
+622     # . . call
+623     e8/call  write/disp32
+624     # . . discard args
+625     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+626     # initialize exit-descriptor 'ed' for the call to 'get-num' below
+627     # . var ed/EAX : (address exit-descriptor)
+628     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
+629     8d/copy-address                 0/mod/indirect  4/rm32/sib    4/base/ESP  4/index/none  .           0/r32/EAX   .               .                 # copy ESP to EAX
+630     # . tailor-exit-descriptor(ed, 16)
+631     # . . push args
+632     68/push  0x10/imm32/nbytes-of-args-for-get-num
+633     50/push-EAX/ed
+634     # . . call
+635     e8/call  tailor-exit-descriptor/disp32
+636     # . . discard args
+637     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+638     # prime the pump
+639     # . get-char(_test-buffered-file)
+640     # . . push args
+641     68/push  _test-buffered-file/imm32
+642     # . . call
+643     e8/call  get-char/disp32
+644     # . . discard args
+645     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+646     # get-num(in, out, err, ed)
+647     # . . push args
+648     50/push-EAX/ed
+649     68/push  _test-error-stream/imm32
+650     68/push  _test-output-stream/imm32
+651     68/push  _test-buffered-file/imm32
+652     # . . call
+653     e8/call  get-num/disp32
+654     # registers except ESP may be clobbered at this point
+655     # . . discard args
+656     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
+657     # check-ints-equal(*_test-output-stream.data, '3456')
+658     # . . push args
+659     68/push  "F - test-get-num-reads-multiple-digits-followed-by-nondigit"/imm32
+660     68/push  0x36353433/imm32
+661     b8/copy-to-EAX  _test-output-stream/imm32
+662     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
+663     # . . call
+664     e8/call  check-ints-equal/disp32
+665     # . . discard args
+666     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+667     # . reclaim locals
+668     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+669     5d/pop-to-EBP
+670     c3/return
+671 
+672 ## helpers
+673 
+674 # write(f, "Error: "+s+" expected\n") then stop(ed, 1)
+675 expected:  # ed : (address exit-descriptor), f : fd or (address stream), s : (address array byte) -> <void>
+676     # . prolog
+677     55/push-EBP
+678     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+679     # write(f, "Error: ")
+680     # . . push args
+681     68/push  "Error: "/imm32
+682     ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0xc/disp8       .                 # push *(EBP+12)
+683     # . . call
+684     e8/call  write/disp32
+685     # . . discard args
+686     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+687     # write(f, s)
+688     # . . push args
+689     ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0x10/disp8      .                 # push *(EBP+16)
+690     ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0xc/disp8       .                 # push *(EBP+12)
+691     # . . call
+692     e8/call  write/disp32
+693     # . . discard args
+694     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+695     # write(f, " expected")
+696     # . . push args
+697     68/push  " expected"/imm32
+698     ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0xc/disp8       .                 # push *(EBP+12)
+699     # . . call
+700     e8/call  write/disp32
+701     # . . discard args
+702     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+703     # write(f, Newline)
+704     # . . push args
+705     68/push  Newline/imm32
+706     ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0xc/disp8       .                 # push *(EBP+12)
+707     # . . call
+708     e8/call  write/disp32
+709     # . . discard args
+710     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+711     # stop(ed, 1)
+712     # . . push args
+713     68/push  1/imm32
+714     ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           8/disp8         .                 # push *(EBP+8)
+715     # . . call
+716     e8/call  stop/disp32
+717     # should never get past this point
+718     # . epilog
+719     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+720     5d/pop-to-EBP
+721     c3/return
+722 
+723 # write(f, "Error: "+s+"\n") then stop(ed, 1)
+724 error:  # ed : (address exit-descriptor), f : fd or (address stream), s : (address array byte) -> <void>
+725     # . prolog
+726     55/push-EBP
+727     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+728     # write(f, "Error: ")
+729     # . . push args
+730     68/push  "Error: "/imm32
+731     ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0xc/disp8       .                 # push *(EBP+12)
+732     # . . call
+733     e8/call  write/disp32
+734     # . . discard args
+735     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+736     # write(f, s)
+737     # . . push args
+738     ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0x10/disp8      .                 # push *(EBP+16)
+739     ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0xc/disp8       .                 # push *(EBP+12)
+740     # . . call
+741     e8/call  write/disp32
+742     # . . discard args
+743     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+744     # write(f, Newline)
+745     # . . push args
+746     68/push  Newline/imm32
+747     ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0xc/disp8       .                 # push *(EBP+12)
+748     # . . call
+749     e8/call  write/disp32
+750     # . . discard args
+751     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+752     # stop(ed, 1)
+753     # . . push args
+754     68/push  1/imm32
+755     ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           8/disp8         .                 # push *(EBP+8)
+756     # . . call
+757     e8/call  stop/disp32
+758     # should never get past this point
+759     # . epilog
+760     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+761     5d/pop-to-EBP
+762     c3/return
+763 
+764 # read a byte from 'f', and save it in 'Look'
+765 get-char:  # f : (address buffered-file) -> <void>
+766     # . prolog
+767     55/push-EBP
+768     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+769     # . save registers
+770     50/push-EAX
+771     # read-byte(f)
+772     # . . push args
+773     ff          6/subop/push        1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0x8/disp8       .                 # push *(EBP+8)
+774     # . . call
+775     e8/call  read-byte/disp32
+776     # . . discard args
+777     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+778     # save EAX to Look
+779     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Look/disp32     .                 # copy EAX to *Look
+780     # . restore registers
+781     58/pop-to-EAX
+782     # . epilog
+783     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+784     5d/pop-to-EBP
+785     c3/return
+786 
+787 is-digit?:  # c : int -> bool/EAX
+788     # . prolog
+789     55/push-EBP
+790     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+791     # EAX = false
+792     b8/copy-to-EAX  0/imm32
+793     # if c < '0' return false
+794     81          7/subop/compare     1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0x8/disp8       0x30/imm32        # compare *(EBP+8)
+795     7c/jump-if-lesser  $is-digit?:end/disp8
+796     # if c > '9' return false
+797     81          7/subop/compare     1/mod/*+disp8   4/rm32/sib    5/base/EBP  4/index/none  .           .           0x8/disp8       0x39/imm32        # compare *(EBP+8)
+798     7f/jump-if-greater  $is-digit?:end/disp8
+799     # otherwise return true
+800     b8/copy-to-EAX  1/imm32
+801 $is-digit?:end:
+802     # . epilog
+803     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+804     5d/pop-to-EBP
+805     c3/return
+806 
+807 == data
+808 
+809 Look: # (char)
+810     00 00 00 00  # = 0
+811 
+812 _test-output-stream:
+813     # current write index
+814     00 00 00 00
+815     # current read index
+816     00 00 00 00
+817     # length (= 8)
+818     08 00 00 00
+819     # data
+820     00 00 00 00 00 00 00 00  # 8 bytes
+821 
+822 _test-error-stream:
+823     # current write index
+824     00 00 00 00
+825     # current read index
+826     00 00 00 00
+827     # length (= 8)
+828     08 00 00 00
+829     # data
+830     00 00 00 00 00 00 00 00  # 8 bytes
+831 
+832 # . . vim:nowrap:textwidth=0
 
-- cgit 1.4.1-2-gfad0