https://github.com/akkartik/mu/blob/main/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 #   $ ./bootstrap 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  |./bootstrap run apps/assort
  17 #   == code
  18 #   abc
  19 #   def
  20 
  21 == code
  22 #   instruction                     effective address                                                   register    displacement    immediate
  23 # . op          subop               mod             rm32          base        index         scale       r32
  24 # . 1-3 bytes   3 bits              2 bits          3 bits        3 bits      3 bits        2 bits      2 bits      0/1/2/4 bytes   0/1/2/4 bytes
  25 
  26 Entry:  # run tests if necessary, convert stdin if not
  27     # . prologue
  28     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
  29 
  30     # initialize heap
  31     # . Heap = new-segment(Heap-size)
  32     # . . push args
  33     68/push  Heap/imm32
  34     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Heap-size/disp32                  # push *Heap-size
  35     # . . call
  36     e8/call  new-segment/disp32
  37     # . . discard args
  38     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
  39 
  40     # - if argc > 1 and argv[1] == "test", then return run_tests()
  41     # if (argc <= 1) goto interactive
  42     81          7/subop/compare     1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0/disp8         1/imm32           # compare *ebp
  43     7e/jump-if-<=  $subx-assort-main:interactive/disp8
  44     # if (!kernel-string-equal?(argv[1], "test")) goto interactive
  45     # . eax = kernel-string-equal?(argv[1], "test")
  46     # . . push args
  47     68/push  "test"/imm32
  48     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
  49     # . . call
  50     e8/call  kernel-string-equal?/disp32
  51     # . . discard args
  52     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
  53     # . if (eax == false) goto interactive
  54     3d/compare-eax-and  0/imm32/false
  55     74/jump-if-=  $subx-assort-main:interactive/disp8
  56     # run-tests()
  57     e8/call  run-tests/disp32
  58     # syscall(exit, *Num-test-failures)
  59     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/ebx   Num-test-failures/disp32          # copy *Num-test-failures to ebx
  60     eb/jump  $subx-assort-main:end/disp8
  61 $subx-assort-main:interactive:
  62     # - otherwise convert stdin
  63     # var ed/eax: exit-descriptor
  64     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # subtract from esp
  65     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           4/r32/esp   .               .                 # copy esp to eax
  66     # configure ed to really exit()
  67     # . ed->target = 0
  68     c7          0/subop/copy        0/mod/direct    0/rm32/eax    .           .             .           .           .               0/imm32           # copy to *eax
  69     # subx-assort(Stdin, Stdout, Stderr, ed)
  70     # . . push args
  71     50/push-eax/ed
  72     68/push  Stderr/imm32
  73     68/push  Stdout/imm32
  74     68/push  Stdin/imm32
  75     # . . call
  76     e8/call  subx-assort/disp32
  77     # . . discard args
  78     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
  79     # syscall(exit, 0)
  80     bb/copy-to-ebx  0/imm32
  81 $subx-assort-main:end:
  82     e8/call  syscall_exit/disp32
  83 
  84 # data structure:
  85 #   table: (addr stream {(handle array byte), (handle stream byte)})     (16 bytes/row)
  86 # inefficient; uses sequential search for looking up segments by name
  87 
  88 subx-assort:  # in: (addr buffered-file), out: (addr buffered-file)
  89     # pseudocode:
  90     #   var table: (addr stream {(handle array byte), (handle stream byte)} 10 rows)
  91     #   read-segments(in, table)
  92     #   write-segments(out, table)
  93     #
  94     # . prologue
  95     55/push-ebp
  96     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
  97     # . save registers
  98     51/push-ecx
  99     # var table/ecx: (stream {string, (handle stream byte)} 160)  # 10 rows * 16 bytes/row
 100     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0xa0/imm32        # subtract from esp
 101     68/push  0xa0/imm32/length
 102     68/push  0/imm32/read
 103     68/push  0/imm32/write
 104     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
 105     # clear-stream(table)
 106     # . . push args
 107     51/push-ecx
 108     # . . call
 109     e8/call  clear-stream/disp32
 110     # . . discard args
 111     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 112 $subx-assort:read:
 113 +--  9 lines: #?     # print("read\n") --------------------------------------------------------------------------------------------------------------------------------------------------
 122     # read-segments(in, table)
 123     # . . push args
 124     51/push-ecx
 125     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 126     # . . call
 127     e8/call  read-segments/disp32
 128     # . . discard args
 129     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 130 $subx-assort:write:
 131 +--  9 lines: #?     # print("write\n") -------------------------------------------------------------------------------------------------------------------------------------------------
 140     # write-segments(out, table)
 141     # . . push args
 142     51/push-ecx
 143     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 144     # . . call
 145     e8/call  write-segments/disp32
 146     # . . discard args
 147     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 148 $subx-assort:end:
 149     # . reclaim locals
 150     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xac/imm32        # add to esp
 151     # . restore registers
 152     59/pop-to-ecx
 153     # . epilogue
 154     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 155     5d/pop-to-ebp
 156     c3/return
 157 
 158 test-subx-assort:
 159     # . prologue
 160     55/push-ebp
 161     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 162     # setup
 163     # . clear-stream(_test-input-stream)
 164     # . . push args
 165     68/push  _test-input-stream/imm32
 166     # . . call
 167     e8/call  clear-stream/disp32
 168     # . . discard args
 169     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 170     # . clear-stream($_test-input-buffered-file->buffer)
 171     # . . push args
 172     68/push  $_test-input-buffered-file->buffer/imm32
 173     # . . call
 174     e8/call  clear-stream/disp32
 175     # . . discard args
 176     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 177     # . clear-stream(_test-output-stream)
 178     # . . push args
 179     68/push  _test-output-stream/imm32
 180     # . . call
 181     e8/call  clear-stream/disp32
 182     # . . discard args
 183     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 184     # . clear-stream($_test-output-buffered-file->buffer)
 185     # . . push args
 186     68/push  $_test-output-buffered-file->buffer/imm32
 187     # . . call
 188     e8/call  clear-stream/disp32
 189     # . . discard args
 190     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 191     # initialize input (meta comments in parens)
 192     #   # comment 1
 193     #     # comment 2 indented
 194     #   == code 0x09000000  (new segment)
 195     #   # comment 3 inside a segment
 196     #   1
 197     #                         (empty line)
 198     #   2 3 # comment 4 inline with other contents
 199     #   == data 0x0a000000  (new segment)
 200     #   4 5/imm32
 201     #   == code  (existing segment but non-contiguous with previous iteration)
 202     #   6 7
 203     #   8 9  (multiple lines)
 204     #   == code  (existing segment contiguous with previous iteration)
 205     #   10 11
 206     # . write(_test-input-stream, "# comment 1\n")
 207     # . . push args
 208     68/push  "# comment 1\n"/imm32
 209     68/push  _test-input-stream/imm32
 210     # . . call
 211     e8/call  write/disp32
 212     # . . discard args
 213     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 214     # . write(_test-input-stream, "  # comment 2 indented\n")
 215     # . . push args
 216     68/push  "  # comment 2 indented\n"/imm32
 217     68/push  _test-input-stream/imm32
 218     # . . call
 219     e8/call  write/disp32
 220     # . . discard args
 221     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 222     # . write(_test-input-stream, "== code 0x09000000\n")
 223     # . . push args
 224     68/push  "== code 0x09000000\n"/imm32
 225     68/push  _test-input-stream/imm32
 226     # . . call
 227     e8/call  write/disp32
 228     # . . discard args
 229     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 230     # . write(_test-input-stream, "# comment 3 inside a segment\n")
 231     # . . push args
 232     68/push  "# comment 3 inside a segment\n"/imm32
 233     68/push  _test-input-stream/imm32
 234     # . . call
 235     e8/call  write/disp32
 236     # . . discard args
 237     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 238     # . write(_test-input-stream, "1\n")
 239     # . . push args
 240     68/push  "1\n"/imm32
 241     68/push  _test-input-stream/imm32
 242     # . . call
 243     e8/call  write/disp32
 244     # . . discard args
 245     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 246     # . write(_test-input-stream, "\n")  # empty line
 247     # . . push args
 248     68/push  Newline/imm32
 249     68/push  _test-input-stream/imm32
 250     # . . call
 251     e8/call  write/disp32
 252     # . . discard args
 253     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 254     # . write(_test-input-stream, "2 3 # comment 4 inline with other contents\n")
 255     # . . push args
 256     68/push  "2 3 # comment 4 inline with other contents\n"/imm32
 257     68/push  _test-input-stream/imm32
 258     # . . call
 259     e8/call  write/disp32
 260     # . . discard args
 261     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 262     # . write(_test-input-stream, "== data 0x0a000000\n")
 263     # . . push args
 264     68/push  "== data 0x0a000000\n"/imm32
 265     68/push  _test-input-stream/imm32
 266     # . . call
 267     e8/call  write/disp32
 268     # . . discard args
 269     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 270     # . write(_test-input-stream, "4 5/imm32\n")
 271     # . . push args
 272     68/push  "4 5/imm32\n"/imm32
 273     68/push  _test-input-stream/imm32
 274     # . . call
 275     e8/call  write/disp32
 276     # . . discard args
 277     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 278     # . write(_test-input-stream, "== code\n")
 279     # . . push args
 280     68/push  "== code\n"/imm32
 281     68/push  _test-input-stream/imm32
 282     # . . call
 283     e8/call  write/disp32
 284     # . . discard args
 285     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 286     # . write(_test-input-stream, "6 7\n")
 287     # . . push args
 288     68/push  "6 7\n"/imm32
 289     68/push  _test-input-stream/imm32
 290     # . . call
 291     e8/call  write/disp32
 292     # . . discard args
 293     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 294     # . write(_test-input-stream, "8 9\n")
 295     # . . push args
 296     68/push  "8 9\n"/imm32
 297     68/push  _test-input-stream/imm32
 298     # . . call
 299     e8/call  write/disp32
 300     # . . discard args
 301     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 302     # . write(_test-input-stream, "== code\n")
 303     # . . push args
 304     68/push  "== code\n"/imm32
 305     68/push  _test-input-stream/imm32
 306     # . . call
 307     e8/call  write/disp32
 308     # . . discard args
 309     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 310     # . write(_test-input-stream, "10 11\n")
 311     # . . push args
 312     68/push  "10 11\n"/imm32
 313     68/push  _test-input-stream/imm32
 314     # . . call
 315     e8/call  write/disp32
 316     # . . discard args
 317     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 318     # subx-assort(_test-input-buffered-file, _test-output-buffered-file)
 319     # . . push args
 320     68/push  _test-output-buffered-file/imm32
 321     68/push  _test-input-buffered-file/imm32
 322     # . . call
 323     e8/call  subx-assort/disp32
 324     # . . discard args
 325     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 326     # . flush(_test-output-buffered-file)
 327     # . . push args
 328     68/push  _test-output-buffered-file/imm32
 329     # . . call
 330     e8/call  flush/disp32
 331     # . . discard args
 332     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 333     # check output
 334     #   == code 0x09000000
 335     #   1
 336     #   2 3 # comment 4 inline with other contents
 337     #   6 7
 338     #   8 9
 339     #   10 11
 340     #   == data 0x0a000000
 341     #   4 5/imm32
 342 +-- 33 lines: #?     # dump output ------------------------------------------------------------------------------------------------------------------------------------------------------
 375     # . check-next-stream-line-equal(_test-output-stream, "== code 0x09000000", msg)
 376     # . . push args
 377     68/push  "F - test-subx-assort/0"/imm32
 378     68/push  "== code 0x09000000"/imm32
 379     68/push  _test-output-stream/imm32
 380     # . . call
 381     e8/call  check-next-stream-line-equal/disp32
 382     # . . discard args
 383     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 384     # . check-next-stream-line-equal(_test-output-stream, "1", msg)
 385     # . . push args
 386     68/push  "F - test-subx-assort/1"/imm32
 387     68/push  "1"/imm32
 388     68/push  _test-output-stream/imm32
 389     # . . call
 390     e8/call  check-next-stream-line-equal/disp32
 391     # . . discard args
 392     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 393     # . check-next-stream-line-equal(_test-output-stream, "2 3 # comment 4 inline with other contents", msg)
 394     # . . push args
 395     68/push  "F - test-subx-assort/2"/imm32
 396     68/push  "2 3 # comment 4 inline with other contents"/imm32
 397     68/push  _test-output-stream/imm32
 398     # . . call
 399     e8/call  check-next-stream-line-equal/disp32
 400     # . . discard args
 401     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 402     # . check-next-stream-line-equal(_test-output-stream, "6 7", msg)
 403     # . . push args
 404     68/push  "F - test-subx-assort/3"/imm32
 405     68/push  "6 7"/imm32
 406     68/push  _test-output-stream/imm32
 407     # . . call
 408     e8/call  check-next-stream-line-equal/disp32
 409     # . . discard args
 410     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 411     # . check-next-stream-line-equal(_test-output-stream, "8 9", msg)
 412     # . . push args
 413     68/push  "F - test-subx-assort/4"/imm32
 414     68/push  "8 9"/imm32
 415     68/push  _test-output-stream/imm32
 416     # . . call
 417     e8/call  check-next-stream-line-equal/disp32
 418     # . . discard args
 419     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 420     # . check-next-stream-line-equal(_test-output-stream, "10 11", msg)
 421     # . . push args
 422     68/push  "F - test-subx-assort/5"/imm32
 423     68/push  "10 11"/imm32
 424     68/push  _test-output-stream/imm32
 425     # . . call
 426     e8/call  check-next-stream-line-equal/disp32
 427     # . . discard args
 428     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 429     # . check-next-stream-line-equal(_test-output-stream, "== data 0x0a000000", msg)
 430     # . . push args
 431     68/push  "F - test-subx-assort/6"/imm32
 432     68/push  "== data 0x0a000000"/imm32
 433     68/push  _test-output-stream/imm32
 434     # . . call
 435     e8/call  check-next-stream-line-equal/disp32
 436     # . . discard args
 437     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 438     # . check-next-stream-line-equal(_test-output-stream, "4 5/imm32", msg)
 439     # . . push args
 440     68/push  "F - test-subx-assort/7"/imm32
 441     68/push  "4 5/imm32"/imm32
 442     68/push  _test-output-stream/imm32
 443     # . . call
 444     e8/call  check-next-stream-line-equal/disp32
 445     # . . discard args
 446     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 447     # . epilogue
 448     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 449     5d/pop-to-ebp
 450     c3/return
 451 
 452 read-segments:  # in: (addr buffered-file), table: (addr stream {(handle array byte), (handle stream byte)})
 453     # pseudocode:
 454     #   var curr-segment: (addr stream byte) = 0
 455     #   var line: (stream byte 512)
 456     #   while true
 457     #     clear-stream(line)
 458     #     read-line-buffered(in, line)
 459     #     if (line->write == 0) break             # end of file
 460     #     var word-slice = next-word-or-string(line)
 461     #     if slice-empty?(word-slice)             # whitespace
 462     #       continue
 463     #     if slice-starts-with?(word-slice, "#")  # comment
 464     #       continue
 465     #     if slice-equal?(word-slice, "==")
 466     #       var segment-name = next-word-or-string(line)
 467     #       var segment-slot: (addr handle stream byte) = get-or-insert-slice(table, segment-name, row-size=16)
 468     #       if *segment-slot != 0
 469     #         curr-segment = lookup(*segment-slot)
 470     #         continue
 471     #       new-stream(Heap, Segment-size, 1, segment-slot)
 472     #       curr-segment = lookup(*segment-slot)
 473     #     rewind-stream(line)
 474     #     write-stream(curr-segment, line)  # abort if curr-segment overflows
 475     #
 476     # word-slice and segment-name are both slices with disjoint lifetimes, so
 477     # we'll use the same address for them.
 478     #
 479     # registers:
 480     #   line: ecx
 481     #   word-slice and segment-name: edx
 482     #   segment-name and curr-segment: ebx
 483     #   word-slice->start: esi
 484     #   temporary: eax
 485     #
 486     # . prologue
 487     55/push-ebp
 488     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 489     # . save registers
 490     50/push-eax
 491     51/push-ecx
 492     52/push-edx
 493     53/push-ebx
 494     56/push-esi
 495     57/push-edi
 496     # var line/ecx: (stream byte 512)
 497     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x200/imm32       # subtract from esp
 498     68/push  0x200/imm32/length
 499     68/push  0/imm32/read
 500     68/push  0/imm32/write
 501     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
 502     # var word-slice/edx: slice
 503     68/push  0/imm32/end
 504     68/push  0/imm32/start
 505     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
 506 $read-segments:loop:
 507     # clear-stream(line)
 508     # . . push args
 509     51/push-ecx
 510     # . . call
 511     e8/call  clear-stream/disp32
 512     # . . discard args
 513     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 514     # read-line-buffered(in, line)
 515     # . . push args
 516     51/push-ecx
 517     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 518     # . . call
 519     e8/call  read-line-buffered/disp32
 520     # . . discard args
 521     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 522 $read-segments:check0:
 523     # if (line->write == 0) break
 524     81          7/subop/compare     0/mod/indirect  1/rm32/ecx    .           .             .           .           .               0/imm32           # compare *ecx
 525     0f 84/jump-if-=  $read-segments:break/disp32
 526 +-- 33 lines: #?     # dump line --------------------------------------------------------------------------------------------------------------------------------------------------------
 559     # next-word-or-string(line, word-slice)
 560     # . . push args
 561     52/push-edx
 562     51/push-ecx
 563     # . . call
 564     e8/call  next-word-or-string/disp32
 565     # . . discard args
 566     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 567 $read-segments:check1:
 568 +--  9 lines: #?     # print("check1\n") ------------------------------------------------------------------------------------------------------------------------------------------------
 577     # if (slice-empty?(word-slice)) continue
 578     # . eax = slice-empty?(word-slice)
 579     # . . push args
 580     52/push-edx
 581     # . . call
 582     e8/call  slice-empty?/disp32
 583     # . . discard args
 584     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 585     # . if (eax != false) continue
 586     3d/compare-eax-and  0/imm32/false
 587     0f 85/jump-if-!=  $read-segments:loop/disp32
 588 $read-segments:check-for-comment:
 589 +--  9 lines: #?     # print("check for comment\n") -------------------------------------------------------------------------------------------------------------------------------------
 598     # if (slice-starts-with?(word-slice, "#")) continue
 599     # . var start/esi: (addr byte) = word-slice->start
 600     8b/copy                         0/mod/indirect  2/rm32/edx    .           .             .           6/r32/esi   .               .                 # copy *ecx to esi
 601     # . var c/eax: byte = *start
 602     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
 603     8a/copy-byte                    0/mod/indirect  6/rm32/esi    .           .             .           0/r32/AL    .               .                 # copy byte at *esi to AL
 604     # . if (c == '#') continue
 605     3d/compare-eax-and  0x23/imm32/hash
 606     0f 84/jump-if-=  $read-segments:loop/disp32
 607 $read-segments:check-for-segment-header:
 608 +--  9 lines: #?     # print("check for segment header\n") ------------------------------------------------------------------------------------------------------------------------------
 617 +-- 40 lines: #?     # dump word-slice --------------------------------------------------------------------------------------------------------------------------------------------------
 657     # if !slice-equal?(word-slice, "==") goto next check
 658     # . eax = slice-equal?(word-slice, "==")
 659     # . . push args
 660     68/push  "=="/imm32
 661     52/push-edx
 662     # . . call
 663     e8/call  slice-equal?/disp32
 664     # . . discard args
 665     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 666     # . if (eax == false) goto next check
 667     3d/compare-eax-and  0/imm32/false
 668     0f 84/jump-if-=  $read-segments:regular-line/disp32
 669     # segment-name = next-word-or-string(line)
 670     # . . push args
 671     52/push-edx
 672     51/push-ecx
 673     # . . call
 674     e8/call  next-word-or-string/disp32
 675     # . . discard args
 676     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 677 +-- 40 lines: #?     # dump segment name ------------------------------------------------------------------------------------------------------------------------------------------------
 717     # var segment-slot/edi: (addr handle stream byte) = get-or-insert-slice(table, segment-name, row-size=16, Heap)
 718     # . eax = get-or-insert-slice(table, segment-name, row-size=16, Heap)
 719     # . . push args
 720     68/push  Heap/imm32
 721     68/push  0x10/imm32/row-size
 722     52/push-edx
 723     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 724     # . . call
 725     e8/call  get-or-insert-slice/disp32
 726     # . . discard args
 727     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
 728     # . edi = eax
 729     89/copy                         3/mod/direct    7/rm32/edi    .           .             .           0/r32/eax   .               .                 # copy eax to edi
 730 +-- 33 lines: #?     # print("slot: " segment-slot "\n") --------------------------------------------------------------------------------------------------------------------------------
 763     # if (*segment-slot != 0) update curr-segment and continue
 764     81          7/subop/compare     0/mod/indirect  7/rm32/edi    .           .             .           .           .               0/imm32           # compare edi
 765     0f 84/jump-if-=  $read-segments:create-segment/disp32
 766     # var curr-segment/ebx: (addr stream byte) = lookup(*segment-slot)
 767     # . eax = lookup(*segment-slot)
 768     # . . push args
 769     ff          6/subop/push        1/mod/*+disp8   7/rm32/edi    .           .             .           .           4/disp8         .                 # push *(edi+4)
 770     ff          6/subop/push        0/mod/indirect  7/rm32/edi    .           .             .           .           .               .                 # push *edi
 771     # . . call
 772     e8/call  lookup/disp32
 773     # . . discard args
 774     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 775     # . ebx = eax
 776     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # copy eax to ebx
 777     # continue
 778     e9/jump  $read-segments:loop/disp32
 779 $read-segments:create-segment:
 780     # new-stream(Heap, Segment-size, 1, segment-slot)
 781     # . . push args
 782     57/push-edi
 783     68/push  1/imm32
 784     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Segment-size/disp32               # push *Segment-size
 785     68/push  Heap/imm32
 786     # . . call
 787     e8/call  new-stream/disp32
 788     # . . discard args
 789     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
 790     # var curr-segment/ebx: (addr stream byte) = lookup(*segment-slot)
 791     # . eax = lookup(*segment-slot)
 792     # . . push args
 793     ff          6/subop/push        1/mod/*+disp8   7/rm32/edi    .           .             .           .           4/disp8         .                 # push *(edi+4)
 794     ff          6/subop/push        0/mod/indirect  7/rm32/edi    .           .             .           .           .               .                 # push *edi
 795     # . . call
 796     e8/call  lookup/disp32
 797     # . . discard args
 798     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 799     # . ebx = eax
 800     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # copy eax to ebx
 801     # fall through
 802 $read-segments:regular-line:
 803 +--  9 lines: #?     # print("regular line\n") ------------------------------------------------------------------------------------------------------------------------------------------
 812 +-- 33 lines: #?     # dump line --------------------------------------------------------------------------------------------------------------------------------------------------------
 845 +-- 33 lines: #?     # print("addr: " curr-segment->write "\n") -------------------------------------------------------------------------------------------------------------------------
 878 +-- 33 lines: #?     # print("write: " curr-segment->write "\n") ------------------------------------------------------------------------------------------------------------------------
 911 +-- 33 lines: #?     # print("size: " curr-segment->size "\n") --------------------------------------------------------------------------------------------------------------------------
 944     # rewind-stream(line)
 945     # . . push args
 946     51/push-ecx
 947     # . . call
 948     e8/call  rewind-stream/disp32
 949     # . . discard args
 950     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 951 +--  9 lines: #?     # print("write stream\n") ------------------------------------------------------------------------------------------------------------------------------------------
 960     # write-stream(curr-segment, line)
 961     # . . push args
 962     51/push-ecx
 963     53/push-ebx
 964     # . . call
 965     e8/call  write-stream/disp32
 966     # . . discard args
 967     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 968     # loop
 969 +--  9 lines: #?     # print("loop\n") --------------------------------------------------------------------------------------------------------------------------------------------------
 978     e9/jump  $read-segments:loop/disp32
 979 $read-segments:break:
 980 $read-segments:end:
 981     # . reclaim locals
 982     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x214/imm32       # add to esp
 983     # . restore registers
 984     5f/pop-to-edi
 985     5e/pop-to-esi
 986     5b/pop-to-ebx
 987     5a/pop-to-edx
 988     59/pop-to-ecx
 989     58/pop-to-eax
 990     # . epilogue
 991     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 992     5d/pop-to-ebp
 993     c3/return
 994 
 995 write-segments:  # out: (addr buffered-file), table: (addr stream {(handle array byte), (handle stream byte)})
 996     # pseudocode:
 997     #   var curr = table->data
 998     #   var max = &table->data[table->write]
 999     #   while curr < max
1000     #     var stream: (addr stream byte) = lookup(table[i].stream)
1001     #     write-stream-data(out, stream)
1002     #     curr += 16
1003     #   flush(out)
1004     #
1005     # . prologue
1006     55/push-ebp
1007     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1008     # . save registers
1009     50/push-eax
1010     52/push-edx
1011     56/push-esi
1012     # esi = table
1013     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   0xc/disp8       .                 # copy *(ebp+12) to esi
1014     # var write/edx: int = table->write
1015     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           2/r32/edx   .               .                 # copy *esi to edx
1016     # var curr/esi: (addr byte) = table->data
1017     81          0/subop/add         3/mod/direct    6/rm32/esi    .           .             .           .           .               0xc/imm32         # add to eax
1018     # var max/edx: (addr byte) = curr + write
1019     01/add                          3/mod/direct    2/rm32/edx    .           .             .           6/r32/esi   .               .                 # add esi to edx
1020 $write-segments:loop:
1021     # if (curr >= max) break
1022     39/compare                      3/mod/direct    6/rm32/esi    .           .             .           2/r32/edx   .               .                 # compare esi with edx
1023     73/jump-if-addr>=  $write-segments:break/disp8
1024     # var stream/eax: (addr stream byte) = lookup(table[i].stream)
1025     # . . push args
1026     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           0xc/disp8       .                 # push *(esi+12)
1027     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           8/disp8         .                 # push *(esi+8)
1028     # . . call
1029     e8/call  lookup/disp32
1030     # . . discard args
1031     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1032     # write-stream-data(out, stream)
1033     # . . push args
1034     50/push-eax
1035     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1036     # . . call
1037     e8/call  write-stream-data/disp32
1038     # . . discard args
1039     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1040 $write-segments:continue:
1041     # curr += 16
1042     81          0/subop/add         3/mod/direct    6/rm32/esi    .           .             .           .           .               0x10/imm32        # add to esi
1043     eb/jump  $write-segments:loop/disp8
1044 $write-segments:break:
1045     # flush(out)
1046     # . . push args
1047     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1048     # . . call
1049     e8/call  flush/disp32
1050     # . . discard args
1051     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1052 $write-segments:end:
1053     # . restore registers
1054     5e/pop-to-esi
1055     5a/pop-to-edx
1056     58/pop-to-eax
1057     # . epilogue
1058     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1059     5d/pop-to-ebp
1060     c3/return
1061 
1062 # . . vim:nowrap:textwidth=0