https://github.com/akkartik/mu/blob/main/linux/survey_baremetal.subx
   1 # Assign addresses (co-ordinates) to labels (landmarks) in a program
   2 # (landscape) on stdin.
   3 #
   4 # To build:
   5 #   $ bootstrap/bootstrap translate [01]*.subx subx-params.subx survey_baremetal.subx  -o survey_baremetal
   6 #
   7 # The expected input is a stream of bytes and some interspersed labels.
   8 # Comments and '==' segment headers are allowed, but segment names are ignored.
   9 # Mappings between labels and addresses are emitted to stdout, assuming the
  10 # program starts at address 0x7c00. Addresses in segment headers are optional.
  11 #
  12 #   $ cat x
  13 #   == code
  14 #   l1:
  15 #   aa bb l1/imm8
  16 #   cc dd l2/disp32
  17 #   l2:
  18 #   ee foo/imm32
  19 #   == data 0x7c10
  20 #   foo:
  21 #     34
  22 #
  23 # The output is a list of labels and their computed addresses.
  24 #
  25 #   $ cat x  |bootstrap/bootstrap run survey_baremetal
  26 #   0x7c00 l1
  27 #   0x7c09 l2
  28 #   0x7c10 foo
  29 
  30 == code
  31 #   instruction                     effective address                                                   register    displacement    immediate
  32 # . op          subop               mod             rm32          base        index         scale       r32
  33 # . 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
  34 
  35 Entry:  # run tests if necessary, convert stdin if not
  36     # . prologue
  37     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
  38 
  39     # Heap = new-segment(Heap-size)
  40     # . . push args
  41     68/push  Heap/imm32
  42     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Heap-size/disp32                  # push *Heap-size
  43     # . . call
  44     e8/call  new-segment/disp32
  45     # . . discard args
  46     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
  47     # initialize-trace-stream(Trace-size)
  48     # . . push args
  49     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-size/disp32                 # push *Heap-size
  50     # . . call
  51     e8/call  initialize-trace-stream/disp32
  52     # . . discard args
  53     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
  54 
  55     # - if argc > 1 and argv[1] == "test", then return run_tests()
  56     # if (argc <= 1) goto interactive
  57     81          7/subop/compare     1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0/disp8         1/imm32           # compare *ebp
  58     7e/jump-if-<=  $subx-survey-main:interactive/disp8
  59     # if (!kernel-string-equal?(argv[1], "test")) goto interactive
  60     # . eax = kernel-string-equal?(argv[1], "test")
  61     # . . push args
  62     68/push  "test"/imm32
  63     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
  64     # . . call
  65     e8/call  kernel-string-equal?/disp32
  66     # . . discard args
  67     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
  68     # . if (eax == false) goto interactive
  69     3d/compare-eax-and  0/imm32/false
  70     74/jump-if-=  $subx-survey-main:interactive/disp8
  71     # run-tests()
  72     e8/call  run-tests/disp32
  73     # syscall_exit(*Num-test-failures)
  74     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/ebx   Num-test-failures/disp32          # copy *Num-test-failures to ebx
  75     eb/jump  $subx-survey-main:end/disp8
  76 $subx-survey-main:interactive:
  77     # - otherwise convert stdin
  78     # subx-survey(Stdin, Stdout)
  79     # . . push args
  80     68/push  Stdout/imm32
  81     68/push  Stdin/imm32
  82     # . . call
  83     e8/call  subx-survey/disp32
  84     # . . discard args
  85     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
  86 #?     # . write-stream(2/stderr, Trace-stream)
  87 #?     # . . push args
  88 #?     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
  89 #?     68/push  2/imm32/stderr
  90 #?     # . . call
  91 #?     e8/call  write-stream/disp32
  92 #?     # . . discard args
  93 #?     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
  94     # syscall_exit(0)
  95     bb/copy-to-ebx  0/imm32
  96 $subx-survey-main:end:
  97     e8/call  syscall_exit/disp32
  98 
  99 subx-survey:  # infile: (addr buffered-file), out: (addr buffered-file)
 100     # pseudocode
 101     #   var in: (stream byte Input-size)
 102     #   slurp(infile, in)
 103     #   var labels: (stream {label-name, address} Max-labels)
 104     #   compute-addresses(in, labels)
 105     #   emit-labels(out, labels)
 106     #
 107     # . prologue
 108     55/push-ebp
 109     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 110     # . save registers
 111     51/push-ecx
 112     52/push-edx
 113     56/push-esi
 114     # var labels/edx: (stream {label-name, address} Max-labels)
 115     # (we get more rows than Max-labels advertises because row size is smaller than in survey_elf)
 116     # . data
 117     2b/subtract                     0/mod/indirect  5/rm32/.disp32            .             .           4/r32/esp   Max-labels/disp32                 # subtract *Max-labels from esp
 118     # . size
 119     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Max-labels/disp32                 # push *Max-labels
 120     # . read
 121     68/push  0/imm32/read
 122     # . write
 123     68/push  0/imm32/write
 124     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
 125     # var in/esi: (stream byte Input-size)
 126     # . data
 127     2b/subtract                     0/mod/indirect  5/rm32/.disp32            .             .           4/r32/esp   Input-size/disp32                 # subtract *Input-size from esp
 128     # . size
 129     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Input-size/disp32                 # push *Input-size
 130     # . read
 131     68/push  0/imm32/read
 132     # . write
 133     68/push  0/imm32/write
 134     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           4/r32/esp   .               .                 # copy esp to esi
 135     # slurp(infile, in)
 136     # . . push args
 137     56/push-esi
 138     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 139     # . . call
 140     e8/call  slurp/disp32
 141     # . . discard args
 142     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 143     # compute-addresses(in, labels)
 144     # . . push args
 145     52/push-edx
 146     56/push-esi
 147     # . . call
 148     e8/call  compute-addresses/disp32
 149     # . . discard args
 150     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 151     # emit-labels(out, labels)
 152     # . . push args
 153     52/push-edx
 154     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 155     # . . call
 156     e8/call  emit-labels/disp32
 157     # . . discard args
 158     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 159     # flush(out)
 160     # . . push args
 161     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 162     # . . call
 163     e8/call  flush/disp32
 164     # . . discard args
 165     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 166 $subx-survey:end:
 167     # . reclaim locals
 168     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x18/imm32        # add to esp
 169     03/add                          0/mod/indirect  5/rm32/.disp32            .             .           4/r32/esp   Max-labels/disp32                 # add *Max-labels to esp
 170     03/add                          0/mod/indirect  5/rm32/.disp32            .             .           4/r32/esp   Input-size/disp32                 # add *Input-size to esp
 171     # . restore registers
 172     5e/pop-to-esi
 173     5a/pop-to-edx
 174     59/pop-to-ecx
 175     # . epilogue
 176     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 177     5d/pop-to-ebp
 178     c3/return
 179 
 180 test-subx-survey-computes-addresses:
 181     # input:
 182     #   == code
 183     #   ab x/imm32
 184     #   == data
 185     #   x:
 186     #     01
 187     #
 188     # trace contains (in any order):
 189     #   label x is at address 0x7c05
 190     #
 191     # . prologue
 192     55/push-ebp
 193     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 194     # setup
 195     # . clear-stream(_test-input-stream)
 196     # . . push args
 197     68/push  _test-input-stream/imm32
 198     # . . call
 199     e8/call  clear-stream/disp32
 200     # . . discard args
 201     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 202     # . clear-stream($_test-input-buffered-file->buffer)
 203     # . . push args
 204     68/push  $_test-input-buffered-file->buffer/imm32
 205     # . . call
 206     e8/call  clear-stream/disp32
 207     # . . discard args
 208     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 209     # . clear-stream(_test-output-stream)
 210     # . . push args
 211     68/push  _test-output-stream/imm32
 212     # . . call
 213     e8/call  clear-stream/disp32
 214     # . . discard args
 215     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 216     # . clear-stream($_test-output-buffered-file->buffer)
 217     # . . push args
 218     68/push  $_test-output-buffered-file->buffer/imm32
 219     # . . call
 220     e8/call  clear-stream/disp32
 221     # . . discard args
 222     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 223     # initialize input
 224     # . write(_test-input-stream, "== code\n")
 225     # . . push args
 226     68/push  "== code\n"/imm32
 227     68/push  _test-input-stream/imm32
 228     # . . call
 229     e8/call  write/disp32
 230     # . . discard args
 231     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 232     # . write(_test-input-stream, "ab x/imm32\n")
 233     # . . push args
 234     68/push  "ab x/imm32\n"/imm32
 235     68/push  _test-input-stream/imm32
 236     # . . call
 237     e8/call  write/disp32
 238     # . . discard args
 239     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 240     # . write(_test-input-stream, "== data\n")
 241     # . . push args
 242     68/push  "== data\n"/imm32
 243     68/push  _test-input-stream/imm32
 244     # . . call
 245     e8/call  write/disp32
 246     # . . discard args
 247     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 248     # . write(_test-input-stream, "x:\n")
 249     # . . push args
 250     68/push  "x:\n"/imm32
 251     68/push  _test-input-stream/imm32
 252     # . . call
 253     e8/call  write/disp32
 254     # . . discard args
 255     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 256     # . write(_test-input-stream, "01\n")
 257     # . . push args
 258     68/push  "01\n"/imm32
 259     68/push  _test-input-stream/imm32
 260     # . . call
 261     e8/call  write/disp32
 262     # . . discard args
 263     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 264     # subx-survey(_test-input-buffered-file, _test-output-buffered-file)
 265     # . . push args
 266     68/push  _test-output-buffered-file/imm32
 267     68/push  _test-input-buffered-file/imm32
 268     # . . call
 269     e8/call  subx-survey/disp32
 270     # . . discard args
 271     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 272     # check trace
 273 +-- 26 lines: #?     # dump *Trace-stream -----------------------------------------------------------------------------------------------------------------------------------------------
 299     # . check-trace-contains("label 'x' is at address 0x00007c05.", msg)
 300     # . . push args
 301     68/push  "F - test-subx-survey-computes-addresses/0"/imm32
 302     68/push  "label 'x' is at address 0x00007c05."/imm32
 303     # . . call
 304     e8/call  check-trace-contains/disp32
 305     # . . discard args
 306     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 307     # . epilogue
 308     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 309     5d/pop-to-ebp
 310     c3/return
 311 
 312 test-subx-survey-computes-addresses-with-padding:
 313     # input:
 314     #   == code
 315     #   ab x/imm32
 316     #   == data 0x7c10
 317     #   x:
 318     #     01
 319     #
 320     # trace contains (in any order):
 321     #   label x is at address 0x7c10
 322     #
 323     # . prologue
 324     55/push-ebp
 325     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 326     # setup
 327     # . clear-stream(_test-input-stream)
 328     # . . push args
 329     68/push  _test-input-stream/imm32
 330     # . . call
 331     e8/call  clear-stream/disp32
 332     # . . discard args
 333     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 334     # . clear-stream($_test-input-buffered-file->buffer)
 335     # . . push args
 336     68/push  $_test-input-buffered-file->buffer/imm32
 337     # . . call
 338     e8/call  clear-stream/disp32
 339     # . . discard args
 340     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 341     # . clear-stream(_test-output-stream)
 342     # . . push args
 343     68/push  _test-output-stream/imm32
 344     # . . call
 345     e8/call  clear-stream/disp32
 346     # . . discard args
 347     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 348     # . clear-stream($_test-output-buffered-file->buffer)
 349     # . . push args
 350     68/push  $_test-output-buffered-file->buffer/imm32
 351     # . . call
 352     e8/call  clear-stream/disp32
 353     # . . discard args
 354     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 355     # initialize input
 356     # . write(_test-input-stream, "== code\n")
 357     # . . push args
 358     68/push  "== code\n"/imm32
 359     68/push  _test-input-stream/imm32
 360     # . . call
 361     e8/call  write/disp32
 362     # . . discard args
 363     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 364     # . write(_test-input-stream, "ab x/imm32\n")
 365     # . . push args
 366     68/push  "ab x/imm32\n"/imm32
 367     68/push  _test-input-stream/imm32
 368     # . . call
 369     e8/call  write/disp32
 370     # . . discard args
 371     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 372     # . write(_test-input-stream, "== data\n")
 373     # . . push args
 374     68/push  "== data 0x7c10\n"/imm32
 375     68/push  _test-input-stream/imm32
 376     # . . call
 377     e8/call  write/disp32
 378     # . . discard args
 379     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 380     # . write(_test-input-stream, "x:\n")
 381     # . . push args
 382     68/push  "x:\n"/imm32
 383     68/push  _test-input-stream/imm32
 384     # . . call
 385     e8/call  write/disp32
 386     # . . discard args
 387     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 388     # . write(_test-input-stream, "01\n")
 389     # . . push args
 390     68/push  "01\n"/imm32
 391     68/push  _test-input-stream/imm32
 392     # . . call
 393     e8/call  write/disp32
 394     # . . discard args
 395     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 396     # subx-survey(_test-input-buffered-file, _test-output-buffered-file)
 397     # . . push args
 398     68/push  _test-output-buffered-file/imm32
 399     68/push  _test-input-buffered-file/imm32
 400     # . . call
 401     e8/call  subx-survey/disp32
 402     # . . discard args
 403     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 404     # check trace
 405 +-- 26 lines: #?     # dump *Trace-stream -----------------------------------------------------------------------------------------------------------------------------------------------
 431     # . check-trace-contains("label 'x' is at address 0x00007c10.", msg)
 432     # . . push args
 433     68/push  "F - test-subx-survey-computes-addresses-with-padding/0"/imm32
 434     68/push  "label 'x' is at address 0x00007c10."/imm32
 435     # . . call
 436     e8/call  check-trace-contains/disp32
 437     # . . discard args
 438     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 439     # . epilogue
 440     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 441     5d/pop-to-ebp
 442     c3/return
 443 
 444 compute-addresses:  # in: (addr stream byte), labels: (addr stream {(handle array byte), address})
 445     # pseudocode:
 446     #   var current-address = 0x7c00
 447     #   var line: (stream byte 512)
 448     #   while true                                  # line loop
 449     #     clear-stream(line)
 450     #     read-line(in, line)
 451     #     if (line->write == 0) break               # end of file
 452     #     while true                                # word loop
 453     #       word-slice = next-word(line)
 454     #       if slice-empty?(word-slice)             # end of line
 455     #         break
 456     #       else if slice-starts-with?(word-slice, "#")  # comment
 457     #         break                                 # end of line
 458     #       else if slice-equal?(word-slice, "==")  # segment header
 459     #         word-slice = next-word(line)
 460     #         if slice-empty?(word-slice)
 461     #           abort
 462     #         word-slice = next-word(line)  # segment address
 463     #         if slice-empty?(word-slice)
 464     #           goto line-loop              # segment address is optional
 465     #         new-address = parse-hex-int-from-slice(word-slice)
 466     #         if new-address < current-address
 467     #           abort
 468     #         current-address = new-address
 469     #       else if label?(word-slice)
 470     #         strip trailing ':' from word-slice
 471     #         var tmp/eax: (addr int) = insert-slice-or-abort(labels, word-slice)
 472     #         *tmp = current-address
 473     #         trace("label '" word-slice "' is at address " current-address ".")
 474     #         # labels occupy no space, so no need to increment offsets
 475     #       else
 476     #         width = compute-width-of-slice(word-slice)
 477     #         current-address += width
 478     #
 479     # . prologue
 480     55/push-ebp
 481     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 482     # . save registers
 483     50/push-eax
 484     51/push-ecx
 485     52/push-edx
 486     53/push-ebx
 487     56/push-esi
 488     57/push-edi
 489     # var current-address/esi: int = 0x7c00
 490     be/copy-to-esi  0x7c00/imm32
 491     # var line/ecx: (stream byte 512)
 492     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x200/imm32       # subtract from esp
 493     68/push  0x200/imm32/size
 494     68/push  0/imm32/read
 495     68/push  0/imm32/write
 496     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
 497     # var word-slice/edx: (addr slice)
 498     68/push  0/imm32
 499     68/push  0/imm32
 500     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
 501 $compute-addresses:line-loop:
 502     # clear-stream(line)
 503     # . . push args
 504     51/push-ecx
 505     # . . call
 506     e8/call  clear-stream/disp32
 507     # . . discard args
 508     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 509     # read-line(in, line)
 510     # . . push args
 511     51/push-ecx
 512     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 513     # . . call
 514     e8/call  read-line/disp32
 515     # . . discard args
 516     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 517     # if (line->write == 0) break
 518     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
 519     3d/compare-eax-and  0/imm32
 520     0f 84/jump-if-=  $compute-addresses:end/disp32
 521 +-- 33 lines: #?     # dump line --------------------------------------------------------------------------------------------------------------------------------------------------------
 554 $compute-addresses:word-loop:
 555     # next-word(line, word-slice)
 556     # . . push args
 557     52/push-edx
 558     51/push-ecx
 559     # . . call
 560     e8/call  next-word/disp32
 561     # . . discard args
 562     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 563 $compute-addresses:case-empty:
 564     # if slice-empty?(word-slice) break
 565     # . eax = slice-empty?(word-slice)
 566     # . . push args
 567     52/push-edx
 568     # . . call
 569     e8/call  slice-empty?/disp32
 570     # . . discard args
 571     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 572     # . if (eax != false) break
 573     3d/compare-eax-and  0/imm32/false
 574     0f 85/jump-if-!=  $compute-addresses:line-loop/disp32
 575 $compute-addresses:case-comment:
 576     # if slice-starts-with?(word-slice, "#") break
 577     # . . push args
 578     68/push  "#"/imm32
 579     52/push-edx
 580     # . . call
 581     e8/call  slice-starts-with?/disp32
 582     # . . discard args
 583     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 584     # . if (eax != false) break
 585     3d/compare-eax-and  0/imm32/false
 586     0f 85/jump-if-!=  $compute-addresses:line-loop/disp32
 587 $compute-addresses:case-segment-header:
 588     # if !slice-equal?(word-slice, "==") goto next case
 589     # . eax = slice-equal?(word-slice, "==")
 590     # . . push args
 591     68/push  "=="/imm32
 592     52/push-edx
 593     # . . call
 594     e8/call  slice-equal?/disp32
 595     # . . discard args
 596     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 597     # . if (eax != false) break
 598     3d/compare-eax-and  0/imm32/false
 599     0f 84/jump-if-=  $compute-addresses:case-label/disp32
 600     # next-word(line, word-slice)
 601     # . . push args
 602     52/push-edx
 603     51/push-ecx
 604     # . . call
 605     e8/call  next-word/disp32
 606     # . . discard args
 607     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 608     # if slice-empty?(word-slice) abort
 609     # . eax = slice-empty?(word-slice)
 610     # . . push args
 611     52/push-edx
 612     # . . call
 613     e8/call  slice-empty?/disp32
 614     # . . discard args
 615     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 616     # . if (eax != false) abort
 617     3d/compare-eax-and  0/imm32/false
 618     0f 85/jump-if-!=  $compute-addresses:abort/disp32
 619     # next-word(line, word-slice)
 620     # . . push args
 621     52/push-edx
 622     51/push-ecx
 623     # . . call
 624     e8/call  next-word/disp32
 625     # . . discard args
 626     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 627     # if slice-empty?(word-slice) break
 628     # . eax = slice-empty?(word-slice)
 629     # . . push args
 630     52/push-edx
 631     # . . call
 632     e8/call  slice-empty?/disp32
 633     # . . discard args
 634     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 635     # . if (eax != false) break
 636     3d/compare-eax-and  0/imm32/false
 637     0f 85/jump-if-!=  $compute-addresses:line-loop/disp32
 638     # var new-address/eax: int = parse-hex-int-from-slice(word-slice)
 639     # . . push args
 640     52/push-edx
 641     # . . call
 642     e8/call  parse-hex-int-from-slice/disp32
 643     # . . discard args
 644     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 645     # if (new-address < current-address) abort
 646     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           6/r32/esi   .               .                 # compare eax with esi
 647     0f 82/jump-if-addr<  $compute-addresses:error-bad-segment-address/disp32
 648     # current-address = new-address
 649     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to esi
 650     # break
 651     e9/jump  $compute-addresses:line-loop/disp32
 652 $compute-addresses:case-label:
 653     # if (!label?(word-slice)) goto next case
 654     # . eax = label?(word-slice)
 655     # . . push args
 656     52/push-edx
 657     # . . call
 658     e8/call  label?/disp32
 659     # . . discard args
 660     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 661     # . if (eax == false) goto next case
 662     3d/compare-eax-and  0/imm32/false
 663     0f 84/jump-if-=  $compute-addresses:case-default/disp32
 664     # strip trailing ':' from word-slice
 665     ff          1/subop/decrement   1/mod/*+disp8   2/rm32/edx    .           .             .           .           4/disp8         .                 # decrement *(edx+4)
 666     # var tmp/eax: (addr int) = insert-slice-or-abort(labels, word-slice, row-size=12)
 667     # . . push args
 668     68/push  Heap/imm32
 669     68/push  0xc/imm32/row-size
 670     52/push-edx
 671     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 672     # . . call
 673     e8/call  insert-slice-or-abort/disp32
 674     # . . discard args
 675     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
 676     # *tmp = current-address
 677     89/copy                         0/mod/indirect  0/rm32/eax    .           .             .           6/r32/esi   .               .                 # copy esi to *eax
 678     # trace-slsns("label '" word-slice "' is at address " current-address ".")
 679     # . . push args
 680     68/push  "."/imm32
 681     56/push-esi
 682     68/push  "' is at address "/imm32
 683     52/push-edx
 684     68/push  "label '"/imm32
 685     # . . call
 686     e8/call  trace-slsns/disp32
 687     # . . discard args
 688     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
 689     # continue
 690     e9/jump  $compute-addresses:word-loop/disp32
 691 $compute-addresses:case-default:
 692     # width/eax = compute-width-of-slice(word-slice)
 693     # . . push args
 694     52/push-edx
 695     # . . call
 696     e8/call compute-width-of-slice/disp32
 697     # . . discard args
 698     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 699     # current-address += width
 700     01/add                          3/mod/direct    6/rm32/esi    .           .             .           0/r32/eax   .               .                 # add eax to *esi
 701 +-- 41 lines: #?     # dump segment-offset ----------------------------------------------------------------------------------------------------------------------------------------------
 742     e9/jump $compute-addresses:word-loop/disp32
 743 $compute-addresses:end:
 744     # . reclaim locals
 745     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x214/imm32       # add to esp
 746     # . restore registers
 747     5f/pop-to-edi
 748     5e/pop-to-esi
 749     5b/pop-to-ebx
 750     5a/pop-to-edx
 751     59/pop-to-ecx
 752     58/pop-to-eax
 753     # . epilogue
 754     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 755     5d/pop-to-ebp
 756     c3/return
 757 
 758 $compute-addresses:abort:
 759     # . _write(2/stderr, error)
 760     # . . push args
 761     68/push  "'==' must be followed by segment name and optionally an address\n"/imm32
 762     68/push  2/imm32/stderr
 763     # . . call
 764     e8/call  _write/disp32
 765     # . . discard args
 766     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 767     # . syscall_exit(1)
 768     bb/copy-to-ebx  1/imm32
 769     e8/call  syscall_exit/disp32
 770     # never gets here
 771 
 772 $compute-addresses:error-bad-segment-address:
 773     # . _write(2/stderr, error)
 774     # . . push args
 775     68/push  "'==' specifies an address that implies negative padding\n"/imm32
 776     68/push  2/imm32/stderr
 777     # . . call
 778     e8/call  _write/disp32
 779     # . . discard args
 780     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 781     # . syscall_exit(1)
 782     bb/copy-to-ebx  1/imm32
 783     e8/call  syscall_exit/disp32
 784     # never gets here
 785 
 786 test-compute-addresses:
 787     # input:
 788     #   == code
 789     #   ab x/imm32  # skip comment
 790     #   == data
 791     #   00
 792     #   x:
 793     #     34
 794     #
 795     # trace contains (in any order):
 796     #   label 'x' is at address 0x7c06.
 797     #
 798     # . prologue
 799     55/push-ebp
 800     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 801     # setup
 802     # . clear-stream(_test-input-stream)
 803     # . . push args
 804     68/push  _test-input-stream/imm32
 805     # . . call
 806     e8/call  clear-stream/disp32
 807     # . . discard args
 808     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 809     # var labels/edx: (stream byte 2*12)
 810     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x18/imm32        # subtract from esp
 811     68/push  0x18/imm32/size
 812     68/push  0/imm32/read
 813     68/push  0/imm32/write
 814     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
 815     # initialize input
 816     # . write(_test-input-stream, "== code\n")
 817     # . . push args
 818     68/push  "== code\n"/imm32
 819     68/push  _test-input-stream/imm32
 820     # . . call
 821     e8/call  write/disp32
 822     # . . discard args
 823     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 824     # . write(_test-input-stream, "ab x/imm32  # skip comment\n")
 825     # . . push args
 826     68/push  "ab x/imm32  # skip comment\n"/imm32
 827     68/push  _test-input-stream/imm32
 828     # . . call
 829     e8/call  write/disp32
 830     # . . discard args
 831     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 832     # . write(_test-input-stream, "== data\n")
 833     # . . push args
 834     68/push  "== data\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, "00\n")
 841     # . . push args
 842     68/push  "00\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, "x:\n")
 849     # . . push args
 850     68/push  "x:\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, "34\n")
 857     # . . push args
 858     68/push  "34\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     # compute-addresses(_test-input-stream, labels)
 865     # . . push args
 866     52/push-edx
 867     68/push  _test-input-stream/imm32
 868     # . . call
 869     e8/call  compute-addresses/disp32
 870     # . . discard args
 871     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32          # add to esp
 872 +-- 26 lines: #?     # dump *Trace-stream -----------------------------------------------------------------------------------------------------------------------------------------------
 898     # . check-trace-contains("label 'x' is at address 0x00007c06.", msg)
 899     # . . push args
 900     68/push  "F - test-compute-addresses"/imm32
 901     68/push  "label 'x' is at address 0x00007c06."/imm32
 902     # . . call
 903     e8/call  check-trace-contains/disp32
 904     # . . discard args
 905     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 906     # . check-ints-equal(labels->write, 0xc, msg)
 907     # . . push args
 908     68/push  "F - test-compute-addresses-maintains-labels-write-index"/imm32
 909     68/push  0xc/imm32/1-entry
 910     ff          6/subop/push        0/mod/indirect  2/rm32/edx    .           .             .           .           .               .                 # push *edx
 911     # . . call
 912     e8/call  check-ints-equal/disp32
 913     # . . discard args
 914     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 915     # . reclaim locals
 916     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x24/imm32        # add to esp
 917     # . epilogue
 918     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 919     5d/pop-to-ebp
 920     c3/return
 921 
 922 emit-labels:  # out: (addr buffered-file), labels: (addr stream {(handle array byte), address})
 923     # pseudocode:
 924     #   curr = table->data
 925     #   max = &table->data[table->write]
 926     #   while curr < max
 927     #     var label: (addr array byte) = lookup(*curr)
 928     #     curr += 8
 929     #     write-buffered(out, label)
 930     #     write-buffered(out, ' ')
 931     #     write-buffered(out, *curr)
 932     #     curr += 4
 933     #     write(out, '\n')
 934     #   return 0
 935     #
 936     # . prologue
 937     55/push-ebp
 938     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 939     # . save registers
 940     50/push-eax
 941     51/push-ecx
 942     52/push-edx
 943     56/push-esi
 944     # esi = table
 945     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   0xc/disp8       .                 # copy *(ebp+12) to esi
 946     # var curr/ecx: (addr handle array byte) = table->data
 947     8d/copy-address                 1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   0xc/disp8       .                 # copy esi+12 to ecx
 948     # var max/edx: (addr byte) = &table->data[table->write]
 949     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           2/r32/edx   .               .                 # copy *esi to edx
 950     8d/copy-address                 0/mod/indirect  4/rm32/sib    1/base/ecx  2/index/edx   .           2/r32/edx   .               .                 # copy ecx+edx to edx
 951 $emit-labels:loop:
 952     # if (curr >= max) return null
 953     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
 954     0f 83/jump-if-addr>=  $emit-labels:end/disp32
 955     # var label/eax: (addr array byte) = lookup(*curr)
 956     # . . push args
 957     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         .                 # push *(ecx+4)
 958     ff          6/subop/push        0/mod/indirect  1/rm32/ecx    .           .             .           .           .               .                 # push *ecx
 959     # . . call
 960     e8/call  lookup/disp32
 961     # . . discard args
 962     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 963     # curr += 8
 964     81          0/subop/add         3/mod/direct    1/rm32/ecx    .           .             .           .           .               8/imm32           # add to ecx
 965     # write-buffered(out, label)
 966     # . . push args
 967     50/push-eax
 968     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 969     # . . call
 970     e8/call  write-buffered/disp32
 971     # . . discard args
 972     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 973     # write-buffered(out, ' ')
 974     # . . push args
 975     68/push  Space/imm32
 976     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 977     # . . call
 978     e8/call  write-buffered/disp32
 979     # . . discard args
 980     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 981     # . flush(Stderr)
 982     # . . push args
 983     68/push  Stderr/imm32
 984     # . . call
 985     e8/call  flush/disp32
 986     # . . discard args
 987     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 988     # write-int32-hex-buffered(out, *curr)
 989     # . . push args
 990     ff          6/subop/push        0/mod/indirect  1/rm32/ecx    .           .             .           .           .               .                 # push *ecx
 991     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 992     # . . call
 993     e8/call  write-int32-hex-buffered/disp32
 994     # . . discard args
 995     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 996     # curr += 4
 997     81          0/subop/add         3/mod/direct    1/rm32/ecx    .           .             .           .           .               4/imm32           # add to ecx
 998     # write-buffered(out, '\n')
 999     # . . push args
1000     68/push  Newline/imm32
1001     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1002     # . . call
1003     e8/call  write-buffered/disp32
1004     # . . discard args
1005     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1006     # loop
1007     e9/jump  $emit-labels:loop/disp32
1008 $emit-labels:end:
1009     # . restore registers
1010     5e/pop-to-esi
1011     5a/pop-to-edx
1012     59/pop-to-ecx
1013     58/pop-to-eax
1014     # . epilogue
1015     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1016     5d/pop-to-ebp
1017     c3/return
1018 
1019 # - some helpers for tests
1020 
1021 # some variants of 'trace' that take multiple arguments in different combinations of types:
1022 #   n: int
1023 #   c: character [4-bytes, will eventually be UTF-8]
1024 #   s: (addr array byte)
1025 #   l: (addr slice)
1026 # one gotcha: 's5' must not be empty
1027 
1028 trace-slsns:  # s1: (addr array byte), l2: (addr slice), s3: (addr array byte), n4: int, s5: (addr array byte)
1029     # . prologue
1030     55/push-ebp
1031     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1032     # write(*Trace-stream, s1)
1033     # . . push args
1034     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1035     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
1036     # . . call
1037     e8/call  write/disp32
1038     # . . discard args
1039     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1040     # write-slice(*Trace-stream, l2)
1041     # . . push args
1042     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1043     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
1044     # . . call
1045     e8/call  write-slice/disp32
1046     # . . discard args
1047     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1048     # write(*Trace-stream, s3)
1049     # . . push args
1050     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
1051     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
1052     # . . call
1053     e8/call  write/disp32
1054     # . . discard args
1055     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1056     # write-int32-hex(*Trace-stream, n4)
1057     # . . push args
1058     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x14/disp8      .                 # push *(ebp+20)
1059     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
1060     # . . call
1061     e8/call  write-int32-hex/disp32
1062     # . . discard args
1063     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1064     # trace(s5)  # implicitly adds a newline and finalizes the trace line
1065     # . . push args
1066     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x18/disp8      .                 # push *(ebp+24)
1067     # . . call
1068     e8/call  trace/disp32
1069     # . . discard args
1070     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1071 $trace-slsns:end:
1072     # . epilogue
1073     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1074     5d/pop-to-ebp
1075     c3/return
1076 
1077 test-trace-slsns:
1078     # . prologue
1079     55/push-ebp
1080     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1081     # setup
1082     # . *Trace-stream->write = 0
1083     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   Trace-stream/disp32               # copy *Trace-stream to eax
1084     c7          0/subop/copy        0/mod/direct    0/rm32/eax    .           .             .           .           .               0/imm32           # clear *eax
1085     # (eax..ecx) = "b"
1086     b8/copy-to-eax  "b"/imm32
1087     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1088     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
1089     05/add-to-eax  4/imm32
1090     # var b/ebx: slice = {eax, ecx}
1091     51/push-ecx
1092     50/push-eax
1093     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
1094     # trace-slsls("A" b "c " 3 " e")
1095     # . . push args
1096     68/push  " e"/imm32
1097     68/push  3/imm32
1098     68/push  "c "/imm32
1099     53/push-ebx
1100     68/push  "A"/imm32
1101     # . . call
1102     e8/call  trace-slsns/disp32
1103     # . . discard args
1104     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
1105 +-- 26 lines: #?     # dump *Trace-stream -----------------------------------------------------------------------------------------------------------------------------------------------
1131     # check-trace-contains("Abc 0x00000003 e")
1132     # . . push args
1133     68/push  "F - test-trace-slsls"/imm32
1134     68/push  "Abc 0x00000003 e"/imm32
1135     # . . call
1136     e8/call  check-trace-contains/disp32
1137     # . . discard args
1138     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1139     # . epilogue
1140     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1141     5d/pop-to-ebp
1142     c3/return
1143 
1144 # . . vim:nowrap:textwidth=0