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     # . prolog
 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-lesser-or-equal  $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 == 0) goto interactive
 54     3d/compare-eax-and  0/imm32
 55     74/jump-if-equal  $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: (address stream {string, (address stream byte)})     (8 bytes per row)
 87 # inefficient; uses sequential search for looking up segments by name
 88 
 89 subx-assort:  # in : (address buffered-file), out : (address buffered-file) -> <void>
 90     # pseudocode:
 91     #   var table : (address stream) = new-stream(10 rows, 8 bytes each)
 92     #   read-segments(in, table)
 93     #   write-segments(out, table)
 94     #
 95     # . prolog
 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 : (address stream byte) = stream(10 * 8)
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     # . epilog
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     # . prolog
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+4)
172     # . . push args
173     b8/copy-to-eax  _test-input-buffered-file/imm32
174     05/add-to-eax  4/imm32
175     50/push-eax
176     # . . call
177     e8/call  clear-stream/disp32
178     # . . discard args
179     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
180     # . clear-stream(_test-output-stream)
181     # . . push args
182     68/push  _test-output-stream/imm32
183     # . . call
184     e8/call  clear-stream/disp32
185     # . . discard args
186     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
187     # . clear-stream(_test-output-buffered-file+4)
188     # . . push args
189     b8/copy-to-eax  _test-output-buffered-file/imm32
190     05/add-to-eax  4/imm32
191     50/push-eax
192     # . . call
193     e8/call  clear-stream/disp32
194     # . . discard args
195     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
196     # initialize input (meta comments in parens)
197     #   # comment 1
198     #     # comment 2 indented
199     #   == code 0x09000000  (new segment)
200     #   # comment 3 inside a segment
201     #   1
202     #                         (empty line)
203     #   2 3 # comment 4 inline with other contents
204     #   == data 0x0a000000  (new segment)
205     #   4 5/imm32
206     #   == code  (existing segment but non-contiguous with previous iteration)
207     #   6 7
208     #   8 9  (multiple lines)
209     #   == code  (existing segment contiguous with previous iteration)
210     #   10 11
211     # . write(_test-input-stream, "# comment 1\n")
212     # . . push args
213     68/push  "# comment 1\n"/imm32
214     68/push  _test-input-stream/imm32
215     # . . call
216     e8/call  write/disp32
217     # . . discard args
218     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
219     # . write(_test-input-stream, "  # comment 2 indented\n")
220     # . . push args
221     68/push  "  # comment 2 indented\n"/imm32
222     68/push  _test-input-stream/imm32
223     # . . call
224     e8/call  write/disp32
225     # . . discard args
226     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
227     # . write(_test-input-stream, "== code 0x09000000\n")
228     # . . push args
229     68/push  "== code 0x09000000\n"/imm32
230     68/push  _test-input-stream/imm32
231     # . . call
232     e8/call  write/disp32
233     # . . discard args
234     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
235     # . write(_test-input-stream, "# comment 3 inside a segment\n")
236     # . . push args
237     68/push  "# comment 3 inside a segment\n"/imm32
238     68/push  _test-input-stream/imm32
239     # . . call
240     e8/call  write/disp32
241     # . . discard args
242     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
243     # . write(_test-input-stream, "1\n")
244     # . . push args
245     68/push  "1\n"/imm32
246     68/push  _test-input-stream/imm32
247     # . . call
248     e8/call  write/disp32
249     # . . discard args
250     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
251     # . write(_test-input-stream, "\n")  # empty line
252     # . . push args
253     68/push  Newline/imm32
254     68/push  _test-input-stream/imm32
255     # . . call
256     e8/call  write/disp32
257     # . . discard args
258     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
259     # . write(_test-input-stream, "2 3 # comment 4 inline with other contents\n")
260     # . . push args
261     68/push  "2 3 # comment 4 inline with other contents\n"/imm32
262     68/push  _test-input-stream/imm32
263     # . . call
264     e8/call  write/disp32
265     # . . discard args
266     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
267     # . write(_test-input-stream, "== data 0x0a000000\n")
268     # . . push args
269     68/push  "== data 0x0a000000\n"/imm32
270     68/push  _test-input-stream/imm32
271     # . . call
272     e8/call  write/disp32
273     # . . discard args
274     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
275     # . write(_test-input-stream, "4 5/imm32\n")
276     # . . push args
277     68/push  "4 5/imm32\n"/imm32
278     68/push  _test-input-stream/imm32
279     # . . call
280     e8/call  write/disp32
281     # . . discard args
282     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
283     # . write(_test-input-stream, "== code\n")
284     # . . push args
285     68/push  "== code\n"/imm32
286     68/push  _test-input-stream/imm32
287     # . . call
288     e8/call  write/disp32
289     # . . discard args
290     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
291     # . write(_test-input-stream, "6 7\n")
292     # . . push args
293     68/push  "6 7\n"/imm32
294     68/push  _test-input-stream/imm32
295     # . . call
296     e8/call  write/disp32
297     # . . discard args
298     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
299     # . write(_test-input-stream, "8 9\n")
300     # . . push args
301     68/push  "8 9\n"/imm32
302     68/push  _test-input-stream/imm32
303     # . . call
304     e8/call  write/disp32
305     # . . discard args
306     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
307     # . write(_test-input-stream, "== code\n")
308     # . . push args
309     68/push  "== code\n"/imm32
310     68/push  _test-input-stream/imm32
311     # . . call
312     e8/call  write/disp32
313     # . . discard args
314     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
315     # . write(_test-input-stream, "10 11\n")
316     # . . push args
317     68/push  "10 11\n"/imm32
318     68/push  _test-input-stream/imm32
319     # . . call
320     e8/call  write/disp32
321     # . . discard args
322     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
323     # subx-assort(_test-input-buffered-file, _test-output-buffered-file)
324     # . . push args
325     68/push  _test-output-buffered-file/imm32
326     68/push  _test-input-buffered-file/imm32
327     # . . call
328     e8/call  subx-assort/disp32
329     # . . discard args
330     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
331     # . flush(_test-output-buffered-file)
332     # . . push args
333     68/push  _test-output-buffered-file/imm32
334     # . . call
335     e8/call  flush/disp32
336     # . . discard args
337     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
338     # check output
339     #   == code 0x09000000
340     #   1
341     #   2 3 # comment 4 inline with other contents
342     #   6 7
343     #   8 9
344     #   10 11
345     #   == data 0x0a000000
346     #   4 5/imm32
347 +-- 33 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
380     # . check-next-stream-line-equal(_test-output-stream, "== code 0x09000000", msg)
381     # . . push args
382     68/push  "F - test-subx-assort/0"/imm32
383     68/push  "== code 0x09000000"/imm32
384     68/push  _test-output-stream/imm32
385     # . . call
386     e8/call  check-next-stream-line-equal/disp32
387     # . . discard args
388     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
389     # . check-next-stream-line-equal(_test-output-stream, "1", msg)
390     # . . push args
391     68/push  "F - test-subx-assort/1"/imm32
392     68/push  "1"/imm32
393     68/push  _test-output-stream/imm32
394     # . . call
395     e8/call  check-next-stream-line-equal/disp32
396     # . . discard args
397     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
398     # . check-next-stream-line-equal(_test-output-stream, "2 3 # comment 4 inline with other contents", msg)
399     # . . push args
400     68/push  "F - test-subx-assort/2"/imm32
401     68/push  "2 3 # comment 4 inline with other contents"/imm32
402     68/push  _test-output-stream/imm32
403     # . . call
404     e8/call  check-next-stream-line-equal/disp32
405     # . . discard args
406     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
407     # . check-next-stream-line-equal(_test-output-stream, "6 7", msg)
408     # . . push args
409     68/push  "F - test-subx-assort/3"/imm32
410     68/push  "6 7"/imm32
411     68/push  _test-output-stream/imm32
412     # . . call
413     e8/call  check-next-stream-line-equal/disp32
414     # . . discard args
415     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
416     # . check-next-stream-line-equal(_test-output-stream, "8 9", msg)
417     # . . push args
418     68/push  "F - test-subx-assort/4"/imm32
419     68/push  "8 9"/imm32
420     68/push  _test-output-stream/imm32
421     # . . call
422     e8/call  check-next-stream-line-equal/disp32
423     # . . discard args
424     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
425     # . check-next-stream-line-equal(_test-output-stream, "10 11", msg)
426     # . . push args
427     68/push  "F - test-subx-assort/5"/imm32
428     68/push  "10 11"/imm32
429     68/push  _test-output-stream/imm32
430     # . . call
431     e8/call  check-next-stream-line-equal/disp32
432     # . . discard args
433     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
434     # . check-next-stream-line-equal(_test-output-stream, "== data 0x0a000000", msg)
435     # . . push args
436     68/push  "F - test-subx-assort/6"/imm32
437     68/push  "== data 0x0a000000"/imm32
438     68/push  _test-output-stream/imm32
439     # . . call
440     e8/call  check-next-stream-line-equal/disp32
441     # . . discard args
442     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
443     # . check-next-stream-line-equal(_test-output-stream, "4 5/imm32", msg)
444     # . . push args
445     68/push  "F - test-subx-assort/7"/imm32
446     68/push  "4 5/imm32"/imm32
447     68/push  _test-output-stream/imm32
448     # . . call
449     e8/call  check-next-stream-line-equal/disp32
450     # . . discard args
451     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
452     # . epilog
453     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
454     5d/pop-to-ebp
455     c3/return
456 
457 # beware: leaks memory (one name per segment read)
458 read-segments:  # in : (address buffered-file), table : (address stream {string, (address stream byte)})
459     # pseudocode:
460     #   var curr-segment = null
461     #   var line = new-stream(512, 1)
462     #   while true
463     #     clear-stream(line)
464     #     read-line-buffered(in, line)
465     #     if (line->write == 0) break             # end of file
466     #     var word-slice = next-word(line)
467     #     if slice-empty?(word-slice)             # whitespace
468     #       continue
469     #     if slice-starts-with?(word-slice, "#")  # comment
470     #       continue
471     #     if slice-equal?(word-slice, "==")
472     #       var segment-name = next-word(line)
473     #       segment-slot = leaky-get-or-insert-slice(table, segment-name, row-size=8)
474     #       curr-segment = *segment-slot
475     #       if curr-segment != 0
476     #         continue
477     #       curr-segment = new-stream(Segment-size)
478     #       *segment-slot = curr-segment
479     #     rewind-stream(line)
480     #     write-stream(curr-segment, line)  # abort if curr-segment overflows
481     #
482     # word-slice and segment-name are both slices with disjoint lifetimes, so
483     # we'll use the same address for them.
484     #
485     # registers:
486     #   line: ecx
487     #   word-slice and segment-name: edx
488     #   segment-name and curr-segment: ebx
489     #   word-slice->start: esi
490     #   temporary: eax
491     #
492     # . prolog
493     55/push-ebp
494     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
495     # . save registers
496     50/push-eax
497     51/push-ecx
498     52/push-edx
499     53/push-ebx
500     56/push-esi
501     # var line/ecx : (address stream byte) = stream(512)
502     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x200/imm32       # subtract from esp
503     68/push  0x200/imm32/length
504     68/push  0/imm32/read
505     68/push  0/imm32/write
506     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
507     # var word-slice/edx = {0, 0}
508     68/push  0/imm32/end
509     68/push  0/imm32/start
510     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
511 $read-segments:loop:
512     # clear-stream(line)
513     # . . push args
514     51/push-ecx
515     # . . call
516     e8/call  clear-stream/disp32
517     # . . discard args
518     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
519     # read-line-buffered(in, line)
520     # . . push args
521     51/push-ecx
522     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
523     # . . call
524     e8/call  read-line-buffered/disp32
525     # . . discard args
526     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
527 $read-segments:check0:
528     # if (line->write == 0) break
529     81          7/subop/compare     0/mod/indirect  1/rm32/ecx    .           .             .           .           .               0/imm32           # compare *ecx
530     0f 84/jump-if-equal  $read-segments:break/disp32
531 +-- 33 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
564     # next-word(line, word-slice)
565     # . . push args
566     52/push-edx
567     51/push-ecx
568     # . . call
569     e8/call  next-word/disp32
570     # . . discard args
571     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
572 $read-segments:check1:
573 +--  9 lines: #?     # print("check1\n") ---------------------------------------------------------------------------------------------------------------------
582     # if (slice-empty?(word-slice)) continue
583     # . eax = slice-empty?(word-slice)
584     # . . push args
585     52/push-edx
586     # . . call
587     e8/call  slice-empty?/disp32
588     # . . discard args
589     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
590     # . if (eax != 0) continue
591     3d/compare-eax-and  0/imm32
592     0f 85/jump-if-not-equal  $read-segments:loop/disp32
593 $read-segments:check-for-comment:
594 +--  9 lines: #?     # print("check for comment\n") ----------------------------------------------------------------------------------------------------------
603     # if (slice-starts-with?(word-slice, "#")) continue
604     # . start/esi = word-slice->start
605     8b/copy                         0/mod/indirect  2/rm32/edx    .           .             .           6/r32/esi   .               .                 # copy *ecx to esi
606     # . c/eax = *start
607     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
608     8a/copy-byte                    0/mod/indirect  6/rm32/esi    .           .             .           0/r32/AL    .               .                 # copy byte at *esi to AL
609     # . if (eax == '#') continue
610     3d/compare-eax-and  0x23/imm32/hash
611     0f 84/jump-if-equal  $read-segments:loop/disp32
612 $read-segments:check-for-segment-header:
613 +--  9 lines: #?     # print("check for segment header\n") ---------------------------------------------------------------------------------------------------
622 +-- 42 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
664     # if !slice-equal?(word-slice, "==") goto next check
665     # . eax = slice-equal?(word-slice, "==")
666     # . . push args
667     68/push  "=="/imm32
668     52/push-edx
669     # . . call
670     e8/call  slice-equal?/disp32
671     # . . discard args
672     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
673     # . if (eax == 0) goto check3
674     3d/compare-eax-and  0/imm32
675     0f 84/jump-if-equal  $read-segments:regular-line/disp32
676     # segment-name = next-word(line)
677     # . . push args
678     52/push-edx
679     51/push-ecx
680     # . . call
681     e8/call  next-word/disp32
682     # . . discard args
683     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
684 +-- 42 lines: #?     # dump segment name ---------------------------------------------------------------------------------------------------------------------
726     # segment-slot/eax = leaky-get-or-insert-slice(table, segment-name, row-size=8)
727     # . . push args
728     68/push  8/imm32/row-size
729     52/push-edx
730     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
731     # . . call
732     e8/call  leaky-get-or-insert-slice/disp32
733     # . . discard args
734     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
735     # curr-segment = *segment-slot
736     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           3/r32/ebx   .               .                 # copy *eax to ebx
737     # if (curr-segment != 0) continue
738     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0/imm32           # compare ebx
739     0f 85/jump-if-not-equal  $read-segments:loop/disp32
740     # curr-segment = new-stream(Heap, Segment-size, 1)
741     # . save segment-slot
742     50/push-eax
743     # . eax = new-stream(Heap, Segment-size, 1)
744     # . . push args
745     68/push  1/imm32
746     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Segment-size/disp32               # push *Segment-size
747     68/push  Heap/imm32
748     # . . call
749     e8/call  new-stream/disp32
750     # . . discard args
751     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
752     # . curr-segment = eax
753     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # copy eax to ebx
754     # . restore segment-slot
755     58/pop-to-eax
756     # *segment-slot = curr-segment
757     89/copy                         0/mod/indirect  0/rm32/eax    .           .             .           3/r32/ebx   .               .                 # copy ebx to *eax
758     # fall through
759 $read-segments:regular-line:
760 +--  9 lines: #?     # print("regular line\n") ---------------------------------------------------------------------------------------------------------------
769 +-- 33 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
802     # rewind-stream(line)
803     # . . push args
804     51/push-ecx
805     # . . call
806     e8/call  rewind-stream/disp32
807     # . . discard args
808     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
809 +--  9 lines: #?     # print("write stream\n") ---------------------------------------------------------------------------------------------------------------
818     # write-stream(curr-segment, line)
819     # . . push args
820     51/push-ecx
821     53/push-ebx
822     # . . call
823     e8/call  write-stream/disp32
824     # . . discard args
825     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
826     # loop
827 +--  9 lines: #?     # print("loop\n") -----------------------------------------------------------------------------------------------------------------------
836     e9/jump  $read-segments:loop/disp32
837 $read-segments:break:
838 $read-segments:end:
839     # . reclaim locals
840     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x214/imm32       # add to esp
841     # . restore registers
842     5e/pop-to-esi
843     5b/pop-to-ebx
844     5a/pop-to-edx
845     59/pop-to-ecx
846     58/pop-to-eax
847     # . epilog
848     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
849     5d/pop-to-ebp
850     c3/return
851 
852 write-segments:  # out : (address buffered-file), table : (address stream {string, (address stream byte)})
853     # pseudocode:
854     #   var curr = table->data
855     #   var max = table->data + table->write
856     #   while curr < max
857     #     stream = table[i].stream
858     #     write-stream-data(out, stream)
859     #     curr += 8
860     #   flush(out)
861     #
862     # . prolog
863     55/push-ebp
864     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
865     # . save registers
866     50/push-eax
867     52/push-edx
868     56/push-esi
869     # esi = table
870     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   0xc/disp8       .                 # copy *(ebp+12) to esi
871     # write/edx = table->write
872     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           2/r32/edx   .               .                 # copy *esi to edx
873     # curr/esi = table->data
874     81          0/subop/add         3/mod/direct    6/rm32/esi    .           .             .           .           .               0xc/imm32         # add to eax
875     # max/edx = curr + write
876     01/add                          3/mod/direct    2/rm32/edx    .           .             .           6/r32/esi   .               .                 # add esi to edx
877 $write-segments:loop:
878     # if (curr >= max) break
879     39/compare                      3/mod/direct    6/rm32/esi    .           .             .           2/r32/edx   .               .                 # compare esi with edx
880     73/jump-if-greater-or-equal-unsigned  $write-segments:break/disp8
881     # stream/eax = table[i].stream
882     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           0/r32/eax   4/disp8         .                 # copy *(esi+4) to eax
883     # write-stream-data(out, stream)
884     # . . push args
885     50/push-eax
886     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
887     # . . call
888     e8/call  write-stream-data/disp32
889     # . . discard args
890     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
891 $write-segments:continue:
892     # curr += 8
893     81          0/subop/add         3/mod/direct    6/rm32/esi    .           .             .           .           .               8/imm32           # add to esi
894     eb/jump  $write-segments:loop/disp8
895 $write-segments:break:
896     # flush(out)
897     # . . push args
898     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
899     # . . call
900     e8/call  flush/disp32
901     # . . discard args
902     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
903 $write-segments:end:
904     # . restore registers
905     5e/pop-to-esi
906     5a/pop-to-edx
907     58/pop-to-eax
908     # . epilog
909     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
910     5d/pop-to-ebp
911     c3/return
912 
913 # . . vim:nowrap:textwidth=0