https://github.com/akkartik/mu/blob/master/subx/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 (from the subx/ directory):
      10 #   $ ./subx translate *.subx apps/assort.subx -o apps/assort
      11 #   $ cat x
      12 #   == code
      13 #   abc
      14 #   == code
      15 #   def
      16 #   $ cat x  |./subx 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
      28     # for debugging: run a single test
      29 #?     e8/call test-convert/disp32
      30 #?     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
      31 #?     eb/jump  $main:end/disp8
      32
      33     # . prolog
      34     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
      35     # - if argc > 1 and argv[1] == "test", then return run_tests()
      36     # . argc > 1
      37     81          7/subop/compare     1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0/disp8         1/imm32           # compare *EBP
      38     7e/jump-if-lesser-or-equal  $run-main/disp8
      39     # . argv[1] == "test"
      40     # . . push args
      41     68/push  "test"/imm32
      42     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
      43     # . . call
      44     e8/call  kernel-string-equal?/disp32
      45     # . . discard args
      46     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
      47     # . check result
      48     3d/compare-EAX-and  1/imm32
      49     75/jump-if-not-equal  $run-main/disp8
      50     # . run-tests()
      51     e8/call  run-tests/disp32
      52     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
      53     eb/jump  $main:end/disp8
      54 $run-main:
      55     # - otherwise convert stdin
      56     # var ed/EAX : exit-descriptor
      57     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
      58     89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EAX
      59     # configure ed to really exit()
      60     # . ed->target = 0
      61     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # copy to *EAX
      62     # return convert(Stdin, 1/stdout, 2/stderr, ed)
      63     # . . push args
      64     50/push-EAX/ed
      65     68/push  Stderr/imm32
      66     68/push  Stdout/imm32
      67     68/push  Stdin/imm32
      68     # . . call
      69     e8/call  convert/disp32
      70     # . . discard args
      71     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
      72     # . syscall(exit, 0)
      73     bb/copy-to-EBX  0/imm32
      74 $main:end:
      75     b8/copy-to-EAX  1/imm32/exit
      76     cd/syscall  0x80/imm8
      77
      78 # data structure:
      79 #   row: pair of (address array byte) and (address stream byte)
      80 #   table: (address stream row)
      81
      82 convert:  # in : (address buffered-file), out : (address buffered-file) -> <void>
      83     # pseudocode:
      84     #   var table : (address stream) = new-stream(10 rows, 8 bytes each)
      85     #   read-segments(in, table)
      86     #   write-segments(out, table)
      87     #
      88     # . prolog
      89     55/push-EBP
      90     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
      91     # . save registers
      92     51/push-ECX
      93     # var table/ECX : (address stream byte) = stream(10 * 8)
      94     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x50/imm32        # subtract from ESP
      95     68/push  0x50/imm32/length
      96     68/push  0/imm32/read
      97     68/push  0/imm32/write
      98     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
      99     # clear-stream(table)
     100     # . . push args
     101     51/push-ECX
     102     # . . call
     103     e8/call  clear-stream/disp32
     104     # . . discard args
     105     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     106 $convert:read:
     107     # read-segments(in, table)
     108     # . . push args
     109     51/push-ECX
     110     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
     111     # . . call
     112     e8/call  read-segments/disp32
     113     # . . discard args
     114     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     115 $convert:write:
     116     # write-segments(out, table)
     117     # . . push args
     118     51/push-ECX
     119     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
     120     # . . call
     121     e8/call  write-segments/disp32
     122     # . . discard args
     123     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     124 $convert:end:
     125     # . reclaim locals
     126     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x5c/imm32        # add to ESP
     127     # . restore registers
     128     59/pop-to-ECX
     129     # . epilog
     130     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
     131     5d/pop-to-EBP
     132     c3/return
     133
     134 test-convert:
     135     # . prolog
     136     55/push-EBP
     137     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
     138     # setup
     139     # . clear-stream(_test-input-stream)
     140     # . . push args
     141     68/push  _test-input-stream/imm32
     142     # . . call
     143     e8/call  clear-stream/disp32
     144     # . . discard args
     145     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     146     # . clear-stream(_test-input-buffered-file+4)
     147     # . . push args
     148     b8/copy-to-EAX  _test-input-buffered-file/imm32
     149     05/add-to-EAX  4/imm32
     150     50/push-EAX
     151     # . . call
     152     e8/call  clear-stream/disp32
     153     # . . discard args
     154     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     155     # . clear-stream(_test-output-stream)
     156     # . . push args
     157     68/push  _test-output-stream/imm32
     158     # . . call
     159     e8/call  clear-stream/disp32
     160     # . . discard args
     161     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     162     # . clear-stream(_test-output-buffered-file+4)
     163     # . . push args
     164     b8/copy-to-EAX  _test-output-buffered-file/imm32
     165     05/add-to-EAX  4/imm32
     166     50/push-EAX
     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     # initialize input (meta comments in parens)
     172     #   # comment 1
     173     #     # comment 2 indented
     174     #   == code  (new segment)
     175     #   # comment 3 inside a segment
     176     #   1
     177     #                         (empty line)
     178     #   2 3 # comment 4 inline with other contents
     179     #   == data  (new segment)
     180     #   4 5/imm32
     181     #   == code  (existing segment but non-contiguous with previous iteration)
     182     #   6 7
     183     #   8 9  (multiple lines)
     184     #   == code  (existing segment contiguous with previous iteration)
     185     #   10 11
     186     # . write(_test-input-stream, "# comment 1\n")
     187     # . . push args
     188     68/push  "# comment 1\n"/imm32
     189     68/push  _test-input-stream/imm32
     190     # . . call
     191     e8/call  write/disp32
     192     # . . discard args
     193     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     194     # . write(_test-input-stream, "  # comment 2 indented\n")
     195     # . . push args
     196     68/push  "  # comment 2 indented\n"/imm32
     197     68/push  _test-input-stream/imm32
     198     # . . call
     199     e8/call  write/disp32
     200     # . . discard args
     201     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     202     # . write(_test-input-stream, "== code\n")
     203     # . . push args
     204     68/push  "== code\n"/imm32
     205     68/push  _test-input-stream/imm32
     206     # . . call
     207     e8/call  write/disp32
     208     # . . discard args
     209     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     210     # . write(_test-input-stream, "# comment 3 inside a segment\n")
     211     # . . push args
     212     68/push  "# comment 3 inside a segment\n"/imm32
     213     68/push  _test-input-stream/imm32
     214     # . . call
     215     e8/call  write/disp32
     216     # . . discard args
     217     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     218     # . write(_test-input-stream, "1\n")
     219     # . . push args
     220     68/push  "1\n"/imm32
     221     68/push  _test-input-stream/imm32
     222     # . . call
     223     e8/call  write/disp32
     224     # . . discard args
     225     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     226     # . write(_test-input-stream, "\n")  # empty line
     227     # . . push args
     228     68/push  "\n"/imm32
     229     68/push  _test-input-stream/imm32
     230     # . . call
     231     e8/call  write/disp32
     232     # . . discard args
     233     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     234     # . write(_test-input-stream, "2 3 # comment 4 inline with other contents\n")
     235     # . . push args
     236     68/push  "2 3 # comment 4 inline with other contents\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  "== 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, "4 5/imm32\n")
     251     # . . push args
     252     68/push  "4 5/imm32\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, "== code\n")
     259     # . . push args
     260     68/push  "== code\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, "6 7\n")
     267     # . . push args
     268     68/push  "6 7\n"/imm32
     269     68/push  _test-input-stream/imm32
     270     # . . call
     271     e8/call  write/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, "8 9\n")
     275     # . . push args
     276     68/push  "8 9\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     # . write(_test-input-stream, "== code\n")
     283     # . . push args
     284     68/push  "== code\n"/imm32
     285     68/push  _test-input-stream/imm32
     286     # . . call
     287     e8/call  write/disp32
     288     # . . discard args
     289     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     290     # . write(_test-input-stream, "10 11\n")
     291     # . . push args
     292     68/push  "10 11\n"/imm32
     293     68/push  _test-input-stream/imm32
     294     # . . call
     295     e8/call  write/disp32
     296     # . . discard args
     297     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     298     # convert(_test-input-buffered-file, _test-output-buffered-file)
     299     # . . push args
     300     68/push  _test-output-buffered-file/imm32
     301     68/push  _test-input-buffered-file/imm32
     302     # . . call
     303     e8/call  convert/disp32
     304     # . . discard args
     305     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     306     # check output
     307     #   == code
     308     #   1
     309     #   2 3 # comment 4 inline with other contents
     310     #   6 7
     311     #   8 9
     312     #   10 11
     313     #   == data
     314     #   4 5/imm32
     315 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
     341     # . flush(_test-output-buffered-file)
     342     # . . push args
     343     68/push  _test-output-buffered-file/imm32
     344     # . . call
     345     e8/call  flush/disp32
     346     # . . discard args
     347     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     348     # . check-next-stream-line-equal(_test-output-stream, "== code", msg)
     349     # . . push args
     350     68/push  "F - test-convert/0"/imm32
     351     68/push  "== code"/imm32
     352     68/push  _test-output-stream/imm32
     353     # . . call
     354     e8/call  check-next-stream-line-equal/disp32
     355     # . . discard args
     356     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
     357     # . check-next-stream-line-equal(_test-output-stream, "1", msg)
     358     # . . push args
     359     68/push  "F - test-convert/1"/imm32
     360     68/push  "1"/imm32
     361     68/push  _test-output-stream/imm32
     362     # . . call
     363     e8/call  check-next-stream-line-equal/disp32
     364     # . . discard args
     365     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
     366     # . check-next-stream-line-equal(_test-output-stream, "2 3 # comment 4 inline with other contents", msg)
     367     # . . push args
     368     68/push  "F - test-convert/2"/imm32
     369     68/push  "2 3 # comment 4 inline with other contents"/imm32
     370     68/push  _test-output-stream/imm32
     371     # . . call
     372     e8/call  check-next-stream-line-equal/disp32
     373     # . . discard args
     374     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
     375     # . check-next-stream-line-equal(_test-output-stream, "6 7", msg)
     376     # . . push args
     377     68/push  "F - test-convert/3"/imm32
     378     68/push  "6 7"/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, "8 9", msg)
     385     # . . push args
     386     68/push  "F - test-convert/4"/imm32
     387     68/push  "8 9"/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, "10 11", msg)
     394     # . . push args
     395     68/push  "F - test-convert/5"/imm32
     396     68/push  "10 11"/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, "== data", msg)
     403     # . . push args
     404     68/push  "F - test-convert/6"/imm32
     405     68/push  "== data"/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, "4 5/imm32", msg)
     412     # . . push args
     413     68/push  "F - test-convert/7"/imm32
     414     68/push  "4 5/imm32"/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     # . epilog
     421     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
     422     5d/pop-to-EBP
     423     c3/return
     424
     425 read-segments:  # in : (address buffered-file), table : (address stream row)
     426     # pseudocode:
     427     #   var curr-segment = null
     428     #   var line = new-stream(512, 1)
     429     #   while true
     430     #     clear-stream(line)
     431     #     read-line(in, line)
     432     #     if (line->write == 0) break             # end of file
     433     #     var word-slice = next-word(line)
     434     #     if slice-empty?(word-slice)             # whitespace
     435     #       continue
     436     #     if slice-starts-with?(word-slice, "#")  # comment
     437     #       continue
     438     #     if slice-equal?(word-slice, "==")
     439     #       var segment-name = next-word(line)
     440     #       curr-segment = get-or-insert-segment(table, segment-name, Segment-size)
     441     #     else
     442     #       rewind-stream(line)
     443     #       write-stream(curr-segment, line)  # abort if curr-segment overflows
     444     #
     445     # word-slice and segment-name are both slices with disjoint lifetimes, so
     446     # we'll use the same address for them.
     447     #
     448     # registers:
     449     #   line: ECX
     450     #   word-slice and segment-name: EDX
     451     #   segment-name and curr-segment: EBX
     452     #   word-slice->start: ESI
     453     #   temporary: EAX
     454     #
     455     # . prolog
     456     55/push-EBP
     457     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
     458     # . save registers
     459     50/push-EAX
     460     51/push-ECX
     461     52/push-EDX
     462     53/push-EBX
     463     56/push-ESI
     464     # var line/ECX : (address stream byte) = stream(512)
     465     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x200/imm32       # subtract from ESP
     466     68/push  0x200/imm32/length
     467     68/push  0/imm32/read
     468     68/push  0/imm32/write
     469     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
     470     # var word-slice/EDX = {0, 0}
     471     68/push  0/imm32/end
     472     68/push  0/imm32/curr
     473     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
     474 $read-segments:loop:
     475     # clear-stream(line)
     476     # . . push args
     477     51/push-ECX
     478     # . . call
     479     e8/call  clear-stream/disp32
     480     # . . discard args
     481     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     482     # read-line(in, line)
     483     # . . push args
     484     51/push-ECX
     485     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
     486     # . . call
     487     e8/call  read-line/disp32
     488     # . . discard args
     489     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     490 $read-segments:check0:
     491     # if (line->write == 0) break
     492     81          7/subop/compare     0/mod/indirect  1/rm32/ECX    .           .             .           .           .               0/imm32           # compare *ECX
     493     0f 84/jump-if-equal  $read-segments:break/disp32
     494 +-- 26 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
     520     # next-word(line, word-slice)
     521     # . . push args
     522     52/push-EDX
     523     51/push-ECX
     524     # . . call
     525     e8/call  next-word/disp32
     526     # . . discard args
     527     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     528 $read-segments:check1:
     529     # if (slice-empty?(word-slice)) continue
     530     # . EAX = slice-empty?(word-slice)
     531     # . . push args
     532     52/push-EDX
     533     # . . call
     534     e8/call  slice-empty?/disp32
     535     # . . discard args
     536     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     537     # . if (EAX != 0) continue
     538     3d/compare-EAX-and  0/imm32
     539     0f 85/jump-if-not-equal  $read-segments:loop/disp32
     540 $read-segments:check-for-comment:
     541     # if (slice-starts-with?(word-slice, "#")) continue
     542     # . start/ESI = word-slice->start
     543     8b/copy                         0/mod/indirect  2/rm32/EDX    .           .             .           6/r32/ESI   .               .                 # copy *ECX to ESI
     544     # . c/EAX = *start
     545     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
     546     8a/copy-byte                    0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/AL    .               .                 # copy byte at *ESI to AL
     547     # . if (EAX == '#') continue
     548     3d/compare-EAX-and  0x23/imm32/hash
     549     0f 84/jump-if-equal  $read-segments:loop/disp32
     550 $read-segments:check-for-segment-header:
     551 +-- 42 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
     593     # if slice-equal?(word-slice, "==")
     594     #   segment-name = next-word(line)
     595     #   curr-segment = get-or-insert(table, segment-name)
     596     # . EAX = slice-equal?(word-slice, "==")
     597     # . . push args
     598     68/push  "=="/imm32
     599     52/push-EDX
     600     # . . call
     601     e8/call  slice-equal?/disp32
     602     # . . discard args
     603     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     604     # . if (EAX == 0) goto check3
     605     3d/compare-EAX-and  0/imm32
     606     0f 84/jump-if-equal  $read-segments:regular-line/disp32
     607     # . next-word(line, segment-name)
     608     # . . push args
     609     52/push-EDX
     610     51/push-ECX
     611     # . . call
     612     e8/call  next-word/disp32
     613     # . . discard args
     614     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     615 +-- 42 lines: #?     # dump segment name ---------------------------------------------------------------------------------------------------------------------
     657     # . EAX = get-or-insert-segment(table, segment-name, Segment-size)
     658     # . . push args
     659     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Segment-size/disp32               # push *Segment-size
     660     52/push-EDX
     661     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
     662     # . . call
     663     e8/call  get-or-insert-segment/disp32
     664     # . . discard args
     665     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
     666     # . curr-segment = EAX
     667     89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EBX
     668     # . continue
     669     e9/jump  $read-segments:loop/disp32
     670 $read-segments:regular-line:
     671     # rewind-stream(line)
     672     # . . push args
     673     51/push-ECX
     674     # . . call
     675     e8/call  rewind-stream/disp32
     676     # . . discard args
     677     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     678     # write-stream(curr-segment, line)
     679     # . . push args
     680     51/push-ECX
     681     53/push-EBX
     682     # . . call
     683     e8/call  write-stream/disp32
     684     # . . discard args
     685     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     686     # loop
     687     e9/jump  $read-segments:loop/disp32
     688 $read-segments:break:
     689 $read-segments:end:
     690     # . reclaim locals
     691     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x214/imm32       # add to ESP
     692     # . restore registers
     693     5e/pop-to-ESI
     694     5b/pop-to-EBX
     695     5a/pop-to-EDX
     696     59/pop-to-ECX
     697     58/pop-to-EAX
     698     # . epilog
     699     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
     700     5d/pop-to-EBP
     701     c3/return
     702
     703 write-segments:  # out : (address buffered-file), table : (address stream row)
     704     # pseudocode:
     705     #   var curr = table->data
     706     #   var max = table->data + table->write
     707     #   while curr < max
     708     #     name = table[i].name
     709     #     print out, "== $name\n"
     710     #     stream = table[i].stream
     711     #     write-stream-data(out, stream)
     712     #     curr += 8
     713     #   flush(out)
     714     #
     715     # . prolog
     716     55/push-EBP
     717     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
     718     # . save registers
     719     50/push-EAX
     720     52/push-EDX
     721     56/push-ESI
     722     # ESI = table
     723     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
     724     # write/EDX = table->write
     725     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           2/r32/EDX   .               .                 # copy *ESI to EDX
     726     # curr/ESI = table->data
     727     81          0/subop/add         3/mod/direct    6/rm32/ESI    .           .             .           .           .               0xc/imm32         # add to EAX
     728     # max/EDX = curr + write
     729     01/add                          3/mod/direct    2/rm32/EDX    .           .             .           6/r32/ESI   .               .                 # add ESI to EDX
     730 $write-segments:loop:
     731     # if (curr >= max) break
     732     39/compare                      3/mod/direct    6/rm32/ESI    .           .             .           2/r32/EDX   .               .                 # compare ESI with EDX
     733     7d/jump-if-greater-or-equal  $write-segments:break/disp8
     734     # name/EAX = table[i].name
     735     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
     736     # print out, "== $name\n"
     737     # . write-buffered(out, "== ")
     738     # . . push args
     739     68/push  "== "/imm32
     740     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
     741     # . . call
     742     e8/call  write-buffered/disp32
     743     # . . discard args
     744     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     745     # . write-buffered(out, name)
     746     # . . push args
     747     50/push-EAX
     748     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
     749     # . . call
     750     e8/call  write-buffered/disp32
     751     # . . discard args
     752     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     753     # . write-buffered(out, "\n")
     754     # . . push args
     755     68/push  Newline/imm32
     756     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
     757     # . . call
     758     e8/call  write-buffered/disp32
     759     # . . discard args
     760     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     761     # stream/EAX = table[i].stream
     762     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ESI+4) to EAX
     763     # write-stream-data(out, stream)
     764     # . . push args
     765     50/push-EAX
     766     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
     767     # . . call
     768     e8/call  write-stream-data/disp32
     769     # . . discard args
     770     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     771 $write-segments:continue:
     772     # curr += 8
     773     81          0/subop/add         3/mod/direct    6/rm32/ESI    .           .             .           .           .               8/imm32           # add to ESI
     774     eb/jump  $write-segments:loop/disp8
     775 $write-segments:break:
     776     # flush(out)
     777     # . . push args
     778     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
     779     # . . call
     780     e8/call  flush/disp32
     781     # . . discard args
     782     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     783 $write-segments:end:
     784     # . restore registers
     785     5e/pop-to-ESI
     786     5a/pop-to-EDX
     787     58/pop-to-EAX
     788     # . epilog
     789     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
     790     5d/pop-to-EBP
     791     c3/return
     792
     793 ## helpers
     794
     795 # TODO: pass in an allocation descriptor
     796 get-or-insert-segment:  # table : (address stream row), s : (address slice), n : int -> EAX : (address stream)
     797     # pseudocode:
     798     #   curr = table->data
     799     #   max = &table->data[table->write]
     800     #   while curr < max
     801     #     if slice-equal?(s, *curr)
     802     #       return *(curr+4)
     803     #     curr += 8
     804     #   if table->write < table->length
     805     #     *max = slice-to-string(Heap, s)
     806     #     result = new-stream(Heap, n, 1)
     807     #     *(max+4) = result
     808     #     table->write += 8
     809     #     return result
     810     #   return 0
     811     #
     812     # . prolog
     813     55/push-EBP
     814     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
     815     # . save registers
     816     51/push-ECX
     817     52/push-EDX
     818     56/push-ESI
     819     # ESI = table
     820     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
     821     # curr/ECX = table->data
     822     8d/copy-address                 1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   0xc/disp8       .                 # copy ESI+12 to ECX
     823     # max/EDX = table->data + table->write
     824     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           2/r32/EDX   .               .                 # copy *ESI to EDX
     825     8d/copy-address                 0/mod/indirect  4/rm32/sib    1/base/ECX  2/index/EDX   .           2/r32/EDX   .               .                 # copy ECX+EDX to EDX
     826 $get-or-insert-segment:search-loop:
     827     # if (curr >= max) break
     828     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
     829     7d/jump-if-greater-or-equal  $get-or-insert-segment:not-found/disp8
     830     # if (slice-equal?(s, *curr)) return *(curr+4)
     831     # . EAX = slice-equal?(s, *curr)
     832     # . . push args
     833     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
     834     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
     835     # . . call
     836     e8/call  slice-equal?/disp32
     837     # . . discard args
     838     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     839     # . if (EAX != 0) return EAX = *(curr+4)
     840     3d/compare-EAX-and  0/imm32
     841     74/jump-if-equal  $get-or-insert-segment:mismatch/disp8
     842     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
     843     eb/jump  $get-or-insert-segment:end/disp8
     844 $get-or-insert-segment:mismatch:
     845     # curr += 8
     846     81          0/subop/add         3/mod/direct    1/rm32/ECX    .           .             .           .           .               8/imm32           # add to ECX
     847     # loop
     848     eb/jump  $get-or-insert-segment:search-loop/disp8
     849 $get-or-insert-segment:not-found:
     850     # result/EAX = 0
     851     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
     852     # if (table->write >= table->length) abort
     853     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # copy *ESI to ECX
     854     3b/compare                      1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   8/disp8         .                 # compare ECX with *(ESI+8)
     855     7d/jump-if-greater-or-equal  $get-or-insert-segment:abort/disp8
     856     # *max = slice-to-string(Heap, s)
     857     # . EAX = slice-to-string(Heap, s)
     858     # . . push args
     859     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
     860     68/push  Heap/imm32
     861     # . . call
     862     e8/call  slice-to-string/disp32
     863     # . . discard args
     864     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     865     # . *max = EAX
     866     89/copy                         0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/EAX   .               .                 # copy EAX to *EDX
     867     # result/EAX = new-stream(Heap, n, 1)
     868     # . . push args
     869     68/push  1/imm32
     870     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
     871     68/push  Heap/imm32
     872     # . . call
     873     e8/call  new-stream/disp32
     874     # . . discard args
     875     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
     876     # *(max+4) = result
     877     89/copy                         1/mod/*+disp8   2/rm32/EDX    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDX+4)
     878     # table->write += 8
     879     81          0/subop/add         0/mod/indirect  6/rm32/ESI    .           .             .           .           .               8/imm32           # add to *ESI
     880 $get-or-insert-segment:end:
     881     # . restore registers
     882     5e/pop-to-ESI
     883     5a/pop-to-EDX
     884     59/pop-to-ECX
     885     # . epilog
     886     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
     887     5d/pop-to-EBP
     888     c3/return
     889
     890 $get-or-insert-segment:abort:
     891     # . _write(2/stderr, error)
     892     # . . push args
     893     68/push  "get-or-insert-segment: too many segments"/imm32
     894     68/push  2/imm32/stderr
     895     # . . call
     896     e8/call  _write/disp32
     897     # . . discard args
     898     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     899     # . syscall(exit, 1)
     900     bb/copy-to-EBX  1/imm32
     901     b8/copy-to-EAX  1/imm32/exit
     902     cd/syscall  0x80/imm8
     903     # never gets here
     904
     905 test-get-or-insert-segment:
     906     # . prolog
     907     55/push-EBP
     908     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
     909     # var table/ECX : (address stream byte) = stream(2 * 8)
     910     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # subtract from ESP
     911     68/push  0x10/imm32/length
     912     68/push  0/imm32/read
     913     68/push  0/imm32/write
     914     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
     915     # EDX : (address slice) = "code"
     916     68/push  _test-code-segment-end/imm32/end
     917     68/push  _test-code-segment/imm32/start
     918     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
     919 $test-get-or-insert-segment:first-call:
     920     # - start with an empty table, insert one segment, verify that it was inserted
     921     # segment/EAX = get-or-insert-segment(table, "code" slice, 10)
     922     # . . push args
     923     68/push  0xa/imm32/segment-length
     924     52/push-EDX
     925     51/push-ECX
     926     # . . call
     927     e8/call  get-or-insert-segment/disp32
     928     # . . discard args
     929     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
     930     # save segment
     931     50/push-EAX
     932     # if (segment != 0) goto next check
     933     3d/compare-EAX-and  0/imm32
     934     75/jump-if-not-equal  $test-get-or-insert-segment:check1/disp8
     935     # fail test
     936     # . _write(2/stderr, msg)
     937     # . . push args
     938     68/push  "F - test-get-or-insert-segment/0\n"/imm32
     939     68/push  2/imm32/stderr
     940     # . . call
     941     e8/call  _write/disp32
     942     # . . discard args
     943     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     944     # . increment Num-test-failures
     945     ff          0/subop/increment   0/mod/indirect  5/rm32/.disp32            .             .           .           Num-test-failures/disp32          # increment *Num-test-failures
     946     e9/jump  $test-get-or-insert-segment:end/disp32
     947 $test-get-or-insert-segment:check1:
     948     # check-ints-equal(segment->length, 10, msg)
     949     # . . push args
     950     68/push  "F - test-get-or-insert-segment/1"/imm32
     951     68/push  0xa/imm32/segment-length
     952     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           8/disp8         .                 # push *(EAX+8)
     953     # . . call
     954     e8/call  check-ints-equal/disp32
     955     # . . discard args
     956     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
     957 $test-get-or-insert-segment:check2:
     958     # check-ints-equal(table->write, rowsize = 8, msg)
     959     # . . push args
     960     68/push  "F - test-get-or-insert-segment/2"/imm32
     961     68/push  8/imm32/row-size
     962     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
     963     # . . call
     964     e8/call  check-ints-equal/disp32
     965     # . . discard args
     966     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
     967     # EAX = string-equal?(*table->data, "code")
     968     # . . push args
     969     68/push  "code"/imm32
     970     ff          6/subop/push        1/mod/*+disp8   1/rm32/ECX    .           .             .           .           0xc/disp8       .                 # push *(ECX+12)
     971     # . . call
     972     e8/call  string-equal?/disp32
     973     # . . discard args
     974     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     975     # check-ints-equal(EAX, 1, msg)
     976     # . . push args
     977     68/push  "F - test-get-or-insert-segment/3"/imm32
     978     68/push  1/imm32
     979     50/push-EAX
     980     # . . call
     981     e8/call  check-ints-equal/disp32
     982     # . . discard args
     983     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
     984 $test-get-or-insert-segment:check3:
     985     # stream/EAX = *(table->data+4)
     986     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   0x10/disp8      .                 # copy *(ECX+16) to EAX
     987     # check-ints-equal(stream->length, 10, msg)
     988     # . . push args
     989     68/push  "F - test-get-or-insert-segment/4"/imm32
     990     68/push  0xa/imm32/segment-size
     991     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           8/disp8         .                 # push *(EAX+8)
     992     # . . call
     993     e8/call  check-ints-equal/disp32
     994     # . . discard args
     995     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
     996 $test-get-or-insert-segment:second-call:
     997     # - insert the same segment name again, verify that it was reused
     998     # segment2/EAX = get-or-insert-segment(table, "code" slice, 8)
     999     # . . push args
    1000     68/push  8/imm32/segment-length
    1001     52/push-EDX
    1002     51/push-ECX
    1003     # . . call
    1004     e8/call  get-or-insert-segment/disp32
    1005     # . . discard args
    1006     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    1007     # restore old segment1
    1008     5a/pop-to-EDX
    1009     # check-ints-equal(segment2/EAX, segment1/EDX, msg)
    1010     # . . push args
    1011     68/push  "F - test-get-or-insert-segment/5"/imm32
    1012     52/push-EDX
    1013     50/push-EAX
    1014     # . . call
    1015     e8/call  check-ints-equal/disp32
    1016     # . . discard args
    1017     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    1018     # no change to table size
    1019     # . check-ints-equal(table->write, rowsize = 8, msg)
    1020     # . . push args
    1021     68/push  "F - test-get-or-insert-segment/6"/imm32
    1022     68/push  8/imm32/row-size
    1023     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
    1024     # . . call
    1025     e8/call  check-ints-equal/disp32
    1026     # . . discard args
    1027     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    1028 $test-get-or-insert-segment:third-call:
    1029     # - insert a new segment name, verify that it was inserted
    1030     # EDX : (address slice) = "data"
    1031     c7          0/subop/copy        0/mod/indirect  2/rm32/EDX    .           .             .           .           .               _test-data-segment/imm32  # copy to *EDX
    1032     c7          0/subop/copy        1/mod/*+disp8   2/rm32/EDX    .           .             .           .           4/disp8         _test-data-segment-end/imm32  # copy to *(EDX+4)
    1033     # segment2/EAX = get-or-insert-segment(table, "data" slice, 8)
    1034     # . . push args
    1035     68/push  8/imm32/segment-length
    1036     52/push-EDX
    1037     51/push-ECX
    1038     # . . call
    1039     e8/call  get-or-insert-segment/disp32
    1040     # . . discard args
    1041     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    1042     # table gets a new row
    1043     # . check-ints-equal(table->write, 2 rows = 16, msg)
    1044     # . . push args
    1045     68/push  "F - test-get-or-insert-segment/7"/imm32
    1046     68/push  0x10/imm32/two-rows
    1047     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
    1048     # . . call
    1049     e8/call  check-ints-equal/disp32
    1050     # . . discard args
    1051     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    1052 $test-get-or-insert-segment:end:
    1053     # . epilog
    1054     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    1055     5d/pop-to-EBP
    1056     c3/return
    1057
    1058 # (re)compute the bounds of the next word in the line
    1059 # return empty string on reaching end of file
    1060 next-word:  # line : (address stream byte), out : (address slice)
    1061     # . prolog
    1062     55/push-EBP
    1063     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    1064     # . save registers
    1065     50/push-EAX
    1066     51/push-ECX
    1067     56/push-ESI
    1068     57/push-EDI
    1069     # ESI = line
    1070     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
    1071     # EDI = out
    1072     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   0xc/disp8       .                 # copy *(EBP+12) to EDI
    1073     # skip-chars-matching(line, ' ')
    1074     # . . push args
    1075     68/push  0x20/imm32/space
    1076     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    1077     # . . call
    1078     e8/call  skip-chars-matching/disp32
    1079     # . . discard args
    1080     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1081 $next-word:check0:
    1082     # if (line->read >= line->write) clear out and return
    1083     # . EAX = line->read
    1084     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ESI+4) to EAX
    1085     # . if (EAX < line->write) goto next check
    1086     3b/compare                      0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # compare EAX with *ESI
    1087     7c/jump-if-lesser  $next-word:check-for-comment/disp8
    1088     # . return out = {0, 0}
    1089     c7          0/subop/copy        0/mod/direct    7/rm32/EDI    .           .             .           .           .               0/imm32           # copy to *EDI
    1090     c7          0/subop/copy        1/mod/*+disp8   7/rm32/EDI    .           .             .           .           4/disp8         0/imm32           # copy to *(EDI+4)
    1091     eb/jump  $next-word:end/disp8
    1092 $next-word:check-for-comment:
    1093     # out->start = &line->data[line->read]
    1094     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
    1095     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/EAX   0xc/disp8       .                 # copy ESI+ECX+12 to EAX
    1096     89/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *EDI
    1097     # if (line->data[line->read] == '#') out->end = &line->data[line->write]), skip rest of stream and return
    1098     # . EAX = line->data[line->read]
    1099     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
    1100     8a/copy-byte                    1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/AL    0xc/disp8       .                 # copy byte at *(ESI+ECX+12) to AL
    1101     # . compare
    1102     3d/compare-EAX-and  0x23/imm32/pound
    1103     75/jump-if-not-equal  $next-word:regular-word/disp8
    1104 $next-word:comment:
    1105     # . out->end = &line->data[line->write]
    1106     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
    1107     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
    1108     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
    1109     # . line->read = line->write
    1110     89/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(ESI+4)
    1111     # . return
    1112     eb/jump  $next-word:end/disp8
    1113 $next-word:regular-word:
    1114     # otherwise skip-chars-not-matching-whitespace(line)  # including trailing newline
    1115     # . . push args
    1116     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    1117     # . . call
    1118     e8/call  skip-chars-not-matching-whitespace/disp32
    1119     # . . discard args
    1120     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1121     # out->end = &line->data[line->read]
    1122     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
    1123     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/EAX   0xc/disp8       .                 # copy ESI+ECX+12 to EAX
    1124     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
    1125 $next-word:end:
    1126     # . restore registers
    1127     5f/pop-to-EDI
    1128     5e/pop-to-ESI
    1129     59/pop-to-ECX
    1130     58/pop-to-EAX
    1131     # . epilog
    1132     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    1133     5d/pop-to-EBP
    1134     c3/return
    1135
    1136 test-next-word:
    1137     # . prolog
    1138     55/push-EBP
    1139     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    1140     # setup
    1141     # . clear-stream(_test-stream)
    1142     # . . push args
    1143     68/push  _test-stream/imm32
    1144     # . . call
    1145     e8/call  clear-stream/disp32
    1146     # . . discard args
    1147     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1148     # var slice/ECX = {0, 0}
    1149     68/push  0/imm32/end
    1150     68/push  0/imm32/start
    1151     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    1152     # write(_test-stream, "  ab")
    1153     # . . push args
    1154     68/push  "  ab"/imm32
    1155     68/push  _test-stream/imm32
    1156     # . . call
    1157     e8/call  write/disp32
    1158     # . . discard args
    1159     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1160     # next-word(_test-stream, slice)
    1161     # . . push args
    1162     51/push-ECX
    1163     68/push  _test-stream/imm32
    1164     # . . call
    1165     e8/call  next-word/disp32
    1166     # . . discard args
    1167     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1168     # check-ints-equal(slice->start - _test-stream->data, 2, msg)
    1169     # . check-ints-equal(slice->start - _test-stream, 14, msg)
    1170     # . . push args
    1171     68/push  "F - test-next-word: start"/imm32
    1172     68/push  0xe/imm32
    1173     # . . push slice->start - _test-stream
    1174     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
    1175     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
    1176     50/push-EAX
    1177     # . . call
    1178     e8/call  check-ints-equal/disp32
    1179     # . . discard args
    1180     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    1181     # check-ints-equal(slice->end - _test-stream->data, 4, msg)
    1182     # . check-ints-equal(slice->end - _test-stream, 16, msg)
    1183     # . . push args
    1184     68/push  "F - test-next-word: end"/imm32
    1185     68/push  0x10/imm32
    1186     # . . push slice->end - _test-stream
    1187     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
    1188     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
    1189     50/push-EAX
    1190     # . . call
    1191     e8/call  check-ints-equal/disp32
    1192     # . . discard args
    1193     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    1194     # . epilog
    1195     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    1196     5d/pop-to-EBP
    1197     c3/return
    1198
    1199 test-next-word-returns-whole-comment:
    1200     # . prolog
    1201     55/push-EBP
    1202     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    1203     # setup
    1204     # . clear-stream(_test-stream)
    1205     # . . push args
    1206     68/push  _test-stream/imm32
    1207     # . . call
    1208     e8/call  clear-stream/disp32
    1209     # . . discard args
    1210     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1211     # var slice/ECX = {0, 0}
    1212     68/push  0/imm32/end
    1213     68/push  0/imm32/start
    1214     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    1215     # write(_test-stream, "  # a")
    1216     # . . push args
    1217     68/push  "  # a"/imm32
    1218     68/push  _test-stream/imm32
    1219     # . . call
    1220     e8/call  write/disp32
    1221     # . . discard args
    1222     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1223     # next-word(_test-stream, slice)
    1224     # . . push args
    1225     51/push-ECX
    1226     68/push  _test-stream/imm32
    1227     # . . call
    1228     e8/call  next-word/disp32
    1229     # . . discard args
    1230     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1231     # check-ints-equal(slice->start - _test-stream->data, 2, msg)
    1232     # . check-ints-equal(slice->start - _test-stream, 14, msg)
    1233     # . . push args
    1234     68/push  "F - test-next-word-returns-whole-comment: start"/imm32
    1235     68/push  0xe/imm32
    1236     # . . push slice->start - _test-stream
    1237     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
    1238     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
    1239     50/push-EAX
    1240     # . . call
    1241     e8/call  check-ints-equal/disp32
    1242     # . . discard args
    1243     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    1244     # check-ints-equal(slice->end - _test-stream->data, 5, msg)
    1245     # . check-ints-equal(slice->end - _test-stream, 17, msg)
    1246     # . . push args
    1247     68/push  "F - test-next-word-returns-whole-comment: end"/imm32
    1248     68/push  0x11/imm32
    1249     # . . push slice->end - _test-stream
    1250     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
    1251     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
    1252     50/push-EAX
    1253     # . . call
    1254     e8/call  check-ints-equal/disp32
    1255     # . . discard args
    1256     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    1257     # . epilog
    1258     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    1259     5d/pop-to-EBP
    1260     c3/return
    1261
    1262 test-next-word-returns-empty-string-on-eof:
    1263     # . prolog
    1264     55/push-EBP
    1265     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    1266     # setup
    1267     # . clear-stream(_test-stream)
    1268     # . . push args
    1269     68/push  _test-stream/imm32
    1270     # . . call
    1271     e8/call  clear-stream/disp32
    1272     # . . discard args
    1273     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1274     # var slice/ECX = {0, 0}
    1275     68/push  0/imm32/end
    1276     68/push  0/imm32/start
    1277     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    1278     # write nothing to _test-stream
    1279     # next-word(_test-stream, slice)
    1280     # . . push args
    1281     51/push-ECX
    1282     68/push  _test-stream/imm32
    1283     # . . call
    1284     e8/call  next-word/disp32
    1285     # . . discard args
    1286     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1287     # check-ints-equal(slice->end - slice->start, 0, msg)
    1288     # . . push args
    1289     68/push  "F - test-next-word-returns-empty-string-on-eof"/imm32
    1290     68/push  0/imm32
    1291     # . . push slice->end - slice->start
    1292     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
    1293     2b/subtract                     0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract *ECX from EAX
    1294     50/push-EAX
    1295     # . . call
    1296     e8/call  check-ints-equal/disp32
    1297     # . . discard args
    1298     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    1299     # . epilog
    1300     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    1301     5d/pop-to-EBP
    1302     c3/return
    1303
    1304 == data
    1305
    1306 _test-code-segment:
    1307   63/c 6f/o 64/d 65/e
    1308 _test-code-segment-end:
    1309
    1310 _test-data-segment:
    1311   64/d 61/a 74/t 61/a
    1312 _test-data-segment-end:
    1313
    1314 Segment-size:
    1315   # TODO: there's currently a tight size limit on segments because we aren't growing the heap
    1316   0x100/imm32
    1317 #?   0x1000/imm32/4KB
    1318
    1319 # . . vim:nowrap:textwidth=0