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 #   $ ./subx 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  |./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     # . 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