# Assign addresses (co-ordinates) to instructions (landmarks) in a program
# (landscape).
# Use the addresses assigned to:
# a) replace labels
# b) add an ELF header and segment headers with addresses and offsets correctly filled in
#
# To build:
# $ ./bootstrap translate init.linux [01]*.subx apps/subx-params.subx apps/survey_elf.subx -o apps/survey_elf
#
# The expected input is a stream of bytes with '==' segment headers, comments
# and some interspersed labels.
# $ cat x
# == code 0x1
# l1:
# aa bb l1/imm8
# cc dd l2/disp32
# l2:
# ee foo/imm32
# == data 0x10
# foo:
# 00
#
# The output is the stream of bytes without segment headers or label definitions,
# and with label references replaced with numeric values/displacements.
#
# $ cat x |./bootstrap run apps/survey_elf
# ...ELF header bytes...
# # ELF header above will specify that code segment begins at this offset
# aa bb nn # some computed address
# cc dd nn nn nn nn # some computed displacement
# ee nn nn nn nn # some computed address
# # ELF header above will specify that data segment begins at this offset
# 00
#
# The ELF format has some persnickety constraints on the starting addresses of
# segments, so input headers are treated as guidelines and adjusted in the
# output.
== code
# instruction effective address register displacement immediate
# . op subop mod rm32 base index scale r32
# . 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
Entry: # run tests if necessary, convert stdin if not
# . prologue
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# Heap = new-segment(Heap-size)
# . . push args
68/push Heap/imm32
ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Heap-size/disp32 # push *Heap-size
# . . call
e8/call new-segment/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# initialize-trace-stream(Trace-size)
# . . push args
ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-size/disp32 # push *Heap-size
# . . call
e8/call initialize-trace-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# - if argc > 1 and argv[1] == "test", then return run_tests()
# if (argc <= 1) goto interactive
81 7/subop/compare 1/mod/*+disp8 5/rm32/ebp . . . . 0/disp8 1/imm32 # compare *ebp
7e/jump-if-<= $subx-survey-main:interactive/disp8
# if (!kernel-string-equal?(argv[1], "test")) goto interactive
# . eax = kernel-string-equal?(argv[1], "test")
# . . push args
68/push "test"/imm32
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
# . . call
e8/call kernel-string-equal?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . if (eax == false) goto interactive
3d/compare-eax-and 0/imm32/false
74/jump-if-= $subx-survey-main:interactive/disp8
# run-tests()
e8/call run-tests/disp32
# syscall(exit, *Num-test-failures)
8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/ebx Num-test-failures/disp32 # copy *Num-test-failures to ebx
eb/jump $subx-survey-main:end/disp8
$subx-survey-main:interactive:
# - otherwise convert stdin
# subx-survey(Stdin, Stdout)
# . . push args
68/push Stdout/imm32
68/push Stdin/imm32
# . . call
e8/call subx-survey/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . write-stream(2/stderr, Trace-stream)
#? # . . push args
#? ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write-stream/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# syscall(exit, 0)
bb/copy-to-ebx 0/imm32
$subx-survey-main:end:
e8/call syscall_exit/disp32
# data structures:
# segment-info: {address, file-offset, size} (12 bytes)
# segments: (addr stream {(handle array byte), segment-info}) (20 bytes per row)
# label-info: {segment-name: (handle array byte), segment-offset, address} (16 bytes)
# labels: (addr stream {(handle array byte), label-info}) (24 bytes per row)
# these are all inefficient, using sequential scans for lookups
subx-survey: # infile: (addr buffered-file), out: (addr buffered-file)
# pseudocode
# var in: (stream byte Input-size)
# slurp(infile, in)
# var segments: (stream {segment-name, segment-info})
# var labels: (stream {label-name, label-info} Max-labels)
# compute-offsets(in, segments, labels)
# compute-addresses(segments, labels)
# rewind-stream(in)
# emit-output(in, out, segments, labels)
#
# . prologue
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# . save registers
51/push-ecx
52/push-edx
56/push-esi
# var segments/ecx: (stream {string, segment-info} 200) # 10 rows * 20 bytes/row
81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0xc8/imm32 # subtract from esp
68/push 0xc8/imm32/size
68/push 0/imm32/read
68/push 0/imm32/write
89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
# var labels/edx: (stream label-info Max-labels)
# . data
2b/subtract 0/mod/indirect 5/rm32/.disp32 . . 4/r32/esp Max-labels/disp32 # subtract *Max-labels from esp
# . size
ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Max-labels/disp32 # push *Max-labels
# . read
68/push 0/imm32/read
# . write
68/push 0/imm32/write
89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx
# var in/esi: (stream byte Input-size)
# . data
2b/subtract 0/mod/indirect 5/rm32/.disp32 . . 4/r32/esp Input-size/disp32 # subtract *Input-size from esp
# . size
ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Input-size/disp32 # push *Input-size
# . read
68/push 0/imm32/read
# . write
68/push 0/imm32/write
89/copy 3/mod/direct 6/rm32/esi . . . 4/r32/esp . . # copy esp to esi
# slurp(infile, in)
# . . push args
56/push-esi
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
# . . call
e8/call slurp/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# compute-offsets(in, segments, labels)
# . . push args
52/push-edx
51/push-ecx
56/push-esi
# . . call
e8/call compute-offsets/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# compute-addresses(segments, labels)
# . . push args
52/push-edx
51/push-ecx
# . . call
e8/call compute-addresses/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# rewind-stream(in)
# . . push args
56/push-esi
# . . call
e8/call rewind-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# emit-output(in, out, segments, labels)
# . . push args
52/push-edx
51/push-ecx
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12)
56/push-esi
# . . call
e8/call emit-output/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp
# flush(out)
# . . push args
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12)
# . . call
e8/call flush/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
$subx-survey:end:
# . reclaim locals
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xec/imm32 # add to esp
03/add 0/mod/indirect 5/rm32/.disp32 . . 4/r32/esp Max-labels/disp32 # add *Max-labels to esp
03/add 0/mod/indirect 5/rm32/.disp32 . . 4/r32/esp Input-size/disp32 # add *Input-size to esp
# . restore registers
5e/pop-to-esi
5a/pop-to-edx
59/pop-to-ecx
# . epilogue
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
test-subx-survey-computes-addresses:
# input:
# == code 0x1
# Entry:
# ab x/imm32
# == data 0x1000
# x:
# 01
#
# trace contains (in any order):
# label x is at address 0x1079
# segment code starts at address 0x74
# segment code has size 5
# segment data starts at address 0x1079
#
# . prologue
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# setup
# . clear-stream(_test-input-stream)
# . . push args
68/push _test-input-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . clear-stream($_test-input-buffered-file->buffer)
# . . push args
68/push $_test-input-buffered-file->buffer/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . clear-stream(_test-output-stream)
# . . push args
68/push _test-output-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . clear-stream($_test-output-buffered-file->buffer)
# . . push args
68/push $_test-output-buffered-file->buffer/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# initialize input
# . write(_test-input-stream, "== code 0x1\n")
# . . push args
68/push "== code 0x1\n"/imm32
68/push _test-input-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . write(_test-input-stream, "Entry:\n")
# . . push args
68/push "Entry:\n"/imm32
68/push _test-input-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . write(_test-input-stream, "ab x/imm32\n")
# . . push args
68/push "ab x/imm32\n"/imm32
68/push _test-input-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . write(_test-input-stream, "== data 0x1000\n")
# . . push args
68/push "== data 0x1000\n"/imm32
68/push _test-input-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . write(_test-input-stream, "x:\n")
# . . push args
68/push "x:\n"/imm32
68/push _test-input-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . write(_test-input-stream, "01\n")
# . . push args
68/push "01\n"/imm32
68/push _test-input-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# subx-survey(_test-input-buffered-file, _test-output-buffered-file)
# . . push args
68/push _test-output-buffered-file/imm32
68/push _test-input-buffered-file/imm32
# . . call
e8/call subx-survey/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# check trace
#? # dump *Trace-stream {{{
#? # . write(2/stderr, "^")
#? # . . push args
#? 68/push "^"/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . write-stream(2/stderr, *Trace-stream)
#? # . . push args
#? ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write-stream/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . write(2/stderr, "$\n")
#? # . . push args
#? 68/push "$\n"/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # }}}
# . check-trace-contains("label 'x' is at address 0x00001079.", msg)
# . . push args
68/push "F - test-subx-survey-computes-addresses/0"/imm32
68/push "label 'x' is at address 0x00001079."/imm32
# . . call
e8/call check-trace-contains/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . check-trace-contains("segment 'code' starts at address 0x00000074.", msg)
# . . push args
68/push "F - test-subx-survey-computes-addresses/1"/imm32
68/push "segment 'code' starts at address 0x00000074."/imm32
# . . call
e8/call check-trace-contains/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . check-trace-contains("segment 'code' has size 0x00000005.", msg)
# . . push args
68/push "F - test-subx-survey-computes-addresses/2"/imm32
68/push "segment 'code' has size 0x00000005."/imm32
# . . call
e8/call check-trace-contains/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . check-trace-contains("segment 'data' starts at address 0x00001079.", msg)
# . . push args
68/push "F - test-subx-survey-computes-addresses/3"/imm32
68/push "segment 'data' starts at address 0x00001079."/imm32
# . . call
e8/call check-trace-contains/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . epilogue
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
# global scratch space for compute-offsets
== data
compute-offsets:file-offset: # int
0/imm32
compute-offsets:segment-offset: # int
0/imm32
compute-offsets:segment-tmp: # slice
0/imm32/start
0/imm32/end
== code
# write segments->file-offset,
# segments->size,
# labels->segment-name, and
# labels->segment-offset
compute-offsets: # in: (addr stream byte), segments: (addr stream {(handle array byte), segment-info}), labels: (addr stream {(handle array byte), label-info})
# skeleton:
# for lines in 'in'
# for words in line
# switch word
# case 1
# case 2
# ...
# default
#
# pseudocode:
# var curr-segment-name: (handle array byte)
# var file-offset = 0
# var segment-offset = 0
# var line: (stream byte 512)
# var sinfo: (addr segment-info)
# var linfo: (addr label-info)
# while true # line loop
# clear-stream(line)
# read-line(in, line)
# if (line->write == 0) break # end of file
# while true # word loop
# word-slice = next-word(line)
# if slice-empty?(word-slice) # end of line
# break
# else if slice-starts-with?(word-slice, "#") # comment
# break # end of line
# else if slice-equal?(word-slice, "==")
# if *curr-segment-name != 0
# sinfo = get-or-insert-handle(segments, curr-segment-name)
# sinfo->size = file-offset - sinfo->file-offset
# trace("segment '", curr-segment-name, "' has size ", sinfo->size)
# segment-tmp = next-word(line)
# if slice-empty?(segment-tmp)
# abort
# curr-segment-name = slice-to-string(segment-tmp)
# segment-tmp = next-word(line)
# if slice-empty?(segment-tmp)
# abort
# sinfo = get-or-insert-handle(segments, curr-segment-name)
# sinfo->starting-address = parse-hex-int-from-slice(segment-tmp)
# sinfo->file-offset = file-offset
# trace("segment '", curr-segment-name, "' is at file offset ", sinfo->file-offset)
# segment-offset = 0
# break (next line)
# else if is-label?(word-slice)
# strip trailing ':' from word-slice
# linfo: (addr label-info) = get-or-insert-slice(labels, word-slice)
# linfo->segment-name = curr-segment-name
# trace("label '", word-slice, "' is in segment '", curr-segment-name, "'.")
# linfo->segment-offset = segment-offset
# trace("label '", word-slice, "' is at segment offset ", segment-offset, ".")
# # labels occupy no space, so no need to increment offsets
# else
# width = compute-width-of-slice(word-slice)
# segment-offset += width
# file-offset += width
# sinfo = get-or-insert-handle(segments, curr-segment-name)
# sinfo->size = file-offset - sinfo->file-offset
#
# . prologue
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# . save registers
50/push-eax
51/push-ecx
52/push-edx
53/push-ebx
56/push-esi
57/push-edi
# var curr-segment-name/esi: (handle array byte)
68/push 0/imm32
68/push 0/imm32
89/copy 3/mod/direct 6/rm32/esi . . . 4/r32/esp . . # copy esp to esi
# file-offset = 0
c7 0/subop/copy 0/mod/indirect 5/rm32/.disp32 . . . compute-offsets:file-offset/disp32 0/imm32 # copy to *compute-offsets:file-offset
# segment-offset = 0
c7 0/subop/copy 0/mod/indirect 5/rm32/.disp32 . . . compute-offsets:segment-offset/disp32 0/imm32 # copy to *compute-offsets:segment-offset
# var line/ecx: (stream byte 512)
81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0x200/imm32 # subtract from esp
68/push 0x200/imm32/size
68/push 0/imm32/read
68/push 0/imm32/write
89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
# var word-slice/edx: (addr slice)
68/push 0/imm32
68/push 0/imm32
89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx
$compute-offsets:line-loop:
# clear-stream(line)
51/push-ecx
e8/call clear-stream/disp32
# . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# read-line(in, line)
51/push-ecx
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
e8/call read-line/disp32
# . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# if (line->write == 0) break
8b/copy 0/mod/indirect 1/rm32/ecx . . . 0/r32/eax . . # copy *ecx to eax
3d/compare-eax-and 0/imm32
0f 84/jump-if-= $compute-offsets:break-line-loop/disp32
#? # dump line {{{
#? # . write(2/stderr, "LL: ")
#? # . . push args
#? 68/push "LL: "/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # write-stream(2/stderr, line)
#? # . . push args
#? 51/push-ecx
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write-stream/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . write(2/stderr, "$\n")
#? # . . push args
#? 68/push "$\n"/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . rewind-stream(line)
#? # . . push args
#? 51/push-ecx
#? # . . call
#? e8/call rewind-stream/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
#? # }}}
$compute-offsets:word-loop:
# next-word(line, word-slice)
52/push-edx
51/push-ecx
e8/call next-word/disp32
# . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
$compute-offsets:case-empty:
# if slice-empty?(word-slice) break
# . eax = slice-empty?(word-slice)
52/push-edx
e8/call slice-empty?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . if (eax != false) break
3d/compare-eax-and 0/imm32/false
0f 85/jump-if-!= $compute-offsets:line-loop/disp32
$compute-offsets:case-comment:
# if slice-starts-with?(word-slice, "#") continue
68/push "#"/imm32
52/push-edx
e8/call slice-starts-with?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . if (eax != false) break
3d/compare-eax-and 0/imm32/false
0f 85/jump-if-!= $compute-offsets:line-loop/disp32
$compute-offsets:case-segment-header:
# if (!slice-equal?(word-slice, "==")) goto next case
# . eax = slice-equal?(word-slice, "==")
68/push "=="/imm32
52/push-edx
e8/call slice-equal?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . if (eax == false) goto next case
3d/compare-eax-and 0/imm32/false
0f 84/jump-if-= $compute-offsets:case-label/disp32
# if (*curr-segment-name == 0) goto construct-next-segment
81 7/subop/compare 0/mod/indirect 6/rm32/esi . . . . . 0/imm32 # compare *esi
74/jump-if-= $compute-offsets:construct-next-segment/disp8
# sinfo/edi = get-or-insert-handle(segments, curr-segment-name, row-size=16)
# . eax = get-or-insert-handle(segments, curr-segment-name, row-size=16)
# . . push args
68/push 0x14/imm32/row-size
ff 6/subop/push 1/mod/*+disp8 6/rm32/esi . . . . 4/disp8 . # push *(esi+4)
ff 6/subop/push 0/mod/indirect 6/rm32/esi . . . . . . # push *esi
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12)
# . . call
e8/call get-or-insert-handle/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp
# . edi = eax
89/copy 3/mod/direct 7/rm32/edi . . . 0/r32/eax . . # copy eax to edi
# sinfo->size = file-offset - sinfo->file-offset
# . save ecx
51/push-ecx
# . ebx = *file-offset
8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/ebx compute-offsets:file-offset/disp32 # copy *file-offset to ebx
# . ecx = sinfo->file-offset
8b/copy 1/mod/*+disp8 7/rm32/edi . . . 1/r32/ecx 4/disp8 . # copy *(edi+4) to ecx
# . ebx -= ecx
29/subtract 3/mod/direct 3/rm32/ebx . . . 1/r32/ecx . . # subtract ecx from ebx
# . sinfo->size = ebx
89/copy 1/mod/*+disp8 7/rm32/edi . . . 3/r32/ebx 8/disp8 . # copy ebx to *(edi+8)
# . restore ecx
59/pop-to-ecx
# trace-sssns("segment '", curr-segment-name, "' has size ", sinfo->size, ".")
# . eax = lookup(curr-segment-name)
# . . push args
ff 6/subop/push 1/mod/*+disp8 6/rm32/esi . . . . 4/disp8 . # push *(esi+4)
ff 6/subop/push 0/mod/indirect 6/rm32/esi . . . . . . # push *esi
# . . call
e8/call lookup/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . trace-sssns("segment '", eax, "' has size ", sinfo->size, ".")
# . . push args
68/push "."/imm32
53/push-ebx
68/push "' has size "/imm32
50/push-eax
68/push "segment '"/imm32
# . . call
e8/call trace-sssns/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x14/imm32 # add to esp
$compute-offsets:construct-next-segment:
# next-word(line, segment-tmp)
68/push compute-offsets:segment-tmp/imm32
51/push-ecx
e8/call next-word/disp32
# . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# if slice-empty?(segment-tmp) abort
# . eax = slice-empty?(segment-tmp)
# . . push args
68/push compute-offsets:segment-tmp/imm32
# . . call
e8/call slice-empty?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . if (eax != false) abort
3d/compare-eax-and 0/imm32/false
0f 85/jump-if-!= $compute-offsets:abort/disp32
$compute-offsets:update-curr-segment-name:
# slice-to-string(Heap, segment-tmp, curr-segment-name)
# . . push args
56/push-esi
68/push compute-offsets:segment-tmp/imm32
68/push Heap/imm32
# . . call
e8/call slice-to-string/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# next-word(line, segment-tmp)
68/push compute-offsets:segment-tmp/imm32
51/push-ecx
e8/call next-word/disp32
# . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# if slice-empty?(segment-tmp) abort
# . eax = slice-empty?(segment-tmp)
# . . push args
68/push compute-offsets:segment-tmp/imm32
# . . call
e8/call slice-empty?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . if (eax != false) abort
3d/compare-eax-and 0/imm32/false
0f 85/jump-if-!= $compute-offsets:abort/disp32
# sinfo/edi = get-or-insert-handle(segments, curr-segment-name, row-size=16)
# . eax = get-or-insert-handle(segments, curr-segment-name, row-size=16)
# . . push args
68/push 0x14/imm32/row-size
ff 6/subop/push 1/mod/*+disp8 6/rm32/esi . . . . 4/disp8 . # push *(esi+4)
ff 6/subop/push 0/mod/indirect 6/rm32/esi . . . . . . # push *esi
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12)
# . . call
e8/call get-or-insert-handle/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp
# . edi = eax
89/copy 3/mod/direct 7/rm32/edi . . . 0/r32/eax . . # copy eax to edi
# sinfo->address = parse-hex-int-from-slice(segment-tmp)
# . eax = parse-hex-int-from-slice(segment-tmp)
# . . push args
68/push compute-offsets:segment-tmp/imm32
# . . call
e8/call parse-hex-int-from-slice/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . sinfo->address = eax
89/copy 0/mod/indirect 7/rm32/edi . . . 0/r32/eax . . # copy eax to *edi
# sinfo->file-offset = *file-offset
8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/eax compute-offsets:file-offset/disp32 # copy *file-offset to eax
89/copy 1/mod/*+disp8 7/rm32/edi . . . 0/r32/eax 4/disp8 . # copy eax to *(edi+4)
# trace-sssns("segment '", curr-segment-name, "' is at file offset ", sinfo->file-offset, "")
# . eax = lookup(curr-segment-name)
# . . push args
ff 6/subop/push 1/mod/*+disp8 6/rm32/esi . . . . 4/disp8 . # push *(esi+4)
ff 6/subop/push 0/mod/indirect 6/rm32/esi . . . . . . # push *esi
# . . call
e8/call lookup/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . trace-sssns("segment '", eax, "' is at file offset ", file-offset, ".")
# . . push args
68/push "."/imm32
ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . compute-offsets:file-offset/disp32 # push *file-offset
68/push "' is at file offset "/imm32
50/push-eax
68/push "segment '"/imm32
# . . call
e8/call trace-sssns/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x14/imm32 # add to esp
# segment-offset = 0
c7 0/subop/copy 0/mod/indirect 5/rm32/.disp32 . . . compute-offsets:segment-offset/disp32 0/imm32 # copy to *segment-offset
# break
e9/jump $compute-offsets:line-loop/disp32
$compute-offsets:case-label:
# if (!is-label?(word-slice)) goto next case
# . eax = is-label?(word-slice)
# . . push args
52/push-edx
# . . call
e8/call is-label?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . if (eax == false) goto next case
3d/compare-eax-and 0/imm32/false
0f 84/jump-if-= $compute-offsets:case-default/disp32
# strip trailing ':' from word-slice
ff 1/subop/decrement 1/mod/*+disp8 2/rm32/edx . . . . 4/disp8 . # decrement *(edx+4)
# linfo/edi = get-or-insert-slice(labels, word-slice, row-size=24)
# . eax = get-or-insert-slice(labels, word-slice, row-size=24)
# . . push args
68/push Heap/imm32
68/push 0x18/imm32/row-size
52/push-edx
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16)
# . . call
e8/call get-or-insert-slice/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp
# . edi = eax
89/copy 3/mod/direct 7/rm32/edi . . . 0/r32/eax . . # copy eax to edi
$compute-offsets:save-label-offset:
# linfo->segment-name = curr-segment-name
8b/copy 0/mod/indirect 6/rm32/esi . . . 0/r32/eax . . # copy *esi to eax
89/copy 0/mod/indirect 7/rm32/edi . . . 0/r32/eax . . # copy eax to *edi
8b/copy 1/mod/*+disp8 6/rm32/esi . . . 0/r32/eax 4/disp8 . # copy *(esi+4) to eax
89/copy 1/mod/*+disp8 7/rm32/edi . . . 0/r32/eax 4/disp8 . # copy eax to *(edi+4)
# trace-slsss("label '" word-slice "' is in segment '" current-segment-name "'.")
# . eax = lookup(curr-segment-name)
# . . push args
ff 6/subop/push 1/mod/*+disp8 6/rm32/esi . . . . 4/disp8 . # push *(esi+4)
ff 6/subop/push 0/mod/indirect 6/rm32/esi . . . . . . # push *esi
# . . call
e8/call lookup/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . trace-slsss("label '" word-slice "' is in segment '" eax "'.")
# . . push args
68/push "'."/imm32
50/push-eax
68/push "' is in segment '"/imm32
52/push-edx
68/push "label '"/imm32
# . . call
e8/call trace-slsss/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x14/imm32 # add to esp
# linfo->segment-offset = segment-offset
# . ebx = segment-offset
8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/ebx compute-offsets:segment-offset/disp32 # copy *segment-offset to ebx
# . linfo->segment-offset = ebx
89/copy 1/mod/*+disp8 7/rm32/edi . . . 3/r32/ebx 8/disp8 . # copy ebx to *(edi+8)
# trace-slsns("label '" word-slice "' is at segment offset " *segment-offset/eax ".")
# . . push args
68/push "."/imm32
53/push-ebx
68/push "' is at segment offset "/imm32
52/push-edx
68/push "label '"/imm32
# . . call
e8/call trace-slsns/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x14/imm32 # add to esp
# continue
e9/jump $compute-offsets:word-loop/disp32
$compute-offsets:case-default:
# width/eax = compute-width-of-slice(word-slice)
# . . push args
52/push-edx
# . . call
e8/call compute-width-of-slice/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# segment-offset += width
01/add 0/mod/indirect 5/rm32/.disp32 . . 0/r32/eax compute-offsets:segment-offset/disp32 # add eax to *segment-offset
# file-offset += width
01/add 0/mod/indirect 5/rm32/.disp32 . . 0/r32/eax compute-offsets:file-offset/disp32 # add eax to *file-offset
#? # dump segment-offset {{{
#? # . write(2/stderr, "segment-offset: ")
#? # . . push args
#? 68/push "segment-offset: "/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . clear-stream($Stderr->buffer)
#? # . . push args
#? 68/push $Stderr->buffer/imm32
#? # . . call
#? e8/call clear-stream/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
#? # . write-int32-hex-buffered(Stderr, segment-offset)
#? # . . push args
#? 52/push-edx
#? ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . compute-offsets:segment-offset/disp32 # push *segment-offset
#? 68/push Stderr/imm32
#? # . . call
#? e8/call write-int32-hex-buffered/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . flush(Stderr)
#? # . . push args
#? 68/push Stderr/imm32
#? # . . call
#? e8/call flush/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
#? # . write(2/stderr, "\n")
#? # . . push args
#? 68/push Newline/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # }}}
e9/jump $compute-offsets:word-loop/disp32
$compute-offsets:break-line-loop:
# sinfo/edi = get-or-insert-handle(segments, curr-segment-name, row-size=16)
# . eax = get-or-insert-handle(segments, curr-segment-name, row-size=16)
# . . push args
68/push 0x14/imm32/row-size
ff 6/subop/push 1/mod/*+disp8 6/rm32/esi . . . . 4/disp8 . # push *(esi+4)
ff 6/subop/push 0/mod/indirect 6/rm32/esi . . . . . . # push *esi
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12)
# . . call
e8/call get-or-insert-handle/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp
# . edi = eax
89/copy 3/mod/direct 7/rm32/edi . . . 0/r32/eax . . # copy eax to edi
# sinfo->size = file-offset - sinfo->file-offset
# . save ecx
51/push-ecx
# . ebx = *file-offset
8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/ebx compute-offsets:file-offset/disp32 # copy *file-offset to ebx
# . ecx = sinfo->file-offset
8b/copy 1/mod/*+disp8 7/rm32/edi . . . 1/r32/ecx 4/disp8 . # copy *(edi+4) to ecx
# . ebx -= ecx
29/subtract 3/mod/direct 3/rm32/ebx . . . 1/r32/ecx . . # subtract ecx from ebx
# . sinfo->size = ebx
89/copy 1/mod/*+disp8 7/rm32/edi . . . 3/r32/ebx 8/disp8 . # copy ebx to *(edi+8)
# . restore ecx
59/pop-to-ecx
# trace-sssns("segment '", curr-segment-name, "' has size ", sinfo->size, ".")
# . eax = lookup(curr-segment-name)
# . . push args
ff 6/subop/push 1/mod/*+disp8 6/rm32/esi . . . . 4/disp8 . # push *(esi+4)
ff 6/subop/push 0/mod/indirect 6/rm32/esi . . . . . . # push *esi
# . . call
e8/call lookup/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . trace-sssns("segment '", eax, "' has size ", ebx, ".")
# . . push args
68/push "."/imm32
53/push-ebx
68/push "' has size "/imm32
50/push-eax
68/push "segment '"/imm32
# . . call
e8/call trace-sssns/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x14/imm32 # add to esp
$compute-offsets:end:
# . reclaim locals
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x21c/imm32 # add to esp
# . restore registers
5f/pop-to-edi
5e/pop-to-esi
5b/pop-to-ebx
5a/pop-to-edx
59/pop-to-ecx
58/pop-to-eax
# . epilogue
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
$compute-offsets:abort:
# . _write(2/stderr, error)
# . . push args
68/push "'==' must be followed by segment name and segment-start\n"/imm32
68/push 2/imm32/stderr
# . . call
e8/call _write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . syscall(exit, 1)
bb/copy-to-ebx 1/imm32
e8/call syscall_exit/disp32
# never gets here
test-compute-offsets:
# input:
# == code 0x1
# ab x/imm32 # skip comment
# == data 0x1000
# 00
# x:
# 34
#
# trace contains (in any order):
# segment 'code' is at file offset 0x0.
# segment 'code' has size 0x5.
# segment 'data' is at file offset 0x5.
# segment 'data' has size 0x2.
# label 'x' is in segment 'data'.
# label 'x' is at segment offset 0x1.
#
# . prologue
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# setup
# . clear-stream(_test-input-stream)
# . . push args
68/push _test-input-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# var segments/ecx: (stream byte 2*20)
81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0x28/imm32 # subtract from esp
68/push 0x28/imm32/size
68/push 0/imm32/read
68/push 0/imm32/write
89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
# var labels/edx: (stream byte 2*24)
81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0x30/imm32 # subtract from esp
68/push 0x30/imm32/size
68/push 0/imm32/read
68/push 0/imm32/write
89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx
# initialize input
# . write(_test-input-stream, "== code 0x1\n")
# . . push args
68/push "== code 0x1\n"/imm32
68/push _test-input-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . write(_test-input-stream, "ab x/imm32 # skip comment\n")
# . . push args
68/push "ab x/imm32 # skip comment\n"/imm32
68/push _test-input-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . write(_test-input-stream, "== data 0x1000\n")
# . . push args
68/push "== data 0x1000\n"/imm32
68/push _test-input-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . write(_test-input-stream, "00\n")
# . . push args
68/push "00\n"/imm32
68/push _test-input-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . write(_test-input-stream, "x:\n")
# . . push args
68/push "x:\n"/imm32
68/push _test-input-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . write(_test-input-stream, "34\n")
# . . push args
68/push "34\n"/imm32
68/push _test-input-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# compute-offsets(_test-input-stream, segments, labels)
# . . push args
52/push-edx
51/push-ecx
68/push _test-input-stream/imm32
# . . call
e8/call compute-offsets/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
#? # dump *Trace-stream {{{
#? # . write(2/stderr, "^")
#? # . . push args
#? 68/push "^"/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . write-stream(2/stderr, *Trace-stream)
#? # . . push args
#? ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write-stream/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . write(2/stderr, "$\n")
#? # . . push args
#? 68/push "$\n"/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # }}}
# check trace
# . check-trace-contains("segment 'code' is at file offset 0x00000000.", msg)
# . . push args
68/push "F - test-compute-offsets/0"/imm32
68/push "segment 'code' is at file offset 0x00000000."/imm32
# . . call
e8/call check-trace-contains/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . check-trace-contains("segment 'code' has size 0x00000005", msg)
# . . push args
68/push "F - test-compute-offsets/1"/imm32
68/push "segment 'code' has size 0x00000005."/imm32
# . . call
e8/call check-trace-contains/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . check-trace-contains("segment 'data' is at file offset 0x00000005.", msg)
# . . push args
68/push "F - test-compute-offsets/2"/imm32
68/push "segment 'data' is at file offset 0x00000005."/imm32
# . . call
e8/call check-trace-contains/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . check-trace-contains("segment 'data' has size 0x00000002.", msg)
# . . push args
68/push "F - test-compute-offsets/3"/imm32
68/push "segment 'data' has size 0x00000002."/imm32
# . . call
e8/call check-trace-contains/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . check-trace-contains("label 'x' is in segment 'data'.", msg)
# . . push args
68/push "F - test-compute-offsets/4"/imm32
68/push "label 'x' is in segment 'data'."/imm32
# . . call
e8/call check-trace-contains/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . check-trace-contains("label 'x' is at segment offset 0x00000001.", msg)
# . . push args
68/push "F - test-compute-offsets/5"/imm32
68/push "label 'x' is at segment offset 0x00000001."/imm32
# . . call
e8/call check-trace-contains/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . check-ints-equal(labels->write, 0x18, msg)
# . . push args
68/push "F - test-compute-offsets-maintains-labels-write-index"/imm32
68/push 0x18/imm32/1-entry
ff 6/subop/push 0/mod/indirect 2/rm32/edx . . . . . . # push *edx
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . epilogue
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
# write segments->file-offset,
# segments->address, and
# labels->address
compute-addresses: # segments: (addr stream {(handle array byte), segment-info}), labels: (addr stream {(handle array byte), label-info})
# pseudocode:
# var srow: (addr segment-row) = segments->data
# var max: (addr byte) = &segments->data[segments->write]
# var num-segments: int = segments->write / 20
# var starting-offset: int = 0x34 + (num-segments * 0x20)
# while true
# if (srow >= max) break
# srow->file-offset += starting-offset
# srow->address &= 0xfffff000 # clear last 12 bits for p_align
# srow->address += (srow->file-offset & 0x00000fff)
# trace-sssns("segment " srow->key " starts at address " srow->address)
# srow += 20 # row-size
# var lrow: (addr label-row) = labels->data
# max = &labels->data[labels->write]
# while true
# if (lrow >= max) break
# var seg-name: (addr array byte) = lookup(lrow->segment-name)
# var label-seg: (addr segment-info) = get(segments, seg-name)
# lrow->address = label-seg->address + lrow->segment-offset
# trace-sssns("label " lrow->key " is at address " lrow->address)
# lrow += 24 # row-size
#
# . prologue
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# . save registers
50/push-eax
51/push-ecx
52/push-edx
53/push-ebx
56/push-esi
57/push-edi
# esi = segments
8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi
# var num-segments/edi: int = segments->write / 20 (row-size)
# . eax = segments->write
8b/copy 0/mod/indirect 6/rm32/esi . . . 0/r32/eax . . # copy *esi to eax
# . edx = 0
ba/copy-to-edx 0/imm32
# . ecx = 20 (row-size)
b9/copy-to-ecx 0x14/imm32/row-size
# . eax /= ecx (clobbering edx)
f7 7/subop/divide 3/mod/direct 1/rm32/ecx . . . . . . # divide eax by ecx
# . edi = eax
89/copy 3/mod/direct 7/rm32/edi . . . 0/r32/eax . . # copy eax to edi
# var starting-offset/edi: int = 0x34 + (num-segments * 0x20) # make room for ELF headers
c1/shift 4/subop/left 3/mod/direct 7/rm32/edi . . . . . 5/imm8 # shift edi left by 5 bits
81 0/subop/add 3/mod/direct 7/rm32/edi . . . . . 0x34/imm32 # add to edi
# var max/ecx: (addr byte) = &segments->data[segments->write]
8b/copy 0/mod/indirect 6/rm32/esi . . . 1/r32/ecx . . # copy *esi to ecx
8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/esi 1/index/ecx . 1/r32/ecx 0xc/disp8 . # copy esi+ecx+12 to ecx
# var srow/esi: (addr segment-row) = segments->data
8d/copy-address 1/mod/*+disp8 6/rm32/esi . . . 6/r32/esi 0xc/disp8 . # copy esi+12 to esi
$compute-addresses:segment-loop:
# if (srow >= max) break
39/compare 3/mod/direct 6/rm32/esi . . . 1/r32/ecx . . # compare esi with ecx
73/jump-if-addr>= $compute-addresses:segment-break/disp8
# srow->file-offset += starting-offset
01/add 1/mod/*+disp8 6/rm32/esi . . . 7/r32/edi 0xc/disp8 . # add edi to *(esi+12)
# clear last 12 bits of srow->address for p_align=0x1000
# . edx = srow->address
8b/copy 1/mod/*+disp8 6/rm32/esi . . . 2/r32/edx 8/disp8 . # copy *(esi+8) to edx
# . edx &= 0xfffff000
81 4/subop/and 3/mod/direct 2/rm32/edx . . . . . 0xfffff000/imm32 # bitwise and of edx
# update last 12 bits from srow->file-offset
# . ebx = srow->file-offset
8b/copy 1/mod/*+disp8 6/rm32/esi . . . 3/r32/ebx 0xc/disp8 . # copy *(esi+12) to ebx
# . ebx &= 0xfff
81 4/subop/and 3/mod/direct 3/rm32/ebx . . . . . 0x00000fff/imm32 # bitwise and of ebx
# . srow->address = edx | ebx
09/or 3/mod/direct 2/rm32/edx . . . 3/r32/ebx . . # edx = bitwise OR with ebx
89/copy 1/mod/*+disp8 6/rm32/esi . . . 2/r32/edx 8/disp8 . # copy edx to *(esi+8)
# trace-sssns("segment " srow " starts at address " srow->address ".")
# . eax = lookup(*srow)
# . . push args
ff 6/subop/push 1/mod/*+disp8 6/rm32/esi . . . . 4/disp8 . # push *(esi+4)
ff 6/subop/push 0/mod/indirect 6/rm32/esi . . . . . . # push *esi
# . . call
e8/call lookup/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . trace-sssns("segment " eax " starts at address " srow->address ".")
# . . push args
68/push "."/imm32
52/push-edx
68/push "' starts at address "/imm32
50/push-eax
68/push "segment '"/imm32
# . . call
e8/call trace-sssns/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x14/imm32 # add to esp
# srow += 20 # size of row
81 0/subop/add 3/mod/direct 6/rm32/esi . . . . . 0x14/imm32 # add to esi
eb/jump $compute-addresses:segment-loop/disp8
$compute-addresses:segment-break:
#? # dump *Trace-stream {{{
#? # . write(2/stderr, "^")
#? # . . push args
#? 68/push "^"/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . write-stream(2/stderr, *Trace-stream)
#? # . . push args
#? ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write-stream/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . write(2/stderr, "$\n")
#? # . . push args
#? 68/push "$\n"/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # }}}
# esi = labels
8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 0xc/disp8 . # copy *(ebp+12) to esi
# var max/ecx: (addr byte) = &labels->data[labels->write]
8b/copy 0/mod/indirect 6/rm32/esi . . . 1/r32/ecx . . # copy *esi to ecx
8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/esi 1/index/ecx . 1/r32/ecx 0xc/disp8 . # copy esi+ecx+12 to ecx
# var lrow/esi: (addr label-row) = labels->data
8d/copy-address 1/mod/*+disp8 6/rm32/esi . . . 6/r32/esi 0xc/disp8 . # copy esi+12 to esi
$compute-addresses:label-loop:
# if (lrow >= max) break
39/compare 3/mod/direct 6/rm32/esi . . . 1/r32/ecx . . # compare esi with ecx
0f 83/jump-if-addr>= $compute-addresses:end/disp32
#? # dump lrow->key {{{
#? # . write(2/stderr, "label: ")
#? # . . push args
#? 68/push "label: "/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . write(2/stderr, lrow->key)
#? # . . push args
#? ff 6/subop/push 0/mod/indirect 6/rm32/esi . . . . . . # push *esi
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . write(2/stderr, "$\n")
#? # . . push args
#? 68/push "$\n"/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # }}}
# var seg-name/edx: (addr array byte) = lookup(lrow->segment-name)
# . eax = lookup(lrow->segment-name)
# . . push args
ff 6/subop/push 1/mod/*+disp8 6/rm32/esi . . . . 0xc/disp8 . # push *(esi+12)
ff 6/subop/push 1/mod/*+disp8 6/rm32/esi . . . . 8/disp8 . # push *(esi+8)
# . . call
e8/call lookup/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . edx = eax
89/copy 3/mod/direct 2/rm32/edx . . . 0/r32/eax . . # copy eax to edx
#? # dump seg-name {{{
#? # . write(2/stderr, "compute-addresses: seg-name: ")
#? # . . push args
#? 68/push "seg-name: "/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . write(2/stderr, seg-name)
#? # . . push args
#? 52/push-edx
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . write(2/stderr, "$\n")
#? # . . push args
#? 68/push "$\n"/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # }}}
# var label-seg/edx: (addr segment-info) = get(segments, seg-name, row-size=20, "segment table")
# . eax = get(segments, seg-name, row-size=20)
# . . push args
68/push "segment table"/imm32
68/push 0x14/imm32/row-size
52/push-edx
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
# . . call
e8/call get/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp
# . edx = eax
89/copy 3/mod/direct 2/rm32/edx . . . 0/r32/eax . . # copy eax to edx
# ebx = label-seg->address
8b/copy 0/mod/indirect 2/rm32/edx . . . 3/r32/ebx . . # copy *edx to ebx
# ebx += lrow->segment-offset
03/add 1/mod/*+disp8 6/rm32/esi . . . 3/r32/ebx 0x10/disp8 . # add *(esi+16) to ebx
# lrow->address = ebx
89/copy 1/mod/*+disp8 6/rm32/esi . . . 3/r32/ebx 0x14/disp8 . # copy ebx to *(esi+20)
# trace-sssns("label " lrow->key " is at address " lrow->address ".")
# . eax = lookup(lrow->key)
# . . push args
ff 6/subop/push 1/mod/*+disp8 6/rm32/esi . . . . 4/disp8 . # push *(esi+4)
ff 6/subop/push 0/mod/indirect 6/rm32/esi . . . . . . # push *esi
# . . call
e8/call lookup/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . trace-sssns("label " eax " is at address " lrow->address ".")
# . . push args
68/push "."/imm32
53/push-ebx
68/push "' is at address "/imm32
50/push-eax
68/push "label '"/imm32
# . . call
e8/call trace-sssns/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x14/imm32 # add to esp
# lrow += 24 # size of row
81 0/subop/add 3/mod/direct 6/rm32/esi . . . . . 0x18/imm32 # add to esi
e9/jump $compute-addresses:label-loop/disp32
$compute-addresses:end:
# . restore registers
5f/pop-to-edi
5e/pop-to-esi
5b/pop-to-ebx
5a/pop-to-edx
59/pop-to-ecx
58/pop-to-eax
# . epilogue
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
test-compute-addresses:
# input:
# segments:
# - 'a': {0x1000, 0, 5}
# - 'b': {0x2018, 5, 1}
# - 'c': {0x5444, 6, 12}
# labels:
# - 'l1': {'a', 3, 0}
# - 'l2': {'b', 0, 0}
#
# trace contains in any order (comments in parens):
# segment 'a' starts at address 0x00001094. (0x34 + 0x20 for each segment)
# segment 'b' starts at address 0x00002099. (0x018 discarded)
# segment 'c' starts at address 0x0000509a. (0x444 discarded)
# label 'l1' is at address 0x00001097. (0x1094 + segment-offset 3)
# label 'l2' is at address 0x00002099. (0x2099 + segment-offset 0)
#
# . prologue
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# setup
# . var segments/ecx: (stream byte 10*20)
81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0xc8/imm32 # subtract from esp
68/push 0xc8/imm32/size
68/push 0/imm32/read
68/push 0/imm32/write
89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
# . var labels/edx: (stream byte 8*24)
81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0xc0/imm32 # subtract from esp
68/push 0xc0/imm32/size
68/push 0/imm32/read
68/push 0/imm32/write
89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx
# . var h/ebx: (handle array byte)
68/push 0/imm32
68/push 0/imm32
89/copy 3/mod/direct 3/rm32/ebx . . . 4/r32/esp . . # copy esp to ebx
# . h = copy-array(Heap, "a")
# . . push args
53/push-ebx
68/push "a"/imm32
68/push Heap/imm32
# . . call
e8/call copy-array/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . stream-add5(segments, "a", 0x1000, 0, 5)
# . . push args
68/push 5/imm32/segment-size
68/push 0/imm32/file-offset
68/push 0x1000/imm32/start-address
ff 6/subop/push 1/mod/*+disp8 3/rm32/ebx . . . . 4/disp8 . # push *(ebx+4)
ff 6/subop/push 0/mod/indirect 3/rm32/ebx . . . . . . # push *ebx
51/push-ecx
# . . call
e8/call stream-add5/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x18/imm32 # add to esp
# . h = copy-array(Heap, "b")
# . . push args
53/push-ebx
68/push "b"/imm32
68/push Heap/imm32
# . . call
e8/call copy-array/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . stream-add5(segments, "b", 0x2018, 5, 1)
# . . push args
68/push 1/imm32/segment-size
68/push 5/imm32/file-offset
68/push 0x2018/imm32/start-address
ff 6/subop/push 1/mod/*+disp8 3/rm32/ebx . . . . 4/disp8 . # push *(ebx+4)
ff 6/subop/push 0/mod/indirect 3/rm32/ebx . . . . . . # push *ebx
51/push-ecx
# . . call
e8/call stream-add5/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x18/imm32 # add to esp
# . h = copy-array(Heap, "c")
# . . push args
53/push-ebx
68/push "c"/imm32
68/push Heap/imm32
# . . call
e8/call copy-array/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . stream-add5(segments, "c", 0x5444, 6, 12)
68/push 0xc/imm32/segment-size
68/push 6/imm32/file-offset
68/push 0x5444/imm32/start-address
ff 6/subop/push 1/mod/*+disp8 3/rm32/ebx . . . . 4/disp8 . # push *(ebx+4)
ff 6/subop/push 0/mod/indirect 3/rm32/ebx . . . . . . # push *ebx
51/push-ecx
# . . call
e8/call stream-add5/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x18/imm32 # add to esp
# . stream-add6(labels, "l1", "a", 3, 0)
# . . push args
68/push 0/imm32/label-address
68/push 3/imm32/segment-offset
# . . push "a"
53/push-ebx
68/push "a"/imm32
68/push Heap/imm32
e8/call copy-array/disp32
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
ff 6/subop/push 1/mod/*+disp8 3/rm32/ebx . . . . 4/disp8 . # push *(ebx+4)
ff 6/subop/push 0/mod/indirect 3/rm32/ebx . . . . . . # push *ebx
# . . push "l1"
53/push-ebx
68/push "l1"/imm32
68/push Heap/imm32
e8/call copy-array/disp32
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
ff 6/subop/push 1/mod/*+disp8 3/rm32/ebx . . . . 4/disp8 . # push *(ebx+4)
ff 6/subop/push 0/mod/indirect 3/rm32/ebx . . . . . . # push *ebx
# . . push labels
52/push-edx
# . . call
e8/call stream-add6/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x1c/imm32 # add to esp
# . stream-add6(labels, "l2", "b", 0, 0)
# . . push args
68/push 0/imm32/label-address
68/push 0/imm32/segment-offset
# . . push "b"
53/push-ebx
68/push "b"/imm32
68/push Heap/imm32
e8/call copy-array/disp32
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
ff 6/subop/push 1/mod/*+disp8 3/rm32/ebx . . . . 4/disp8 . # push *(ebx+4)
ff 6/subop/push 0/mod/indirect 3/rm32/ebx . . . . . . # push *ebx
# . . push "l2"
53/push-ebx
68/push "l2"/imm32
68/push Heap/imm32
e8/call copy-array/disp32
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
ff 6/subop/push 1/mod/*+disp8 3/rm32/ebx . . . . 4/disp8 . # push *(ebx+4)
ff 6/subop/push 0/mod/indirect 3/rm32/ebx . . . . . . # push *ebx
# . . push labels
52/push-edx
# . . call
e8/call stream-add6/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x1c/imm32 # add to esp
# component under test
# . compute-addresses(segments, labels)
# . . push args
52/push-edx
51/push-ecx
# . . call
e8/call compute-addresses/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# checks
#? # dump *Trace-stream {{{
#? # . write(2/stderr, "^")
#? # . . push args
#? 68/push "^"/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . write-stream(2/stderr, *Trace-stream)
#? # . . push args
#? ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write-stream/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . write(2/stderr, "$\n")
#? # . . push args
#? 68/push "$\n"/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # }}}
# . check-trace-contains("segment 'a' starts at address 0x00001094.", msg)
# . . push args
68/push "F - test-compute-addresses/0"/imm32
68/push "segment 'a' starts at address 0x00001094."/imm32
# . . call
e8/call check-trace-contains/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . check-trace-contains("segment 'b' starts at address 0x00002099.", msg)
# . . push args
68/push "F - test-compute-addresses/1"/imm32
68/push "segment 'b' starts at address 0x00002099."/imm32
# . . call
e8/call check-trace-contains/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . check-trace-contains("segment 'c' starts at address 0x0000509a.", msg)
# . . push args
68/push "F - test-compute-addresses/2"/imm32
68/push "segment 'c' starts at address 0x0000509a."/imm32
# . . call
e8/call check-trace-contains/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . check-trace-contains("label 'l1' is at address 0x00001097.", msg)
# . . push args
68/push "F - test-compute-addresses/3"/imm32
68/push "label 'l1' is at address 0x00001097."/imm32
# . . call
e8/call check-trace-contains/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . check-trace-contains("label 'l2' is at address 0x00002099.", msg)
# . . push args
68/push "F - test-compute-addresses/4"/imm32
68/push "label 'l2' is at address 0x00002099."/imm32
# . . call
e8/call check-trace-contains/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . check-ints-equal(labels->write, 0x30, msg)
# . . push args
68/push "F - test-compute-addresses/maintains-labels-write-index"/imm32
68/push 0x30/imm32/2-entries
ff 6/subop/push 0/mod/indirect 2/rm32/edx . . . . . . # push *edx
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . epilogue
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
test-compute-addresses-large-segments:
# input:
# segments:
# - 'a': {0x1000, 0, 0x5604}
# - 'b': {0x2018, 0x5604, 1}
# labels:
# - 'l1': {'a', 3, 0}
#
# trace contains in any order (comments in parens):
# segment 'a' starts at address 0x00001074. (0x34 + 0x20 for each segment)
# segment 'b' starts at address 0x00002678. (0x018 discarded; last 3 nibbles from 0x1074 + 0x5604)
# label 'l1' is at address 0x00001077. (0x1074 + segment-offset 3)
#
# . prologue
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# setup
# . var segments/ecx: (stream byte 10*20)
81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0xc8/imm32 # subtract from esp
68/push 0xc8/imm32/size
68/push 0/imm32/read
68/push 0/imm32/write
89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
# . var labels/edx: (stream byte 8*24)
81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0xc0/imm32 # subtract from esp
68/push 0xc0/imm32/size
68/push 0/imm32/read
68/push 0/imm32/write
89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx
# . var h/ebx: (handle array byte)
68/push 0/imm32
68/push 0/imm32
89/copy 3/mod/direct 3/rm32/ebx . . . 4/r32/esp . . # copy esp to ebx
# . h = copy-array(Heap, "a")
# . . push args
53/push-ebx
68/push "a"/imm32
68/push Heap/imm32
# . . call
e8/call copy-array/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . stream-add5(segments, "a", 0x1000, 0, 0x5604)
68/push 0x5604/imm32/segment-size
68/push 0/imm32/file-offset
68/push 0x1000/imm32/start-address
ff 6/subop/push 1/mod/*+disp8 3/rm32/ebx . . . . 4/disp8 . # push *(ebx+4)
ff 6/subop/push 0/mod/indirect 3/rm32/ebx . . . . . . # push *ebx
51/push-ecx
# . . call
e8/call stream-add5/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x18/imm32 # add to esp
# . h = copy-array(Heap, "b")
# . . push args
53/push-ebx
68/push "b"/imm32
68/push Heap/imm32
# . . call
e8/call copy-array/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . stream-add5(segments, "b", 0x2018, 0x5604, 1)
68/push 1/imm32/segment-size
68/push 0x5604/imm32/file-offset
68/push 0x2018/imm32/start-address
ff 6/subop/push 1/mod/*+disp8 3/rm32/ebx . . . . 4/disp8 . # push *(ebx+4)
ff 6/subop/push 0/mod/indirect 3/rm32/ebx . . . . . . # push *ebx
51/push-ecx
# . . call
e8/call stream-add5/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x18/imm32 # add to esp
# . stream-add6(labels, "l1", "a", 3, 0)
68/push 0/imm32/label-address
68/push 3/imm32/segment-offset
# . . push "a"
53/push-ebx
68/push "a"/imm32
68/push Heap/imm32
e8/call copy-array/disp32
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
ff 6/subop/push 1/mod/*+disp8 3/rm32/ebx . . . . 4/disp8 . # push *(ebx+4)
ff 6/subop/push 0/mod/indirect 3/rm32/ebx . . . . . . # push *ebx
# . . push "l1"
53/push-ebx
68/push "l1"/imm32
68/push Heap/imm32
e8/call copy-array/disp32
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
ff 6/subop/push 1/mod/*+disp8 3/rm32/ebx . . . . 4/disp8 . # push *(ebx+4)
ff 6/subop/push 0/mod/indirect 3/rm32/ebx . . . . . . # push *ebx
# . . push labels
52/push-edx
# . . call
e8/call stream-add6/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x1c/imm32 # add to esp
# component under test
# . compute-addresses(segments, labels)
# . . push args
52/push-edx
51/push-ecx
# . . call
e8/call compute-addresses/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# checks
#? # dump *Trace-stream {{{
#? # . write(2/stderr, "^")
#? # . . push args
#? 68/push "^"/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . write-stream(2/stderr, *Trace-stream)
#? # . . push args
#? ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write-stream/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . write(2/stderr, "$\n")
#? # . . push args
#? 68/push "$\n"/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # }}}
# . check-trace-contains("segment 'a' starts at address 0x00001074.", msg)
# . . push args
68/push "F - test-compute-addresses-large-segments/0"/imm32
68/push "segment 'a' starts at address 0x00001074."/imm32
# . . call
e8/call check-trace-contains/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . check-trace-contains("segment 'b' starts at address 0x00002678.", msg)
# . . push args
68/push "F - test-compute-addresses-large-segments/1"/imm32
68/push "segment 'b' starts at address 0x00002678."/imm32
# . . call
e8/call check-trace-contains/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . check-trace-contains("label 'l1' is at address 0x00001077.", msg)
# . . push args
68/push "F - test-compute-addresses-large-segments/3"/imm32
68/push "label 'l1' is at address 0x00001077."/imm32
# . . call
e8/call check-trace-contains/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . epilogue
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
emit-output: # in: (addr stream byte), out: (addr buffered-file), segments: (addr stream {(handle array byte), segment-info}), labels: (addr stream {(handle array byte), label-info})
# pseudocode:
# emit-headers(out, segments, labels)
# emit-segments(in, out, labels)
#
# . prologue
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
#? # write(2/stderr, "emit-headers\n") {{{
#? # . . push args
#? 68/push "emit-headers\n"/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # }}}
# emit-headers(out, segments, labels)
# . . push args
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x14/disp8 . # push *(ebp+20)
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16)
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12)
# . . call
e8/call emit-headers/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
#? # write(2/stderr, "emit-segments\n") {{{
#? # . . push args
#? 68/push "emit-segments\n"/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # }}}
# emit-segments(in, out, labels)
# . . push args
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x14/disp8 . # push *(ebp+20)
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12)
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
# . . call
e8/call emit-segments/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
$emit-output:end:
# . epilogue
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
# global scratch space for emit-segments
== data
emit-segments:datum: # slice
0/imm32/start
0/imm32/end
== code
emit-segments: # in: (addr stream byte), out: (addr buffered-file), labels: (addr stream {(handle array byte), label-info})
# pseudocode:
# var offset-of-next-instruction = 0
# var line: (stream byte 512)
# line-loop:
# while true
# clear-stream(line)
# read-line(in, line)
# if (line->write == 0) break # end of file
# offset-of-next-instruction += num-bytes(line)
# var is-far-jump-or-call? = is-far-jump-or-call?(line)
# rewind-stream(line)
# while true
# var word-slice = next-word(line)
# if slice-empty?(word-slice) # end of line
# break
# if slice-starts-with?(word-slice, "#") # comment
# break
# if is-label?(word-slice) # no need for label declarations anymore
# goto line-loop # don't insert empty lines
# if slice-equal?(word-slice, "==") # no need for segment header lines
# goto line-loop # don't insert empty lines
# if length(word-slice) == 2
# write-slice-buffered(out, word-slice)
# write-buffered(out, " ")
# continue
# var datum: (addr slice) = next-token-from-slice(word-slice->start, word-slice->end, "/")
# var info: (addr label-info) = get-slice(labels, datum)
# if has-metadata?(word-slice, "imm8")
# abort
# else if has-metadata?(word-slice, "imm32")
# emit(out, info->address, 4)
# else if has-metadata?(word-slice, "disp8")
# value = info->offset - offset-of-next-instruction
# emit(out, value, 1)
# else if has-metadata?(word-slice, "disp32")
# if is-far-jump-or-call?
# value = info->offset - offset-of-next-instruction
# else
# value = info->address
# emit(out, value, 4)
# else
# abort
# write-buffered(out, "\n")
#
# registers:
# line: ecx
# word-slice: edx
# offset-of-next-instruction: ebx
# is-far-jump-or-call?: edi
# info: esi (inner loop only)
# temporaries: eax, esi (outer loop)
#
# . prologue
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# . save registers
50/push-eax
51/push-ecx
52/push-edx
53/push-ebx
56/push-esi
57/push-edi
# var line/ecx: (stream byte 512)
81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0x200/imm32 # subtract from esp
68/push 0x200/imm32/size
68/push 0/imm32/read
68/push 0/imm32/write
89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
# var word-slice/edx: slice
68/push 0/imm32/end
68/push 0/imm32/start
89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx
# offset-of-next-instruction/ebx = 0
31/xor 3/mod/direct 3/rm32/ebx . . . 3/r32/ebx . . # clear ebx
$emit-segments:line-loop:
# clear-stream(line)
# . . push args
51/push-ecx
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# read-line(in, line)
# . . push args
51/push-ecx
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
# . . call
e8/call read-line/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # dump line {{{
#? # . write(2/stderr, "LL: ")
#? # . . push args
#? 68/push "LL: "/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # write-stream(2/stderr, line)
#? # . . push args
#? 51/push-ecx
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write-stream/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . write(2/stderr, "$\n")
#? # . . push args
#? 68/push "$\n"/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . rewind-stream(line)
#? # . . push args
#? 51/push-ecx
#? # . . call
#? e8/call rewind-stream/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
#? # }}}
$emit-segments:check-for-end-of-input:
# if (line->write == 0) break
81 7/subop/compare 0/mod/indirect 1/rm32/ecx . . . . . 0/imm32 # compare *ecx
0f 84/jump-if-= $emit-segments:end/disp32
# offset-of-next-instruction += num-bytes(line)
# . eax = num-bytes(line)
# . . push args
51/push-ecx
# . . call
e8/call num-bytes/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . ebx += eax
01/add 3/mod/direct 3/rm32/ebx . . . 0/r32/eax . . # add eax to ebx
# var is-far-jump-or-call?/edi: boolean = is-far-jump-or-call?(line)
# . . push args
51/push-ecx
# . . call
e8/call is-far-jump-or-call?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# rewind-stream(line)
# . . push args
51/push-ecx
# . . call
e8/call rewind-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
$emit-segments:word-loop:
# next-word(line, word-slice)
# . . push args
52/push-edx
51/push-ecx
# . . call
e8/call next-word/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # dump word-slice {{{
#? # . write(2/stderr, "w: ")
#? # . . push args
#? 68/push "w: "/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . write-slice-buffered(Stderr, word-slice)
#? # . . push args
#? 52/push-edx
#? 68/push Stderr/imm32
#? # . . call
#? e8/call write-slice-buffered/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . flush(Stderr)
#? # . . push args
#? 68/push Stderr/imm32
#? # . . call
#? e8/call flush/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
#? # . write(2/stderr, "$\n")
#? # . . push args
#? 68/push "$\n"/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # }}}
$emit-segments:check-for-end-of-line:
# if (slice-empty?(word-slice)) break
# . eax = slice-empty?(word-slice)
# . . push args
52/push-edx
# . . call
e8/call slice-empty?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . if (eax != 0) break
3d/compare-eax-and 0/imm32/false
0f 85/jump-if-!= $emit-segments:next-line/disp32
$emit-segments:check-for-comment:
# if (slice-starts-with?(word-slice, "#")) break
# . start/esi = word-slice->start
8b/copy 0/mod/indirect 2/rm32/edx . . . 6/r32/esi . . # copy *edx to esi
# . c/eax = *start
31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax
8a/copy-byte 0/mod/indirect 6/rm32/esi . . . 0/r32/AL . . # copy byte at *esi to AL
# . if (eax == '#') break
3d/compare-eax-and 0x23/imm32/hash
0f 84/jump-if-= $emit-segments:next-line/disp32
$emit-segments:check-for-label:
# if is-label?(word-slice) break
# . eax = is-label?(word-slice)
# . . push args
52/push-edx
# . . call
e8/call is-label?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . if (eax != false) break
3d/compare-eax-and 0/imm32/false
0f 85/jump-if-!= $emit-segments:line-loop/disp32
$emit-segments:check-for-segment-header:
# if (slice-equal?(word-slice, "==")) break
# . eax = slice-equal?(word-slice, "==")
# . . push args
68/push "=="/imm32
52/push-edx
# . . call
e8/call slice-equal?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . if (eax != false) break
3d/compare-eax-and 0/imm32/false
0f 85/jump-if-!= $emit-segments:line-loop/disp32
$emit-segments:2-character:
# if (size(word-slice) != 2) goto next check
# . eax = size(word-slice)
8b/copy 1/mod/*+disp8 2/rm32/edx . . . 0/r32/eax 4/disp8 . # copy *(edx+4) to eax
2b/subtract 0/mod/indirect 2/rm32/edx . . . 0/r32/eax . . # subtract *edx from eax
# . if (eax != 2) goto next check
3d/compare-eax-and 2/imm32
75/jump-if-!= $emit-segments:check-metadata/disp8
# write-slice-buffered(out, word-slice)
# . . push args
52/push-edx
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12)
# . . call
e8/call write-slice-buffered/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# write-buffered(out, " ")
# . . push args
68/push Space/imm32
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12)
# . . call
e8/call write-buffered/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# continue
e9/jump $emit-segments:word-loop/disp32
$emit-segments:check-metadata:
# - if we get here, 'word-slice' must be a label to be looked up
# datum = next-token-from-slice(word-slice->start, word-slice->end, "/")
# . . push args
68/push emit-segments:datum/imm32
68/push 0x2f/imm32/slash
ff 6/subop/push 1/mod/*+disp8 2/rm32/edx . . . . 4/disp8 . # push *(edx+4)
ff 6/subop/push 0/mod/indirect 2/rm32/edx . . . . . . # push *edx
# . . call
e8/call next-token-from-slice/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp
#? # dump datum {{{
#? # . write(2/stderr, "datum: ")
#? # . . push args
#? 68/push "datum: "/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . write-slice-buffered(Stderr, datum)
#? # . . push args
#? 68/push emit-segments:datum/imm32
#? 68/push Stderr/imm32
#? # . . call
#? e8/call write-slice-buffered/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . flush(Stderr)
#? # . . push args
#? 68/push Stderr/imm32
#? # . . call
#? e8/call flush/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
#? # . write(2/stderr, "$\n")
#? # . . push args
#? 68/push "$\n"/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # }}}
# info/esi = get-slice(labels, datum, row-size=24, "label table")
# . eax = get-slice(labels, datum, row-size=24, "label table")
# . . push args
68/push "label table"/imm32
68/push 0x18/imm32/row-size
68/push emit-segments:datum/imm32
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16)
# . . call
e8/call get-slice/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp
# . esi = eax
89/copy 3/mod/direct 6/rm32/esi . . . 0/r32/eax . . # copy eax to esi
$emit-segments:check-imm8:
# if (has-metadata?(word-slice, "imm8")) abort
# . eax = has-metadata?(edx, "imm8")
# . . push args
68/push "imm8"/imm32
52/push-edx
# . . call
e8/call has-metadata?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . if (eax != false) abort
3d/compare-eax-and 0/imm32/false
0f 85/jump-if-!= $emit-segments:imm8-abort/disp32
$emit-segments:check-imm32:
# if (!has-metadata?(word-slice, "imm32")) goto next check
# . eax = has-metadata?(edx, "imm32")
# . . push args
68/push "imm32"/imm32
52/push-edx
# . . call
e8/call has-metadata?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . if (eax == false) goto next check
3d/compare-eax-and 0/imm32/false
74/jump-if-= $emit-segments:check-disp8/disp8
#? # dump info->address {{{
#? # . write(2/stderr, "info->address: ")
#? # . . push args
#? 68/push "info->address: "/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . write-int32-hex-buffered(Stderr, info->address)
#? # . . push args
#? ff 6/subop/push 1/mod/*+disp8 6/rm32/esi . . . . 0xc/disp8 . # push *(esi+12)
#? 68/push Stderr/imm32
#? # . . call
#? e8/call write-int32-hex-buffered/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . flush(Stderr)
#? # . . push args
#? 68/push Stderr/imm32
#? # . . call
#? e8/call flush/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
#? # . write(2/stderr, "$\n")
#? # . . push args
#? 68/push "$\n"/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # }}}
$emit-segments:emit-imm32:
# emit-hex(out, info->address, 4)
# . . push args
68/push 4/imm32
ff 6/subop/push 1/mod/*+disp8 6/rm32/esi . . . . 0xc/disp8 . # push *(esi+12)
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12)
# . . call
e8/call emit-hex/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# continue
e9/jump $emit-segments:word-loop/disp32
$emit-segments:check-disp8:
# if (!has-metadata?(word-slice, "disp8")) goto next check
# . eax = has-metadata?(edx, "disp8")
# . . push args
68/push "disp8"/imm32
52/push-edx
# . . call
e8/call has-metadata?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . if (eax == false) goto next check
3d/compare-eax-and 0/imm32/false
74/jump-if-= $emit-segments:check-disp32/disp8
$emit-segments:emit-disp8:
# emit-hex(out, info->offset - offset-of-next-instruction, 1)
# . . push args
68/push 1/imm32
8b/copy 1/mod/*+disp8 6/rm32/esi . . . 0/r32/eax 8/disp8 . # copy *(esi+8) to eax
29/subtract 3/mod/direct 0/rm32/eax . . . 3/r32/ebx . . # subtract ebx from eax
50/push-eax
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12)
# . . call
e8/call emit-hex/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# continue
e9/jump $emit-segments:word-loop/disp32
$emit-segments:check-disp32:
# if (!has-metadata?(word-slice, "disp32")) abort
# . eax = has-metadata?(edx, "disp32")
# . . push args
68/push "disp32"/imm32
52/push-edx
# . . call
e8/call has-metadata?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . if (eax == false) abort
3d/compare-eax-and 0/imm32/false
0f 84/jump-if-= $emit-segments:abort/disp32
$emit-segments:emit-disp32:
# var value/eax = info->address
8b/copy 1/mod/*+disp8 6/rm32/esi . . . 0/r32/eax 0xc/disp8 . # copy *(esi+12) to eax
# if (is-far-jump-or-call?) value = info->offset - offset-of-next-instruction
81 7/subop/compare 3/mod/direct 7/rm32/edi . . . . . 0/imm32/false # compare edi
74/jump-if-= $emit-segments:really-emit-disp32/disp8
8b/copy 1/mod/*+disp8 6/rm32/esi . . . 0/r32/eax 8/disp8 . # copy *(esi+8) to eax
29/subtract 3/mod/direct 0/rm32/eax . . . 3/r32/ebx . . # subtract ebx from eax
$emit-segments:really-emit-disp32:
# emit-hex(out, value, 4)
# . . push args
68/push 4/imm32
50/push-eax
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12)
# . . call
e8/call emit-hex/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# continue
e9/jump $emit-segments:word-loop/disp32
$emit-segments:next-line:
# write-buffered(out, "\n")
# . . push args
68/push Newline/imm32
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12)
# . . call
e8/call write-buffered/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# loop
e9/jump $emit-segments:line-loop/disp32
$emit-segments:end:
# . reclaim locals
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x214/imm32 # add to esp
# . restore registers
5f/pop-to-edi
5e/pop-to-esi
5b/pop-to-ebx
5a/pop-to-edx
59/pop-to-ecx
58/pop-to-eax
# . epilogue
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
$emit-segments:global-variable-abort:
# . _write(2/stderr, error)
# . . push args
68/push "emit-segments: must refer to global variables with /disp32 or /imm32"/imm32
68/push 2/imm32/stderr
# . . call
e8/call _write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . syscall(exit, 1)
bb/copy-to-ebx 1/imm32
e8/call syscall_exit/disp32
# never gets here
$emit-segments:imm8-abort:
# . _write(2/stderr, error)
# . . push args
68/push "emit-segments: cannot refer to code labels with /imm8"/imm32
68/push 2/imm32/stderr
# . . call
e8/call _write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . syscall(exit, 1)
bb/copy-to-ebx 1/imm32
e8/call syscall_exit/disp32
# never gets here
$emit-segments:abort:
# print(stderr, "missing metadata in " word-slice)
# . _write(2/stderr, "missing metadata in word ")
# . . push args
68/push "emit-segments: missing metadata in "/imm32
68/push 2/imm32/stderr
# . . call
e8/call _write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . write-slice-buffered(Stderr, word-slice)
# . . push args
52/push-edx
68/push Stderr/imm32
# . . call
e8/call write-slice-buffered/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . flush(Stderr)
# . . push args
68/push Stderr/imm32
# . . call
e8/call flush/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . syscall(exit, 1)
bb/copy-to-ebx 1/imm32
e8/call syscall_exit/disp32
# never gets here
test-emit-segments-non-far-control-flow:
# labels turn into absolute addresses if opcodes are not far jumps or calls
#
# input:
# in:
# == code 0x1000
# ab cd ef gh
# ij x/disp32
# == data 0x2000
# 00
# x:
# 34
# labels:
# - 'x': {'data', 1, 0x207a}
#
# output:
# ab cd ef gh
# ij 7a 20 00 00
# 00
# 34
#
# . prologue
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# setup
# . clear-stream(_test-input-stream)
# . . push args
68/push _test-input-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . clear-stream(_test-output-stream)
# . . push args
68/push _test-output-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . clear-stream($_test-output-buffered-file->buffer)
# . . push args
68/push $_test-output-buffered-file->buffer/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . var labels/edx: (stream byte 8*24)
81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0xc0/imm32 # subtract from esp
68/push 0xc0/imm32/size
68/push 0/imm32/read
68/push 0/imm32/write
89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx
# . var h/ebx: (handle array byte)
68/push 0/imm32
68/push 0/imm32
89/copy 3/mod/direct 3/rm32/ebx . . . 4/r32/esp . . # copy esp to ebx
# initialize input
# . write(_test-input-stream, "== code 0x1000\n")
# . . push args
68/push "== code 0x1000\n"/imm32
68/push _test-input-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . write(_test-input-stream, "ab cd ef gh\n")
# . . push args
68/push "ab cd ef gh\n"/imm32
68/push _test-input-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . write(_test-input-stream, "ij x/disp32\n")
# . . push args
68/push "ij x/disp32\n"/imm32
68/push _test-input-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . write(_test-input-stream, "== data 0x2000\n")
# . . push args
68/push "== data 0x2000\n"/imm32
68/push _test-input-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . write(_test-input-stream, "00\n")
# . . push args
68/push "00\n"/imm32
68/push _test-input-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . write(_test-input-stream, "x:\n")
# . . push args
68/push "x:\n"/imm32
68/push _test-input-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . write(_test-input-stream, "34\n")
# . . push args
68/push "34\n"/imm32
68/push _test-input-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . stream-add6(labels, "x", "data", 1, 0x207a)
68/push 0x207a/imm32/label-address
68/push 1/imm32/segment-offset
# . . push "data"
53/push-ebx
68/push "data"/imm32
68/push Heap/imm32
e8/call copy-array/disp32
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
ff 6/subop/push 1/mod/*+disp8 3/rm32/ebx . . . . 4/disp8 . # push *(ebx+4)
ff 6/subop/push 0/mod/indirect 3/rm32/ebx . . . . . . # push *ebx
# . . push "l1"
53/push-ebx
68/push "x"/imm32
68/push Heap/imm32
e8/call copy-array/disp32
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
ff 6/subop/push 1/mod/*+disp8 3/rm32/ebx . . . . 4/disp8 . # push *(ebx+4)
ff 6/subop/push 0/mod/indirect 3/rm32/ebx . . . . . . # push *ebx
# . . push labels
52/push-edx
# . . call
e8/call stream-add6/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x1c/imm32 # add to esp
# component under test
# . emit-segments(_test-input-stream, _test-output-buffered-file, labels)
# . . push args
52/push-edx
68/push _test-output-buffered-file/imm32
68/push _test-input-stream/imm32
# . . call
e8/call emit-segments/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# checks
# . flush(_test-output-buffered-file)
# . . push args
68/push _test-output-buffered-file/imm32
# . . call
e8/call flush/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
#? # dump output {{{
#? # . write(2/stderr, "result: ^")
#? # . . push args
#? 68/push "result: ^"/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . write-stream(2/stderr, _test-output-stream)
#? # . . push args
#? 68/push _test-output-stream/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write-stream/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . write(2/stderr, "$\n")
#? # . . push args
#? 68/push "$\n"/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . rewind-stream(_test-output-stream)
#? # . . push args
#? 68/push _test-output-stream/imm32
#? # . . call
#? e8/call rewind-stream/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
#? # }}}
# . check-next-stream-line-equal(_test-output-stream, "ab cd ef gh ", msg)
# . . push args
68/push "F - test-emit-segments-global-variable/0"/imm32
68/push "ab cd ef gh "/imm32
68/push _test-output-stream/imm32
# . . call
e8/call check-next-stream-line-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . check-next-stream-line-equal(_test-output-stream, "ij 7a 20 00 00 ", msg)
# . . push args
68/push "F - test-emit-segments-global-variable/1"/imm32
68/push "ij 7a 20 00 00 "/imm32
68/push _test-output-stream/imm32
# . . call
e8/call check-next-stream-line-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . check-next-stream-line-equal(_test-output-stream, "00 ", msg)
# . . push args
68/push "F - test-emit-segments-global-variable/2"/imm32
68/push "00 "/imm32
68/push _test-output-stream/imm32
# . . call
e8/call check-next-stream-line-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . check-next-stream-line-equal(_test-output-stream, "34 ", msg)
# . . push args
68/push "F - test-emit-segments-global-variable/3"/imm32
68/push "34 "/imm32
68/push _test-output-stream/imm32
# . . call
e8/call check-next-stream-line-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . epilogue
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
test-emit-segments-code-label:
# labels turn into PC-relative addresses if opcodes are far jumps or calls
#
# input:
# in:
# == code 0x1000
# ab cd
# l1:
# ef gh
# e8 l1/disp32
# labels:
# - 'l1': {'code', 2, 0x1056}
#
# output:
# ab cd
# ef gh
# e8 f9 ff ff ff # -7
#
# . prologue
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# setup
# . clear-stream(_test-input-stream)
# . . push args
68/push _test-input-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . clear-stream(_test-output-stream)
# . . push args
68/push _test-output-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . clear-stream($_test-output-buffered-file->buffer)
# . . push args
68/push $_test-output-buffered-file->buffer/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . var labels/edx: (stream byte 8*24)
81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0xc0/imm32 # subtract from esp
68/push 0xc0/imm32/size
68/push 0/imm32/read
68/push 0/imm32/write
89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx
# . var h/ebx: (handle array byte)
68/push 0/imm32
68/push 0/imm32
89/copy 3/mod/direct 3/rm32/ebx . . . 4/r32/esp . . # copy esp to ebx
# initialize input
# . write(_test-input-stream, "== code 0x1000\n")
# . . push args
68/push "== code 0x1000\n"/imm32
68/push _test-input-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . write(_test-input-stream, "ab cd\n")
# . . push args
68/push "ab cd\n"/imm32
68/push _test-input-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . write(_test-input-stream, "l1:\n")
# . . push args
68/push "l1:\n"/imm32
68/push _test-input-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . write(_test-input-stream, " ef gh\n")
# . . push args
68/push " ef gh\n"/imm32
68/push _test-input-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . write(_test-input-stream, " e8 l1/disp32\n")
# . . push args
68/push " e8 l1/disp32\n"/imm32
68/push _test-input-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . stream-add6(labels, "l1", "code", 2, 0x1056)
68/push 0x1056/imm32/label-address
68/push 2/imm32/segment-offset
# . . push "data"
53/push-ebx
68/push "code"/imm32
68/push Heap/imm32
e8/call copy-array/disp32
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
ff 6/subop/push 1/mod/*+disp8 3/rm32/ebx . . . . 4/disp8 . # push *(ebx+4)
ff 6/subop/push 0/mod/indirect 3/rm32/ebx . . . . . . # push *ebx
# . . push "l1"
53/push-ebx
68/push "l1"/imm32
68/push Heap/imm32
e8/call copy-array/disp32
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
ff 6/subop/push 1/mod/*+disp8 3/rm32/ebx . . . . 4/disp8 . # push *(ebx+4)
ff 6/subop/push 0/mod/indirect 3/rm32/ebx . . . . . . # push *ebx
# . . push labels
52/push-edx
# . . call
e8/call stream-add6/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x1c/imm32 # add to esp
# component under test
# . emit-segments(_test-input-stream, _test-output-buffered-file, labels)
# . . push args
52/push-edx
68/push _test-output-buffered-file/imm32
68/push _test-input-stream/imm32
# . . call
e8/call emit-segments/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# checks
# . flush(_test-output-buffered-file)
# . . push args
68/push _test-output-buffered-file/imm32
# . . call
e8/call flush/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
#? # dump output {{{
#? # . write(2/stderr, "result: ^")
#? # . . push args
#? 68/push "result: ^"/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . write-stream(2/stderr, _test-output-stream)
#? # . . push args
#? 68/push _test-output-stream/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write-stream/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . write(2/stderr, "$\n")
#? # . . push args
#? 68/push "$\n"/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . rewind-stream(_test-output-stream)
#? # . . push args
#? 68/push _test-output-stream/imm32
#? # . . call
#? e8/call rewind-stream/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
#? # }}}
# . check-next-stream-line-equal(_test-output-stream, "ab cd ", msg)
# . . push args
68/push "F - test-emit-segments-code-label/0"/imm32
68/push "ab cd "/imm32
68/push _test-output-stream/imm32
# . . call
e8/call check-next-stream-line-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . check-next-stream-line-equal(_test-output-stream, "ef gh ", msg)
# . . push args
68/push "F - test-emit-segments-code-label/1"/imm32
68/push "ef gh "/imm32
68/push _test-output-stream/imm32
# . . call
e8/call check-next-stream-line-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . check-next-stream-line-equal(_test-output-stream, "e8 f9 ff ff ff ", msg)
# . . push args
68/push "F - test-emit-segments-code-label/2"/imm32
68/push "e8 f9 ff ff ff "/imm32
68/push _test-output-stream/imm32
# . . call
e8/call check-next-stream-line-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . epilogue
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
test-emit-segments-code-label-absolute:
# labels can also convert to absolute addresses
#
# input:
# in:
# == code 0x1000
# ab cd
# l1:
# ef gh
# ij l1/imm32
# labels:
# - 'l1': {'code', 2, 0x1056}
#
# output:
# ab cd
# ef gh
# ij 56 10 00 00
#
# . prologue
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# setup
# . clear-stream(_test-input-stream)
# . . push args
68/push _test-input-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . clear-stream(_test-output-stream)
# . . push args
68/push _test-output-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . clear-stream($_test-output-buffered-file->buffer)
# . . push args
68/push $_test-output-buffered-file->buffer/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . var labels/edx: (stream byte 8*24)
81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0xc0/imm32 # subtract from esp
68/push 0xc0/imm32/size
68/push 0/imm32/read
68/push 0/imm32/write
89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx
# . var h/ebx: (handle array byte)
68/push 0/imm32
68/push 0/imm32
89/copy 3/mod/direct 3/rm32/ebx . . . 4/r32/esp . . # copy esp to ebx
# initialize input
# . write(_test-input-stream, "== code 0x1000\n")
# . . push args
68/push "== code 0x1000\n"/imm32
68/push _test-input-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . write(_test-input-stream, "ab cd\n")
# . . push args
68/push "ab cd\n"/imm32
68/push _test-input-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . write(_test-input-stream, "l1:\n")
# . . push args
68/push "l1:\n"/imm32
68/push _test-input-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . write(_test-input-stream, " ef gh\n")
# . . push args
68/push " ef gh\n"/imm32
68/push _test-input-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . write(_test-input-stream, " ij l1/imm32\n")
# . . push args
68/push " ij l1/imm32\n"/imm32
68/push _test-input-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . stream-add6(labels, "l1", "code", 2, 0x1056)
68/push 0x1056/imm32/label-address
68/push 2/imm32/segment-offset
# . . push "data"
53/push-ebx
68/push "code"/imm32
68/push Heap/imm32
e8/call copy-array/disp32
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
ff 6/subop/push 1/mod/*+disp8 3/rm32/ebx . . . . 4/disp8 . # push *(ebx+4)
ff 6/subop/push 0/mod/indirect 3/rm32/ebx . . . . . . # push *ebx
# . . push "l1"
53/push-ebx
68/push "l1"/imm32
68/push Heap/imm32
e8/call copy-array/disp32
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
ff 6/subop/push 1/mod/*+disp8 3/rm32/ebx . . . . 4/disp8 . # push *(ebx+4)
ff 6/subop/push 0/mod/indirect 3/rm32/ebx . . . . . . # push *ebx
# . . push labels
52/push-edx
# . . call
e8/call stream-add6/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x1c/imm32 # add to esp
# component under test
# . emit-segments(_test-input-stream, _test-output-buffered-file, labels)
# . . push args
52/push-edx
68/push _test-output-buffered-file/imm32
68/push _test-input-stream/imm32
# . . call
e8/call emit-segments/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# checks
# . flush(_test-output-buffered-file)
# . . push args
68/push _test-output-buffered-file/imm32
# . . call
e8/call flush/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
#? # dump output {{{
#? # . write(2/stderr, "result: ^")
#? # . . push args
#? 68/push "result: ^"/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . write-stream(2/stderr, _test-output-stream)
#? # . . push args
#? 68/push _test-output-stream/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write-stream/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . write(2/stderr, "$\n")
#? # . . push args
#? 68/push "$\n"/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . rewind-stream(_test-output-stream)
#? # . . push args
#? 68/push _test-output-stream/imm32
#? # . . call
#? e8/call rewind-stream/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
#? # }}}
# . check-next-stream-line-equal(_test-output-stream, "ab cd ", msg)
# . . push args
68/push "F - test-emit-segments-code-label-absolute/0"/imm32
68/push "ab cd "/imm32
68/push _test-output-stream/imm32
# . . call
e8/call check-next-stream-line-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . check-next-stream-line-equal(_test-output-stream, "ef gh ", msg)
# . . push args
68/push "F - test-emit-segments-code-label-absolute/1"/imm32
68/push "ef gh "/imm32
68/push _test-output-stream/imm32
# . . call
e8/call check-next-stream-line-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . check-next-stream-line-equal(_test-output-stream, "ij f9 ff ff ff ", msg)
# . . push args
68/push "F - test-emit-segments-code-label-absolute/2"/imm32
68/push "ij 56 10 00 00 "/imm32
68/push _test-output-stream/imm32
# . . call
e8/call check-next-stream-line-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . epilogue
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
# reads line to make some checks
# don't assume the read state of line after calling this function
is-far-jump-or-call?: # line: (addr stream byte) -> result/edi: boolean
# . prologue
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# . save registers
50/push-eax
51/push-ecx
52/push-edx
53/push-ebx
# ecx = line
8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 8/disp8 . # copy *(ebp+8) to ecx
# var word-slice/edx: slice
68/push 0/imm32/end
68/push 0/imm32/start
89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx
# var datum-slice/ebx: slice
68/push 0/imm32/end
68/push 0/imm32/start
89/copy 3/mod/direct 3/rm32/ebx . . . 4/r32/esp . . # copy esp to ebx
# result = false
bf/copy-to-edi 0/imm32/false
$is-far-jump-or-call?:check-first-word:
# next-word(line, word-slice)
# . . push args
52/push-edx
51/push-ecx
# . . call
e8/call next-word/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# if (slice-empty?(word-slice)) return false
# . eax = slice-empty?(word-slice)
# . . push args
52/push-edx
# . . call
e8/call slice-empty?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . if (eax != 0) return
3d/compare-eax-and 0/imm32/false
0f 85/jump-if-!= $is-far-jump-or-call?:end/disp32
# datum = next-token-from-slice(word-slice->start, word-slice->end, "/")
# . . push args
53/push-ebx
68/push 0x2f/imm32/slash
ff 6/subop/push 1/mod/*+disp8 2/rm32/edx . . . . 4/disp8 . # push *(edx+4)
ff 6/subop/push 0/mod/indirect 2/rm32/edx . . . . . . # push *edx
# . . call
e8/call next-token-from-slice/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp
# if (datum-slice == "e8") return true
# . eax = slice-equal?(datum-slice, "e8")
# . . push args
68/push "e8"/imm32
53/push-ebx
# . . call
e8/call slice-equal?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . if (eax != false) return true
3d/compare-eax-and 0/imm32/false
75/jump-if-!= $is-far-jump-or-call?:return-true/disp8
# if (datum-slice == "e9") return true
# . eax = slice-equal?(datum-slice, "e9")
# . . push args
68/push "e9"/imm32
53/push-ebx
# . . call
e8/call slice-equal?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . if (eax != false) return true
3d/compare-eax-and 0/imm32/false
75/jump-if-!= $is-far-jump-or-call?:return-true/disp8
# if (datum-slice != "0f") return false
# . eax = slice-equal?(datum-slice, "0f")
# . . push args
68/push "0f"/imm32
53/push-ebx
# . . call
e8/call slice-equal?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . if (eax == false) return
3d/compare-eax-and 0/imm32/false
74/jump-if-= $is-far-jump-or-call?:end/disp8
$is-far-jump-or-call?:check-second-word:
# next-word(line, word-slice)
# . . push args
52/push-edx
51/push-ecx
# . . call
e8/call next-word/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# if (slice-empty?(word-slice)) return false
# . eax = slice-empty?(word-slice)
# . . push args
52/push-edx
# . . call
e8/call slice-empty?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . if (eax != 0) return
3d/compare-eax-and 0/imm32/false
75/jump-if-!= $is-far-jump-or-call?:end/disp8
# if datum of word-slice does not start with "8", return false
# . start/eax = word-slice->start
8b/copy 0/mod/indirect 2/rm32/edx . . . 0/r32/eax . . # copy *edx to eax
# . c/eax = *start
8a/copy-byte 0/mod/indirect 0/rm32/eax . . . 0/r32/AL . . # copy byte at *eax to AL
81 4/subop/and 3/mod/direct 0/rm32/eax . . . . . 0xff/imm32 # bitwise and of eax
# . if (eax != '8') return
3d/compare-eax-and 0x38/imm32/8
75/jump-if-!= $is-far-jump-or-call?:end/disp8
# otherwise return true
$is-far-jump-or-call?:return-true:
bf/copy-to-edi 1/imm32/true
$is-far-jump-or-call?:end:
# . reclaim locals
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp
# . restore registers
5b/pop-to-ebx
5a/pop-to-edx
59/pop-to-ecx
58/pop-to-eax
# . epilogue
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
emit-headers: # out: (addr buffered-file), segments: (addr stream {(handle array byte), segment-info}), labels: (addr stream {(handle array byte), label-info})
# pseudocode:
# emit-elf-header(out, segments, labels)
# var curr-segment-row: (addr handle array byte) = segments->data
# max = &segments->data[segments->write]
# while true
# if (curr-segment >= max) break
# emit-elf-program-header-entry(out, curr-segment-row)
# curr-segment-row += 20 # size of a row
#
# . prologue
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# . save registers
50/push-eax
51/push-ecx
# emit-elf-header(out, segments, labels)
# . . push args
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16)
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12)
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
# . . call
e8/call emit-elf-header/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# eax = segments
8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 0xc/disp8 . # copy *(ebp+12) to eax
# ecx = segments->write
8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx
# curr-segment/eax = segments->data
8d/copy-address 1/mod/*+disp8 0/rm32/eax . . . 0/r32/eax 0xc/disp8 . # copy eax+12 to eax
# max/ecx = &segments->data[segments->write]
01/add 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # add eax to ecx
$emit-headers:loop:
# if (curr-segment >= max) break
39/compare 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # compare eax with ecx
0f 83/jump-if-addr>= $emit-headers:end/disp32
#? # dump curr-segment->name {{{
#? # . write(2/stderr, "about to emit ph entry: segment->name: ")
#? # . . push args
#? 68/push "about to emit ph entry: segment->name: "/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . clear-stream($Stderr->buffer)
#? # . . push args
#? 68/push $Stderr->buffer/imm32
#? # . . call
#? e8/call clear-stream/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
#? # . write-int32-hex-buffered(Stderr, &curr-segment)
#? # . . push args
#? 50/push-eax
#? 68/push Stderr/imm32
#? # . . call
#? e8/call write-int32-hex-buffered/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . flush(Stderr)
#? # . . push args
#? 68/push Stderr/imm32
#? # . . call
#? e8/call flush/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
#? # . write(2/stderr, " -> ")
#? # . . push args
#? 68/push " -> "/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . write-int32-hex-buffered(Stderr, curr-segment->name)
#? # . . push args
#? ff 6/subop/push 0/mod/indirect 0/rm32/eax . . . . . . # push *eax
#? 68/push Stderr/imm32
#? # . . call
#? e8/call write-int32-hex-buffered/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . flush(Stderr)
#? # . . push args
#? 68/push Stderr/imm32
#? # . . call
#? e8/call flush/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
#? # . write(2/stderr, "\n")
#? # . . push args
#? 68/push Newline/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # }}}
#? # write(2/stderr, "emit-segment-header\n") {{{
#? # . . push args
#? 68/push "emit-segment-header\n"/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # }}}
# emit-elf-program-header-entry(out, curr-segment)
# . . push args
50/push-eax
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
# . . call
e8/call emit-elf-program-header-entry/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# curr-segment += 20 # size of a row
81 0/subop/add 3/mod/direct 0/rm32/eax . . . . . 0x14/imm32 # add to eax
e9/jump $emit-headers:loop/disp32
$emit-headers:end:
# . restore registers
59/pop-to-ecx
58/pop-to-eax
# . epilogue
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
emit-elf-header: # out: (addr buffered-file), segments: (addr stream {(handle array byte), segment-info}), labels: (addr stream {(handle array byte), label-info})
# pseudocode
# *$Elf_e_entry = get(labels, "Entry")->address
# *$Elf_e_phnum = segments->write / 20 # size of a row
# emit-hex-array(out, Elf_header)
# write-buffered(out, "\n")
#
# . prologue
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# . save registers
50/push-eax
51/push-ecx
52/push-edx # just because we need to call idiv
# *$Elf_e_entry = get(labels, "Entry")->address
# . eax = labels
8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 0x10/disp8 . # copy *(ebp+16) to eax
# . label-info/eax = get(labels, "Entry", row-size=24, "label table")
# . . push args
68/push "label table"/imm32
68/push 0x18/imm32/row-size
68/push "Entry"/imm32
50/push-eax
# . . call
e8/call get/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp
# . eax = label-info->address
8b/copy 1/mod/*+disp8 0/rm32/eax . . . 0/r32/eax 0xc/disp8 . # copy *(eax+12) to eax
# . *$Elf_e_entry = eax
89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/eax $Elf_e_entry/disp32 # copy eax to *$Elf_e_entry
# *$Elf_e_phnum = segments->write / 20
# . eax = segments
8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 0xc/disp8 . # copy *(ebp+12) to eax
# . len/eax = segments->write
8b/copy 0/mod/indirect 0/rm32/eax . . . 0/r32/eax . . # copy *eax to eax
# . eax = len / 20 (clobbering ecx and edx)
b9/copy-to-ecx 0x14/imm32
31/xor 3/mod/direct 2/rm32/edx . . . 2/r32/edx . . # clear edx
f7 7/subop/idiv 3/mod/direct 1/rm32/ecx . . . . . . # divide edx:eax by ecx, storing quotient in eax and remainder in edx
# . *$Elf_e_phnum = eax
89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/eax $Elf_e_phnum/disp32 # copy eax to *$Elf_e_phnum
# emit-hex-array(out, Elf_header)
# . . push args
68/push Elf_header/imm32
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
# . . call
e8/call emit-hex-array/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# write-buffered(out, "\n")
# . . push args
68/push Newline/imm32
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
# . . call
e8/call write-buffered/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
$emit-elf-header:end:
# . restore registers
5a/pop-to-edx
59/pop-to-ecx
58/pop-to-eax
# . epilogue
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
# segment-info: {address, file-offset, size} (12 bytes)
# segments: (addr stream {(handle array byte), segment-info}) (20 bytes per row)
emit-elf-program-header-entry: # out: (addr buffered-file), curr-segment: (addr {(handle array byte), segment-info})
# pseudocode:
# *$Elf_p_offset = curr-segment->file-offset
# *$Elf_p_vaddr = curr-segment->address
# *$Elf_p_paddr = curr-segment->address
# *$Elf_p_filesz = curr-segment->size
# *$Elf_p_memsz = curr-segment->size
# if curr-segment->name == "code"
# *$Elf_p_flags = 5 # r-x
# else
# *$Elf_p_flags = 6 # rw-
# emit-hex-array(out, Elf_program_header_entry)
# write-buffered(out, "\n")
#
# . prologue
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# . save registers
50/push-eax
56/push-esi
# esi = curr-segment
8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 0xc/disp8 . # copy *(ebp+12) to esi
# *$Elf_p_offset = curr-segment->file-offset
# . eax = curr-segment->file-offset
8b/copy 1/mod/*+disp8 6/rm32/esi . . . 0/r32/eax 0xc/disp8 . # copy *(esi+12) to eax
# . *$Elf_p_offset = eax
89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/eax $Elf_p_offset/disp32 # copy eax to *$Elf_p_offset
# *$Elf_p_vaddr = curr-segment->address
# . eax = curr-segment->address
8b/copy 1/mod/*+disp8 6/rm32/esi . . . 0/r32/eax 8/disp8 . # copy *(esi+8) to eax
# . *$Elf_p_vaddr = eax
89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/eax $Elf_p_vaddr/disp32 # copy eax to *$Elf_p_vaddr
# *$Elf_p_paddr = curr-segment->address
89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/eax $Elf_p_paddr/disp32 # copy eax to *$Elf_p_paddr
# *$Elf_p_filesz = curr-segment->size
# . eax = curr-segment->size
8b/copy 1/mod/*+disp8 6/rm32/esi . . . 0/r32/eax 0x10/disp8 . # copy *(esi+16) to eax
# . *$Elf_p_filesz = eax
89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/eax $Elf_p_filesz/disp32 # copy eax to *$Elf_p_filesz
# *$Elf_p_memsz = curr-segment->size
89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/eax $Elf_p_memsz/disp32 # copy eax to *$Elf_p_memsz
# if (!string-equal?(name, "code") goto next check
# . var name/eax: (addr array byte) = lookup(curr-segment->name)
# . . push args
ff 6/subop/push 1/mod/*+disp8 6/rm32/esi . . . . 4/disp8 . # push *(esi+4)
ff 6/subop/push 0/mod/indirect 6/rm32/esi . . . . . . # push *esi
# . . call
e8/call lookup/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . eax = string-equal?(name, "code")
# . . push args
68/push "code"/imm32
50/push-eax
# . . call
e8/call string-equal?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . if (eax == false) goto next check
3d/compare-eax-and 0/imm32/false
74/jump-if-= $emit-elf-program-header-entry:data/disp8
# *$Elf_p_flags = r-x
c7 0/subop/copy 0/mod/indirect 5/rm32/.disp32 . . . $Elf_p_flags/disp32 5/imm32 # copy to *$Elf_p_flags
eb/jump $emit-elf-program-header-entry:really-emit/disp8
$emit-elf-program-header-entry:data:
# otherwise *$Elf_p_flags = rw-
c7 0/subop/copy 0/mod/indirect 5/rm32/.disp32 . . . $Elf_p_flags/disp32 6/imm32 # copy to *$Elf_p_flags
$emit-elf-program-header-entry:really-emit:
# emit-hex-array(out, Elf_program_header_entry)
# . . push args
68/push Elf_program_header_entry/imm32
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
# . . call
e8/call emit-hex-array/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# write-buffered(out, "\n")
# . . push args
68/push Newline/imm32
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
# . . call
e8/call write-buffered/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
$emit-elf-program-header-entry:end:
# . restore registers
5e/pop-to-esi
58/pop-to-eax
# . epilogue
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
# - some helpers for tests
stream-add5: # in: (addr stream byte), key: handle, val1: addr, val2: addr, val3: addr
# . prologue
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# . save registers
50/push-eax
51/push-ecx
52/push-edx
56/push-esi
# esi = in
8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi
# curr/eax = &in->data[in->write]
# . eax = in->write
8b/copy 0/mod/indirect 6/rm32/esi . . . 0/r32/eax . . # copy *esi to eax
# . eax = esi+eax+12
8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/esi 0/index/eax . 0/r32/eax 0xc/disp8 . # copy esi+eax+12 to eax
# max/edx = &in->data[in->size]
# . edx = in->size
8b/copy 1/mod/*+disp8 6/rm32/esi . . . 2/r32/edx 8/disp8 . # copy *(esi+8) to edx
# . edx = esi+edx+12
8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/esi 2/index/edx . 2/r32/edx 0xc/disp8 . # copy esi+edx+12 to edx
# if (curr >= max) abort
39/compare 3/mod/direct 0/rm32/eax . . . 2/r32/edx . . # compare eax with edx
73/jump-if-addr>= $stream-add5:abort/disp8
# *curr = key->alloc-id
8b/copy 1/mod/*+disp8 5/rm32/ebp . . 1/r32/ecx 0xc/disp8 . # copy *(ebp+12) to ecx
89/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy ecx to *eax
# curr += 4
05/add-to-eax 4/imm32
# if (curr >= max) abort
39/compare 3/mod/direct 0/rm32/eax . . . 2/r32/edx . . # compare eax with edx
73/jump-if-addr>= $stream-add5:abort/disp8
# *curr = key->payload
8b/copy 1/mod/*+disp8 5/rm32/ebp . . 1/r32/ecx 0x10/disp8 . # copy *(ebp+16) to ecx
89/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy ecx to *eax
# curr += 4
05/add-to-eax 4/imm32
# if (curr >= max) abort
39/compare 3/mod/direct 0/rm32/eax . . . 2/r32/edx . . # compare eax with edx
73/jump-if-addr>= $stream-add5:abort/disp8
# *curr = val1
8b/copy 1/mod/*+disp8 5/rm32/ebp . . 1/r32/ecx 0x14/disp8 . # copy *(ebp+20) to ecx
89/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy ecx to *eax
# curr += 4
05/add-to-eax 4/imm32
# if (curr >= max) abort
39/compare 3/mod/direct 0/rm32/eax . . . 2/r32/edx . . # compare eax with edx
73/jump-if-addr>= $stream-add5:abort/disp8
# *curr = val2
8b/copy 1/mod/*+disp8 5/rm32/ebp . . 1/r32/ecx 0x18/disp8 . # copy *(ebp+24) to ecx
89/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy ecx to *eax
# curr += 4
05/add-to-eax 4/imm32
# if (curr >= max) abort
39/compare 3/mod/direct 0/rm32/eax . . . 2/r32/edx . . # compare eax with edx
73/jump-if-addr>= $stream-add5:abort/disp8
# *curr = val3
8b/copy 1/mod/*+disp8 5/rm32/ebp . . 1/r32/ecx 0x1c/disp8 . # copy *(ebp+28) to ecx
89/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy ecx to *eax
# in->write += 20
81 0/subop/add 0/mod/indirect 6/rm32/esi . . . . . 0x14/imm32 # add to *esi
$stream-add5:end:
# . restore registers
5e/pop-to-esi
5a/pop-to-edx
59/pop-to-ecx
58/pop-to-eax
# . epilogue
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
$stream-add5:abort:
# . _write(2/stderr, error)
# . . push args
68/push "overflow in stream-add5\n"/imm32
68/push 2/imm32/stderr
# . . call
e8/call _write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . syscall(exit, 1)
bb/copy-to-ebx 1/imm32
e8/call syscall_exit/disp32
# never gets here
stream-add6: # in: (addr stream byte), key: handle, val1: addr, val2: addr, val3: addr, val4: addr
# . prologue
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# . save registers
50/push-eax
51/push-ecx
52/push-edx
56/push-esi
# esi = in
8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi
# curr/eax = &in->data[in->write]
# . eax = in->write
8b/copy 0/mod/indirect 6/rm32/esi . . . 0/r32/eax . . # copy *esi to eax
# . eax = esi+eax+12
8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/esi 0/index/eax . 0/r32/eax 0xc/disp8 . # copy esi+eax+12 to eax
# max/edx = &in->data[in->size]
# . edx = in->size
8b/copy 1/mod/*+disp8 6/rm32/esi . . . 2/r32/edx 8/disp8 . # copy *(esi+8) to edx
# . edx = esi+edx+12
8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/esi 2/index/edx . 2/r32/edx 0xc/disp8 . # copy esi+edx+12 to edx
# if (curr >= max) abort
39/compare 3/mod/direct 0/rm32/eax . . . 2/r32/edx . . # compare eax with edx
73/jump-if-addr>= $stream-add6:abort/disp8
# *curr = key->alloc-id
8b/copy 1/mod/*+disp8 5/rm32/ebp . . 1/r32/ecx 0xc/disp8 . # copy *(ebp+12) to ecx
89/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy ecx to *eax
# curr += 4
05/add-to-eax 4/imm32
# if (curr >= max) abort
39/compare 3/mod/direct 0/rm32/eax . . . 2/r32/edx . . # compare eax with edx
73/jump-if-addr>= $stream-add6:abort/disp8
# *curr = key->payload
8b/copy 1/mod/*+disp8 5/rm32/ebp . . 1/r32/ecx 0x10/disp8 . # copy *(ebp+16) to ecx
89/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy ecx to *eax
# curr += 4
05/add-to-eax 4/imm32
# if (curr >= max) abort
39/compare 3/mod/direct 0/rm32/eax . . . 2/r32/edx . . # compare eax with edx
73/jump-if-addr>= $stream-add6:abort/disp8
# *curr = val1
8b/copy 1/mod/*+disp8 5/rm32/ebp . . 1/r32/ecx 0x14/disp8 . # copy *(ebp+20) to ecx
89/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy ecx to *eax
# curr += 4
05/add-to-eax 4/imm32
# if (curr >= max) abort
39/compare 3/mod/direct 0/rm32/eax . . . 2/r32/edx . . # compare eax with edx
73/jump-if-addr>= $stream-add6:abort/disp8
# *curr = val2
8b/copy 1/mod/*+disp8 5/rm32/ebp . . 1/r32/ecx 0x18/disp8 . # copy *(ebp+24) to ecx
89/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy ecx to *eax
# curr += 4
05/add-to-eax 4/imm32
# if (curr >= max) abort
39/compare 3/mod/direct 0/rm32/eax . . . 2/r32/edx . . # compare eax with edx
73/jump-if-addr>= $stream-add6:abort/disp8
$aa-write-segment-offset:
# *curr = val3
8b/copy 1/mod/*+disp8 5/rm32/ebp . . 1/r32/ecx 0x1c/disp8 . # copy *(ebp+28) to ecx
89/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy ecx to *eax
# curr += 4
05/add-to-eax 4/imm32
# if (curr >= max) abort
39/compare 3/mod/direct 0/rm32/eax . . . 2/r32/edx . . # compare eax with edx
73/jump-if-addr>= $stream-add6:abort/disp8
# *curr = val4
8b/copy 1/mod/*+disp8 5/rm32/ebp . . 1/r32/ecx 0x20/disp8 . # copy *(ebp+32) to ecx
89/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy ecx to *eax
# in->write += 24
81 0/subop/add 0/mod/indirect 6/rm32/esi . . . . . 0x18/imm32 # add to *esi
$stream-add6:end:
# . restore registers
5e/pop-to-esi
5a/pop-to-edx
59/pop-to-ecx
58/pop-to-eax
# . epilogue
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
$stream-add6:abort:
# . _write(2/stderr, error)
# . . push args
68/push "overflow in stream-add6\n"/imm32
68/push 2/imm32/stderr
# . . call
e8/call _write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . syscall(exit, 1)
bb/copy-to-ebx 1/imm32
e8/call syscall_exit/disp32
# never gets here
# some variants of 'trace' that take multiple arguments in different combinations of types:
# n: int
# c: character [4-bytes, will eventually be UTF-8]
# s: (addr array byte)
# l: (addr slice)
# one gotcha: 's5' must not be empty
trace-sssns: # s1: (addr array byte), s2: (addr array byte), s3: (addr array byte), n4: int, s5: (addr array byte)
# . prologue
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# write(*Trace-stream, s1)
# . . push args
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# write(*Trace-stream, s2)
# . . push args
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12)
ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# write(*Trace-stream, s3)
# . . push args
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16)
ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# write-int32-hex(*Trace-stream, n4)
# . . push args
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x14/disp8 . # push *(ebp+20)
ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream
# . . call
e8/call write-int32-hex/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# trace(s5) # implicitly adds a newline and finalizes the trace line
# . . push args
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x18/disp8 . # push *(ebp+24)
# . . call
e8/call trace/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
$trace-sssns:end:
# . epilogue
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
test-trace-sssns:
# . prologue
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# setup
# . *Trace-stream->write = 0
8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/eax Trace-stream/disp32 # copy *Trace-stream to eax
c7 0/subop/copy 0/mod/direct 0/rm32/eax . . . . . 0/imm32 # clear *eax
# trace-sssns("A" "b" "c " 3 " e")
# . . push args
68/push " e"/imm32
68/push 3/imm32
68/push "c "/imm32
68/push "b"/imm32
68/push "A"/imm32
# . . call
e8/call trace-sssns/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x14/imm32 # add to esp
#? # dump *Trace-stream {{{
#? # . write(2/stderr, "^")
#? # . . push args
#? 68/push "^"/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . write-stream(2/stderr, *Trace-stream)
#? # . . push args
#? ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write-stream/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . write(2/stderr, "$\n")
#? # . . push args
#? 68/push "$\n"/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # }}}
# check-trace-contains("Abc 0x00000003 e")
# . . push args
68/push "F - test-trace-sssns"/imm32
68/push "Abc 0x00000003 e"/imm32
# . . call
e8/call check-trace-contains/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . epilogue
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
trace-slsls: # s1: (addr array byte), l2: (addr slice), s3: (addr array byte), l4: (addr slice), s5: (addr array byte)
# . prologue
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# write(*Trace-stream, s1)
# . . push args
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# write-slice(*Trace-stream, l2)
# . . push args
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12)
ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream
# . . call
e8/call write-slice/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# write(*Trace-stream, s3)
# . . push args
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16)
ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# write-slice(*Trace-stream, l4)
# . . push args
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x14/disp8 . # push *(ebp+20)
ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream
# . . call
e8/call write-slice/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# trace(s5) # implicitly adds a newline and finalizes the trace line
# . . push args
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x18/disp8 . # push *(ebp+24)
# . . call
e8/call trace/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
$trace-slsls:end:
# . epilogue
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
test-trace-slsls:
# . prologue
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# setup
# . *Trace-stream->write = 0
8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/eax Trace-stream/disp32 # copy *Trace-stream to eax
c7 0/subop/copy 0/mod/direct 0/rm32/eax . . . . . 0/imm32 # clear *eax
# (eax..ecx) = "b"
b8/copy-to-eax "b"/imm32
8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx
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
05/add-to-eax 4/imm32
# var b/ebx: slice = {eax, ecx}
51/push-ecx
50/push-eax
89/copy 3/mod/direct 3/rm32/ebx . . . 4/r32/esp . . # copy esp to ebx
# (eax..ecx) = "d"
b8/copy-to-eax "d"/imm32
8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx
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
05/add-to-eax 4/imm32
# var d/edx: slice = {eax, ecx}
51/push-ecx
50/push-eax
89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx
# trace-slsls("A" b "c" d "e")
# . . push args
68/push "e"/imm32
52/push-edx
68/push "c"/imm32
53/push-ebx
68/push "A"/imm32
# . . call
e8/call trace-slsls/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x14/imm32 # add to esp
#? # dump *Trace-stream {{{
#? # . write(2/stderr, "^")
#? # . . push args
#? 68/push "^"/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . write-stream(2/stderr, *Trace-stream)
#? # . . push args
#? ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write-stream/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . write(2/stderr, "$\n")
#? # . . push args
#? 68/push "$\n"/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # }}}
# check-trace-contains("Abcde")
# . . push args
68/push "F - test-trace-slsls"/imm32
68/push "Abcde"/imm32
# . . call
e8/call check-trace-contains/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . epilogue
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
trace-slsns: # s1: (addr array byte), l2: (addr slice), s3: (addr array byte), n4: int, s5: (addr array byte)
# . prologue
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# write(*Trace-stream, s1)
# . . push args
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# write-slice(*Trace-stream, l2)
# . . push args
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12)
ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream
# . . call
e8/call write-slice/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# write(*Trace-stream, s3)
# . . push args
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16)
ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# write-int32-hex(*Trace-stream, n4)
# . . push args
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x14/disp8 . # push *(ebp+20)
ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream
# . . call
e8/call write-int32-hex/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# trace(s5) # implicitly adds a newline and finalizes the trace line
# . . push args
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x18/disp8 . # push *(ebp+24)
# . . call
e8/call trace/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
$trace-slsns:end:
# . epilogue
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
test-trace-slsns:
# . prologue
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# setup
# . *Trace-stream->write = 0
8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/eax Trace-stream/disp32 # copy *Trace-stream to eax
c7 0/subop/copy 0/mod/direct 0/rm32/eax . . . . . 0/imm32 # clear *eax
# (eax..ecx) = "b"
b8/copy-to-eax "b"/imm32
8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx
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
05/add-to-eax 4/imm32
# var b/ebx: slice = {eax, ecx}
51/push-ecx
50/push-eax
89/copy 3/mod/direct 3/rm32/ebx . . . 4/r32/esp . . # copy esp to ebx
# trace-slsls("A" b "c " 3 " e")
# . . push args
68/push " e"/imm32
68/push 3/imm32
68/push "c "/imm32
53/push-ebx
68/push "A"/imm32
# . . call
e8/call trace-slsns/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x14/imm32 # add to esp
#? # dump *Trace-stream {{{
#? # . write(2/stderr, "^")
#? # . . push args
#? 68/push "^"/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . write-stream(2/stderr, *Trace-stream)
#? # . . push args
#? ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write-stream/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . write(2/stderr, "$\n")
#? # . . push args
#? 68/push "$\n"/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # }}}
# check-trace-contains("Abc 0x00000003 e")
# . . push args
68/push "F - test-trace-slsls"/imm32
68/push "Abc 0x00000003 e"/imm32
# . . call
e8/call check-trace-contains/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . epilogue
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
trace-slsss: # s1: (addr array byte), l2: (addr slice), s3: (addr array byte), s4: (addr array byte), s5: (addr array byte)
# . prologue
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# write(*Trace-stream, s1)
# . . push args
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# write-slice(*Trace-stream, l2)
# . . push args
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12)
ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream
# . . call
e8/call write-slice/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# write(*Trace-stream, s3)
# . . push args
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16)
ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# write(*Trace-stream, s4)
# . . push args
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x14/disp8 . # push *(ebp+20)
ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# trace(s5) # implicitly adds a newline and finalizes the trace line
# . . push args
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x18/disp8 . # push *(ebp+24)
# . . call
e8/call trace/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
$trace-slsss:end:
# . epilogue
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
test-trace-slsss:
# . prologue
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# setup
# . *Trace-stream->write = 0
8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/eax Trace-stream/disp32 # copy *Trace-stream to eax
c7 0/subop/copy 0/mod/direct 0/rm32/eax . . . . . 0/imm32 # clear *eax
# (eax..ecx) = "b"
b8/copy-to-eax "b"/imm32
8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx
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
05/add-to-eax 4/imm32
# var b/ebx: slice = {eax, ecx}
51/push-ecx
50/push-eax
89/copy 3/mod/direct 3/rm32/ebx . . . 4/r32/esp . . # copy esp to ebx
# trace-slsss("A" b "c" "d" "e")
# . . push args
68/push "e"/imm32
68/push "d"/imm32
68/push "c"/imm32
53/push-ebx
68/push "A"/imm32
# . . call
e8/call trace-slsss/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x14/imm32 # add to esp
#? # dump *Trace-stream {{{
#? # . write(2/stderr, "^")
#? # . . push args
#? 68/push "^"/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . write-stream(2/stderr, *Trace-stream)
#? # . . push args
#? ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write-stream/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . write(2/stderr, "$\n")
#? # . . push args
#? 68/push "$\n"/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # }}}
# check-trace-contains("Abcde")
# . . push args
68/push "F - test-trace-slsss"/imm32
68/push "Abcde"/imm32
# . . call
e8/call check-trace-contains/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . epilogue
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
num-bytes: # line: (addr stream byte) -> eax: int
# pseudocode:
# result = 0
# while true
# var word-slice = next-word(line)
# if slice-empty?(word-slice) # end of line
# break
# if slice-starts-with?(word-slice, "#") # comment
# break
# if is-label?(word-slice) # no need for label declarations anymore
# break
# if slice-equal?(word-slice, "==")
# break # no need for segment header lines
# result += compute-width(word-slice)
# return result
#
# . prologue
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# . save registers
51/push-ecx
52/push-edx
53/push-ebx
# var result/eax = 0
31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax
# var word-slice/ecx: slice
68/push 0/imm32/end
68/push 0/imm32/start
89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx
#? # dump line {{{
#? # . write(2/stderr, "LL: ")
#? # . . push args
#? 68/push "LL: "/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # write-stream(2/stderr, line)
#? # . . push args
#? ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write-stream/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . write(2/stderr, "$\n")
#? # . . push args
#? 68/push "$\n"/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # }}}
# . rewind-stream(line)
# . . push args
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
# . . call
e8/call rewind-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
$num-bytes:loop:
# next-word(line, word-slice)
# . . push args
51/push-ecx
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
# . . call
e8/call next-word/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # dump word-slice {{{
#? # . write(2/stderr, "AA: ")
#? # . . push args
#? 68/push "AA: "/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . clear-stream($Stderr->buffer)
#? # . . push args
#? 68/push $Stderr->buffer/imm32
#? # . . call
#? e8/call clear-stream/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
#? # . write-slice-buffered(Stderr, word-slice)
#? # . . push args
#? 51/push-ecx
#? 68/push Stderr/imm32
#? # . . call
#? e8/call write-slice-buffered/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . flush(Stderr)
#? # . . push args
#? 68/push Stderr/imm32
#? # . . call
#? e8/call flush/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
#? # . write(2/stderr, "$\n")
#? # . . push args
#? 68/push "$\n"/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # }}}
$num-bytes:check0:
# if (slice-empty?(word-slice)) break
# . save result
50/push-eax
# . eax = slice-empty?(word-slice)
# . . push args
51/push-ecx
# . . call
e8/call slice-empty?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . if (eax != false) break
3d/compare-eax-and 0/imm32/false
# . restore result now that ZF is set
58/pop-to-eax
75/jump-if-!= $num-bytes:end/disp8
$num-bytes:check-for-comment:
# if (slice-starts-with?(word-slice, "#")) break
# . start/edx = word-slice->start
8b/copy 0/mod/indirect 1/rm32/ecx . . . 2/r32/edx . . # copy *ecx to edx
# . c/ebx = *start
31/xor 3/mod/direct 3/rm32/ebx . . . 3/r32/ebx . . # clear ebx
8a/copy-byte 0/mod/indirect 2/rm32/edx . . . 3/r32/BL . . # copy byte at *edx to BL
# . if (ebx == '#') break
81 7/subop/compare 3/mod/direct 3/rm32/ebx . . . . . 0x23/imm32/hash # compare ebx
74/jump-if-= $num-bytes:end/disp8
$num-bytes:check-for-label:
# if (slice-ends-with?(word-slice, ":")) break
# . end/edx = word-slice->end
8b/copy 1/mod/*+disp8 1/rm32/ecx . . . 2/r32/edx 4/disp8 . # copy *(ecx+4) to edx
# . c/ebx = *(end-1)
31/xor 3/mod/direct 3/rm32/ebx . . . 3/r32/ebx . . # clear ebx
8a/copy-byte 1/mod/*+disp8 2/rm32/edx . . . 3/r32/BL -1/disp8 . # copy byte at *ecx to BL
# . if (ebx == ':') break
81 7/subop/compare 3/mod/direct 3/rm32/ebx . . . . . 0x3a/imm32/colon # compare ebx
74/jump-if-= $num-bytes:end/disp8
$num-bytes:check-for-segment-header:
# if (slice-equal?(word-slice, "==")) break
# . push result
50/push-eax
# . eax = slice-equal?(word-slice, "==")
# . . push args
68/push "=="/imm32
51/push-ecx
# . . call
e8/call slice-equal?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . if (eax != false) break
3d/compare-eax-and 0/imm32/false
# . restore result now that ZF is set
58/pop-to-eax
75/jump-if-!= $num-bytes:end/disp8
$num-bytes:loop-body:
# result += compute-width-of-slice(word-slice)
# . copy result to edx
89/copy 3/mod/direct 2/rm32/edx . . . 0/r32/eax . . # copy eax to edx
# . eax = compute-width-of-slice(word-slice)
# . . push args
51/push-ecx
# . . call
e8/call compute-width-of-slice/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . eax += result
01/add 3/mod/direct 0/rm32/eax . . . 2/r32/edx . . # add edx to eax
e9/jump $num-bytes:loop/disp32
$num-bytes:end:
# . rewind-stream(line)
# . . push args
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
# . . call
e8/call rewind-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . reclaim locals
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# . restore registers
5b/pop-to-ebx
5a/pop-to-edx
59/pop-to-ecx
# . epilogue
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
test-num-bytes-handles-empty-string:
# if a line starts with '#', return 0
# . prologue
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# setup
# . clear-stream(_test-input-stream)
# . . push args
68/push _test-input-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . clear-stream(_test-output-stream)
# . . push args
68/push _test-output-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# no contents in input
# eax = num-bytes(_test-input-stream)
# . . push args
68/push _test-input-stream/imm32
# . . call
e8/call num-bytes/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# check-ints-equal(eax, 0, msg)
# . . push args
68/push "F - test-num-bytes-handles-empty-string"/imm32
68/push 0/imm32/true
50/push-eax
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . epilogue
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
test-num-bytes-ignores-comments:
# if a line starts with '#', return 0
# . prologue
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# setup
# . clear-stream(_test-input-stream)
# . . push args
68/push _test-input-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . clear-stream(_test-output-stream)
# . . push args
68/push _test-output-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# initialize input
# . write(_test-input-stream, "# abcd")
# . . push args
68/push "# abcd"/imm32
68/push _test-input-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# eax = num-bytes(_test-input-stream)
# . . push args
68/push _test-input-stream/imm32
# . . call
e8/call num-bytes/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# check-ints-equal(eax, 0, msg)
# . . push args
68/push "F - test-num-bytes-ignores-comments"/imm32
68/push 0/imm32/true
50/push-eax
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . epilogue
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
test-num-bytes-ignores-labels:
# if the first word ends with ':', return 0
# . prologue
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# setup
# . clear-stream(_test-input-stream)
# . . push args
68/push _test-input-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . clear-stream(_test-output-stream)
# . . push args
68/push _test-output-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# initialize input
# . write(_test-input-stream, "ab: # cd")
# . . push args
68/push "ab: # cd"/imm32
68/push _test-input-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# eax = num-bytes(_test-input-stream)
# . . push args
68/push _test-input-stream/imm32
# . . call
e8/call num-bytes/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# check-ints-equal(eax, 0, msg)
# . . push args
68/push "F - test-num-bytes-ignores-labels"/imm32
68/push 0/imm32/true
50/push-eax
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . epilogue
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
test-num-bytes-ignores-segment-headers:
# if the first word is '==', return 0
# . prologue
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# setup
# . clear-stream(_test-input-stream)
# . . push args
68/push _test-input-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . clear-stream(_test-output-stream)
# . . push args
68/push _test-output-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# initialize input
# . write(_test-input-stream, "== ab cd")
# . . push args
68/push "== ab cd"/imm32
68/push _test-input-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# eax = num-bytes(_test-input-stream)
# . . push args
68/push _test-input-stream/imm32
# . . call
e8/call num-bytes/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# check-ints-equal(eax, 0, msg)
# . . push args
68/push "F - test-num-bytes-ignores-segment-headers"/imm32
68/push 0/imm32/true
50/push-eax
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . epilogue
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
test-num-bytes-counts-words-by-default:
# without metadata, count words
# . prologue
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# setup
# . clear-stream(_test-input-stream)
# . . push args
68/push _test-input-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . clear-stream(_test-output-stream)
# . . push args
68/push _test-output-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# initialize input
# . write(_test-input-stream, "ab cd ef")
# . . push args
68/push "ab cd ef"/imm32
68/push _test-input-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# eax = num-bytes(_test-input-stream)
# . . push args
68/push _test-input-stream/imm32
# . . call
e8/call num-bytes/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# check-ints-equal(eax, 3, msg)
# . . push args
68/push "F - test-num-bytes-counts-words-by-default"/imm32
68/push 3/imm32/true
50/push-eax
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . epilogue
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
test-num-bytes-ignores-trailing-comment:
# trailing comments appropriately ignored
# . prologue
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# setup
# . clear-stream(_test-input-stream)
# . . push args
68/push _test-input-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . clear-stream(_test-output-stream)
# . . push args
68/push _test-output-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# initialize input
# . write(_test-input-stream, "ab cd # ef")
# . . push args
68/push "ab cd # ef"/imm32
68/push _test-input-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# eax = num-bytes(_test-input-stream)
# . . push args
68/push _test-input-stream/imm32
# . . call
e8/call num-bytes/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# check-ints-equal(eax, 2, msg)
# . . push args
68/push "F - test-num-bytes-ignores-trailing-comment"/imm32
68/push 2/imm32/true
50/push-eax
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . epilogue
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
test-num-bytes-handles-imm32:
# if a word has the /imm32 metadata, count it as 4 bytes
# . prologue
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# setup
# . clear-stream(_test-input-stream)
# . . push args
68/push _test-input-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# . clear-stream(_test-output-stream)
# . . push args
68/push _test-output-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# initialize input
# . write(_test-input-stream, "ab cd/imm32 ef")
# . . push args
68/push "ab cd/imm32 ef"/imm32
68/push _test-input-stream/imm32
# . . call
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# eax = num-bytes(_test-input-stream)
# . . push args
68/push _test-input-stream/imm32
# . . call
e8/call num-bytes/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# check-ints-equal(eax, 6, msg)
# . . push args
68/push "F - test-num-bytes-handles-imm32"/imm32
68/push 6/imm32/true
50/push-eax
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . epilogue
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
== data
# This block of bytes gets copied to the start of the output ELF file, with
# some fields (the ones with labels capitalized) filled in.
# http://www.sco.com/developers/gabi/latest/ch4.eheader.html
Elf_header:
# - size
0x34/imm32
# - data
$e_ident:
7f 45/E 4c/L 46/F
01/32-bit 01/little-endian 01/file-version 00/no-os-extensions
00 00 00 00 00 00 00 00 # 8 bytes of padding
$e_type:
02 00
$e_machine:
03 00
$e_version:
1/imm32
$Elf_e_entry:
0x09000000/imm32 # approximate default; must be updated
$e_phoff:
0x34/imm32 # offset for the 'program header table' containing segment headers
$e_shoff:
0/imm32 # no sections
$e_flags:
0/imm32 # unused
$e_ehsize:
0x34 00
$e_phentsize:
0x20 00
$Elf_e_phnum:
00 00 # number of segments; must be updated
$e_shentsize:
00 00 # no sections
$e_shnum:
00 00
$e_shstrndx:
00 00
# This block of bytes gets copied after the Elf_header once for each segment.
# Some fields need filling in each time.
# https://docs.oracle.com/cd/E19683-01/816-1386/chapter6-83432/index.html
Elf_program_header_entry:
# - size
0x20/imm32
# - data
$p_type:
1/imm32/PT_LOAD
$Elf_p_offset:
0/imm32 # byte offset in the file at which a segment begins; must be updated
$Elf_p_vaddr:
0/imm32 # starting address to store the segment at before running the program
$Elf_p_paddr:
0/imm32 # should have same value as $Elf_p_vaddr
$Elf_p_filesz:
0/imm32
$Elf_p_memsz:
0/imm32 # should have same value as $Elf_p_filesz
$Elf_p_flags:
6/imm32/rw- # read/write/execute permissions for the segment; must be updated for the code segment
$p_align:
# we hold this constant; changing it will require adjusting the way we
# compute the starting address for each segment
0x1000/imm32
# . . vim:nowrap:textwidth=0