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     # for debugging: run a single test
  38 #?     e8/call test-convert/disp32
  39 #?     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
  40 #?     eb/jump  $main:end/disp8
  41 
  42     # run tests if necessary, convert stdin if not
  43     # . prolog
  44     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
  45     # - if argc > 1 and argv[1] == "test", then return run_tests()
  46     # . argc > 1
  47     81          7/subop/compare     1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0/disp8         1/imm32           # compare *EBP
  48     7e/jump-if-lesser-or-equal  $run-main/disp8
  49     # . argv[1] == "test"
  50     # . . push args
  51     68/push  "test"/imm32
  52     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
  53     # . . call
  54     e8/call  kernel-string-equal?/disp32
  55     # . . discard args
  56     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
  57     # . check result
  58     3d/compare-EAX-and  1/imm32
  59     75/jump-if-not-equal  $run-main/disp8
  60     # . run-tests()
  61     e8/call  run-tests/disp32
  62     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
  63     eb/jump  $main:end/disp8
  64 $run-main:
  65     # - otherwise convert stdin
  66     # var ed/EAX : exit-descriptor
  67     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
  68     89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EAX
  69     # configure ed to really exit()
  70     # . ed->target = 0
  71     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # copy to *EAX
  72     # return convert(Stdin, 1/stdout, 2/stderr, ed)
  73     # . . push args
  74     50/push-EAX/ed
  75     68/push  Stderr/imm32
  76     68/push  Stdout/imm32
  77     68/push  Stdin/imm32
  78     # . . call
  79     e8/call  convert/disp32
  80     # . . discard args
  81     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
  82     # . syscall(exit, 0)
  83     bb/copy-to-EBX  0/imm32
  84 $main:end:
  85     b8/copy-to-EAX  1/imm32/exit
  86     cd/syscall  0x80/imm8
  87 
  88 # data structure:
  89 #   row: pair of (address array byte) and (address stream byte)
  90 #   table: (address stream row)
  91 
  92 convert:  # in : (address buffered-file), out : (address buffered-file) -> <void>
  93     # pseudocode:
  94     #   var table : (address stream) = new-stream(10 rows, 8 bytes each)
  95     #   read-segments(in, table)
  96     #   write-segments(out, table)
  97     #
  98     # . prolog
  99     55/push-EBP
 100     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 101     # . save registers
 102     51/push-ECX
 103     # var table/ECX : (address stream byte) = stream(10 * 8)
 104     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x50/imm32        # subtract from ESP
 105     68/push  0x50/imm32/length
 106     68/push  0/imm32/read
 107     68/push  0/imm32/write
 108     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
 109     # clear-stream(table)
 110     # . . push args
 111     51/push-ECX
 112     # . . call
 113     e8/call  clear-stream/disp32
 114     # . . discard args
 115     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 116 $convert:read:
 117     # read-segments(in, table)
 118     # . . push args
 119     51/push-ECX
 120     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 121     # . . call
 122     e8/call  read-segments/disp32
 123     # . . discard args
 124     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 125 $convert:write:
 126     # write-segments(out, table)
 127     # . . push args
 128     51/push-ECX
 129     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 130     # . . call
 131     e8/call  write-segments/disp32
 132     # . . discard args
 133     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 134 $convert:end:
 135     # . reclaim locals
 136     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x5c/imm32        # add to ESP
 137     # . restore registers
 138     59/pop-to-ECX
 139     # . epilog
 140     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 141     5d/pop-to-EBP
 142     c3/return
 143 
 144 test-convert:
 145     # . prolog
 146     55/push-EBP
 147     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 148     # setup
 149     # . clear-stream(_test-input-stream)
 150     # . . push args
 151     68/push  _test-input-stream/imm32
 152     # . . call
 153     e8/call  clear-stream/disp32
 154     # . . discard args
 155     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 156     # . clear-stream(_test-input-buffered-file+4)
 157     # . . push args
 158     b8/copy-to-EAX  _test-input-buffered-file/imm32
 159     05/add-to-EAX  4/imm32
 160     50/push-EAX
 161     # . . call
 162     e8/call  clear-stream/disp32
 163     # . . discard args
 164     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 165     # . clear-stream(_test-output-stream)
 166     # . . push args
 167     68/push  _test-output-stream/imm32
 168     # . . call
 169     e8/call  clear-stream/disp32
 170     # . . discard args
 171     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 172     # . clear-stream(_test-output-buffered-file+4)
 173     # . . push args
 174     b8/copy-to-EAX  _test-output-buffered-file/imm32
 175     05/add-to-EAX  4/imm32
 176     50/push-EAX
 177     # . . call
 178     e8/call  clear-stream/disp32
 179     # . . discard args
 180     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 181     # initialize input (meta comments in parens)
 182     #   # comment 1
 183     #     # comment 2 indented
 184     #   == code 0x09000000  (new segment)
 185     #   # comment 3 inside a segment
 186     #   1
 187     #                         (empty line)
 188     #   2 3 # comment 4 inline with other contents
 189     #   == data 0x0a000000  (new segment)
 190     #   4 5/imm32
 191     #   == code  (existing segment but non-contiguous with previous iteration)
 192     #   6 7
 193     #   8 9  (multiple lines)
 194     #   == code  (existing segment contiguous with previous iteration)
 195     #   10 11
 196     # . write(_test-input-stream, "# comment 1\n")
 197     # . . push args
 198     68/push  "# comment 1\n"/imm32
 199     68/push  _test-input-stream/imm32
 200     # . . call
 201     e8/call  write/disp32
 202     # . . discard args
 203     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 204     # . write(_test-input-stream, "  # comment 2 indented\n")
 205     # . . push args
 206     68/push  "  # comment 2 indented\n"/imm32
 207     68/push  _test-input-stream/imm32
 208     # . . call
 209     e8/call  write/disp32
 210     # . . discard args
 211     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 212     # . write(_test-input-stream, "== code 0x09000000\n")
 213     # . . push args
 214     68/push  "== code 0x09000000\n"/imm32
 215     68/push  _test-input-stream/imm32
 216     # . . call
 217     e8/call  write/disp32
 218     # . . discard args
 219     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 220     # . write(_test-input-stream, "# comment 3 inside a segment\n")
 221     # . . push args
 222     68/push  "# comment 3 inside a segment\n"/imm32
 223     68/push  _test-input-stream/imm32
 224     # . . call
 225     e8/call  write/disp32
 226     # . . discard args
 227     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 228     # . write(_test-input-stream, "1\n")
 229     # . . push args
 230     68/push  "1\n"/imm32
 231     68/push  _test-input-stream/imm32
 232     # . . call
 233     e8/call  write/disp32
 234     # . . discard args
 235     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 236     # . write(_test-input-stream, "\n")  # empty line
 237     # . . push args
 238     68/push  "\n"/imm32
 239     68/push  _test-input-stream/imm32
 240     # . . call
 241     e8/call  write/disp32
 242     # . . discard args
 243     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 244     # . write(_test-input-stream, "2 3 # comment 4 inline with other contents\n")
 245     # . . push args
 246     68/push  "2 3 # comment 4 inline with other contents\n"/imm32
 247     68/push  _test-input-stream/imm32
 248     # . . call
 249     e8/call  write/disp32
 250     # . . discard args
 251     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 252     # . write(_test-input-stream, "== data 0x0a000000\n")
 253     # . . push args
 254     68/push  "== data 0x0a000000\n"/imm32
 255     68/push  _test-input-stream/imm32
 256     # . . call
 257     e8/call  write/disp32
 258     # . . discard args
 259     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 260     # . write(_test-input-stream, "4 5/imm32\n")
 261     # . . push args
 262     68/push  "4 5/imm32\n"/imm32
 263     68/push  _test-input-stream/imm32
 264     # . . call
 265     e8/call  write/disp32
 266     # . . discard args
 267     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 268     # . write(_test-input-stream, "== code\n")
 269     # . . push args
 270     68/push  "== code\n"/imm32
 271     68/push  _test-input-stream/imm32
 272     # . . call
 273     e8/call  write/disp32
 274     # . . discard args
 275     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 276     # . write(_test-input-stream, "6 7\n")
 277     # . . push args
 278     68/push  "6 7\n"/imm32
 279     68/push  _test-input-stream/imm32
 280     # . . call
 281     e8/call  write/disp32
 282     # . . discard args
 283     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 284     # . write(_test-input-stream, "8 9\n")
 285     # . . push args
 286     68/push  "8 9\n"/imm32
 287     68/push  _test-input-stream/imm32
 288     # . . call
 289     e8/call  write/disp32
 290     # . . discard args
 291     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 292     # . write(_test-input-stream, "== code\n")
 293     # . . push args
 294     68/push  "== code\n"/imm32
 295     68/push  _test-input-stream/imm32
 296     # . . call
 297     e8/call  write/disp32
 298     # . . discard args
 299     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 300     # . write(_test-input-stream, "10 11\n")
 301     # . . push args
 302     68/push  "10 11\n"/imm32
 303     68/push  _test-input-stream/imm32
 304     # . . call
 305     e8/call  write/disp32
 306     # . . discard args
 307     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 308     # convert(_test-input-buffered-file, _test-output-buffered-file)
 309     # . . push args
 310     68/push  _test-output-buffered-file/imm32
 311     68/push  _test-input-buffered-file/imm32
 312     # . . call
 313     e8/call  convert/disp32
 314     # . . discard args
 315     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 316     # . flush(_test-output-buffered-file)
 317     # . . push args
 318     68/push  _test-output-buffered-file/imm32
 319     # . . call
 320     e8/call  flush/disp32
 321     # . . discard args
 322     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 323     # check output
 324     #   == code 0x09000000
 325     #   1
 326     #   2 3 # comment 4 inline with other contents
 327     #   6 7
 328     #   8 9
 329     #   10 11
 330     #   == data 0x0a000000
 331     #   4 5/imm32
 332 +-- 33 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
 365     # . check-next-stream-line-equal(_test-output-stream, "== code 0x09000000", msg)
 366     # . . push args
 367     68/push  "F - test-convert/0"/imm32
 368     68/push  "== code 0x09000000"/imm32
 369     68/push  _test-output-stream/imm32
 370     # . . call
 371     e8/call  check-next-stream-line-equal/disp32
 372     # . . discard args
 373     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 374     # . check-next-stream-line-equal(_test-output-stream, "1", msg)
 375     # . . push args
 376     68/push  "F - test-convert/1"/imm32
 377     68/push  "1"/imm32
 378     68/push  _test-output-stream/imm32
 379     # . . call
 380     e8/call  check-next-stream-line-equal/disp32
 381     # . . discard args
 382     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 383     # . check-next-stream-line-equal(_test-output-stream, "2 3 # comment 4 inline with other contents", msg)
 384     # . . push args
 385     68/push  "F - test-convert/2"/imm32
 386     68/push  "2 3 # comment 4 inline with other contents"/imm32
 387     68/push  _test-output-stream/imm32
 388     # . . call
 389     e8/call  check-next-stream-line-equal/disp32
 390     # . . discard args
 391     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 392     # . check-next-stream-line-equal(_test-output-stream, "6 7", msg)
 393     # . . push args
 394     68/push  "F - test-convert/3"/imm32
 395     68/push  "6 7"/imm32
 396     68/push  _test-output-stream/imm32
 397     # . . call
 398     e8/call  check-next-stream-line-equal/disp32
 399     # . . discard args
 400     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 401     # . check-next-stream-line-equal(_test-output-stream, "8 9", msg)
 402     # . . push args
 403     68/push  "F - test-convert/4"/imm32
 404     68/push  "8 9"/imm32
 405     68/push  _test-output-stream/imm32
 406     # . . call
 407     e8/call  check-next-stream-line-equal/disp32
 408     # . . discard args
 409     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 410     # . check-next-stream-line-equal(_test-output-stream, "10 11", msg)
 411     # . . push args
 412     68/push  "F - test-convert/5"/imm32
 413     68/push  "10 11"/imm32
 414     68/push  _test-output-stream/imm32
 415     # . . call
 416     e8/call  check-next-stream-line-equal/disp32
 417     # . . discard args
 418     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 419     # . check-next-stream-line-equal(_test-output-stream, "== data 0x0a000000", msg)
 420     # . . push args
 421     68/push  "F - test-convert/6"/imm32
 422     68/push  "== data 0x0a000000"/imm32
 423     68/push  _test-output-stream/imm32
 424     # . . call
 425     e8/call  check-next-stream-line-equal/disp32
 426     # . . discard args
 427     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 428     # . check-next-stream-line-equal(_test-output-stream, "4 5/imm32", msg)
 429     # . . push args
 430     68/push  "F - test-convert/7"/imm32
 431     68/push  "4 5/imm32"/imm32
 432     68/push  _test-output-stream/imm32
 433     # . . call
 434     e8/call  check-next-stream-line-equal/disp32
 435     # . . discard args
 436     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 437     # . epilog
 438     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 439     5d/pop-to-EBP
 440     c3/return
 441 
 442 read-segments:  # in : (address buffered-file), table : (address stream row)
 443     # pseudocode:
 444     #   var curr-segment = null
 445     #   var line = new-stream(512, 1)
 446     #   while true
 447     #     clear-stream(line)
 448     #     read-line-buffered(in, line)
 449     #     if (line->write == 0) break             # end of file
 450     #     var word-slice = next-word(line)
 451     #     if slice-empty?(word-slice)             # whitespace
 452     #       continue
 453     #     if slice-starts-with?(word-slice, "#")  # comment
 454     #       continue
 455     #     if slice-equal?(word-slice, "==")
 456     #       var segment-name = next-word(line)
 457     #       curr-segment = get-or-insert-segment(table, segment-name, Segment-size)
 458     #       if curr-segment->write == 0
 459     #         rewind-stream(line)
 460     #         write-stream(curr-segment, line)
 461     #     else
 462     #       rewind-stream(line)
 463     #       write-stream(curr-segment, line)  # abort if curr-segment overflows
 464     #
 465     # word-slice and segment-name are both slices with disjoint lifetimes, so
 466     # we'll use the same address for them.
 467     #
 468     # registers:
 469     #   line: ECX
 470     #   word-slice and segment-name: EDX
 471     #   segment-name and curr-segment: EBX
 472     #   word-slice->start: ESI
 473     #   temporary: EAX
 474     #
 475     # . prolog
 476     55/push-EBP
 477     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 478     # . save registers
 479     50/push-EAX
 480     51/push-ECX
 481     52/push-EDX
 482     53/push-EBX
 483     56/push-ESI
 484     # var line/ECX : (address stream byte) = stream(512)
 485     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x200/imm32       # subtract from ESP
 486     68/push  0x200/imm32/length
 487     68/push  0/imm32/read
 488     68/push  0/imm32/write
 489     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
 490     # var word-slice/EDX = {0, 0}
 491     68/push  0/imm32/end
 492     68/push  0/imm32/curr
 493     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
 494 $read-segments:loop:
 495     # clear-stream(line)
 496     # . . push args
 497     51/push-ECX
 498     # . . call
 499     e8/call  clear-stream/disp32
 500     # . . discard args
 501     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 502     # read-line-buffered(in, line)
 503     # . . push args
 504     51/push-ECX
 505     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 506     # . . call
 507     e8/call  read-line-buffered/disp32
 508     # . . discard args
 509     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 510 $read-segments:check0:
 511     # if (line->write == 0) break
 512     81          7/subop/compare     0/mod/indirect  1/rm32/ECX    .           .             .           .           .               0/imm32           # compare *ECX
 513     0f 84/jump-if-equal  $read-segments:break/disp32
 514 +-- 26 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
 540     # next-word(line, word-slice)
 541     # . . push args
 542     52/push-EDX
 543     51/push-ECX
 544     # . . call
 545     e8/call  next-word/disp32
 546     # . . discard args
 547     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 548 $read-segments:check1:
 549     # if (slice-empty?(word-slice)) continue
 550     # . EAX = slice-empty?(word-slice)
 551     # . . push args
 552     52/push-EDX
 553     # . . call
 554     e8/call  slice-empty?/disp32
 555     # . . discard args
 556     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 557     # . if (EAX != 0) continue
 558     3d/compare-EAX-and  0/imm32
 559     0f 85/jump-if-not-equal  $read-segments:loop/disp32
 560 $read-segments:check-for-comment:
 561     # if (slice-starts-with?(word-slice, "#")) continue
 562     # . start/ESI = word-slice->start
 563     8b/copy                         0/mod/indirect  2/rm32/EDX    .           .             .           6/r32/ESI   .               .                 # copy *ECX to ESI
 564     # . c/EAX = *start
 565     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
 566     8a/copy-byte                    0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/AL    .               .                 # copy byte at *ESI to AL
 567     # . if (EAX == '#') continue
 568     3d/compare-EAX-and  0x23/imm32/hash
 569     0f 84/jump-if-equal  $read-segments:loop/disp32
 570 $read-segments:check-for-segment-header:
 571 +-- 42 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
 613     # if slice-equal?(word-slice, "==")
 614     #   segment-name = next-word(line)
 615     #   curr-segment = get-or-insert(table, segment-name)
 616     #   if (curr-segment->write > 0) continue
 617     # . EAX = slice-equal?(word-slice, "==")
 618     # . . push args
 619     68/push  "=="/imm32
 620     52/push-EDX
 621     # . . call
 622     e8/call  slice-equal?/disp32
 623     # . . discard args
 624     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 625     # . if (EAX == 0) goto check3
 626     3d/compare-EAX-and  0/imm32
 627     0f 84/jump-if-equal  $read-segments:regular-line/disp32
 628     # . next-word(line, segment-name)
 629     # . . push args
 630     52/push-EDX
 631     51/push-ECX
 632     # . . call
 633     e8/call  next-word/disp32
 634     # . . discard args
 635     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 636 +-- 42 lines: #?     # dump segment name ---------------------------------------------------------------------------------------------------------------------
 678     # . EAX = get-or-insert-segment(table, segment-name, Segment-size)
 679     # . . push args
 680     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Segment-size/disp32               # push *Segment-size
 681     52/push-EDX
 682     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 683     # . . call
 684     e8/call  get-or-insert-segment/disp32
 685     # . . discard args
 686     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 687     # . curr-segment = EAX
 688     89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EBX
 689     # . if (curr-segment->write > 0) continue
 690     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # copy *EAX to EAX
 691     3d/compare-EAX-and  0/imm32
 692     0f 8f/jump-if-greater  $read-segments:loop/disp32
 693     # fall through
 694 $read-segments:regular-line:
 695     # rewind-stream(line)
 696     # . . push args
 697     51/push-ECX
 698     # . . call
 699     e8/call  rewind-stream/disp32
 700     # . . discard args
 701     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 702     # write-stream(curr-segment, line)
 703     # . . push args
 704     51/push-ECX
 705     53/push-EBX
 706     # . . call
 707     e8/call  write-stream/disp32
 708     # . . discard args
 709     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 710     # loop
 711     e9/jump  $read-segments:loop/disp32
 712 $read-segments:break:
 713 $read-segments:end:
 714     # . reclaim locals
 715     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x214/imm32       # add to ESP
 716     # . restore registers
 717     5e/pop-to-ESI
 718     5b/pop-to-EBX
 719     5a/pop-to-EDX
 720     59/pop-to-ECX
 721     58/pop-to-EAX
 722     # . epilog
 723     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 724     5d/pop-to-EBP
 725     c3/return
 726 
 727 write-segments:  # out : (address buffered-file), table : (address stream row)
 728     # pseudocode:
 729     #   var curr = table->data
 730     #   var max = table->data + table->write
 731     #   while curr < max
 732     #     stream = table[i].stream
 733     #     write-stream-data(out, stream)
 734     #     curr += 8
 735     #   flush(out)
 736     #
 737     # . prolog
 738     55/push-EBP
 739     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 740     # . save registers
 741     50/push-EAX
 742     52/push-EDX
 743     56/push-ESI
 744     # ESI = table
 745     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
 746     # write/EDX = table->write
 747     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           2/r32/EDX   .               .                 # copy *ESI to EDX
 748     # curr/ESI = table->data
 749     81          0/subop/add         3/mod/direct    6/rm32/ESI    .           .             .           .           .               0xc/imm32         # add to EAX
 750     # max/EDX = curr + write
 751     01/add                          3/mod/direct    2/rm32/EDX    .           .             .           6/r32/ESI   .               .                 # add ESI to EDX
 752 $write-segments:loop:
 753     # if (curr >= max) break
 754     39/compare                      3/mod/direct    6/rm32/ESI    .           .             .           2/r32/EDX   .               .                 # compare ESI with EDX
 755     7d/jump-if-greater-or-equal  $write-segments:break/disp8
 756     # stream/EAX = table[i].stream
 757     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ESI+4) to EAX
 758     # write-stream-data(out, stream)
 759     # . . push args
 760     50/push-EAX
 761     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 762     # . . call
 763     e8/call  write-stream-data/disp32
 764     # . . discard args
 765     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 766 $write-segments:continue:
 767     # curr += 8
 768     81          0/subop/add         3/mod/direct    6/rm32/ESI    .           .             .           .           .               8/imm32           # add to ESI
 769     eb/jump  $write-segments:loop/disp8
 770 $write-segments:break:
 771     # flush(out)
 772     # . . push args
 773     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 774     # . . call
 775     e8/call  flush/disp32
 776     # . . discard args
 777     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 778 $write-segments:end:
 779     # . restore registers
 780     5e/pop-to-ESI
 781     5a/pop-to-EDX
 782     58/pop-to-EAX
 783     # . epilog
 784     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 785     5d/pop-to-EBP
 786     c3/return
 787 
 788 ## helpers
 789 
 790 # TODO: pass in an allocation descriptor
 791 get-or-insert-segment:  # table : (address stream row), s : (address slice), n : int -> EAX : (address stream)
 792     # pseudocode:
 793     #   curr = table->data
 794     #   max = &table->data[table->write]
 795     #   while curr < max
 796     #     if slice-equal?(s, *curr)
 797     #       return *(curr+4)
 798     #     curr += 8
 799     #   if table->write < table->length
 800     #     *max = slice-to-string(Heap, s)
 801     #     result = new-stream(Heap, n, 1)
 802     #     *(max+4) = result
 803     #     table->write += 8
 804     #     return result
 805     #   return 0
 806     #
 807     # . prolog
 808     55/push-EBP
 809     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 810     # . save registers
 811     51/push-ECX
 812     52/push-EDX
 813     56/push-ESI
 814     # ESI = table
 815     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
 816     # curr/ECX = table->data
 817     8d/copy-address                 1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   0xc/disp8       .                 # copy ESI+12 to ECX
 818     # max/EDX = table->data + table->write
 819     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           2/r32/EDX   .               .                 # copy *ESI to EDX
 820     8d/copy-address                 0/mod/indirect  4/rm32/sib    1/base/ECX  2/index/EDX   .           2/r32/EDX   .               .                 # copy ECX+EDX to EDX
 821 $get-or-insert-segment:search-loop:
 822     # if (curr >= max) break
 823     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
 824     7d/jump-if-greater-or-equal  $get-or-insert-segment:not-found/disp8
 825     # if (slice-equal?(s, *curr)) return *(curr+4)
 826     # . EAX = slice-equal?(s, *curr)
 827     # . . push args
 828     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
 829     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 830     # . . call
 831     e8/call  slice-equal?/disp32
 832     # . . discard args
 833     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 834     # . if (EAX != 0) return EAX = *(curr+4)
 835     3d/compare-EAX-and  0/imm32
 836     74/jump-if-equal  $get-or-insert-segment:mismatch/disp8
 837     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
 838     eb/jump  $get-or-insert-segment:end/disp8
 839 $get-or-insert-segment:mismatch:
 840     # curr += 8
 841     81          0/subop/add         3/mod/direct    1/rm32/ECX    .           .             .           .           .               8/imm32           # add to ECX
 842     # loop
 843     eb/jump  $get-or-insert-segment:search-loop/disp8
 844 $get-or-insert-segment:not-found:
 845     # result/EAX = 0
 846     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
 847     # if (table->write >= table->length) abort
 848     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # copy *ESI to ECX
 849     3b/compare                      1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   8/disp8         .                 # compare ECX with *(ESI+8)
 850     7d/jump-if-greater-or-equal  $get-or-insert-segment:abort/disp8
 851     # *max = slice-to-string(Heap, s)
 852     # . EAX = slice-to-string(Heap, s)
 853     # . . push args
 854     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 855     68/push  Heap/imm32
 856     # . . call
 857     e8/call  slice-to-string/disp32
 858     # . . discard args
 859     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 860     # . *max = EAX
 861     89/copy                         0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/EAX   .               .                 # copy EAX to *EDX
 862     # result/EAX = new-stream(Heap, n, 1)
 863     # . . push args
 864     68/push  1/imm32
 865     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
 866     68/push  Heap/imm32
 867     # . . call
 868     e8/call  new-stream/disp32
 869     # . . discard args
 870     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 871     # *(max+4) = result
 872     89/copy                         1/mod/*+disp8   2/rm32/EDX    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDX+4)
 873     # table->write += 8
 874     81          0/subop/add         0/mod/indirect  6/rm32/ESI    .           .             .           .           .               8/imm32           # add to *ESI
 875 $get-or-insert-segment:end:
 876     # . restore registers
 877     5e/pop-to-ESI
 878     5a/pop-to-EDX
 879     59/pop-to-ECX
 880     # . epilog
 881     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 882     5d/pop-to-EBP
 883     c3/return
 884 
 885 $get-or-insert-segment:abort:
 886     # . _write(2/stderr, error)
 887     # . . push args
 888     68/push  "get-or-insert-segment: too many segments\n"/imm32
 889     68/push  2/imm32/stderr
 890     # . . call
 891     e8/call  _write/disp32
 892     # . . discard args
 893     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 894     # . syscall(exit, 1)
 895     bb/copy-to-EBX  1/imm32
 896     b8/copy-to-EAX  1/imm32/exit
 897     cd/syscall  0x80/imm8
 898     # never gets here
 899 
 900 test-get-or-insert-segment:
 901     # . prolog
 902     55/push-EBP
 903     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 904     # var table/ECX : (address stream byte) = stream(2 * 8)
 905     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # subtract from ESP
 906     68/push  0x10/imm32/length
 907     68/push  0/imm32/read
 908     68/push  0/imm32/write
 909     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
 910     # EDX : (address slice) = "code"
 911     68/push  _test-code-segment-end/imm32/end
 912     68/push  _test-code-segment/imm32/start
 913     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
 914 $test-get-or-insert-segment:first-call:
 915     # - start with an empty table, insert one segment, verify that it was inserted
 916     # segment/EAX = get-or-insert-segment(table, "code" slice, 10)
 917     # . . push args
 918     68/push  0xa/imm32/segment-length
 919     52/push-EDX
 920     51/push-ECX
 921     # . . call
 922     e8/call  get-or-insert-segment/disp32
 923     # . . discard args
 924     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 925     # save segment
 926     50/push-EAX
 927     # if (segment != 0) goto next check
 928     3d/compare-EAX-and  0/imm32
 929     75/jump-if-not-equal  $test-get-or-insert-segment:check1/disp8
 930     # fail test
 931     # . _write(2/stderr, msg)
 932     # . . push args
 933     68/push  "F - test-get-or-insert-segment/0\n"/imm32
 934     68/push  2/imm32/stderr
 935     # . . call
 936     e8/call  _write/disp32
 937     # . . discard args
 938     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 939     # . increment Num-test-failures
 940     ff          0/subop/increment   0/mod/indirect  5/rm32/.disp32            .             .           .           Num-test-failures/disp32          # increment *Num-test-failures
 941     e9/jump  $test-get-or-insert-segment:end/disp32
 942 $test-get-or-insert-segment:check1:
 943     # check-ints-equal(segment->length, 10, msg)
 944     # . . push args
 945     68/push  "F - test-get-or-insert-segment/1"/imm32
 946     68/push  0xa/imm32/segment-length
 947     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           8/disp8         .                 # push *(EAX+8)
 948     # . . call
 949     e8/call  check-ints-equal/disp32
 950     # . . discard args
 951     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 952 $test-get-or-insert-segment:check2:
 953     # check-ints-equal(table->write, rowsize = 8, msg)
 954     # . . push args
 955     68/push  "F - test-get-or-insert-segment/2"/imm32
 956     68/push  8/imm32/row-size
 957     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
 958     # . . call
 959     e8/call  check-ints-equal/disp32
 960     # . . discard args
 961     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 962     # EAX = string-equal?(*table->data, "code")
 963     # . . push args
 964     68/push  "code"/imm32
 965     ff          6/subop/push        1/mod/*+disp8   1/rm32/ECX    .           .             .           .           0xc/disp8       .                 # push *(ECX+12)
 966     # . . call
 967     e8/call  string-equal?/disp32
 968     # . . discard args
 969     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 970     # check-ints-equal(EAX, 1, msg)
 971     # . . push args
 972     68/push  "F - test-get-or-insert-segment/3"/imm32
 973     68/push  1/imm32
 974     50/push-EAX
 975     # . . call
 976     e8/call  check-ints-equal/disp32
 977     # . . discard args
 978     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 979 $test-get-or-insert-segment:check3:
 980     # stream/EAX = *(table->data+4)
 981     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   0x10/disp8      .                 # copy *(ECX+16) to EAX
 982     # check-ints-equal(stream->length, 10, msg)
 983     # . . push args
 984     68/push  "F - test-get-or-insert-segment/4"/imm32
 985     68/push  0xa/imm32/segment-size
 986     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           8/disp8         .                 # push *(EAX+8)
 987     # . . call
 988     e8/call  check-ints-equal/disp32
 989     # . . discard args
 990     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 991 $test-get-or-insert-segment:second-call:
 992     # - insert the same segment name again, verify that it was reused
 993     # segment2/EAX = get-or-insert-segment(table, "code" slice, 8)
 994     # . . push args
 995     68/push  8/imm32/segment-length
 996     52/push-EDX
 997     51/push-ECX
 998     # . . call
 999     e8/call  get-or-insert-segment/disp32
1000     # . . discard args
1001     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1002     # restore old segment1
1003     5a/pop-to-EDX
1004     # check-ints-equal(segment2/EAX, segment1/EDX, msg)
1005     # . . push args
1006     68/push  "F - test-get-or-insert-segment/5"/imm32
1007     52/push-EDX
1008     50/push-EAX
1009     # . . call
1010     e8/call  check-ints-equal/disp32
1011     # . . discard args
1012     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1013     # no change to table size
1014     # . check-ints-equal(table->write, rowsize = 8, msg)
1015     # . . push args
1016     68/push  "F - test-get-or-insert-segment/6"/imm32
1017     68/push  8/imm32/row-size
1018     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
1019     # . . call
1020     e8/call  check-ints-equal/disp32
1021     # . . discard args
1022     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1023 $test-get-or-insert-segment:third-call:
1024     # - insert a new segment name, verify that it was inserted
1025     # EDX : (address slice) = "data"
1026     c7          0/subop/copy        0/mod/indirect  2/rm32/EDX    .           .             .           .           .               _test-data-segment/imm32  # copy to *EDX
1027     c7          0/subop/copy        1/mod/*+disp8   2/rm32/EDX    .           .             .           .           4/disp8         _test-data-segment-end/imm32  # copy to *(EDX+4)
1028     # segment2/EAX = get-or-insert-segment(table, "data" slice, 8)
1029     # . . push args
1030     68/push  8/imm32/segment-length
1031     52/push-EDX
1032     51/push-ECX
1033     # . . call
1034     e8/call  get-or-insert-segment/disp32
1035     # . . discard args
1036     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1037     # table gets a new row
1038     # . check-ints-equal(table->write, 2 rows = 16, msg)
1039     # . . push args
1040     68/push  "F - test-get-or-insert-segment/7"/imm32
1041     68/push  0x10/imm32/two-rows
1042     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
1043     # . . call
1044     e8/call  check-ints-equal/disp32
1045     # . . discard args
1046     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1047 $test-get-or-insert-segment:end:
1048     # . epilog
1049     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1050     5d/pop-to-EBP
1051     c3/return
1052 
1053 # (re)compute the bounds of the next word in the line
1054 # return empty string on reaching end of file
1055 next-word:  # line : (address stream byte), out : (address slice)
1056     # . prolog
1057     55/push-EBP
1058     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1059     # . save registers
1060     50/push-EAX
1061     51/push-ECX
1062     56/push-ESI
1063     57/push-EDI
1064     # ESI = line
1065     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
1066     # EDI = out
1067     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   0xc/disp8       .                 # copy *(EBP+12) to EDI
1068     # skip-chars-matching(line, ' ')
1069     # . . push args
1070     68/push  0x20/imm32/space
1071     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
1072     # . . call
1073     e8/call  skip-chars-matching/disp32
1074     # . . discard args
1075     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1076 $next-word:check0:
1077     # if (line->read >= line->write) clear out and return
1078     # . EAX = line->read
1079     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ESI+4) to EAX
1080     # . if (EAX < line->write) goto next check
1081     3b/compare                      0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # compare EAX with *ESI
1082     7c/jump-if-lesser  $next-word:check-for-comment/disp8
1083     # . return out = {0, 0}
1084     c7          0/subop/copy        0/mod/direct    7/rm32/EDI    .           .             .           .           .               0/imm32           # copy to *EDI
1085     c7          0/subop/copy        1/mod/*+disp8   7/rm32/EDI    .           .             .           .           4/disp8         0/imm32           # copy to *(EDI+4)
1086     eb/jump  $next-word:end/disp8
1087 $next-word:check-for-comment:
1088     # out->start = &line->data[line->read]
1089     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
1090     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/EAX   0xc/disp8       .                 # copy ESI+ECX+12 to EAX
1091     89/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *EDI
1092     # if (line->data[line->read] == '#') out->end = &line->data[line->write]), skip rest of stream and return
1093     # . EAX = line->data[line->read]
1094     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
1095     8a/copy-byte                    1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/AL    0xc/disp8       .                 # copy byte at *(ESI+ECX+12) to AL
1096     # . compare
1097     3d/compare-EAX-and  0x23/imm32/pound
1098     75/jump-if-not-equal  $next-word:regular-word/disp8
1099 $next-word:comment:
1100     # . out->end = &line->data[line->write]
1101     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
1102     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  0/index/EAX   .           0/r32/EAX   0xc/disp8       .                 # copy ESI+EAX+12 to EAX
1103     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
1104     # . line->read = line->write
1105     89/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(ESI+4)
1106     # . return
1107     eb/jump  $next-word:end/disp8
1108 $next-word:regular-word:
1109     # otherwise skip-chars-not-matching-whitespace(line)  # including trailing newline
1110     # . . push args
1111     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
1112     # . . call
1113     e8/call  skip-chars-not-matching-whitespace/disp32
1114     # . . discard args
1115     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1116     # out->end = &line->data[line->read]
1117     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
1118     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/EAX   0xc/disp8       .                 # copy ESI+ECX+12 to EAX
1119     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
1120 $next-word:end:
1121     # . restore registers
1122     5f/pop-to-EDI
1123     5e/pop-to-ESI
1124     59/pop-to-ECX
1125     58/pop-to-EAX
1126     # . epilog
1127     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1128     5d/pop-to-EBP
1129     c3/return
1130 
1131 test-next-word:
1132     # . prolog
1133     55/push-EBP
1134     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1135     # setup
1136     # . clear-stream(_test-stream)
1137     # . . push args
1138     68/push  _test-stream/imm32
1139     # . . call
1140     e8/call  clear-stream/disp32
1141     # . . discard args
1142     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1143     # var slice/ECX = {0, 0}
1144     68/push  0/imm32/end
1145     68/push  0/imm32/start
1146     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
1147     # write(_test-stream, "  ab")
1148     # . . push args
1149     68/push  "  ab"/imm32
1150     68/push  _test-stream/imm32
1151     # . . call
1152     e8/call  write/disp32
1153     # . . discard args
1154     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1155     # next-word(_test-stream, slice)
1156     # . . push args
1157     51/push-ECX
1158     68/push  _test-stream/imm32
1159     # . . call
1160     e8/call  next-word/disp32
1161     # . . discard args
1162     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1163     # check-ints-equal(slice->start - _test-stream->data, 2, msg)
1164     # . check-ints-equal(slice->start - _test-stream, 14, msg)
1165     # . . push args
1166     68/push  "F - test-next-word: start"/imm32
1167     68/push  0xe/imm32
1168     # . . push slice->start - _test-stream
1169     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
1170     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
1171     50/push-EAX
1172     # . . call
1173     e8/call  check-ints-equal/disp32
1174     # . . discard args
1175     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1176     # check-ints-equal(slice->end - _test-stream->data, 4, msg)
1177     # . check-ints-equal(slice->end - _test-stream, 16, msg)
1178     # . . push args
1179     68/push  "F - test-next-word: end"/imm32
1180     68/push  0x10/imm32
1181     # . . push slice->end - _test-stream
1182     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
1183     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
1184     50/push-EAX
1185     # . . call
1186     e8/call  check-ints-equal/disp32
1187     # . . discard args
1188     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1189     # . epilog
1190     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1191     5d/pop-to-EBP
1192     c3/return
1193 
1194 test-next-word-returns-whole-comment:
1195     # . prolog
1196     55/push-EBP
1197     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1198     # setup
1199     # . clear-stream(_test-stream)
1200     # . . push args
1201     68/push  _test-stream/imm32
1202     # . . call
1203     e8/call  clear-stream/disp32
1204     # . . discard args
1205     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1206     # var slice/ECX = {0, 0}
1207     68/push  0/imm32/end
1208     68/push  0/imm32/start
1209     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
1210     # write(_test-stream, "  # a")
1211     # . . push args
1212     68/push  "  # a"/imm32
1213     68/push  _test-stream/imm32
1214     # . . call
1215     e8/call  write/disp32
1216     # . . discard args
1217     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1218     # next-word(_test-stream, slice)
1219     # . . push args
1220     51/push-ECX
1221     68/push  _test-stream/imm32
1222     # . . call
1223     e8/call  next-word/disp32
1224     # . . discard args
1225     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1226     # check-ints-equal(slice->start - _test-stream->data, 2, msg)
1227     # . check-ints-equal(slice->start - _test-stream, 14, msg)
1228     # . . push args
1229     68/push  "F - test-next-word-returns-whole-comment: start"/imm32
1230     68/push  0xe/imm32
1231     # . . push slice->start - _test-stream
1232     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
1233     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
1234     50/push-EAX
1235     # . . call
1236     e8/call  check-ints-equal/disp32
1237     # . . discard args
1238     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1239     # check-ints-equal(slice->end - _test-stream->data, 5, msg)
1240     # . check-ints-equal(slice->end - _test-stream, 17, msg)
1241     # . . push args
1242     68/push  "F - test-next-word-returns-whole-comment: end"/imm32
1243     68/push  0x11/imm32
1244     # . . push slice->end - _test-stream
1245     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
1246     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
1247     50/push-EAX
1248     # . . call
1249     e8/call  check-ints-equal/disp32
1250     # . . discard args
1251     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1252     # . epilog
1253     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1254     5d/pop-to-EBP
1255     c3/return
1256 
1257 test-next-word-returns-empty-string-on-eof:
1258     # . prolog
1259     55/push-EBP
1260     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1261     # setup
1262     # . clear-stream(_test-stream)
1263     # . . push args
1264     68/push  _test-stream/imm32
1265     # . . call
1266     e8/call  clear-stream/disp32
1267     # . . discard args
1268     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1269     # var slice/ECX = {0, 0}
1270     68/push  0/imm32/end
1271     68/push  0/imm32/start
1272     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
1273     # write nothing to _test-stream
1274     # next-word(_test-stream, slice)
1275     # . . push args
1276     51/push-ECX
1277     68/push  _test-stream/imm32
1278     # . . call
1279     e8/call  next-word/disp32
1280     # . . discard args
1281     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1282     # check-ints-equal(slice->end - slice->start, 0, msg)
1283     # . . push args
1284     68/push  "F - test-next-word-returns-empty-string-on-eof"/imm32
1285     68/push  0/imm32
1286     # . . push slice->end - slice->start
1287     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
1288     2b/subtract                     0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract *ECX from EAX
1289     50/push-EAX
1290     # . . call
1291     e8/call  check-ints-equal/disp32
1292     # . . discard args
1293     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1294     # . epilog
1295     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1296     5d/pop-to-EBP
1297     c3/return
1298 
1299 == data
1300 
1301 _test-code-segment:
1302   63/c 6f/o 64/d 65/e
1303 _test-code-segment-end:
1304 
1305 _test-data-segment:
1306   64/d 61/a 74/t 61/a
1307 _test-data-segment-end:
1308 
1309 Segment-size:
1310   0x1000/imm32/4KB
1311 
1312 Heap:
1313   # curr
1314   0/imm32
1315   # limit
1316   0/imm32
1317 
1318 # . . vim:nowrap:textwidth=0