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-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)
 90     # pseudocode:
 91     #   var table : (address stream {string, (address 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 : (address stream byte 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     # . 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 # beware: leaks memory (one name per segment read)
454 read-segments:  # in : (address buffered-file), table : (address stream {string, (address stream byte)})
455     # pseudocode:
456     #   var curr-segment : (handle stream byte) = 0
457     #   var line : (stream byte 512)
458     #   while true
459     #     clear-stream(line)
460     #     read-line-buffered(in, line)
461     #     if (line->write == 0) break             # end of file
462     #     var word-slice = next-word(line)
463     #     if slice-empty?(word-slice)             # whitespace
464     #       continue
465     #     if slice-starts-with?(word-slice, "#")  # comment
466     #       continue
467     #     if slice-equal?(word-slice, "==")
468     #       var segment-name = next-word(line)
469     #       segment-slot = leaky-get-or-insert-slice(table, segment-name, row-size=8)
470     #       curr-segment = *segment-slot
471     #       if curr-segment != 0
472     #         continue
473     #       curr-segment = new-stream(Heap, Segment-size, 1)
474     #       *segment-slot = curr-segment
475     #     rewind-stream(line)
476     #     write-stream(curr-segment, line)  # abort if curr-segment overflows
477     #
478     # word-slice and segment-name are both slices with disjoint lifetimes, so
479     # we'll use the same address for them.
480     #
481     # registers:
482     #   line: ecx
483     #   word-slice and segment-name: edx
484     #   segment-name and curr-segment: ebx
485     #   word-slice->start: esi
486     #   temporary: eax
487     #
488     # . prologue
489     55/push-ebp
490     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
491     # . save registers
492     50/push-eax
493     51/push-ecx
494     52/push-edx
495     53/push-ebx
496     56/push-esi
497     # var line/ecx : (stream byte 512)
498     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x200/imm32       # subtract from esp
499     68/push  0x200/imm32/length
500     68/push  0/imm32/read
501     68/push  0/imm32/write
502     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
503     # var word-slice/edx = {0, 0}
504     68/push  0/imm32/end
505     68/push  0/imm32/start
506     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
507 $read-segments:loop:
508     # clear-stream(line)
509     # . . push args
510     51/push-ecx
511     # . . call
512     e8/call  clear-stream/disp32
513     # . . discard args
514     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
515     # read-line-buffered(in, line)
516     # . . push args
517     51/push-ecx
518     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
519     # . . call
520     e8/call  read-line-buffered/disp32
521     # . . discard args
522     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
523 $read-segments:check0:
524     # if (line->write == 0) break
525     81          7/subop/compare     0/mod/indirect  1/rm32/ecx    .           .             .           .           .               0/imm32           # compare *ecx
526     0f 84/jump-if-equal  $read-segments:break/disp32
527 +-- 33 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
560     # next-word(line, word-slice)
561     # . . push args
562     52/push-edx
563     51/push-ecx
564     # . . call
565     e8/call  next-word/disp32
566     # . . discard args
567     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
568 $read-segments:check1:
569 +--  9 lines: #?     # print("check1\n") ---------------------------------------------------------------------------------------------------------------------
578     # if (slice-empty?(word-slice)) continue
579     # . eax = slice-empty?(word-slice)
580     # . . push args
581     52/push-edx
582     # . . call
583     e8/call  slice-empty?/disp32
584     # . . discard args
585     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
586     # . if (eax != 0) continue
587     3d/compare-eax-and  0/imm32
588     0f 85/jump-if-not-equal  $read-segments:loop/disp32
589 $read-segments:check-for-comment:
590 +--  9 lines: #?     # print("check for comment\n") ----------------------------------------------------------------------------------------------------------
599     # if (slice-starts-with?(word-slice, "#")) continue
600     # . start/esi = word-slice->start
601     8b/copy                         0/mod/indirect  2/rm32/edx    .           .             .           6/r32/esi   .               .                 # copy *ecx to esi
602     # . c/eax = *start
603     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
604     8a/copy-byte                    0/mod/indirect  6/rm32/esi    .           .             .           0/r32/AL    .               .                 # copy byte at *esi to AL
605     # . if (eax == '#') continue
606     3d/compare-eax-and  0x23/imm32/hash
607     0f 84/jump-if-equal  $read-segments:loop/disp32
608 $read-segments:check-for-segment-header:
609 +--  9 lines: #?     # print("check for segment header\n") ---------------------------------------------------------------------------------------------------
618 +-- 40 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
658     # if !slice-equal?(word-slice, "==") goto next check
659     # . eax = slice-equal?(word-slice, "==")
660     # . . push args
661     68/push  "=="/imm32
662     52/push-edx
663     # . . call
664     e8/call  slice-equal?/disp32
665     # . . discard args
666     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
667     # . if (eax == 0) goto check3
668     3d/compare-eax-and  0/imm32
669     0f 84/jump-if-equal  $read-segments:regular-line/disp32
670     # segment-name = next-word(line)
671     # . . push args
672     52/push-edx
673     51/push-ecx
674     # . . call
675     e8/call  next-word/disp32
676     # . . discard args
677     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
678 +-- 40 lines: #?     # dump segment name ---------------------------------------------------------------------------------------------------------------------
718     # segment-slot/eax = leaky-get-or-insert-slice(table, segment-name, row-size=8)
719     # . . push args
720     68/push  8/imm32/row-size
721     52/push-edx
722     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
723     # . . call
724     e8/call  leaky-get-or-insert-slice/disp32
725     # . . discard args
726     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
727     # var curr-segment/ebx : (handle stream byte) = *segment-slot
728     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           3/r32/ebx   .               .                 # copy *eax to ebx
729     # if (curr-segment != 0) continue
730     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0/imm32           # compare ebx
731     0f 85/jump-if-not-equal  $read-segments:loop/disp32
732     # curr-segment = new-stream(Heap, Segment-size, 1)
733     # . save segment-slot
734     50/push-eax
735     # . eax = new-stream(Heap, Segment-size, 1)
736     # . . push args
737     68/push  1/imm32
738     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Segment-size/disp32               # push *Segment-size
739     68/push  Heap/imm32
740     # . . call
741     e8/call  new-stream/disp32
742     # . . discard args
743     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
744     # . curr-segment = eax
745     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # copy eax to ebx
746     # . restore segment-slot
747     58/pop-to-eax
748     # *segment-slot = curr-segment
749     89/copy                         0/mod/indirect  0/rm32/eax    .           .             .           3/r32/ebx   .               .                 # copy ebx to *eax
750     # fall through
751 $read-segments:regular-line:
752 +--  9 lines: #?     # print("regular line\n") ---------------------------------------------------------------------------------------------------------------
761 +-- 33 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
794     # rewind-stream(line)
795     # . . push args
796     51/push-ecx
797     # . . call
798     e8/call  rewind-stream/disp32
799     # . . discard args
800     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
801 +--  9 lines: #?     # print("write stream\n") ---------------------------------------------------------------------------------------------------------------
810     # write-stream(curr-segment, line)
811     # . . push args
812     51/push-ecx
813     53/push-ebx
814     # . . call
815     e8/call  write-stream/disp32
816     # . . discard args
817     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
818     # loop
819 +--  9 lines: #?     # print("loop\n") -----------------------------------------------------------------------------------------------------------------------
828     e9/jump  $read-segments:loop/disp32
829 $read-segments:break:
830 $read-segments:end:
831     # . reclaim locals
832     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x214/imm32       # add to esp
833     # . restore registers
834     5e/pop-to-esi
835     5b/pop-to-ebx
836     5a/pop-to-edx
837     59/pop-to-ecx
838     58/pop-to-eax
839     # . epilogue
840     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
841     5d/pop-to-ebp
842     c3/return
843 
844 write-segments:  # out : (address buffered-file), table : (address stream {string, (address stream byte)})
845     # pseudocode:
846     #   var curr = table->data
847     #   var max = table->data + table->write
848     #   while curr < max
849     #     stream = table[i].stream
850     #     write-stream-data(out, stream)
851     #     curr += 8
852     #   flush(out)
853     #
854     # . prologue
855     55/push-ebp
856     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
857     # . save registers
858     50/push-eax
859     52/push-edx
860     56/push-esi
861     # esi = table
862     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   0xc/disp8       .                 # copy *(ebp+12) to esi
863     # write/edx = table->write
864     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           2/r32/edx   .               .                 # copy *esi to edx
865     # curr/esi = table->data
866     81          0/subop/add         3/mod/direct    6/rm32/esi    .           .             .           .           .               0xc/imm32         # add to eax
867     # max/edx = curr + write
868     01/add                          3/mod/direct    2/rm32/edx    .           .             .           6/r32/esi   .               .                 # add esi to edx
869 $write-segments:loop:
870     # if (curr >= max) break
871     39/compare                      3/mod/direct    6/rm32/esi    .           .             .           2/r32/edx   .               .                 # compare esi with edx
872     73/jump-if-greater-or-equal-unsigned  $write-segments:break/disp8
873     # stream/eax = table[i].stream
874     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           0/r32/eax   4/disp8         .                 # copy *(esi+4) to eax
875     # write-stream-data(out, stream)
876     # . . push args
877     50/push-eax
878     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
879     # . . call
880     e8/call  write-stream-data/disp32
881     # . . discard args
882     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
883 $write-segments:continue:
884     # curr += 8
885     81          0/subop/add         3/mod/direct    6/rm32/esi    .           .             .           .           .               8/imm32           # add to esi
886     eb/jump  $write-segments:loop/disp8
887 $write-segments:break:
888     # flush(out)
889     # . . push args
890     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
891     # . . call
892     e8/call  flush/disp32
893     # . . discard args
894     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
895 $write-segments:end:
896     # . restore registers
897     5e/pop-to-esi
898     5a/pop-to-edx
899     58/pop-to-eax
900     # . epilogue
901     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
902     5d/pop-to-ebp
903     c3/return
904 
905 # . . vim:nowrap:textwidth=0