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:
|
|
0x100000/imm32/1MB
|
|
|
|
# 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
|