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