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 #
   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 names are ignored. The
   9 # emitted code will all lie in a single contiguous address range starting at
  10 # address 0x7c00. Addresses in segment headers are optional. If provided, this
  11 # program will insert padding in the output until the desired address is
  12 # reached.
  13 #
  14 #   $ cat x
  15 #   == code
  16 #   l1:
  17 #   aa bb l1/imm8
  18 #   cc dd l2/disp32
  19 #   l2:
  20 #   ee foo/imm32
  21 #   == data 0x7c10
  22 #   foo:
  23 #     34
  24 #
  25 # The output is a list of labels and their computed addresses.
  26 #
  27 #   $ cat x  |bootstrap/bootstrap run survey_baremetal
  28 #   0x7c00 l1
  29 #   0x7c09 l2
  30 #   0x7c10 foo
  31 
  32 == code
  33 #   instruction                     effective address                                                   register    displacement    immediate
  34 # . op          subop               mod             rm32          base        index         scale       r32
  35 # . 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
  36 
  37 Entry:  # run tests if necessary, convert stdin if not
  38     # . prologue
  39     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
  40 
  41     # Heap = new-segment(Heap-size)
  42     # . . push args
  43     68/push  Heap/imm32
  44     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Heap-size/disp32                  # push *Heap-size
  45     # . . call
  46     e8/call  new-segment/disp32
  47     # . . discard args
  48     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
  49     # initialize-trace-stream(Trace-size)
  50     # . . push args
  51     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-size/disp32                 # push *Heap-size
  52     # . . call
  53     e8/call  initialize-trace-stream/disp32
  54     # . . discard args
  55     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
  56 
  57     # - if argc > 1 and argv[1] == "test", then return run_tests()
  58     # if (argc <= 1) goto interactive
  59     81          7/subop/compare     1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0/disp8         1/imm32           # compare *ebp
  60     7e/jump-if-<=  $subx-survey-main:interactive/disp8
  61     # if (!kernel-string-equal?(argv[1], "test")) goto interactive
  62     # . eax = kernel-string-equal?(argv[1], "test")
  63     # . . push args
  64     68/push  "test"/imm32
  65     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
  66     # . . call
  67     e8/call  kernel-string-equal?/disp32
  68     # . . discard args
  69     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
  70     # . if (eax == false) goto interactive
  71     3d/compare-eax-and  0/imm32/false
  72     74/jump-if-=  $subx-survey-main:interactive/disp8
  73     # run-tests()
  74     e8/call  run-tests/disp32
  75     # syscall(exit, *Num-test-failures)
  76     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/ebx   Num-test-failures/disp32          # copy *Num-test-failures to ebx
  77     eb/jump  $subx-survey-main:end/disp8
  78 $subx-survey-main:interactive:
  79     # - otherwise convert stdin
  80     # subx-survey(Stdin, Stdout)
  81     # . . push args
  82     68/push  Stdout/imm32
  83     68/push  Stdin/imm32
  84     # . . call
  85     e8/call  subx-survey/disp32
  86     # . . discard args
  87     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
  88 #?     # . write-stream(2/stderr, Trace-stream)
  89 #?     # . . push args
  90 #?     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
  91 #?     68/push  2/imm32/stderr
  92 #?     # . . call
  93 #?     e8/call  write-stream/disp32
  94 #?     # . . discard args
  95 #?     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
  96     # syscall(exit, 0)
  97     bb/copy-to-ebx  0/imm32
  98 $subx-survey-main:end:
  99     e8/call  syscall_exit/disp32
 100 
 101 subx-survey:  # infile: (addr buffered-file), out: (addr buffered-file)
 102     # pseudocode
 103     #   var in: (stream byte Input-size)
 104     #   slurp(infile, in)
 105     #   var labels: (stream {label-name, address} Max-labels)
 106     #   compute-addresses(in, labels)
 107     #   emit-labels(out, labels)
 108     #
 109     # . prologue
 110     55/push-ebp
 111     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 112     # . save registers
 113     51/push-ecx
 114     52/push-edx
 115     56/push-esi
 116     # var labels/edx: (stream {label-name, address} Max-labels)
 117     # (we get more rows than Max-labels advertises because row size is smaller than in survey_elf)
 118     # . data
 119     2b/subtract                     0/mod/indirect  5/rm32/.disp32            .             .           4/r32/esp   Max-labels/disp32                 # subtract *Max-labels from esp
 120     # . size
 121     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Max-labels/disp32                 # push *Max-labels
 122     # . read
 123     68/push  0/imm32/read
 124     # . write
 125     68/push  0/imm32/write
 126     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
 127     # var in/esi: (stream byte Input-size)
 128     # . data
 129     2b/subtract                     0/mod/indirect  5/rm32/.disp32            .             .           4/r32/esp   Input-size/disp32                 # subtract *Input-size from esp
 130     # . size
 131     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Input-size/disp32                 # push *Input-size
 132     # . read
 133     68/push  0/imm32/read
 134     # . write
 135     68/push  0/imm32/write
 136     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           4/r32/esp   .               .                 # copy esp to esi
 137     # slurp(infile, in)
 138     # . . push args
 139     56/push-esi
 140     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 141     # . . call
 142     e8/call  slurp/disp32
 143     # . . discard args
 144     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 145     # compute-addresses(in, labels)
 146     # . . push args
 147     52/push-edx
 148     56/push-esi
 149     # . . call
 150     e8/call  compute-addresses/disp32
 151     # . . discard args
 152     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 153     # emit-labels(out, labels)
 154     # . . push args
 155     52/push-edx
 156     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 157     # . . call
 158     e8/call  emit-labels/disp32
 159     # . . discard args
 160     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 161     # flush(out)
 162     # . . push args
 163     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 164     # . . call
 165     e8/call  flush/disp32
 166     # . . discard args
 167     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 168 $subx-survey:end:
 169     # . reclaim locals
 170     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x18/imm32        # add to esp
 171     03/add                          0/mod/indirect  5/rm32/.disp32            .             .           4/r32/esp   Max-labels/disp32                 # add *Max-labels to esp
 172     03/add                          0/mod/indirect  5/rm32/.disp32            .             .           4/r32/esp   Input-size/disp32                 # add *Input-size to esp
 173     # . restore registers
 174     5e/pop-to-esi
 175     5a/pop-to-edx
 176     59/pop-to-ecx
 177     # . epilogue
 178     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 179     5d/pop-to-ebp
 180     c3/return
 181 
 182 test-subx-survey-computes-addresses:
 183     # input:
 184     #   == code
 185     #   ab x/imm32
 186     #   == data
 187     #   x:
 188     #     01
 189     #
 190     # trace contains (in any order):
 191     #   label x is at address 0x7c05
 192     #
 193     # . prologue
 194     55/push-ebp
 195     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 196     # setup
 197     # . clear-stream(_test-input-stream)
 198     # . . push args
 199     68/push  _test-input-stream/imm32
 200     # . . call
 201     e8/call  clear-stream/disp32
 202     # . . discard args
 203     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 204     # . clear-stream($_test-input-buffered-file->buffer)
 205     # . . push args
 206     68/push  $_test-input-buffered-file->buffer/imm32
 207     # . . call
 208     e8/call  clear-stream/disp32
 209     # . . discard args
 210     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 211     # . clear-stream(_test-output-stream)
 212     # . . push args
 213     68/push  _test-output-stream/imm32
 214     # . . call
 215     e8/call  clear-stream/disp32
 216     # . . discard args
 217     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 218     # . clear-stream($_test-output-buffered-file->buffer)
 219     # . . push args
 220     68/push  $_test-output-buffered-file->buffer/imm32
 221     # . . call
 222     e8/call  clear-stream/disp32
 223     # . . discard args
 224     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 225     # initialize input
 226     # . write(_test-input-stream, "== code\n")
 227     # . . push args
 228     68/push  "== code\n"/imm32
 229     68/push  _test-input-stream/imm32
 230     # . . call
 231     e8/call  write/disp32
 232     # . . discard args
 233     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 234     # . write(_test-input-stream, "ab x/imm32\n")
 235     # . . push args
 236     68/push  "ab x/imm32\n"/imm32
 237     68/push  _test-input-stream/imm32
 238     # . . call
 239     e8/call  write/disp32
 240     # . . discard args
 241     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 242     # . write(_test-input-stream, "== data\n")
 243     # . . push args
 244     68/push  "== data\n"/imm32
 245     68/push  _test-input-stream/imm32
 246     # . . call
 247     e8/call  write/disp32
 248     # . . discard args
 249     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 250     # . write(_test-input-stream, "x:\n")
 251     # . . push args
 252     68/push  "x:\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, "01\n")
 259     # . . push args
 260     68/push  "01\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     # subx-survey(_test-input-buffered-file, _test-output-buffered-file)
 267     # . . push args
 268     68/push  _test-output-buffered-file/imm32
 269     68/push  _test-input-buffered-file/imm32
 270     # . . call
 271     e8/call  subx-survey/disp32
 272     # . . discard args
 273     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 274     # check trace
 275 +-- 26 lines: #?     # dump *Trace-stream ----------------------------------------------------------------------------------------------------------------------------------
 301     # . check-trace-contains("label 'x' is at address 0x00007c05.", msg)
 302     # . . push args
 303     68/push  "F - test-subx-survey-computes-addresses/0"/imm32
 304     68/push  "label 'x' is at address 0x00007c05."/imm32
 305     # . . call
 306     e8/call  check-trace-contains/disp32
 307     # . . discard args
 308     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 309     # . epilogue
 310     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 311     5d/pop-to-ebp
 312     c3/return
 313 
 314 test-subx-survey-computes-addresses-with-padding:
 315     # input:
 316     #   == code
 317     #   ab x/imm32
 318     #   == data 0x7c10
 319     #   x:
 320     #     01
 321     #
 322     # trace contains (in any order):
 323     #   label x is at address 0x7c10
 324     #
 325     # . prologue
 326     55/push-ebp
 327     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 328     # setup
 329     # . clear-stream(_test-input-stream)
 330     # . . push args
 331     68/push  _test-input-stream/imm32
 332     # . . call
 333     e8/call  clear-stream/disp32
 334     # . . discard args
 335     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 336     # . clear-stream($_test-input-buffered-file->buffer)
 337     # . . push args
 338     68/push  $_test-input-buffered-file->buffer/imm32
 339     # . . call
 340     e8/call  clear-stream/disp32
 341     # . . discard args
 342     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 343     # . clear-stream(_test-output-stream)
 344     # . . push args
 345     68/push  _test-output-stream/imm32
 346     # . . call
 347     e8/call  clear-stream/disp32
 348     # . . discard args
 349     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 350     # . clear-stream($_test-output-buffered-file->buffer)
 351     # . . push args
 352     68/push  $_test-output-buffered-file->buffer/imm32
 353     # . . call
 354     e8/call  clear-stream/disp32
 355     # . . discard args
 356     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 357     # initialize input
 358     # . write(_test-input-stream, "== code\n")
 359     # . . push args
 360     68/push  "== code\n"/imm32
 361     68/push  _test-input-stream/imm32
 362     # . . call
 363     e8/call  write/disp32
 364     # . . discard args
 365     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 366     # . write(_test-input-stream, "ab x/imm32\n")
 367     # . . push args
 368     68/push  "ab x/imm32\n"/imm32
 369     68/push  _test-input-stream/imm32
 370     # . . call
 371     e8/call  write/disp32
 372     # . . discard args
 373     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 374     # . write(_test-input-stream, "== data\n")
 375     # . . push args
 376     68/push  "== data 0x7c10\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, "x:\n")
 383     # . . push args
 384     68/push  "x:\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, "01\n")
 391     # . . push args
 392     68/push  "01\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     # subx-survey(_test-input-buffered-file, _test-output-buffered-file)
 399     # . . push args
 400     68/push  _test-output-buffered-file/imm32
 401     68/push  _test-input-buffered-file/imm32
 402     # . . call
 403     e8/call  subx-survey/disp32
 404     # . . discard args
 405     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 406     # check trace
 407 +-- 26 lines: #?     # dump *Trace-stream ----------------------------------------------------------------------------------------------------------------------------------
 433     # . check-trace-contains("label 'x' is at address 0x00007c10.", msg)
 434     # . . push args
 435     68/push  "F - test-subx-survey-computes-addresses-with-padding/0"/imm32
 436     68/push  "label 'x' is at address 0x00007c10."/imm32
 437     # . . call
 438     e8/call  check-trace-contains/disp32
 439     # . . discard args
 440     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 441     # . epilogue
 442     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 443     5d/pop-to-ebp
 444     c3/return
 445 
 446 compute-addresses:  # in: (addr stream byte), labels: (addr stream {(handle array byte), address})
 447     # pseudocode:
 448     #   var current-address = 0x7c00
 449     #   var line: (stream byte 512)
 450     #   while true                                  # line loop
 451     #     clear-stream(line)
 452     #     read-line(in, line)
 453     #     if (line->write == 0) break               # end of file
 454     #     while true                                # word loop
 455     #       word-slice = next-word(line)
 456     #       if slice-empty?(word-slice)             # end of line
 457     #         break
 458     #       else if slice-starts-with?(word-slice, "#")  # comment
 459     #         break                                 # end of line
 460     #       else if slice-equal?(word-slice, "==")  # segment header
 461     #         word-slice = next-word(line)
 462     #         if slice-empty?(word-slice)
 463     #           abort
 464     #         word-slice = next-word(line)  # segment address
 465     #         if slice-empty?(word-slice)
 466     #           goto line-loop              # segment address is optional
 467     #         new-address = parse-hex-int-from-slice(word-slice)
 468     #         if new-address < current-address
 469     #           abort
 470     #         current-address = new-address
 471     #       else if label?(word-slice)
 472     #         strip trailing ':' from word-slice
 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) = get-or-insert-slice(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  get-or-insert-slice/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