https://github.com/akkartik/mu/blob/main/linux/survey_baremetal.subx
   1 # Assign addresses (co-ordinates) to instructions (landmarks) in a program
   2 # (landscape).
   3 # Use the addresses assigned to replace labels.
   4 #
   5 # To build:
   6 #   $ bootstrap/bootstrap translate [01]*.subx subx-params.subx survey_baremetal.subx  -o survey_baremetal
   7 #
   8 # The expected input is a stream of bytes and some interspersed labels.
   9 # Comments and '==' segment headers are allowed, but names are ignored. The
  10 # emitted code will all lie in a single contiguous address range starting at
  11 # address 0x7c00. Addresses in segment headers are optional. If provided, this
  12 # program will insert padding in the output until the desired address is
  13 # reached.
  14 #
  15 #   $ cat x
  16 #   == code
  17 #   l1:
  18 #   aa bb l1/imm8
  19 #   cc dd l2/disp32
  20 #   l2:
  21 #   ee foo/imm32
  22 #   == data 0x7c10
  23 #   foo:
  24 #     34
  25 #
  26 # The output is the stream of bytes without segment headers or label definitions,
  27 # and with label references replaced with numeric values/displacements.
  28 #
  29 #   $ cat x  |bootstrap/bootstrap run survey_baremetal
  30 #   # 0x7c00
  31 #   aa bb nn  # some computed address
  32 #   cc dd nn nn nn nn  # some computed displacement
  33 #   ee nn nn nn nn  # address right after this instruction
  34 #   # 0x7c0e
  35 #   00 00  # padding
  36 #   # 0x7c10
  37 #   34  # data segment interleaved with code
  38 
  39 == code
  40 #   instruction                     effective address                                                   register    displacement    immediate
  41 # . op          subop               mod             rm32          base        index         scale       r32
  42 # . 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
  43 
  44 Entry:  # run tests if necessary, convert stdin if not
  45     # . prologue
  46     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
  47 
  48     # Heap = new-segment(Heap-size)
  49     # . . push args
  50     68/push  Heap/imm32
  51     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Heap-size/disp32                  # push *Heap-size
  52     # . . call
  53     e8/call  new-segment/disp32
  54     # . . discard args
  55     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
  56     # initialize-trace-stream(Trace-size)
  57     # . . push args
  58     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-size/disp32                 # push *Heap-size
  59     # . . call
  60     e8/call  initialize-trace-stream/disp32
  61     # . . discard args
  62     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
  63 
  64     # - if argc > 1 and argv[1] == "test", then return run_tests()
  65     # if (argc <= 1) goto interactive
  66     81          7/subop/compare     1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0/disp8         1/imm32           # compare *ebp
  67     7e/jump-if-<=  $subx-survey-main:interactive/disp8
  68     # if (!kernel-string-equal?(argv[1], "test")) goto interactive
  69     # . eax = kernel-string-equal?(argv[1], "test")
  70     # . . push args
  71     68/push  "test"/imm32
  72     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
  73     # . . call
  74     e8/call  kernel-string-equal?/disp32
  75     # . . discard args
  76     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
  77     # . if (eax == false) goto interactive
  78     3d/compare-eax-and  0/imm32/false
  79     74/jump-if-=  $subx-survey-main:interactive/disp8
  80     # run-tests()
  81 #?     e8/call  test-emit-output-with-padding/disp32
  82     e8/call  run-tests/disp32
  83     # syscall(exit, *Num-test-failures)
  84     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/ebx   Num-test-failures/disp32          # copy *Num-test-failures to ebx
  85     eb/jump  $subx-survey-main:end/disp8
  86 $subx-survey-main:interactive:
  87     # - otherwise convert stdin
  88     # subx-survey(Stdin, Stdout)
  89     # . . push args
  90     68/push  Stdout/imm32
  91     68/push  Stdin/imm32
  92     # . . call
  93     e8/call  subx-survey/disp32
  94     # . . discard args
  95     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
  96 #?     # . write-stream(2/stderr, Trace-stream)
  97 #?     # . . push args
  98 #?     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
  99 #?     68/push  2/imm32/stderr
 100 #?     # . . call
 101 #?     e8/call  write-stream/disp32
 102 #?     # . . discard args
 103 #?     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 104     # syscall(exit, 0)
 105     bb/copy-to-ebx  0/imm32
 106 $subx-survey-main:end:
 107     e8/call  syscall_exit/disp32
 108 
 109 subx-survey:  # infile: (addr buffered-file), out: (addr buffered-file)
 110     # pseudocode
 111     #   var in: (stream byte Input-size)
 112     #   slurp(infile, in)
 113     #   var labels: (stream {label-name, address} Max-labels)
 114     #   compute-offsets(in, labels)
 115     #   rewind-stream(in)
 116     #   emit-output(in, out, labels)
 117     #
 118     # . prologue
 119     55/push-ebp
 120     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 121     # . save registers
 122     51/push-ecx
 123     52/push-edx
 124     56/push-esi
 125     # var labels/edx: (stream {label-name, address} Max-labels)
 126     # (we get more rows than Max-labels advertises because row size is smaller than in survey_elf)
 127     # . data
 128     2b/subtract                     0/mod/indirect  5/rm32/.disp32            .             .           4/r32/esp   Max-labels/disp32                 # subtract *Max-labels from esp
 129     # . size
 130     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Max-labels/disp32                 # push *Max-labels
 131     # . read
 132     68/push  0/imm32/read
 133     # . write
 134     68/push  0/imm32/write
 135     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
 136     # var in/esi: (stream byte Input-size)
 137     # . data
 138     2b/subtract                     0/mod/indirect  5/rm32/.disp32            .             .           4/r32/esp   Input-size/disp32                 # subtract *Input-size from esp
 139     # . size
 140     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Input-size/disp32                 # push *Input-size
 141     # . read
 142     68/push  0/imm32/read
 143     # . write
 144     68/push  0/imm32/write
 145     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           4/r32/esp   .               .                 # copy esp to esi
 146     # slurp(infile, in)
 147     # . . push args
 148     56/push-esi
 149     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 150     # . . call
 151     e8/call  slurp/disp32
 152     # . . discard args
 153     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 154     # compute-offsets(in, labels)
 155     # . . push args
 156     52/push-edx
 157     56/push-esi
 158     # . . call
 159     e8/call  compute-offsets/disp32
 160     # . . discard args
 161     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 162     # rewind-stream(in)
 163     # . . push args
 164     56/push-esi
 165     # . . call
 166     e8/call  rewind-stream/disp32
 167     # . . discard args
 168     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 169     # emit-output(in, out, labels)
 170     # . . push args
 171     52/push-edx
 172     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 173     56/push-esi
 174     # . . call
 175     e8/call  emit-output/disp32
 176     # . . discard args
 177     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 178     # flush(out)
 179     # . . push args
 180     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 181     # . . call
 182     e8/call  flush/disp32
 183     # . . discard args
 184     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 185 $subx-survey:end:
 186     # . reclaim locals
 187     03/add                          0/mod/indirect  5/rm32/.disp32            .             .           4/r32/esp   Max-labels/disp32                 # add *Max-labels to esp
 188     03/add                          0/mod/indirect  5/rm32/.disp32            .             .           4/r32/esp   Input-size/disp32                 # add *Input-size to esp
 189     # . restore registers
 190     5e/pop-to-esi
 191     5a/pop-to-edx
 192     59/pop-to-ecx
 193     # . epilogue
 194     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 195     5d/pop-to-ebp
 196     c3/return
 197 
 198 test-subx-survey-computes-addresses:
 199     # input:
 200     #   == code
 201     #   ab x/imm32
 202     #   == data
 203     #   x:
 204     #     01
 205     #
 206     # trace contains (in any order):
 207     #   label x is at address 0x7c05
 208     #
 209     # . prologue
 210     55/push-ebp
 211     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 212     # setup
 213     # . clear-stream(_test-input-stream)
 214     # . . push args
 215     68/push  _test-input-stream/imm32
 216     # . . call
 217     e8/call  clear-stream/disp32
 218     # . . discard args
 219     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 220     # . clear-stream($_test-input-buffered-file->buffer)
 221     # . . push args
 222     68/push  $_test-input-buffered-file->buffer/imm32
 223     # . . call
 224     e8/call  clear-stream/disp32
 225     # . . discard args
 226     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 227     # . clear-stream(_test-output-stream)
 228     # . . push args
 229     68/push  _test-output-stream/imm32
 230     # . . call
 231     e8/call  clear-stream/disp32
 232     # . . discard args
 233     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 234     # . clear-stream($_test-output-buffered-file->buffer)
 235     # . . push args
 236     68/push  $_test-output-buffered-file->buffer/imm32
 237     # . . call
 238     e8/call  clear-stream/disp32
 239     # . . discard args
 240     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 241     # initialize input
 242     # . write(_test-input-stream, "== code\n")
 243     # . . push args
 244     68/push  "== code\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, "ab x/imm32\n")
 251     # . . push args
 252     68/push  "ab x/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, "== data\n")
 259     # . . push args
 260     68/push  "== data\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, "x:\n")
 267     # . . push args
 268     68/push  "x:\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, "01\n")
 275     # . . push args
 276     68/push  "01\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     # subx-survey(_test-input-buffered-file, _test-output-buffered-file)
 283     # . . push args
 284     68/push  _test-output-buffered-file/imm32
 285     68/push  _test-input-buffered-file/imm32
 286     # . . call
 287     e8/call  subx-survey/disp32
 288     # . . discard args
 289     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 290     # check trace
 291 +-- 26 lines: #?     # dump *Trace-stream -----------------------------------------------------------------------------------------------------------------------------------------------
 317     # . check-trace-contains("label 'x' is at address 0x00007c05.", msg)
 318     # . . push args
 319     68/push  "F - test-subx-survey-computes-addresses/0"/imm32
 320     68/push  "label 'x' is at address 0x00007c05."/imm32
 321     # . . call
 322     e8/call  check-trace-contains/disp32
 323     # . . discard args
 324     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 325     # . epilogue
 326     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 327     5d/pop-to-ebp
 328     c3/return
 329 
 330 test-subx-survey-computes-addresses-with-padding:
 331     # input:
 332     #   == code
 333     #   ab x/imm32
 334     #   == data 0x7c10
 335     #   x:
 336     #     01
 337     #
 338     # trace contains (in any order):
 339     #   label x is at address 0x7c10
 340     #
 341     # . prologue
 342     55/push-ebp
 343     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 344     # setup
 345     # . clear-stream(_test-input-stream)
 346     # . . push args
 347     68/push  _test-input-stream/imm32
 348     # . . call
 349     e8/call  clear-stream/disp32
 350     # . . discard args
 351     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 352     # . clear-stream($_test-input-buffered-file->buffer)
 353     # . . push args
 354     68/push  $_test-input-buffered-file->buffer/imm32
 355     # . . call
 356     e8/call  clear-stream/disp32
 357     # . . discard args
 358     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 359     # . clear-stream(_test-output-stream)
 360     # . . push args
 361     68/push  _test-output-stream/imm32
 362     # . . call
 363     e8/call  clear-stream/disp32
 364     # . . discard args
 365     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 366     # . clear-stream($_test-output-buffered-file->buffer)
 367     # . . push args
 368     68/push  $_test-output-buffered-file->buffer/imm32
 369     # . . call
 370     e8/call  clear-stream/disp32
 371     # . . discard args
 372     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 373     # initialize input
 374     # . write(_test-input-stream, "== code\n")
 375     # . . push args
 376     68/push  "== code\n"/imm32
 377     68/push  _test-input-stream/imm32
 378     # . . call
 379     e8/call  write/disp32
 380     # . . discard args
 381     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 382     # . write(_test-input-stream, "ab x/imm32\n")
 383     # . . push args
 384     68/push  "ab x/imm32\n"/imm32
 385     68/push  _test-input-stream/imm32
 386     # . . call
 387     e8/call  write/disp32
 388     # . . discard args
 389     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 390     # . write(_test-input-stream, "== data\n")
 391     # . . push args
 392     68/push  "== data 0x7c10\n"/imm32
 393     68/push  _test-input-stream/imm32
 394     # . . call
 395     e8/call  write/disp32
 396     # . . discard args
 397     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 398     # . write(_test-input-stream, "x:\n")
 399     # . . push args
 400     68/push  "x:\n"/imm32
 401     68/push  _test-input-stream/imm32
 402     # . . call
 403     e8/call  write/disp32
 404     # . . discard args
 405     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 406     # . write(_test-input-stream, "01\n")
 407     # . . push args
 408     68/push  "01\n"/imm32
 409     68/push  _test-input-stream/imm32
 410     # . . call
 411     e8/call  write/disp32
 412     # . . discard args
 413     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 414     # subx-survey(_test-input-buffered-file, _test-output-buffered-file)
 415     # . . push args
 416     68/push  _test-output-buffered-file/imm32
 417     68/push  _test-input-buffered-file/imm32
 418     # . . call
 419     e8/call  subx-survey/disp32
 420     # . . discard args
 421     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 422     # check trace
 423 +-- 26 lines: #?     # dump *Trace-stream -----------------------------------------------------------------------------------------------------------------------------------------------
 449     # . check-trace-contains("label 'x' is at address 0x00007c10.", msg)
 450     # . . push args
 451     68/push  "F - test-subx-survey-computes-addresses-with-padding/0"/imm32
 452     68/push  "label 'x' is at address 0x00007c10."/imm32
 453     # . . call
 454     e8/call  check-trace-contains/disp32
 455     # . . discard args
 456     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 457     # . epilogue
 458     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 459     5d/pop-to-ebp
 460     c3/return
 461 
 462 compute-offsets:  # in: (addr stream byte), labels: (addr stream {(handle array byte), address})
 463     # pseudocode:
 464     #   var current-address = 0x7c00
 465     #   var line: (stream byte 512)
 466     #   while true                                  # line loop
 467     #     clear-stream(line)
 468     #     read-line(in, line)
 469     #     if (line->write == 0) break               # end of file
 470     #     while true                                # word loop
 471     #       word-slice = next-word(line)
 472     #       if slice-empty?(word-slice)             # end of line
 473     #         break
 474     #       else if slice-starts-with?(word-slice, "#")  # comment
 475     #         break                                 # end of line
 476     #       else if slice-equal?(word-slice, "==")  # segment header
 477     #         word-slice = next-word(line)
 478     #         if slice-empty?(word-slice)
 479     #           abort
 480     #         word-slice = next-word(line)  # segment address
 481     #         if slice-empty?(word-slice)
 482     #           goto line-loop              # segment address is optional
 483     #         new-address = parse-hex-int-from-slice(word-slice)
 484     #         if new-address < current-address
 485     #           abort
 486     #         current-address = new-address
 487     #       else if label?(word-slice)
 488     #         strip trailing ':' from word-slice
 489     #         trace("label '" word-slice "' is at address " current-address ".")
 490     #         # labels occupy no space, so no need to increment offsets
 491     #       else
 492     #         width = compute-width-of-slice(word-slice)
 493     #         current-address += width
 494     #
 495     # . prologue
 496     55/push-ebp
 497     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 498     # . save registers
 499     50/push-eax
 500     51/push-ecx
 501     52/push-edx
 502     53/push-ebx
 503     56/push-esi
 504     57/push-edi
 505     # var current-address/esi: int = 0x7c00
 506     be/copy-to-esi  0x7c00/imm32
 507     # var line/ecx: (stream byte 512)
 508     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x200/imm32       # subtract from esp
 509     68/push  0x200/imm32/size
 510     68/push  0/imm32/read
 511     68/push  0/imm32/write
 512     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
 513     # var word-slice/edx: (addr slice)
 514     68/push  0/imm32
 515     68/push  0/imm32
 516     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
 517 $compute-offsets:line-loop:
 518     # clear-stream(line)
 519     # . . push args
 520     51/push-ecx
 521     # . . call
 522     e8/call  clear-stream/disp32
 523     # . . discard args
 524     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 525     # read-line(in, line)
 526     # . . push args
 527     51/push-ecx
 528     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 529     # . . call
 530     e8/call  read-line/disp32
 531     # . . discard args
 532     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 533     # if (line->write == 0) break
 534     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
 535     3d/compare-eax-and  0/imm32
 536     0f 84/jump-if-=  $compute-offsets:end/disp32
 537 +-- 33 lines: #?     # dump line --------------------------------------------------------------------------------------------------------------------------------------------------------
 570 $compute-offsets:word-loop:
 571     # next-word(line, word-slice)
 572     # . . push args
 573     52/push-edx
 574     51/push-ecx
 575     # . . call
 576     e8/call  next-word/disp32
 577     # . . discard args
 578     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 579 $compute-offsets:case-empty:
 580     # if slice-empty?(word-slice) break
 581     # . eax = slice-empty?(word-slice)
 582     # . . push args
 583     52/push-edx
 584     # . . call
 585     e8/call  slice-empty?/disp32
 586     # . . discard args
 587     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 588     # . if (eax != false) break
 589     3d/compare-eax-and  0/imm32/false
 590     0f 85/jump-if-!=  $compute-offsets:line-loop/disp32
 591 $compute-offsets:case-comment:
 592     # if slice-starts-with?(word-slice, "#") break
 593     # . . push args
 594     68/push  "#"/imm32
 595     52/push-edx
 596     # . . call
 597     e8/call  slice-starts-with?/disp32
 598     # . . discard args
 599     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 600     # . if (eax != false) break
 601     3d/compare-eax-and  0/imm32/false
 602     0f 85/jump-if-!=  $compute-offsets:line-loop/disp32
 603 $compute-offsets:case-segment-header:
 604     # if !slice-equal?(word-slice, "==") goto next case
 605     # . eax = slice-equal?(word-slice, "==")
 606     # . . push args
 607     68/push  "=="/imm32
 608     52/push-edx
 609     # . . call
 610     e8/call  slice-equal?/disp32
 611     # . . discard args
 612     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 613     # . if (eax != false) break
 614     3d/compare-eax-and  0/imm32/false
 615     0f 84/jump-if-=  $compute-offsets:case-label/disp32
 616     # next-word(line, word-slice)
 617     # . . push args
 618     52/push-edx
 619     51/push-ecx
 620     # . . call
 621     e8/call  next-word/disp32
 622     # . . discard args
 623     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 624     # if slice-empty?(word-slice) abort
 625     # . eax = slice-empty?(word-slice)
 626     # . . push args
 627     52/push-edx
 628     # . . call
 629     e8/call  slice-empty?/disp32
 630     # . . discard args
 631     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 632     # . if (eax != false) abort
 633     3d/compare-eax-and  0/imm32/false
 634     0f 85/jump-if-!=  $compute-offsets:abort/disp32
 635     # next-word(line, word-slice)
 636     # . . push args
 637     52/push-edx
 638     51/push-ecx
 639     # . . call
 640     e8/call  next-word/disp32
 641     # . . discard args
 642     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 643     # if slice-empty?(word-slice) break
 644     # . eax = slice-empty?(word-slice)
 645     # . . push args
 646     52/push-edx
 647     # . . call
 648     e8/call  slice-empty?/disp32
 649     # . . discard args
 650     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 651     # . if (eax != false) break
 652     3d/compare-eax-and  0/imm32/false
 653     0f 85/jump-if-!=  $compute-offsets:line-loop/disp32
 654     # var new-address/eax: int = parse-hex-int-from-slice(word-slice)
 655     # . . push args
 656     52/push-edx
 657     # . . call
 658     e8/call  parse-hex-int-from-slice/disp32
 659     # . . discard args
 660     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 661     # if (new-address < current-address) abort
 662     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           6/r32/esi   .               .                 # compare eax with esi
 663     0f 82/jump-if-addr<  $compute-offsets:error-bad-segment-address/disp32
 664     # current-address = new-address
 665     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to esi
 666     # break
 667     e9/jump  $compute-offsets:line-loop/disp32
 668 $compute-offsets:case-label:
 669     # if (!label?(word-slice)) goto next case
 670     # . eax = label?(word-slice)
 671     # . . push args
 672     52/push-edx
 673     # . . call
 674     e8/call  label?/disp32
 675     # . . discard args
 676     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 677     # . if (eax == false) goto next case
 678     3d/compare-eax-and  0/imm32/false
 679     0f 84/jump-if-=  $compute-offsets:case-default/disp32
 680     # strip trailing ':' from word-slice
 681     ff          1/subop/decrement   1/mod/*+disp8   2/rm32/edx    .           .             .           .           4/disp8         .                 # decrement *(edx+4)
 682     # var tmp/eax: (addr int) = get-or-insert-slice(labels, word-slice, row-size=12)
 683     # . . push args
 684     68/push  Heap/imm32
 685     68/push  0xc/imm32/row-size
 686     52/push-edx
 687     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 688     # . . call
 689     e8/call  get-or-insert-slice/disp32
 690     # . . discard args
 691     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
 692     # *tmp = current-address
 693     89/copy                         0/mod/indirect  0/rm32/eax    .           .             .           6/r32/esi   .               .                 # copy esi to *eax
 694     # trace-slsns("label '" word-slice "' is at address " current-address ".")
 695     # . . push args
 696     68/push  "."/imm32
 697     56/push-esi
 698     68/push  "' is at address "/imm32
 699     52/push-edx
 700     68/push  "label '"/imm32
 701     # . . call
 702     e8/call  trace-slsns/disp32
 703     # . . discard args
 704     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
 705     # continue
 706     e9/jump  $compute-offsets:word-loop/disp32
 707 $compute-offsets:case-default:
 708     # width/eax = compute-width-of-slice(word-slice)
 709     # . . push args
 710     52/push-edx
 711     # . . call
 712     e8/call compute-width-of-slice/disp32
 713     # . . discard args
 714     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 715     # current-address += width
 716     01/add                          3/mod/direct    6/rm32/esi    .           .             .           0/r32/eax   .               .                 # add eax to *esi
 717 +-- 41 lines: #?     # dump segment-offset ----------------------------------------------------------------------------------------------------------------------------------------------
 758     e9/jump $compute-offsets:word-loop/disp32
 759 $compute-offsets:end:
 760     # . reclaim locals
 761     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x214/imm32       # add to esp
 762     # . restore registers
 763     5f/pop-to-edi
 764     5e/pop-to-esi
 765     5b/pop-to-ebx
 766     5a/pop-to-edx
 767     59/pop-to-ecx
 768     58/pop-to-eax
 769     # . epilogue
 770     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 771     5d/pop-to-ebp
 772     c3/return
 773 
 774 $compute-offsets:abort:
 775     # . _write(2/stderr, error)
 776     # . . push args
 777     68/push  "'==' must be followed by segment name and optionally an address\n"/imm32
 778     68/push  2/imm32/stderr
 779     # . . call
 780     e8/call  _write/disp32
 781     # . . discard args
 782     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 783     # . syscall(exit, 1)
 784     bb/copy-to-ebx  1/imm32
 785     e8/call  syscall_exit/disp32
 786     # never gets here
 787 
 788 $compute-offsets:error-bad-segment-address:
 789     # . _write(2/stderr, error)
 790     # . . push args
 791     68/push  "'==' specifies an address that implies negative padding\n"/imm32
 792     68/push  2/imm32/stderr
 793     # . . call
 794     e8/call  _write/disp32
 795     # . . discard args
 796     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 797     # . syscall(exit, 1)
 798     bb/copy-to-ebx  1/imm32
 799     e8/call  syscall_exit/disp32
 800     # never gets here
 801 
 802 test-compute-offsets:
 803     # input:
 804     #   == code
 805     #   ab x/imm32  # skip comment
 806     #   == data
 807     #   00
 808     #   x:
 809     #     34
 810     #
 811     # trace contains (in any order):
 812     #   label 'x' is at address 0x7c06.
 813     #
 814     # . prologue
 815     55/push-ebp
 816     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 817     # setup
 818     # . clear-stream(_test-input-stream)
 819     # . . push args
 820     68/push  _test-input-stream/imm32
 821     # . . call
 822     e8/call  clear-stream/disp32
 823     # . . discard args
 824     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 825     # var labels/edx: (stream byte 2*12)
 826     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x18/imm32        # subtract from esp
 827     68/push  0x18/imm32/size
 828     68/push  0/imm32/read
 829     68/push  0/imm32/write
 830     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
 831     # initialize input
 832     # . write(_test-input-stream, "== code\n")
 833     # . . push args
 834     68/push  "== code\n"/imm32
 835     68/push  _test-input-stream/imm32
 836     # . . call
 837     e8/call  write/disp32
 838     # . . discard args
 839     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 840     # . write(_test-input-stream, "ab x/imm32  # skip comment\n")
 841     # . . push args
 842     68/push  "ab x/imm32  # skip comment\n"/imm32
 843     68/push  _test-input-stream/imm32
 844     # . . call
 845     e8/call  write/disp32
 846     # . . discard args
 847     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 848     # . write(_test-input-stream, "== data\n")
 849     # . . push args
 850     68/push  "== data\n"/imm32
 851     68/push  _test-input-stream/imm32
 852     # . . call
 853     e8/call  write/disp32
 854     # . . discard args
 855     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 856     # . write(_test-input-stream, "00\n")
 857     # . . push args
 858     68/push  "00\n"/imm32
 859     68/push  _test-input-stream/imm32
 860     # . . call
 861     e8/call  write/disp32
 862     # . . discard args
 863     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 864     # . write(_test-input-stream, "x:\n")
 865     # . . push args
 866     68/push  "x:\n"/imm32
 867     68/push  _test-input-stream/imm32
 868     # . . call
 869     e8/call  write/disp32
 870     # . . discard args
 871     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 872     # . write(_test-input-stream, "34\n")
 873     # . . push args
 874     68/push  "34\n"/imm32
 875     68/push  _test-input-stream/imm32
 876     # . . call
 877     e8/call  write/disp32
 878     # . . discard args
 879     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 880     # compute-offsets(_test-input-stream, labels)
 881     # . . push args
 882     52/push-edx
 883     68/push  _test-input-stream/imm32
 884     # . . call
 885     e8/call  compute-offsets/disp32
 886     # . . discard args
 887     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32          # add to esp
 888 +-- 26 lines: #?     # dump *Trace-stream -----------------------------------------------------------------------------------------------------------------------------------------------
 914     # . check-trace-contains("label 'x' is at address 0x00007c06.", msg)
 915     # . . push args
 916     68/push  "F - test-compute-offsets"/imm32
 917     68/push  "label 'x' is at address 0x00007c06."/imm32
 918     # . . call
 919     e8/call  check-trace-contains/disp32
 920     # . . discard args
 921     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 922     # . check-ints-equal(labels->write, 0xc, msg)
 923     # . . push args
 924     68/push  "F - test-compute-offsets-maintains-labels-write-index"/imm32
 925     68/push  0xc/imm32/1-entry
 926     ff          6/subop/push        0/mod/indirect  2/rm32/edx    .           .             .           .           .               .                 # push *edx
 927     # . . call
 928     e8/call  check-ints-equal/disp32
 929     # . . discard args
 930     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 931     # . reclaim locals
 932     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x24/imm32        # add to esp
 933     # . epilogue
 934     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 935     5d/pop-to-ebp
 936     c3/return
 937 
 938 # global scratch space for emit-output
 939 == data
 940 
 941 emit-output:datum:  # slice
 942   0/imm32/start
 943   0/imm32/end
 944 
 945 == code
 946 
 947 emit-output:  # in: (addr stream byte), out: (addr buffered-file), labels: (addr stream {(handle array byte), address})
 948     # pseudocode:
 949     #   var address-of-next-instruction = 0x7c00
 950     #   var line: (stream byte 512)
 951     #   line-loop:
 952     #   while true
 953     #     clear-stream(line)
 954     #     read-line(in, line)
 955     #     if (line->write == 0) break               # end of file
 956     #     address-of-next-instruction += num-bytes(line)
 957     #     var far-jump-or-call? = far-jump-or-call?(line)
 958     #     rewind-stream(line)
 959     #     while true
 960     #       var word-slice = next-word(line)
 961     #       if slice-empty?(word-slice)             # end of line
 962     #         break
 963     #       if slice-starts-with?(word-slice, "#")  # comment
 964     #         break
 965     #       if label?(word-slice)                # no need for label declarations anymore
 966     #         goto line-loop                        # don't insert empty lines
 967     #       if slice-equal?(word-slice, "==")       # no need for segment header lines
 968     #         word-slice = next-word(line)          # skip segment name
 969     #         word-slice = next-word(line)
 970     #         if !slice-empty?(word-slice)
 971     #           new-address = parse-hex-int-from-slice(word-slice)
 972     #           write-buffered(out, "# " address-of-next-instruction "\n")
 973     #           while address-of-next-instruction < new-address
 974     #             write-buffered("00")
 975     #             ++address-of-next-instruction
 976     #           write-buffered(out, "# " address-of-next-instruction "\n")
 977     #           goto line-loop                      # don't insert empty lines
 978     #       if length(word-slice) == 2
 979     #         write-slice-buffered(out, word-slice)
 980     #         write-buffered(out, " ")
 981     #         continue
 982     #       var datum: (addr slice) = next-token-from-slice(word-slice->start, word-slice->end, "/")
 983     #       var address: (addr int) = get-slice(labels, datum)
 984     #       if has-metadata?(word-slice, "imm8")
 985     #         emit(out, *address, 1)
 986     #       else if has-metadata?(word-slice, "imm16")
 987     #         emit(out, *address, 2)
 988     #       else if has-metadata?(word-slice, "imm32")
 989     #         emit(out, *address, 4)
 990     #       else if has-metadata?(word-slice, "disp8")
 991     #         value = *address - address-of-next-instruction
 992     #         emit(out, value, 1)
 993     #       else if has-metadata?(word-slice, "disp32")
 994     #         if far-jump-or-call?
 995     #           value = *address - address-of-next-instruction
 996     #         else
 997     #           value = *address
 998     #         emit(out, value, 4)
 999     #       else
1000     #         abort
1001     #     write-buffered(out, "\n")
1002     #
1003     # registers:
1004     #   line: ecx
1005     #   word-slice: edx
1006     #   address-of-next-instruction: ebx
1007     #   far-jump-or-call?: edi
1008     #   address: esi (inner loop only)
1009     #   temporaries: eax, esi (outer loop)
1010     #
1011     # . prologue
1012     55/push-ebp
1013     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1014     # . save registers
1015     50/push-eax
1016     51/push-ecx
1017     52/push-edx
1018     53/push-ebx
1019     56/push-esi
1020     57/push-edi
1021     # var line/ecx: (stream byte 512)
1022     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x200/imm32       # subtract from esp
1023     68/push  0x200/imm32/size
1024     68/push  0/imm32/read
1025     68/push  0/imm32/write
1026     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1027     # var word-slice/edx: slice
1028     68/push  0/imm32/end
1029     68/push  0/imm32/start
1030     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
1031     # var address-of-next-instruction/ebx = 0x7c00
1032     bb/copy-to-ebx  0x7c00/imm32
1033 $emit-output:line-loop:
1034     # clear-stream(line)
1035     # . . push args
1036     51/push-ecx
1037     # . . call
1038     e8/call  clear-stream/disp32
1039     # . . discard args
1040     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1041     # read-line(in, line)
1042     # . . push args
1043     51/push-ecx
1044     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1045     # . . call
1046     e8/call  read-line/disp32
1047     # . . discard args
1048     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1049 +-- 33 lines: #?     # dump line --------------------------------------------------------------------------------------------------------------------------------------------------------
1082 $emit-output:check-for-end-of-input:
1083     # if (line->write == 0) break
1084     81          7/subop/compare     0/mod/indirect  1/rm32/ecx    .           .             .           .           .               0/imm32           # compare *ecx
1085     0f 84/jump-if-=  $emit-output:end/disp32
1086     # address-of-next-instruction += num-bytes(line)
1087     # . eax = num-bytes(line)
1088     # . . push args
1089     51/push-ecx
1090     # . . call
1091     e8/call  num-bytes/disp32
1092     # . . discard args
1093     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1094     # . ebx += eax
1095     01/add                          3/mod/direct    3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # add eax to ebx
1096     # var far-jump-or-call?/edi: boolean = far-jump-or-call?(line)
1097     # . . push args
1098     51/push-ecx
1099     # . . call
1100     e8/call  far-jump-or-call?/disp32
1101     # . . discard args
1102     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1103     # rewind-stream(line)
1104     # . . push args
1105     51/push-ecx
1106     # . . call
1107     e8/call  rewind-stream/disp32
1108     # . . discard args
1109     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1110 $emit-output:word-loop:
1111     # next-word(line, word-slice)
1112     # . . push args
1113     52/push-edx
1114     51/push-ecx
1115     # . . call
1116     e8/call  next-word/disp32
1117     # . . discard args
1118     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1119 +-- 33 lines: #?     # dump word-slice --------------------------------------------------------------------------------------------------------------------------------------------------
1152 $emit-output:check-for-end-of-line:
1153     # if (slice-empty?(word-slice)) break
1154     # . eax = slice-empty?(word-slice)
1155     # . . push args
1156     52/push-edx
1157     # . . call
1158     e8/call  slice-empty?/disp32
1159     # . . discard args
1160     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1161     # . if (eax != 0) break
1162     3d/compare-eax-and  0/imm32/false
1163     0f 85/jump-if-!=  $emit-output:next-line/disp32
1164 $emit-output:check-for-comment:
1165     # if (slice-starts-with?(word-slice, "#")) break
1166     # . start/esi = word-slice->start
1167     8b/copy                         0/mod/indirect  2/rm32/edx    .           .             .           6/r32/esi   .               .                 # copy *edx to esi
1168     # . c/eax = *start
1169     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
1170     8a/copy-byte                    0/mod/indirect  6/rm32/esi    .           .             .           0/r32/AL    .               .                 # copy byte at *esi to AL
1171     # . if (eax == '#') break
1172     3d/compare-eax-and  0x23/imm32/hash
1173     0f 84/jump-if-=  $emit-output:next-line/disp32
1174 $emit-output:check-for-label:
1175     # if label?(word-slice) break
1176     # . eax = label?(word-slice)
1177     # . . push args
1178     52/push-edx
1179     # . . call
1180     e8/call  label?/disp32
1181     # . . discard args
1182     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1183     # . if (eax != false) break
1184     3d/compare-eax-and  0/imm32/false
1185     0f 85/jump-if-!=  $emit-output:line-loop/disp32
1186 $emit-output:check-for-segment-header:
1187     # if !slice-equal?(word-slice, "==") goto next check
1188     # . eax = slice-equal?(word-slice, "==")
1189     # . . push args
1190     68/push  "=="/imm32
1191     52/push-edx
1192     # . . call
1193     e8/call  slice-equal?/disp32
1194     # . . discard args
1195     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1196     # . if (eax == false) goto next check
1197     3d/compare-eax-and  0/imm32/false
1198     0f 84/jump-if-=  $emit-output:2-character/disp32
1199     # skip segment name
1200     # . next-word(line, word-slice)
1201     # . . push args
1202     52/push-edx
1203     51/push-ecx
1204     # . . call
1205     e8/call  next-word/disp32
1206     # . . discard args
1207     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1208     # compute segment address if it exists
1209     # . next-word(line, word-slice)
1210     # . . push args
1211     52/push-edx
1212     51/push-ecx
1213     # . . call
1214     e8/call  next-word/disp32
1215     # . . discard args
1216     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1217     # . if slice-empty?(word-slice) goto padding-done
1218     # . . push args
1219     52/push-edx
1220     # . . call
1221     e8/call  slice-empty?/disp32
1222     # . . discard args
1223     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1224     # . .
1225     3d/compare-eax-and  0/imm32/false
1226     0f 85/jump-if-!=  $emit-output:padding-done/disp32
1227     # . var new-address/eax: int = parse-hex-int-from-slice(word-slice)
1228     # . . push args
1229     52/push-edx
1230     # . . call
1231     e8/call  parse-hex-int-from-slice/disp32
1232     # . . discard args
1233     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1234     # write-buffered(out, "# " address-of-next-instruction "\n")
1235     # . write-buffered(out, "# ")
1236     # . . push args
1237     68/push  "# "/imm32
1238     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1239     # . . call
1240     e8/call  write-buffered/disp32
1241     # . . discard args
1242     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1243     # . write-int32-hex-buffered(out, address-of-next-instruction)
1244     # . . push args
1245     53/push-ebx
1246     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1247     # . . call
1248     e8/call  write-int32-hex-buffered/disp32
1249     # . . discard args
1250     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1251     # . write-buffered(out, "\n")
1252     # . . push args
1253     68/push  Newline/imm32
1254     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1255     # . . call
1256     e8/call  write-buffered/disp32
1257     # . . discard args
1258     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1259 $emit-output:padding-loop:
1260     # if (address-of-next-instruction >= new-address) goto padding-loop-done
1261     39/compare                      3/mod/direct    3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # compare ebx with eax
1262     73/jump-if-addr>=  $emit-output:padding-loop-done/disp8
1263     # if (address-of-next-instruction % 8 == 0) write-buffered("\n")
1264     53/push-ebx
1265     81          4/subop/and         3/mod/direct    3/rm32/ebx    .           .             .           .           .               7/imm32           # bitwise and of ebx
1266     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0/imm32           # compare ebx
1267     5b/pop-to-ebx
1268     75/jump-if-!=  $emit-output:padding-core/disp8
1269     # . write-buffered(out, "\n")
1270     # . . push args
1271     68/push  Newline/imm32
1272     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1273     # . . call
1274     e8/call  write-buffered/disp32
1275     # . . discard args
1276     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1277 $emit-output:padding-core:
1278     # write-buffered("00")
1279     # . . push args
1280     68/push  "00 "/imm32
1281     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1282     # . . call
1283     e8/call  write-buffered/disp32
1284     # . . discard args
1285     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1286     # ++address-of-next-instruction
1287     43/increment-ebx
1288     # loop
1289     eb/jump $emit-output:padding-loop/disp8
1290 $emit-output:padding-loop-done:
1291     # . write-buffered(out, "\n")
1292     # . . push args
1293     68/push  Newline/imm32
1294     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1295     # . . call
1296     e8/call  write-buffered/disp32
1297     # . . discard args
1298     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1299 $emit-output:padding-done:
1300     # write-buffered(out, "# " address-of-next-instruction "\n")
1301     # . write-buffered(out, "# ")
1302     # . . push args
1303     68/push  "# "/imm32
1304     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1305     # . . call
1306     e8/call  write-buffered/disp32
1307     # . . discard args
1308     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1309     # . write-int32-hex-buffered(out, address-of-next-instruction)
1310     # . . push args
1311     53/push-ebx
1312     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1313     # . . call
1314     e8/call  write-int32-hex-buffered/disp32
1315     # . . discard args
1316     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1317     # . write-buffered(out, "\n")
1318     # . . push args
1319     68/push  Newline/imm32
1320     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1321     # . . call
1322     e8/call  write-buffered/disp32
1323     # . . discard args
1324     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1325     #
1326     e9/jump  $emit-output:line-loop/disp32
1327 $emit-output:2-character:
1328     # if (size(word-slice) != 2) goto next check
1329     # . eax = size(word-slice)
1330     8b/copy                         1/mod/*+disp8   2/rm32/edx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(edx+4) to eax
1331     2b/subtract                     0/mod/indirect  2/rm32/edx    .           .             .           0/r32/eax   .               .                 # subtract *edx from eax
1332     # . if (eax != 2) goto next check
1333     3d/compare-eax-and  2/imm32
1334     75/jump-if-!=  $emit-output:check-metadata/disp8
1335     # write-slice-buffered(out, word-slice)
1336     # . . push args
1337     52/push-edx
1338     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1339     # . . call
1340     e8/call  write-slice-buffered/disp32
1341     # . . discard args
1342     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1343     # write-buffered(out, " ")
1344     # . . push args
1345     68/push  Space/imm32
1346     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1347     # . . call
1348     e8/call  write-buffered/disp32
1349     # . . discard args
1350     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1351     # continue
1352     e9/jump  $emit-output:word-loop/disp32
1353 $emit-output:check-metadata:
1354     # - if we get here, 'word-slice' must be a label to be looked up
1355     # datum = next-token-from-slice(word-slice->start, word-slice->end, "/")
1356     # . . push args
1357     68/push  emit-output:datum/imm32
1358     68/push  0x2f/imm32/slash
1359     ff          6/subop/push        1/mod/*+disp8   2/rm32/edx    .           .             .           .           4/disp8         .                 # push *(edx+4)
1360     ff          6/subop/push        0/mod/indirect  2/rm32/edx    .           .             .           .           .               .                 # push *edx
1361     # . . call
1362     e8/call  next-token-from-slice/disp32
1363     # . . discard args
1364     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
1365 +-- 33 lines: #?     # dump datum -------------------------------------------------------------------------------------------------------------------------------------------------------
1398     # address/esi: (addr int) = get-slice(labels, datum, row-size=12, "label table")
1399     # . eax = get-slice(labels, datum, row-size=24, "label table")
1400     # . . push args
1401     68/push  "label table"/imm32
1402     68/push  0xc/imm32/row-size
1403     68/push  emit-output:datum/imm32
1404     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
1405     # . . call
1406     e8/call  get-slice/disp32
1407     # . . discard args
1408     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
1409     # . esi = eax
1410     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to esi
1411 $emit-output:check-imm8:
1412     # if (!has-metadata?(word-slice, "imm8")) goto next check
1413     # . eax = has-metadata?(edx, "imm8")
1414     # . . push args
1415     68/push  "imm8"/imm32
1416     52/push-edx
1417     # . . call
1418     e8/call  has-metadata?/disp32
1419     # . . discard args
1420     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1421     # . if (eax != false) abort
1422     3d/compare-eax-and  0/imm32/false
1423     74/jump-if-=  $emit-output:check-imm16/disp8
1424 $emit-output:emit-imm8:
1425     # emit-hex(out, *address, 1)
1426     # . . push args
1427     68/push  1/imm32
1428     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
1429     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1430     # . . call
1431     e8/call  emit-hex/disp32
1432     # . . discard args
1433     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1434     # continue
1435     e9/jump  $emit-output:word-loop/disp32
1436 $emit-output:check-imm16:
1437     # if (!has-metadata?(word-slice, "imm16")) goto next check
1438     # . eax = has-metadata?(edx, "imm16")
1439     # . . push args
1440     68/push  "imm16"/imm32
1441     52/push-edx
1442     # . . call
1443     e8/call  has-metadata?/disp32
1444     # . . discard args
1445     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1446     # . if (eax == false) goto next check
1447     3d/compare-eax-and  0/imm32/false
1448     74/jump-if-=  $emit-output:check-imm32/disp8
1449 +-- 33 lines: #?     # dump *address ----------------------------------------------------------------------------------------------------------------------------------------------------
1482 $emit-output:emit-imm16:
1483     # emit-hex(out, *address, 2)
1484     # . . push args
1485     68/push  2/imm32
1486     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
1487     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1488     # . . call
1489     e8/call  emit-hex/disp32
1490     # . . discard args
1491     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1492     # TODO: ensure that the higher 2 bytes are zero
1493     # continue
1494     e9/jump  $emit-output:word-loop/disp32
1495 $emit-output:check-imm32:
1496     # if (!has-metadata?(word-slice, "imm32")) goto next check
1497     # . eax = has-metadata?(edx, "imm32")
1498     # . . push args
1499     68/push  "imm32"/imm32
1500     52/push-edx
1501     # . . call
1502     e8/call  has-metadata?/disp32
1503     # . . discard args
1504     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1505     # . if (eax == false) goto next check
1506     3d/compare-eax-and  0/imm32/false
1507     74/jump-if-=  $emit-output:check-disp8/disp8
1508 +-- 33 lines: #?     # dump *address ----------------------------------------------------------------------------------------------------------------------------------------------------
1541 $emit-output:emit-imm32:
1542     # emit-hex(out, *address, 4)
1543     # . . push args
1544     68/push  4/imm32
1545     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
1546     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1547     # . . call
1548     e8/call  emit-hex/disp32
1549     # . . discard args
1550     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1551     # continue
1552     e9/jump  $emit-output:word-loop/disp32
1553 $emit-output:check-disp8:
1554     # if (!has-metadata?(word-slice, "disp8")) goto next check
1555     # . eax = has-metadata?(edx, "disp8")
1556     # . . push args
1557     68/push  "disp8"/imm32
1558     52/push-edx
1559     # . . call
1560     e8/call  has-metadata?/disp32
1561     # . . discard args
1562     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1563     # . if (eax == false) goto next check
1564     3d/compare-eax-and  0/imm32/false
1565     74/jump-if-=  $emit-output:check-disp16/disp8
1566 $emit-output:emit-disp8:
1567     # emit-hex(out, *address - address-of-next-instruction, 1)
1568     # . . push args
1569     68/push  1/imm32
1570     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
1571     29/subtract                     3/mod/direct    0/rm32/eax    .           .             .           3/r32/ebx   .               .                 # subtract ebx from eax
1572     50/push-eax
1573     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1574     # . . call
1575     e8/call  emit-hex/disp32
1576     # . . discard args
1577     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1578     # continue
1579     e9/jump  $emit-output:word-loop/disp32
1580 $emit-output:check-disp16:
1581     # if (!has-metadata?(word-slice, "disp16")) goto next check
1582     # . eax = has-metadata?(edx, "disp16")
1583     # . . push args
1584     68/push  "disp16"/imm32
1585     52/push-edx
1586     # . . call
1587     e8/call  has-metadata?/disp32
1588     # . . discard args
1589     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1590     # . if (eax == false) goto next check
1591     3d/compare-eax-and  0/imm32/false
1592     74/jump-if-=  $emit-output:check-disp32/disp8
1593 $emit-output:emit-disp16:
1594     # emit-hex(out, *address - address-of-next-instruction, 2)
1595     # . . push args
1596     68/push  2/imm32
1597     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
1598     29/subtract                     3/mod/direct    0/rm32/eax    .           .             .           3/r32/ebx   .               .                 # subtract ebx from eax
1599     50/push-eax
1600     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1601     # . . call
1602     e8/call  emit-hex/disp32
1603     # . . discard args
1604     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1605     # continue
1606     e9/jump  $emit-output:word-loop/disp32
1607 $emit-output:check-disp32:
1608     # if (!has-metadata?(word-slice, "disp32")) abort
1609     # . eax = has-metadata?(edx, "disp32")
1610     # . . push args
1611     68/push  "disp32"/imm32
1612     52/push-edx
1613     # . . call
1614     e8/call  has-metadata?/disp32
1615     # . . discard args
1616     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1617     # . if (eax == false) abort
1618     3d/compare-eax-and  0/imm32/false
1619     0f 84/jump-if-=  $emit-output:abort/disp32
1620 $emit-output:emit-disp32:
1621     # var value/eax = *address
1622     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
1623     # if (far-jump-or-call?) value -= address-of-next-instruction
1624     81          7/subop/compare     3/mod/direct    7/rm32/edi    .           .             .           .           .               0/imm32/false     # compare edi
1625     74/jump-if-=  $emit-output:really-emit-disp32/disp8
1626     29/subtract                     3/mod/direct    0/rm32/eax    .           .             .           3/r32/ebx   .               .                 # subtract ebx from eax
1627 $emit-output:really-emit-disp32:
1628     # emit-hex(out, value, 4)
1629     # . . push args
1630     68/push  4/imm32
1631     50/push-eax
1632     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1633     # . . call
1634     e8/call  emit-hex/disp32
1635     # . . discard args
1636     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1637     # continue
1638     e9/jump  $emit-output:word-loop/disp32
1639 $emit-output:next-line:
1640     # write-buffered(out, "\n")
1641     # . . push args
1642     68/push  Newline/imm32
1643     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1644     # . . call
1645     e8/call  write-buffered/disp32
1646     # . . discard args
1647     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1648     # loop
1649     e9/jump  $emit-output:line-loop/disp32
1650 $emit-output:end:
1651     # . reclaim locals
1652     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x214/imm32       # add to esp
1653     # . restore registers
1654     5f/pop-to-edi
1655     5e/pop-to-esi
1656     5b/pop-to-ebx
1657     5a/pop-to-edx
1658     59/pop-to-ecx
1659     58/pop-to-eax
1660     # . epilogue
1661     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1662     5d/pop-to-ebp
1663     c3/return
1664 
1665 $emit-output:abort:
1666     # print(stderr, "missing metadata in " word-slice)
1667     # . _write(2/stderr, "missing metadata in word ")
1668     # . . push args
1669     68/push  "emit-output: missing metadata in "/imm32
1670     68/push  2/imm32/stderr
1671     # . . call
1672     e8/call  _write/disp32
1673     # . . discard args
1674     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1675     # . write-slice-buffered(Stderr, word-slice)
1676     # . . push args
1677     52/push-edx
1678     68/push  Stderr/imm32
1679     # . . call
1680     e8/call  write-slice-buffered/disp32
1681     # . . discard args
1682     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1683     # . flush(Stderr)
1684     # . . push args
1685     68/push  Stderr/imm32
1686     # . . call
1687     e8/call  flush/disp32
1688     # . . discard args
1689     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1690     # . syscall(exit, 1)
1691     bb/copy-to-ebx  1/imm32
1692     e8/call  syscall_exit/disp32
1693     # never gets here
1694 
1695 test-emit-output-non-far-control-flow:
1696     # labels turn into absolute addresses if opcodes are not far jumps or calls
1697     #
1698     # input:
1699     #   in:
1700     #     == code
1701     #     ab cd ef gh
1702     #     ij x/disp32
1703     #     == data
1704     #     00
1705     #     34
1706     #   labels:
1707     #     - 'x': 0x11223344
1708     #
1709     # output:
1710     #   ab cd ef gh
1711     #   ij 44 33 22 11
1712     #   00
1713     #   34
1714     #
1715     # . prologue
1716     55/push-ebp
1717     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1718     # setup
1719     # . clear-stream(_test-input-stream)
1720     # . . push args
1721     68/push  _test-input-stream/imm32
1722     # . . call
1723     e8/call  clear-stream/disp32
1724     # . . discard args
1725     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1726     # . clear-stream(_test-output-stream)
1727     # . . push args
1728     68/push  _test-output-stream/imm32
1729     # . . call
1730     e8/call  clear-stream/disp32
1731     # . . discard args
1732     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1733     # . clear-stream($_test-output-buffered-file->buffer)
1734     # . . push args
1735     68/push  $_test-output-buffered-file->buffer/imm32
1736     # . . call
1737     e8/call  clear-stream/disp32
1738     # . . discard args
1739     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1740     # . var labels/edx: (stream byte 8*24)
1741     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc0/imm32        # subtract from esp
1742     68/push  0xc0/imm32/size
1743     68/push  0/imm32/read
1744     68/push  0/imm32/write
1745     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
1746     # . var h/ebx: (handle array byte)
1747     68/push  0/imm32
1748     68/push  0/imm32
1749     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
1750     # initialize input
1751     # . write(_test-input-stream, "== code\n")
1752     # . . push args
1753     68/push  "== code\n"/imm32
1754     68/push  _test-input-stream/imm32
1755     # . . call
1756     e8/call  write/disp32
1757     # . . discard args
1758     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1759     # . write(_test-input-stream, "ab cd ef gh\n")
1760     # . . push args
1761     68/push  "ab cd ef gh\n"/imm32
1762     68/push  _test-input-stream/imm32
1763     # . . call
1764     e8/call  write/disp32
1765     # . . discard args
1766     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1767     # . write(_test-input-stream, "ij x/disp32\n")
1768     # . . push args
1769     68/push  "ij x/disp32\n"/imm32
1770     68/push  _test-input-stream/imm32
1771     # . . call
1772     e8/call  write/disp32
1773     # . . discard args
1774     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1775     # . write(_test-input-stream, "== data\n")
1776     # . . push args
1777     68/push  "== data\n"/imm32
1778     68/push  _test-input-stream/imm32
1779     # . . call
1780     e8/call  write/disp32
1781     # . . discard args
1782     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1783     # . write(_test-input-stream, "00\n")
1784     # . . push args
1785     68/push  "00\n"/imm32
1786     68/push  _test-input-stream/imm32
1787     # . . call
1788     e8/call  write/disp32
1789     # . . discard args
1790     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1791     # . write(_test-input-stream, "34\n")
1792     # . . push args
1793     68/push  "34\n"/imm32
1794     68/push  _test-input-stream/imm32
1795     # . . call
1796     e8/call  write/disp32
1797     # . . discard args
1798     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1799     # . stream-add2(labels, "x", 0x11223344)
1800     68/push  0x11223344/imm32/label-address
1801     # . . push handle for "x"
1802     53/push-ebx
1803     68/push  "x"/imm32
1804     68/push  Heap/imm32
1805     e8/call  copy-array/disp32
1806     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1807     ff          6/subop/push        1/mod/*+disp8   3/rm32/ebx    .           .             .           .           4/disp8         .                 # push *(ebx+4)
1808     ff          6/subop/push        0/mod/indirect  3/rm32/ebx    .           .             .           .           .               .                 # push *ebx
1809     # . . push labels
1810     52/push-edx
1811     # . . call
1812     e8/call  stream-add2/disp32
1813     # . . discard args
1814     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
1815     # component under test
1816     # . emit-output(_test-input-stream, _test-output-buffered-file, labels)
1817     # . . push args
1818     52/push-edx
1819     68/push  _test-output-buffered-file/imm32
1820     68/push  _test-input-stream/imm32
1821     # . . call
1822     e8/call  emit-output/disp32
1823     # . . discard args
1824     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1825     # checks
1826     # . flush(_test-output-buffered-file)
1827     # . . push args
1828     68/push  _test-output-buffered-file/imm32
1829     # . . call
1830     e8/call  flush/disp32
1831     # . . discard args
1832     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1833 +-- 33 lines: #?     # dump output ------------------------------------------------------------------------------------------------------------------------------------------------------
1866     # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c00", msg)
1867     # . . push args
1868     68/push  "F - test-emit-output-non-far-control-flow/0"/imm32
1869     68/push  "# 0x00007c00"/imm32
1870     68/push  _test-output-stream/imm32
1871     # . . call
1872     e8/call  check-next-stream-line-equal/disp32
1873     # . . discard args
1874     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1875     # . check-next-stream-line-equal(_test-output-stream, "ab cd ef gh ", msg)
1876     # . . push args
1877     68/push  "F - test-emit-output-non-far-control-flow/1"/imm32
1878     68/push  "ab cd ef gh "/imm32
1879     68/push  _test-output-stream/imm32
1880     # . . call
1881     e8/call  check-next-stream-line-equal/disp32
1882     # . . discard args
1883     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1884     # . check-next-stream-line-equal(_test-output-stream, "ij 44 33 22 11 ", msg)
1885     # . . push args
1886     68/push  "F - test-emit-output-non-far-control-flow/2"/imm32
1887     68/push  "ij 44 33 22 11 "/imm32
1888     68/push  _test-output-stream/imm32
1889     # . . call
1890     e8/call  check-next-stream-line-equal/disp32
1891     # . . discard args
1892     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1893     # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c09", msg)
1894     # . . push args
1895     68/push  "F - test-emit-output-non-far-control-flow/3"/imm32
1896     68/push  "# 0x00007c09"/imm32
1897     68/push  _test-output-stream/imm32
1898     # . . call
1899     e8/call  check-next-stream-line-equal/disp32
1900     # . . discard args
1901     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1902     # . check-next-stream-line-equal(_test-output-stream, "00 ", msg)
1903     # . . push args
1904     68/push  "F - test-emit-output-non-far-control-flow/3"/imm32
1905     68/push  "00 "/imm32
1906     68/push  _test-output-stream/imm32
1907     # . . call
1908     e8/call  check-next-stream-line-equal/disp32
1909     # . . discard args
1910     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1911     # . check-next-stream-line-equal(_test-output-stream, "34 ", msg)
1912     # . . push args
1913     68/push  "F - test-emit-output-non-far-control-flow/4"/imm32
1914     68/push  "34 "/imm32
1915     68/push  _test-output-stream/imm32
1916     # . . call
1917     e8/call  check-next-stream-line-equal/disp32
1918     # . . discard args
1919     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1920     # . epilogue
1921     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1922     5d/pop-to-ebp
1923     c3/return
1924 
1925 test-emit-output-with-padding:
1926     # labels turn into absolute addresses if opcodes are not far jumps or calls
1927     #
1928     # input:
1929     #   in:
1930     #     == code
1931     #     ab cd ef gh
1932     #     == data 0x7c10
1933     #     34
1934     #
1935     # output:
1936     #   ab cd ef gh
1937     #   # 0x7c04
1938     #   00 00 00 00
1939     #   00 00 00 00 00 00 00 00
1940     #   # 0x7c10
1941     #   34
1942     #
1943     # . prologue
1944     55/push-ebp
1945     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1946     # setup
1947     # . clear-stream(_test-input-stream)
1948     # . . push args
1949     68/push  _test-input-stream/imm32
1950     # . . call
1951     e8/call  clear-stream/disp32
1952     # . . discard args
1953     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1954     # . clear-stream(_test-output-stream)
1955     # . . push args
1956     68/push  _test-output-stream/imm32
1957     # . . call
1958     e8/call  clear-stream/disp32
1959     # . . discard args
1960     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1961     # . clear-stream($_test-output-buffered-file->buffer)
1962     # . . push args
1963     68/push  $_test-output-buffered-file->buffer/imm32
1964     # . . call
1965     e8/call  clear-stream/disp32
1966     # . . discard args
1967     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1968     # . var labels/edx: (stream byte 8*24)
1969     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc0/imm32        # subtract from esp
1970     68/push  0xc0/imm32/size
1971     68/push  0/imm32/read
1972     68/push  0/imm32/write
1973     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
1974     # . var h/ebx: (handle array byte)
1975     68/push  0/imm32
1976     68/push  0/imm32
1977     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
1978     # initialize input
1979     # . write(_test-input-stream, "== code\n")
1980     # . . push args
1981     68/push  "== code\n"/imm32
1982     68/push  _test-input-stream/imm32
1983     # . . call
1984     e8/call  write/disp32
1985     # . . discard args
1986     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1987     # . write(_test-input-stream, "ab cd ef gh\n")
1988     # . . push args
1989     68/push  "ab cd ef gh\n"/imm32
1990     68/push  _test-input-stream/imm32
1991     # . . call
1992     e8/call  write/disp32
1993     # . . discard args
1994     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1995     # . write(_test-input-stream, "== data 0x7c10\n")
1996     # . . push args
1997     68/push  "== data 0x7c10\n"/imm32
1998     68/push  _test-input-stream/imm32
1999     # . . call
2000     e8/call  write/disp32
2001     # . . discard args
2002     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2003     # . write(_test-input-stream, "34\n")
2004     # . . push args
2005     68/push  "34\n"/imm32
2006     68/push  _test-input-stream/imm32
2007     # . . call
2008     e8/call  write/disp32
2009     # . . discard args
2010     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2011     # component under test
2012     # . emit-output(_test-input-stream, _test-output-buffered-file, labels)
2013     # . . push args
2014     52/push-edx
2015     68/push  _test-output-buffered-file/imm32
2016     68/push  _test-input-stream/imm32
2017     # . . call
2018     e8/call  emit-output/disp32
2019     # . . discard args
2020     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2021     # checks
2022     # . flush(_test-output-buffered-file)
2023     # . . push args
2024     68/push  _test-output-buffered-file/imm32
2025     # . . call
2026     e8/call  flush/disp32
2027     # . . discard args
2028     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2029 +-- 33 lines: #?     # dump output ------------------------------------------------------------------------------------------------------------------------------------------------------
2062     # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c00", msg)
2063     # . . push args
2064     68/push  "F - test-emit-output-with-padding/0"/imm32
2065     68/push  "# 0x00007c00"/imm32
2066     68/push  _test-output-stream/imm32
2067     # . . call
2068     e8/call  check-next-stream-line-equal/disp32
2069     # . . discard args
2070     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2071     # . check-next-stream-line-equal(_test-output-stream, "ab cd ef gh ", msg)
2072     # . . push args
2073     68/push  "F - test-emit-output-with-padding/1"/imm32
2074     68/push  "ab cd ef gh "/imm32
2075     68/push  _test-output-stream/imm32
2076     # . . call
2077     e8/call  check-next-stream-line-equal/disp32
2078     # . . discard args
2079     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2080     # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c04", msg)
2081     # . . push args
2082     68/push  "F - test-emit-output-with-padding/0"/imm32
2083     68/push  "# 0x00007c04"/imm32
2084     68/push  _test-output-stream/imm32
2085     # . . call
2086     e8/call  check-next-stream-line-equal/disp32
2087     # . . discard args
2088     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2089     # . check-next-stream-line-equal(_test-output-stream, "00 00 00 00 ", msg)
2090     # . . push args
2091     68/push  "F - test-emit-output-with-padding/2"/imm32
2092     68/push  "00 00 00 00 "/imm32
2093     68/push  _test-output-stream/imm32
2094     # . . call
2095     e8/call  check-next-stream-line-equal/disp32
2096     # . . discard args
2097     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2098     # . check-next-stream-line-equal(_test-output-stream, "00 00 00 00 ", msg)
2099     # . . push args
2100     68/push  "F - test-emit-output-with-padding/3"/imm32
2101     68/push  "00 00 00 00 00 00 00 00 "/imm32
2102     68/push  _test-output-stream/imm32
2103     # . . call
2104     e8/call  check-next-stream-line-equal/disp32
2105     # . . discard args
2106     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2107     # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c10", msg)
2108     # . . push args
2109     68/push  "F - test-emit-output-with-padding/0"/imm32
2110     68/push  "# 0x00007c10"/imm32
2111     68/push  _test-output-stream/imm32
2112     # . . call
2113     e8/call  check-next-stream-line-equal/disp32
2114     # . . discard args
2115     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2116     # . check-next-stream-line-equal(_test-output-stream, "34 ", msg)
2117     # . . push args
2118     68/push  "F - test-emit-output-with-padding/4"/imm32
2119     68/push  "34 "/imm32
2120     68/push  _test-output-stream/imm32
2121     # . . call
2122     e8/call  check-next-stream-line-equal/disp32
2123     # . . discard args
2124     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2125     # . epilogue
2126     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2127     5d/pop-to-ebp
2128     c3/return
2129 
2130 test-emit-output-code-label:
2131     # labels turn into PC-relative addresses if opcodes are far jumps or calls
2132     #
2133     # input:
2134     #   in:
2135     #     == code
2136     #     ab cd
2137     #     ef gh
2138     #     e8 l1/disp32
2139     #   labels:
2140     #     - 'l1': 0x7c10
2141     #
2142     # output:
2143     #   ab cd
2144     #   ef gh
2145     #   e8 07 00 00 00  # 0x7c10 - 0x7c09 = 7
2146     #
2147     # . prologue
2148     55/push-ebp
2149     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2150     # setup
2151     # . clear-stream(_test-input-stream)
2152     # . . push args
2153     68/push  _test-input-stream/imm32
2154     # . . call
2155     e8/call  clear-stream/disp32
2156     # . . discard args
2157     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2158     # . clear-stream(_test-output-stream)
2159     # . . push args
2160     68/push  _test-output-stream/imm32
2161     # . . call
2162     e8/call  clear-stream/disp32
2163     # . . discard args
2164     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2165     # . clear-stream($_test-output-buffered-file->buffer)
2166     # . . push args
2167     68/push  $_test-output-buffered-file->buffer/imm32
2168     # . . call
2169     e8/call  clear-stream/disp32
2170     # . . discard args
2171     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2172     # . var labels/edx: (stream byte 8*24)
2173     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc0/imm32        # subtract from esp
2174     68/push  0xc0/imm32/size
2175     68/push  0/imm32/read
2176     68/push  0/imm32/write
2177     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
2178     # . var h/ebx: (handle array byte)
2179     68/push  0/imm32
2180     68/push  0/imm32
2181     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
2182     # initialize input
2183     # . write(_test-input-stream, "== code\n")
2184     # . . push args
2185     68/push  "== code\n"/imm32
2186     68/push  _test-input-stream/imm32
2187     # . . call
2188     e8/call  write/disp32
2189     # . . discard args
2190     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2191     # . write(_test-input-stream, "ab cd\n")
2192     # . . push args
2193     68/push  "ab cd\n"/imm32
2194     68/push  _test-input-stream/imm32
2195     # . . call
2196     e8/call  write/disp32
2197     # . . discard args
2198     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2199     # . write(_test-input-stream, "ef gh\n")
2200     # . . push args
2201     68/push  "ef gh\n"/imm32
2202     68/push  _test-input-stream/imm32
2203     # . . call
2204     e8/call  write/disp32
2205     # . . discard args
2206     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2207     # . write(_test-input-stream, "e8 l1/disp32\n")
2208     # . . push args
2209     68/push  "e8 l1/disp32\n"/imm32
2210     68/push  _test-input-stream/imm32
2211     # . . call
2212     e8/call  write/disp32
2213     # . . discard args
2214     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2215     # . stream-add2(labels, "l1", 0x7c10)
2216     68/push  0x7c10/imm32/label-address
2217     # . . push handle for "l1"
2218     53/push-ebx
2219     68/push  "l1"/imm32
2220     68/push  Heap/imm32
2221     e8/call  copy-array/disp32
2222     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2223     ff          6/subop/push        1/mod/*+disp8   3/rm32/ebx    .           .             .           .           4/disp8         .                 # push *(ebx+4)
2224     ff          6/subop/push        0/mod/indirect  3/rm32/ebx    .           .             .           .           .               .                 # push *ebx
2225     # . . push labels
2226     52/push-edx
2227     # . . call
2228     e8/call  stream-add2/disp32
2229     # . . discard args
2230     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
2231     # component under test
2232     # . emit-output(_test-input-stream, _test-output-buffered-file, labels)
2233     # . . push args
2234     52/push-edx
2235     68/push  _test-output-buffered-file/imm32
2236     68/push  _test-input-stream/imm32
2237     # . . call
2238     e8/call  emit-output/disp32
2239     # . . discard args
2240     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2241     # checks
2242     # . flush(_test-output-buffered-file)
2243     # . . push args
2244     68/push  _test-output-buffered-file/imm32
2245     # . . call
2246     e8/call  flush/disp32
2247     # . . discard args
2248     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2249 +-- 33 lines: #?     # dump output ------------------------------------------------------------------------------------------------------------------------------------------------------
2282     # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c00", msg)
2283     # . . push args
2284     68/push  "F - test-emit-output-code-label/0"/imm32
2285     68/push  "# 0x00007c00"/imm32
2286     68/push  _test-output-stream/imm32
2287     # . . call
2288     e8/call  check-next-stream-line-equal/disp32
2289     # . . discard args
2290     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2291     # . check-next-stream-line-equal(_test-output-stream, "ab cd ", msg)
2292     # . . push args
2293     68/push  "F - test-emit-output-code-label/1"/imm32
2294     68/push  "ab cd "/imm32
2295     68/push  _test-output-stream/imm32
2296     # . . call
2297     e8/call  check-next-stream-line-equal/disp32
2298     # . . discard args
2299     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2300     # . check-next-stream-line-equal(_test-output-stream, "ef gh ", msg)
2301     # . . push args
2302     68/push  "F - test-emit-output-code-label/2"/imm32
2303     68/push  "ef gh "/imm32
2304     68/push  _test-output-stream/imm32
2305     # . . call
2306     e8/call  check-next-stream-line-equal/disp32
2307     # . . discard args
2308     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2309     # . check-next-stream-line-equal(_test-output-stream, "e8 07 00 00 00 ", msg)
2310     # . . push args
2311     68/push  "F - test-emit-output-code-label/3"/imm32
2312     68/push  "e8 07 00 00 00 "/imm32
2313     68/push  _test-output-stream/imm32
2314     # . . call
2315     e8/call  check-next-stream-line-equal/disp32
2316     # . . discard args
2317     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2318     # . epilogue
2319     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2320     5d/pop-to-ebp
2321     c3/return
2322 
2323 test-emit-output-code-label-absolute:
2324     # labels can also convert to absolute addresses
2325     #
2326     # input:
2327     #   in:
2328     #     == code
2329     #     ab cd
2330     #     ef gh
2331     #     ij l1/imm32
2332     #   labels:
2333     #     - 'l1': 0x1056
2334     #
2335     # output:
2336     #   ab cd
2337     #   ef gh
2338     #   ij 56 10 00 00
2339     #
2340     # . prologue
2341     55/push-ebp
2342     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2343     # setup
2344     # . clear-stream(_test-input-stream)
2345     # . . push args
2346     68/push  _test-input-stream/imm32
2347     # . . call
2348     e8/call  clear-stream/disp32
2349     # . . discard args
2350     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2351     # . clear-stream(_test-output-stream)
2352     # . . push args
2353     68/push  _test-output-stream/imm32
2354     # . . call
2355     e8/call  clear-stream/disp32
2356     # . . discard args
2357     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2358     # . clear-stream($_test-output-buffered-file->buffer)
2359     # . . push args
2360     68/push  $_test-output-buffered-file->buffer/imm32
2361     # . . call
2362     e8/call  clear-stream/disp32
2363     # . . discard args
2364     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2365     # . var labels/edx: (stream byte 8*24)
2366     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc0/imm32        # subtract from esp
2367     68/push  0xc0/imm32/size
2368     68/push  0/imm32/read
2369     68/push  0/imm32/write
2370     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
2371     # . var h/ebx: (handle array byte)
2372     68/push  0/imm32
2373     68/push  0/imm32
2374     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
2375     # initialize input
2376     # . write(_test-input-stream, "== code 0x1000\n")
2377     # . . push args
2378     68/push  "== code\n"/imm32
2379     68/push  _test-input-stream/imm32
2380     # . . call
2381     e8/call  write/disp32
2382     # . . discard args
2383     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2384     # . write(_test-input-stream, "ab cd\n")
2385     # . . push args
2386     68/push  "ab cd\n"/imm32
2387     68/push  _test-input-stream/imm32
2388     # . . call
2389     e8/call  write/disp32
2390     # . . discard args
2391     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2392     # . write(_test-input-stream, "ef gh\n")
2393     # . . push args
2394     68/push  "ef gh\n"/imm32
2395     68/push  _test-input-stream/imm32
2396     # . . call
2397     e8/call  write/disp32
2398     # . . discard args
2399     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2400     # . write(_test-input-stream, "ij l1/imm32\n")
2401     # . . push args
2402     68/push  "ij l1/imm32\n"/imm32
2403     68/push  _test-input-stream/imm32
2404     # . . call
2405     e8/call  write/disp32
2406     # . . discard args
2407     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2408     # . stream-add2(labels, "l1", 0x1056)
2409     68/push  0x1056/imm32/label-address
2410     # . . push handle for "l1"
2411     53/push-ebx
2412     68/push  "l1"/imm32
2413     68/push  Heap/imm32
2414     e8/call  copy-array/disp32
2415     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2416     ff          6/subop/push        1/mod/*+disp8   3/rm32/ebx    .           .             .           .           4/disp8         .                 # push *(ebx+4)
2417     ff          6/subop/push        0/mod/indirect  3/rm32/ebx    .           .             .           .           .               .                 # push *ebx
2418     # . . push labels
2419     52/push-edx
2420     # . . call
2421     e8/call  stream-add2/disp32
2422     # . . discard args
2423     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
2424     # component under test
2425     # . emit-output(_test-input-stream, _test-output-buffered-file, labels)
2426     # . . push args
2427     52/push-edx
2428     68/push  _test-output-buffered-file/imm32
2429     68/push  _test-input-stream/imm32
2430     # . . call
2431     e8/call  emit-output/disp32
2432     # . . discard args
2433     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2434     # checks
2435     # . flush(_test-output-buffered-file)
2436     # . . push args
2437     68/push  _test-output-buffered-file/imm32
2438     # . . call
2439     e8/call  flush/disp32
2440     # . . discard args
2441     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2442 +-- 33 lines: #?     # dump output ------------------------------------------------------------------------------------------------------------------------------------------------------
2475     # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c00", msg)
2476     # . . push args
2477     68/push  "F - test-emit-output-code-label-absolute/0"/imm32
2478     68/push  "# 0x00007c00"/imm32
2479     68/push  _test-output-stream/imm32
2480     # . . call
2481     e8/call  check-next-stream-line-equal/disp32
2482     # . . discard args
2483     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2484     # . check-next-stream-line-equal(_test-output-stream, "ab cd ", msg)
2485     # . . push args
2486     68/push  "F - test-emit-output-code-label-absolute/1"/imm32
2487     68/push  "ab cd "/imm32
2488     68/push  _test-output-stream/imm32
2489     # . . call
2490     e8/call  check-next-stream-line-equal/disp32
2491     # . . discard args
2492     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2493     # . check-next-stream-line-equal(_test-output-stream, "ef gh ", msg)
2494     # . . push args
2495     68/push  "F - test-emit-output-code-label-absolute/2"/imm32
2496     68/push  "ef gh "/imm32
2497     68/push  _test-output-stream/imm32
2498     # . . call
2499     e8/call  check-next-stream-line-equal/disp32
2500     # . . discard args
2501     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2502     # . check-next-stream-line-equal(_test-output-stream, "ij f9 ff ff ff ", msg)
2503     # . . push args
2504     68/push  "F - test-emit-output-code-label-absolute/3"/imm32
2505     68/push  "ij 56 10 00 00 "/imm32
2506     68/push  _test-output-stream/imm32
2507     # . . call
2508     e8/call  check-next-stream-line-equal/disp32
2509     # . . discard args
2510     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2511     # . epilogue
2512     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2513     5d/pop-to-ebp
2514     c3/return
2515 
2516 # reads line to make some checks
2517 # don't assume the read state of line after calling this function
2518 far-jump-or-call?:  # line: (addr stream byte) -> result/edi: boolean
2519     # . prologue
2520     55/push-ebp
2521     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2522     # . save registers
2523     50/push-eax
2524     51/push-ecx
2525     52/push-edx
2526     53/push-ebx
2527     # ecx = line
2528     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
2529     # var word-slice/edx: slice
2530     68/push  0/imm32/end
2531     68/push  0/imm32/start
2532     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
2533     # var datum-slice/ebx: slice
2534     68/push  0/imm32/end
2535     68/push  0/imm32/start
2536     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
2537     # result = false
2538     bf/copy-to-edi  0/imm32/false
2539 $far-jump-or-call?:check-first-word:
2540     # next-word(line, word-slice)
2541     # . . push args
2542     52/push-edx
2543     51/push-ecx
2544     # . . call
2545     e8/call  next-word/disp32
2546     # . . discard args
2547     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2548     # if (slice-empty?(word-slice)) return false
2549     # . eax = slice-empty?(word-slice)
2550     # . . push args
2551     52/push-edx
2552     # . . call
2553     e8/call  slice-empty?/disp32
2554     # . . discard args
2555     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2556     # . if (eax != 0) return
2557     3d/compare-eax-and  0/imm32/false
2558     0f 85/jump-if-!=  $far-jump-or-call?:end/disp32
2559     # datum = next-token-from-slice(word-slice->start, word-slice->end, "/")
2560     # . . push args
2561     53/push-ebx
2562     68/push  0x2f/imm32/slash
2563     ff          6/subop/push        1/mod/*+disp8   2/rm32/edx    .           .             .           .           4/disp8         .                 # push *(edx+4)
2564     ff          6/subop/push        0/mod/indirect  2/rm32/edx    .           .             .           .           .               .                 # push *edx
2565     # . . call
2566     e8/call  next-token-from-slice/disp32
2567     # . . discard args
2568     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
2569     # if (datum-slice == "e8") return true
2570     # . eax = slice-equal?(datum-slice, "e8")
2571     # . . push args
2572     68/push  "e8"/imm32
2573     53/push-ebx
2574     # . . call
2575     e8/call  slice-equal?/disp32
2576     # . . discard args
2577     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2578     # . if (eax != false) return true
2579     3d/compare-eax-and  0/imm32/false
2580     75/jump-if-!=  $far-jump-or-call?:return-true/disp8
2581     # if (datum-slice == "e9") return true
2582     # . eax = slice-equal?(datum-slice, "e9")
2583     # . . push args
2584     68/push  "e9"/imm32
2585     53/push-ebx
2586     # . . call
2587     e8/call  slice-equal?/disp32
2588     # . . discard args
2589     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2590     # . if (eax != false) return true
2591     3d/compare-eax-and  0/imm32/false
2592     75/jump-if-!=  $far-jump-or-call?:return-true/disp8
2593     # if (datum-slice != "0f") return false
2594     # . eax = slice-equal?(datum-slice, "0f")
2595     # . . push args
2596     68/push  "0f"/imm32
2597     53/push-ebx
2598     # . . call
2599     e8/call  slice-equal?/disp32
2600     # . . discard args
2601     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2602     # . if (eax == false) return
2603     3d/compare-eax-and  0/imm32/false
2604     74/jump-if-=  $far-jump-or-call?:end/disp8
2605 $far-jump-or-call?:check-second-word:
2606     # next-word(line, word-slice)
2607     # . . push args
2608     52/push-edx
2609     51/push-ecx
2610     # . . call
2611     e8/call  next-word/disp32
2612     # . . discard args
2613     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2614     # if (slice-empty?(word-slice)) return false
2615     # . eax = slice-empty?(word-slice)
2616     # . . push args
2617     52/push-edx
2618     # . . call
2619     e8/call  slice-empty?/disp32
2620     # . . discard args
2621     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2622     # . if (eax != 0) return
2623     3d/compare-eax-and  0/imm32/false
2624     75/jump-if-!=  $far-jump-or-call?:end/disp8
2625     # if datum of word-slice does not start with "8", return false
2626     # . start/eax = word-slice->start
2627     8b/copy                         0/mod/indirect  2/rm32/edx    .           .             .           0/r32/eax   .               .                 # copy *edx to eax
2628     # . c/eax = *start
2629     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           0/r32/AL    .               .                 # copy byte at *eax to AL
2630     81          4/subop/and         3/mod/direct    0/rm32/eax    .           .             .           .           .               0xff/imm32        # bitwise and of eax
2631     # . if (eax != '8') return
2632     3d/compare-eax-and  0x38/imm32/8
2633     75/jump-if-!=  $far-jump-or-call?:end/disp8
2634     # otherwise return true
2635 $far-jump-or-call?:return-true:
2636     bf/copy-to-edi  1/imm32/true
2637 $far-jump-or-call?:end:
2638     # . reclaim locals
2639     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
2640     # . restore registers
2641     5b/pop-to-ebx
2642     5a/pop-to-edx
2643     59/pop-to-ecx
2644     58/pop-to-eax
2645     # . epilogue
2646     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2647     5d/pop-to-ebp
2648     c3/return
2649 
2650 # - some helpers for tests
2651 
2652 stream-add2:  # in: (addr stream byte), key: handle, val: int
2653     # . prologue
2654     55/push-ebp
2655     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2656     # . save registers
2657     50/push-eax
2658     51/push-ecx
2659     52/push-edx
2660     56/push-esi
2661     # esi = in
2662     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
2663     # curr/eax = &in->data[in->write]
2664     # . eax = in->write
2665     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
2666     # . eax = esi+eax+12
2667     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
2668     # max/edx = &in->data[in->size]
2669     # . edx = in->size
2670     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           2/r32/edx   8/disp8         .                 # copy *(esi+8) to edx
2671     # . edx = esi+edx+12
2672     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/esi  2/index/edx   .           2/r32/edx   0xc/disp8       .                 # copy esi+edx+12 to edx
2673     # if (curr >= max) abort
2674     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # compare eax with edx
2675     73/jump-if-addr>=  $stream-add2:abort/disp8
2676     # *curr = key->alloc-id
2677     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   0xc/disp8       .                 # copy *(ebp+12) to ecx
2678     89/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy ecx to *eax
2679     # curr += 4
2680     05/add-to-eax  4/imm32
2681     # if (curr >= max) abort
2682     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # compare eax with edx
2683     73/jump-if-addr>=  $stream-add2:abort/disp8
2684     # *curr = key->payload
2685     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   0x10/disp8      .                 # copy *(ebp+16) to ecx
2686     89/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy ecx to *eax
2687     # curr += 4
2688     05/add-to-eax  4/imm32
2689     # if (curr >= max) abort
2690     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # compare eax with edx
2691     73/jump-if-addr>=  $stream-add2:abort/disp8
2692     # *curr = val
2693     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   0x14/disp8      .                 # copy *(ebp+20) to ecx
2694     89/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy ecx to *eax
2695     # in->write += 0xc
2696     81          0/subop/add         0/mod/indirect  6/rm32/esi    .           .             .           .           .               0xc/imm32         # add to *esi
2697 $stream-add2:end:
2698     # . restore registers
2699     5e/pop-to-esi
2700     5a/pop-to-edx
2701     59/pop-to-ecx
2702     58/pop-to-eax
2703     # . epilogue
2704     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2705     5d/pop-to-ebp
2706     c3/return
2707 
2708 $stream-add2:abort:
2709     # . _write(2/stderr, error)
2710     # . . push args
2711     68/push  "overflow in stream-add2\n"/imm32
2712     68/push  2/imm32/stderr
2713     # . . call
2714     e8/call  _write/disp32
2715     # . . discard args
2716     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2717     # . syscall(exit, 1)
2718     bb/copy-to-ebx  1/imm32
2719     e8/call  syscall_exit/disp32
2720     # never gets here
2721 
2722 # some variants of 'trace' that take multiple arguments in different combinations of types:
2723 #   n: int
2724 #   c: character [4-bytes, will eventually be UTF-8]
2725 #   s: (addr array byte)
2726 #   l: (addr slice)
2727 # one gotcha: 's5' must not be empty
2728 
2729 trace-slsns:  # s1: (addr array byte), l2: (addr slice), s3: (addr array byte), n4: int, s5: (addr array byte)
2730     # . prologue
2731     55/push-ebp
2732     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2733     # write(*Trace-stream, s1)
2734     # . . push args
2735     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
2736     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
2737     # . . call
2738     e8/call  write/disp32
2739     # . . discard args
2740     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2741     # write-slice(*Trace-stream, l2)
2742     # . . push args
2743     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
2744     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
2745     # . . call
2746     e8/call  write-slice/disp32
2747     # . . discard args
2748     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2749     # write(*Trace-stream, s3)
2750     # . . push args
2751     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
2752     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
2753     # . . call
2754     e8/call  write/disp32
2755     # . . discard args
2756     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2757     # write-int32-hex(*Trace-stream, n4)
2758     # . . push args
2759     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x14/disp8      .                 # push *(ebp+20)
2760     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
2761     # . . call
2762     e8/call  write-int32-hex/disp32
2763     # . . discard args
2764     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2765     # trace(s5)  # implicitly adds a newline and finalizes the trace line
2766     # . . push args
2767     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x18/disp8      .                 # push *(ebp+24)
2768     # . . call
2769     e8/call  trace/disp32
2770     # . . discard args
2771     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2772 $trace-slsns:end:
2773     # . epilogue
2774     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2775     5d/pop-to-ebp
2776     c3/return
2777 
2778 test-trace-slsns:
2779     # . prologue
2780     55/push-ebp
2781     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2782     # setup
2783     # . *Trace-stream->write = 0
2784     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   Trace-stream/disp32               # copy *Trace-stream to eax
2785     c7          0/subop/copy        0/mod/direct    0/rm32/eax    .           .             .           .           .               0/imm32           # clear *eax
2786     # (eax..ecx) = "b"
2787     b8/copy-to-eax  "b"/imm32
2788     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
2789     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
2790     05/add-to-eax  4/imm32
2791     # var b/ebx: slice = {eax, ecx}
2792     51/push-ecx
2793     50/push-eax
2794     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
2795     # trace-slsls("A" b "c " 3 " e")
2796     # . . push args
2797     68/push  " e"/imm32
2798     68/push  3/imm32
2799     68/push  "c "/imm32
2800     53/push-ebx
2801     68/push  "A"/imm32
2802     # . . call
2803     e8/call  trace-slsns/disp32
2804     # . . discard args
2805     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
2806 +-- 26 lines: #?     # dump *Trace-stream -----------------------------------------------------------------------------------------------------------------------------------------------
2832     # check-trace-contains("Abc 0x00000003 e")
2833     # . . push args
2834     68/push  "F - test-trace-slsls"/imm32
2835     68/push  "Abc 0x00000003 e"/imm32
2836     # . . call
2837     e8/call  check-trace-contains/disp32
2838     # . . discard args
2839     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2840     # . epilogue
2841     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2842     5d/pop-to-ebp
2843     c3/return
2844 
2845 num-bytes:  # line: (addr stream byte) -> eax: int
2846     # pseudocode:
2847     #   result = 0
2848     #   while true
2849     #     var word-slice = next-word(line)
2850     #     if slice-empty?(word-slice)             # end of line
2851     #       break
2852     #     if slice-starts-with?(word-slice, "#")  # comment
2853     #       break
2854     #     if label?(word-slice)                # no need for label declarations anymore
2855     #       break
2856     #     if slice-equal?(word-slice, "==")
2857     #       break                                 # no need for segment header lines
2858     #     result += compute-width(word-slice)
2859     #   return result
2860     #
2861     # . prologue
2862     55/push-ebp
2863     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2864     # . save registers
2865     51/push-ecx
2866     52/push-edx
2867     53/push-ebx
2868     # var result/eax = 0
2869     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
2870     # var word-slice/ecx: slice
2871     68/push  0/imm32/end
2872     68/push  0/imm32/start
2873     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2874 +-- 26 lines: #?     # dump line --------------------------------------------------------------------------------------------------------------------------------------------------------
2900     # . rewind-stream(line)
2901     # . . push args
2902     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
2903     # . . call
2904     e8/call  rewind-stream/disp32
2905     # . . discard args
2906     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2907 $num-bytes:loop:
2908     # next-word(line, word-slice)
2909     # . . push args
2910     51/push-ecx
2911     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
2912     # . . call
2913     e8/call  next-word/disp32
2914     # . . discard args
2915     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2916 +-- 40 lines: #?     # dump word-slice --------------------------------------------------------------------------------------------------------------------------------------------------
2956 $num-bytes:check0:
2957     # if (slice-empty?(word-slice)) break
2958     # . save result
2959     50/push-eax
2960     # . eax = slice-empty?(word-slice)
2961     # . . push args
2962     51/push-ecx
2963     # . . call
2964     e8/call  slice-empty?/disp32
2965     # . . discard args
2966     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2967     # . if (eax != false) break
2968     3d/compare-eax-and  0/imm32/false
2969     # . restore result now that ZF is set
2970     58/pop-to-eax
2971     75/jump-if-!=  $num-bytes:end/disp8
2972 $num-bytes:check-for-comment:
2973     # if (slice-starts-with?(word-slice, "#")) break
2974     # . start/edx = word-slice->start
2975     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # copy *ecx to edx
2976     # . c/ebx = *start
2977     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
2978     8a/copy-byte                    0/mod/indirect  2/rm32/edx    .           .             .           3/r32/BL    .               .                 # copy byte at *edx to BL
2979     # . if (ebx == '#') break
2980     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x23/imm32/hash   # compare ebx
2981     74/jump-if-=  $num-bytes:end/disp8
2982 $num-bytes:check-for-label:
2983     # if (slice-ends-with?(word-slice, ":")) break
2984     # . end/edx = word-slice->end
2985     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           2/r32/edx   4/disp8         .                 # copy *(ecx+4) to edx
2986     # . c/ebx = *(end-1)
2987     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
2988     8a/copy-byte                    1/mod/*+disp8   2/rm32/edx    .           .             .           3/r32/BL    -1/disp8        .                 # copy byte at *ecx to BL
2989     # . if (ebx == ':') break
2990     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x3a/imm32/colon  # compare ebx
2991     74/jump-if-=  $num-bytes:end/disp8
2992 $num-bytes:check-for-segment-header:
2993     # if (slice-equal?(word-slice, "==")) break
2994     # . push result
2995     50/push-eax
2996     # . eax = slice-equal?(word-slice, "==")
2997     # . . push args
2998     68/push  "=="/imm32
2999     51/push-ecx
3000     # . . call
3001     e8/call  slice-equal?/disp32
3002     # . . discard args
3003     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3004     # . if (eax != false) break
3005     3d/compare-eax-and  0/imm32/false
3006     # . restore result now that ZF is set
3007     58/pop-to-eax
3008     75/jump-if-!=  $num-bytes:end/disp8
3009 $num-bytes:loop-body:
3010     # result += compute-width-of-slice(word-slice)
3011     # . copy result to edx
3012     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           0/r32/eax   .               .                 # copy eax to edx
3013     # . eax = compute-width-of-slice(word-slice)
3014     # . . push args
3015     51/push-ecx
3016     # . . call
3017     e8/call compute-width-of-slice/disp32
3018     # . . discard args
3019     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3020     # . eax += result
3021     01/add                          3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # add edx to eax
3022     e9/jump  $num-bytes:loop/disp32
3023 $num-bytes:end:
3024     # . rewind-stream(line)
3025     # . . push args
3026     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3027     # . . call
3028     e8/call  rewind-stream/disp32
3029     # . . discard args
3030     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3031     # . reclaim locals
3032     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3033     # . restore registers
3034     5b/pop-to-ebx
3035     5a/pop-to-edx
3036     59/pop-to-ecx
3037     # . epilogue
3038     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3039     5d/pop-to-ebp
3040     c3/return
3041 
3042 test-num-bytes-handles-empty-string:
3043     # if a line starts with '#', return 0
3044     # . prologue
3045     55/push-ebp
3046     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3047     # setup
3048     # . clear-stream(_test-input-stream)
3049     # . . push args
3050     68/push  _test-input-stream/imm32
3051     # . . call
3052     e8/call  clear-stream/disp32
3053     # . . discard args
3054     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3055     # . clear-stream(_test-output-stream)
3056     # . . push args
3057     68/push  _test-output-stream/imm32
3058     # . . call
3059     e8/call  clear-stream/disp32
3060     # . . discard args
3061     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3062     # no contents in input
3063     # eax = num-bytes(_test-input-stream)
3064     # . . push args
3065     68/push  _test-input-stream/imm32
3066     # . . call
3067     e8/call  num-bytes/disp32
3068     # . . discard args
3069     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3070     # check-ints-equal(eax, 0, msg)
3071     # . . push args
3072     68/push  "F - test-num-bytes-handles-empty-string"/imm32
3073     68/push  0/imm32/true
3074     50/push-eax
3075     # . . call
3076     e8/call  check-ints-equal/disp32
3077     # . . discard args
3078     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3079     # . epilogue
3080     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3081     5d/pop-to-ebp
3082     c3/return
3083 
3084 test-num-bytes-ignores-comments:
3085     # if a line starts with '#', return 0
3086     # . prologue
3087     55/push-ebp
3088     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3089     # setup
3090     # . clear-stream(_test-input-stream)
3091     # . . push args
3092     68/push  _test-input-stream/imm32
3093     # . . call
3094     e8/call  clear-stream/disp32
3095     # . . discard args
3096     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3097     # . clear-stream(_test-output-stream)
3098     # . . push args
3099     68/push  _test-output-stream/imm32
3100     # . . call
3101     e8/call  clear-stream/disp32
3102     # . . discard args
3103     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3104     # initialize input
3105     # . write(_test-input-stream, "# abcd")
3106     # . . push args
3107     68/push  "# abcd"/imm32
3108     68/push  _test-input-stream/imm32
3109     # . . call
3110     e8/call  write/disp32
3111     # . . discard args
3112     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3113     # eax = num-bytes(_test-input-stream)
3114     # . . push args
3115     68/push  _test-input-stream/imm32
3116     # . . call
3117     e8/call  num-bytes/disp32
3118     # . . discard args
3119     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3120     # check-ints-equal(eax, 0, msg)
3121     # . . push args
3122     68/push  "F - test-num-bytes-ignores-comments"/imm32
3123     68/push  0/imm32/true
3124     50/push-eax
3125     # . . call
3126     e8/call  check-ints-equal/disp32
3127     # . . discard args
3128     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3129     # . epilogue
3130     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3131     5d/pop-to-ebp
3132     c3/return
3133 
3134 test-num-bytes-ignores-labels:
3135     # if the first word ends with ':', return 0
3136     # . prologue
3137     55/push-ebp
3138     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3139     # setup
3140     # . clear-stream(_test-input-stream)
3141     # . . push args
3142     68/push  _test-input-stream/imm32
3143     # . . call
3144     e8/call  clear-stream/disp32
3145     # . . discard args
3146     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3147     # . clear-stream(_test-output-stream)
3148     # . . push args
3149     68/push  _test-output-stream/imm32
3150     # . . call
3151     e8/call  clear-stream/disp32
3152     # . . discard args
3153     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3154     # initialize input
3155     # . write(_test-input-stream, "ab: # cd")
3156     # . . push args
3157     68/push  "ab: # cd"/imm32
3158     68/push  _test-input-stream/imm32
3159     # . . call
3160     e8/call  write/disp32
3161     # . . discard args
3162     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3163     # eax = num-bytes(_test-input-stream)
3164     # . . push args
3165     68/push  _test-input-stream/imm32
3166     # . . call
3167     e8/call  num-bytes/disp32
3168     # . . discard args
3169     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3170     # check-ints-equal(eax, 0, msg)
3171     # . . push args
3172     68/push  "F - test-num-bytes-ignores-labels"/imm32
3173     68/push  0/imm32/true
3174     50/push-eax
3175     # . . call
3176     e8/call  check-ints-equal/disp32
3177     # . . discard args
3178     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3179     # . epilogue
3180     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3181     5d/pop-to-ebp
3182     c3/return
3183 
3184 test-num-bytes-ignores-segment-headers:
3185     # if the first word is '==', return 0
3186     # . prologue
3187     55/push-ebp
3188     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3189     # setup
3190     # . clear-stream(_test-input-stream)
3191     # . . push args
3192     68/push  _test-input-stream/imm32
3193     # . . call
3194     e8/call  clear-stream/disp32
3195     # . . discard args
3196     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3197     # . clear-stream(_test-output-stream)
3198     # . . push args
3199     68/push  _test-output-stream/imm32
3200     # . . call
3201     e8/call  clear-stream/disp32
3202     # . . discard args
3203     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3204     # initialize input
3205     # . write(_test-input-stream, "== ab cd")
3206     # . . push args
3207     68/push  "== ab cd"/imm32
3208     68/push  _test-input-stream/imm32
3209     # . . call
3210     e8/call  write/disp32
3211     # . . discard args
3212     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3213     # eax = num-bytes(_test-input-stream)
3214     # . . push args
3215     68/push  _test-input-stream/imm32
3216     # . . call
3217     e8/call  num-bytes/disp32
3218     # . . discard args
3219     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3220     # check-ints-equal(eax, 0, msg)
3221     # . . push args
3222     68/push  "F - test-num-bytes-ignores-segment-headers"/imm32
3223     68/push  0/imm32/true
3224     50/push-eax
3225     # . . call
3226     e8/call  check-ints-equal/disp32
3227     # . . discard args
3228     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3229     # . epilogue
3230     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3231     5d/pop-to-ebp
3232     c3/return
3233 
3234 test-num-bytes-counts-words-by-default:
3235     # without metadata, count words
3236     # . prologue
3237     55/push-ebp
3238     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3239     # setup
3240     # . clear-stream(_test-input-stream)
3241     # . . push args
3242     68/push  _test-input-stream/imm32
3243     # . . call
3244     e8/call  clear-stream/disp32
3245     # . . discard args
3246     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3247     # . clear-stream(_test-output-stream)
3248     # . . push args
3249     68/push  _test-output-stream/imm32
3250     # . . call
3251     e8/call  clear-stream/disp32
3252     # . . discard args
3253     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3254     # initialize input
3255     # . write(_test-input-stream, "ab cd ef")
3256     # . . push args
3257     68/push  "ab cd ef"/imm32
3258     68/push  _test-input-stream/imm32
3259     # . . call
3260     e8/call  write/disp32
3261     # . . discard args
3262     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3263     # eax = num-bytes(_test-input-stream)
3264     # . . push args
3265     68/push  _test-input-stream/imm32
3266     # . . call
3267     e8/call  num-bytes/disp32
3268     # . . discard args
3269     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3270     # check-ints-equal(eax, 3, msg)
3271     # . . push args
3272     68/push  "F - test-num-bytes-counts-words-by-default"/imm32
3273     68/push  3/imm32/true
3274     50/push-eax
3275     # . . call
3276     e8/call  check-ints-equal/disp32
3277     # . . discard args
3278     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3279     # . epilogue
3280     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3281     5d/pop-to-ebp
3282     c3/return
3283 
3284 test-num-bytes-ignores-trailing-comment:
3285     # trailing comments appropriately ignored
3286     # . prologue
3287     55/push-ebp
3288     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3289     # setup
3290     # . clear-stream(_test-input-stream)
3291     # . . push args
3292     68/push  _test-input-stream/imm32
3293     # . . call
3294     e8/call  clear-stream/disp32
3295     # . . discard args
3296     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3297     # . clear-stream(_test-output-stream)
3298     # . . push args
3299     68/push  _test-output-stream/imm32
3300     # . . call
3301     e8/call  clear-stream/disp32
3302     # . . discard args
3303     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3304     # initialize input
3305     # . write(_test-input-stream, "ab cd # ef")
3306     # . . push args
3307     68/push  "ab cd # ef"/imm32
3308     68/push  _test-input-stream/imm32
3309     # . . call
3310     e8/call  write/disp32
3311     # . . discard args
3312     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3313     # eax = num-bytes(_test-input-stream)
3314     # . . push args
3315     68/push  _test-input-stream/imm32
3316     # . . call
3317     e8/call  num-bytes/disp32
3318     # . . discard args
3319     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3320     # check-ints-equal(eax, 2, msg)
3321     # . . push args
3322     68/push  "F - test-num-bytes-ignores-trailing-comment"/imm32
3323     68/push  2/imm32/true
3324     50/push-eax
3325     # . . call
3326     e8/call  check-ints-equal/disp32
3327     # . . discard args
3328     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3329     # . epilogue
3330     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3331     5d/pop-to-ebp
3332     c3/return
3333 
3334 test-num-bytes-handles-imm32:
3335     # if a word has the /imm32 metadata, count it as 4 bytes
3336     # . prologue
3337     55/push-ebp
3338     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3339     # setup
3340     # . clear-stream(_test-input-stream)
3341     # . . push args
3342     68/push  _test-input-stream/imm32
3343     # . . call
3344     e8/call  clear-stream/disp32
3345     # . . discard args
3346     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3347     # . clear-stream(_test-output-stream)
3348     # . . push args
3349     68/push  _test-output-stream/imm32
3350     # . . call
3351     e8/call  clear-stream/disp32
3352     # . . discard args
3353     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3354     # initialize input
3355     # . write(_test-input-stream, "ab cd/imm32 ef")
3356     # . . push args
3357     68/push  "ab cd/imm32 ef"/imm32
3358     68/push  _test-input-stream/imm32
3359     # . . call
3360     e8/call  write/disp32
3361     # . . discard args
3362     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3363     # eax = num-bytes(_test-input-stream)
3364     # . . push args
3365     68/push  _test-input-stream/imm32
3366     # . . call
3367     e8/call  num-bytes/disp32
3368     # . . discard args
3369     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3370     # check-ints-equal(eax, 6, msg)
3371     # . . push args
3372     68/push  "F - test-num-bytes-handles-imm32"/imm32
3373     68/push  6/imm32/true
3374     50/push-eax
3375     # . . call
3376     e8/call  check-ints-equal/disp32
3377     # . . discard args
3378     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3379     # . epilogue
3380     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3381     5d/pop-to-ebp
3382     c3/return
3383 
3384 # . . vim:nowrap:textwidth=0