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(64KB)
 29     # . . push args
 30     68/push  Heap/imm32
 31     68/push  0x10000/imm32/64KB
 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     # read-segments(in, table)
113     # . . push args
114     51/push-ECX
115     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
116     # . . call
117     e8/call  read-segments/disp32
118     # . . discard args
119     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
120 $convert:write:
121     # write-segments(out, table)
122     # . . push args
123     51/push-ECX
124     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
125     # . . call
126     e8/call  write-segments/disp32
127     # . . discard args
128     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
129 $convert:end:
130     # . reclaim locals
131     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x5c/imm32        # add to ESP
132     # . restore registers
133     59/pop-to-ECX
134     # . epilog
135     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
136     5d/pop-to-EBP
137     c3/return
138 
139 test-convert:
140     # . prolog
141     55/push-EBP
142     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
143     # setup
144     # . clear-stream(_test-input-stream)
145     # . . push args
146     68/push  _test-input-stream/imm32
147     # . . call
148     e8/call  clear-stream/disp32
149     # . . discard args
150     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
151     # . clear-stream(_test-input-buffered-file+4)
152     # . . push args
153     b8/copy-to-EAX  _test-input-buffered-file/imm32
154     05/add-to-EAX  4/imm32
155     50/push-EAX
156     # . . call
157     e8/call  clear-stream/disp32
158     # . . discard args
159     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
160     # . clear-stream(_test-output-stream)
161     # . . push args
162     68/push  _test-output-stream/imm32
163     # . . call
164     e8/call  clear-stream/disp32
165     # . . discard args
166     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
167     # . clear-stream(_test-output-buffered-file+4)
168     # . . push args
169     b8/copy-to-EAX  _test-output-buffered-file/imm32
170     05/add-to-EAX  4/imm32
171     50/push-EAX
172     # . . call
173     e8/call  clear-stream/disp32
174     # . . discard args
175     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
176     # initialize input (meta comments in parens)
177     #   # comment 1
178     #     # comment 2 indented
179     #   == code 0x09000000  (new segment)
180     #   # comment 3 inside a segment
181     #   1
182     #                         (empty line)
183     #   2 3 # comment 4 inline with other contents
184     #   == data 0x0a000000  (new segment)
185     #   4 5/imm32
186     #   == code  (existing segment but non-contiguous with previous iteration)
187     #   6 7
188     #   8 9  (multiple lines)
189     #   == code  (existing segment contiguous with previous iteration)
190     #   10 11
191     # . write(_test-input-stream, "# comment 1\n")
192     # . . push args
193     68/push  "# comment 1\n"/imm32
194     68/push  _test-input-stream/imm32
195     # . . call
196     e8/call  write/disp32
197     # . . discard args
198     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
199     # . write(_test-input-stream, "  # comment 2 indented\n")
200     # . . push args
201     68/push  "  # comment 2 indented\n"/imm32
202     68/push  _test-input-stream/imm32
203     # . . call
204     e8/call  write/disp32
205     # . . discard args
206     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
207     # . write(_test-input-stream, "== code 0x09000000\n")
208     # . . push args
209     68/push  "== code 0x09000000\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 3 inside a segment\n")
216     # . . push args
217     68/push  "# comment 3 inside a segment\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, "1\n")
224     # . . push args
225     68/push  "1\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, "\n")  # empty line
232     # . . push args
233     68/push  "\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, "2 3 # comment 4 inline with other contents\n")
240     # . . push args
241     68/push  "2 3 # comment 4 inline with other contents\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, "== data 0x0a000000\n")
248     # . . push args
249     68/push  "== data 0x0a000000\n"/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, "4 5/imm32\n")
256     # . . push args
257     68/push  "4 5/imm32\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, "== code\n")
264     # . . push args
265     68/push  "== code\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, "6 7\n")
272     # . . push args
273     68/push  "6 7\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, "8 9\n")
280     # . . push args
281     68/push  "8 9\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, "== code\n")
288     # . . push args
289     68/push  "== code\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, "10 11\n")
296     # . . push args
297     68/push  "10 11\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     # convert(_test-input-buffered-file, _test-output-buffered-file)
304     # . . push args
305     68/push  _test-output-buffered-file/imm32
306     68/push  _test-input-buffered-file/imm32
307     # . . call
308     e8/call  convert/disp32
309     # . . discard args
310     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
311     # . flush(_test-output-buffered-file)
312     # . . push args
313     68/push  _test-output-buffered-file/imm32
314     # . . call
315     e8/call  flush/disp32
316     # . . discard args
317     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
318     # check output
319     #   == code 0x09000000
320     #   1
321     #   2 3 # comment 4 inline with other contents
322     #   6 7
323     #   8 9
324     #   10 11
325     #   == data 0x0a000000
326     #   4 5/imm32
327 +-- 33 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
360     # . check-next-stream-line-equal(_test-output-stream, "== code 0x09000000", msg)
361     # . . push args
362     68/push  "F - test-convert/0"/imm32
363     68/push  "== code 0x09000000"/imm32
364     68/push  _test-output-stream/imm32
365     # . . call
366     e8/call  check-next-stream-line-equal/disp32
367     # . . discard args
368     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
369     # . check-next-stream-line-equal(_test-output-stream, "1", msg)
370     # . . push args
371     68/push  "F - test-convert/1"/imm32
372     68/push  "1"/imm32
373     68/push  _test-output-stream/imm32
374     # . . call
375     e8/call  check-next-stream-line-equal/disp32
376     # . . discard args
377     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
378     # . check-next-stream-line-equal(_test-output-stream, "2 3 # comment 4 inline with other contents", msg)
379     # . . push args
380     68/push  "F - test-convert/2"/imm32
381     68/push  "2 3 # comment 4 inline with other contents"/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, "6 7", msg)
388     # . . push args
389     68/push  "F - test-convert/3"/imm32
390     68/push  "6 7"/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, "8 9", msg)
397     # . . push args
398     68/push  "F - test-convert/4"/imm32
399     68/push  "8 9"/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, "10 11", msg)
406     # . . push args
407     68/push  "F - test-convert/5"/imm32
408     68/push  "10 11"/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, "== data 0x0a000000", msg)
415     # . . push args
416     68/push  "F - test-convert/6"/imm32
417     68/push  "== data 0x0a000000"/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, "4 5/imm32", msg)
424     # . . push args
425     68/push  "F - test-convert/7"/imm32
426     68/push  "4 5/imm32"/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     # . epilog
433     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
434     5d/pop-to-EBP
435     c3/return
436 
437 # beware: leaks memory (one name per segment read)
438 read-segments:  # in : (address buffered-file), table : (address stream {string, (address stream byte)})
439     # pseudocode:
440     #   var curr-segment = null
441     #   var line = new-stream(512, 1)
442     #   while true
443     #     clear-stream(line)
444     #     read-line-buffered(in, line)
445     #     if (line->write == 0) break             # end of file
446     #     var word-slice = next-word(line)
447     #     if slice-empty?(word-slice)             # whitespace
448     #       continue
449     #     if slice-starts-with?(word-slice, "#")  # comment
450     #       continue
451     #     if slice-equal?(word-slice, "==")
452     #       var segment-name = next-word(line)
453     #       segment-slot = leaky-get-or-insert-slice(table, segment-name, row-size=8)
454     #       curr-segment = *segment-slot
455     #       if curr-segment != 0
456     #         continue
457     #       curr-segment = new-stream(Segment-size)
458     #       *segment-slot = curr-segment
459     #     rewind-stream(line)
460     #     write-stream(curr-segment, line)  # abort if curr-segment overflows
461     #
462     # word-slice and segment-name are both slices with disjoint lifetimes, so
463     # we'll use the same address for them.
464     #
465     # registers:
466     #   line: ECX
467     #   word-slice and segment-name: EDX
468     #   segment-name and curr-segment: EBX
469     #   word-slice->start: ESI
470     #   temporary: EAX
471     #
472     # . prolog
473     55/push-EBP
474     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
475     # . save registers
476     50/push-EAX
477     51/push-ECX
478     52/push-EDX
479     53/push-EBX
480     56/push-ESI
481     # var line/ECX : (address stream byte) = stream(512)
482     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x200/imm32       # subtract from ESP
483     68/push  0x200/imm32/length
484     68/push  0/imm32/read
485     68/push  0/imm32/write
486     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
487     # var word-slice/EDX = {0, 0}
488     68/push  0/imm32/end
489     68/push  0/imm32/start
490     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
491 $read-segments:loop:
492     # clear-stream(line)
493     # . . push args
494     51/push-ECX
495     # . . call
496     e8/call  clear-stream/disp32
497     # . . discard args
498     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
499     # read-line-buffered(in, line)
500     # . . push args
501     51/push-ECX
502     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
503     # . . call
504     e8/call  read-line-buffered/disp32
505     # . . discard args
506     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
507 $read-segments:check0:
508     # if (line->write == 0) break
509     81          7/subop/compare     0/mod/indirect  1/rm32/ECX    .           .             .           .           .               0/imm32           # compare *ECX
510     0f 84/jump-if-equal  $read-segments:break/disp32
511 +-- 26 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
537     # next-word(line, word-slice)
538     # . . push args
539     52/push-EDX
540     51/push-ECX
541     # . . call
542     e8/call  next-word/disp32
543     # . . discard args
544     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
545 $read-segments:check1:
546     # if (slice-empty?(word-slice)) continue
547     # . EAX = slice-empty?(word-slice)
548     # . . push args
549     52/push-EDX
550     # . . call
551     e8/call  slice-empty?/disp32
552     # . . discard args
553     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
554     # . if (EAX != 0) continue
555     3d/compare-EAX-and  0/imm32
556     0f 85/jump-if-not-equal  $read-segments:loop/disp32
557 $read-segments:check-for-comment:
558     # if (slice-starts-with?(word-slice, "#")) continue
559     # . start/ESI = word-slice->start
560     8b/copy                         0/mod/indirect  2/rm32/EDX    .           .             .           6/r32/ESI   .               .                 # copy *ECX to ESI
561     # . c/EAX = *start
562     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
563     8a/copy-byte                    0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/AL    .               .                 # copy byte at *ESI to AL
564     # . if (EAX == '#') continue
565     3d/compare-EAX-and  0x23/imm32/hash
566     0f 84/jump-if-equal  $read-segments:loop/disp32
567 $read-segments:check-for-segment-header:
568 +-- 42 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
610     # if !slice-equal?(word-slice, "==") goto next check
611     # . EAX = slice-equal?(word-slice, "==")
612     # . . push args
613     68/push  "=="/imm32
614     52/push-EDX
615     # . . call
616     e8/call  slice-equal?/disp32
617     # . . discard args
618     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
619     # . if (EAX == 0) goto check3
620     3d/compare-EAX-and  0/imm32
621     0f 84/jump-if-equal  $read-segments:regular-line/disp32
622     # segment-name = next-word(line)
623     # . . push args
624     52/push-EDX
625     51/push-ECX
626     # . . call
627     e8/call  next-word/disp32
628     # . . discard args
629     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
630 +-- 42 lines: #?     # dump segment name ---------------------------------------------------------------------------------------------------------------------
672     # segment-slot/EAX = leaky-get-or-insert-slice(table, segment-name, row-size=8)
673     # . . push args
674     68/push  8/imm32/row-size
675     52/push-EDX
676     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
677     # . . call
678     e8/call  leaky-get-or-insert-slice/disp32
679     # . . discard args
680     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
681     # curr-segment = *segment-slot
682     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           3/r32/EBX   .               .                 # copy *EAX to EBX
683     # if (curr-segment != 0) continue
684     81          7/subop/compare     3/mod/direct    3/rm32/EBX    .           .             .           .           .               0/imm32           # compare EBX
685     0f 85/jump-if-not-equal  $read-segments:loop/disp32
686     # curr-segment = new-stream(Heap, Segment-size, 1)
687     # . save segment-slot
688     50/push-EAX
689     # . EAX = new-stream(Heap, Segment-size, 1)
690     # . . push args
691     68/push  1/imm32
692     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Segment-size/disp32               # push *Segment-size
693     68/push  Heap/imm32
694     # . . call
695     e8/call  new-stream/disp32
696     # . . discard args
697     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
698     # . curr-segment = EAX
699     89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EBX
700     # . restore segment-slot
701     58/pop-to-EAX
702     # *segment-slot = curr-segment
703     89/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           3/r32/EBX   .               .                 # copy EBX to *EAX
704     # fall through
705 $read-segments:regular-line:
706     # rewind-stream(line)
707     # . . push args
708     51/push-ECX
709     # . . call
710     e8/call  rewind-stream/disp32
711     # . . discard args
712     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
713     # write-stream(curr-segment, line)
714     # . . push args
715     51/push-ECX
716     53/push-EBX
717     # . . call
718     e8/call  write-stream/disp32
719     # . . discard args
720     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
721     # loop
722     e9/jump  $read-segments:loop/disp32
723 $read-segments:break:
724 $read-segments:end:
725     # . reclaim locals
726     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x214/imm32       # add to ESP
727     # . restore registers
728     5e/pop-to-ESI
729     5b/pop-to-EBX
730     5a/pop-to-EDX
731     59/pop-to-ECX
732     58/pop-to-EAX
733     # . epilog
734     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
735     5d/pop-to-EBP
736     c3/return
737 
738 write-segments:  # out : (address buffered-file), table : (address stream {string, (address stream byte)})
739     # pseudocode:
740     #   var curr = table->data
741     #   var max = table->data + table->write
742     #   while curr < max
743     #     stream = table[i].stream
744     #     write-stream-data(out, stream)
745     #     curr += 8
746     #   flush(out)
747     #
748     # . prolog
749     55/push-EBP
750     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
751     # . save registers
752     50/push-EAX
753     52/push-EDX
754     56/push-ESI
755     # ESI = table
756     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
757     # write/EDX = table->write
758     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           2/r32/EDX   .               .                 # copy *ESI to EDX
759     # curr/ESI = table->data
760     81          0/subop/add         3/mod/direct    6/rm32/ESI    .           .             .           .           .               0xc/imm32         # add to EAX
761     # max/EDX = curr + write
762     01/add                          3/mod/direct    2/rm32/EDX    .           .             .           6/r32/ESI   .               .                 # add ESI to EDX
763 $write-segments:loop:
764     # if (curr >= max) break
765     39/compare                      3/mod/direct    6/rm32/ESI    .           .             .           2/r32/EDX   .               .                 # compare ESI with EDX
766     73/jump-if-greater-or-equal-unsigned  $write-segments:break/disp8
767     # stream/EAX = table[i].stream
768     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ESI+4) to EAX
769     # write-stream-data(out, stream)
770     # . . push args
771     50/push-EAX
772     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
773     # . . call
774     e8/call  write-stream-data/disp32
775     # . . discard args
776     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
777 $write-segments:continue:
778     # curr += 8
779     81          0/subop/add         3/mod/direct    6/rm32/ESI    .           .             .           .           .               8/imm32           # add to ESI
780     eb/jump  $write-segments:loop/disp8
781 $write-segments:break:
782     # flush(out)
783     # . . push args
784     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
785     # . . call
786     e8/call  flush/disp32
787     # . . discard args
788     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
789 $write-segments:end:
790     # . restore registers
791     5e/pop-to-ESI
792     5a/pop-to-EDX
793     58/pop-to-EAX
794     # . epilog
795     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
796     5d/pop-to-EBP
797     c3/return
798 
799 == data
800 
801 Segment-size:
802   0x1000/imm32/4KB
803 
804 # . . vim:nowrap:textwidth=0