https://github.com/akkartik/mu/blob/master/subx/apps/assort.subx
  1 # Read a series of segments from stdin and concatenate segments with the same
  2 # name on stdout.
  3 #
  4 # Segments are emitted in order of first encounter.
  5 #
  6 # Drop lines that are all comments. They could get misleading after assortment
  7 # because we don't know if they refer to the line above or the line below.
  8 #
  9 # To run (from the subx/ directory):
 10 #   $ ./subx translate *.subx apps/assort.subx -o apps/assort
 11 #   $ cat x
 12 #   == code
 13 #   abc
 14 #   == code
 15 #   def
 16 #   $ cat x  |./subx run apps/assort
 17 #   == code
 18 #   abc
 19 #   def
 20 
 21 == code
 22 #   instruction                     effective address                                                   register    displacement    immediate
 23 # . op          subop               mod             rm32          base        index         scale       r32
 24 # . 1-3 bytes   3 bits              2 bits          3 bits        3 bits      3 bits        2 bits      2 bits      0/1/2/4 bytes   0/1/2/4 bytes
 25 
 26 Entry:
 27     # initialize heap
 28     # . Heap = new-segment(Heap-size)
 29     # . . push args
 30     68/push  Heap/imm32
 31     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Heap-size/disp32                  # push *Heap-size
 32     # . . call
 33     e8/call  new-segment/disp32
 34     # . . discard args
 35     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 36 
 37     # run tests if necessary, convert stdin if not
 38     # . prolog
 39     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 40     # - if argc > 1 and argv[1] == "test", then return run_tests()
 41     # . argc > 1
 42     81          7/subop/compare     1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0/disp8         1/imm32           # compare *EBP
 43     7e/jump-if-lesser-or-equal  $run-main/disp8
 44     # . argv[1] == "test"
 45     # . . push args
 46     68/push  "test"/imm32
 47     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 48     # . . call
 49     e8/call  kernel-string-equal?/disp32
 50     # . . discard args
 51     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 52     # . check result
 53     3d/compare-EAX-and  1/imm32
 54     75/jump-if-not-equal  $run-main/disp8
 55     # . run-tests()
 56     e8/call  run-tests/disp32
 57     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
 58     eb/jump  $main:end/disp8
 59 $run-main:
 60     # - otherwise convert stdin
 61     # var ed/EAX : exit-descriptor
 62     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
 63     89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EAX
 64     # configure ed to really exit()
 65     # . ed->target = 0
 66     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # copy to *EAX
 67     # return convert(Stdin, 1/stdout, 2/stderr, ed)
 68     # . . push args
 69     50/push-EAX/ed
 70     68/push  Stderr/imm32
 71     68/push  Stdout/imm32
 72     68/push  Stdin/imm32
 73     # . . call
 74     e8/call  convert/disp32
 75     # . . discard args
 76     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
 77     # . syscall(exit, 0)
 78     bb/copy-to-EBX  0/imm32
 79 $main:end:
 80     b8/copy-to-EAX  1/imm32/exit
 81     cd/syscall  0x80/imm8
 82 
 83 # data structure:
 84 #   table: (address stream {string, (address stream byte)})     (8 bytes per row)
 85 # inefficient; uses sequential search for looking up segments by name
 86 
 87 convert:  # in : (address buffered-file), out : (address buffered-file) -> <void>
 88     # pseudocode:
 89     #   var table : (address stream) = new-stream(10 rows, 8 bytes each)
 90     #   read-segments(in, table)
 91     #   write-segments(out, table)
 92     #
 93     # . prolog
 94     55/push-EBP
 95     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 96     # . save registers
 97     51/push-ECX
 98     # var table/ECX : (address stream byte) = stream(10 * 8)
 99     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x50/imm32        # subtract from ESP
100     68/push  0x50/imm32/length
101     68/push  0/imm32/read
102     68/push  0/imm32/write
103     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
104     # clear-stream(table)
105     # . . push args
106     51/push-ECX
107     # . . call
108     e8/call  clear-stream/disp32
109     # . . discard args
110     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
111 $convert:read:
112 +--  9 lines: #?     # print("read\n") -----------------------------------------------------------------------------------------------------------------------
121     # read-segments(in, table)
122     # . . push args
123     51/push-ECX
124     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
125     # . . call
126     e8/call  read-segments/disp32
127     # . . discard args
128     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
129 $convert:write:
130 +--  9 lines: #?     # print("write\n") ----------------------------------------------------------------------------------------------------------------------
139     # write-segments(out, table)
140     # . . push args
141     51/push-ECX
142     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
143     # . . call
144     e8/call  write-segments/disp32
145     # . . discard args
146     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
147 $convert:end:
148     # . reclaim locals
149     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x5c/imm32        # add to ESP
150     # . restore registers
151     59/pop-to-ECX
152     # . epilog
153     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
154     5d/pop-to-EBP
155     c3/return
156 
157 test-convert:
158     # . prolog
159     55/push-EBP
160     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
161     # setup
162     # . clear-stream(_test-input-stream)
163     # . . push args
164     68/push  _test-input-stream/imm32
165     # . . call
166     e8/call  clear-stream/disp32
167     # . . discard args
168     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
169     # . clear-stream(_test-input-buffered-file+4)
170     # . . push args
171     b8/copy-to-EAX  _test-input-buffered-file/imm32
172     05/add-to-EAX  4/imm32
173     50/push-EAX
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+4)
186     # . . push args
187     b8/copy-to-EAX  _test-output-buffered-file/imm32
188     05/add-to-EAX  4/imm32
189     50/push-EAX
190     # . . call
191     e8/call  clear-stream/disp32
192     # . . discard args
193     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
194     # initialize input (meta comments in parens)
195     #   # comment 1
196     #     # comment 2 indented
197     #   == code 0x09000000  (new segment)
198     #   # comment 3 inside a segment
199     #   1
200     #                         (empty line)
201     #   2 3 # comment 4 inline with other contents
202     #   == data 0x0a000000  (new segment)
203     #   4 5/imm32
204     #   == code  (existing segment but non-contiguous with previous iteration)
205     #   6 7
206     #   8 9  (multiple lines)
207     #   == code  (existing segment contiguous with previous iteration)
208     #   10 11
209     # . write(_test-input-stream, "# comment 1\n")
210     # . . push args
211     68/push  "# comment 1\n"/imm32
212     68/push  _test-input-stream/imm32
213     # . . call
214     e8/call  write/disp32
215     # . . discard args
216     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
217     # . write(_test-input-stream, "  # comment 2 indented\n")
218     # . . push args
219     68/push  "  # comment 2 indented\n"/imm32
220     68/push  _test-input-stream/imm32
221     # . . call
222     e8/call  write/disp32
223     # . . discard args
224     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
225     # . write(_test-input-stream, "== code 0x09000000\n")
226     # . . push args
227     68/push  "== code 0x09000000\n"/imm32
228     68/push  _test-input-stream/imm32
229     # . . call
230     e8/call  write/disp32
231     # . . discard args
232     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
233     # . write(_test-input-stream, "# comment 3 inside a segment\n")
234     # . . push args
235     68/push  "# comment 3 inside a segment\n"/imm32
236     68/push  _test-input-stream/imm32
237     # . . call
238     e8/call  write/disp32
239     # . . discard args
240     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
241     # . write(_test-input-stream, "1\n")
242     # . . push args
243     68/push  "1\n"/imm32
244     68/push  _test-input-stream/imm32
245     # . . call
246     e8/call  write/disp32
247     # . . discard args
248     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
249     # . write(_test-input-stream, "\n")  # empty line
250     # . . push args
251     68/push  "\n"/imm32
252     68/push  _test-input-stream/imm32
253     # . . call
254     e8/call  write/disp32
255     # . . discard args
256     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
257     # . write(_test-input-stream, "2 3 # comment 4 inline with other contents\n")
258     # . . push args
259     68/push  "2 3 # comment 4 inline with other contents\n"/imm32
260     68/push  _test-input-stream/imm32
261     # . . call
262     e8/call  write/disp32
263     # . . discard args
264     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
265     # . write(_test-input-stream, "== data 0x0a000000\n")
266     # . . push args
267     68/push  "== data 0x0a000000\n"/imm32
268     68/push  _test-input-stream/imm32
269     # . . call
270     e8/call  write/disp32
271     # . . discard args
272     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
273     # . write(_test-input-stream, "4 5/imm32\n")
274     # . . push args
275     68/push  "4 5/imm32\n"/imm32
276     68/push  _test-input-stream/imm32
277     # . . call
278     e8/call  write/disp32
279     # . . discard args
280     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
281     # . write(_test-input-stream, "== code\n")
282     # . . push args
283     68/push  "== code\n"/imm32
284     68/push  _test-input-stream/imm32
285     # . . call
286     e8/call  write/disp32
287     # . . discard args
288     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
289     # . write(_test-input-stream, "6 7\n")
290     # . . push args
291     68/push  "6 7\n"/imm32
292     68/push  _test-input-stream/imm32
293     # . . call
294     e8/call  write/disp32
295     # . . discard args
296     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
297     # . write(_test-input-stream, "8 9\n")
298     # . . push args
299     68/push  "8 9\n"/imm32
300     68/push  _test-input-stream/imm32
301     # . . call
302     e8/call  write/disp32
303     # . . discard args
304     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
305     # . write(_test-input-stream, "== code\n")
306     # . . push args
307     68/push  "== code\n"/imm32
308     68/push  _test-input-stream/imm32
309     # . . call
310     e8/call  write/disp32
311     # . . discard args
312     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
313     # . write(_test-input-stream, "10 11\n")
314     # . . push args
315     68/push  "10 11\n"/imm32
316     68/push  _test-input-stream/imm32
317     # . . call
318     e8/call  write/disp32
319     # . . discard args
320     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
321     # convert(_test-input-buffered-file, _test-output-buffered-file)
322     # . . push args
323     68/push  _test-output-buffered-file/imm32
324     68/push  _test-input-buffered-file/imm32
325     # . . call
326     e8/call  convert/disp32
327     # . . discard args
328     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
329     # . flush(_test-output-buffered-file)
330     # . . push args
331     68/push  _test-output-buffered-file/imm32
332     # . . call
333     e8/call  flush/disp32
334     # . . discard args
335     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
336     # check output
337     #   == code 0x09000000
338     #   1
339     #   2 3 # comment 4 inline with other contents
340     #   6 7
341     #   8 9
342     #   10 11
343     #   == data 0x0a000000
344     #   4 5/imm32
345 +-- 33 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
378     # . check-next-stream-line-equal(_test-output-stream, "== code 0x09000000", msg)
379     # . . push args
380     68/push  "F - test-convert/0"/imm32
381     68/push  "== code 0x09000000"/imm32
382     68/push  _test-output-stream/imm32
383     # . . call
384     e8/call  check-next-stream-line-equal/disp32
385     # . . discard args
386     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
387     # . check-next-stream-line-equal(_test-output-stream, "1", msg)
388     # . . push args
389     68/push  "F - test-convert/1"/imm32
390     68/push  "1"/imm32
391     68/push  _test-output-stream/imm32
392     # . . call
393     e8/call  check-next-stream-line-equal/disp32
394     # . . discard args
395     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
396     # . check-next-stream-line-equal(_test-output-stream, "2 3 # comment 4 inline with other contents", msg)
397     # . . push args
398     68/push  "F - test-convert/2"/imm32
399     68/push  "2 3 # comment 4 inline with other contents"/imm32
400     68/push  _test-output-stream/imm32
401     # . . call
402     e8/call  check-next-stream-line-equal/disp32
403     # . . discard args
404     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
405     # . check-next-stream-line-equal(_test-output-stream, "6 7", msg)
406     # . . push args
407     68/push  "F - test-convert/3"/imm32
408     68/push  "6 7"/imm32
409     68/push  _test-output-stream/imm32
410     # . . call
411     e8/call  check-next-stream-line-equal/disp32
412     # . . discard args
413     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
414     # . check-next-stream-line-equal(_test-output-stream, "8 9", msg)
415     # . . push args
416     68/push  "F - test-convert/4"/imm32
417     68/push  "8 9"/imm32
418     68/push  _test-output-stream/imm32
419     # . . call
420     e8/call  check-next-stream-line-equal/disp32
421     # . . discard args
422     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
423     # . check-next-stream-line-equal(_test-output-stream, "10 11", msg)
424     # . . push args
425     68/push  "F - test-convert/5"/imm32
426     68/push  "10 11"/imm32
427     68/push  _test-output-stream/imm32
428     # . . call
429     e8/call  check-next-stream-line-equal/disp32
430     # . . discard args
431     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
432     # . check-next-stream-line-equal(_test-output-stream, "== data 0x0a000000", msg)
433     # . . push args
434     68/push  "F - test-convert/6"/imm32
435     68/push  "== data 0x0a000000"/imm32
436     68/push  _test-output-stream/imm32
437     # . . call
438     e8/call  check-next-stream-line-equal/disp32
439     # . . discard args
440     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
441     # . check-next-stream-line-equal(_test-output-stream, "4 5/imm32", msg)
442     # . . push args
443     68/push  "F - test-convert/7"/imm32
444     68/push  "4 5/imm32"/imm32
445     68/push  _test-output-stream/imm32
446     # . . call
447     e8/call  check-next-stream-line-equal/disp32
448     # . . discard args
449     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
450     # . epilog
451     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
452     5d/pop-to-EBP
453     c3/return
454 
455 # beware: leaks memory (one name per segment read)
456 read-segments:  # in : (address buffered-file), table : (address stream {string, (address stream byte)})
457     # pseudocode:
458     #   var curr-segment = null
459     #   var line = new-stream(512, 1)
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(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(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(Segment-size)
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     # . prolog
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 : (address stream byte) = stream(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 = {0, 0}
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-equal  $read-segments:break/disp32
529 +-- 33 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
562     # next-word(line, word-slice)
563     # . . push args
564     52/push-EDX
565     51/push-ECX
566     # . . call
567     e8/call  next-word/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 != 0) continue
589     3d/compare-EAX-and  0/imm32
590     0f 85/jump-if-not-equal  $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     # . start/ESI = word-slice->start
603     8b/copy                         0/mod/indirect  2/rm32/EDX    .           .             .           6/r32/ESI   .               .                 # copy *ECX to ESI
604     # . c/EAX = *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 (EAX == '#') continue
608     3d/compare-EAX-and  0x23/imm32/hash
609     0f 84/jump-if-equal  $read-segments:loop/disp32
610 $read-segments:check-for-segment-header:
611 +--  9 lines: #?     # print("check for segment header\n") ---------------------------------------------------------------------------------------------------
620 +-- 42 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
662     # if !slice-equal?(word-slice, "==") goto next check
663     # . EAX = slice-equal?(word-slice, "==")
664     # . . push args
665     68/push  "=="/imm32
666     52/push-EDX
667     # . . call
668     e8/call  slice-equal?/disp32
669     # . . discard args
670     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
671     # . if (EAX == 0) goto check3
672     3d/compare-EAX-and  0/imm32
673     0f 84/jump-if-equal  $read-segments:regular-line/disp32
674     # segment-name = next-word(line)
675     # . . push args
676     52/push-EDX
677     51/push-ECX
678     # . . call
679     e8/call  next-word/disp32
680     # . . discard args
681     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
682 +-- 42 lines: #?     # dump segment name ---------------------------------------------------------------------------------------------------------------------
724     # segment-slot/EAX = leaky-get-or-insert-slice(table, segment-name, row-size=8)
725     # . . push args
726     68/push  8/imm32/row-size
727     52/push-EDX
728     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
729     # . . call
730     e8/call  leaky-get-or-insert-slice/disp32
731     # . . discard args
732     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
733     # curr-segment = *segment-slot
734     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           3/r32/EBX   .               .                 # copy *EAX to EBX
735     # if (curr-segment != 0) continue
736     81          7/subop/compare     3/mod/direct    3/rm32/EBX    .           .             .           .           .               0/imm32           # compare EBX
737     0f 85/jump-if-not-equal  $read-segments:loop/disp32
738     # curr-segment = new-stream(Heap, Segment-size, 1)
739     # . save segment-slot
740     50/push-EAX
741     # . EAX = new-stream(Heap, Segment-size, 1)
742     # . . push args
743     68/push  1/imm32
744     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Segment-size/disp32               # push *Segment-size
745     68/push  Heap/imm32
746     # . . call
747     e8/call  new-stream/disp32
748     # . . discard args
749     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
750     # . curr-segment = EAX
751     89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EBX
752     # . restore segment-slot
753     58/pop-to-EAX
754     # *segment-slot = curr-segment
755     89/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           3/r32/EBX   .               .                 # copy EBX to *EAX
756     # fall through
757 $read-segments:regular-line:
758 +--  9 lines: #?     # print("regular line\n") ---------------------------------------------------------------------------------------------------------------
767 +-- 33 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
800     # rewind-stream(line)
801     # . . push args
802     51/push-ECX
803     # . . call
804     e8/call  rewind-stream/disp32
805     # . . discard args
806     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
807 +--  9 lines: #?     # print("write stream\n") ---------------------------------------------------------------------------------------------------------------
816     # write-stream(curr-segment, line)
817     # . . push args
818     51/push-ECX
819     53/push-EBX
820     # . . call
821     e8/call  write-stream/disp32
822     # . . discard args
823     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
824     # loop
825 +--  9 lines: #?     # print("loop\n") -----------------------------------------------------------------------------------------------------------------------
834     e9/jump  $read-segments:loop/disp32
835 $read-segments:break:
836 $read-segments:end:
837     # . reclaim locals
838     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x214/imm32       # add to ESP
839     # . restore registers
840     5e/pop-to-ESI
841     5b/pop-to-EBX
842     5a/pop-to-EDX
843     59/pop-to-ECX
844     58/pop-to-EAX
845     # . epilog
846     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
847     5d/pop-to-EBP
848     c3/return
849 
850 write-segments:  # out : (address buffered-file), table : (address stream {string, (address stream byte)})
851     # pseudocode:
852     #   var curr = table->data
853     #   var max = table->data + table->write
854     #   while curr < max
855     #     stream = table[i].stream
856     #     write-stream-data(out, stream)
857     #     curr += 8
858     #   flush(out)
859     #
860     # . prolog
861     55/push-EBP
862     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
863     # . save registers
864     50/push-EAX
865     52/push-EDX
866     56/push-ESI
867     # ESI = table
868     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
869     # write/EDX = table->write
870     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           2/r32/EDX   .               .                 # copy *ESI to EDX
871     # curr/ESI = table->data
872     81          0/subop/add         3/mod/direct    6/rm32/ESI    .           .             .           .           .               0xc/imm32         # add to EAX
873     # max/EDX = curr + write
874     01/add                          3/mod/direct    2/rm32/EDX    .           .             .           6/r32/ESI   .               .                 # add ESI to EDX
875 $write-segments:loop:
876     # if (curr >= max) break
877     39/compare                      3/mod/direct    6/rm32/ESI    .           .             .           2/r32/EDX   .               .                 # compare ESI with EDX
878     73/jump-if-greater-or-equal-unsigned  $write-segments:break/disp8
879     # stream/EAX = table[i].stream
880     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ESI+4) to EAX
881     # write-stream-data(out, stream)
882     # . . push args
883     50/push-EAX
884     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
885     # . . call
886     e8/call  write-stream-data/disp32
887     # . . discard args
888     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
889 $write-segments:continue:
890     # curr += 8
891     81          0/subop/add         3/mod/direct    6/rm32/ESI    .           .             .           .           .               8/imm32           # add to ESI
892     eb/jump  $write-segments:loop/disp8
893 $write-segments:break:
894     # flush(out)
895     # . . push args
896     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
897     # . . call
898     e8/call  flush/disp32
899     # . . discard args
900     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
901 $write-segments:end:
902     # . restore registers
903     5e/pop-to-ESI
904     5a/pop-to-EDX
905     58/pop-to-EAX
906     # . epilog
907     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
908     5d/pop-to-EBP
909     c3/return
910 
911 # . . vim:nowrap:textwidth=0