f04fdc8e57
A little more resizing of buffers. apps/hex.subx is now building an identical binary. I'm now aborting on allocation failures. That requires disabling a couple of tests. (I'm not quite confident enough of this decision to delete them outright.) I want to treat all segfaults as bugs, and machine code is no place to add boilerplate checks for return values of standard library functions.
3119 lines
157 KiB
Plaintext
3119 lines
157 KiB
Plaintext
# common helpers shared by phases of the SubX translator
|
|
|
|
# - some limits on the programs we can translate
|
|
== data
|
|
|
|
# maximum memory available for allocation
|
|
Heap-size:
|
|
0x200000/imm32/2MB
|
|
|
|
# maximum size of a single segment
|
|
Segment-size:
|
|
0x80000/imm32/512KB
|
|
|
|
# maximum size of input textual stream (spanning all segments)
|
|
Input-size:
|
|
0x80000/imm32/512KB
|
|
|
|
# maximum size of the 'labels' table in survey.subx
|
|
Max-labels:
|
|
0x10000/imm32/4K-labels/64KB
|
|
|
|
== 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
|
|
|
|
# - managing tables
|
|
# SubX has rudimentary support for tables.
|
|
#
|
|
# Each table is a stream of rows.
|
|
#
|
|
# Each row consists of a 4-byte row (address to a string) and a variable-size
|
|
# value.
|
|
#
|
|
# Accessing the table performs a linear scan for a key string, and always
|
|
# requires passing in the row size.
|
|
#
|
|
# Table primitives:
|
|
# get(stream, string, row-size)
|
|
# aborts if not found
|
|
# get-or-insert(stream, string, row-size)
|
|
# inserts if not found
|
|
# get-slice(stream, slice, row-size)
|
|
# aborts if not found
|
|
# leaky-get-or-insert-slice(stream, slice, row-size)
|
|
# inserts if not found
|
|
|
|
# 'table' is a stream of (key, value) rows
|
|
# keys are always strings (addresses; size 4 bytes)
|
|
# values may be any type, but rows (key+value) always occupy 'row-size' bytes
|
|
# scan 'table' for a row with a key 'key' and return the address of the corresponding value
|
|
# if no row is found, abort
|
|
get: # table : (address stream {string, _}), key : (address string), row-size : int -> EAX : (address _)
|
|
# pseudocode:
|
|
# curr = table->data
|
|
# max = &table->data[table->write]
|
|
# while curr < max
|
|
# if string-equal?(key, *curr)
|
|
# return curr+4
|
|
# curr += row-size
|
|
# abort
|
|
#
|
|
# . prolog
|
|
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
|
|
# ESI = table
|
|
8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI
|
|
# curr/ECX = table->data
|
|
8d/copy-address 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 0xc/disp8 . # copy ESI+12 to ECX
|
|
# max/EDX = table->data + table->write
|
|
8b/copy 0/mod/indirect 6/rm32/ESI . . . 2/r32/EDX . . # copy *ESI to EDX
|
|
8d/copy-address 0/mod/indirect 4/rm32/sib 1/base/ECX 2/index/EDX . 2/r32/EDX . . # copy ECX+EDX to EDX
|
|
$get:search-loop:
|
|
# if (curr >= max) abort
|
|
39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX
|
|
73/jump-if-greater-or-equal-unsigned $get:abort/disp8
|
|
# if (string-equal?(key, *curr)) return curr+4
|
|
# . EAX = string-equal?(key, *curr)
|
|
# . . push args
|
|
ff 6/subop/push 0/mod/indirect 1/rm32/ECX . . . . . . # push *ECX
|
|
ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12)
|
|
# . . 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 != 0) return EAX = curr+4
|
|
3d/compare-EAX-and 0/imm32
|
|
74/jump-if-equal $get:mismatch/disp8
|
|
8d/copy-address 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy ECX+4 to EAX
|
|
eb/jump $get:end/disp8
|
|
$get:mismatch:
|
|
# curr += row-size
|
|
03/add 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 0x10/disp8 . # add *(EBP+16) to ECX
|
|
# loop
|
|
eb/jump $get:search-loop/disp8
|
|
$get:end:
|
|
# . restore registers
|
|
5e/pop-to-ESI
|
|
5a/pop-to-EDX
|
|
59/pop-to-ECX
|
|
# . epilog
|
|
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
|
|
5d/pop-to-EBP
|
|
c3/return
|
|
|
|
$get:abort:
|
|
# . _write(2/stderr, error)
|
|
# . . push args
|
|
68/push "get: key not found: "/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, key)
|
|
# . . push args
|
|
ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12)
|
|
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
|
|
# . syscall(exit, 1)
|
|
bb/copy-to-EBX 1/imm32
|
|
b8/copy-to-EAX 1/imm32/exit
|
|
cd/syscall 0x80/imm8
|
|
# never gets here
|
|
|
|
test-get:
|
|
# . prolog
|
|
55/push-EBP
|
|
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
|
|
# - setup: create a table with a couple of keys
|
|
# var table/ECX : (address stream {string, number}) = stream(2 rows * 8 bytes)
|
|
81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # subtract from ESP
|
|
68/push 0x10/imm32/length
|
|
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
|
|
# insert(table, "code", 8 bytes per row)
|
|
# . . push args
|
|
68/push 8/imm32/row-size
|
|
68/push "code"/imm32
|
|
51/push-ECX
|
|
# . . call
|
|
e8/call get-or-insert/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
# insert(table, "data", 8 bytes per row)
|
|
# . . push args
|
|
68/push 8/imm32/row-size
|
|
68/push "data"/imm32
|
|
51/push-ECX
|
|
# . . call
|
|
e8/call get-or-insert/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
$test-get:check1:
|
|
# EAX = get(table, "code", 8 bytes per row)
|
|
# . . push args
|
|
68/push 8/imm32/row-size
|
|
68/push "code"/imm32
|
|
51/push-ECX
|
|
# . . call
|
|
e8/call get/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
# check-ints-equal(EAX - table->data, 4, msg)
|
|
# . check-ints-equal(EAX - table, 16, msg)
|
|
# . . push args
|
|
68/push "F - test-get/0"/imm32
|
|
68/push 0x10/imm32
|
|
29/subtract 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # subtract ECX from EAX
|
|
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
|
|
$test-get:check2:
|
|
# EAX = get(table, "data", 8 bytes per row)
|
|
# . . push args
|
|
68/push 8/imm32/row-size
|
|
68/push "data"/imm32
|
|
51/push-ECX
|
|
# . . call
|
|
e8/call get/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
# check-ints-equal(EAX - table->data, 12, msg)
|
|
# . check-ints-equal(EAX - table, 24, msg)
|
|
# . . push args
|
|
68/push "F - test-get/1"/imm32
|
|
68/push 0x18/imm32
|
|
29/subtract 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # subtract ECX from EAX
|
|
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
|
|
$test-get:end:
|
|
# . epilog
|
|
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
|
|
5d/pop-to-EBP
|
|
c3/return
|
|
|
|
# 'table' is a stream of (key, value) rows
|
|
# keys are always strings (addresses; size 4 bytes)
|
|
# values may be any type, but rows (key+value) always occupy 'row-size' bytes
|
|
# scan 'table' for a row with a key 'key' and return the address of the corresponding value
|
|
# if no row is found, abort
|
|
get-slice: # table : (address stream {string, _}), key : (address slice), row-size : int -> EAX : (address _)
|
|
# pseudocode:
|
|
# curr = table->data
|
|
# max = &table->data[table->write]
|
|
# while curr < max
|
|
# if slice-equal?(key, *curr)
|
|
# return curr+4
|
|
# curr += row-size
|
|
# abort
|
|
#
|
|
# . prolog
|
|
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
|
|
# ESI = table
|
|
8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI
|
|
# curr/ECX = table->data
|
|
8d/copy-address 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 0xc/disp8 . # copy ESI+12 to ECX
|
|
# max/EDX = table->data + table->write
|
|
8b/copy 0/mod/indirect 6/rm32/ESI . . . 2/r32/EDX . . # copy *ESI to EDX
|
|
8d/copy-address 0/mod/indirect 4/rm32/sib 1/base/ECX 2/index/EDX . 2/r32/EDX . . # copy ECX+EDX to EDX
|
|
$get-slice:search-loop:
|
|
# if (curr >= max) abort
|
|
39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX
|
|
73/jump-if-greater-or-equal-unsigned $get-slice:abort/disp8
|
|
# if (slice-equal?(key, *curr)) return curr+4
|
|
# . EAX = slice-equal?(key, *curr)
|
|
# . . push args
|
|
ff 6/subop/push 0/mod/indirect 1/rm32/ECX . . . . . . # push *ECX
|
|
ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12)
|
|
# . . 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 != 0) return EAX = curr+4
|
|
3d/compare-EAX-and 0/imm32
|
|
74/jump-if-equal $get-slice:mismatch/disp8
|
|
8d/copy-address 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy ECX+4 to EAX
|
|
eb/jump $get-slice:end/disp8
|
|
$get-slice:mismatch:
|
|
# curr += row-size
|
|
03/add 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 0x10/disp8 . # add *(EBP+16) to ECX
|
|
# loop
|
|
eb/jump $get-slice:search-loop/disp8
|
|
$get-slice:end:
|
|
# . restore registers
|
|
5e/pop-to-ESI
|
|
5a/pop-to-EDX
|
|
59/pop-to-ECX
|
|
# . epilog
|
|
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
|
|
5d/pop-to-EBP
|
|
c3/return
|
|
|
|
$get-slice:abort:
|
|
# . _write(2/stderr, error)
|
|
# . . push args
|
|
68/push "get-slice: key not found: "/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, key)
|
|
# . . push args
|
|
ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12)
|
|
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
|
|
# . syscall(exit, 1)
|
|
bb/copy-to-EBX 1/imm32
|
|
b8/copy-to-EAX 1/imm32/exit
|
|
cd/syscall 0x80/imm8
|
|
# never gets here
|
|
|
|
test-get-slice:
|
|
# . prolog
|
|
55/push-EBP
|
|
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
|
|
# - setup: create a table with a couple of keys
|
|
# var table/ECX : (address stream {string, number}) = stream(2 rows * 8 bytes)
|
|
81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # subtract from ESP
|
|
68/push 0x10/imm32/length
|
|
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
|
|
# insert(table, "code", 8 bytes per row)
|
|
# . . push args
|
|
68/push 8/imm32/row-size
|
|
68/push "code"/imm32
|
|
51/push-ECX
|
|
# . . call
|
|
e8/call get-or-insert/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
# insert(table, "data", 8 bytes per row)
|
|
# . . push args
|
|
68/push 8/imm32/row-size
|
|
68/push "data"/imm32
|
|
51/push-ECX
|
|
# . . call
|
|
e8/call get-or-insert/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
$test-get-slice:check1:
|
|
# (EAX..EDX) = "code"
|
|
b8/copy-to-EAX "code"/imm32
|
|
8b/copy 0/mod/indirect 0/rm32/EAX . . . 2/r32/EDX . . # copy *EAX to EDX
|
|
8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 2/index/EDX . 2/r32/EDX 4/disp8 . # copy EAX+EDX+4 to EDX
|
|
05/add-to-EAX 4/imm32
|
|
# var slice/EDX = {EAX, EDX}
|
|
52/push-EDX
|
|
50/push-EAX
|
|
89/copy 3/mod/direct 2/rm32/EDX . . . 4/r32/ESP . . # copy ESP to EDX
|
|
# EAX = get-slice(table, "code", 8 bytes per row)
|
|
# . . push args
|
|
68/push 8/imm32/row-size
|
|
52/push-EDX
|
|
51/push-ECX
|
|
# . . call
|
|
e8/call get-slice/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
# check-ints-equal(EAX - table->data, 4, msg) # first row's value slot returned
|
|
# . check-ints-equal(EAX - table, 16, msg)
|
|
# . . push args
|
|
68/push "F - test-get-slice/0"/imm32
|
|
68/push 0x10/imm32
|
|
29/subtract 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # subtract ECX from EAX
|
|
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
|
|
$test-get-slice:check2:
|
|
# (EAX..EDX) = "data"
|
|
b8/copy-to-EAX "data"/imm32
|
|
8b/copy 0/mod/indirect 0/rm32/EAX . . . 2/r32/EDX . . # copy *EAX to EDX
|
|
8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 2/index/EDX . 2/r32/EDX 4/disp8 . # copy EAX+EDX+4 to EDX
|
|
05/add-to-EAX 4/imm32
|
|
# var slice/EDX = {EAX, EDX}
|
|
52/push-EDX
|
|
50/push-EAX
|
|
89/copy 3/mod/direct 2/rm32/EDX . . . 4/r32/ESP . . # copy ESP to EDX
|
|
# EAX = get-slice(table, "data" slice, 8 bytes per row)
|
|
# . . push args
|
|
68/push 8/imm32/row-size
|
|
52/push-EDX
|
|
51/push-ECX
|
|
# . . call
|
|
e8/call get-slice/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
# check-ints-equal(EAX - table->data, 12, msg)
|
|
# . check-ints-equal(EAX - table, 24, msg)
|
|
# . . push args
|
|
68/push "F - test-get-slice/1"/imm32
|
|
68/push 0x18/imm32
|
|
29/subtract 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # subtract ECX from EAX
|
|
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
|
|
$test-get-slice:end:
|
|
# . epilog
|
|
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
|
|
5d/pop-to-EBP
|
|
c3/return
|
|
|
|
# 'table' is a stream of (key, value) rows
|
|
# keys are always strings (addresses; size 4 bytes)
|
|
# values may be any type, but rows (key+value) always occupy 'row-size' bytes
|
|
# scan 'table' for a row with a key 'key' and return the address of the corresponding value
|
|
# if no row is found, save 'key' to the next available row
|
|
# if there are no rows free, abort
|
|
# return the address of the value
|
|
# Beware: assume keys are immutable; they're inserted by reference
|
|
# TODO: pass in an allocation descriptor
|
|
get-or-insert: # table : (address stream {string, _}), key : (address string), row-size : int -> EAX : (address _)
|
|
# pseudocode:
|
|
# curr = table->data
|
|
# max = &table->data[table->write]
|
|
# while curr < max
|
|
# if string-equal?(key, *curr)
|
|
# return curr+4
|
|
# curr += row-size
|
|
# if table->write >= table->length
|
|
# abort
|
|
# zero-out(max, row-size)
|
|
# *max = key
|
|
# table->write += row-size
|
|
# return max+4
|
|
#
|
|
# . prolog
|
|
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
|
|
# ESI = table
|
|
8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI
|
|
# curr/ECX = table->data
|
|
8d/copy-address 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 0xc/disp8 . # copy ESI+12 to ECX
|
|
# max/EDX = table->data + table->write
|
|
8b/copy 0/mod/indirect 6/rm32/ESI . . . 2/r32/EDX . . # copy *ESI to EDX
|
|
8d/copy-address 0/mod/indirect 4/rm32/sib 1/base/ECX 2/index/EDX . 2/r32/EDX . . # copy ECX+EDX to EDX
|
|
$get-or-insert:search-loop:
|
|
# if (curr >= max) break
|
|
39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX
|
|
73/jump-if-greater-or-equal-unsigned $get-or-insert:not-found/disp8
|
|
# if (string-equal?(key, *curr)) return curr+4
|
|
# . EAX = string-equal?(key, *curr)
|
|
# . . push args
|
|
ff 6/subop/push 0/mod/indirect 1/rm32/ECX . . . . . . # push *ECX
|
|
ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12)
|
|
# . . 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 != 0) return EAX = curr+4
|
|
3d/compare-EAX-and 0/imm32
|
|
74/jump-if-equal $get-or-insert:mismatch/disp8
|
|
8d/copy-address 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy ECX+4 to EAX
|
|
eb/jump $get-or-insert:end/disp8
|
|
$get-or-insert:mismatch:
|
|
# curr += row-size
|
|
03/add 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 0x10/disp8 . # add *(EBP+16) to ECX
|
|
# loop
|
|
eb/jump $get-or-insert:search-loop/disp8
|
|
$get-or-insert:not-found:
|
|
# result/EAX = 0
|
|
31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX
|
|
# if (table->write >= table->length) abort
|
|
8b/copy 0/mod/indirect 6/rm32/ESI . . . 1/r32/ECX . . # copy *ESI to ECX
|
|
3b/compare 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 8/disp8 . # compare ECX with *(ESI+8)
|
|
73/jump-if-greater-or-equal-unsigned $get-or-insert:abort/disp8
|
|
# zero-out(max, row-size)
|
|
# . . push args
|
|
ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16)
|
|
52/push-EDX
|
|
# . . call
|
|
e8/call zero-out/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
|
|
# *max = key
|
|
# . EAX = key
|
|
8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 0xc/disp8 . # copy *(EBP+12) to EAX
|
|
# . *max = EAX
|
|
89/copy 0/mod/indirect 2/rm32/EDX . . . 0/r32/EAX . . # copy EAX to *EDX
|
|
# table->write += row-size
|
|
# . EAX = row-size
|
|
8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 0x10/disp8 . # copy *(EBP+16) to EAX
|
|
# . table->write += EAX
|
|
01/add 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # add EAX to *ESI
|
|
# return max+4
|
|
# . EAX = max
|
|
89/copy 3/mod/direct 0/rm32/EAX . . . 2/r32/EDX . . # copy EDX to EAX
|
|
# . EAX += 4
|
|
05/add-to-EAX 4/imm32
|
|
$get-or-insert:end:
|
|
# . restore registers
|
|
5e/pop-to-ESI
|
|
5a/pop-to-EDX
|
|
59/pop-to-ECX
|
|
# . epilog
|
|
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
|
|
5d/pop-to-EBP
|
|
c3/return
|
|
|
|
$get-or-insert:abort:
|
|
# . _write(2/stderr, error)
|
|
# . . push args
|
|
68/push "get-or-insert: table is full\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
|
|
b8/copy-to-EAX 1/imm32/exit
|
|
cd/syscall 0x80/imm8
|
|
# never gets here
|
|
|
|
test-get-or-insert:
|
|
# . prolog
|
|
55/push-EBP
|
|
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
|
|
# var table/ECX : (address stream {string, number}) = stream(2 rows * 8 bytes)
|
|
81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # subtract from ESP
|
|
68/push 0x10/imm32/length
|
|
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
|
|
$test-get-or-insert:first-call:
|
|
# - start with an empty table, insert one key, verify that it was inserted
|
|
# EAX = get-or-insert(table, "code", 8 bytes per row)
|
|
# . . push args
|
|
68/push 8/imm32/row-size
|
|
68/push "code"/imm32
|
|
51/push-ECX
|
|
# . . call
|
|
e8/call get-or-insert/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
# check-ints-equal(EAX - table->data, 4, msg) # first row's value slot returned
|
|
# . check-ints-equal(EAX - table, 16, msg)
|
|
# . . push args
|
|
68/push "F - test-get-or-insert/0"/imm32
|
|
68/push 0x10/imm32
|
|
29/subtract 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # subtract ECX from EAX
|
|
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
|
|
$test-get-or-insert:check2:
|
|
# check-ints-equal(table->write, row-size = 8, msg)
|
|
# . . push args
|
|
68/push "F - test-get-or-insert/1"/imm32
|
|
68/push 8/imm32/row-size
|
|
ff 6/subop/push 0/mod/indirect 1/rm32/ECX . . . . . . # push *ECX
|
|
# . . call
|
|
e8/call check-ints-equal/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
# check-string-equal(*table->data, "code", msg)
|
|
# . . push args
|
|
68/push "F - test-get-or-insert/2"/imm32
|
|
68/push "code"/imm32
|
|
ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 0xc/disp8 . # push *(ECX+12)
|
|
# . . call
|
|
e8/call check-string-equal/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
$test-get-or-insert:second-call:
|
|
# - insert the same key again, verify that it was reused
|
|
# EAX = get-or-insert(table, "code", 8 bytes per row)
|
|
# . . push args
|
|
68/push 8/imm32/row-size
|
|
68/push "code"/imm32
|
|
51/push-ECX
|
|
# . . call
|
|
e8/call get-or-insert/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
# check-ints-equal(EAX - table->data, 4, msg)
|
|
# . check-ints-equal(EAX - table, 16, msg)
|
|
# . . push args
|
|
68/push "F - test-get-or-insert/3"/imm32
|
|
68/push 0x10/imm32
|
|
29/subtract 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # subtract ECX from EAX
|
|
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
|
|
# no new row inserted
|
|
# . check-ints-equal(table->write, row-size = 8, msg)
|
|
# . . push args
|
|
68/push "F - test-get-or-insert/4"/imm32
|
|
68/push 8/imm32/row-size
|
|
ff 6/subop/push 0/mod/indirect 1/rm32/ECX . . . . . . # push *ECX
|
|
# . . call
|
|
e8/call check-ints-equal/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
# check-string-equal(*table->data, "code", msg)
|
|
# . . push args
|
|
68/push "F - test-get-or-insert/5"/imm32
|
|
68/push "code"/imm32
|
|
ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 0xc/disp8 . # push *(ECX+12)
|
|
# . . call
|
|
e8/call check-string-equal/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
$test-get-or-insert:third-call:
|
|
# - insert a new key, verify that it was inserted
|
|
# EAX = get-or-insert(table, "data", 8 bytes per row)
|
|
# . . push args
|
|
68/push 8/imm32/row-size
|
|
68/push "data"/imm32
|
|
51/push-ECX
|
|
# . . call
|
|
e8/call get-or-insert/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
# table gets a new row
|
|
# check-ints-equal(EAX - table->data, 12, msg) # second row's value slot returned
|
|
# . check-ints-equal(EAX - table, 24, msg)
|
|
# . . push args
|
|
68/push "F - test-get-or-insert/6"/imm32
|
|
68/push 0x18/imm32
|
|
29/subtract 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # subtract ECX from EAX
|
|
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
|
|
# check-ints-equal(table->write, 2 rows = 16, msg)
|
|
# . . push args
|
|
68/push "F - test-get-or-insert/7"/imm32
|
|
68/push 0x10/imm32/two-rows
|
|
ff 6/subop/push 0/mod/indirect 1/rm32/ECX . . . . . . # push *ECX
|
|
# . . call
|
|
e8/call check-ints-equal/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
# check-string-equal(*table->data+8, "data", msg)
|
|
# check-string-equal(*(table+20), "data", msg)
|
|
# . . push args
|
|
68/push "F - test-get-or-insert/8"/imm32
|
|
68/push "data"/imm32
|
|
ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 0x14/disp8 . # push *(ECX+20)
|
|
# . . call
|
|
e8/call check-string-equal/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
$test-get-or-insert:end:
|
|
# . epilog
|
|
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
|
|
5d/pop-to-EBP
|
|
c3/return
|
|
|
|
# 'table' is a stream of (key, value) rows
|
|
# keys are always strings (addresses; size 4 bytes)
|
|
# values may be any type, but rows (key+value) always occupy 'row-size' bytes
|
|
# scan 'table' for a row with a key 'key' and return the address of the corresponding value
|
|
# if no row is found, save 'key' in the next available row
|
|
# if there are no rows free, abort
|
|
# WARNING: leaks memory
|
|
# TODO: pass in an allocation descriptor
|
|
leaky-get-or-insert-slice: # table : (address stream {string, _}), key : (address slice), row-size : int -> EAX : (address _)
|
|
# pseudocode:
|
|
# curr = table->data
|
|
# max = &table->data[table->write]
|
|
# while curr < max
|
|
# if slice-equal?(key, *curr)
|
|
# return curr+4
|
|
# curr += row-size
|
|
# if table->write >= table->length
|
|
# abort
|
|
# zero-out(max, row-size)
|
|
# *max = slice-to-string(Heap, key)
|
|
# table->write += row-size
|
|
# return max+4
|
|
#
|
|
# . prolog
|
|
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
|
|
# ESI = table
|
|
8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI
|
|
# curr/ECX = table->data
|
|
8d/copy-address 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 0xc/disp8 . # copy ESI+12 to ECX
|
|
# max/EDX = table->data + table->write
|
|
8b/copy 0/mod/indirect 6/rm32/ESI . . . 2/r32/EDX . . # copy *ESI to EDX
|
|
8d/copy-address 0/mod/indirect 4/rm32/sib 1/base/ECX 2/index/EDX . 2/r32/EDX . . # copy ECX+EDX to EDX
|
|
$leaky-get-or-insert-slice:search-loop:
|
|
# if (curr >= max) break
|
|
39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX
|
|
73/jump-if-greater-or-equal-unsigned $leaky-get-or-insert-slice:not-found/disp8
|
|
# if (slice-equal?(key, *curr)) return curr+4
|
|
# . EAX = slice-equal?(key, *curr)
|
|
# . . push args
|
|
ff 6/subop/push 0/mod/indirect 1/rm32/ECX . . . . . . # push *ECX
|
|
ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12)
|
|
# . . 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 != 0) return EAX = curr+4
|
|
3d/compare-EAX-and 0/imm32
|
|
74/jump-if-equal $leaky-get-or-insert-slice:mismatch/disp8
|
|
8d/copy-address 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy ECX+4 to EAX
|
|
eb/jump $leaky-get-or-insert-slice:end/disp8
|
|
$leaky-get-or-insert-slice:mismatch:
|
|
# curr += row-size
|
|
03/add 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 0x10/disp8 . # add *(EBP+16) to ECX
|
|
# loop
|
|
eb/jump $leaky-get-or-insert-slice:search-loop/disp8
|
|
$leaky-get-or-insert-slice:not-found:
|
|
# result/EAX = 0
|
|
31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX
|
|
# if (table->write >= table->length) abort
|
|
8b/copy 0/mod/indirect 6/rm32/ESI . . . 1/r32/ECX . . # copy *ESI to ECX
|
|
3b/compare 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 8/disp8 . # compare ECX with *(ESI+8)
|
|
7d/jump-if-greater-or-equal $leaky-get-or-insert-slice:abort/disp8
|
|
# zero-out(max, row-size)
|
|
# . . push args
|
|
ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16)
|
|
52/push-EDX
|
|
# . . call
|
|
e8/call zero-out/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
|
|
# *max = slice-to-string(Heap, key)
|
|
# . EAX = slice-to-string(Heap, key)
|
|
# . . push args
|
|
ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12)
|
|
68/push Heap/imm32
|
|
# . . call
|
|
e8/call slice-to-string/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
|
|
# . *max = EAX
|
|
89/copy 0/mod/indirect 2/rm32/EDX . . . 0/r32/EAX . . # copy EAX to *EDX
|
|
# table->write += row-size
|
|
# . EAX = row-size
|
|
8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 0x10/disp8 . # copy *(EBP+16) to EAX
|
|
# . table->write += EAX
|
|
01/add 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # add EAX to *ESI
|
|
# return max+4
|
|
# . EAX = max
|
|
89/copy 3/mod/direct 0/rm32/EAX . . . 2/r32/EDX . . # copy EDX to EAX
|
|
# . EAX += 4
|
|
05/add-to-EAX 4/imm32
|
|
$leaky-get-or-insert-slice:end:
|
|
# . restore registers
|
|
5e/pop-to-ESI
|
|
5a/pop-to-EDX
|
|
59/pop-to-ECX
|
|
# . epilog
|
|
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
|
|
5d/pop-to-EBP
|
|
c3/return
|
|
|
|
$leaky-get-or-insert-slice:abort:
|
|
# . _write(2/stderr, error)
|
|
# . . push args
|
|
68/push "leaky-get-or-insert-slice: table is full\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
|
|
b8/copy-to-EAX 1/imm32/exit
|
|
cd/syscall 0x80/imm8
|
|
# never gets here
|
|
|
|
test-leaky-get-or-insert-slice:
|
|
# . prolog
|
|
55/push-EBP
|
|
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
|
|
# var table/ECX : (address stream {string, number}) = stream(2 rows * 8 bytes)
|
|
81 5/subop/subtract 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # subtract from ESP
|
|
68/push 0x10/imm32/length
|
|
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
|
|
# (EAX..EDX) = "code"
|
|
b8/copy-to-EAX "code"/imm32
|
|
8b/copy 0/mod/indirect 0/rm32/EAX . . . 2/r32/EDX . . # copy *EAX to EDX
|
|
8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 2/index/EDX . 2/r32/EDX 4/disp8 . # copy EAX+EDX+4 to EDX
|
|
05/add-to-EAX 4/imm32
|
|
# var slice/EDX = {EAX, EDX}
|
|
52/push-EDX
|
|
50/push-EAX
|
|
89/copy 3/mod/direct 2/rm32/EDX . . . 4/r32/ESP . . # copy ESP to EDX
|
|
$test-leaky-get-or-insert-slice:first-call:
|
|
# - start with an empty table, insert one key, verify that it was inserted
|
|
# EAX = leaky-get-or-insert-slice(table, "code" slice, 8 bytes per row)
|
|
# . . push args
|
|
68/push 8/imm32/row-size
|
|
52/push-EDX
|
|
51/push-ECX
|
|
# . . call
|
|
e8/call leaky-get-or-insert-slice/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
# check-ints-equal(EAX - table->data, 4, msg) # first row's value slot returned
|
|
# . check-ints-equal(EAX - table, 16, msg)
|
|
# . . push args
|
|
68/push "F - test-leaky-get-or-insert-slice/0"/imm32
|
|
68/push 0x10/imm32
|
|
29/subtract 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # subtract ECX from EAX
|
|
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
|
|
$test-leaky-get-or-insert-slice:check2:
|
|
# check-ints-equal(table->write, row-size = 8, msg)
|
|
# . . push args
|
|
68/push "F - test-leaky-get-or-insert-slice/1"/imm32
|
|
68/push 8/imm32/row-size
|
|
ff 6/subop/push 0/mod/indirect 1/rm32/ECX . . . . . . # push *ECX
|
|
# . . call
|
|
e8/call check-ints-equal/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
# check-string-equal(*table->data, "code", msg)
|
|
# . . push args
|
|
68/push "F - test-leaky-get-or-insert-slice/2"/imm32
|
|
68/push "code"/imm32
|
|
ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 0xc/disp8 . # push *(ECX+12)
|
|
# . . call
|
|
e8/call check-string-equal/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
$test-leaky-get-or-insert-slice:second-call:
|
|
# - insert the same key again, verify that it was reused
|
|
# EAX = leaky-get-or-insert-slice(table, "code" slice, 8 bytes per row)
|
|
# . . push args
|
|
68/push 8/imm32/row-size
|
|
52/push-EDX
|
|
51/push-ECX
|
|
# . . call
|
|
e8/call leaky-get-or-insert-slice/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
# check-ints-equal(EAX - table->data, 4, msg)
|
|
# . check-ints-equal(EAX - table, 16, msg)
|
|
# . . push args
|
|
68/push "F - test-leaky-get-or-insert-slice/3"/imm32
|
|
68/push 0x10/imm32
|
|
29/subtract 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # subtract ECX from EAX
|
|
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
|
|
# no new row inserted
|
|
# . check-ints-equal(table->write, row-size = 8, msg)
|
|
# . . push args
|
|
68/push "F - test-leaky-get-or-insert-slice/4"/imm32
|
|
68/push 8/imm32/row-size
|
|
ff 6/subop/push 0/mod/indirect 1/rm32/ECX . . . . . . # push *ECX
|
|
# . . call
|
|
e8/call check-ints-equal/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
# check-string-equal(*table->data, "code", msg)
|
|
# . . push args
|
|
68/push "F - test-leaky-get-or-insert-slice/5"/imm32
|
|
68/push "code"/imm32
|
|
ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 0xc/disp8 . # push *(ECX+12)
|
|
# . . call
|
|
e8/call check-string-equal/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
$test-leaky-get-or-insert-slice:third-call:
|
|
# - insert a new key, verify that it was inserted
|
|
# (EAX..EDX) = "data"
|
|
b8/copy-to-EAX "data"/imm32
|
|
8b/copy 0/mod/indirect 0/rm32/EAX . . . 2/r32/EDX . . # copy *EAX to EDX
|
|
8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/EAX 2/index/EDX . 2/r32/EDX 4/disp8 . # copy EAX+EDX+4 to EDX
|
|
05/add-to-EAX 4/imm32
|
|
# var slice/EDX = {EAX, EDX}
|
|
52/push-EDX
|
|
50/push-EAX
|
|
89/copy 3/mod/direct 2/rm32/EDX . . . 4/r32/ESP . . # copy ESP to EDX
|
|
# EAX = leaky-get-or-insert-slice(table, "data" slice, 8 bytes per row)
|
|
# . . push args
|
|
68/push 8/imm32/row-size
|
|
52/push-EDX
|
|
51/push-ECX
|
|
# . . call
|
|
e8/call leaky-get-or-insert-slice/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
# table gets a new row
|
|
# check-ints-equal(EAX - table->data, 12, msg) # second row's value slot returned
|
|
# . check-ints-equal(EAX - table, 24, msg)
|
|
# . . push args
|
|
68/push "F - test-leaky-get-or-insert-slice/6"/imm32
|
|
68/push 0x18/imm32
|
|
29/subtract 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # subtract ECX from EAX
|
|
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
|
|
# check-ints-equal(table->write, 2 rows = 16, msg)
|
|
# . . push args
|
|
68/push "F - test-leaky-get-or-insert-slice/7"/imm32
|
|
68/push 0x10/imm32/two-rows
|
|
ff 6/subop/push 0/mod/indirect 1/rm32/ECX . . . . . . # push *ECX
|
|
# . . call
|
|
e8/call check-ints-equal/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
# check-string-equal(*table->data+8, "data", msg)
|
|
# check-string-equal(*(table+20), "data", msg)
|
|
# . . push args
|
|
68/push "F - test-leaky-get-or-insert-slice/8"/imm32
|
|
68/push "data"/imm32
|
|
ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 0x14/disp8 . # push *(ECX+20)
|
|
# . . call
|
|
e8/call check-string-equal/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
$test-leaky-get-or-insert-slice:end:
|
|
# . epilog
|
|
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
|
|
5d/pop-to-EBP
|
|
c3/return
|
|
|
|
# (re)compute the bounds of the next word in the line
|
|
# return empty string on reaching end of file
|
|
next-word: # line : (address stream byte), out : (address slice)
|
|
# . prolog
|
|
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
|
|
56/push-ESI
|
|
57/push-EDI
|
|
# ESI = line
|
|
8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI
|
|
# EDI = out
|
|
8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI
|
|
# skip-chars-matching(line, ' ')
|
|
# . . push args
|
|
68/push 0x20/imm32/space
|
|
ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8)
|
|
# . . call
|
|
e8/call skip-chars-matching/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
|
|
$next-word:check0:
|
|
# if (line->read >= line->write) clear out and return
|
|
# . EAX = line->read
|
|
8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 4/disp8 . # copy *(ESI+4) to EAX
|
|
# . if (EAX < line->write) goto next check
|
|
3b/compare 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # compare EAX with *ESI
|
|
7c/jump-if-lesser $next-word:check-for-comment/disp8
|
|
# . return out = {0, 0}
|
|
c7 0/subop/copy 0/mod/direct 7/rm32/EDI . . . . . 0/imm32 # copy to *EDI
|
|
c7 0/subop/copy 1/mod/*+disp8 7/rm32/EDI . . . . 4/disp8 0/imm32 # copy to *(EDI+4)
|
|
eb/jump $next-word:end/disp8
|
|
$next-word:check-for-comment:
|
|
# out->start = &line->data[line->read]
|
|
8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # copy *(ESI+4) to ECX
|
|
8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/EAX 0xc/disp8 . # copy ESI+ECX+12 to EAX
|
|
89/copy 0/mod/indirect 7/rm32/EDI . . . 0/r32/EAX . . # copy EAX to *EDI
|
|
# if (line->data[line->read] == '#') out->end = &line->data[line->write]), skip rest of stream and return
|
|
# . EAX = line->data[line->read]
|
|
31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX
|
|
8a/copy-byte 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/AL 0xc/disp8 . # copy byte at *(ESI+ECX+12) to AL
|
|
# . compare
|
|
3d/compare-EAX-and 0x23/imm32/pound
|
|
75/jump-if-not-equal $next-word:regular-word/disp8
|
|
$next-word:comment:
|
|
# . out->end = &line->data[line->write]
|
|
8b/copy 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # copy *ESI to EAX
|
|
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
|
|
89/copy 1/mod/*+disp8 7/rm32/EDI . . . 0/r32/EAX 4/disp8 . # copy EAX to *(EDI+4)
|
|
# . line->read = line->write
|
|
89/copy 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 4/disp8 . # copy EAX to *(ESI+4)
|
|
# . return
|
|
eb/jump $next-word:end/disp8
|
|
$next-word:regular-word:
|
|
# otherwise skip-chars-not-matching-whitespace(line) # including trailing newline
|
|
# . . push args
|
|
ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8)
|
|
# . . call
|
|
e8/call skip-chars-not-matching-whitespace/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
|
|
# out->end = &line->data[line->read]
|
|
8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # copy *(ESI+4) to ECX
|
|
8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 1/index/ECX . 0/r32/EAX 0xc/disp8 . # copy ESI+ECX+12 to EAX
|
|
89/copy 1/mod/*+disp8 7/rm32/EDI . . . 0/r32/EAX 4/disp8 . # copy EAX to *(EDI+4)
|
|
$next-word:end:
|
|
# . restore registers
|
|
5f/pop-to-EDI
|
|
5e/pop-to-ESI
|
|
59/pop-to-ECX
|
|
58/pop-to-EAX
|
|
# . epilog
|
|
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
|
|
5d/pop-to-EBP
|
|
c3/return
|
|
|
|
test-next-word:
|
|
# . prolog
|
|
55/push-EBP
|
|
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
|
|
# setup
|
|
# . clear-stream(_test-stream)
|
|
# . . push args
|
|
68/push _test-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 slice/ECX = {0, 0}
|
|
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
|
|
# write(_test-stream, " ab")
|
|
# . . push args
|
|
68/push " ab"/imm32
|
|
68/push _test-stream/imm32
|
|
# . . call
|
|
e8/call write/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
|
|
# next-word(_test-stream, slice)
|
|
# . . push args
|
|
51/push-ECX
|
|
68/push _test-stream/imm32
|
|
# . . call
|
|
e8/call next-word/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
|
|
# check-ints-equal(slice->start - _test-stream->data, 2, msg)
|
|
# . check-ints-equal(slice->start - _test-stream, 14, msg)
|
|
# . . push args
|
|
68/push "F - test-next-word: start"/imm32
|
|
68/push 0xe/imm32
|
|
# . . push slice->start - _test-stream
|
|
8b/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy *ECX to EAX
|
|
81 5/subop/subtract 3/mod/direct 0/rm32/EAX . . . . . _test-stream/imm32 # subtract from EAX
|
|
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
|
|
# check-ints-equal(slice->end - _test-stream->data, 4, msg)
|
|
# . check-ints-equal(slice->end - _test-stream, 16, msg)
|
|
# . . push args
|
|
68/push "F - test-next-word: end"/imm32
|
|
68/push 0x10/imm32
|
|
# . . push slice->end - _test-stream
|
|
8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy *(ECX+4) to EAX
|
|
81 5/subop/subtract 3/mod/direct 0/rm32/EAX . . . . . _test-stream/imm32 # subtract from EAX
|
|
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
|
|
# . epilog
|
|
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
|
|
5d/pop-to-EBP
|
|
c3/return
|
|
|
|
test-next-word-returns-whole-comment:
|
|
# . prolog
|
|
55/push-EBP
|
|
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
|
|
# setup
|
|
# . clear-stream(_test-stream)
|
|
# . . push args
|
|
68/push _test-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 slice/ECX = {0, 0}
|
|
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
|
|
# write(_test-stream, " # a")
|
|
# . . push args
|
|
68/push " # a"/imm32
|
|
68/push _test-stream/imm32
|
|
# . . call
|
|
e8/call write/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
|
|
# next-word(_test-stream, slice)
|
|
# . . push args
|
|
51/push-ECX
|
|
68/push _test-stream/imm32
|
|
# . . call
|
|
e8/call next-word/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
|
|
# check-ints-equal(slice->start - _test-stream->data, 2, msg)
|
|
# . check-ints-equal(slice->start - _test-stream, 14, msg)
|
|
# . . push args
|
|
68/push "F - test-next-word-returns-whole-comment: start"/imm32
|
|
68/push 0xe/imm32
|
|
# . . push slice->start - _test-stream
|
|
8b/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy *ECX to EAX
|
|
81 5/subop/subtract 3/mod/direct 0/rm32/EAX . . . . . _test-stream/imm32 # subtract from EAX
|
|
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
|
|
# check-ints-equal(slice->end - _test-stream->data, 5, msg)
|
|
# . check-ints-equal(slice->end - _test-stream, 17, msg)
|
|
# . . push args
|
|
68/push "F - test-next-word-returns-whole-comment: end"/imm32
|
|
68/push 0x11/imm32
|
|
# . . push slice->end - _test-stream
|
|
8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy *(ECX+4) to EAX
|
|
81 5/subop/subtract 3/mod/direct 0/rm32/EAX . . . . . _test-stream/imm32 # subtract from EAX
|
|
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
|
|
# . epilog
|
|
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
|
|
5d/pop-to-EBP
|
|
c3/return
|
|
|
|
test-next-word-returns-empty-string-on-eof:
|
|
# . prolog
|
|
55/push-EBP
|
|
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
|
|
# setup
|
|
# . clear-stream(_test-stream)
|
|
# . . push args
|
|
68/push _test-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 slice/ECX = {0, 0}
|
|
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
|
|
# write nothing to _test-stream
|
|
# next-word(_test-stream, slice)
|
|
# . . push args
|
|
51/push-ECX
|
|
68/push _test-stream/imm32
|
|
# . . call
|
|
e8/call next-word/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
|
|
# check-ints-equal(slice->end - slice->start, 0, msg)
|
|
# . . push args
|
|
68/push "F - test-next-word-returns-empty-string-on-eof"/imm32
|
|
68/push 0/imm32
|
|
# . . push slice->end - slice->start
|
|
8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy *(ECX+4) to EAX
|
|
2b/subtract 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # subtract *ECX from EAX
|
|
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
|
|
# . epilog
|
|
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
|
|
5d/pop-to-EBP
|
|
c3/return
|
|
|
|
# write an entire stream's contents to a buffered-file
|
|
# ways to do this:
|
|
# - construct a 'maximal slice' and pass it to write-slice-buffered
|
|
# - flush the buffered-file and pass the stream directly to its fd (disabling buffering)
|
|
# we'll go with the first way for now
|
|
write-stream-data: # f : (address buffered-file), s : (address stream) -> <void>
|
|
# . prolog
|
|
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
|
|
56/push-ESI
|
|
# ESI = s
|
|
8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI
|
|
# var slice/ECX = {s->data, s->data + s->write}
|
|
# . push s->data + s->write
|
|
8b/copy 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # copy *ESI to EAX
|
|
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
|
|
50/push-EAX
|
|
# . push s->data
|
|
8d/copy-address 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 0xc/disp8 . # copy ESI+12 to EAX
|
|
50/push-EAX
|
|
# . ECX = ESP
|
|
89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX
|
|
# write-slice-buffered(f, slice)
|
|
# . . push args
|
|
51/push-ECX
|
|
ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8)
|
|
# . . 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-stream-data:end:
|
|
# . restore locals
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
|
|
# . restore registers
|
|
5e/pop-to-ESI
|
|
59/pop-to-ECX
|
|
58/pop-to-EAX
|
|
# . epilog
|
|
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
|
|
5d/pop-to-EBP
|
|
c3/return
|
|
|
|
test-write-stream-data:
|
|
# . prolog
|
|
55/push-EBP
|
|
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
|
|
# setup
|
|
# . 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+4)
|
|
# . . push args
|
|
b8/copy-to-EAX _test-output-buffered-file/imm32
|
|
05/add-to-EAX 4/imm32
|
|
50/push-EAX
|
|
# . . 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-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
|
|
# 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
|
|
# write-stream-data(_test-output-buffered-file, _test-input-stream)
|
|
# . . push args
|
|
68/push _test-input-stream/imm32
|
|
68/push _test-output-buffered-file/imm32
|
|
# . . call
|
|
e8/call write-stream-data/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
|
|
# check that the write happened as expected
|
|
# . 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
|
|
# . check-stream-equal(_test-output-stream, "abcd", msg)
|
|
# . . push args
|
|
68/push "F - test-write-stream-data"/imm32
|
|
68/push "abcd"/imm32
|
|
68/push _test-output-stream/imm32
|
|
# . . call
|
|
e8/call check-stream-equal/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
# . epilog
|
|
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
|
|
5d/pop-to-EBP
|
|
c3/return
|
|
|
|
has-metadata?: # word : (address slice), s : (address string) -> EAX : boolean
|
|
# pseudocode:
|
|
# var twig : &slice = next-token-from-slice(word->start, word->end, '/') # skip name
|
|
# curr = twig->end
|
|
# while true
|
|
# twig = next-token-from-slice(curr, word->end, '/')
|
|
# if (twig.empty()) break
|
|
# if (slice-equal?(twig, s)) return true
|
|
# curr = twig->end
|
|
# return false
|
|
# . prolog
|
|
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
|
|
57/push-EDI
|
|
# ESI = word
|
|
8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI
|
|
# EDX = word->end
|
|
8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 2/r32/EDX 4/disp8 . # copy *(ESI+4) to EDX
|
|
# var twig/EDI : (address slice) = {0, 0}
|
|
68/push 0/imm32/end
|
|
68/push 0/imm32/start
|
|
89/copy 3/mod/direct 7/rm32/EDI . . . 4/r32/ESP . . # copy ESP to EDI
|
|
# next-token-from-slice(word->start, word->end, '/', twig)
|
|
# . . push args
|
|
57/push-EDI
|
|
68/push 0x2f/imm32/slash
|
|
52/push-EDX
|
|
ff 6/subop/push 0/mod/indirect 6/rm32/ESI . . . . . . # push *ESI
|
|
# . . 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
|
|
# curr/ECX = twig->end
|
|
8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 1/r32/ECX 4/disp8 . # copy *(EDI+4) to ECX
|
|
$has-metadata?:loop:
|
|
# next-token-from-slice(curr, word->end, '/', twig)
|
|
# . . push args
|
|
57/push-EDI
|
|
68/push 0x2f/imm32/slash
|
|
52/push-EDX
|
|
51/push-ECX
|
|
# . . 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 (slice-empty?(twig)) return false
|
|
# . EAX = slice-empty?(twig)
|
|
# . . push args
|
|
57/push-EDI
|
|
# . . 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 false
|
|
3d/compare-EAX-and 0/imm32
|
|
75/jump-if-not-equal $has-metadata?:false/disp8
|
|
# if (slice-equal?(twig, s)) return true
|
|
# . EAX = slice-equal?(twig, s)
|
|
# . . push args
|
|
ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12)
|
|
57/push-EDI
|
|
# . . 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 != 0) return true
|
|
3d/compare-EAX-and 0/imm32
|
|
75/jump-if-not-equal $has-metadata?:true/disp8
|
|
# curr = twig->end
|
|
8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 1/r32/ECX 4/disp8 . # copy *(EDI+4) to ECX
|
|
eb/jump $has-metadata?:loop/disp8
|
|
$has-metadata?:true:
|
|
b8/copy-to-EAX 1/imm32/true
|
|
eb/jump $has-metadata?:end/disp8
|
|
$has-metadata?:false:
|
|
b8/copy-to-EAX 0/imm32/false
|
|
$has-metadata?:end:
|
|
# . reclaim locals
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
|
|
# . restore registers
|
|
5f/pop-to-EDI
|
|
5e/pop-to-ESI
|
|
5a/pop-to-EDX
|
|
59/pop-to-ECX
|
|
# . epilog
|
|
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
|
|
5d/pop-to-EBP
|
|
c3/return
|
|
|
|
test-has-metadata-true:
|
|
# . prolog
|
|
55/push-EBP
|
|
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
|
|
# (EAX..ECX) = "ab/imm32"
|
|
b8/copy-to-EAX "ab/imm32"/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 in/ESI : (address slice) = {EAX, ECX}
|
|
51/push-ECX
|
|
50/push-EAX
|
|
89/copy 3/mod/direct 6/rm32/ESI . . . 4/r32/ESP . . # copy ESP to ESI
|
|
# EAX = has-metadata?(ESI, "imm32")
|
|
# . . push args
|
|
68/push "imm32"/imm32
|
|
56/push-ESI
|
|
# . . call
|
|
e8/call has-metadata?/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
|
|
# check-ints-equal(EAX, 1, msg)
|
|
# . . push args
|
|
68/push "F - test-has-metadata-true"/imm32
|
|
68/push 1/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
|
|
# . epilog
|
|
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
|
|
5d/pop-to-EBP
|
|
c3/return
|
|
|
|
test-has-metadata-false:
|
|
# . prolog
|
|
55/push-EBP
|
|
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
|
|
# (EAX..ECX) = "ab/c"
|
|
b8/copy-to-EAX "ab/c"/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 in/ESI : (address slice) = {EAX, ECX}
|
|
51/push-ECX
|
|
50/push-EAX
|
|
89/copy 3/mod/direct 6/rm32/ESI . . . 4/r32/ESP . . # copy ESP to ESI
|
|
# EAX = has-metadata?(ESI, "d")
|
|
# . . push args
|
|
68/push "d"/imm32
|
|
56/push-ESI
|
|
# . . call
|
|
e8/call has-metadata?/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
|
|
# check-ints-equal(EAX, 0, msg)
|
|
# . . push args
|
|
68/push "F - test-has-metadata-false"/imm32
|
|
68/push 0/imm32/false
|
|
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
|
|
# . epilog
|
|
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
|
|
5d/pop-to-EBP
|
|
c3/return
|
|
|
|
test-has-metadata-ignore-name:
|
|
# . prolog
|
|
55/push-EBP
|
|
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
|
|
# (EAX..ECX) = "a/b"
|
|
b8/copy-to-EAX "a/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 in/ESI : (address slice) = {EAX, ECX}
|
|
51/push-ECX
|
|
50/push-EAX
|
|
89/copy 3/mod/direct 6/rm32/ESI . . . 4/r32/ESP . . # copy ESP to ESI
|
|
# EAX = has-metadata?(ESI, "a")
|
|
# . . push args
|
|
68/push "a"/imm32
|
|
56/push-ESI
|
|
# . . call
|
|
e8/call has-metadata?/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
|
|
# check-ints-equal(EAX, 0, msg)
|
|
# . . push args
|
|
68/push "F - test-has-metadata-ignore-name"/imm32
|
|
68/push 0/imm32/false
|
|
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
|
|
# . epilog
|
|
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
|
|
5d/pop-to-EBP
|
|
c3/return
|
|
|
|
test-has-metadata-multiple-true:
|
|
# . prolog
|
|
55/push-EBP
|
|
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
|
|
# (EAX..ECX) = "a/b/c"
|
|
b8/copy-to-EAX "a/b/c"/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 in/ESI : (address slice) = {EAX, ECX}
|
|
51/push-ECX
|
|
50/push-EAX
|
|
89/copy 3/mod/direct 6/rm32/ESI . . . 4/r32/ESP . . # copy ESP to ESI
|
|
# EAX = has-metadata?(ESI, "c")
|
|
# . . push args
|
|
68/push "c"/imm32
|
|
56/push-ESI
|
|
# . . call
|
|
e8/call has-metadata?/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
|
|
# check-ints-equal(EAX, 1, msg)
|
|
# . . push args
|
|
68/push "F - test-has-metadata-multiple-true"/imm32
|
|
68/push 1/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
|
|
# . epilog
|
|
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
|
|
5d/pop-to-EBP
|
|
c3/return
|
|
|
|
test-has-metadata-multiple-false:
|
|
# . prolog
|
|
55/push-EBP
|
|
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
|
|
# (EAX..ECX) = "a/b/c"
|
|
b8/copy-to-EAX "a/b/c"/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 in/ESI : (address slice) = {EAX, ECX}
|
|
51/push-ECX
|
|
50/push-EAX
|
|
89/copy 3/mod/direct 6/rm32/ESI . . . 4/r32/ESP . . # copy ESP to ESI
|
|
# EAX = has-metadata?(ESI, "d")
|
|
# . . push args
|
|
68/push "d"/imm32
|
|
56/push-ESI
|
|
# . . call
|
|
e8/call has-metadata?/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
|
|
# check-ints-equal(EAX, 0, msg)
|
|
# . . push args
|
|
68/push "F - test-has-metadata-multiple-false"/imm32
|
|
68/push 0/imm32/false
|
|
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
|
|
# . epilog
|
|
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
|
|
5d/pop-to-EBP
|
|
c3/return
|
|
|
|
# If datum of 'word' is not a valid name, it must be a hex int. Parse and print
|
|
# it in 'width' bytes of hex, least significant first.
|
|
# Otherwise just print the entire word including metadata.
|
|
# Always print a trailing space.
|
|
emit: # out : (address buffered-file), word : (address slice), width : int -> <void>
|
|
# . prolog
|
|
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
|
|
57/push-EDI
|
|
# ESI = word
|
|
8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI
|
|
# var name/EDI : (address slice) = {0, 0}
|
|
68/push 0/imm32/end
|
|
68/push 0/imm32/start
|
|
89/copy 3/mod/direct 7/rm32/EDI . . . 4/r32/ESP . . # copy ESP to EDI
|
|
# datum = next-token-from-slice(word->start, word->end, '/')
|
|
# . . push args
|
|
57/push-EDI
|
|
68/push 0x2f/imm32/slash
|
|
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 next-token-from-slice/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP
|
|
# if (is-valid-name?(datum)) write-slice-buffered(out, word) and return
|
|
# . EAX = is-valid-name?(name)
|
|
# . . push args
|
|
57/push-EDI
|
|
# . . call
|
|
e8/call is-valid-name?/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
|
|
# . if (EAX != 0)
|
|
3d/compare-EAX-and 0/imm32
|
|
74/jump-if-equal $emit:hex-int/disp8
|
|
$emit:name:
|
|
# . write-slice-buffered(out, word)
|
|
# . . push args
|
|
56/push-ESI
|
|
ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8)
|
|
# . . 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 " "/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
|
|
# . return
|
|
eb/jump $emit:end/disp8
|
|
# otherwise emit-hex(out, parse-hex-int(datum), width)
|
|
# (Weird shit can happen here if the datum of 'word' isn't either a valid
|
|
# name or a hex number, but we're only going to be passing in real legal
|
|
# programs. We just want to make sure that valid names aren't treated as
|
|
# (valid) hex numbers.)
|
|
$emit:hex-int:
|
|
# . value/EAX = parse-hex-int(datum)
|
|
# . . push args
|
|
57/push-EDI
|
|
# . . call
|
|
e8/call parse-hex-int/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
|
|
# . emit-hex(out, value, width)
|
|
# . . push args
|
|
ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16)
|
|
50/push-EAX
|
|
ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8)
|
|
# . . call
|
|
e8/call emit-hex/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
$emit:end:
|
|
# . reclaim locals
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
|
|
# . restore registers
|
|
5f/pop-to-EDI
|
|
5e/pop-to-ESI
|
|
58/pop-to-EAX
|
|
# . epilog
|
|
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
|
|
5d/pop-to-EBP
|
|
c3/return
|
|
|
|
test-emit-number:
|
|
# . prolog
|
|
55/push-EBP
|
|
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
|
|
# setup
|
|
# . 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+4)
|
|
# . . push args
|
|
b8/copy-to-EAX _test-output-buffered-file/imm32
|
|
05/add-to-EAX 4/imm32
|
|
50/push-EAX
|
|
# . . call
|
|
e8/call clear-stream/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
|
|
# (EAX..ECX) = "30"
|
|
b8/copy-to-EAX "30"/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 slice/ECX = {EAX, ECX}
|
|
51/push-ECX
|
|
50/push-EAX
|
|
89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX
|
|
# emit(_test-output-buffered-file, slice, 1)
|
|
# . . push args
|
|
68/push 1/imm32
|
|
51/push-ECX
|
|
68/push _test-output-buffered-file/imm32
|
|
# . . call
|
|
e8/call emit/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
# 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
|
|
# check-stream-equal(_test-output-stream, "30 ", msg)
|
|
# . . push args
|
|
68/push "F - test-emit-number/1"/imm32
|
|
68/push "30 "/imm32
|
|
68/push _test-output-stream/imm32
|
|
# . . call
|
|
e8/call check-stream-equal/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
# . epilog
|
|
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
|
|
5d/pop-to-EBP
|
|
c3/return
|
|
|
|
test-emit-negative-number:
|
|
# test support for sign-extending negative numbers
|
|
# . prolog
|
|
55/push-EBP
|
|
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
|
|
# setup
|
|
# . 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+4)
|
|
# . . push args
|
|
b8/copy-to-EAX _test-output-buffered-file/imm32
|
|
05/add-to-EAX 4/imm32
|
|
50/push-EAX
|
|
# . . call
|
|
e8/call clear-stream/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
|
|
# (EAX..ECX) = "-2"
|
|
b8/copy-to-EAX "-2"/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 slice/ECX = {EAX, ECX}
|
|
51/push-ECX
|
|
50/push-EAX
|
|
89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX
|
|
# emit(_test-output-buffered-file, slice, 2)
|
|
# . . push args
|
|
68/push 2/imm32
|
|
51/push-ECX
|
|
68/push _test-output-buffered-file/imm32
|
|
# . . call
|
|
e8/call emit/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
# 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
|
|
# check-stream-equal(_test-output-stream, "fe ff ", msg)
|
|
# . . push args
|
|
68/push "F - test-emit-number/1"/imm32
|
|
68/push "fe ff "/imm32
|
|
68/push _test-output-stream/imm32
|
|
# . . call
|
|
e8/call check-stream-equal/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
# . epilog
|
|
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
|
|
5d/pop-to-EBP
|
|
c3/return
|
|
|
|
test-emit-number-with-metadata:
|
|
# . prolog
|
|
55/push-EBP
|
|
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
|
|
# setup
|
|
# . 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+4)
|
|
# . . push args
|
|
b8/copy-to-EAX _test-output-buffered-file/imm32
|
|
05/add-to-EAX 4/imm32
|
|
50/push-EAX
|
|
# . . call
|
|
e8/call clear-stream/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
|
|
# (EAX..ECX) = "-2/foo"
|
|
b8/copy-to-EAX "-2/foo"/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 slice/ECX = {EAX, ECX}
|
|
51/push-ECX
|
|
50/push-EAX
|
|
89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX
|
|
# emit(_test-output-buffered-file, slice, 2)
|
|
# . . push args
|
|
68/push 2/imm32
|
|
51/push-ECX
|
|
68/push _test-output-buffered-file/imm32
|
|
# . . call
|
|
e8/call emit/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
# 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
|
|
# the '/foo' will have no impact on the output
|
|
# check-stream-equal(_test-output-stream, "fe ff ", msg)
|
|
# . . push args
|
|
68/push "F - test-emit-number-with-metadata"/imm32
|
|
68/push "fe ff "/imm32
|
|
68/push _test-output-stream/imm32
|
|
# . . call
|
|
e8/call check-stream-equal/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
# . epilog
|
|
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
|
|
5d/pop-to-EBP
|
|
c3/return
|
|
|
|
test-emit-non-number:
|
|
# . prolog
|
|
55/push-EBP
|
|
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
|
|
# setup
|
|
# . 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+4)
|
|
# . . push args
|
|
b8/copy-to-EAX _test-output-buffered-file/imm32
|
|
05/add-to-EAX 4/imm32
|
|
50/push-EAX
|
|
# . . call
|
|
e8/call clear-stream/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
|
|
# (EAX..ECX) = "xyz"
|
|
b8/copy-to-EAX "xyz"/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 slice/ECX = {EAX, ECX}
|
|
51/push-ECX
|
|
50/push-EAX
|
|
89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX
|
|
# emit(_test-output-buffered-file, slice, 2)
|
|
# . . push args
|
|
68/push 2/imm32
|
|
51/push-ECX
|
|
68/push _test-output-buffered-file/imm32
|
|
# . . call
|
|
e8/call emit/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
# 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
|
|
# check-stream-equal(_test-output-stream, "xyz", msg)
|
|
# . . push args
|
|
68/push "F - test-emit-non-number"/imm32
|
|
68/push "xyz "/imm32
|
|
68/push _test-output-stream/imm32
|
|
# . . call
|
|
e8/call check-stream-equal/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
# . epilog
|
|
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
|
|
5d/pop-to-EBP
|
|
c3/return
|
|
|
|
test-emit-non-number-with-metadata:
|
|
# . prolog
|
|
55/push-EBP
|
|
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
|
|
# setup
|
|
# . 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+4)
|
|
# . . push args
|
|
b8/copy-to-EAX _test-output-buffered-file/imm32
|
|
05/add-to-EAX 4/imm32
|
|
50/push-EAX
|
|
# . . call
|
|
e8/call clear-stream/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
|
|
# (EAX..ECX) = "xyz/"
|
|
b8/copy-to-EAX "xyz/"/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 slice/ECX = {EAX, ECX}
|
|
51/push-ECX
|
|
50/push-EAX
|
|
89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX
|
|
# emit(_test-output-buffered-file, slice, 2)
|
|
# . . push args
|
|
68/push 2/imm32
|
|
51/push-ECX
|
|
68/push _test-output-buffered-file/imm32
|
|
# . . call
|
|
e8/call emit/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
# 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
|
|
# check-stream-equal(_test-output-stream, "xyz/", msg)
|
|
# . . push args
|
|
68/push "F - test-emit-non-number-with-metadata"/imm32
|
|
68/push "xyz/ "/imm32
|
|
68/push _test-output-stream/imm32
|
|
# . . call
|
|
e8/call check-stream-equal/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
# . epilog
|
|
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
|
|
5d/pop-to-EBP
|
|
c3/return
|
|
|
|
test-emit-non-number-with-all-hex-digits-and-metadata:
|
|
# . prolog
|
|
55/push-EBP
|
|
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
|
|
# setup
|
|
# . 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+4)
|
|
# . . push args
|
|
b8/copy-to-EAX _test-output-buffered-file/imm32
|
|
05/add-to-EAX 4/imm32
|
|
50/push-EAX
|
|
# . . call
|
|
e8/call clear-stream/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
|
|
# (EAX..ECX) = "abcd/xyz"
|
|
b8/copy-to-EAX "abcd/xyz"/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 slice/ECX = {EAX, ECX}
|
|
51/push-ECX
|
|
50/push-EAX
|
|
89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX
|
|
# emit(_test-output-buffered-file, slice, 2)
|
|
# . . push args
|
|
68/push 2/imm32
|
|
51/push-ECX
|
|
68/push _test-output-buffered-file/imm32
|
|
# . . call
|
|
e8/call emit/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
# 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, "^")
|
|
#? # . . 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, _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
|
|
#? # }}}
|
|
# check-stream-equal(_test-output-stream, "abcd/xyz")
|
|
# . . push args
|
|
68/push "F - test-emit-non-number-with-all-hex-digits"/imm32
|
|
68/push "abcd/xyz "/imm32
|
|
68/push _test-output-stream/imm32
|
|
# . . call
|
|
e8/call check-stream-equal/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
# . epilog
|
|
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
|
|
5d/pop-to-EBP
|
|
c3/return
|
|
|
|
# conditions for 'valid' names that are not at risk of looking like hex numbers
|
|
# keep in sync with the rules in labels.cc
|
|
#: - if it starts with a digit, it's treated as a number. If it can't be
|
|
#: parsed as hex it will raise an error.
|
|
#: - if it starts with '-' it's treated as a number.
|
|
#: - if it starts with '0x' it's treated as a number. (redundant)
|
|
#: - if it's two characters long, it can't be a name. Either it's a hex
|
|
#: byte, or it raises an error.
|
|
is-valid-name?: # in : (address slice) -> EAX : boolean
|
|
# . prolog
|
|
55/push-EBP
|
|
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
|
|
# . save registers
|
|
51/push-ECX
|
|
56/push-ESI
|
|
# ESI = in
|
|
8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI
|
|
# start/ECX = in->start
|
|
8b/copy 0/mod/indirect 6/rm32/ESI . . . 1/r32/ECX . . # copy *ESI to ECX
|
|
# end/EAX = in->end
|
|
8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 4/disp8 . # copy *(ESI+4) to EAX
|
|
$is-valid-name?:check0:
|
|
# if (start >= end) return false
|
|
39/compare 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # compare ECX with EAX
|
|
73/jump-if-greater-or-equal-unsigned $is-valid-name?:false/disp8
|
|
$is-valid-name?:check1:
|
|
# EAX -= ECX
|
|
29/subtract 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # subtract ECX from EAX
|
|
# if (EAX == 2) return false
|
|
3d/compare-EAX-and 2/imm32
|
|
74/jump-if-equal $is-valid-name?:false/disp8
|
|
$is-valid-name?:check2:
|
|
# c/EAX = *ECX
|
|
31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX
|
|
8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL
|
|
# if (c == "-") return false
|
|
3d/compare-EAX-and 2d/imm32/-
|
|
74/jump-if-equal $is-valid-name?:false/disp8
|
|
$is-valid-name?:check3a:
|
|
# if (c < "0") return true
|
|
3d/compare-EAX-with 30/imm32/0
|
|
7c/jump-if-lesser $is-valid-name?:true/disp8
|
|
$is-valid-name?:check3b:
|
|
# if (c > "9") return true
|
|
3d/compare-EAX-with 39/imm32/9
|
|
7f/jump-if-greater $is-valid-name?:true/disp8
|
|
$is-valid-name?:false:
|
|
# return false
|
|
b8/copy-to-EAX 0/imm32/false
|
|
eb/jump $is-valid-name?:end/disp8
|
|
$is-valid-name?:true:
|
|
# return true
|
|
b8/copy-to-EAX 1/imm32/true
|
|
$is-valid-name?:end:
|
|
# . restore registers
|
|
5e/pop-to-ESI
|
|
59/pop-to-ECX
|
|
# . epilog
|
|
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
|
|
5d/pop-to-EBP
|
|
c3/return
|
|
|
|
test-is-valid-name-digit-prefix:
|
|
# . prolog
|
|
55/push-EBP
|
|
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
|
|
# (EAX..ECX) = "34"
|
|
b8/copy-to-EAX "34"/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 slice/ECX = {EAX, ECX}
|
|
51/push-ECX
|
|
50/push-EAX
|
|
89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX
|
|
# EAX = is-valid-name?(slice)
|
|
# . . push args
|
|
51/push-ECX
|
|
# . . call
|
|
e8/call is-valid-name?/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-is-valid-name-digit-prefix"/imm32
|
|
68/push 0/imm32/false
|
|
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
|
|
# . epilog
|
|
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
|
|
5d/pop-to-EBP
|
|
c3/return
|
|
|
|
test-is-valid-name-negative-prefix:
|
|
# . prolog
|
|
55/push-EBP
|
|
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
|
|
# (EAX..ECX) = "-0x34"
|
|
b8/copy-to-EAX "-0x34"/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 slice/ECX = {EAX, ECX}
|
|
51/push-ECX
|
|
50/push-EAX
|
|
89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX
|
|
# EAX = is-valid-name?(slice)
|
|
# . . push args
|
|
51/push-ECX
|
|
# . . call
|
|
e8/call is-valid-name?/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-is-valid-name-negative-prefix"/imm32
|
|
68/push 0/imm32/false
|
|
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
|
|
# . epilog
|
|
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
|
|
5d/pop-to-EBP
|
|
c3/return
|
|
|
|
test-is-valid-name-0x-prefix:
|
|
# . prolog
|
|
55/push-EBP
|
|
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
|
|
# (EAX..ECX) = "0x34"
|
|
b8/copy-to-EAX "0x34"/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 slice/ECX = {EAX, ECX}
|
|
51/push-ECX
|
|
50/push-EAX
|
|
89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX
|
|
# EAX = is-valid-name?(slice)
|
|
# . . push args
|
|
51/push-ECX
|
|
# . . call
|
|
e8/call is-valid-name?/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-is-valid-name-0x-prefix"/imm32
|
|
68/push 0/imm32/false
|
|
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
|
|
# . epilog
|
|
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
|
|
5d/pop-to-EBP
|
|
c3/return
|
|
|
|
test-is-valid-name-starts-with-pre-digit:
|
|
# . prolog
|
|
55/push-EBP
|
|
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
|
|
# (EAX..ECX) = "/03"
|
|
b8/copy-to-EAX "/03"/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 slice/ECX = {EAX, ECX}
|
|
51/push-ECX
|
|
50/push-EAX
|
|
89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX
|
|
# EAX = is-valid-name?(slice)
|
|
# . . push args
|
|
51/push-ECX
|
|
# . . call
|
|
e8/call is-valid-name?/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
|
|
# check-ints-equal(EAX, 1, msg)
|
|
# . . push args
|
|
68/push "F - test-is-valid-name-starts-with-pre-digit"/imm32
|
|
68/push 1/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
|
|
# . epilog
|
|
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
|
|
5d/pop-to-EBP
|
|
c3/return
|
|
|
|
test-is-valid-name-starts-with-post-digit:
|
|
# . prolog
|
|
55/push-EBP
|
|
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
|
|
# (EAX..ECX) = "q34"
|
|
b8/copy-to-EAX "q34"/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 slice/ECX = {EAX, ECX}
|
|
51/push-ECX
|
|
50/push-EAX
|
|
89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX
|
|
# EAX = is-valid-name?(slice)
|
|
# . . push args
|
|
51/push-ECX
|
|
# . . call
|
|
e8/call is-valid-name?/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
|
|
# check-ints-equal(EAX, 1, msg)
|
|
# . . push args
|
|
68/push "F - test-is-valid-name-starts-with-post-digit"/imm32
|
|
68/push 1/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
|
|
# . epilog
|
|
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
|
|
5d/pop-to-EBP
|
|
c3/return
|
|
|
|
test-is-valid-name-starts-with-digit:
|
|
# . prolog
|
|
55/push-EBP
|
|
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
|
|
# (EAX..ECX) = "0x34"
|
|
b8/copy-to-EAX "0x34"/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 slice/ECX = {EAX, ECX}
|
|
51/push-ECX
|
|
50/push-EAX
|
|
89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX
|
|
# EAX = is-valid-name?(slice)
|
|
# . . push args
|
|
51/push-ECX
|
|
# . . call
|
|
e8/call is-valid-name?/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-is-valid-name-starts-with-digit"/imm32
|
|
68/push 0/imm32/false
|
|
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
|
|
# . epilog
|
|
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
|
|
5d/pop-to-EBP
|
|
c3/return
|
|
|
|
# print 'n' in hex in 'width' bytes in lower-endian order, with a space after every byte
|
|
emit-hex: # out : (address buffered-file), n : int, width : int -> <void>
|
|
# . prolog
|
|
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
|
|
57/push-EDI
|
|
# EDI = out
|
|
8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 8/disp8 . # copy *(EBP+8) to EDI
|
|
# EBX = n
|
|
8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 3/r32/EBX 0xc/disp8 . # copy *(EBP+12) to EBX
|
|
# EDX = width
|
|
8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 2/r32/EDX 0x10/disp8 . # copy *(EBP+16) to EDX
|
|
# var curr/ECX = 0
|
|
31/xor 3/mod/direct 1/rm32/ECX . . . 1/r32/ECX . . # clear ECX
|
|
$emit-hex:loop:
|
|
# if (curr >= width) break
|
|
39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX
|
|
7d/jump-if-greater-or-equal $emit-hex:end/disp8
|
|
# print-byte-buffered(out, EBX)
|
|
# . . push args
|
|
53/push-EBX
|
|
57/push-EDI
|
|
# . . call
|
|
e8/call print-byte-buffered/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
|
|
# write-byte-buffered(out, ' ')
|
|
# . . push args
|
|
68/push 0x20/imm32/space
|
|
57/push-EDI
|
|
# . . call
|
|
e8/call write-byte-buffered/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
|
|
# EBX = EBX >> 8
|
|
c1/shift 5/subop/logic-right 3/mod/direct 3/rm32/EBX . . . . . 8/imm8 # shift EBX right by 8 bits, while padding zeroes
|
|
$emit-hex:continue:
|
|
# ++curr
|
|
41/increment-ECX
|
|
eb/jump $emit-hex:loop/disp8
|
|
$emit-hex:end:
|
|
# . restore registers
|
|
5f/pop-to-EDI
|
|
5b/pop-to-EBX
|
|
5a/pop-to-EDX
|
|
59/pop-to-ECX
|
|
58/pop-to-EAX
|
|
# . epilog
|
|
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
|
|
5d/pop-to-EBP
|
|
c3/return
|
|
|
|
test-emit-hex-single-byte:
|
|
# setup
|
|
# . 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+4)
|
|
# . . push args
|
|
b8/copy-to-EAX _test-output-buffered-file/imm32
|
|
05/add-to-EAX 4/imm32
|
|
50/push-EAX
|
|
# . . call
|
|
e8/call clear-stream/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
|
|
# emit-hex(_test-output-buffered-file, 0xab, 1)
|
|
# . . push args
|
|
68/push 1/imm32
|
|
68/push 0xab/imm32
|
|
68/push _test-output-buffered-file/imm32
|
|
# . . call
|
|
e8/call emit-hex/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
# 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
|
|
# check-ints-equal(*_test-output-stream->data, 'ab ', msg)
|
|
# . . push args
|
|
68/push "F - test-emit-hex-single-byte"/imm32
|
|
68/push 0x206261/imm32
|
|
# . . push *_test-output-stream->data
|
|
b8/copy-to-EAX _test-output-stream/imm32
|
|
ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12)
|
|
# . . call
|
|
e8/call check-ints-equal/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
# . end
|
|
c3/return
|
|
|
|
test-emit-hex-multiple-byte:
|
|
# setup
|
|
# . 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+4)
|
|
# . . push args
|
|
b8/copy-to-EAX _test-output-buffered-file/imm32
|
|
05/add-to-EAX 4/imm32
|
|
50/push-EAX
|
|
# . . call
|
|
e8/call clear-stream/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
|
|
# emit-hex(_test-output-buffered-file, 0x1234, 2)
|
|
# . . push args
|
|
68/push 2/imm32
|
|
68/push 0x1234/imm32
|
|
68/push _test-output-buffered-file/imm32
|
|
# . . call
|
|
e8/call emit-hex/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
# 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
|
|
# check-stream-equal(_test-output-stream, "34 12 ", msg)
|
|
# . . push args
|
|
68/push "F - test-emit-hex-multiple-byte/1"/imm32
|
|
68/push "34 12 "/imm32
|
|
68/push _test-output-stream/imm32
|
|
# . . call
|
|
e8/call check-stream-equal/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
# . end
|
|
c3/return
|
|
|
|
test-emit-hex-zero-pad:
|
|
# setup
|
|
# . 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+4)
|
|
# . . push args
|
|
b8/copy-to-EAX _test-output-buffered-file/imm32
|
|
05/add-to-EAX 4/imm32
|
|
50/push-EAX
|
|
# . . call
|
|
e8/call clear-stream/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
|
|
# emit-hex(_test-output-buffered-file, 0xab, 2)
|
|
# . . push args
|
|
68/push 2/imm32
|
|
68/push 0xab/imm32
|
|
68/push _test-output-buffered-file/imm32
|
|
# . . call
|
|
e8/call emit-hex/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
# 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
|
|
# check(_test-output-stream->data == 'ab 00 ')
|
|
# . . push args
|
|
68/push "F - test-emit-hex-zero-pad/1"/imm32
|
|
68/push "ab 00 "/imm32
|
|
68/push _test-output-stream/imm32
|
|
# . . call
|
|
e8/call check-stream-equal/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
# . end
|
|
c3/return
|
|
|
|
test-emit-hex-negative:
|
|
# setup
|
|
# . 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+4)
|
|
# . . push args
|
|
b8/copy-to-EAX _test-output-buffered-file/imm32
|
|
05/add-to-EAX 4/imm32
|
|
50/push-EAX
|
|
# . . call
|
|
e8/call clear-stream/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
|
|
# emit-hex(_test-output-buffered-file, -1, 2)
|
|
# . . push args
|
|
68/push 2/imm32
|
|
68/push -1/imm32
|
|
68/push _test-output-buffered-file/imm32
|
|
# . . call
|
|
e8/call emit-hex/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
# 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
|
|
# check-stream-equal(_test-output-stream == "ff ff ")
|
|
# . . push args
|
|
68/push "F - test-emit-hex-negative/1"/imm32
|
|
68/push "ff ff "/imm32
|
|
68/push _test-output-stream/imm32
|
|
# . . call
|
|
e8/call check-stream-equal/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
# . end
|
|
c3/return
|
|
|
|
# print 'arr' in hex with a space after every byte
|
|
emit-hex-array: # out : (address buffered-file), arr : (address array byte) -> <void>
|
|
# . prolog
|
|
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
|
|
57/push-EDI
|
|
# EDI = out
|
|
8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 8/disp8 . # copy *(EBP+8) to EDI
|
|
# EDX = arr # <== 0xbdffffe4
|
|
8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 2/r32/EDX 0xc/disp8 . # copy *(EBP+12) to EDX
|
|
# curr/ECX = arr->data
|
|
8d/copy-address 1/mod/*+disp8 2/rm32/EDX . . . 1/r32/ECX 4/disp8 . # copy EDX+4 to ECX
|
|
# max/EDX = arr->data + arr->length
|
|
8b/copy 0/mod/indirect 2/rm32/EDX . . . 2/r32/EDX . . # copy *EDX to EDX
|
|
01/add 3/mod/direct 2/rm32/EDX . . . 1/r32/ECX . . # add ECX to EDX
|
|
# EAX = 0
|
|
31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX
|
|
$emit-hex-array:loop:
|
|
# if (curr >= width) break
|
|
39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX
|
|
73/jump-if-greater-or-equal-unsigned $emit-hex-array:end/disp8
|
|
# emit-hex(out, *curr, width=1)
|
|
# . . push args
|
|
68/push 1/imm32/width
|
|
8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL
|
|
50/push-EAX
|
|
57/push-EDI
|
|
# . . call
|
|
e8/call emit-hex/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
# ++curr
|
|
41/increment-ECX
|
|
eb/jump $emit-hex-array:loop/disp8
|
|
$emit-hex-array:end:
|
|
# . restore registers
|
|
5f/pop-to-EDI
|
|
5a/pop-to-EDX
|
|
59/pop-to-ECX
|
|
58/pop-to-EAX
|
|
# . epilog
|
|
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
|
|
5d/pop-to-EBP
|
|
c3/return
|
|
|
|
test-emit-hex-array:
|
|
# . prolog
|
|
55/push-EBP
|
|
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
|
|
# setup
|
|
# . 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+4)
|
|
# . . push args
|
|
b8/copy-to-EAX _test-output-buffered-file/imm32
|
|
05/add-to-EAX 4/imm32
|
|
50/push-EAX
|
|
# . . call
|
|
e8/call clear-stream/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
|
|
# var arr/ECX (address array byte) = [01, 02, 03]
|
|
68/push 0x00030201/imm32 # bytes 01 02 03
|
|
68/push 3/imm32/length
|
|
89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX
|
|
# emit-hex-array(_test-output-buffered-file, arr)
|
|
# . . push args
|
|
51/push-ECX
|
|
68/push _test-output-buffered-file/imm32
|
|
# . . call
|
|
e8/call emit-hex-array/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
|
|
# . 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, "01 02 03 ", msg)
|
|
# . . push args
|
|
68/push "F - test-emit-hex-array"/imm32
|
|
68/push "01 02 03 "/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
|
|
# . epilog
|
|
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
|
|
5d/pop-to-EBP
|
|
c3/return
|
|
|
|
compute-width: # word : (address array byte) -> EAX : int
|
|
# . prolog
|
|
55/push-EBP
|
|
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
|
|
# . save registers
|
|
51/push-ECX
|
|
# EAX = word
|
|
8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 8/disp8 . # copy *(EBP+8) to ECX
|
|
# ECX = word + word->length
|
|
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
|
|
# EAX = word->data
|
|
05/add-to-EAX 4/imm32
|
|
# var in/ECX : (address slice) = {EAX, ECX}
|
|
51/push-ECX
|
|
50/push-EAX
|
|
89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX
|
|
# return compute-width-of-slice(ECX)
|
|
# . . 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
|
|
$compute-width:end:
|
|
# . reclaim locals
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
|
|
# . restore registers
|
|
59/pop-to-ECX
|
|
# . epilog
|
|
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
|
|
5d/pop-to-EBP
|
|
c3/return
|
|
|
|
compute-width-of-slice: # s : (address slice) -> EAX : int
|
|
# . prolog
|
|
55/push-EBP
|
|
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
|
|
# . save registers
|
|
51/push-ECX
|
|
# ECX = s
|
|
8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX
|
|
# if (has-metadata?(word, "imm32")) return 4
|
|
# . EAX = has-metadata?(word, "imm32")
|
|
# . . push args
|
|
68/push "imm32"/imm32
|
|
51/push-ECX
|
|
# . . 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 != 0) return 4
|
|
3d/compare-EAX-and 0/imm32
|
|
b8/copy-to-EAX 4/imm32 # ZF is set, so we can overwrite EAX now
|
|
75/jump-if-not-equal $compute-width-of-slice:end/disp8
|
|
# if (has-metadata?(word, "disp32")) return 4
|
|
# . EAX = has-metadata?(word, "disp32")
|
|
# . . push args
|
|
68/push "disp32"/imm32
|
|
51/push-ECX
|
|
# . . 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 != 0) return 4
|
|
3d/compare-EAX-and 0/imm32
|
|
b8/copy-to-EAX 4/imm32 # ZF is set, so we can overwrite EAX now
|
|
75/jump-if-not-equal $compute-width-of-slice:end/disp8
|
|
# if (has-metadata?(word, "imm16")) return 2
|
|
# . EAX = has-metadata?(word, "imm16")
|
|
# . . push args
|
|
68/push "imm16"/imm32
|
|
51/push-ECX
|
|
# . . 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 != 0) return 2
|
|
3d/compare-EAX-and 0/imm32
|
|
b8/copy-to-EAX 2/imm32 # ZF is set, so we can overwrite EAX now
|
|
75/jump-if-not-equal $compute-width-of-slice:end/disp8
|
|
# if (has-metadata?(word, "disp16")) return 2
|
|
# . EAX = has-metadata?(word, "disp16")
|
|
# . . push args
|
|
68/push "disp16"/imm32
|
|
51/push-ECX
|
|
# . . 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 != 0) return 2
|
|
3d/compare-EAX-and 0/imm32
|
|
b8/copy-to-EAX 2/imm32 # ZF is set, so we can overwrite EAX now
|
|
75/jump-if-not-equal $compute-width-of-slice:end/disp8
|
|
# otherwise return 1
|
|
b8/copy-to-EAX 1/imm32
|
|
$compute-width-of-slice:end:
|
|
# . restore registers
|
|
59/pop-to-ECX
|
|
# . epilog
|
|
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
|
|
5d/pop-to-EBP
|
|
c3/return
|
|
|
|
test-compute-width:
|
|
# . prolog
|
|
55/push-EBP
|
|
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
|
|
$test-compute-width:imm8:
|
|
# EAX = compute-width("0x2/imm8")
|
|
# . . push args
|
|
68/push "0x2/imm8"/imm32
|
|
# . . call
|
|
e8/call compute-width/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
|
|
# check-ints-equal(EAX, 1, msg)
|
|
# . . push args
|
|
68/push "F - test-compute-width: 0x2/imm8"/imm32
|
|
50/push-EAX
|
|
68/push 1/imm32
|
|
# . . call
|
|
e8/call check-ints-equal/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
$test-compute-width:imm16:
|
|
# EAX = compute-width("4/imm16")
|
|
# . . push args
|
|
68/push "4/imm16"/imm32
|
|
# . . call
|
|
e8/call compute-width/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-compute-width: 4/imm16"/imm32
|
|
50/push-EAX
|
|
68/push 2/imm32
|
|
# . . call
|
|
e8/call check-ints-equal/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
$test-compute-width:imm32:
|
|
# EAX = compute-width("4/imm32")
|
|
# . . push args
|
|
68/push "4/imm32"/imm32
|
|
# . . call
|
|
e8/call compute-width/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
|
|
# check-ints-equal(EAX, 4, msg)
|
|
# . . push args
|
|
68/push "F - test-compute-width: 4/imm32"/imm32
|
|
50/push-EAX
|
|
68/push 4/imm32
|
|
# . . call
|
|
e8/call check-ints-equal/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
$test-compute-width:disp8:
|
|
# EAX = compute-width("foo/disp8")
|
|
# . . push args
|
|
68/push "foo/disp8"/imm32
|
|
# . . call
|
|
e8/call compute-width/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
|
|
# check-ints-equal(EAX, 1, msg)
|
|
# . . push args
|
|
68/push "F - test-compute-width: foo/disp8"/imm32
|
|
50/push-EAX
|
|
68/push 1/imm32
|
|
# . . call
|
|
e8/call check-ints-equal/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
$test-compute-width:disp16:
|
|
# EAX = compute-width("foo/disp16")
|
|
# . . push args
|
|
68/push "foo/disp16"/imm32
|
|
# . . call
|
|
e8/call compute-width/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-compute-width: foo/disp16"/imm32
|
|
50/push-EAX
|
|
68/push 2/imm32
|
|
# . . call
|
|
e8/call check-ints-equal/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
$test-compute-width:disp32:
|
|
# EAX = compute-width("foo/disp32")
|
|
# . . push args
|
|
68/push "foo/disp32"/imm32
|
|
# . . call
|
|
e8/call compute-width/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
|
|
# check-ints-equal(EAX, 4, msg)
|
|
# . . push args
|
|
68/push "F - test-compute-width: foo/disp32"/imm32
|
|
50/push-EAX
|
|
68/push 4/imm32
|
|
# . . call
|
|
e8/call check-ints-equal/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
$test-compute-width:no-metadata:
|
|
# EAX = compute-width("45")
|
|
# . . push args
|
|
68/push "45"/imm32
|
|
# . . call
|
|
e8/call compute-width/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
|
|
# check-ints-equal(EAX, 1, msg)
|
|
# . . push args
|
|
68/push "F - test-compute-width: 45 (no metadata)"/imm32
|
|
50/push-EAX
|
|
68/push 1/imm32
|
|
# . . call
|
|
e8/call check-ints-equal/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
|
|
# . epilog
|
|
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
|
|
5d/pop-to-EBP
|
|
c3/return
|
|
|
|
is-label?: # word : (address slice) -> EAX : boolean
|
|
# . prolog
|
|
55/push-EBP
|
|
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
|
|
# . save registers
|
|
51/push-ECX
|
|
# ECX = word
|
|
8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX
|
|
# ECX = word->end
|
|
8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 1/r32/ECX 4/disp8 . # copy *(ECX+4) to ECX
|
|
# return *(word->end - 1) == ':'
|
|
# . EAX = 0
|
|
31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX
|
|
# . EAX = *((char *) word->end - 1)
|
|
8a/copy-byte 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/AL -1/disp8 . # copy byte at *(ECX-1) to AL
|
|
# . return (EAX == ':')
|
|
3d/compare-EAX-and 0x3a/imm32/colon
|
|
b8/copy-to-EAX 1/imm32/true
|
|
74/jump-if-equal $is-label?:end/disp8
|
|
b8/copy-to-EAX 0/imm32/false
|
|
$is-label?:end:
|
|
# . restore registers
|
|
59/pop-to-ECX
|
|
# . epilog
|
|
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
|
|
5d/pop-to-EBP
|
|
c3/return
|
|
|
|
test-is-label?:
|
|
# . prolog
|
|
55/push-EBP
|
|
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
|
|
$test-is-label?:true:
|
|
# (EAX..ECX) = "AAA:"
|
|
b8/copy-to-EAX "AAA:"/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 slice/ECX = {EAX, ECX}
|
|
51/push-ECX
|
|
50/push-EAX
|
|
89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX
|
|
# is-label?(slice/ECX)
|
|
# . . push args
|
|
51/push-ECX
|
|
# . . call
|
|
e8/call is-label?/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
|
|
# check-ints-equal(EAX, 1, msg)
|
|
# . . push args
|
|
68/push "F - test-is-label?:true"/imm32
|
|
68/push 1/imm32
|
|
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
|
|
$test-is-label?:false:
|
|
# (EAX..ECX) = "AAA"
|
|
b8/copy-to-EAX "AAA"/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 slice/ECX = {EAX, ECX}
|
|
51/push-ECX
|
|
50/push-EAX
|
|
89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX
|
|
# is-label?(slice/ECX)
|
|
# . . push args
|
|
51/push-ECX
|
|
# . . call
|
|
e8/call is-label?/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-is-label?:false"/imm32
|
|
68/push 0/imm32
|
|
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
|
|
# . epilog
|
|
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
|
|
5d/pop-to-EBP
|
|
c3/return
|
|
|
|
== data
|
|
|
|
_test-input-stream:
|
|
# current write index
|
|
0/imm32
|
|
# current read index
|
|
0/imm32
|
|
# length
|
|
0x100/imm32 # 256 bytes
|
|
# data (16 lines x 16 bytes/line)
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
|
|
# a test buffered file for _test-input-stream
|
|
_test-input-buffered-file:
|
|
# file descriptor or (address stream)
|
|
_test-input-stream/imm32
|
|
# current write index
|
|
0/imm32
|
|
# current read index
|
|
0/imm32
|
|
# length
|
|
6/imm32
|
|
# data
|
|
00 00 00 00 00 00 # 6 bytes
|
|
|
|
_test-output-stream:
|
|
# current write index
|
|
0/imm32
|
|
# current read index
|
|
0/imm32
|
|
# length
|
|
0x200/imm32 # 512 bytes
|
|
# data (32 lines x 16 bytes/line)
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
|
|
# a test buffered file for _test-output-stream
|
|
_test-output-buffered-file:
|
|
# file descriptor or (address stream)
|
|
_test-output-stream/imm32
|
|
# current write index
|
|
0/imm32
|
|
# current read index
|
|
0/imm32
|
|
# length
|
|
6/imm32
|
|
# data
|
|
00 00 00 00 00 00 # 6 bytes
|
|
|
|
_test-data-segment:
|
|
64/d 61/a 74/t 61/a
|
|
_test-data-segment-end:
|
|
|
|
# . . vim:nowrap:textwidth=0
|