From 86351aafe218a7386f6578be3c4da3edcdcb0c98 Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Fri, 12 Jun 2020 07:57:27 -0700 Subject: 6513 --- html/apps/assort.subx.html | 1367 ++++++++++++++++++++++---------------------- 1 file changed, 697 insertions(+), 670 deletions(-) (limited to 'html/apps/assort.subx.html') diff --git a/html/apps/assort.subx.html b/html/apps/assort.subx.html index efb6a8f2..cd170945 100644 --- a/html/apps/assort.subx.html +++ b/html/apps/assort.subx.html @@ -16,10 +16,10 @@ a { color:inherit; } * { font-size:12pt; font-size: 1em; } .subxComment { color: #005faf; } .subxS2Comment { color: #8a8a8a; } +.subxTest { color: #5f8700; } .subxFunction { color: #af5f00; text-decoration: underline; } .LineNr { } .subxS1Comment { color: #0000af; } -.subxTest { color: #5f8700; } .SpecialChar { color: #d70000; } .Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } .Folded { color: #080808; background-color: #949494; } @@ -60,675 +60,702 @@ if ('onhashchange' in window) { https://github.com/akkartik/mu/blob/master/apps/assort.subx
-  1 # Read a series of segments from stdin and concatenate segments with the same
-  2 # name on stdout.
-  3 #
-  4 # Segments are emitted in order of first encounter.
-  5 #
-  6 # Drop lines that are all comments. They could get misleading after assortment
-  7 # because we don't know if they refer to the line above or the line below.
-  8 #
-  9 # To run:
- 10 #   $ ./bootstrap translate init.linux 0*.subx apps/subx-params.subx apps/assort.subx  -o apps/assort
- 11 #   $ cat x
- 12 #   == code
- 13 #   abc
- 14 #   == code
- 15 #   def
- 16 #   $ cat x  |./bootstrap run apps/assort
- 17 #   == code
- 18 #   abc
- 19 #   def
- 20 
- 21 == code
- 22 #   instruction                     effective address                                                   register    displacement    immediate
- 23 # . op          subop               mod             rm32          base        index         scale       r32
- 24 # . 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
- 25 
- 26 Entry:  # run tests if necessary, convert stdin if not
- 27     # . prologue
- 28     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
- 29 
- 30     # initialize heap
- 31     # . Heap = new-segment(Heap-size)
- 32     # . . push args
- 33     68/push  Heap/imm32
- 34     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Heap-size/disp32                  # push *Heap-size
- 35     # . . call
- 36     e8/call  new-segment/disp32
- 37     # . . discard args
- 38     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
- 39 
- 40     # - if argc > 1 and argv[1] == "test", then return run_tests()
- 41     # if (argc <= 1) goto interactive
- 42     81          7/subop/compare     1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0/disp8         1/imm32           # compare *ebp
- 43     7e/jump-if-<=  $subx-assort-main:interactive/disp8
- 44     # if (!kernel-string-equal?(argv[1], "test")) goto interactive
- 45     # . eax = kernel-string-equal?(argv[1], "test")
- 46     # . . push args
- 47     68/push  "test"/imm32
- 48     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
- 49     # . . call
- 50     e8/call  kernel-string-equal?/disp32
- 51     # . . discard args
- 52     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
- 53     # . if (eax == false) goto interactive
- 54     3d/compare-eax-and  0/imm32/false
- 55     74/jump-if-=  $subx-assort-main:interactive/disp8
- 56     # run-tests()
- 57     e8/call  run-tests/disp32
- 58     # syscall(exit, *Num-test-failures)
- 59     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/ebx   Num-test-failures/disp32          # copy *Num-test-failures to ebx
- 60     eb/jump  $subx-assort-main:end/disp8
- 61 $subx-assort-main:interactive:
- 62     # - otherwise convert stdin
- 63     # var ed/eax: exit-descriptor
- 64     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
- 65     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           4/r32/esp   .               .                 # copy esp to eax
- 66     # configure ed to really exit()
- 67     # . ed->target = 0
- 68     c7          0/subop/copy        0/mod/direct    0/rm32/eax    .           .             .           .           .               0/imm32           # copy to *eax
- 69     # subx-assort(Stdin, Stdout, Stderr, ed)
- 70     # . . push args
- 71     50/push-eax/ed
- 72     68/push  Stderr/imm32
- 73     68/push  Stdout/imm32
- 74     68/push  Stdin/imm32
- 75     # . . call
- 76     e8/call  subx-assort/disp32
- 77     # . . discard args
- 78     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
- 79     # syscall(exit, 0)
- 80     bb/copy-to-ebx  0/imm32
- 81 $subx-assort-main:end:
- 82     b8/copy-to-eax  1/imm32/exit
- 83     cd/syscall  0x80/imm8
- 84 
- 85 # data structure:
- 86 #   table: (addr stream {string, (addr stream byte)})     (8 bytes per row)
- 87 # inefficient; uses sequential search for looking up segments by name
- 88 
- 89 subx-assort:  # in: (addr buffered-file), out: (addr buffered-file)
- 90     # pseudocode:
- 91     #   var table: (addr stream {string, (addr stream byte)} 10/rows)
- 92     #   read-segments(in, table)
- 93     #   write-segments(out, table)
- 94     #
- 95     # . prologue
- 96     55/push-ebp
- 97     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
- 98     # . save registers
- 99     51/push-ecx
-100     # var table/ecx: (stream {string, (addr stream byte)} 80)  # 10 rows * 8 bytes/row
-101     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x50/imm32        # subtract from esp
-102     68/push  0x50/imm32/length
-103     68/push  0/imm32/read
-104     68/push  0/imm32/write
-105     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
-106     # clear-stream(table)
-107     # . . push args
-108     51/push-ecx
-109     # . . call
-110     e8/call  clear-stream/disp32
-111     # . . discard args
-112     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-113 $subx-assort:read:
-114 +--  9 lines: #?     # print("read\n") --------------------------------------------------------------------------------------------------------------------------------------------------
-123     # read-segments(in, table)
-124     # . . push args
-125     51/push-ecx
-126     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
-127     # . . call
-128     e8/call  read-segments/disp32
-129     # . . discard args
-130     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-131 $subx-assort:write:
-132 +--  9 lines: #?     # print("write\n") -------------------------------------------------------------------------------------------------------------------------------------------------
-141     # write-segments(out, table)
-142     # . . push args
-143     51/push-ecx
-144     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
-145     # . . call
-146     e8/call  write-segments/disp32
-147     # . . discard args
-148     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-149 $subx-assort:end:
-150     # . reclaim locals
-151     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x5c/imm32        # add to esp
-152     # . restore registers
-153     59/pop-to-ecx
-154     # . epilogue
-155     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
-156     5d/pop-to-ebp
-157     c3/return
-158 
-159 test-subx-assort:
-160     # . prologue
-161     55/push-ebp
-162     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
-163     # setup
-164     # . clear-stream(_test-input-stream)
-165     # . . push args
-166     68/push  _test-input-stream/imm32
-167     # . . call
-168     e8/call  clear-stream/disp32
-169     # . . discard args
-170     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-171     # . clear-stream($_test-input-buffered-file->buffer)
-172     # . . push args
-173     68/push  $_test-input-buffered-file->buffer/imm32
-174     # . . call
-175     e8/call  clear-stream/disp32
-176     # . . discard args
-177     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-178     # . clear-stream(_test-output-stream)
-179     # . . push args
-180     68/push  _test-output-stream/imm32
-181     # . . call
-182     e8/call  clear-stream/disp32
-183     # . . discard args
-184     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-185     # . clear-stream($_test-output-buffered-file->buffer)
-186     # . . push args
-187     68/push  $_test-output-buffered-file->buffer/imm32
-188     # . . call
-189     e8/call  clear-stream/disp32
-190     # . . discard args
-191     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-192     # initialize input (meta comments in parens)
-193     #   # comment 1
-194     #     # comment 2 indented
-195     #   == code 0x09000000  (new segment)
-196     #   # comment 3 inside a segment
-197     #   1
-198     #                         (empty line)
-199     #   2 3 # comment 4 inline with other contents
-200     #   == data 0x0a000000  (new segment)
-201     #   4 5/imm32
-202     #   == code  (existing segment but non-contiguous with previous iteration)
-203     #   6 7
-204     #   8 9  (multiple lines)
-205     #   == code  (existing segment contiguous with previous iteration)
-206     #   10 11
-207     # . write(_test-input-stream, "# comment 1\n")
-208     # . . push args
-209     68/push  "# comment 1\n"/imm32
-210     68/push  _test-input-stream/imm32
-211     # . . call
-212     e8/call  write/disp32
-213     # . . discard args
-214     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-215     # . write(_test-input-stream, "  # comment 2 indented\n")
-216     # . . push args
-217     68/push  "  # comment 2 indented\n"/imm32
-218     68/push  _test-input-stream/imm32
-219     # . . call
-220     e8/call  write/disp32
-221     # . . discard args
-222     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-223     # . write(_test-input-stream, "== code 0x09000000\n")
-224     # . . push args
-225     68/push  "== code 0x09000000\n"/imm32
-226     68/push  _test-input-stream/imm32
-227     # . . call
-228     e8/call  write/disp32
-229     # . . discard args
-230     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-231     # . write(_test-input-stream, "# comment 3 inside a segment\n")
-232     # . . push args
-233     68/push  "# comment 3 inside a segment\n"/imm32
-234     68/push  _test-input-stream/imm32
-235     # . . call
-236     e8/call  write/disp32
-237     # . . discard args
-238     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-239     # . write(_test-input-stream, "1\n")
-240     # . . push args
-241     68/push  "1\n"/imm32
-242     68/push  _test-input-stream/imm32
-243     # . . call
-244     e8/call  write/disp32
-245     # . . discard args
-246     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-247     # . write(_test-input-stream, "\n")  # empty line
-248     # . . push args
-249     68/push  Newline/imm32
-250     68/push  _test-input-stream/imm32
-251     # . . call
-252     e8/call  write/disp32
-253     # . . discard args
-254     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-255     # . write(_test-input-stream, "2 3 # comment 4 inline with other contents\n")
-256     # . . push args
-257     68/push  "2 3 # comment 4 inline with other contents\n"/imm32
-258     68/push  _test-input-stream/imm32
-259     # . . call
-260     e8/call  write/disp32
-261     # . . discard args
-262     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-263     # . write(_test-input-stream, "== data 0x0a000000\n")
-264     # . . push args
-265     68/push  "== data 0x0a000000\n"/imm32
-266     68/push  _test-input-stream/imm32
-267     # . . call
-268     e8/call  write/disp32
-269     # . . discard args
-270     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-271     # . write(_test-input-stream, "4 5/imm32\n")
-272     # . . push args
-273     68/push  "4 5/imm32\n"/imm32
-274     68/push  _test-input-stream/imm32
-275     # . . call
-276     e8/call  write/disp32
-277     # . . discard args
-278     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-279     # . write(_test-input-stream, "== code\n")
-280     # . . push args
-281     68/push  "== code\n"/imm32
-282     68/push  _test-input-stream/imm32
-283     # . . call
-284     e8/call  write/disp32
-285     # . . discard args
-286     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-287     # . write(_test-input-stream, "6 7\n")
-288     # . . push args
-289     68/push  "6 7\n"/imm32
-290     68/push  _test-input-stream/imm32
-291     # . . call
-292     e8/call  write/disp32
-293     # . . discard args
-294     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-295     # . write(_test-input-stream, "8 9\n")
-296     # . . push args
-297     68/push  "8 9\n"/imm32
-298     68/push  _test-input-stream/imm32
-299     # . . call
-300     e8/call  write/disp32
-301     # . . discard args
-302     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-303     # . write(_test-input-stream, "== code\n")
-304     # . . push args
-305     68/push  "== code\n"/imm32
-306     68/push  _test-input-stream/imm32
-307     # . . call
-308     e8/call  write/disp32
-309     # . . discard args
-310     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-311     # . write(_test-input-stream, "10 11\n")
-312     # . . push args
-313     68/push  "10 11\n"/imm32
-314     68/push  _test-input-stream/imm32
-315     # . . call
-316     e8/call  write/disp32
-317     # . . discard args
-318     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-319     # subx-assort(_test-input-buffered-file, _test-output-buffered-file)
-320     # . . push args
-321     68/push  _test-output-buffered-file/imm32
-322     68/push  _test-input-buffered-file/imm32
-323     # . . call
-324     e8/call  subx-assort/disp32
-325     # . . discard args
-326     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-327     # . flush(_test-output-buffered-file)
-328     # . . push args
-329     68/push  _test-output-buffered-file/imm32
-330     # . . call
-331     e8/call  flush/disp32
-332     # . . discard args
-333     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-334     # check output
-335     #   == code 0x09000000
-336     #   1
-337     #   2 3 # comment 4 inline with other contents
-338     #   6 7
-339     #   8 9
-340     #   10 11
-341     #   == data 0x0a000000
-342     #   4 5/imm32
-343 +-- 33 lines: #?     # dump output ------------------------------------------------------------------------------------------------------------------------------------------------------
-376     # . check-next-stream-line-equal(_test-output-stream, "== code 0x09000000", msg)
-377     # . . push args
-378     68/push  "F - test-subx-assort/0"/imm32
-379     68/push  "== code 0x09000000"/imm32
-380     68/push  _test-output-stream/imm32
-381     # . . call
-382     e8/call  check-next-stream-line-equal/disp32
-383     # . . discard args
-384     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-385     # . check-next-stream-line-equal(_test-output-stream, "1", msg)
-386     # . . push args
-387     68/push  "F - test-subx-assort/1"/imm32
-388     68/push  "1"/imm32
-389     68/push  _test-output-stream/imm32
-390     # . . call
-391     e8/call  check-next-stream-line-equal/disp32
-392     # . . discard args
-393     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-394     # . check-next-stream-line-equal(_test-output-stream, "2 3 # comment 4 inline with other contents", msg)
-395     # . . push args
-396     68/push  "F - test-subx-assort/2"/imm32
-397     68/push  "2 3 # comment 4 inline with other contents"/imm32
-398     68/push  _test-output-stream/imm32
-399     # . . call
-400     e8/call  check-next-stream-line-equal/disp32
-401     # . . discard args
-402     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-403     # . check-next-stream-line-equal(_test-output-stream, "6 7", msg)
-404     # . . push args
-405     68/push  "F - test-subx-assort/3"/imm32
-406     68/push  "6 7"/imm32
-407     68/push  _test-output-stream/imm32
-408     # . . call
-409     e8/call  check-next-stream-line-equal/disp32
-410     # . . discard args
-411     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-412     # . check-next-stream-line-equal(_test-output-stream, "8 9", msg)
-413     # . . push args
-414     68/push  "F - test-subx-assort/4"/imm32
-415     68/push  "8 9"/imm32
-416     68/push  _test-output-stream/imm32
-417     # . . call
-418     e8/call  check-next-stream-line-equal/disp32
-419     # . . discard args
-420     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-421     # . check-next-stream-line-equal(_test-output-stream, "10 11", msg)
-422     # . . push args
-423     68/push  "F - test-subx-assort/5"/imm32
-424     68/push  "10 11"/imm32
-425     68/push  _test-output-stream/imm32
-426     # . . call
-427     e8/call  check-next-stream-line-equal/disp32
-428     # . . discard args
-429     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-430     # . check-next-stream-line-equal(_test-output-stream, "== data 0x0a000000", msg)
-431     # . . push args
-432     68/push  "F - test-subx-assort/6"/imm32
-433     68/push  "== data 0x0a000000"/imm32
-434     68/push  _test-output-stream/imm32
-435     # . . call
-436     e8/call  check-next-stream-line-equal/disp32
-437     # . . discard args
-438     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-439     # . check-next-stream-line-equal(_test-output-stream, "4 5/imm32", msg)
-440     # . . push args
-441     68/push  "F - test-subx-assort/7"/imm32
-442     68/push  "4 5/imm32"/imm32
-443     68/push  _test-output-stream/imm32
-444     # . . call
-445     e8/call  check-next-stream-line-equal/disp32
-446     # . . discard args
-447     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-448     # . epilogue
-449     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
-450     5d/pop-to-ebp
-451     c3/return
-452 
-453 # type string_key = (addr array byte)
-454 
-455 # beware: leaks memory (one name per segment read)
-456 read-segments:  # in: (addr buffered-file), table: (addr stream {string_key, (handle stream byte)})
-457     # pseudocode:
-458     #   var curr-segment: (handle stream byte) = 0
-459     #   var line: (stream byte 512)
-460     #   while true
-461     #     clear-stream(line)
-462     #     read-line-buffered(in, line)
-463     #     if (line->write == 0) break             # end of file
-464     #     var word-slice = next-word-or-string(line)
-465     #     if slice-empty?(word-slice)             # whitespace
-466     #       continue
-467     #     if slice-starts-with?(word-slice, "#")  # comment
-468     #       continue
-469     #     if slice-equal?(word-slice, "==")
-470     #       var segment-name = next-word-or-string(line)
-471     #       segment-slot = leaky-get-or-insert-slice(table, segment-name, row-size=8)
-472     #       curr-segment = *segment-slot
-473     #       if curr-segment != 0
-474     #         continue
-475     #       curr-segment = new-stream(Heap, Segment-size, 1)
-476     #       *segment-slot = curr-segment
-477     #     rewind-stream(line)
-478     #     write-stream(curr-segment, line)  # abort if curr-segment overflows
-479     #
-480     # word-slice and segment-name are both slices with disjoint lifetimes, so
-481     # we'll use the same address for them.
-482     #
-483     # registers:
-484     #   line: ecx
-485     #   word-slice and segment-name: edx
-486     #   segment-name and curr-segment: ebx
-487     #   word-slice->start: esi
-488     #   temporary: eax
-489     #
-490     # . prologue
-491     55/push-ebp
-492     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
-493     # . save registers
-494     50/push-eax
-495     51/push-ecx
-496     52/push-edx
-497     53/push-ebx
-498     56/push-esi
-499     # var line/ecx: (stream byte 512)
-500     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x200/imm32       # subtract from esp
-501     68/push  0x200/imm32/length
-502     68/push  0/imm32/read
-503     68/push  0/imm32/write
-504     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
-505     # var word-slice/edx: slice
-506     68/push  0/imm32/end
-507     68/push  0/imm32/start
-508     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
-509 $read-segments:loop:
-510     # clear-stream(line)
-511     # . . push args
-512     51/push-ecx
-513     # . . call
-514     e8/call  clear-stream/disp32
-515     # . . discard args
-516     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-517     # read-line-buffered(in, line)
-518     # . . push args
-519     51/push-ecx
-520     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
-521     # . . call
-522     e8/call  read-line-buffered/disp32
-523     # . . discard args
-524     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-525 $read-segments:check0:
-526     # if (line->write == 0) break
-527     81          7/subop/compare     0/mod/indirect  1/rm32/ecx    .           .             .           .           .               0/imm32           # compare *ecx
-528     0f 84/jump-if-=  $read-segments:break/disp32
-529 +-- 33 lines: #?     # dump line --------------------------------------------------------------------------------------------------------------------------------------------------------
-562     # next-word-or-string(line, word-slice)
-563     # . . push args
-564     52/push-edx
-565     51/push-ecx
-566     # . . call
-567     e8/call  next-word-or-string/disp32
-568     # . . discard args
-569     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-570 $read-segments:check1:
-571 +--  9 lines: #?     # print("check1\n") ------------------------------------------------------------------------------------------------------------------------------------------------
-580     # if (slice-empty?(word-slice)) continue
-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) continue
-589     3d/compare-eax-and  0/imm32/false
-590     0f 85/jump-if-!=  $read-segments:loop/disp32
-591 $read-segments:check-for-comment:
-592 +--  9 lines: #?     # print("check for comment\n") -------------------------------------------------------------------------------------------------------------------------------------
-601     # if (slice-starts-with?(word-slice, "#")) continue
-602     # . var start/esi: (addr byte) = word-slice->start
-603     8b/copy                         0/mod/indirect  2/rm32/edx    .           .             .           6/r32/esi   .               .                 # copy *ecx to esi
-604     # . var c/eax: byte = *start
-605     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
-606     8a/copy-byte                    0/mod/indirect  6/rm32/esi    .           .             .           0/r32/AL    .               .                 # copy byte at *esi to AL
-607     # . if (c == '#') continue
-608     3d/compare-eax-and  0x23/imm32/hash
-609     0f 84/jump-if-=  $read-segments:loop/disp32
-610 $read-segments:check-for-segment-header:
-611 +--  9 lines: #?     # print("check for segment header\n") ------------------------------------------------------------------------------------------------------------------------------
-620 +-- 40 lines: #?     # dump word-slice --------------------------------------------------------------------------------------------------------------------------------------------------
-660     # if !slice-equal?(word-slice, "==") goto next check
-661     # . eax = slice-equal?(word-slice, "==")
-662     # . . push args
-663     68/push  "=="/imm32
-664     52/push-edx
-665     # . . call
-666     e8/call  slice-equal?/disp32
-667     # . . discard args
-668     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-669     # . if (eax == false) goto check3
-670     3d/compare-eax-and  0/imm32/false
-671     0f 84/jump-if-=  $read-segments:regular-line/disp32
-672     # segment-name = next-word-or-string(line)
-673     # . . push args
-674     52/push-edx
-675     51/push-ecx
-676     # . . call
-677     e8/call  next-word-or-string/disp32
-678     # . . discard args
-679     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-680 +-- 40 lines: #?     # dump segment name ------------------------------------------------------------------------------------------------------------------------------------------------
-720     # var segment-slot/eax: (addr handle stream byte) = leaky-get-or-insert-slice(table, segment-name, row-size=8)
-721     # . . push args
-722     68/push  8/imm32/row-size
-723     52/push-edx
-724     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
-725     # . . call
-726     e8/call  leaky-get-or-insert-slice/disp32
-727     # . . discard args
-728     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-729     # var curr-segment/ebx: (handle stream byte) = *segment-slot
-730     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           3/r32/ebx   .               .                 # copy *eax to ebx
-731     # if (curr-segment != 0) continue
-732     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0/imm32           # compare ebx
-733     0f 85/jump-if-!=  $read-segments:loop/disp32
-734     # curr-segment = new-stream(Heap, Segment-size, 1)
-735     # . save segment-slot
-736     50/push-eax
-737     # . eax = new-stream(Heap, Segment-size, 1)
-738     # . . push args
-739     68/push  1/imm32
-740     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Segment-size/disp32               # push *Segment-size
-741     68/push  Heap/imm32
-742     # . . call
-743     e8/call  new-stream/disp32
-744     # . . discard args
-745     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
-746     # . curr-segment = eax
-747     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # copy eax to ebx
-748     # . restore segment-slot
-749     58/pop-to-eax
-750     # *segment-slot = curr-segment
-751     89/copy                         0/mod/indirect  0/rm32/eax    .           .             .           3/r32/ebx   .               .                 # copy ebx to *eax
-752     # fall through
-753 $read-segments:regular-line:
-754 +--  9 lines: #?     # print("regular line\n") ------------------------------------------------------------------------------------------------------------------------------------------
-763 +-- 33 lines: #?     # dump line --------------------------------------------------------------------------------------------------------------------------------------------------------
-796     # rewind-stream(line)
-797     # . . push args
-798     51/push-ecx
-799     # . . call
-800     e8/call  rewind-stream/disp32
-801     # . . discard args
-802     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-803 +--  9 lines: #?     # print("write stream\n") ------------------------------------------------------------------------------------------------------------------------------------------
-812     # write-stream(curr-segment, line)
-813     # . . push args
-814     51/push-ecx
-815     53/push-ebx
-816     # . . call
-817     e8/call  write-stream/disp32
-818     # . . discard args
-819     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-820     # loop
-821 +--  9 lines: #?     # print("loop\n") --------------------------------------------------------------------------------------------------------------------------------------------------
-830     e9/jump  $read-segments:loop/disp32
-831 $read-segments:break:
-832 $read-segments:end:
-833     # . reclaim locals
-834     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x214/imm32       # add to esp
-835     # . restore registers
-836     5e/pop-to-esi
-837     5b/pop-to-ebx
-838     5a/pop-to-edx
-839     59/pop-to-ecx
-840     58/pop-to-eax
-841     # . epilogue
-842     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
-843     5d/pop-to-ebp
-844     c3/return
-845 
-846 write-segments:  # out: (addr buffered-file), table: (addr stream {string_key, (handle stream byte)})
-847     # pseudocode:
-848     #   var curr = table->data
-849     #   var max = &table->data[table->write]
-850     #   while curr < max
-851     #     stream = table[i].stream
-852     #     write-stream-data(out, stream)
-853     #     curr += 8
-854     #   flush(out)
-855     #
-856     # . prologue
-857     55/push-ebp
-858     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
-859     # . save registers
-860     50/push-eax
-861     52/push-edx
-862     56/push-esi
-863     # esi = table
-864     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   0xc/disp8       .                 # copy *(ebp+12) to esi
-865     # var write/edx: int = table->write
-866     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           2/r32/edx   .               .                 # copy *esi to edx
-867     # var curr/esi: (addr byte) = table->data
-868     81          0/subop/add         3/mod/direct    6/rm32/esi    .           .             .           .           .               0xc/imm32         # add to eax
-869     # var max/edx: (addr byte) = curr + write
-870     01/add                          3/mod/direct    2/rm32/edx    .           .             .           6/r32/esi   .               .                 # add esi to edx
-871 $write-segments:loop:
-872     # if (curr >= max) break
-873     39/compare                      3/mod/direct    6/rm32/esi    .           .             .           2/r32/edx   .               .                 # compare esi with edx
-874     73/jump-if-addr>=  $write-segments:break/disp8
-875     # var stream/eax: (addr stream byte) = table[i].stream
-876     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           0/r32/eax   4/disp8         .                 # copy *(esi+4) to eax
-877     # write-stream-data(out, stream)
-878     # . . push args
-879     50/push-eax
-880     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
-881     # . . call
-882     e8/call  write-stream-data/disp32
-883     # . . discard args
-884     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
-885 $write-segments:continue:
-886     # curr += 8
-887     81          0/subop/add         3/mod/direct    6/rm32/esi    .           .             .           .           .               8/imm32           # add to esi
-888     eb/jump  $write-segments:loop/disp8
-889 $write-segments:break:
-890     # flush(out)
-891     # . . push args
-892     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
-893     # . . call
-894     e8/call  flush/disp32
-895     # . . discard args
-896     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
-897 $write-segments:end:
-898     # . restore registers
-899     5e/pop-to-esi
-900     5a/pop-to-edx
-901     58/pop-to-eax
-902     # . epilogue
-903     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
-904     5d/pop-to-ebp
-905     c3/return
-906 
-907 # . . vim:nowrap:textwidth=0
+   1 # Read a series of segments from stdin and concatenate segments with the same
+   2 # name on stdout.
+   3 #
+   4 # Segments are emitted in order of first encounter.
+   5 #
+   6 # Drop lines that are all comments. They could get misleading after assortment
+   7 # because we don't know if they refer to the line above or the line below.
+   8 #
+   9 # To run:
+  10 #   $ ./bootstrap translate init.linux 0*.subx apps/subx-params.subx apps/assort.subx  -o apps/assort
+  11 #   $ cat x
+  12 #   == code
+  13 #   abc
+  14 #   == code
+  15 #   def
+  16 #   $ cat x  |./bootstrap run apps/assort
+  17 #   == code
+  18 #   abc
+  19 #   def
+  20 
+  21 == code
+  22 #   instruction                     effective address                                                   register    displacement    immediate
+  23 # . op          subop               mod             rm32          base        index         scale       r32
+  24 # . 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
+  25 
+  26 Entry:  # run tests if necessary, convert stdin if not
+  27     # . prologue
+  28     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
+  29 
+  30     # initialize heap
+  31     # . Heap = new-segment(Heap-size)
+  32     # . . push args
+  33     68/push  Heap/imm32
+  34     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Heap-size/disp32                  # push *Heap-size
+  35     # . . call
+  36     e8/call  new-segment/disp32
+  37     # . . discard args
+  38     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+  39 
+  40     # - if argc > 1 and argv[1] == "test", then return run_tests()
+  41     # if (argc <= 1) goto interactive
+  42     81          7/subop/compare     1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0/disp8         1/imm32           # compare *ebp
+  43     7e/jump-if-<=  $subx-assort-main:interactive/disp8
+  44     # if (!kernel-string-equal?(argv[1], "test")) goto interactive
+  45     # . eax = kernel-string-equal?(argv[1], "test")
+  46     # . . push args
+  47     68/push  "test"/imm32
+  48     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
+  49     # . . call
+  50     e8/call  kernel-string-equal?/disp32
+  51     # . . discard args
+  52     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+  53     # . if (eax == false) goto interactive
+  54     3d/compare-eax-and  0/imm32/false
+  55     74/jump-if-=  $subx-assort-main:interactive/disp8
+  56     # run-tests()
+  57     e8/call  run-tests/disp32
+  58     # syscall(exit, *Num-test-failures)
+  59     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/ebx   Num-test-failures/disp32          # copy *Num-test-failures to ebx
+  60     eb/jump  $subx-assort-main:end/disp8
+  61 $subx-assort-main:interactive:
+  62     # - otherwise convert stdin
+  63     # var ed/eax: exit-descriptor
+  64     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
+  65     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           4/r32/esp   .               .                 # copy esp to eax
+  66     # configure ed to really exit()
+  67     # . ed->target = 0
+  68     c7          0/subop/copy        0/mod/direct    0/rm32/eax    .           .             .           .           .               0/imm32           # copy to *eax
+  69     # subx-assort(Stdin, Stdout, Stderr, ed)
+  70     # . . push args
+  71     50/push-eax/ed
+  72     68/push  Stderr/imm32
+  73     68/push  Stdout/imm32
+  74     68/push  Stdin/imm32
+  75     # . . call
+  76     e8/call  subx-assort/disp32
+  77     # . . discard args
+  78     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
+  79     # syscall(exit, 0)
+  80     bb/copy-to-ebx  0/imm32
+  81 $subx-assort-main:end:
+  82     e8/call  syscall_exit/disp32
+  83 
+  84 # data structure:
+  85 #   table: (addr stream {(handle array byte), (handle stream byte)})     (16 bytes/row)
+  86 # inefficient; uses sequential search for looking up segments by name
+  87 
+  88 subx-assort:  # in: (addr buffered-file), out: (addr buffered-file)
+  89     # pseudocode:
+  90     #   var table: (addr stream {(handle array byte), (handle stream byte)} 10 rows)
+  91     #   read-segments(in, table)
+  92     #   write-segments(out, table)
+  93     #
+  94     # . prologue
+  95     55/push-ebp
+  96     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
+  97     # . save registers
+  98     51/push-ecx
+  99     # var table/ecx: (stream {string, (handle stream byte)} 160)  # 10 rows * 16 bytes/row
+ 100     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0xa0/imm32        # subtract from esp
+ 101     68/push  0xa0/imm32/length
+ 102     68/push  0/imm32/read
+ 103     68/push  0/imm32/write
+ 104     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
+ 105     # clear-stream(table)
+ 106     # . . push args
+ 107     51/push-ecx
+ 108     # . . call
+ 109     e8/call  clear-stream/disp32
+ 110     # . . discard args
+ 111     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+ 112 $subx-assort:read:
+ 113 +--  9 lines: #?     # print("read\n") --------------------------------------------------------------------------------------------------------------------------------------------------
+ 122     # read-segments(in, table)
+ 123     # . . push args
+ 124     51/push-ecx
+ 125     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
+ 126     # . . call
+ 127     e8/call  read-segments/disp32
+ 128     # . . discard args
+ 129     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 130 $subx-assort:write:
+ 131 +--  9 lines: #?     # print("write\n") -------------------------------------------------------------------------------------------------------------------------------------------------
+ 140     # write-segments(out, table)
+ 141     # . . push args
+ 142     51/push-ecx
+ 143     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
+ 144     # . . call
+ 145     e8/call  write-segments/disp32
+ 146     # . . discard args
+ 147     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 148 $subx-assort:end:
+ 149     # . reclaim locals
+ 150     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xac/imm32        # add to esp
+ 151     # . restore registers
+ 152     59/pop-to-ecx
+ 153     # . epilogue
+ 154     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
+ 155     5d/pop-to-ebp
+ 156     c3/return
+ 157 
+ 158 test-subx-assort:
+ 159     # . prologue
+ 160     55/push-ebp
+ 161     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
+ 162     # setup
+ 163     # . clear-stream(_test-input-stream)
+ 164     # . . push args
+ 165     68/push  _test-input-stream/imm32
+ 166     # . . call
+ 167     e8/call  clear-stream/disp32
+ 168     # . . discard args
+ 169     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+ 170     # . clear-stream($_test-input-buffered-file->buffer)
+ 171     # . . push args
+ 172     68/push  $_test-input-buffered-file->buffer/imm32
+ 173     # . . call
+ 174     e8/call  clear-stream/disp32
+ 175     # . . discard args
+ 176     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+ 177     # . clear-stream(_test-output-stream)
+ 178     # . . push args
+ 179     68/push  _test-output-stream/imm32
+ 180     # . . call
+ 181     e8/call  clear-stream/disp32
+ 182     # . . discard args
+ 183     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+ 184     # . clear-stream($_test-output-buffered-file->buffer)
+ 185     # . . push args
+ 186     68/push  $_test-output-buffered-file->buffer/imm32
+ 187     # . . call
+ 188     e8/call  clear-stream/disp32
+ 189     # . . discard args
+ 190     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+ 191     # initialize input (meta comments in parens)
+ 192     #   # comment 1
+ 193     #     # comment 2 indented
+ 194     #   == code 0x09000000  (new segment)
+ 195     #   # comment 3 inside a segment
+ 196     #   1
+ 197     #                         (empty line)
+ 198     #   2 3 # comment 4 inline with other contents
+ 199     #   == data 0x0a000000  (new segment)
+ 200     #   4 5/imm32
+ 201     #   == code  (existing segment but non-contiguous with previous iteration)
+ 202     #   6 7
+ 203     #   8 9  (multiple lines)
+ 204     #   == code  (existing segment contiguous with previous iteration)
+ 205     #   10 11
+ 206     # . write(_test-input-stream, "# comment 1\n")
+ 207     # . . push args
+ 208     68/push  "# comment 1\n"/imm32
+ 209     68/push  _test-input-stream/imm32
+ 210     # . . call
+ 211     e8/call  write/disp32
+ 212     # . . discard args
+ 213     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 214     # . write(_test-input-stream, "  # comment 2 indented\n")
+ 215     # . . push args
+ 216     68/push  "  # comment 2 indented\n"/imm32
+ 217     68/push  _test-input-stream/imm32
+ 218     # . . call
+ 219     e8/call  write/disp32
+ 220     # . . discard args
+ 221     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 222     # . write(_test-input-stream, "== code 0x09000000\n")
+ 223     # . . push args
+ 224     68/push  "== code 0x09000000\n"/imm32
+ 225     68/push  _test-input-stream/imm32
+ 226     # . . call
+ 227     e8/call  write/disp32
+ 228     # . . discard args
+ 229     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 230     # . write(_test-input-stream, "# comment 3 inside a segment\n")
+ 231     # . . push args
+ 232     68/push  "# comment 3 inside a segment\n"/imm32
+ 233     68/push  _test-input-stream/imm32
+ 234     # . . call
+ 235     e8/call  write/disp32
+ 236     # . . discard args
+ 237     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 238     # . write(_test-input-stream, "1\n")
+ 239     # . . push args
+ 240     68/push  "1\n"/imm32
+ 241     68/push  _test-input-stream/imm32
+ 242     # . . call
+ 243     e8/call  write/disp32
+ 244     # . . discard args
+ 245     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 246     # . write(_test-input-stream, "\n")  # empty line
+ 247     # . . push args
+ 248     68/push  Newline/imm32
+ 249     68/push  _test-input-stream/imm32
+ 250     # . . call
+ 251     e8/call  write/disp32
+ 252     # . . discard args
+ 253     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 254     # . write(_test-input-stream, "2 3 # comment 4 inline with other contents\n")
+ 255     # . . push args
+ 256     68/push  "2 3 # comment 4 inline with other contents\n"/imm32
+ 257     68/push  _test-input-stream/imm32
+ 258     # . . call
+ 259     e8/call  write/disp32
+ 260     # . . discard args
+ 261     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 262     # . write(_test-input-stream, "== data 0x0a000000\n")
+ 263     # . . push args
+ 264     68/push  "== data 0x0a000000\n"/imm32
+ 265     68/push  _test-input-stream/imm32
+ 266     # . . call
+ 267     e8/call  write/disp32
+ 268     # . . discard args
+ 269     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 270     # . write(_test-input-stream, "4 5/imm32\n")
+ 271     # . . push args
+ 272     68/push  "4 5/imm32\n"/imm32
+ 273     68/push  _test-input-stream/imm32
+ 274     # . . call
+ 275     e8/call  write/disp32
+ 276     # . . discard args
+ 277     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 278     # . write(_test-input-stream, "== code\n")
+ 279     # . . push args
+ 280     68/push  "== code\n"/imm32
+ 281     68/push  _test-input-stream/imm32
+ 282     # . . call
+ 283     e8/call  write/disp32
+ 284     # . . discard args
+ 285     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 286     # . write(_test-input-stream, "6 7\n")
+ 287     # . . push args
+ 288     68/push  "6 7\n"/imm32
+ 289     68/push  _test-input-stream/imm32
+ 290     # . . call
+ 291     e8/call  write/disp32
+ 292     # . . discard args
+ 293     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 294     # . write(_test-input-stream, "8 9\n")
+ 295     # . . push args
+ 296     68/push  "8 9\n"/imm32
+ 297     68/push  _test-input-stream/imm32
+ 298     # . . call
+ 299     e8/call  write/disp32
+ 300     # . . discard args
+ 301     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 302     # . write(_test-input-stream, "== code\n")
+ 303     # . . push args
+ 304     68/push  "== code\n"/imm32
+ 305     68/push  _test-input-stream/imm32
+ 306     # . . call
+ 307     e8/call  write/disp32
+ 308     # . . discard args
+ 309     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 310     # . write(_test-input-stream, "10 11\n")
+ 311     # . . push args
+ 312     68/push  "10 11\n"/imm32
+ 313     68/push  _test-input-stream/imm32
+ 314     # . . call
+ 315     e8/call  write/disp32
+ 316     # . . discard args
+ 317     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 318     # subx-assort(_test-input-buffered-file, _test-output-buffered-file)
+ 319     # . . push args
+ 320     68/push  _test-output-buffered-file/imm32
+ 321     68/push  _test-input-buffered-file/imm32
+ 322     # . . call
+ 323     e8/call  subx-assort/disp32
+ 324     # . . discard args
+ 325     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 326     # . flush(_test-output-buffered-file)
+ 327     # . . push args
+ 328     68/push  _test-output-buffered-file/imm32
+ 329     # . . call
+ 330     e8/call  flush/disp32
+ 331     # . . discard args
+ 332     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+ 333     # check output
+ 334     #   == code 0x09000000
+ 335     #   1
+ 336     #   2 3 # comment 4 inline with other contents
+ 337     #   6 7
+ 338     #   8 9
+ 339     #   10 11
+ 340     #   == data 0x0a000000
+ 341     #   4 5/imm32
+ 342 +-- 33 lines: #?     # dump output ------------------------------------------------------------------------------------------------------------------------------------------------------
+ 375     # . check-next-stream-line-equal(_test-output-stream, "== code 0x09000000", msg)
+ 376     # . . push args
+ 377     68/push  "F - test-subx-assort/0"/imm32
+ 378     68/push  "== code 0x09000000"/imm32
+ 379     68/push  _test-output-stream/imm32
+ 380     # . . call
+ 381     e8/call  check-next-stream-line-equal/disp32
+ 382     # . . discard args
+ 383     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+ 384     # . check-next-stream-line-equal(_test-output-stream, "1", msg)
+ 385     # . . push args
+ 386     68/push  "F - test-subx-assort/1"/imm32
+ 387     68/push  "1"/imm32
+ 388     68/push  _test-output-stream/imm32
+ 389     # . . call
+ 390     e8/call  check-next-stream-line-equal/disp32
+ 391     # . . discard args
+ 392     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+ 393     # . check-next-stream-line-equal(_test-output-stream, "2 3 # comment 4 inline with other contents", msg)
+ 394     # . . push args
+ 395     68/push  "F - test-subx-assort/2"/imm32
+ 396     68/push  "2 3 # comment 4 inline with other contents"/imm32
+ 397     68/push  _test-output-stream/imm32
+ 398     # . . call
+ 399     e8/call  check-next-stream-line-equal/disp32
+ 400     # . . discard args
+ 401     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+ 402     # . check-next-stream-line-equal(_test-output-stream, "6 7", msg)
+ 403     # . . push args
+ 404     68/push  "F - test-subx-assort/3"/imm32
+ 405     68/push  "6 7"/imm32
+ 406     68/push  _test-output-stream/imm32
+ 407     # . . call
+ 408     e8/call  check-next-stream-line-equal/disp32
+ 409     # . . discard args
+ 410     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+ 411     # . check-next-stream-line-equal(_test-output-stream, "8 9", msg)
+ 412     # . . push args
+ 413     68/push  "F - test-subx-assort/4"/imm32
+ 414     68/push  "8 9"/imm32
+ 415     68/push  _test-output-stream/imm32
+ 416     # . . call
+ 417     e8/call  check-next-stream-line-equal/disp32
+ 418     # . . discard args
+ 419     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+ 420     # . check-next-stream-line-equal(_test-output-stream, "10 11", msg)
+ 421     # . . push args
+ 422     68/push  "F - test-subx-assort/5"/imm32
+ 423     68/push  "10 11"/imm32
+ 424     68/push  _test-output-stream/imm32
+ 425     # . . call
+ 426     e8/call  check-next-stream-line-equal/disp32
+ 427     # . . discard args
+ 428     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+ 429     # . check-next-stream-line-equal(_test-output-stream, "== data 0x0a000000", msg)
+ 430     # . . push args
+ 431     68/push  "F - test-subx-assort/6"/imm32
+ 432     68/push  "== data 0x0a000000"/imm32
+ 433     68/push  _test-output-stream/imm32
+ 434     # . . call
+ 435     e8/call  check-next-stream-line-equal/disp32
+ 436     # . . discard args
+ 437     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+ 438     # . check-next-stream-line-equal(_test-output-stream, "4 5/imm32", msg)
+ 439     # . . push args
+ 440     68/push  "F - test-subx-assort/7"/imm32
+ 441     68/push  "4 5/imm32"/imm32
+ 442     68/push  _test-output-stream/imm32
+ 443     # . . call
+ 444     e8/call  check-next-stream-line-equal/disp32
+ 445     # . . discard args
+ 446     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
+ 447     # . epilogue
+ 448     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
+ 449     5d/pop-to-ebp
+ 450     c3/return
+ 451 
+ 452 read-segments:  # in: (addr buffered-file), table: (addr stream {(handle array byte), (handle stream byte)})
+ 453     # pseudocode:
+ 454     #   var curr-segment: (addr stream byte) = 0
+ 455     #   var line: (stream byte 512)
+ 456     #   while true
+ 457     #     clear-stream(line)
+ 458     #     read-line-buffered(in, line)
+ 459     #     if (line->write == 0) break             # end of file
+ 460     #     var word-slice = next-word-or-string(line)
+ 461     #     if slice-empty?(word-slice)             # whitespace
+ 462     #       continue
+ 463     #     if slice-starts-with?(word-slice, "#")  # comment
+ 464     #       continue
+ 465     #     if slice-equal?(word-slice, "==")
+ 466     #       var segment-name = next-word-or-string(line)
+ 467     #       var segment-slot: (addr handle stream byte) = get-or-insert-slice(table, segment-name, row-size=16)
+ 468     #       if *segment-slot != 0
+ 469     #         curr-segment = lookup(*segment-slot)
+ 470     #         continue
+ 471     #       new-stream(Heap, Segment-size, 1, segment-slot)
+ 472     #       curr-segment = lookup(*segment-slot)
+ 473     #     rewind-stream(line)
+ 474     #     write-stream(curr-segment, line)  # abort if curr-segment overflows
+ 475     #
+ 476     # word-slice and segment-name are both slices with disjoint lifetimes, so
+ 477     # we'll use the same address for them.
+ 478     #
+ 479     # registers:
+ 480     #   line: ecx
+ 481     #   word-slice and segment-name: edx
+ 482     #   segment-name and curr-segment: ebx
+ 483     #   word-slice->start: esi
+ 484     #   temporary: eax
+ 485     #
+ 486     # . prologue
+ 487     55/push-ebp
+ 488     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
+ 489     # . save registers
+ 490     50/push-eax
+ 491     51/push-ecx
+ 492     52/push-edx
+ 493     53/push-ebx
+ 494     56/push-esi
+ 495     57/push-edi
+ 496     # var line/ecx: (stream byte 512)
+ 497     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x200/imm32       # subtract from esp
+ 498     68/push  0x200/imm32/length
+ 499     68/push  0/imm32/read
+ 500     68/push  0/imm32/write
+ 501     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
+ 502     # var word-slice/edx: slice
+ 503     68/push  0/imm32/end
+ 504     68/push  0/imm32/start
+ 505     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
+ 506 $read-segments:loop:
+ 507     # clear-stream(line)
+ 508     # . . push args
+ 509     51/push-ecx
+ 510     # . . call
+ 511     e8/call  clear-stream/disp32
+ 512     # . . discard args
+ 513     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+ 514     # read-line-buffered(in, line)
+ 515     # . . push args
+ 516     51/push-ecx
+ 517     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
+ 518     # . . call
+ 519     e8/call  read-line-buffered/disp32
+ 520     # . . discard args
+ 521     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 522 $read-segments:check0:
+ 523     # if (line->write == 0) break
+ 524     81          7/subop/compare     0/mod/indirect  1/rm32/ecx    .           .             .           .           .               0/imm32           # compare *ecx
+ 525     0f 84/jump-if-=  $read-segments:break/disp32
+ 526 +-- 33 lines: #?     # dump line --------------------------------------------------------------------------------------------------------------------------------------------------------
+ 559     # next-word-or-string(line, word-slice)
+ 560     # . . push args
+ 561     52/push-edx
+ 562     51/push-ecx
+ 563     # . . call
+ 564     e8/call  next-word-or-string/disp32
+ 565     # . . discard args
+ 566     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 567 $read-segments:check1:
+ 568 +--  9 lines: #?     # print("check1\n") ------------------------------------------------------------------------------------------------------------------------------------------------
+ 577     # if (slice-empty?(word-slice)) continue
+ 578     # . eax = slice-empty?(word-slice)
+ 579     # . . push args
+ 580     52/push-edx
+ 581     # . . call
+ 582     e8/call  slice-empty?/disp32
+ 583     # . . discard args
+ 584     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+ 585     # . if (eax != false) continue
+ 586     3d/compare-eax-and  0/imm32/false
+ 587     0f 85/jump-if-!=  $read-segments:loop/disp32
+ 588 $read-segments:check-for-comment:
+ 589 +--  9 lines: #?     # print("check for comment\n") -------------------------------------------------------------------------------------------------------------------------------------
+ 598     # if (slice-starts-with?(word-slice, "#")) continue
+ 599     # . var start/esi: (addr byte) = word-slice->start
+ 600     8b/copy                         0/mod/indirect  2/rm32/edx    .           .             .           6/r32/esi   .               .                 # copy *ecx to esi
+ 601     # . var c/eax: byte = *start
+ 602     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
+ 603     8a/copy-byte                    0/mod/indirect  6/rm32/esi    .           .             .           0/r32/AL    .               .                 # copy byte at *esi to AL
+ 604     # . if (c == '#') continue
+ 605     3d/compare-eax-and  0x23/imm32/hash
+ 606     0f 84/jump-if-=  $read-segments:loop/disp32
+ 607 $read-segments:check-for-segment-header:
+ 608 +--  9 lines: #?     # print("check for segment header\n") ------------------------------------------------------------------------------------------------------------------------------
+ 617 +-- 40 lines: #?     # dump word-slice --------------------------------------------------------------------------------------------------------------------------------------------------
+ 657     # if !slice-equal?(word-slice, "==") goto next check
+ 658     # . eax = slice-equal?(word-slice, "==")
+ 659     # . . push args
+ 660     68/push  "=="/imm32
+ 661     52/push-edx
+ 662     # . . call
+ 663     e8/call  slice-equal?/disp32
+ 664     # . . discard args
+ 665     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 666     # . if (eax == false) goto next check
+ 667     3d/compare-eax-and  0/imm32/false
+ 668     0f 84/jump-if-=  $read-segments:regular-line/disp32
+ 669     # segment-name = next-word-or-string(line)
+ 670     # . . push args
+ 671     52/push-edx
+ 672     51/push-ecx
+ 673     # . . call
+ 674     e8/call  next-word-or-string/disp32
+ 675     # . . discard args
+ 676     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 677 +-- 40 lines: #?     # dump segment name ------------------------------------------------------------------------------------------------------------------------------------------------
+ 717     # var segment-slot/edi: (addr handle stream byte) = get-or-insert-slice(table, segment-name, row-size=16, Heap)
+ 718     # . eax = get-or-insert-slice(table, segment-name, row-size=16, Heap)
+ 719     # . . push args
+ 720     68/push  Heap/imm32
+ 721     68/push  0x10/imm32/row-size
+ 722     52/push-edx
+ 723     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
+ 724     # . . call
+ 725     e8/call  get-or-insert-slice/disp32
+ 726     # . . discard args
+ 727     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
+ 728     # . edi = eax
+ 729     89/copy                         3/mod/direct    7/rm32/edi    .           .             .           0/r32/eax   .               .                 # copy eax to edi
+ 730 +-- 33 lines: #?     # print("slot: " segment-slot "\n") --------------------------------------------------------------------------------------------------------------------------------
+ 763     # if (*segment-slot != 0) update curr-segment and continue
+ 764     81          7/subop/compare     0/mod/indirect  7/rm32/edi    .           .             .           .           .               0/imm32           # compare edi
+ 765     0f 84/jump-if-=  $read-segments:create-segment/disp32
+ 766     # var curr-segment/ebx: (addr stream byte) = lookup(*segment-slot)
+ 767     # . eax = lookup(*segment-slot)
+ 768     # . . push args
+ 769     ff          6/subop/push        1/mod/*+disp8   7/rm32/edi    .           .             .           .           4/disp8         .                 # push *(edi+4)
+ 770     ff          6/subop/push        0/mod/indirect  7/rm32/edi    .           .             .           .           .               .                 # push *edi
+ 771     # . . call
+ 772     e8/call  lookup/disp32
+ 773     # . . discard args
+ 774     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 775     # . ebx = eax
+ 776     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # copy eax to ebx
+ 777     # continue
+ 778     e9/jump  $read-segments:loop/disp32
+ 779 $read-segments:create-segment:
+ 780     # new-stream(Heap, Segment-size, 1, segment-slot)
+ 781     # . . push args
+ 782     57/push-edi
+ 783     68/push  1/imm32
+ 784     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Segment-size/disp32               # push *Segment-size
+ 785     68/push  Heap/imm32
+ 786     # . . call
+ 787     e8/call  new-stream/disp32
+ 788     # . . discard args
+ 789     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
+ 790     # var curr-segment/ebx: (addr stream byte) = lookup(*segment-slot)
+ 791     # . eax = lookup(*segment-slot)
+ 792     # . . push args
+ 793     ff          6/subop/push        1/mod/*+disp8   7/rm32/edi    .           .             .           .           4/disp8         .                 # push *(edi+4)
+ 794     ff          6/subop/push        0/mod/indirect  7/rm32/edi    .           .             .           .           .               .                 # push *edi
+ 795     # . . call
+ 796     e8/call  lookup/disp32
+ 797     # . . discard args
+ 798     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 799     # . ebx = eax
+ 800     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # copy eax to ebx
+ 801     # fall through
+ 802 $read-segments:regular-line:
+ 803 +--  9 lines: #?     # print("regular line\n") ------------------------------------------------------------------------------------------------------------------------------------------
+ 812 +-- 33 lines: #?     # dump line --------------------------------------------------------------------------------------------------------------------------------------------------------
+ 845 +-- 33 lines: #?     # print("addr: " curr-segment->write "\n") -------------------------------------------------------------------------------------------------------------------------
+ 878 +-- 33 lines: #?     # print("write: " curr-segment->write "\n") ------------------------------------------------------------------------------------------------------------------------
+ 911 +-- 33 lines: #?     # print("size: " curr-segment->size "\n") --------------------------------------------------------------------------------------------------------------------------
+ 944     # rewind-stream(line)
+ 945     # . . push args
+ 946     51/push-ecx
+ 947     # . . call
+ 948     e8/call  rewind-stream/disp32
+ 949     # . . discard args
+ 950     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+ 951 +--  9 lines: #?     # print("write stream\n") ------------------------------------------------------------------------------------------------------------------------------------------
+ 960     # write-stream(curr-segment, line)
+ 961     # . . push args
+ 962     51/push-ecx
+ 963     53/push-ebx
+ 964     # . . call
+ 965     e8/call  write-stream/disp32
+ 966     # . . discard args
+ 967     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+ 968     # loop
+ 969 +--  9 lines: #?     # print("loop\n") --------------------------------------------------------------------------------------------------------------------------------------------------
+ 978     e9/jump  $read-segments:loop/disp32
+ 979 $read-segments:break:
+ 980 $read-segments:end:
+ 981     # . reclaim locals
+ 982     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x214/imm32       # add to esp
+ 983     # . restore registers
+ 984     5f/pop-to-edi
+ 985     5e/pop-to-esi
+ 986     5b/pop-to-ebx
+ 987     5a/pop-to-edx
+ 988     59/pop-to-ecx
+ 989     58/pop-to-eax
+ 990     # . epilogue
+ 991     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
+ 992     5d/pop-to-ebp
+ 993     c3/return
+ 994 
+ 995 write-segments:  # out: (addr buffered-file), table: (addr stream {(handle array byte), (handle stream byte)})
+ 996     # pseudocode:
+ 997     #   var curr = table->data
+ 998     #   var max = &table->data[table->write]
+ 999     #   while curr < max
+1000     #     var stream: (addr stream byte) = lookup(table[i].stream)
+1001     #     write-stream-data(out, stream)
+1002     #     curr += 16
+1003     #   flush(out)
+1004     #
+1005     # . prologue
+1006     55/push-ebp
+1007     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
+1008     # . save registers
+1009     50/push-eax
+1010     52/push-edx
+1011     56/push-esi
+1012     # esi = table
+1013     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   0xc/disp8       .                 # copy *(ebp+12) to esi
+1014     # var write/edx: int = table->write
+1015     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           2/r32/edx   .               .                 # copy *esi to edx
+1016     # var curr/esi: (addr byte) = table->data
+1017     81          0/subop/add         3/mod/direct    6/rm32/esi    .           .             .           .           .               0xc/imm32         # add to eax
+1018     # var max/edx: (addr byte) = curr + write
+1019     01/add                          3/mod/direct    2/rm32/edx    .           .             .           6/r32/esi   .               .                 # add esi to edx
+1020 $write-segments:loop:
+1021     # if (curr >= max) break
+1022     39/compare                      3/mod/direct    6/rm32/esi    .           .             .           2/r32/edx   .               .                 # compare esi with edx
+1023     73/jump-if-addr>=  $write-segments:break/disp8
+1024     # var stream/eax: (addr stream byte) = lookup(table[i].stream)
+1025     # . . push args
+1026     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           0xc/disp8       .                 # push *(esi+12)
+1027     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           8/disp8         .                 # push *(esi+8)
+1028     # . . call
+1029     e8/call  lookup/disp32
+1030     # . . discard args
+1031     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+1032     # write-stream-data(out, stream)
+1033     # . . push args
+1034     50/push-eax
+1035     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
+1036     # . . call
+1037     e8/call  write-stream-data/disp32
+1038     # . . discard args
+1039     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
+1040 $write-segments:continue:
+1041     # curr += 16
+1042     81          0/subop/add         3/mod/direct    6/rm32/esi    .           .             .           .           .               0x10/imm32        # add to esi
+1043     eb/jump  $write-segments:loop/disp8
+1044 $write-segments:break:
+1045     # flush(out)
+1046     # . . push args
+1047     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
+1048     # . . call
+1049     e8/call  flush/disp32
+1050     # . . discard args
+1051     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
+1052 $write-segments:end:
+1053     # . restore registers
+1054     5e/pop-to-esi
+1055     5a/pop-to-edx
+1056     58/pop-to-eax
+1057     # . epilogue
+1058     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
+1059     5d/pop-to-ebp
+1060     c3/return
+1061 
+1062 # . . vim:nowrap:textwidth=0
 
-- cgit 1.4.1-2-gfad0