From 5396e24cba8390ca2d70d99c7e8772eee4ec3a11 Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Tue, 18 May 2021 13:01:59 -0700 Subject: . --- html/linux/survey_baremetal.subx.html | 3656 ++++++++------------------------- 1 file changed, 884 insertions(+), 2772 deletions(-) (limited to 'html/linux/survey_baremetal.subx.html') diff --git a/html/linux/survey_baremetal.subx.html b/html/linux/survey_baremetal.subx.html index 90aa4eeb..c35811d7 100644 --- a/html/linux/survey_baremetal.subx.html +++ b/html/linux/survey_baremetal.subx.html @@ -63,2864 +63,976 @@ if ('onhashchange' in window) {
    1 # Assign addresses (co-ordinates) to instructions (landmarks) in a program
    2 # (landscape).
-   3 # Use the addresses assigned to replace labels.
-   4 #
-   5 # To build:
-   6 #   $ bootstrap/bootstrap translate [01]*.subx subx-params.subx survey_baremetal.subx  -o survey_baremetal
-   7 #
-   8 # The expected input is a stream of bytes and some interspersed labels.
-   9 # Comments and '==' segment headers are allowed, but names are ignored. The
-  10 # emitted code will all lie in a single contiguous address range starting at
-  11 # address 0x7c00. Addresses in segment headers are optional. If provided, this
-  12 # program will insert padding in the output until the desired address is
-  13 # reached.
-  14 #
-  15 #   $ cat x
-  16 #   == code
-  17 #   l1:
-  18 #   aa bb l1/imm8
-  19 #   cc dd l2/disp32
-  20 #   l2:
-  21 #   ee foo/imm32
-  22 #   == data 0x7c10
-  23 #   foo:
-  24 #     34
-  25 #
-  26 # The output is the stream of bytes without segment headers or label definitions,
-  27 # and with label references replaced with numeric values/displacements.
-  28 #
-  29 #   $ cat x  |bootstrap/bootstrap run survey_baremetal
-  30 #   # 0x7c00
-  31 #   aa bb nn  # some computed address
-  32 #   cc dd nn nn nn nn  # some computed displacement
-  33 #   ee nn nn nn nn  # address right after this instruction
-  34 #   # 0x7c0e
-  35 #   00 00  # padding
-  36 #   # 0x7c10
-  37 #   34  # data segment interleaved with code
-  38 
-  39 == code
-  40 #   instruction                     effective address                                                   register    displacement    immediate
-  41 # . op          subop               mod             rm32          base        index         scale       r32
-  42 # . 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
-  43 
-  44 Entry:  # run tests if necessary, convert stdin if not
-  45     # . prologue
-  46     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
-  47 
-  48     # Heap = new-segment(Heap-size)
-  49     # . . push args
-  50     68/push  Heap/imm32
-  51     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Heap-size/disp32                  # push *Heap-size
+   3 #
+   4 # To build:
+   5 #   $ bootstrap/bootstrap translate [01]*.subx subx-params.subx survey_baremetal.subx  -o survey_baremetal
+   6 #
+   7 # The expected input is a stream of bytes and some interspersed labels.
+   8 # Comments and '==' segment headers are allowed, but names are ignored. The
+   9 # emitted code will all lie in a single contiguous address range starting at
+  10 # address 0x7c00. Addresses in segment headers are optional. If provided, this
+  11 # program will insert padding in the output until the desired address is
+  12 # reached.
+  13 #
+  14 #   $ cat x
+  15 #   == code
+  16 #   l1:
+  17 #   aa bb l1/imm8
+  18 #   cc dd l2/disp32
+  19 #   l2:
+  20 #   ee foo/imm32
+  21 #   == data 0x7c10
+  22 #   foo:
+  23 #     34
+  24 #
+  25 # The output is a list of labels and their computed addresses.
+  26 #
+  27 #   $ cat x  |bootstrap/bootstrap run survey_baremetal
+  28 #   0x7c00 l1
+  29 #   0x7c09 l2
+  30 #   0x7c10 foo
+  31 
+  32 == code
+  33 #   instruction                     effective address                                                   register    displacement    immediate
+  34 # . op          subop               mod             rm32          base        index         scale       r32
+  35 # . 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
+  36 
+  37 Entry:  # run tests if necessary, convert stdin if not
+  38     # . prologue
+  39     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
+  40 
+  41     # Heap = new-segment(Heap-size)
+  42     # . . push args
+  43     68/push  Heap/imm32
+  44     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Heap-size/disp32                  # push *Heap-size
+  45     # . . call
+  46     e8/call  new-segment/disp32
+  47     # . . discard args
+  48     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+  49     # initialize-trace-stream(Trace-size)
+  50     # . . push args
+  51     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-size/disp32                 # push *Heap-size
   52     # . . call
-  53     e8/call  new-segment/disp32
+  53     e8/call  initialize-trace-stream/disp32
   54     # . . discard args
-  55     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-  56     # initialize-trace-stream(Trace-size)
-  57     # . . push args
-  58     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-size/disp32                 # push *Heap-size
-  59     # . . call
-  60     e8/call  initialize-trace-stream/disp32
-  61     # . . discard args
-  62     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-  63 
-  64     # - if argc > 1 and argv[1] == "test", then return run_tests()
-  65     # if (argc <= 1) goto interactive
-  66     81          7/subop/compare     1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0/disp8         1/imm32           # compare *ebp
-  67     7e/jump-if-<=  $subx-survey-main:interactive/disp8
-  68     # if (!kernel-string-equal?(argv[1], "test")) goto interactive
-  69     # . eax = kernel-string-equal?(argv[1], "test")
-  70     # . . push args
-  71     68/push  "test"/imm32
-  72     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
-  73     # . . call
-  74     e8/call  kernel-string-equal?/disp32
-  75     # . . discard args
-  76     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-  77     # . if (eax == false) goto interactive
-  78     3d/compare-eax-and  0/imm32/false
-  79     74/jump-if-=  $subx-survey-main:interactive/disp8
-  80     # run-tests()
-  81 #?     e8/call  test-emit-output-with-padding/disp32
-  82     e8/call  run-tests/disp32
-  83     # syscall(exit, *Num-test-failures)
-  84     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/ebx   Num-test-failures/disp32          # copy *Num-test-failures to ebx
-  85     eb/jump  $subx-survey-main:end/disp8
-  86 $subx-survey-main:interactive:
-  87     # - otherwise convert stdin
-  88     # subx-survey(Stdin, Stdout)
-  89     # . . push args
-  90     68/push  Stdout/imm32
-  91     68/push  Stdin/imm32
-  92     # . . call
-  93     e8/call  subx-survey/disp32
-  94     # . . discard args
-  95     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-  96 #?     # . write-stream(2/stderr, Trace-stream)
-  97 #?     # . . push args
-  98 #?     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
-  99 #?     68/push  2/imm32/stderr
- 100 #?     # . . call
- 101 #?     e8/call  write-stream/disp32
- 102 #?     # . . discard args
- 103 #?     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
- 104     # syscall(exit, 0)
- 105     bb/copy-to-ebx  0/imm32
- 106 $subx-survey-main:end:
- 107     e8/call  syscall_exit/disp32
- 108 
- 109 subx-survey:  # infile: (addr buffered-file), out: (addr buffered-file)
- 110     # pseudocode
- 111     #   var in: (stream byte Input-size)
- 112     #   slurp(infile, in)
- 113     #   var labels: (stream {label-name, address} Max-labels)
- 114     #   compute-offsets(in, labels)
- 115     #   rewind-stream(in)
- 116     #   emit-output(in, out, labels)
- 117     #
- 118     # . prologue
- 119     55/push-ebp
- 120     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
- 121     # . save registers
- 122     51/push-ecx
- 123     52/push-edx
- 124     56/push-esi
- 125     # var labels/edx: (stream {label-name, address} Max-labels)
- 126     # (we get more rows than Max-labels advertises because row size is smaller than in survey_elf)
- 127     # . data
- 128     2b/subtract                     0/mod/indirect  5/rm32/.disp32            .             .           4/r32/esp   Max-labels/disp32                 # subtract *Max-labels from esp
- 129     # . size
- 130     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Max-labels/disp32                 # push *Max-labels
- 131     # . read
- 132     68/push  0/imm32/read
- 133     # . write
- 134     68/push  0/imm32/write
- 135     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
- 136     # var in/esi: (stream byte Input-size)
- 137     # . data
- 138     2b/subtract                     0/mod/indirect  5/rm32/.disp32            .             .           4/r32/esp   Input-size/disp32                 # subtract *Input-size from esp
- 139     # . size
- 140     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Input-size/disp32                 # push *Input-size
- 141     # . read
- 142     68/push  0/imm32/read
- 143     # . write
- 144     68/push  0/imm32/write
- 145     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           4/r32/esp   .               .                 # copy esp to esi
- 146     # slurp(infile, in)
- 147     # . . push args
+  55     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+  56 
+  57     # - if argc > 1 and argv[1] == "test", then return run_tests()
+  58     # if (argc <= 1) goto interactive
+  59     81          7/subop/compare     1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0/disp8         1/imm32           # compare *ebp
+  60     7e/jump-if-<=  $subx-survey-main:interactive/disp8
+  61     # if (!kernel-string-equal?(argv[1], "test")) goto interactive
+  62     # . eax = kernel-string-equal?(argv[1], "test")
+  63     # . . push args
+  64     68/push  "test"/imm32
+  65     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
+  66     # . . call
+  67     e8/call  kernel-string-equal?/disp32
+  68     # . . discard args
+  69     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+  70     # . if (eax == false) goto interactive
+  71     3d/compare-eax-and  0/imm32/false
+  72     74/jump-if-=  $subx-survey-main:interactive/disp8
+  73     # run-tests()
+  74     e8/call  run-tests/disp32
+  75     # syscall(exit, *Num-test-failures)
+  76     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/ebx   Num-test-failures/disp32          # copy *Num-test-failures to ebx
+  77     eb/jump  $subx-survey-main:end/disp8
+  78 $subx-survey-main:interactive:
+  79     # - otherwise convert stdin
+  80     # subx-survey(Stdin, Stdout)
+  81     # . . push args
+  82     68/push  Stdout/imm32
+  83     68/push  Stdin/imm32
+  84     # . . call
+  85     e8/call  subx-survey/disp32
+  86     # . . discard args
+  87     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+  88 #?     # . write-stream(2/stderr, Trace-stream)
+  89 #?     # . . push args
+  90 #?     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
+  91 #?     68/push  2/imm32/stderr
+  92 #?     # . . call
+  93 #?     e8/call  write-stream/disp32
+  94 #?     # . . discard args
+  95 #?     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+  96     # syscall(exit, 0)
+  97     bb/copy-to-ebx  0/imm32
+  98 $subx-survey-main:end:
+  99     e8/call  syscall_exit/disp32
+ 100 
+ 101 subx-survey:  # infile: (addr buffered-file), out: (addr buffered-file)
+ 102     # pseudocode
+ 103     #   var in: (stream byte Input-size)
+ 104     #   slurp(infile, in)
+ 105     #   var labels: (stream {label-name, address} Max-labels)
+ 106     #   compute-addresses(in, labels)
+ 107     #   emit-labels(out, labels)
+ 108     #
+ 109     # . prologue
+ 110     55/push-ebp
+ 111     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
+ 112     # . save registers
+ 113     51/push-ecx
+ 114     52/push-edx
+ 115     56/push-esi
+ 116     # var labels/edx: (stream {label-name, address} Max-labels)
+ 117     # (we get more rows than Max-labels advertises because row size is smaller than in survey_elf)
+ 118     # . data
+ 119     2b/subtract                     0/mod/indirect  5/rm32/.disp32            .             .           4/r32/esp   Max-labels/disp32                 # subtract *Max-labels from esp
+ 120     # . size
+ 121     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Max-labels/disp32                 # push *Max-labels
+ 122     # . read
+ 123     68/push  0/imm32/read
+ 124     # . write
+ 125     68/push  0/imm32/write
+ 126     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
+ 127     # var in/esi: (stream byte Input-size)
+ 128     # . data
+ 129     2b/subtract                     0/mod/indirect  5/rm32/.disp32            .             .           4/r32/esp   Input-size/disp32                 # subtract *Input-size from esp
+ 130     # . size
+ 131     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Input-size/disp32                 # push *Input-size
+ 132     # . read
+ 133     68/push  0/imm32/read
+ 134     # . write
+ 135     68/push  0/imm32/write
+ 136     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           4/r32/esp   .               .                 # copy esp to esi
+ 137     # slurp(infile, in)
+ 138     # . . push args
+ 139     56/push-esi
+ 140     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
+ 141     # . . call
+ 142     e8/call  slurp/disp32
+ 143     # . . discard args
+ 144     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 145     # compute-addresses(in, labels)
+ 146     # . . push args
+ 147     52/push-edx
  148     56/push-esi
- 149     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
- 150     # . . call
- 151     e8/call  slurp/disp32
- 152     # . . discard args
- 153     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
- 154     # compute-offsets(in, labels)
- 155     # . . push args
- 156     52/push-edx
- 157     56/push-esi
- 158     # . . call
- 159     e8/call  compute-offsets/disp32
- 160     # . . discard args
- 161     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
- 162     # rewind-stream(in)
- 163     # . . push args
- 164     56/push-esi
- 165     # . . call
- 166     e8/call  rewind-stream/disp32
- 167     # . . discard args
- 168     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
- 169     # emit-output(in, out, labels)
- 170     # . . push args
- 171     52/push-edx
- 172     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
- 173     56/push-esi
- 174     # . . call
- 175     e8/call  emit-output/disp32
- 176     # . . discard args
- 177     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
- 178     # flush(out)
- 179     # . . push args
- 180     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
- 181     # . . call
- 182     e8/call  flush/disp32
- 183     # . . discard args
- 184     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
- 185 $subx-survey:end:
- 186     # . reclaim locals
- 187     03/add                          0/mod/indirect  5/rm32/.disp32            .             .           4/r32/esp   Max-labels/disp32                 # add *Max-labels to esp
- 188     03/add                          0/mod/indirect  5/rm32/.disp32            .             .           4/r32/esp   Input-size/disp32                 # add *Input-size to esp
- 189     # . restore registers
- 190     5e/pop-to-esi
- 191     5a/pop-to-edx
- 192     59/pop-to-ecx
- 193     # . epilogue
- 194     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
- 195     5d/pop-to-ebp
- 196     c3/return
- 197 
- 198 test-subx-survey-computes-addresses:
- 199     # input:
- 200     #   == code
- 201     #   ab x/imm32
- 202     #   == data
- 203     #   x:
- 204     #     01
- 205     #
- 206     # trace contains (in any order):
- 207     #   label x is at address 0x7c05
- 208     #
- 209     # . prologue
- 210     55/push-ebp
- 211     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
- 212     # setup
- 213     # . clear-stream(_test-input-stream)
- 214     # . . push args
- 215     68/push  _test-input-stream/imm32
- 216     # . . call
- 217     e8/call  clear-stream/disp32
- 218     # . . discard args
- 219     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
- 220     # . clear-stream($_test-input-buffered-file->buffer)
- 221     # . . push args
- 222     68/push  $_test-input-buffered-file->buffer/imm32
- 223     # . . call
- 224     e8/call  clear-stream/disp32
- 225     # . . discard args
- 226     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
- 227     # . clear-stream(_test-output-stream)
- 228     # . . push args
- 229     68/push  _test-output-stream/imm32
+ 149     # . . call
+ 150     e8/call  compute-addresses/disp32
+ 151     # . . discard args
+ 152     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 153     # emit-labels(out, labels)
+ 154     # . . push args
+ 155     52/push-edx
+ 156     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
+ 157     # . . call
+ 158     e8/call  emit-labels/disp32
+ 159     # . . discard args
+ 160     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 161     # flush(out)
+ 162     # . . push args
+ 163     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
+ 164     # . . call
+ 165     e8/call  flush/disp32
+ 166     # . . discard args
+ 167     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+ 168 $subx-survey:end:
+ 169     # . reclaim locals
+ 170     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x18/imm32        # add to esp
+ 171     03/add                          0/mod/indirect  5/rm32/.disp32            .             .           4/r32/esp   Max-labels/disp32                 # add *Max-labels to esp
+ 172     03/add                          0/mod/indirect  5/rm32/.disp32            .             .           4/r32/esp   Input-size/disp32                 # add *Input-size to esp
+ 173     # . restore registers
+ 174     5e/pop-to-esi
+ 175     5a/pop-to-edx
+ 176     59/pop-to-ecx
+ 177     # . epilogue
+ 178     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
+ 179     5d/pop-to-ebp
+ 180     c3/return
+ 181 
+ 182 test-subx-survey-computes-addresses:
+ 183     # input:
+ 184     #   == code
+ 185     #   ab x/imm32
+ 186     #   == data
+ 187     #   x:
+ 188     #     01
+ 189     #
+ 190     # trace contains (in any order):
+ 191     #   label x is at address 0x7c05
+ 192     #
+ 193     # . prologue
+ 194     55/push-ebp
+ 195     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
+ 196     # setup
+ 197     # . clear-stream(_test-input-stream)
+ 198     # . . push args
+ 199     68/push  _test-input-stream/imm32
+ 200     # . . call
+ 201     e8/call  clear-stream/disp32
+ 202     # . . discard args
+ 203     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+ 204     # . clear-stream($_test-input-buffered-file->buffer)
+ 205     # . . push args
+ 206     68/push  $_test-input-buffered-file->buffer/imm32
+ 207     # . . call
+ 208     e8/call  clear-stream/disp32
+ 209     # . . discard args
+ 210     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+ 211     # . clear-stream(_test-output-stream)
+ 212     # . . push args
+ 213     68/push  _test-output-stream/imm32
+ 214     # . . call
+ 215     e8/call  clear-stream/disp32
+ 216     # . . discard args
+ 217     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+ 218     # . clear-stream($_test-output-buffered-file->buffer)
+ 219     # . . push args
+ 220     68/push  $_test-output-buffered-file->buffer/imm32
+ 221     # . . call
+ 222     e8/call  clear-stream/disp32
+ 223     # . . discard args
+ 224     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+ 225     # initialize input
+ 226     # . write(_test-input-stream, "== code\n")
+ 227     # . . push args
+ 228     68/push  "== code\n"/imm32
+ 229     68/push  _test-input-stream/imm32
  230     # . . call
- 231     e8/call  clear-stream/disp32
+ 231     e8/call  write/disp32
  232     # . . discard args
- 233     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
- 234     # . clear-stream($_test-output-buffered-file->buffer)
+ 233     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 234     # . write(_test-input-stream, "ab x/imm32\n")
  235     # . . push args
- 236     68/push  $_test-output-buffered-file->buffer/imm32
- 237     # . . call
- 238     e8/call  clear-stream/disp32
- 239     # . . discard args
- 240     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
- 241     # initialize input
- 242     # . write(_test-input-stream, "== code\n")
+ 236     68/push  "ab x/imm32\n"/imm32
+ 237     68/push  _test-input-stream/imm32
+ 238     # . . call
+ 239     e8/call  write/disp32
+ 240     # . . discard args
+ 241     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 242     # . write(_test-input-stream, "== data\n")
  243     # . . push args
- 244     68/push  "== code\n"/imm32
+ 244     68/push  "== data\n"/imm32
  245     68/push  _test-input-stream/imm32
  246     # . . call
  247     e8/call  write/disp32
  248     # . . discard args
  249     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
- 250     # . write(_test-input-stream, "ab x/imm32\n")
+ 250     # . write(_test-input-stream, "x:\n")
  251     # . . push args
- 252     68/push  "ab x/imm32\n"/imm32
+ 252     68/push  "x:\n"/imm32
  253     68/push  _test-input-stream/imm32
  254     # . . call
  255     e8/call  write/disp32
  256     # . . discard args
  257     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
- 258     # . write(_test-input-stream, "== data\n")
+ 258     # . write(_test-input-stream, "01\n")
  259     # . . push args
- 260     68/push  "== data\n"/imm32
+ 260     68/push  "01\n"/imm32
  261     68/push  _test-input-stream/imm32
  262     # . . call
  263     e8/call  write/disp32
  264     # . . discard args
  265     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
- 266     # . write(_test-input-stream, "x:\n")
+ 266     # subx-survey(_test-input-buffered-file, _test-output-buffered-file)
  267     # . . push args
- 268     68/push  "x:\n"/imm32
- 269     68/push  _test-input-stream/imm32
+ 268     68/push  _test-output-buffered-file/imm32
+ 269     68/push  _test-input-buffered-file/imm32
  270     # . . call
- 271     e8/call  write/disp32
+ 271     e8/call  subx-survey/disp32
  272     # . . discard args
  273     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
- 274     # . write(_test-input-stream, "01\n")
- 275     # . . push args
- 276     68/push  "01\n"/imm32
- 277     68/push  _test-input-stream/imm32
- 278     # . . call
- 279     e8/call  write/disp32
- 280     # . . discard args
- 281     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
- 282     # subx-survey(_test-input-buffered-file, _test-output-buffered-file)
- 283     # . . push args
- 284     68/push  _test-output-buffered-file/imm32
- 285     68/push  _test-input-buffered-file/imm32
- 286     # . . call
- 287     e8/call  subx-survey/disp32
- 288     # . . discard args
- 289     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
- 290     # check trace
- 291 +-- 26 lines: #?     # dump *Trace-stream -----------------------------------------------------------------------------------------------------------------------------------------------
- 317     # . check-trace-contains("label 'x' is at address 0x00007c05.", msg)
- 318     # . . push args
- 319     68/push  "F - test-subx-survey-computes-addresses/0"/imm32
- 320     68/push  "label 'x' is at address 0x00007c05."/imm32
- 321     # . . call
- 322     e8/call  check-trace-contains/disp32
- 323     # . . discard args
- 324     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
- 325     # . epilogue
- 326     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
- 327     5d/pop-to-ebp
- 328     c3/return
- 329 
- 330 test-subx-survey-computes-addresses-with-padding:
- 331     # input:
- 332     #   == code
- 333     #   ab x/imm32
- 334     #   == data 0x7c10
- 335     #   x:
- 336     #     01
- 337     #
- 338     # trace contains (in any order):
- 339     #   label x is at address 0x7c10
- 340     #
- 341     # . prologue
- 342     55/push-ebp
- 343     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
- 344     # setup
- 345     # . clear-stream(_test-input-stream)
- 346     # . . push args
- 347     68/push  _test-input-stream/imm32
- 348     # . . call
- 349     e8/call  clear-stream/disp32
- 350     # . . discard args
- 351     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
- 352     # . clear-stream($_test-input-buffered-file->buffer)
- 353     # . . push args
- 354     68/push  $_test-input-buffered-file->buffer/imm32
- 355     # . . call
- 356     e8/call  clear-stream/disp32
- 357     # . . discard args
- 358     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
- 359     # . clear-stream(_test-output-stream)
- 360     # . . push args
- 361     68/push  _test-output-stream/imm32
+ 274     # check trace
+ 275 +-- 26 lines: #?     # dump *Trace-stream -----------------------------------------------------------------------------------------------------------------------------------------------
+ 301     # . check-trace-contains("label 'x' is at address 0x00007c05.", msg)
+ 302     # . . push args
+ 303     68/push  "F - test-subx-survey-computes-addresses/0"/imm32
+ 304     68/push  "label 'x' is at address 0x00007c05."/imm32
+ 305     # . . call
+ 306     e8/call  check-trace-contains/disp32
+ 307     # . . discard args
+ 308     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 309     # . epilogue
+ 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-subx-survey-computes-addresses-with-padding:
+ 315     # input:
+ 316     #   == code
+ 317     #   ab x/imm32
+ 318     #   == data 0x7c10
+ 319     #   x:
+ 320     #     01
+ 321     #
+ 322     # trace contains (in any order):
+ 323     #   label x is at address 0x7c10
+ 324     #
+ 325     # . prologue
+ 326     55/push-ebp
+ 327     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
+ 328     # setup
+ 329     # . clear-stream(_test-input-stream)
+ 330     # . . push args
+ 331     68/push  _test-input-stream/imm32
+ 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-input-buffered-file->buffer)
+ 337     # . . push args
+ 338     68/push  $_test-input-buffered-file->buffer/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-output-stream)
+ 344     # . . push args
+ 345     68/push  _test-output-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     # . clear-stream($_test-output-buffered-file->buffer)
+ 351     # . . push args
+ 352     68/push  $_test-output-buffered-file->buffer/imm32
+ 353     # . . call
+ 354     e8/call  clear-stream/disp32
+ 355     # . . discard args
+ 356     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+ 357     # initialize input
+ 358     # . write(_test-input-stream, "== code\n")
+ 359     # . . push args
+ 360     68/push  "== code\n"/imm32
+ 361     68/push  _test-input-stream/imm32
  362     # . . call
- 363     e8/call  clear-stream/disp32
+ 363     e8/call  write/disp32
  364     # . . discard args
- 365     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
- 366     # . clear-stream($_test-output-buffered-file->buffer)
+ 365     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 366     # . write(_test-input-stream, "ab x/imm32\n")
  367     # . . push args
- 368     68/push  $_test-output-buffered-file->buffer/imm32
- 369     # . . call
- 370     e8/call  clear-stream/disp32
- 371     # . . discard args
- 372     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
- 373     # initialize input
- 374     # . write(_test-input-stream, "== code\n")
+ 368     68/push  "ab x/imm32\n"/imm32
+ 369     68/push  _test-input-stream/imm32
+ 370     # . . call
+ 371     e8/call  write/disp32
+ 372     # . . discard args
+ 373     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 374     # . write(_test-input-stream, "== data\n")
  375     # . . push args
- 376     68/push  "== code\n"/imm32
+ 376     68/push  "== data 0x7c10\n"/imm32
  377     68/push  _test-input-stream/imm32
  378     # . . call
  379     e8/call  write/disp32
  380     # . . discard args
  381     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
- 382     # . write(_test-input-stream, "ab x/imm32\n")
+ 382     # . write(_test-input-stream, "x:\n")
  383     # . . push args
- 384     68/push  "ab x/imm32\n"/imm32
+ 384     68/push  "x:\n"/imm32
  385     68/push  _test-input-stream/imm32
  386     # . . call
  387     e8/call  write/disp32
  388     # . . discard args
  389     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
- 390     # . write(_test-input-stream, "== data\n")
+ 390     # . write(_test-input-stream, "01\n")
  391     # . . push args
- 392     68/push  "== data 0x7c10\n"/imm32
+ 392     68/push  "01\n"/imm32
  393     68/push  _test-input-stream/imm32
  394     # . . call
  395     e8/call  write/disp32
  396     # . . discard args
  397     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
- 398     # . write(_test-input-stream, "x:\n")
+ 398     # subx-survey(_test-input-buffered-file, _test-output-buffered-file)
  399     # . . push args
- 400     68/push  "x:\n"/imm32
- 401     68/push  _test-input-stream/imm32
+ 400     68/push  _test-output-buffered-file/imm32
+ 401     68/push  _test-input-buffered-file/imm32
  402     # . . call
- 403     e8/call  write/disp32
+ 403     e8/call  subx-survey/disp32
  404     # . . discard args
  405     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
- 406     # . write(_test-input-stream, "01\n")
- 407     # . . push args
- 408     68/push  "01\n"/imm32
- 409     68/push  _test-input-stream/imm32
- 410     # . . call
- 411     e8/call  write/disp32
- 412     # . . discard args
- 413     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
- 414     # subx-survey(_test-input-buffered-file, _test-output-buffered-file)
- 415     # . . push args
- 416     68/push  _test-output-buffered-file/imm32
- 417     68/push  _test-input-buffered-file/imm32
- 418     # . . call
- 419     e8/call  subx-survey/disp32
- 420     # . . discard args
- 421     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
- 422     # check trace
- 423 +-- 26 lines: #?     # dump *Trace-stream -----------------------------------------------------------------------------------------------------------------------------------------------
- 449     # . check-trace-contains("label 'x' is at address 0x00007c10.", msg)
- 450     # . . push args
- 451     68/push  "F - test-subx-survey-computes-addresses-with-padding/0"/imm32
- 452     68/push  "label 'x' is at address 0x00007c10."/imm32
- 453     # . . call
- 454     e8/call  check-trace-contains/disp32
- 455     # . . discard args
- 456     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
- 457     # . epilogue
- 458     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
- 459     5d/pop-to-ebp
- 460     c3/return
- 461 
- 462 compute-offsets:  # in: (addr stream byte), labels: (addr stream {(handle array byte), address})
- 463     # pseudocode:
- 464     #   var current-address = 0x7c00
- 465     #   var line: (stream byte 512)
- 466     #   while true                                  # line loop
- 467     #     clear-stream(line)
- 468     #     read-line(in, line)
- 469     #     if (line->write == 0) break               # end of file
- 470     #     while true                                # word loop
- 471     #       word-slice = next-word(line)
- 472     #       if slice-empty?(word-slice)             # end of line
- 473     #         break
- 474     #       else if slice-starts-with?(word-slice, "#")  # comment
- 475     #         break                                 # end of line
- 476     #       else if slice-equal?(word-slice, "==")  # segment header
- 477     #         word-slice = next-word(line)
- 478     #         if slice-empty?(word-slice)
- 479     #           abort
- 480     #         word-slice = next-word(line)  # segment address
- 481     #         if slice-empty?(word-slice)
- 482     #           goto line-loop              # segment address is optional
- 483     #         new-address = parse-hex-int-from-slice(word-slice)
- 484     #         if new-address < current-address
- 485     #           abort
- 486     #         current-address = new-address
- 487     #       else if label?(word-slice)
- 488     #         strip trailing ':' from word-slice
- 489     #         trace("label '" word-slice "' is at address " current-address ".")
- 490     #         # labels occupy no space, so no need to increment offsets
- 491     #       else
- 492     #         width = compute-width-of-slice(word-slice)
- 493     #         current-address += width
- 494     #
- 495     # . prologue
- 496     55/push-ebp
- 497     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
- 498     # . save registers
- 499     50/push-eax
- 500     51/push-ecx
- 501     52/push-edx
- 502     53/push-ebx
- 503     56/push-esi
- 504     57/push-edi
- 505     # var current-address/esi: int = 0x7c00
- 506     be/copy-to-esi  0x7c00/imm32
- 507     # var line/ecx: (stream byte 512)
- 508     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x200/imm32       # subtract from esp
- 509     68/push  0x200/imm32/size
- 510     68/push  0/imm32/read
- 511     68/push  0/imm32/write
- 512     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
- 513     # var word-slice/edx: (addr slice)
- 514     68/push  0/imm32
- 515     68/push  0/imm32
- 516     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
- 517 $compute-offsets:line-loop:
- 518     # clear-stream(line)
- 519     # . . push args
- 520     51/push-ecx
- 521     # . . call
- 522     e8/call  clear-stream/disp32
- 523     # . . discard args
- 524     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
- 525     # read-line(in, line)
- 526     # . . push args
- 527     51/push-ecx
- 528     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
- 529     # . . call
- 530     e8/call  read-line/disp32
- 531     # . . discard args
- 532     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
- 533     # if (line->write == 0) break
- 534     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
- 535     3d/compare-eax-and  0/imm32
- 536     0f 84/jump-if-=  $compute-offsets:end/disp32
- 537 +-- 33 lines: #?     # dump line --------------------------------------------------------------------------------------------------------------------------------------------------------
- 570 $compute-offsets:word-loop:
- 571     # next-word(line, word-slice)
- 572     # . . push args
- 573     52/push-edx
- 574     51/push-ecx
- 575     # . . call
- 576     e8/call  next-word/disp32
- 577     # . . discard args
- 578     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
- 579 $compute-offsets:case-empty:
- 580     # if slice-empty?(word-slice) break
- 581     # . eax = slice-empty?(word-slice)
- 582     # . . push args
- 583     52/push-edx
- 584     # . . call
- 585     e8/call  slice-empty?/disp32
- 586     # . . discard args
- 587     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
- 588     # . if (eax != false) break
- 589     3d/compare-eax-and  0/imm32/false
- 590     0f 85/jump-if-!=  $compute-offsets:line-loop/disp32
- 591 $compute-offsets:case-comment:
- 592     # if slice-starts-with?(word-slice, "#") break
- 593     # . . push args
- 594     68/push  "#"/imm32
- 595     52/push-edx
- 596     # . . call
- 597     e8/call  slice-starts-with?/disp32
- 598     # . . discard args
- 599     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
- 600     # . if (eax != false) break
- 601     3d/compare-eax-and  0/imm32/false
- 602     0f 85/jump-if-!=  $compute-offsets:line-loop/disp32
- 603 $compute-offsets:case-segment-header:
- 604     # if !slice-equal?(word-slice, "==") goto next case
- 605     # . eax = slice-equal?(word-slice, "==")
- 606     # . . push args
- 607     68/push  "=="/imm32
- 608     52/push-edx
- 609     # . . call
- 610     e8/call  slice-equal?/disp32
- 611     # . . discard args
- 612     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
- 613     # . if (eax != false) break
- 614     3d/compare-eax-and  0/imm32/false
- 615     0f 84/jump-if-=  $compute-offsets:case-label/disp32
- 616     # next-word(line, word-slice)
- 617     # . . push args
- 618     52/push-edx
- 619     51/push-ecx
- 620     # . . call
- 621     e8/call  next-word/disp32
- 622     # . . discard args
- 623     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
- 624     # if slice-empty?(word-slice) abort
- 625     # . eax = slice-empty?(word-slice)
- 626     # . . push args
- 627     52/push-edx
- 628     # . . call
- 629     e8/call  slice-empty?/disp32
- 630     # . . discard args
- 631     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
- 632     # . if (eax != false) abort
- 633     3d/compare-eax-and  0/imm32/false
- 634     0f 85/jump-if-!=  $compute-offsets:abort/disp32
- 635     # next-word(line, word-slice)
- 636     # . . push args
- 637     52/push-edx
- 638     51/push-ecx
- 639     # . . call
- 640     e8/call  next-word/disp32
- 641     # . . discard args
- 642     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
- 643     # if slice-empty?(word-slice) break
- 644     # . eax = slice-empty?(word-slice)
- 645     # . . push args
- 646     52/push-edx
- 647     # . . call
- 648     e8/call  slice-empty?/disp32
- 649     # . . discard args
- 650     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
- 651     # . if (eax != false) break
- 652     3d/compare-eax-and  0/imm32/false
- 653     0f 85/jump-if-!=  $compute-offsets:line-loop/disp32
- 654     # var new-address/eax: int = parse-hex-int-from-slice(word-slice)
+ 406     # check trace
+ 407 +-- 26 lines: #?     # dump *Trace-stream -----------------------------------------------------------------------------------------------------------------------------------------------
+ 433     # . check-trace-contains("label 'x' is at address 0x00007c10.", msg)
+ 434     # . . push args
+ 435     68/push  "F - test-subx-survey-computes-addresses-with-padding/0"/imm32
+ 436     68/push  "label 'x' is at address 0x00007c10."/imm32
+ 437     # . . call
+ 438     e8/call  check-trace-contains/disp32
+ 439     # . . discard args
+ 440     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 441     # . epilogue
+ 442     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
+ 443     5d/pop-to-ebp
+ 444     c3/return
+ 445 
+ 446 compute-addresses:  # in: (addr stream byte), labels: (addr stream {(handle array byte), address})
+ 447     # pseudocode:
+ 448     #   var current-address = 0x7c00
+ 449     #   var line: (stream byte 512)
+ 450     #   while true                                  # line loop
+ 451     #     clear-stream(line)
+ 452     #     read-line(in, line)
+ 453     #     if (line->write == 0) break               # end of file
+ 454     #     while true                                # word loop
+ 455     #       word-slice = next-word(line)
+ 456     #       if slice-empty?(word-slice)             # end of line
+ 457     #         break
+ 458     #       else if slice-starts-with?(word-slice, "#")  # comment
+ 459     #         break                                 # end of line
+ 460     #       else if slice-equal?(word-slice, "==")  # segment header
+ 461     #         word-slice = next-word(line)
+ 462     #         if slice-empty?(word-slice)
+ 463     #           abort
+ 464     #         word-slice = next-word(line)  # segment address
+ 465     #         if slice-empty?(word-slice)
+ 466     #           goto line-loop              # segment address is optional
+ 467     #         new-address = parse-hex-int-from-slice(word-slice)
+ 468     #         if new-address < current-address
+ 469     #           abort
+ 470     #         current-address = new-address
+ 471     #       else if label?(word-slice)
+ 472     #         strip trailing ':' from word-slice
+ 473     #         trace("label '" word-slice "' is at address " current-address ".")
+ 474     #         # labels occupy no space, so no need to increment offsets
+ 475     #       else
+ 476     #         width = compute-width-of-slice(word-slice)
+ 477     #         current-address += width
+ 478     #
+ 479     # . prologue
+ 480     55/push-ebp
+ 481     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
+ 482     # . save registers
+ 483     50/push-eax
+ 484     51/push-ecx
+ 485     52/push-edx
+ 486     53/push-ebx
+ 487     56/push-esi
+ 488     57/push-edi
+ 489     # var current-address/esi: int = 0x7c00
+ 490     be/copy-to-esi  0x7c00/imm32
+ 491     # var line/ecx: (stream byte 512)
+ 492     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x200/imm32       # subtract from esp
+ 493     68/push  0x200/imm32/size
+ 494     68/push  0/imm32/read
+ 495     68/push  0/imm32/write
+ 496     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
+ 497     # var word-slice/edx: (addr slice)
+ 498     68/push  0/imm32
+ 499     68/push  0/imm32
+ 500     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
+ 501 $compute-addresses:line-loop:
+ 502     # clear-stream(line)
+ 503     # . . push args
+ 504     51/push-ecx
+ 505     # . . call
+ 506     e8/call  clear-stream/disp32
+ 507     # . . discard args
+ 508     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+ 509     # read-line(in, line)
+ 510     # . . push args
+ 511     51/push-ecx
+ 512     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
+ 513     # . . call
+ 514     e8/call  read-line/disp32
+ 515     # . . discard args
+ 516     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 517     # if (line->write == 0) break
+ 518     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
+ 519     3d/compare-eax-and  0/imm32
+ 520     0f 84/jump-if-=  $compute-addresses:end/disp32
+ 521 +-- 33 lines: #?     # dump line --------------------------------------------------------------------------------------------------------------------------------------------------------
+ 554 $compute-addresses:word-loop:
+ 555     # next-word(line, word-slice)
+ 556     # . . push args
+ 557     52/push-edx
+ 558     51/push-ecx
+ 559     # . . call
+ 560     e8/call  next-word/disp32
+ 561     # . . discard args
+ 562     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 563 $compute-addresses:case-empty:
+ 564     # if slice-empty?(word-slice) break
+ 565     # . eax = slice-empty?(word-slice)
+ 566     # . . push args
+ 567     52/push-edx
+ 568     # . . call
+ 569     e8/call  slice-empty?/disp32
+ 570     # . . discard args
+ 571     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+ 572     # . if (eax != false) break
+ 573     3d/compare-eax-and  0/imm32/false
+ 574     0f 85/jump-if-!=  $compute-addresses:line-loop/disp32
+ 575 $compute-addresses:case-comment:
+ 576     # if slice-starts-with?(word-slice, "#") break
+ 577     # . . push args
+ 578     68/push  "#"/imm32
+ 579     52/push-edx
+ 580     # . . call
+ 581     e8/call  slice-starts-with?/disp32
+ 582     # . . discard args
+ 583     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 584     # . if (eax != false) break
+ 585     3d/compare-eax-and  0/imm32/false
+ 586     0f 85/jump-if-!=  $compute-addresses:line-loop/disp32
+ 587 $compute-addresses:case-segment-header:
+ 588     # if !slice-equal?(word-slice, "==") goto next case
+ 589     # . eax = slice-equal?(word-slice, "==")
+ 590     # . . push args
+ 591     68/push  "=="/imm32
+ 592     52/push-edx
+ 593     # . . call
+ 594     e8/call  slice-equal?/disp32
+ 595     # . . discard args
+ 596     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 597     # . if (eax != false) break
+ 598     3d/compare-eax-and  0/imm32/false
+ 599     0f 84/jump-if-=  $compute-addresses:case-label/disp32
+ 600     # next-word(line, word-slice)
+ 601     # . . push args
+ 602     52/push-edx
+ 603     51/push-ecx
+ 604     # . . call
+ 605     e8/call  next-word/disp32
+ 606     # . . discard args
+ 607     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 608     # if slice-empty?(word-slice) abort
+ 609     # . eax = slice-empty?(word-slice)
+ 610     # . . push args
+ 611     52/push-edx
+ 612     # . . call
+ 613     e8/call  slice-empty?/disp32
+ 614     # . . discard args
+ 615     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+ 616     # . if (eax != false) abort
+ 617     3d/compare-eax-and  0/imm32/false
+ 618     0f 85/jump-if-!=  $compute-addresses:abort/disp32
+ 619     # next-word(line, word-slice)
+ 620     # . . push args
+ 621     52/push-edx
+ 622     51/push-ecx
+ 623     # . . call
+ 624     e8/call  next-word/disp32
+ 625     # . . discard args
+ 626     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 627     # if slice-empty?(word-slice) break
+ 628     # . eax = slice-empty?(word-slice)
+ 629     # . . push args
+ 630     52/push-edx
+ 631     # . . call
+ 632     e8/call  slice-empty?/disp32
+ 633     # . . discard args
+ 634     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+ 635     # . if (eax != false) break
+ 636     3d/compare-eax-and  0/imm32/false
+ 637     0f 85/jump-if-!=  $compute-addresses:line-loop/disp32
+ 638     # var new-address/eax: int = parse-hex-int-from-slice(word-slice)
+ 639     # . . push args
+ 640     52/push-edx
+ 641     # . . call
+ 642     e8/call  parse-hex-int-from-slice/disp32
+ 643     # . . discard args
+ 644     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+ 645     # if (new-address < current-address) abort
+ 646     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           6/r32/esi   .               .                 # compare eax with esi
+ 647     0f 82/jump-if-addr<  $compute-addresses:error-bad-segment-address/disp32
+ 648     # current-address = new-address
+ 649     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to esi
+ 650     # break
+ 651     e9/jump  $compute-addresses:line-loop/disp32
+ 652 $compute-addresses:case-label:
+ 653     # if (!label?(word-slice)) goto next case
+ 654     # . eax = label?(word-slice)
  655     # . . push args
  656     52/push-edx
  657     # . . call
- 658     e8/call  parse-hex-int-from-slice/disp32
+ 658     e8/call  label?/disp32
  659     # . . discard args
  660     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
- 661     # if (new-address < current-address) abort
- 662     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           6/r32/esi   .               .                 # compare eax with esi
- 663     0f 82/jump-if-addr<  $compute-offsets:error-bad-segment-address/disp32
- 664     # current-address = new-address
- 665     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to esi
- 666     # break
- 667     e9/jump  $compute-offsets:line-loop/disp32
- 668 $compute-offsets:case-label:
- 669     # if (!label?(word-slice)) goto next case
- 670     # . eax = label?(word-slice)
- 671     # . . push args
- 672     52/push-edx
- 673     # . . call
- 674     e8/call  label?/disp32
- 675     # . . discard args
- 676     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
- 677     # . if (eax == false) goto next case
- 678     3d/compare-eax-and  0/imm32/false
- 679     0f 84/jump-if-=  $compute-offsets:case-default/disp32
- 680     # strip trailing ':' from word-slice
- 681     ff          1/subop/decrement   1/mod/*+disp8   2/rm32/edx    .           .             .           .           4/disp8         .                 # decrement *(edx+4)
- 682     # var tmp/eax: (addr int) = get-or-insert-slice(labels, word-slice, row-size=12)
- 683     # . . push args
- 684     68/push  Heap/imm32
- 685     68/push  0xc/imm32/row-size
- 686     52/push-edx
- 687     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
- 688     # . . call
- 689     e8/call  get-or-insert-slice/disp32
- 690     # . . discard args
- 691     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
- 692     # *tmp = current-address
- 693     89/copy                         0/mod/indirect  0/rm32/eax    .           .             .           6/r32/esi   .               .                 # copy esi to *eax
- 694     # trace-slsns("label '" word-slice "' is at address " current-address ".")
- 695     # . . push args
- 696     68/push  "."/imm32
- 697     56/push-esi
- 698     68/push  "' is at address "/imm32
- 699     52/push-edx
- 700     68/push  "label '"/imm32
- 701     # . . call
- 702     e8/call  trace-slsns/disp32
- 703     # . . discard args
- 704     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
- 705     # continue
- 706     e9/jump  $compute-offsets:word-loop/disp32
- 707 $compute-offsets:case-default:
- 708     # width/eax = compute-width-of-slice(word-slice)
- 709     # . . push args
- 710     52/push-edx
- 711     # . . call
- 712     e8/call compute-width-of-slice/disp32
- 713     # . . discard args
- 714     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
- 715     # current-address += width
- 716     01/add                          3/mod/direct    6/rm32/esi    .           .             .           0/r32/eax   .               .                 # add eax to *esi
- 717 +-- 41 lines: #?     # dump segment-offset ----------------------------------------------------------------------------------------------------------------------------------------------
- 758     e9/jump $compute-offsets:word-loop/disp32
- 759 $compute-offsets:end:
- 760     # . reclaim locals
- 761     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x214/imm32       # add to esp
- 762     # . restore registers
- 763     5f/pop-to-edi
- 764     5e/pop-to-esi
- 765     5b/pop-to-ebx
- 766     5a/pop-to-edx
- 767     59/pop-to-ecx
- 768     58/pop-to-eax
- 769     # . epilogue
- 770     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
- 771     5d/pop-to-ebp
- 772     c3/return
- 773 
- 774 $compute-offsets:abort:
- 775     # . _write(2/stderr, error)
- 776     # . . push args
- 777     68/push  "'==' must be followed by segment name and optionally an address\n"/imm32
- 778     68/push  2/imm32/stderr
- 779     # . . call
- 780     e8/call  _write/disp32
- 781     # . . discard args
- 782     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
- 783     # . syscall(exit, 1)
- 784     bb/copy-to-ebx  1/imm32
- 785     e8/call  syscall_exit/disp32
- 786     # never gets here
- 787 
- 788 $compute-offsets:error-bad-segment-address:
- 789     # . _write(2/stderr, error)
- 790     # . . push args
- 791     68/push  "'==' specifies an address that implies negative padding\n"/imm32
- 792     68/push  2/imm32/stderr
- 793     # . . call
- 794     e8/call  _write/disp32
- 795     # . . discard args
- 796     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
- 797     # . syscall(exit, 1)
- 798     bb/copy-to-ebx  1/imm32
- 799     e8/call  syscall_exit/disp32
- 800     # never gets here
- 801 
- 802 test-compute-offsets:
- 803     # input:
- 804     #   == code
- 805     #   ab x/imm32  # skip comment
- 806     #   == data
- 807     #   00
- 808     #   x:
- 809     #     34
- 810     #
- 811     # trace contains (in any order):
- 812     #   label 'x' is at address 0x7c06.
- 813     #
- 814     # . prologue
- 815     55/push-ebp
- 816     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
- 817     # setup
- 818     # . clear-stream(_test-input-stream)
- 819     # . . push args
- 820     68/push  _test-input-stream/imm32
- 821     # . . call
- 822     e8/call  clear-stream/disp32
- 823     # . . discard args
- 824     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
- 825     # var labels/edx: (stream byte 2*12)
- 826     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x18/imm32        # subtract from esp
- 827     68/push  0x18/imm32/size
- 828     68/push  0/imm32/read
- 829     68/push  0/imm32/write
- 830     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
- 831     # initialize input
- 832     # . write(_test-input-stream, "== code\n")
+ 661     # . if (eax == false) goto next case
+ 662     3d/compare-eax-and  0/imm32/false
+ 663     0f 84/jump-if-=  $compute-addresses:case-default/disp32
+ 664     # strip trailing ':' from word-slice
+ 665     ff          1/subop/decrement   1/mod/*+disp8   2/rm32/edx    .           .             .           .           4/disp8         .                 # decrement *(edx+4)
+ 666     # var tmp/eax: (addr int) = get-or-insert-slice(labels, word-slice, row-size=12)
+ 667     # . . push args
+ 668     68/push  Heap/imm32
+ 669     68/push  0xc/imm32/row-size
+ 670     52/push-edx
+ 671     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
+ 672     # . . call
+ 673     e8/call  get-or-insert-slice/disp32
+ 674     # . . discard args
+ 675     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
+ 676     # *tmp = current-address
+ 677     89/copy                         0/mod/indirect  0/rm32/eax    .           .             .           6/r32/esi   .               .                 # copy esi to *eax
+ 678     # trace-slsns("label '" word-slice "' is at address " current-address ".")
+ 679     # . . push args
+ 680     68/push  "."/imm32
+ 681     56/push-esi
+ 682     68/push  "' is at address "/imm32
+ 683     52/push-edx
+ 684     68/push  "label '"/imm32
+ 685     # . . call
+ 686     e8/call  trace-slsns/disp32
+ 687     # . . discard args
+ 688     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
+ 689     # continue
+ 690     e9/jump  $compute-addresses:word-loop/disp32
+ 691 $compute-addresses:case-default:
+ 692     # width/eax = compute-width-of-slice(word-slice)
+ 693     # . . push args
+ 694     52/push-edx
+ 695     # . . call
+ 696     e8/call compute-width-of-slice/disp32
+ 697     # . . discard args
+ 698     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+ 699     # current-address += width
+ 700     01/add                          3/mod/direct    6/rm32/esi    .           .             .           0/r32/eax   .               .                 # add eax to *esi
+ 701 +-- 41 lines: #?     # dump segment-offset ----------------------------------------------------------------------------------------------------------------------------------------------
+ 742     e9/jump $compute-addresses:word-loop/disp32
+ 743 $compute-addresses:end:
+ 744     # . reclaim locals
+ 745     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x214/imm32       # add to esp
+ 746     # . restore registers
+ 747     5f/pop-to-edi
+ 748     5e/pop-to-esi
+ 749     5b/pop-to-ebx
+ 750     5a/pop-to-edx
+ 751     59/pop-to-ecx
+ 752     58/pop-to-eax
+ 753     # . epilogue
+ 754     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
+ 755     5d/pop-to-ebp
+ 756     c3/return
+ 757 
+ 758 $compute-addresses:abort:
+ 759     # . _write(2/stderr, error)
+ 760     # . . push args
+ 761     68/push  "'==' must be followed by segment name and optionally an address\n"/imm32
+ 762     68/push  2/imm32/stderr
+ 763     # . . call
+ 764     e8/call  _write/disp32
+ 765     # . . discard args
+ 766     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 767     # . syscall(exit, 1)
+ 768     bb/copy-to-ebx  1/imm32
+ 769     e8/call  syscall_exit/disp32
+ 770     # never gets here
+ 771 
+ 772 $compute-addresses:error-bad-segment-address:
+ 773     # . _write(2/stderr, error)
+ 774     # . . push args
+ 775     68/push  "'==' specifies an address that implies negative padding\n"/imm32
+ 776     68/push  2/imm32/stderr
+ 777     # . . call
+ 778     e8/call  _write/disp32
+ 779     # . . discard args
+ 780     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 781     # . syscall(exit, 1)
+ 782     bb/copy-to-ebx  1/imm32
+ 783     e8/call  syscall_exit/disp32
+ 784     # never gets here
+ 785 
+ 786 test-compute-addresses:
+ 787     # input:
+ 788     #   == code
+ 789     #   ab x/imm32  # skip comment
+ 790     #   == data
+ 791     #   00
+ 792     #   x:
+ 793     #     34
+ 794     #
+ 795     # trace contains (in any order):
+ 796     #   label 'x' is at address 0x7c06.
+ 797     #
+ 798     # . prologue
+ 799     55/push-ebp
+ 800     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
+ 801     # setup
+ 802     # . clear-stream(_test-input-stream)
+ 803     # . . push args
+ 804     68/push  _test-input-stream/imm32
+ 805     # . . call
+ 806     e8/call  clear-stream/disp32
+ 807     # . . discard args
+ 808     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+ 809     # var labels/edx: (stream byte 2*12)
+ 810     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x18/imm32        # subtract from esp
+ 811     68/push  0x18/imm32/size
+ 812     68/push  0/imm32/read
+ 813     68/push  0/imm32/write
+ 814     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
+ 815     # initialize input
+ 816     # . write(_test-input-stream, "== code\n")
+ 817     # . . push args
+ 818     68/push  "== code\n"/imm32
+ 819     68/push  _test-input-stream/imm32
+ 820     # . . call
+ 821     e8/call  write/disp32
+ 822     # . . discard args
+ 823     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 824     # . write(_test-input-stream, "ab x/imm32  # skip comment\n")
+ 825     # . . push args
+ 826     68/push  "ab x/imm32  # skip comment\n"/imm32
+ 827     68/push  _test-input-stream/imm32
+ 828     # . . call
+ 829     e8/call  write/disp32
+ 830     # . . discard args
+ 831     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 832     # . write(_test-input-stream, "== data\n")
  833     # . . push args
- 834     68/push  "== code\n"/imm32
+ 834     68/push  "== data\n"/imm32
  835     68/push  _test-input-stream/imm32
  836     # . . call
  837     e8/call  write/disp32
  838     # . . discard args
  839     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
- 840     # . write(_test-input-stream, "ab x/imm32  # skip comment\n")
+ 840     # . write(_test-input-stream, "00\n")
  841     # . . push args
- 842     68/push  "ab x/imm32  # skip comment\n"/imm32
+ 842     68/push  "00\n"/imm32
  843     68/push  _test-input-stream/imm32
  844     # . . call
  845     e8/call  write/disp32
  846     # . . discard args
  847     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
- 848     # . write(_test-input-stream, "== data\n")
+ 848     # . write(_test-input-stream, "x:\n")
  849     # . . push args
- 850     68/push  "== data\n"/imm32
+ 850     68/push  "x:\n"/imm32
  851     68/push  _test-input-stream/imm32
  852     # . . call
  853     e8/call  write/disp32
  854     # . . discard args
  855     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
- 856     # . write(_test-input-stream, "00\n")
+ 856     # . write(_test-input-stream, "34\n")
  857     # . . push args
- 858     68/push  "00\n"/imm32
+ 858     68/push  "34\n"/imm32
  859     68/push  _test-input-stream/imm32
  860     # . . call
  861     e8/call  write/disp32
  862     # . . discard args
  863     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
- 864     # . write(_test-input-stream, "x:\n")
+ 864     # compute-addresses(_test-input-stream, labels)
  865     # . . push args
- 866     68/push  "x:\n"/imm32
+ 866     52/push-edx
  867     68/push  _test-input-stream/imm32
  868     # . . call
- 869     e8/call  write/disp32
+ 869     e8/call  compute-addresses/disp32
  870     # . . discard args
- 871     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
- 872     # . write(_test-input-stream, "34\n")
- 873     # . . push args
- 874     68/push  "34\n"/imm32
- 875     68/push  _test-input-stream/imm32
- 876     # . . call
- 877     e8/call  write/disp32
- 878     # . . discard args
- 879     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
- 880     # compute-offsets(_test-input-stream, labels)
- 881     # . . push args
- 882     52/push-edx
- 883     68/push  _test-input-stream/imm32
- 884     # . . call
- 885     e8/call  compute-offsets/disp32
- 886     # . . discard args
- 887     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32          # add to esp
- 888 +-- 26 lines: #?     # dump *Trace-stream -----------------------------------------------------------------------------------------------------------------------------------------------
- 914     # . check-trace-contains("label 'x' is at address 0x00007c06.", msg)
- 915     # . . push args
- 916     68/push  "F - test-compute-offsets"/imm32
- 917     68/push  "label 'x' is at address 0x00007c06."/imm32
- 918     # . . call
- 919     e8/call  check-trace-contains/disp32
- 920     # . . discard args
- 921     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
- 922     # . check-ints-equal(labels->write, 0xc, msg)
- 923     # . . push args
- 924     68/push  "F - test-compute-offsets-maintains-labels-write-index"/imm32
- 925     68/push  0xc/imm32/1-entry
- 926     ff          6/subop/push        0/mod/indirect  2/rm32/edx    .           .             .           .           .               .                 # push *edx
- 927     # . . call
- 928     e8/call  check-ints-equal/disp32
- 929     # . . discard args
- 930     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
- 931     # . reclaim locals
- 932     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x24/imm32        # add to esp
- 933     # . epilogue
- 934     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
- 935     5d/pop-to-ebp
- 936     c3/return
- 937 
- 938 # global scratch space for emit-output
- 939 == data
- 940 
- 941 emit-output:datum:  # slice
- 942   0/imm32/start
- 943   0/imm32/end
- 944 
- 945 == code
- 946 
- 947 emit-output:  # in: (addr stream byte), out: (addr buffered-file), labels: (addr stream {(handle array byte), address})
- 948     # pseudocode:
- 949     #   var address-of-next-instruction = 0x7c00
- 950     #   var line: (stream byte 512)
- 951     #   line-loop:
- 952     #   while true
- 953     #     clear-stream(line)
- 954     #     read-line(in, line)
- 955     #     if (line->write == 0) break               # end of file
- 956     #     address-of-next-instruction += num-bytes(line)
- 957     #     var far-jump-or-call? = far-jump-or-call?(line)
- 958     #     rewind-stream(line)
- 959     #     while true
- 960     #       var word-slice = next-word(line)
- 961     #       if slice-empty?(word-slice)             # end of line
- 962     #         break
- 963     #       if slice-starts-with?(word-slice, "#")  # comment
- 964     #         break
- 965     #       if label?(word-slice)                # no need for label declarations anymore
- 966     #         goto line-loop                        # don't insert empty lines
- 967     #       if slice-equal?(word-slice, "==")       # no need for segment header lines
- 968     #         word-slice = next-word(line)          # skip segment name
- 969     #         word-slice = next-word(line)
- 970     #         if !slice-empty?(word-slice)
- 971     #           new-address = parse-hex-int-from-slice(word-slice)
- 972     #           write-buffered(out, "# " address-of-next-instruction "\n")
- 973     #           while address-of-next-instruction < new-address
- 974     #             write-buffered("00")
- 975     #             ++address-of-next-instruction
- 976     #           write-buffered(out, "# " address-of-next-instruction "\n")
- 977     #           goto line-loop                      # don't insert empty lines
- 978     #       if length(word-slice) == 2
- 979     #         write-slice-buffered(out, word-slice)
- 980     #         write-buffered(out, " ")
- 981     #         continue
- 982     #       var datum: (addr slice) = next-token-from-slice(word-slice->start, word-slice->end, "/")
- 983     #       var address: (addr int) = get-slice(labels, datum)
- 984     #       if has-metadata?(word-slice, "imm8")
- 985     #         emit(out, *address, 1)
- 986     #       else if has-metadata?(word-slice, "imm16")
- 987     #         emit(out, *address, 2)
- 988     #       else if has-metadata?(word-slice, "imm32")
- 989     #         emit(out, *address, 4)
- 990     #       else if has-metadata?(word-slice, "disp8")
- 991     #         value = *address - address-of-next-instruction
- 992     #         emit(out, value, 1)
- 993     #       else if has-metadata?(word-slice, "disp32")
- 994     #         if far-jump-or-call?
- 995     #           value = *address - address-of-next-instruction
- 996     #         else
- 997     #           value = *address
- 998     #         emit(out, value, 4)
- 999     #       else
-1000     #         abort
-1001     #     write-buffered(out, "\n")
-1002     #
-1003     # registers:
-1004     #   line: ecx
-1005     #   word-slice: edx
-1006     #   address-of-next-instruction: ebx
-1007     #   far-jump-or-call?: edi
-1008     #   address: esi (inner loop only)
-1009     #   temporaries: eax, esi (outer loop)
-1010     #
-1011     # . prologue
-1012     55/push-ebp
-1013     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
-1014     # . save registers
-1015     50/push-eax
-1016     51/push-ecx
-1017     52/push-edx
-1018     53/push-ebx
-1019     56/push-esi
-1020     57/push-edi
-1021     # var line/ecx: (stream byte 512)
-1022     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x200/imm32       # subtract from esp
-1023     68/push  0x200/imm32/size
-1024     68/push  0/imm32/read
-1025     68/push  0/imm32/write
-1026     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
-1027     # var word-slice/edx: slice
-1028     68/push  0/imm32/end
-1029     68/push  0/imm32/start
-1030     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
-1031     # var address-of-next-instruction/ebx = 0x7c00
-1032     bb/copy-to-ebx  0x7c00/imm32
-1033 $emit-output:line-loop:
-1034     # clear-stream(line)
-1035     # . . push args
-1036     51/push-ecx
-1037     # . . call
-1038     e8/call  clear-stream/disp32
-1039     # . . discard args
-1040     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-1041     # read-line(in, line)
-1042     # . . push args
-1043     51/push-ecx
-1044     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
-1045     # . . call
-1046     e8/call  read-line/disp32
-1047     # . . discard args
-1048     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-1049 +-- 33 lines: #?     # dump line --------------------------------------------------------------------------------------------------------------------------------------------------------
-1082 $emit-output:check-for-end-of-input:
-1083     # if (line->write == 0) break
-1084     81          7/subop/compare     0/mod/indirect  1/rm32/ecx    .           .             .           .           .               0/imm32           # compare *ecx
-1085     0f 84/jump-if-=  $emit-output:end/disp32
-1086     # address-of-next-instruction += num-bytes(line)
-1087     # . eax = num-bytes(line)
-1088     # . . push args
-1089     51/push-ecx
-1090     # . . call
-1091     e8/call  num-bytes/disp32
-1092     # . . discard args
-1093     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-1094     # . ebx += eax
-1095     01/add                          3/mod/direct    3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # add eax to ebx
-1096     # var far-jump-or-call?/edi: boolean = far-jump-or-call?(line)
-1097     # . . push args
-1098     51/push-ecx
-1099     # . . call
-1100     e8/call  far-jump-or-call?/disp32
-1101     # . . discard args
-1102     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-1103     # rewind-stream(line)
-1104     # . . push args
-1105     51/push-ecx
-1106     # . . call
-1107     e8/call  rewind-stream/disp32
-1108     # . . discard args
-1109     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-1110 $emit-output:word-loop:
-1111     # next-word(line, word-slice)
-1112     # . . push args
-1113     52/push-edx
-1114     51/push-ecx
-1115     # . . call
-1116     e8/call  next-word/disp32
-1117     # . . discard args
-1118     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-1119 +-- 33 lines: #?     # dump word-slice --------------------------------------------------------------------------------------------------------------------------------------------------
-1152 $emit-output:check-for-end-of-line:
-1153     # if (slice-empty?(word-slice)) break
-1154     # . eax = slice-empty?(word-slice)
-1155     # . . push args
-1156     52/push-edx
-1157     # . . call
-1158     e8/call  slice-empty?/disp32
-1159     # . . discard args
-1160     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-1161     # . if (eax != 0) break
-1162     3d/compare-eax-and  0/imm32/false
-1163     0f 85/jump-if-!=  $emit-output:next-line/disp32
-1164 $emit-output:check-for-comment:
-1165     # if (slice-starts-with?(word-slice, "#")) break
-1166     # . start/esi = word-slice->start
-1167     8b/copy                         0/mod/indirect  2/rm32/edx    .           .             .           6/r32/esi   .               .                 # copy *edx to esi
-1168     # . c/eax = *start
-1169     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
-1170     8a/copy-byte                    0/mod/indirect  6/rm32/esi    .           .             .           0/r32/AL    .               .                 # copy byte at *esi to AL
-1171     # . if (eax == '#') break
-1172     3d/compare-eax-and  0x23/imm32/hash
-1173     0f 84/jump-if-=  $emit-output:next-line/disp32
-1174 $emit-output:check-for-label:
-1175     # if label?(word-slice) break
-1176     # . eax = label?(word-slice)
-1177     # . . push args
-1178     52/push-edx
-1179     # . . call
-1180     e8/call  label?/disp32
-1181     # . . discard args
-1182     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-1183     # . if (eax != false) break
-1184     3d/compare-eax-and  0/imm32/false
-1185     0f 85/jump-if-!=  $emit-output:line-loop/disp32
-1186 $emit-output:check-for-segment-header:
-1187     # if !slice-equal?(word-slice, "==") goto next check
-1188     # . eax = slice-equal?(word-slice, "==")
-1189     # . . push args
-1190     68/push  "=="/imm32
-1191     52/push-edx
-1192     # . . call
-1193     e8/call  slice-equal?/disp32
-1194     # . . discard args
-1195     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-1196     # . if (eax == false) goto next check
-1197     3d/compare-eax-and  0/imm32/false
-1198     0f 84/jump-if-=  $emit-output:2-character/disp32
-1199     # skip segment name
-1200     # . next-word(line, word-slice)
-1201     # . . push args
-1202     52/push-edx
-1203     51/push-ecx
-1204     # . . call
-1205     e8/call  next-word/disp32
-1206     # . . discard args
-1207     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-1208     # compute segment address if it exists
-1209     # . next-word(line, word-slice)
-1210     # . . push args
-1211     52/push-edx
-1212     51/push-ecx
-1213     # . . call
-1214     e8/call  next-word/disp32
-1215     # . . discard args
-1216     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-1217     # . if slice-empty?(word-slice) goto padding-done
-1218     # . . push args
-1219     52/push-edx
-1220     # . . call
-1221     e8/call  slice-empty?/disp32
-1222     # . . discard args
-1223     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-1224     # . .
-1225     3d/compare-eax-and  0/imm32/false
-1226     0f 85/jump-if-!=  $emit-output:padding-done/disp32
-1227     # . var new-address/eax: int = parse-hex-int-from-slice(word-slice)
-1228     # . . push args
-1229     52/push-edx
-1230     # . . call
-1231     e8/call  parse-hex-int-from-slice/disp32
-1232     # . . discard args
-1233     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-1234     # write-buffered(out, "# " address-of-next-instruction "\n")
-1235     # . write-buffered(out, "# ")
-1236     # . . push args
-1237     68/push  "# "/imm32
-1238     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
-1239     # . . call
-1240     e8/call  write-buffered/disp32
-1241     # . . discard args
-1242     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-1243     # . write-int32-hex-buffered(out, address-of-next-instruction)
-1244     # . . push args
-1245     53/push-ebx
-1246     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
-1247     # . . call
-1248     e8/call  write-int32-hex-buffered/disp32
-1249     # . . discard args
-1250     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-1251     # . write-buffered(out, "\n")
-1252     # . . push args
-1253     68/push  Newline/imm32
-1254     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
-1255     # . . call
-1256     e8/call  write-buffered/disp32
-1257     # . . discard args
-1258     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-1259 $emit-output:padding-loop:
-1260     # if (address-of-next-instruction >= new-address) goto padding-loop-done
-1261     39/compare                      3/mod/direct    3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # compare ebx with eax
-1262     73/jump-if-addr>=  $emit-output:padding-loop-done/disp8
-1263     # if (address-of-next-instruction % 8 == 0) write-buffered("\n")
-1264     53/push-ebx
-1265     81          4/subop/and         3/mod/direct    3/rm32/ebx    .           .             .           .           .               7/imm32           # bitwise and of ebx
-1266     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0/imm32           # compare ebx
-1267     5b/pop-to-ebx
-1268     75/jump-if-!=  $emit-output:padding-core/disp8
-1269     # . write-buffered(out, "\n")
-1270     # . . push args
-1271     68/push  Newline/imm32
-1272     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
-1273     # . . call
-1274     e8/call  write-buffered/disp32
-1275     # . . discard args
-1276     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-1277 $emit-output:padding-core:
-1278     # write-buffered("00")
-1279     # . . push args
-1280     68/push  "00 "/imm32
-1281     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
-1282     # . . call
-1283     e8/call  write-buffered/disp32
-1284     # . . discard args
-1285     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-1286     # ++address-of-next-instruction
-1287     43/increment-ebx
-1288     # loop
-1289     eb/jump $emit-output:padding-loop/disp8
-1290 $emit-output:padding-loop-done:
-1291     # . write-buffered(out, "\n")
-1292     # . . push args
-1293     68/push  Newline/imm32
-1294     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
-1295     # . . call
-1296     e8/call  write-buffered/disp32
-1297     # . . discard args
-1298     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-1299 $emit-output:padding-done:
-1300     # write-buffered(out, "# " address-of-next-instruction "\n")
-1301     # . write-buffered(out, "# ")
-1302     # . . push args
-1303     68/push  "# "/imm32
-1304     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
-1305     # . . call
-1306     e8/call  write-buffered/disp32
-1307     # . . discard args
-1308     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-1309     # . write-int32-hex-buffered(out, address-of-next-instruction)
-1310     # . . push args
-1311     53/push-ebx
-1312     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
-1313     # . . call
-1314     e8/call  write-int32-hex-buffered/disp32
-1315     # . . discard args
-1316     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-1317     # . write-buffered(out, "\n")
-1318     # . . push args
-1319     68/push  Newline/imm32
-1320     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
-1321     # . . call
-1322     e8/call  write-buffered/disp32
-1323     # . . discard args
-1324     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-1325     #
-1326     e9/jump  $emit-output:line-loop/disp32
-1327 $emit-output:2-character:
-1328     # if (size(word-slice) != 2) goto next check
-1329     # . eax = size(word-slice)
-1330     8b/copy                         1/mod/*+disp8   2/rm32/edx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(edx+4) to eax
-1331     2b/subtract                     0/mod/indirect  2/rm32/edx    .           .             .           0/r32/eax   .               .                 # subtract *edx from eax
-1332     # . if (eax != 2) goto next check
-1333     3d/compare-eax-and  2/imm32
-1334     75/jump-if-!=  $emit-output:check-metadata/disp8
-1335     # write-slice-buffered(out, word-slice)
-1336     # . . push args
-1337     52/push-edx
-1338     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
-1339     # . . call
-1340     e8/call  write-slice-buffered/disp32
-1341     # . . discard args
-1342     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-1343     # write-buffered(out, " ")
-1344     # . . push args
-1345     68/push  Space/imm32
-1346     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
-1347     # . . call
-1348     e8/call  write-buffered/disp32
-1349     # . . discard args
-1350     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-1351     # continue
-1352     e9/jump  $emit-output:word-loop/disp32
-1353 $emit-output:check-metadata:
-1354     # - if we get here, 'word-slice' must be a label to be looked up
-1355     # datum = next-token-from-slice(word-slice->start, word-slice->end, "/")
-1356     # . . push args
-1357     68/push  emit-output:datum/imm32
-1358     68/push  0x2f/imm32/slash
-1359     ff          6/subop/push        1/mod/*+disp8   2/rm32/edx    .           .             .           .           4/disp8         .                 # push *(edx+4)
-1360     ff          6/subop/push        0/mod/indirect  2/rm32/edx    .           .             .           .           .               .                 # push *edx
-1361     # . . call
-1362     e8/call  next-token-from-slice/disp32
-1363     # . . discard args
-1364     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
-1365 +-- 33 lines: #?     # dump datum -------------------------------------------------------------------------------------------------------------------------------------------------------
-1398     # address/esi: (addr int) = get-slice(labels, datum, row-size=12, "label table")
-1399     # . eax = get-slice(labels, datum, row-size=24, "label table")
-1400     # . . push args
-1401     68/push  "label table"/imm32
-1402     68/push  0xc/imm32/row-size
-1403     68/push  emit-output:datum/imm32
-1404     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
-1405     # . . call
-1406     e8/call  get-slice/disp32
-1407     # . . discard args
-1408     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
-1409     # . esi = eax
-1410     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to esi
-1411 $emit-output:check-imm8:
-1412     # if (!has-metadata?(word-slice, "imm8")) goto next check
-1413     # . eax = has-metadata?(edx, "imm8")
-1414     # . . push args
-1415     68/push  "imm8"/imm32
-1416     52/push-edx
-1417     # . . call
-1418     e8/call  has-metadata?/disp32
-1419     # . . discard args
-1420     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-1421     # . if (eax != false) abort
-1422     3d/compare-eax-and  0/imm32/false
-1423     74/jump-if-=  $emit-output:check-imm16/disp8
-1424 $emit-output:emit-imm8:
-1425     # emit-hex(out, *address, 1)
-1426     # . . push args
-1427     68/push  1/imm32
-1428     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
-1429     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
-1430     # . . call
-1431     e8/call  emit-hex/disp32
-1432     # . . discard args
-1433     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-1434     # continue
-1435     e9/jump  $emit-output:word-loop/disp32
-1436 $emit-output:check-imm16:
-1437     # if (!has-metadata?(word-slice, "imm16")) goto next check
-1438     # . eax = has-metadata?(edx, "imm16")
-1439     # . . push args
-1440     68/push  "imm16"/imm32
-1441     52/push-edx
-1442     # . . call
-1443     e8/call  has-metadata?/disp32
-1444     # . . discard args
-1445     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-1446     # . if (eax == false) goto next check
-1447     3d/compare-eax-and  0/imm32/false
-1448     74/jump-if-=  $emit-output:check-imm32/disp8
-1449 +-- 33 lines: #?     # dump *address ----------------------------------------------------------------------------------------------------------------------------------------------------
-1482 $emit-output:emit-imm16:
-1483     # emit-hex(out, *address, 2)
-1484     # . . push args
-1485     68/push  2/imm32
-1486     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
-1487     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
-1488     # . . call
-1489     e8/call  emit-hex/disp32
-1490     # . . discard args
-1491     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-1492     # TODO: ensure that the higher 2 bytes are zero
-1493     # continue
-1494     e9/jump  $emit-output:word-loop/disp32
-1495 $emit-output:check-imm32:
-1496     # if (!has-metadata?(word-slice, "imm32")) goto next check
-1497     # . eax = has-metadata?(edx, "imm32")
-1498     # . . push args
-1499     68/push  "imm32"/imm32
-1500     52/push-edx
-1501     # . . call
-1502     e8/call  has-metadata?/disp32
-1503     # . . discard args
-1504     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-1505     # . if (eax == false) goto next check
-1506     3d/compare-eax-and  0/imm32/false
-1507     74/jump-if-=  $emit-output:check-disp8/disp8
-1508 +-- 33 lines: #?     # dump *address ----------------------------------------------------------------------------------------------------------------------------------------------------
-1541 $emit-output:emit-imm32:
-1542     # emit-hex(out, *address, 4)
-1543     # . . push args
-1544     68/push  4/imm32
-1545     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
-1546     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
-1547     # . . call
-1548     e8/call  emit-hex/disp32
-1549     # . . discard args
-1550     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-1551     # continue
-1552     e9/jump  $emit-output:word-loop/disp32
-1553 $emit-output:check-disp8:
-1554     # if (!has-metadata?(word-slice, "disp8")) goto next check
-1555     # . eax = has-metadata?(edx, "disp8")
-1556     # . . push args
-1557     68/push  "disp8"/imm32
-1558     52/push-edx
-1559     # . . call
-1560     e8/call  has-metadata?/disp32
-1561     # . . discard args
-1562     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-1563     # . if (eax == false) goto next check
-1564     3d/compare-eax-and  0/imm32/false
-1565     74/jump-if-=  $emit-output:check-disp16/disp8
-1566 $emit-output:emit-disp8:
-1567     # emit-hex(out, *address - address-of-next-instruction, 1)
-1568     # . . push args
-1569     68/push  1/imm32
-1570     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
-1571     29/subtract                     3/mod/direct    0/rm32/eax    .           .             .           3/r32/ebx   .               .                 # subtract ebx from eax
-1572     50/push-eax
-1573     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
-1574     # . . call
-1575     e8/call  emit-hex/disp32
-1576     # . . discard args
-1577     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-1578     # continue
-1579     e9/jump  $emit-output:word-loop/disp32
-1580 $emit-output:check-disp16:
-1581     # if (!has-metadata?(word-slice, "disp16")) goto next check
-1582     # . eax = has-metadata?(edx, "disp16")
-1583     # . . push args
-1584     68/push  "disp16"/imm32
-1585     52/push-edx
-1586     # . . call
-1587     e8/call  has-metadata?/disp32
-1588     # . . discard args
-1589     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-1590     # . if (eax == false) goto next check
-1591     3d/compare-eax-and  0/imm32/false
-1592     74/jump-if-=  $emit-output:check-disp32/disp8
-1593 $emit-output:emit-disp16:
-1594     # emit-hex(out, *address - address-of-next-instruction, 2)
-1595     # . . push args
-1596     68/push  2/imm32
-1597     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
-1598     29/subtract                     3/mod/direct    0/rm32/eax    .           .             .           3/r32/ebx   .               .                 # subtract ebx from eax
-1599     50/push-eax
-1600     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
-1601     # . . call
-1602     e8/call  emit-hex/disp32
-1603     # . . discard args
-1604     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-1605     # continue
-1606     e9/jump  $emit-output:word-loop/disp32
-1607 $emit-output:check-disp32:
-1608     # if (!has-metadata?(word-slice, "disp32")) abort
-1609     # . eax = has-metadata?(edx, "disp32")
-1610     # . . push args
-1611     68/push  "disp32"/imm32
-1612     52/push-edx
-1613     # . . call
-1614     e8/call  has-metadata?/disp32
-1615     # . . discard args
-1616     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-1617     # . if (eax == false) abort
-1618     3d/compare-eax-and  0/imm32/false
-1619     0f 84/jump-if-=  $emit-output:abort/disp32
-1620 $emit-output:emit-disp32:
-1621     # var value/eax = *address
-1622     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
-1623     # if (far-jump-or-call?) value -= address-of-next-instruction
-1624     81          7/subop/compare     3/mod/direct    7/rm32/edi    .           .             .           .           .               0/imm32/false     # compare edi
-1625     74/jump-if-=  $emit-output:really-emit-disp32/disp8
-1626     29/subtract                     3/mod/direct    0/rm32/eax    .           .             .           3/r32/ebx   .               .                 # subtract ebx from eax
-1627 $emit-output:really-emit-disp32:
-1628     # emit-hex(out, value, 4)
-1629     # . . push args
-1630     68/push  4/imm32
-1631     50/push-eax
-1632     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
-1633     # . . call
-1634     e8/call  emit-hex/disp32
-1635     # . . discard args
-1636     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-1637     # continue
-1638     e9/jump  $emit-output:word-loop/disp32
-1639 $emit-output:next-line:
-1640     # write-buffered(out, "\n")
-1641     # . . push args
-1642     68/push  Newline/imm32
-1643     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
-1644     # . . call
-1645     e8/call  write-buffered/disp32
-1646     # . . discard args
-1647     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-1648     # loop
-1649     e9/jump  $emit-output:line-loop/disp32
-1650 $emit-output:end:
-1651     # . reclaim locals
-1652     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x214/imm32       # add to esp
-1653     # . restore registers
-1654     5f/pop-to-edi
-1655     5e/pop-to-esi
-1656     5b/pop-to-ebx
-1657     5a/pop-to-edx
-1658     59/pop-to-ecx
-1659     58/pop-to-eax
-1660     # . epilogue
-1661     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
-1662     5d/pop-to-ebp
-1663     c3/return
-1664 
-1665 $emit-output:abort:
-1666     # print(stderr, "missing metadata in " word-slice)
-1667     # . _write(2/stderr, "missing metadata in word ")
-1668     # . . push args
-1669     68/push  "emit-output: missing metadata in "/imm32
-1670     68/push  2/imm32/stderr
-1671     # . . call
-1672     e8/call  _write/disp32
-1673     # . . discard args
-1674     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-1675     # . write-slice-buffered(Stderr, word-slice)
-1676     # . . push args
-1677     52/push-edx
-1678     68/push  Stderr/imm32
-1679     # . . call
-1680     e8/call  write-slice-buffered/disp32
-1681     # . . discard args
-1682     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-1683     # . flush(Stderr)
-1684     # . . push args
-1685     68/push  Stderr/imm32
-1686     # . . call
-1687     e8/call  flush/disp32
-1688     # . . discard args
-1689     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-1690     # . syscall(exit, 1)
-1691     bb/copy-to-ebx  1/imm32
-1692     e8/call  syscall_exit/disp32
-1693     # never gets here
-1694 
-1695 test-emit-output-non-far-control-flow:
-1696     # labels turn into absolute addresses if opcodes are not far jumps or calls
-1697     #
-1698     # input:
-1699     #   in:
-1700     #     == code
-1701     #     ab cd ef gh
-1702     #     ij x/disp32
-1703     #     == data
-1704     #     00
-1705     #     34
-1706     #   labels:
-1707     #     - 'x': 0x11223344
-1708     #
-1709     # output:
-1710     #   ab cd ef gh
-1711     #   ij 44 33 22 11
-1712     #   00
-1713     #   34
-1714     #
-1715     # . prologue
-1716     55/push-ebp
-1717     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
-1718     # setup
-1719     # . clear-stream(_test-input-stream)
-1720     # . . push args
-1721     68/push  _test-input-stream/imm32
-1722     # . . call
-1723     e8/call  clear-stream/disp32
-1724     # . . discard args
-1725     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-1726     # . clear-stream(_test-output-stream)
-1727     # . . push args
-1728     68/push  _test-output-stream/imm32
-1729     # . . call
-1730     e8/call  clear-stream/disp32
-1731     # . . discard args
-1732     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-1733     # . clear-stream($_test-output-buffered-file->buffer)
-1734     # . . push args
-1735     68/push  $_test-output-buffered-file->buffer/imm32
-1736     # . . call
-1737     e8/call  clear-stream/disp32
-1738     # . . discard args
-1739     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-1740     # . var labels/edx: (stream byte 8*24)
-1741     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc0/imm32        # subtract from esp
-1742     68/push  0xc0/imm32/size
-1743     68/push  0/imm32/read
-1744     68/push  0/imm32/write
-1745     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
-1746     # . var h/ebx: (handle array byte)
-1747     68/push  0/imm32
-1748     68/push  0/imm32
-1749     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
-1750     # initialize input
-1751     # . write(_test-input-stream, "== code\n")
-1752     # . . push args
-1753     68/push  "== code\n"/imm32
-1754     68/push  _test-input-stream/imm32
-1755     # . . call
-1756     e8/call  write/disp32
-1757     # . . discard args
-1758     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-1759     # . write(_test-input-stream, "ab cd ef gh\n")
-1760     # . . push args
-1761     68/push  "ab cd ef gh\n"/imm32
-1762     68/push  _test-input-stream/imm32
-1763     # . . call
-1764     e8/call  write/disp32
-1765     # . . discard args
-1766     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-1767     # . write(_test-input-stream, "ij x/disp32\n")
-1768     # . . push args
-1769     68/push  "ij x/disp32\n"/imm32
-1770     68/push  _test-input-stream/imm32
-1771     # . . call
-1772     e8/call  write/disp32
-1773     # . . discard args
-1774     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-1775     # . write(_test-input-stream, "== data\n")
-1776     # . . push args
-1777     68/push  "== data\n"/imm32
-1778     68/push  _test-input-stream/imm32
-1779     # . . call
-1780     e8/call  write/disp32
-1781     # . . discard args
-1782     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-1783     # . write(_test-input-stream, "00\n")
-1784     # . . push args
-1785     68/push  "00\n"/imm32
-1786     68/push  _test-input-stream/imm32
-1787     # . . call
-1788     e8/call  write/disp32
-1789     # . . discard args
-1790     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-1791     # . write(_test-input-stream, "34\n")
-1792     # . . push args
-1793     68/push  "34\n"/imm32
-1794     68/push  _test-input-stream/imm32
-1795     # . . call
-1796     e8/call  write/disp32
-1797     # . . discard args
-1798     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-1799     # . stream-add2(labels, "x", 0x11223344)
-1800     68/push  0x11223344/imm32/label-address
-1801     # . . push handle for "x"
-1802     53/push-ebx
-1803     68/push  "x"/imm32
-1804     68/push  Heap/imm32
-1805     e8/call  copy-array/disp32
-1806     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-1807     ff          6/subop/push        1/mod/*+disp8   3/rm32/ebx    .           .             .           .           4/disp8         .                 # push *(ebx+4)
-1808     ff          6/subop/push        0/mod/indirect  3/rm32/ebx    .           .             .           .           .               .                 # push *ebx
-1809     # . . push labels
-1810     52/push-edx
-1811     # . . call
-1812     e8/call  stream-add2/disp32
-1813     # . . discard args
-1814     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
-1815     # component under test
-1816     # . emit-output(_test-input-stream, _test-output-buffered-file, labels)
-1817     # . . push args
-1818     52/push-edx
-1819     68/push  _test-output-buffered-file/imm32
-1820     68/push  _test-input-stream/imm32
-1821     # . . call
-1822     e8/call  emit-output/disp32
-1823     # . . discard args
-1824     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-1825     # checks
-1826     # . flush(_test-output-buffered-file)
-1827     # . . push args
-1828     68/push  _test-output-buffered-file/imm32
-1829     # . . call
-1830     e8/call  flush/disp32
-1831     # . . discard args
-1832     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-1833 +-- 33 lines: #?     # dump output ------------------------------------------------------------------------------------------------------------------------------------------------------
-1866     # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c00", msg)
-1867     # . . push args
-1868     68/push  "F - test-emit-output-non-far-control-flow/0"/imm32
-1869     68/push  "# 0x00007c00"/imm32
-1870     68/push  _test-output-stream/imm32
-1871     # . . call
-1872     e8/call  check-next-stream-line-equal/disp32
-1873     # . . discard args
-1874     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-1875     # . check-next-stream-line-equal(_test-output-stream, "ab cd ef gh ", msg)
-1876     # . . push args
-1877     68/push  "F - test-emit-output-non-far-control-flow/1"/imm32
-1878     68/push  "ab cd ef gh "/imm32
-1879     68/push  _test-output-stream/imm32
-1880     # . . call
-1881     e8/call  check-next-stream-line-equal/disp32
-1882     # . . discard args
-1883     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-1884     # . check-next-stream-line-equal(_test-output-stream, "ij 44 33 22 11 ", msg)
-1885     # . . push args
-1886     68/push  "F - test-emit-output-non-far-control-flow/2"/imm32
-1887     68/push  "ij 44 33 22 11 "/imm32
-1888     68/push  _test-output-stream/imm32
-1889     # . . call
-1890     e8/call  check-next-stream-line-equal/disp32
-1891     # . . discard args
-1892     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-1893     # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c09", msg)
-1894     # . . push args
-1895     68/push  "F - test-emit-output-non-far-control-flow/3"/imm32
-1896     68/push  "# 0x00007c09"/imm32
-1897     68/push  _test-output-stream/imm32
-1898     # . . call
-1899     e8/call  check-next-stream-line-equal/disp32
-1900     # . . discard args
-1901     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-1902     # . check-next-stream-line-equal(_test-output-stream, "00 ", msg)
-1903     # . . push args
-1904     68/push  "F - test-emit-output-non-far-control-flow/3"/imm32
-1905     68/push  "00 "/imm32
-1906     68/push  _test-output-stream/imm32
-1907     # . . call
-1908     e8/call  check-next-stream-line-equal/disp32
-1909     # . . discard args
-1910     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-1911     # . check-next-stream-line-equal(_test-output-stream, "34 ", msg)
-1912     # . . push args
-1913     68/push  "F - test-emit-output-non-far-control-flow/4"/imm32
-1914     68/push  "34 "/imm32
-1915     68/push  _test-output-stream/imm32
-1916     # . . call
-1917     e8/call  check-next-stream-line-equal/disp32
-1918     # . . discard args
-1919     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-1920     # . epilogue
-1921     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
-1922     5d/pop-to-ebp
-1923     c3/return
-1924 
-1925 test-emit-output-with-padding:
-1926     # labels turn into absolute addresses if opcodes are not far jumps or calls
-1927     #
-1928     # input:
-1929     #   in:
-1930     #     == code
-1931     #     ab cd ef gh
-1932     #     == data 0x7c10
-1933     #     34
-1934     #
-1935     # output:
-1936     #   ab cd ef gh
-1937     #   # 0x7c04
-1938     #   00 00 00 00
-1939     #   00 00 00 00 00 00 00 00
-1940     #   # 0x7c10
-1941     #   34
-1942     #
-1943     # . prologue
-1944     55/push-ebp
-1945     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
-1946     # setup
-1947     # . clear-stream(_test-input-stream)
-1948     # . . push args
-1949     68/push  _test-input-stream/imm32
-1950     # . . call
-1951     e8/call  clear-stream/disp32
-1952     # . . discard args
-1953     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-1954     # . clear-stream(_test-output-stream)
-1955     # . . push args
-1956     68/push  _test-output-stream/imm32
-1957     # . . call
-1958     e8/call  clear-stream/disp32
-1959     # . . discard args
-1960     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-1961     # . clear-stream($_test-output-buffered-file->buffer)
-1962     # . . push args
-1963     68/push  $_test-output-buffered-file->buffer/imm32
-1964     # . . call
-1965     e8/call  clear-stream/disp32
-1966     # . . discard args
-1967     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-1968     # . var labels/edx: (stream byte 8*24)
-1969     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc0/imm32        # subtract from esp
-1970     68/push  0xc0/imm32/size
-1971     68/push  0/imm32/read
-1972     68/push  0/imm32/write
-1973     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
-1974     # . var h/ebx: (handle array byte)
-1975     68/push  0/imm32
-1976     68/push  0/imm32
-1977     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
-1978     # initialize input
-1979     # . write(_test-input-stream, "== code\n")
-1980     # . . push args
-1981     68/push  "== code\n"/imm32
-1982     68/push  _test-input-stream/imm32
-1983     # . . call
-1984     e8/call  write/disp32
-1985     # . . discard args
-1986     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-1987     # . write(_test-input-stream, "ab cd ef gh\n")
-1988     # . . push args
-1989     68/push  "ab cd ef gh\n"/imm32
-1990     68/push  _test-input-stream/imm32
-1991     # . . call
-1992     e8/call  write/disp32
-1993     # . . discard args
-1994     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-1995     # . write(_test-input-stream, "== data 0x7c10\n")
-1996     # . . push args
-1997     68/push  "== data 0x7c10\n"/imm32
-1998     68/push  _test-input-stream/imm32
-1999     # . . call
-2000     e8/call  write/disp32
-2001     # . . discard args
-2002     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-2003     # . write(_test-input-stream, "34\n")
-2004     # . . push args
-2005     68/push  "34\n"/imm32
-2006     68/push  _test-input-stream/imm32
-2007     # . . call
-2008     e8/call  write/disp32
-2009     # . . discard args
-2010     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-2011     # component under test
-2012     # . emit-output(_test-input-stream, _test-output-buffered-file, labels)
-2013     # . . push args
-2014     52/push-edx
-2015     68/push  _test-output-buffered-file/imm32
-2016     68/push  _test-input-stream/imm32
-2017     # . . call
-2018     e8/call  emit-output/disp32
-2019     # . . discard args
-2020     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-2021     # checks
-2022     # . flush(_test-output-buffered-file)
-2023     # . . push args
-2024     68/push  _test-output-buffered-file/imm32
-2025     # . . call
-2026     e8/call  flush/disp32
-2027     # . . discard args
-2028     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-2029 +-- 33 lines: #?     # dump output ------------------------------------------------------------------------------------------------------------------------------------------------------
-2062     # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c00", msg)
-2063     # . . push args
-2064     68/push  "F - test-emit-output-with-padding/0"/imm32
-2065     68/push  "# 0x00007c00"/imm32
-2066     68/push  _test-output-stream/imm32
-2067     # . . call
-2068     e8/call  check-next-stream-line-equal/disp32
-2069     # . . discard args
-2070     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-2071     # . check-next-stream-line-equal(_test-output-stream, "ab cd ef gh ", msg)
-2072     # . . push args
-2073     68/push  "F - test-emit-output-with-padding/1"/imm32
-2074     68/push  "ab cd ef gh "/imm32
-2075     68/push  _test-output-stream/imm32
-2076     # . . call
-2077     e8/call  check-next-stream-line-equal/disp32
-2078     # . . discard args
-2079     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-2080     # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c04", msg)
-2081     # . . push args
-2082     68/push  "F - test-emit-output-with-padding/0"/imm32
-2083     68/push  "# 0x00007c04"/imm32
-2084     68/push  _test-output-stream/imm32
-2085     # . . call
-2086     e8/call  check-next-stream-line-equal/disp32
-2087     # . . discard args
-2088     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-2089     # . check-next-stream-line-equal(_test-output-stream, "00 00 00 00 ", msg)
-2090     # . . push args
-2091     68/push  "F - test-emit-output-with-padding/2"/imm32
-2092     68/push  "00 00 00 00 "/imm32
-2093     68/push  _test-output-stream/imm32
-2094     # . . call
-2095     e8/call  check-next-stream-line-equal/disp32
-2096     # . . discard args
-2097     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-2098     # . check-next-stream-line-equal(_test-output-stream, "00 00 00 00 ", msg)
-2099     # . . push args
-2100     68/push  "F - test-emit-output-with-padding/3"/imm32
-2101     68/push  "00 00 00 00 00 00 00 00 "/imm32
-2102     68/push  _test-output-stream/imm32
-2103     # . . call
-2104     e8/call  check-next-stream-line-equal/disp32
-2105     # . . discard args
-2106     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-2107     # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c10", msg)
-2108     # . . push args
-2109     68/push  "F - test-emit-output-with-padding/0"/imm32
-2110     68/push  "# 0x00007c10"/imm32
-2111     68/push  _test-output-stream/imm32
-2112     # . . call
-2113     e8/call  check-next-stream-line-equal/disp32
-2114     # . . discard args
-2115     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-2116     # . check-next-stream-line-equal(_test-output-stream, "34 ", msg)
-2117     # . . push args
-2118     68/push  "F - test-emit-output-with-padding/4"/imm32
-2119     68/push  "34 "/imm32
-2120     68/push  _test-output-stream/imm32
-2121     # . . call
-2122     e8/call  check-next-stream-line-equal/disp32
-2123     # . . discard args
-2124     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-2125     # . epilogue
-2126     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
-2127     5d/pop-to-ebp
-2128     c3/return
-2129 
-2130 test-emit-output-code-label:
-2131     # labels turn into PC-relative addresses if opcodes are far jumps or calls
-2132     #
-2133     # input:
-2134     #   in:
-2135     #     == code
-2136     #     ab cd
-2137     #     ef gh
-2138     #     e8 l1/disp32
-2139     #   labels:
-2140     #     - 'l1': 0x7c10
-2141     #
-2142     # output:
-2143     #   ab cd
-2144     #   ef gh
-2145     #   e8 07 00 00 00  # 0x7c10 - 0x7c09 = 7
-2146     #
-2147     # . prologue
-2148     55/push-ebp
-2149     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
-2150     # setup
-2151     # . clear-stream(_test-input-stream)
-2152     # . . push args
-2153     68/push  _test-input-stream/imm32
-2154     # . . call
-2155     e8/call  clear-stream/disp32
-2156     # . . discard args
-2157     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-2158     # . clear-stream(_test-output-stream)
-2159     # . . push args
-2160     68/push  _test-output-stream/imm32
-2161     # . . call
-2162     e8/call  clear-stream/disp32
-2163     # . . discard args
-2164     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-2165     # . clear-stream($_test-output-buffered-file->buffer)
-2166     # . . push args
-2167     68/push  $_test-output-buffered-file->buffer/imm32
-2168     # . . call
-2169     e8/call  clear-stream/disp32
-2170     # . . discard args
-2171     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-2172     # . var labels/edx: (stream byte 8*24)
-2173     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc0/imm32        # subtract from esp
-2174     68/push  0xc0/imm32/size
-2175     68/push  0/imm32/read
-2176     68/push  0/imm32/write
-2177     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
-2178     # . var h/ebx: (handle array byte)
-2179     68/push  0/imm32
-2180     68/push  0/imm32
-2181     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
-2182     # initialize input
-2183     # . write(_test-input-stream, "== code\n")
-2184     # . . push args
-2185     68/push  "== code\n"/imm32
-2186     68/push  _test-input-stream/imm32
-2187     # . . call
-2188     e8/call  write/disp32
-2189     # . . discard args
-2190     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-2191     # . write(_test-input-stream, "ab cd\n")
-2192     # . . push args
-2193     68/push  "ab cd\n"/imm32
-2194     68/push  _test-input-stream/imm32
-2195     # . . call
-2196     e8/call  write/disp32
-2197     # . . discard args
-2198     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-2199     # . write(_test-input-stream, "ef gh\n")
-2200     # . . push args
-2201     68/push  "ef gh\n"/imm32
-2202     68/push  _test-input-stream/imm32
-2203     # . . call
-2204     e8/call  write/disp32
-2205     # . . discard args
-2206     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-2207     # . write(_test-input-stream, "e8 l1/disp32\n")
-2208     # . . push args
-2209     68/push  "e8 l1/disp32\n"/imm32
-2210     68/push  _test-input-stream/imm32
-2211     # . . call
-2212     e8/call  write/disp32
-2213     # . . discard args
-2214     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-2215     # . stream-add2(labels, "l1", 0x7c10)
-2216     68/push  0x7c10/imm32/label-address
-2217     # . . push handle for "l1"
-2218     53/push-ebx
-2219     68/push  "l1"/imm32
-2220     68/push  Heap/imm32
-2221     e8/call  copy-array/disp32
-2222     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-2223     ff          6/subop/push        1/mod/*+disp8   3/rm32/ebx    .           .             .           .           4/disp8         .                 # push *(ebx+4)
-2224     ff          6/subop/push        0/mod/indirect  3/rm32/ebx    .           .             .           .           .               .                 # push *ebx
-2225     # . . push labels
-2226     52/push-edx
-2227     # . . call
-2228     e8/call  stream-add2/disp32
-2229     # . . discard args
-2230     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
-2231     # component under test
-2232     # . emit-output(_test-input-stream, _test-output-buffered-file, labels)
-2233     # . . push args
-2234     52/push-edx
-2235     68/push  _test-output-buffered-file/imm32
-2236     68/push  _test-input-stream/imm32
-2237     # . . call
-2238     e8/call  emit-output/disp32
-2239     # . . discard args
-2240     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-2241     # checks
-2242     # . flush(_test-output-buffered-file)
-2243     # . . push args
-2244     68/push  _test-output-buffered-file/imm32
-2245     # . . call
-2246     e8/call  flush/disp32
-2247     # . . discard args
-2248     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-2249 +-- 33 lines: #?     # dump output ------------------------------------------------------------------------------------------------------------------------------------------------------
-2282     # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c00", msg)
-2283     # . . push args
-2284     68/push  "F - test-emit-output-code-label/0"/imm32
-2285     68/push  "# 0x00007c00"/imm32
-2286     68/push  _test-output-stream/imm32
-2287     # . . call
-2288     e8/call  check-next-stream-line-equal/disp32
-2289     # . . discard args
-2290     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-2291     # . check-next-stream-line-equal(_test-output-stream, "ab cd ", msg)
-2292     # . . push args
-2293     68/push  "F - test-emit-output-code-label/1"/imm32
-2294     68/push  "ab cd "/imm32
-2295     68/push  _test-output-stream/imm32
-2296     # . . call
-2297     e8/call  check-next-stream-line-equal/disp32
-2298     # . . discard args
-2299     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-2300     # . check-next-stream-line-equal(_test-output-stream, "ef gh ", msg)
-2301     # . . push args
-2302     68/push  "F - test-emit-output-code-label/2"/imm32
-2303     68/push  "ef gh "/imm32
-2304     68/push  _test-output-stream/imm32
-2305     # . . call
-2306     e8/call  check-next-stream-line-equal/disp32
-2307     # . . discard args
-2308     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-2309     # . check-next-stream-line-equal(_test-output-stream, "e8 07 00 00 00 ", msg)
-2310     # . . push args
-2311     68/push  "F - test-emit-output-code-label/3"/imm32
-2312     68/push  "e8 07 00 00 00 "/imm32
-2313     68/push  _test-output-stream/imm32
-2314     # . . call
-2315     e8/call  check-next-stream-line-equal/disp32
-2316     # . . discard args
-2317     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-2318     # . epilogue
-2319     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
-2320     5d/pop-to-ebp
-2321     c3/return
-2322 
-2323 test-emit-output-code-label-absolute:
-2324     # labels can also convert to absolute addresses
-2325     #
-2326     # input:
-2327     #   in:
-2328     #     == code
-2329     #     ab cd
-2330     #     ef gh
-2331     #     ij l1/imm32
-2332     #   labels:
-2333     #     - 'l1': 0x1056
-2334     #
-2335     # output:
-2336     #   ab cd
-2337     #   ef gh
-2338     #   ij 56 10 00 00
-2339     #
-2340     # . prologue
-2341     55/push-ebp
-2342     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
-2343     # setup
-2344     # . clear-stream(_test-input-stream)
-2345     # . . push args
-2346     68/push  _test-input-stream/imm32
-2347     # . . call
-2348     e8/call  clear-stream/disp32
-2349     # . . discard args
-2350     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-2351     # . clear-stream(_test-output-stream)
-2352     # . . push args
-2353     68/push  _test-output-stream/imm32
-2354     # . . call
-2355     e8/call  clear-stream/disp32
-2356     # . . discard args
-2357     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-2358     # . clear-stream($_test-output-buffered-file->buffer)
-2359     # . . push args
-2360     68/push  $_test-output-buffered-file->buffer/imm32
-2361     # . . call
-2362     e8/call  clear-stream/disp32
-2363     # . . discard args
-2364     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-2365     # . var labels/edx: (stream byte 8*24)
-2366     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc0/imm32        # subtract from esp
-2367     68/push  0xc0/imm32/size
-2368     68/push  0/imm32/read
-2369     68/push  0/imm32/write
-2370     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
-2371     # . var h/ebx: (handle array byte)
-2372     68/push  0/imm32
-2373     68/push  0/imm32
-2374     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
-2375     # initialize input
-2376     # . write(_test-input-stream, "== code 0x1000\n")
-2377     # . . push args
-2378     68/push  "== code\n"/imm32
-2379     68/push  _test-input-stream/imm32
-2380     # . . call
-2381     e8/call  write/disp32
-2382     # . . discard args
-2383     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-2384     # . write(_test-input-stream, "ab cd\n")
-2385     # . . push args
-2386     68/push  "ab cd\n"/imm32
-2387     68/push  _test-input-stream/imm32
-2388     # . . call
-2389     e8/call  write/disp32
-2390     # . . discard args
-2391     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-2392     # . write(_test-input-stream, "ef gh\n")
-2393     # . . push args
-2394     68/push  "ef gh\n"/imm32
-2395     68/push  _test-input-stream/imm32
-2396     # . . call
-2397     e8/call  write/disp32
-2398     # . . discard args
-2399     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-2400     # . write(_test-input-stream, "ij l1/imm32\n")
-2401     # . . push args
-2402     68/push  "ij l1/imm32\n"/imm32
-2403     68/push  _test-input-stream/imm32
-2404     # . . call
-2405     e8/call  write/disp32
-2406     # . . discard args
-2407     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-2408     # . stream-add2(labels, "l1", 0x1056)
-2409     68/push  0x1056/imm32/label-address
-2410     # . . push handle for "l1"
-2411     53/push-ebx
-2412     68/push  "l1"/imm32
-2413     68/push  Heap/imm32
-2414     e8/call  copy-array/disp32
-2415     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-2416     ff          6/subop/push        1/mod/*+disp8   3/rm32/ebx    .           .             .           .           4/disp8         .                 # push *(ebx+4)
-2417     ff          6/subop/push        0/mod/indirect  3/rm32/ebx    .           .             .           .           .               .                 # push *ebx
-2418     # . . push labels
-2419     52/push-edx
-2420     # . . call
-2421     e8/call  stream-add2/disp32
-2422     # . . discard args
-2423     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
-2424     # component under test
-2425     # . emit-output(_test-input-stream, _test-output-buffered-file, labels)
-2426     # . . push args
-2427     52/push-edx
-2428     68/push  _test-output-buffered-file/imm32
-2429     68/push  _test-input-stream/imm32
-2430     # . . call
-2431     e8/call  emit-output/disp32
-2432     # . . discard args
-2433     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-2434     # checks
-2435     # . flush(_test-output-buffered-file)
-2436     # . . push args
-2437     68/push  _test-output-buffered-file/imm32
-2438     # . . call
-2439     e8/call  flush/disp32
-2440     # . . discard args
-2441     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-2442 +-- 33 lines: #?     # dump output ------------------------------------------------------------------------------------------------------------------------------------------------------
-2475     # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c00", msg)
-2476     # . . push args
-2477     68/push  "F - test-emit-output-code-label-absolute/0"/imm32
-2478     68/push  "# 0x00007c00"/imm32
-2479     68/push  _test-output-stream/imm32
-2480     # . . call
-2481     e8/call  check-next-stream-line-equal/disp32
-2482     # . . discard args
-2483     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-2484     # . check-next-stream-line-equal(_test-output-stream, "ab cd ", msg)
-2485     # . . push args
-2486     68/push  "F - test-emit-output-code-label-absolute/1"/imm32
-2487     68/push  "ab cd "/imm32
-2488     68/push  _test-output-stream/imm32
-2489     # . . call
-2490     e8/call  check-next-stream-line-equal/disp32
-2491     # . . discard args
-2492     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-2493     # . check-next-stream-line-equal(_test-output-stream, "ef gh ", msg)
-2494     # . . push args
-2495     68/push  "F - test-emit-output-code-label-absolute/2"/imm32
-2496     68/push  "ef gh "/imm32
-2497     68/push  _test-output-stream/imm32
-2498     # . . call
-2499     e8/call  check-next-stream-line-equal/disp32
-2500     # . . discard args
-2501     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-2502     # . check-next-stream-line-equal(_test-output-stream, "ij f9 ff ff ff ", msg)
-2503     # . . push args
-2504     68/push  "F - test-emit-output-code-label-absolute/3"/imm32
-2505     68/push  "ij 56 10 00 00 "/imm32
-2506     68/push  _test-output-stream/imm32
-2507     # . . call
-2508     e8/call  check-next-stream-line-equal/disp32
-2509     # . . discard args
-2510     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-2511     # . epilogue
-2512     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
-2513     5d/pop-to-ebp
-2514     c3/return
-2515 
-2516 # reads line to make some checks
-2517 # don't assume the read state of line after calling this function
-2518 far-jump-or-call?:  # line: (addr stream byte) -> result/edi: boolean
-2519     # . prologue
-2520     55/push-ebp
-2521     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
-2522     # . save registers
-2523     50/push-eax
-2524     51/push-ecx
-2525     52/push-edx
-2526     53/push-ebx
-2527     # ecx = line
-2528     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
-2529     # var word-slice/edx: slice
-2530     68/push  0/imm32/end
-2531     68/push  0/imm32/start
-2532     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
-2533     # var datum-slice/ebx: slice
-2534     68/push  0/imm32/end
-2535     68/push  0/imm32/start
-2536     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
-2537     # result = false
-2538     bf/copy-to-edi  0/imm32/false
-2539 $far-jump-or-call?:check-first-word:
-2540     # next-word(line, word-slice)
-2541     # . . push args
-2542     52/push-edx
-2543     51/push-ecx
-2544     # . . call
-2545     e8/call  next-word/disp32
-2546     # . . discard args
-2547     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-2548     # if (slice-empty?(word-slice)) return false
-2549     # . eax = slice-empty?(word-slice)
-2550     # . . push args
-2551     52/push-edx
-2552     # . . call
-2553     e8/call  slice-empty?/disp32
-2554     # . . discard args
-2555     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-2556     # . if (eax != 0) return
-2557     3d/compare-eax-and  0/imm32/false
-2558     0f 85/jump-if-!=  $far-jump-or-call?:end/disp32
-2559     # datum = next-token-from-slice(word-slice->start, word-slice->end, "/")
-2560     # . . push args
-2561     53/push-ebx
-2562     68/push  0x2f/imm32/slash
-2563     ff          6/subop/push        1/mod/*+disp8   2/rm32/edx    .           .             .           .           4/disp8         .                 # push *(edx+4)
-2564     ff          6/subop/push        0/mod/indirect  2/rm32/edx    .           .             .           .           .               .                 # push *edx
-2565     # . . call
-2566     e8/call  next-token-from-slice/disp32
-2567     # . . discard args
-2568     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
-2569     # if (datum-slice == "e8") return true
-2570     # . eax = slice-equal?(datum-slice, "e8")
-2571     # . . push args
-2572     68/push  "e8"/imm32
-2573     53/push-ebx
-2574     # . . call
-2575     e8/call  slice-equal?/disp32
-2576     # . . discard args
-2577     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-2578     # . if (eax != false) return true
-2579     3d/compare-eax-and  0/imm32/false
-2580     75/jump-if-!=  $far-jump-or-call?:return-true/disp8
-2581     # if (datum-slice == "e9") return true
-2582     # . eax = slice-equal?(datum-slice, "e9")
-2583     # . . push args
-2584     68/push  "e9"/imm32
-2585     53/push-ebx
-2586     # . . call
-2587     e8/call  slice-equal?/disp32
-2588     # . . discard args
-2589     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-2590     # . if (eax != false) return true
-2591     3d/compare-eax-and  0/imm32/false
-2592     75/jump-if-!=  $far-jump-or-call?:return-true/disp8
-2593     # if (datum-slice != "0f") return false
-2594     # . eax = slice-equal?(datum-slice, "0f")
-2595     # . . push args
-2596     68/push  "0f"/imm32
-2597     53/push-ebx
-2598     # . . call
-2599     e8/call  slice-equal?/disp32
-2600     # . . discard args
-2601     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-2602     # . if (eax == false) return
-2603     3d/compare-eax-and  0/imm32/false
-2604     74/jump-if-=  $far-jump-or-call?:end/disp8
-2605 $far-jump-or-call?:check-second-word:
-2606     # next-word(line, word-slice)
-2607     # . . push args
-2608     52/push-edx
-2609     51/push-ecx
-2610     # . . call
-2611     e8/call  next-word/disp32
-2612     # . . discard args
-2613     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-2614     # if (slice-empty?(word-slice)) return false
-2615     # . eax = slice-empty?(word-slice)
-2616     # . . push args
-2617     52/push-edx
-2618     # . . call
-2619     e8/call  slice-empty?/disp32
-2620     # . . discard args
-2621     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-2622     # . if (eax != 0) return
-2623     3d/compare-eax-and  0/imm32/false
-2624     75/jump-if-!=  $far-jump-or-call?:end/disp8
-2625     # if datum of word-slice does not start with "8", return false
-2626     # . start/eax = word-slice->start
-2627     8b/copy                         0/mod/indirect  2/rm32/edx    .           .             .           0/r32/eax   .               .                 # copy *edx to eax
-2628     # . c/eax = *start
-2629     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           0/r32/AL    .               .                 # copy byte at *eax to AL
-2630     81          4/subop/and         3/mod/direct    0/rm32/eax    .           .             .           .           .               0xff/imm32        # bitwise and of eax
-2631     # . if (eax != '8') return
-2632     3d/compare-eax-and  0x38/imm32/8
-2633     75/jump-if-!=  $far-jump-or-call?:end/disp8
-2634     # otherwise return true
-2635 $far-jump-or-call?:return-true:
-2636     bf/copy-to-edi  1/imm32/true
-2637 $far-jump-or-call?:end:
-2638     # . reclaim locals
-2639     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
-2640     # . restore registers
-2641     5b/pop-to-ebx
-2642     5a/pop-to-edx
-2643     59/pop-to-ecx
-2644     58/pop-to-eax
-2645     # . epilogue
-2646     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
-2647     5d/pop-to-ebp
-2648     c3/return
-2649 
-2650 # - some helpers for tests
-2651 
-2652 stream-add2:  # in: (addr stream byte), key: handle, val: int
-2653     # . prologue
-2654     55/push-ebp
-2655     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
-2656     # . save registers
-2657     50/push-eax
-2658     51/push-ecx
-2659     52/push-edx
-2660     56/push-esi
-2661     # esi = in
-2662     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
-2663     # curr/eax = &in->data[in->write]
-2664     # . eax = in->write
-2665     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
-2666     # . eax = esi+eax+12
-2667     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/esi  0/index/eax   .           0/r32/eax   0xc/disp8       .                 # copy esi+eax+12 to eax
-2668     # max/edx = &in->data[in->size]
-2669     # . edx = in->size
-2670     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           2/r32/edx   8/disp8         .                 # copy *(esi+8) to edx
-2671     # . edx = esi+edx+12
-2672     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/esi  2/index/edx   .           2/r32/edx   0xc/disp8       .                 # copy esi+edx+12 to edx
-2673     # if (curr >= max) abort
-2674     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # compare eax with edx
-2675     73/jump-if-addr>=  $stream-add2:abort/disp8
-2676     # *curr = key->alloc-id
-2677     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   0xc/disp8       .                 # copy *(ebp+12) to ecx
-2678     89/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy ecx to *eax
-2679     # curr += 4
-2680     05/add-to-eax  4/imm32
-2681     # if (curr >= max) abort
-2682     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # compare eax with edx
-2683     73/jump-if-addr>=  $stream-add2:abort/disp8
-2684     # *curr = key->payload
-2685     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   0x10/disp8      .                 # copy *(ebp+16) to ecx
-2686     89/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy ecx to *eax
-2687     # curr += 4
-2688     05/add-to-eax  4/imm32
-2689     # if (curr >= max) abort
-2690     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # compare eax with edx
-2691     73/jump-if-addr>=  $stream-add2:abort/disp8
-2692     # *curr = val
-2693     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   0x14/disp8      .                 # copy *(ebp+20) to ecx
-2694     89/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy ecx to *eax
-2695     # in->write += 0xc
-2696     81          0/subop/add         0/mod/indirect  6/rm32/esi    .           .             .           .           .               0xc/imm32         # add to *esi
-2697 $stream-add2:end:
-2698     # . restore registers
-2699     5e/pop-to-esi
-2700     5a/pop-to-edx
-2701     59/pop-to-ecx
-2702     58/pop-to-eax
-2703     # . epilogue
-2704     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
-2705     5d/pop-to-ebp
-2706     c3/return
-2707 
-2708 $stream-add2:abort:
-2709     # . _write(2/stderr, error)
-2710     # . . push args
-2711     68/push  "overflow in stream-add2\n"/imm32
-2712     68/push  2/imm32/stderr
-2713     # . . call
-2714     e8/call  _write/disp32
-2715     # . . discard args
-2716     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-2717     # . syscall(exit, 1)
-2718     bb/copy-to-ebx  1/imm32
-2719     e8/call  syscall_exit/disp32
-2720     # never gets here
-2721 
-2722 # some variants of 'trace' that take multiple arguments in different combinations of types:
-2723 #   n: int
-2724 #   c: character [4-bytes, will eventually be UTF-8]
-2725 #   s: (addr array byte)
-2726 #   l: (addr slice)
-2727 # one gotcha: 's5' must not be empty
-2728 
-2729 trace-slsns:  # s1: (addr array byte), l2: (addr slice), s3: (addr array byte), n4: int, s5: (addr array byte)
-2730     # . prologue
-2731     55/push-ebp
-2732     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
-2733     # write(*Trace-stream, s1)
-2734     # . . push args
-2735     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
-2736     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
-2737     # . . call
-2738     e8/call  write/disp32
-2739     # . . discard args
-2740     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-2741     # write-slice(*Trace-stream, l2)
-2742     # . . push args
-2743     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
-2744     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
-2745     # . . call
-2746     e8/call  write-slice/disp32
-2747     # . . discard args
-2748     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-2749     # write(*Trace-stream, s3)
-2750     # . . push args
-2751     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
-2752     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
-2753     # . . call
-2754     e8/call  write/disp32
-2755     # . . discard args
-2756     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-2757     # write-int32-hex(*Trace-stream, n4)
-2758     # . . push args
-2759     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x14/disp8      .                 # push *(ebp+20)
-2760     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
-2761     # . . call
-2762     e8/call  write-int32-hex/disp32
-2763     # . . discard args
-2764     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-2765     # trace(s5)  # implicitly adds a newline and finalizes the trace line
-2766     # . . push args
-2767     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x18/disp8      .                 # push *(ebp+24)
-2768     # . . call
-2769     e8/call  trace/disp32
-2770     # . . discard args
-2771     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-2772 $trace-slsns:end:
-2773     # . epilogue
-2774     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
-2775     5d/pop-to-ebp
-2776     c3/return
-2777 
-2778 test-trace-slsns:
-2779     # . prologue
-2780     55/push-ebp
-2781     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
-2782     # setup
-2783     # . *Trace-stream->write = 0
-2784     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   Trace-stream/disp32               # copy *Trace-stream to eax
-2785     c7          0/subop/copy        0/mod/direct    0/rm32/eax    .           .             .           .           .               0/imm32           # clear *eax
-2786     # (eax..ecx) = "b"
-2787     b8/copy-to-eax  "b"/imm32
-2788     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
-2789     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
-2790     05/add-to-eax  4/imm32
-2791     # var b/ebx: slice = {eax, ecx}
-2792     51/push-ecx
-2793     50/push-eax
-2794     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
-2795     # trace-slsls("A" b "c " 3 " e")
-2796     # . . push args
-2797     68/push  " e"/imm32
-2798     68/push  3/imm32
-2799     68/push  "c "/imm32
-2800     53/push-ebx
-2801     68/push  "A"/imm32
-2802     # . . call
-2803     e8/call  trace-slsns/disp32
-2804     # . . discard args
-2805     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
-2806 +-- 26 lines: #?     # dump *Trace-stream -----------------------------------------------------------------------------------------------------------------------------------------------
-2832     # check-trace-contains("Abc 0x00000003 e")
-2833     # . . push args
-2834     68/push  "F - test-trace-slsls"/imm32
-2835     68/push  "Abc 0x00000003 e"/imm32
-2836     # . . call
-2837     e8/call  check-trace-contains/disp32
-2838     # . . discard args
-2839     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-2840     # . epilogue
-2841     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
-2842     5d/pop-to-ebp
-2843     c3/return
-2844 
-2845 num-bytes:  # line: (addr stream byte) -> eax: int
-2846     # pseudocode:
-2847     #   result = 0
-2848     #   while true
-2849     #     var word-slice = next-word(line)
-2850     #     if slice-empty?(word-slice)             # end of line
-2851     #       break
-2852     #     if slice-starts-with?(word-slice, "#")  # comment
-2853     #       break
-2854     #     if label?(word-slice)                # no need for label declarations anymore
-2855     #       break
-2856     #     if slice-equal?(word-slice, "==")
-2857     #       break                                 # no need for segment header lines
-2858     #     result += compute-width(word-slice)
-2859     #   return result
-2860     #
-2861     # . prologue
-2862     55/push-ebp
-2863     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
-2864     # . save registers
-2865     51/push-ecx
-2866     52/push-edx
-2867     53/push-ebx
-2868     # var result/eax = 0
-2869     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
-2870     # var word-slice/ecx: slice
-2871     68/push  0/imm32/end
-2872     68/push  0/imm32/start
-2873     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
-2874 +-- 26 lines: #?     # dump line --------------------------------------------------------------------------------------------------------------------------------------------------------
-2900     # . rewind-stream(line)
-2901     # . . push args
-2902     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
-2903     # . . call
-2904     e8/call  rewind-stream/disp32
-2905     # . . discard args
-2906     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-2907 $num-bytes:loop:
-2908     # next-word(line, word-slice)
-2909     # . . push args
-2910     51/push-ecx
-2911     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
-2912     # . . call
-2913     e8/call  next-word/disp32
-2914     # . . discard args
-2915     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-2916 +-- 40 lines: #?     # dump word-slice --------------------------------------------------------------------------------------------------------------------------------------------------
-2956 $num-bytes:check0:
-2957     # if (slice-empty?(word-slice)) break
-2958     # . save result
-2959     50/push-eax
-2960     # . eax = slice-empty?(word-slice)
-2961     # . . push args
-2962     51/push-ecx
-2963     # . . call
-2964     e8/call  slice-empty?/disp32
-2965     # . . discard args
-2966     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-2967     # . if (eax != false) break
-2968     3d/compare-eax-and  0/imm32/false
-2969     # . restore result now that ZF is set
-2970     58/pop-to-eax
-2971     75/jump-if-!=  $num-bytes:end/disp8
-2972 $num-bytes:check-for-comment:
-2973     # if (slice-starts-with?(word-slice, "#")) break
-2974     # . start/edx = word-slice->start
-2975     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # copy *ecx to edx
-2976     # . c/ebx = *start
-2977     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
-2978     8a/copy-byte                    0/mod/indirect  2/rm32/edx    .           .             .           3/r32/BL    .               .                 # copy byte at *edx to BL
-2979     # . if (ebx == '#') break
-2980     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x23/imm32/hash   # compare ebx
-2981     74/jump-if-=  $num-bytes:end/disp8
-2982 $num-bytes:check-for-label:
-2983     # if (slice-ends-with?(word-slice, ":")) break
-2984     # . end/edx = word-slice->end
-2985     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           2/r32/edx   4/disp8         .                 # copy *(ecx+4) to edx
-2986     # . c/ebx = *(end-1)
-2987     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
-2988     8a/copy-byte                    1/mod/*+disp8   2/rm32/edx    .           .             .           3/r32/BL    -1/disp8        .                 # copy byte at *ecx to BL
-2989     # . if (ebx == ':') break
-2990     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x3a/imm32/colon  # compare ebx
-2991     74/jump-if-=  $num-bytes:end/disp8
-2992 $num-bytes:check-for-segment-header:
-2993     # if (slice-equal?(word-slice, "==")) break
-2994     # . push result
-2995     50/push-eax
-2996     # . eax = slice-equal?(word-slice, "==")
-2997     # . . push args
-2998     68/push  "=="/imm32
-2999     51/push-ecx
-3000     # . . call
-3001     e8/call  slice-equal?/disp32
-3002     # . . discard args
-3003     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-3004     # . if (eax != false) break
-3005     3d/compare-eax-and  0/imm32/false
-3006     # . restore result now that ZF is set
-3007     58/pop-to-eax
-3008     75/jump-if-!=  $num-bytes:end/disp8
-3009 $num-bytes:loop-body:
-3010     # result += compute-width-of-slice(word-slice)
-3011     # . copy result to edx
-3012     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           0/r32/eax   .               .                 # copy eax to edx
-3013     # . eax = compute-width-of-slice(word-slice)
-3014     # . . push args
-3015     51/push-ecx
-3016     # . . call
-3017     e8/call compute-width-of-slice/disp32
-3018     # . . discard args
-3019     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-3020     # . eax += result
-3021     01/add                          3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # add edx to eax
-3022     e9/jump  $num-bytes:loop/disp32
-3023 $num-bytes:end:
-3024     # . rewind-stream(line)
-3025     # . . push args
-3026     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
-3027     # . . call
-3028     e8/call  rewind-stream/disp32
-3029     # . . discard args
-3030     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-3031     # . reclaim locals
-3032     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-3033     # . restore registers
-3034     5b/pop-to-ebx
-3035     5a/pop-to-edx
-3036     59/pop-to-ecx
-3037     # . epilogue
-3038     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
-3039     5d/pop-to-ebp
-3040     c3/return
-3041 
-3042 test-num-bytes-handles-empty-string:
-3043     # if a line starts with '#', return 0
-3044     # . prologue
-3045     55/push-ebp
-3046     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
-3047     # setup
-3048     # . clear-stream(_test-input-stream)
-3049     # . . push args
-3050     68/push  _test-input-stream/imm32
-3051     # . . call
-3052     e8/call  clear-stream/disp32
-3053     # . . discard args
-3054     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-3055     # . clear-stream(_test-output-stream)
-3056     # . . push args
-3057     68/push  _test-output-stream/imm32
-3058     # . . call
-3059     e8/call  clear-stream/disp32
-3060     # . . discard args
-3061     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-3062     # no contents in input
-3063     # eax = num-bytes(_test-input-stream)
-3064     # . . push args
-3065     68/push  _test-input-stream/imm32
-3066     # . . call
-3067     e8/call  num-bytes/disp32
-3068     # . . discard args
-3069     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-3070     # check-ints-equal(eax, 0, msg)
-3071     # . . push args
-3072     68/push  "F - test-num-bytes-handles-empty-string"/imm32
-3073     68/push  0/imm32/true
-3074     50/push-eax
-3075     # . . call
-3076     e8/call  check-ints-equal/disp32
-3077     # . . discard args
-3078     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-3079     # . epilogue
-3080     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
-3081     5d/pop-to-ebp
-3082     c3/return
-3083 
-3084 test-num-bytes-ignores-comments:
-3085     # if a line starts with '#', return 0
-3086     # . prologue
-3087     55/push-ebp
-3088     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
-3089     # setup
-3090     # . clear-stream(_test-input-stream)
-3091     # . . push args
-3092     68/push  _test-input-stream/imm32
-3093     # . . call
-3094     e8/call  clear-stream/disp32
-3095     # . . discard args
-3096     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-3097     # . clear-stream(_test-output-stream)
-3098     # . . push args
-3099     68/push  _test-output-stream/imm32
-3100     # . . call
-3101     e8/call  clear-stream/disp32
-3102     # . . discard args
-3103     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-3104     # initialize input
-3105     # . write(_test-input-stream, "# abcd")
-3106     # . . push args
-3107     68/push  "# abcd"/imm32
-3108     68/push  _test-input-stream/imm32
-3109     # . . call
-3110     e8/call  write/disp32
-3111     # . . discard args
-3112     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-3113     # eax = num-bytes(_test-input-stream)
-3114     # . . push args
-3115     68/push  _test-input-stream/imm32
-3116     # . . call
-3117     e8/call  num-bytes/disp32
-3118     # . . discard args
-3119     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-3120     # check-ints-equal(eax, 0, msg)
-3121     # . . push args
-3122     68/push  "F - test-num-bytes-ignores-comments"/imm32
-3123     68/push  0/imm32/true
-3124     50/push-eax
-3125     # . . call
-3126     e8/call  check-ints-equal/disp32
-3127     # . . discard args
-3128     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-3129     # . epilogue
-3130     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
-3131     5d/pop-to-ebp
-3132     c3/return
-3133 
-3134 test-num-bytes-ignores-labels:
-3135     # if the first word ends with ':', return 0
-3136     # . prologue
-3137     55/push-ebp
-3138     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
-3139     # setup
-3140     # . clear-stream(_test-input-stream)
-3141     # . . push args
-3142     68/push  _test-input-stream/imm32
-3143     # . . call
-3144     e8/call  clear-stream/disp32
-3145     # . . discard args
-3146     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-3147     # . clear-stream(_test-output-stream)
-3148     # . . push args
-3149     68/push  _test-output-stream/imm32
-3150     # . . call
-3151     e8/call  clear-stream/disp32
-3152     # . . discard args
-3153     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-3154     # initialize input
-3155     # . write(_test-input-stream, "ab: # cd")
-3156     # . . push args
-3157     68/push  "ab: # cd"/imm32
-3158     68/push  _test-input-stream/imm32
-3159     # . . call
-3160     e8/call  write/disp32
-3161     # . . discard args
-3162     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-3163     # eax = num-bytes(_test-input-stream)
-3164     # . . push args
-3165     68/push  _test-input-stream/imm32
-3166     # . . call
-3167     e8/call  num-bytes/disp32
-3168     # . . discard args
-3169     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-3170     # check-ints-equal(eax, 0, msg)
-3171     # . . push args
-3172     68/push  "F - test-num-bytes-ignores-labels"/imm32
-3173     68/push  0/imm32/true
-3174     50/push-eax
-3175     # . . call
-3176     e8/call  check-ints-equal/disp32
-3177     # . . discard args
-3178     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-3179     # . epilogue
-3180     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
-3181     5d/pop-to-ebp
-3182     c3/return
-3183 
-3184 test-num-bytes-ignores-segment-headers:
-3185     # if the first word is '==', return 0
-3186     # . prologue
-3187     55/push-ebp
-3188     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
-3189     # setup
-3190     # . clear-stream(_test-input-stream)
-3191     # . . push args
-3192     68/push  _test-input-stream/imm32
-3193     # . . call
-3194     e8/call  clear-stream/disp32
-3195     # . . discard args
-3196     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-3197     # . clear-stream(_test-output-stream)
-3198     # . . push args
-3199     68/push  _test-output-stream/imm32
-3200     # . . call
-3201     e8/call  clear-stream/disp32
-3202     # . . discard args
-3203     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-3204     # initialize input
-3205     # . write(_test-input-stream, "== ab cd")
-3206     # . . push args
-3207     68/push  "== ab cd"/imm32
-3208     68/push  _test-input-stream/imm32
-3209     # . . call
-3210     e8/call  write/disp32
-3211     # . . discard args
-3212     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-3213     # eax = num-bytes(_test-input-stream)
-3214     # . . push args
-3215     68/push  _test-input-stream/imm32
-3216     # . . call
-3217     e8/call  num-bytes/disp32
-3218     # . . discard args
-3219     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-3220     # check-ints-equal(eax, 0, msg)
-3221     # . . push args
-3222     68/push  "F - test-num-bytes-ignores-segment-headers"/imm32
-3223     68/push  0/imm32/true
-3224     50/push-eax
-3225     # . . call
-3226     e8/call  check-ints-equal/disp32
-3227     # . . discard args
-3228     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-3229     # . epilogue
-3230     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
-3231     5d/pop-to-ebp
-3232     c3/return
-3233 
-3234 test-num-bytes-counts-words-by-default:
-3235     # without metadata, count words
-3236     # . prologue
-3237     55/push-ebp
-3238     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
-3239     # setup
-3240     # . clear-stream(_test-input-stream)
-3241     # . . push args
-3242     68/push  _test-input-stream/imm32
-3243     # . . call
-3244     e8/call  clear-stream/disp32
-3245     # . . discard args
-3246     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-3247     # . clear-stream(_test-output-stream)
-3248     # . . push args
-3249     68/push  _test-output-stream/imm32
-3250     # . . call
-3251     e8/call  clear-stream/disp32
-3252     # . . discard args
-3253     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-3254     # initialize input
-3255     # . write(_test-input-stream, "ab cd ef")
-3256     # . . push args
-3257     68/push  "ab cd ef"/imm32
-3258     68/push  _test-input-stream/imm32
-3259     # . . call
-3260     e8/call  write/disp32
-3261     # . . discard args
-3262     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-3263     # eax = num-bytes(_test-input-stream)
-3264     # . . push args
-3265     68/push  _test-input-stream/imm32
-3266     # . . call
-3267     e8/call  num-bytes/disp32
-3268     # . . discard args
-3269     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-3270     # check-ints-equal(eax, 3, msg)
-3271     # . . push args
-3272     68/push  "F - test-num-bytes-counts-words-by-default"/imm32
-3273     68/push  3/imm32/true
-3274     50/push-eax
-3275     # . . call
-3276     e8/call  check-ints-equal/disp32
-3277     # . . discard args
-3278     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-3279     # . epilogue
-3280     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
-3281     5d/pop-to-ebp
-3282     c3/return
-3283 
-3284 test-num-bytes-ignores-trailing-comment:
-3285     # trailing comments appropriately ignored
-3286     # . prologue
-3287     55/push-ebp
-3288     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
-3289     # setup
-3290     # . clear-stream(_test-input-stream)
-3291     # . . push args
-3292     68/push  _test-input-stream/imm32
-3293     # . . call
-3294     e8/call  clear-stream/disp32
-3295     # . . discard args
-3296     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-3297     # . clear-stream(_test-output-stream)
-3298     # . . push args
-3299     68/push  _test-output-stream/imm32
-3300     # . . call
-3301     e8/call  clear-stream/disp32
-3302     # . . discard args
-3303     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-3304     # initialize input
-3305     # . write(_test-input-stream, "ab cd # ef")
-3306     # . . push args
-3307     68/push  "ab cd # ef"/imm32
-3308     68/push  _test-input-stream/imm32
-3309     # . . call
-3310     e8/call  write/disp32
-3311     # . . discard args
-3312     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-3313     # eax = num-bytes(_test-input-stream)
-3314     # . . push args
-3315     68/push  _test-input-stream/imm32
-3316     # . . call
-3317     e8/call  num-bytes/disp32
-3318     # . . discard args
-3319     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-3320     # check-ints-equal(eax, 2, msg)
-3321     # . . push args
-3322     68/push  "F - test-num-bytes-ignores-trailing-comment"/imm32
-3323     68/push  2/imm32/true
-3324     50/push-eax
-3325     # . . call
-3326     e8/call  check-ints-equal/disp32
-3327     # . . discard args
-3328     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-3329     # . epilogue
-3330     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
-3331     5d/pop-to-ebp
-3332     c3/return
-3333 
-3334 test-num-bytes-handles-imm32:
-3335     # if a word has the /imm32 metadata, count it as 4 bytes
-3336     # . prologue
-3337     55/push-ebp
-3338     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
-3339     # setup
-3340     # . clear-stream(_test-input-stream)
-3341     # . . push args
-3342     68/push  _test-input-stream/imm32
-3343     # . . call
-3344     e8/call  clear-stream/disp32
-3345     # . . discard args
-3346     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-3347     # . clear-stream(_test-output-stream)
-3348     # . . push args
-3349     68/push  _test-output-stream/imm32
-3350     # . . call
-3351     e8/call  clear-stream/disp32
-3352     # . . discard args
-3353     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-3354     # initialize input
-3355     # . write(_test-input-stream, "ab cd/imm32 ef")
-3356     # . . push args
-3357     68/push  "ab cd/imm32 ef"/imm32
-3358     68/push  _test-input-stream/imm32
-3359     # . . call
-3360     e8/call  write/disp32
-3361     # . . discard args
-3362     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-3363     # eax = num-bytes(_test-input-stream)
-3364     # . . push args
-3365     68/push  _test-input-stream/imm32
-3366     # . . call
-3367     e8/call  num-bytes/disp32
-3368     # . . discard args
-3369     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-3370     # check-ints-equal(eax, 6, msg)
-3371     # . . push args
-3372     68/push  "F - test-num-bytes-handles-imm32"/imm32
-3373     68/push  6/imm32/true
-3374     50/push-eax
-3375     # . . call
-3376     e8/call  check-ints-equal/disp32
-3377     # . . discard args
-3378     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-3379     # . epilogue
-3380     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
-3381     5d/pop-to-ebp
-3382     c3/return
-3383 
-3384 # . . vim:nowrap:textwidth=0
+ 871     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32          # add to esp
+ 872 +-- 26 lines: #?     # dump *Trace-stream -----------------------------------------------------------------------------------------------------------------------------------------------
+ 898     # . check-trace-contains("label 'x' is at address 0x00007c06.", msg)
+ 899     # . . push args
+ 900     68/push  "F - test-compute-addresses"/imm32
+ 901     68/push  "label 'x' is at address 0x00007c06."/imm32
+ 902     # . . call
+ 903     e8/call  check-trace-contains/disp32
+ 904     # . . discard args
+ 905     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 906     # . check-ints-equal(labels->write, 0xc, msg)
+ 907     # . . push args
+ 908     68/push  "F - test-compute-addresses-maintains-labels-write-index"/imm32
+ 909     68/push  0xc/imm32/1-entry
+ 910     ff          6/subop/push        0/mod/indirect  2/rm32/edx    .           .             .           .           .               .                 # push *edx
+ 911     # . . call
+ 912     e8/call  check-ints-equal/disp32
+ 913     # . . discard args
+ 914     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+ 915     # . reclaim locals
+ 916     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x24/imm32        # add to esp
+ 917     # . epilogue
+ 918     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
+ 919     5d/pop-to-ebp
+ 920     c3/return
+ 921 
+ 922 emit-labels:  # out: (addr buffered-file), labels: (addr stream {(handle array byte), address})
+ 923     # pseudocode:
+ 924     #   curr = table->data
+ 925     #   max = &table->data[table->write]
+ 926     #   while curr < max
+ 927     #     var label: (addr array byte) = lookup(*curr)
+ 928     #     curr += 8
+ 929     #     write-buffered(out, label)
+ 930     #     write-buffered(out, ' ')
+ 931     #     write-buffered(out, *curr)
+ 932     #     curr += 4
+ 933     #     write(out, '\n')
+ 934     #   return 0
+ 935     #
+ 936     # . prologue
+ 937     55/push-ebp
+ 938     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
+ 939     # . save registers
+ 940     50/push-eax
+ 941     51/push-ecx
+ 942     52/push-edx
+ 943     56/push-esi
+ 944     # esi = table
+ 945     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   0xc/disp8       .                 # copy *(ebp+12) to esi
+ 946     # var curr/ecx: (addr handle array byte) = table->data
+ 947     8d/copy-address                 1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   0xc/disp8       .                 # copy esi+12 to ecx
+ 948     # var max/edx: (addr byte) = &table->data[table->write]
+ 949     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           2/r32/edx   .               .                 # copy *esi to edx
+ 950     8d/copy-address                 0/mod/indirect  4/rm32/sib    1/base/ecx  2/index/edx   .           2/r32/edx   .               .                 # copy ecx+edx to edx
+ 951 $emit-labels:loop:
+ 952     # if (curr >= max) return null
+ 953     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
+ 954     0f 83/jump-if-addr>=  $emit-labels:end/disp32
+ 955     # var label/eax: (addr array byte) = lookup(*curr)
+ 956     # . . push args
+ 957     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         .                 # push *(ecx+4)
+ 958     ff          6/subop/push        0/mod/indirect  1/rm32/ecx    .           .             .           .           .               .                 # push *ecx
+ 959     # . . call
+ 960     e8/call  lookup/disp32
+ 961     # . . discard args
+ 962     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 963     # curr += 8
+ 964     81          0/subop/add         3/mod/direct    1/rm32/ecx    .           .             .           .           .               8/imm32           # add to ecx
+ 965     # write-buffered(out, label)
+ 966     # . . push args
+ 967     50/push-eax
+ 968     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
+ 969     # . . call
+ 970     e8/call  write-buffered/disp32
+ 971     # . . discard args
+ 972     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 973     # write-buffered(out, ' ')
+ 974     # . . push args
+ 975     68/push  Space/imm32
+ 976     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
+ 977     # . . call
+ 978     e8/call  write-buffered/disp32
+ 979     # . . discard args
+ 980     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 981     # . flush(Stderr)
+ 982     # . . push args
+ 983     68/push  Stderr/imm32
+ 984     # . . call
+ 985     e8/call  flush/disp32
+ 986     # . . discard args
+ 987     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+ 988     # write-int32-hex-buffered(out, *curr)
+ 989     # . . push args
+ 990     ff          6/subop/push        0/mod/indirect  1/rm32/ecx    .           .             .           .           .               .                 # push *ecx
+ 991     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
+ 992     # . . call
+ 993     e8/call  write-int32-hex-buffered/disp32
+ 994     # . . discard args
+ 995     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 996     # curr += 4
+ 997     81          0/subop/add         3/mod/direct    1/rm32/ecx    .           .             .           .           .               4/imm32           # add to ecx
+ 998     # write-buffered(out, '\n')
+ 999     # . . push args
+1000     68/push  Newline/imm32
+1001     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
+1002     # . . call
+1003     e8/call  write-buffered/disp32
+1004     # . . discard args
+1005     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+1006     # loop
+1007     e9/jump  $emit-labels:loop/disp32
+1008 $emit-labels:end:
+1009     # . restore registers
+1010     5e/pop-to-esi
+1011     5a/pop-to-edx
+1012     59/pop-to-ecx
+1013     58/pop-to-eax
+1014     # . epilogue
+1015     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
+1016     5d/pop-to-ebp
+1017     c3/return
+1018 
+1019 # - some helpers for tests
+1020 
+1021 # some variants of 'trace' that take multiple arguments in different combinations of types:
+1022 #   n: int
+1023 #   c: character [4-bytes, will eventually be UTF-8]
+1024 #   s: (addr array byte)
+1025 #   l: (addr slice)
+1026 # one gotcha: 's5' must not be empty
+1027 
+1028 trace-slsns:  # s1: (addr array byte), l2: (addr slice), s3: (addr array byte), n4: int, s5: (addr array byte)
+1029     # . prologue
+1030     55/push-ebp
+1031     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
+1032     # write(*Trace-stream, s1)
+1033     # . . push args
+1034     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
+1035     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
+1036     # . . call
+1037     e8/call  write/disp32
+1038     # . . discard args
+1039     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+1040     # write-slice(*Trace-stream, l2)
+1041     # . . push args
+1042     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
+1043     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
+1044     # . . call
+1045     e8/call  write-slice/disp32
+1046     # . . discard args
+1047     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+1048     # write(*Trace-stream, s3)
+1049     # . . push args
+1050     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
+1051     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
+1052     # . . call
+1053     e8/call  write/disp32
+1054     # . . discard args
+1055     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+1056     # write-int32-hex(*Trace-stream, n4)
+1057     # . . push args
+1058     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x14/disp8      .                 # push *(ebp+20)
+1059     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
+1060     # . . call
+1061     e8/call  write-int32-hex/disp32
+1062     # . . discard args
+1063     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+1064     # trace(s5)  # implicitly adds a newline and finalizes the trace line
+1065     # . . push args
+1066     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x18/disp8      .                 # push *(ebp+24)
+1067     # . . call
+1068     e8/call  trace/disp32
+1069     # . . discard args
+1070     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+1071 $trace-slsns:end:
+1072     # . epilogue
+1073     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
+1074     5d/pop-to-ebp
+1075     c3/return
+1076 
+1077 test-trace-slsns:
+1078     # . prologue
+1079     55/push-ebp
+1080     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
+1081     # setup
+1082     # . *Trace-stream->write = 0
+1083     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   Trace-stream/disp32               # copy *Trace-stream to eax
+1084     c7          0/subop/copy        0/mod/direct    0/rm32/eax    .           .             .           .           .               0/imm32           # clear *eax
+1085     # (eax..ecx) = "b"
+1086     b8/copy-to-eax  "b"/imm32
+1087     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
+1088     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
+1089     05/add-to-eax  4/imm32
+1090     # var b/ebx: slice = {eax, ecx}
+1091     51/push-ecx
+1092     50/push-eax
+1093     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
+1094     # trace-slsls("A" b "c " 3 " e")
+1095     # . . push args
+1096     68/push  " e"/imm32
+1097     68/push  3/imm32
+1098     68/push  "c "/imm32
+1099     53/push-ebx
+1100     68/push  "A"/imm32
+1101     # . . call
+1102     e8/call  trace-slsns/disp32
+1103     # . . discard args
+1104     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
+1105 +-- 26 lines: #?     # dump *Trace-stream -----------------------------------------------------------------------------------------------------------------------------------------------
+1131     # check-trace-contains("Abc 0x00000003 e")
+1132     # . . push args
+1133     68/push  "F - test-trace-slsls"/imm32
+1134     68/push  "Abc 0x00000003 e"/imm32
+1135     # . . call
+1136     e8/call  check-trace-contains/disp32
+1137     # . . discard args
+1138     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+1139     # . epilogue
+1140     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
+1141     5d/pop-to-ebp
+1142     c3/return
+1143 
+1144 # . . vim:nowrap:textwidth=0
 
-- cgit 1.4.1-2-gfad0