Merge branch 'master' into dquotes-1

Segfault in this branch is now fixed.
This commit is contained in:
Kartik Agaram 2019-05-10 17:21:17 -07:00
commit c12e85e103
21 changed files with 277 additions and 146 deletions

View File

@ -66,6 +66,7 @@ void load_elf_contents(uint8_t* elf_contents, size_t size, int argc, char* argv[
assert(overlap.find(STACK_SEGMENT) == overlap.end());
Mem.push_back(vma(STACK_SEGMENT));
assert(overlap.find(AFTER_STACK) == overlap.end());
// The stack grows downward.
Reg[ESP].u = AFTER_STACK;
Reg[EBP].u = 0;
EIP = e_entry;
@ -129,15 +130,24 @@ void load_segment_from_program_header(uint8_t* elf_contents, int segment_index,
}
:(before "End Includes")
// Very primitive/fixed/insecure ELF segments for now: just consecutive VMAs.
// code: 0x09000000 -> 0x09ffffff
// data/heap: 0x0a000000 -> 0x0affffff
// stack: 0x0b000ffc -> 0x0b000000 (downward)
const int CODE_SEGMENT = 0x09000000;
const int DATA_SEGMENT = 0x0a000000; // keep sync'd with `Heap.limit` in allocate.subx
const int STACK_SEGMENT = 0x0b000000;
const int AFTER_STACK = 0x0c000000;
const int ARGV_DATA_SEGMENT = 0x0c000000;
// Very primitive/fixed/insecure ELF segments for now.
// code: 0x09000000 -> 0x09ffffff (specified in ELF binary)
// data: 0x0a000000 -> 0x0affffff (specified in ELF binary)
// --- heap gets mmap'd somewhere here ---
// stack: 0x7dffffff -> 0x7d000000 (downward; not in ELF binary)
// argv hack: 0x7f000000 -> 0x7fffffff (not in ELF binary)
//
// For now we avoid addresses with the most significant bit set; SubX doesn't
// support unsigned comparison yet (https://github.com/akkartik/mu/issues/30)
// Once we do, we can go up to 0xc0000000; higher addresses are reserved for
// the Linux kernel.
const int CODE_SEGMENT = 0x09000000;
const int DATA_SEGMENT = 0x0a000000;
const int STACK_SEGMENT = 0x7d000000;
const int AFTER_STACK = 0x7e000000;
const int ARGV_DATA_SEGMENT = 0x7f000000;
// When updating the above memory map, don't forget to update `mmap`'s
// implementation in the 'syscalls' layer.
:(before "End Dump Info for Instruction")
//? dump_stack(); // slow
:(code)

View File

@ -488,7 +488,7 @@ void test_shift_right_logical_r32_with_cl() {
}
:(before "End Op d3 Subops")
case 5: { // shift right r/m32 by CL, preserving sign
case 5: { // shift right r/m32 by CL, padding zeroes
trace(Callstack_depth+1, "run") << "subop: shift right by CL bits, while padding zeroes" << end();
uint8_t count = Reg[ECX].u & 0x1f;
// OF is only defined if count is 1

View File

@ -24,14 +24,14 @@ void process_int80() {
trace(Callstack_depth+1, "run") << "read: " << Reg[EBX].u << ' ' << Reg[ECX].u << ' ' << Reg[EDX].u << end();
Reg[EAX].i = read(/*file descriptor*/Reg[EBX].u, /*memory buffer*/mem_addr_u8(Reg[ECX].u), /*size*/Reg[EDX].u);
trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].i << end();
if (Reg[EAX].i == -1) raise << strerror(errno) << '\n' << end();
if (Reg[EAX].i == -1) raise << "read: " << strerror(errno) << '\n' << end();
break;
case 4:
trace(Callstack_depth+1, "run") << "write: " << Reg[EBX].u << ' ' << Reg[ECX].u << ' ' << Reg[EDX].u << end();
trace(Callstack_depth+1, "run") << Reg[ECX].u << " => " << mem_addr_string(Reg[ECX].u, Reg[EDX].u) << end();
Reg[EAX].i = write(/*file descriptor*/Reg[EBX].u, /*memory buffer*/mem_addr_u8(Reg[ECX].u), /*size*/Reg[EDX].u);
trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].i << end();
if (Reg[EAX].i == -1) raise << strerror(errno) << '\n' << end();
if (Reg[EAX].i == -1) raise << "write: " << strerror(errno) << '\n' << end();
break;
case 5: {
check_flags(ECX);
@ -40,14 +40,14 @@ void process_int80() {
trace(Callstack_depth+1, "run") << Reg[EBX].u << " => " << mem_addr_kernel_string(Reg[EBX].u) << end();
Reg[EAX].i = open(/*filename*/mem_addr_kernel_string(Reg[EBX].u), /*flags*/Reg[ECX].u, /*mode*/0640);
trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].i << end();
if (Reg[EAX].i == -1) raise << strerror(errno) << '\n' << end();
if (Reg[EAX].i == -1) raise << "open: " << strerror(errno) << '\n' << end();
break;
}
case 6:
trace(Callstack_depth+1, "run") << "close: " << Reg[EBX].u << end();
Reg[EAX].i = close(/*file descriptor*/Reg[EBX].u);
trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].i << end();
if (Reg[EAX].i == -1) raise << strerror(errno) << '\n' << end();
if (Reg[EAX].i == -1) raise << "close: " << strerror(errno) << '\n' << end();
break;
case 8:
check_mode(ECX);
@ -55,14 +55,14 @@ void process_int80() {
trace(Callstack_depth+1, "run") << Reg[EBX].u << " => " << mem_addr_kernel_string(Reg[EBX].u) << end();
Reg[EAX].i = creat(/*filename*/mem_addr_kernel_string(Reg[EBX].u), /*mode*/0640);
trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].i << end();
if (Reg[EAX].i == -1) raise << strerror(errno) << '\n' << end();
if (Reg[EAX].i == -1) raise << "creat: " << strerror(errno) << '\n' << end();
break;
case 10:
trace(Callstack_depth+1, "run") << "unlink: " << Reg[EBX].u << end();
trace(Callstack_depth+1, "run") << Reg[EBX].u << " => " << mem_addr_kernel_string(Reg[EBX].u) << end();
Reg[EAX].i = unlink(/*filename*/mem_addr_kernel_string(Reg[EBX].u));
trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].i << end();
if (Reg[EAX].i == -1) raise << strerror(errno) << '\n' << end();
if (Reg[EAX].i == -1) raise << "unlink: " << strerror(errno) << '\n' << end();
break;
case 38:
trace(Callstack_depth+1, "run") << "rename: " << Reg[EBX].u << " -> " << Reg[ECX].u << end();
@ -70,7 +70,7 @@ void process_int80() {
trace(Callstack_depth+1, "run") << Reg[ECX].u << " => " << mem_addr_kernel_string(Reg[ECX].u) << end();
Reg[EAX].i = rename(/*old filename*/mem_addr_kernel_string(Reg[EBX].u), /*new filename*/mem_addr_kernel_string(Reg[ECX].u));
trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].i << end();
if (Reg[EAX].i == -1) raise << strerror(errno) << '\n' << end();
if (Reg[EAX].i == -1) raise << "rename: " << strerror(errno) << '\n' << end();
break;
case 45: // brk: modify size of data segment
trace(Callstack_depth+1, "run") << "grow data segment to " << Reg[EBX].u << end();
@ -110,7 +110,12 @@ void check_mode(int reg) {
}
:(before "End Globals")
uint32_t Next_segment = 0xb0000000; // 0xc0000000 and up is reserved for Linux kernel
// Very primitive/fixed/insecure mmap segments for now.
// For now we avoid addresses with the most significant bit set; SubX doesn't
// support unsigned comparison yet (https://github.com/akkartik/mu/issues/30)
// Once we do, we can go up to 0xc0000000; higher addresses are reserved for
// the Linux kernel.
uint32_t Next_segment = 0x7c000000;
const uint32_t SPACE_FOR_SEGMENT = 0x01000000;
:(code)
uint32_t new_segment(uint32_t length) {

View File

@ -1,29 +0,0 @@
//: Support for dynamic allocation.
//:
//: Just provide a special label marking the first unused address in the data
//: segment. Then we'll write SubX helpers to make use of it.
:(before "Begin rewrite_global_variables")
insert_heap_global_variable(p);
:(code)
void insert_heap_global_variable(program& p) {
if (SIZE(p.segments) < 2)
return; // no data segment defined
// Start-of-heap:
p.segments.at(1).lines.push_back(label("Start-of-heap"));
}
line label(string s) {
line result;
result.words.push_back(word());
result.words.back().data = (s+":");
return result;
}
line imm32(const string& s) {
line result;
result.words.push_back(word());
result.words.back().data = s;
result.words.back().metadata.push_back("imm32");
return result;
}

View File

@ -184,6 +184,13 @@ void skip_comment(istream& in) {
}
}
line label(string s) {
line result;
result.words.push_back(word());
result.words.back().data = (s+":");
return result;
}
// helper for tests
void parse_instruction_character_by_character(const string& line_data) {
vector<line> out;

View File

@ -46,14 +46,14 @@ else
:(code)
string debug_info(uint32_t inst_address) {
uint8_t op = read_mem_u8(EIP);
uint8_t op = read_mem_u8(inst_address);
if (op != 0xe8) {
ostringstream out;
out << HEXBYTE << NUM(op);
return out.str();
}
int32_t offset = read_mem_i32(EIP+/*skip op*/1);
uint32_t next_eip = EIP+/*inst length*/5+offset;
int32_t offset = read_mem_i32(inst_address+/*skip op*/1);
uint32_t next_eip = inst_address+/*inst length*/5+offset;
if (contains_key(Symbol_name, next_eip))
return "e8/call "+get(Symbol_name, next_eip);
ostringstream out;

View File

@ -1,4 +1,15 @@
# Create a new segment (for data) using mmap().
# Create a new segment (pool of memory for allocating chunks from) in the form
# of an *allocation descriptor* that can be passed to the memory allocator
# (defined in a later layer).
#
# Currently an allocation descriptor consists of just the bounds of the pool of
# available memory:
#
# curr : address
# end : address
#
# This isn't enough information to reclaim individual allocations. We can't
# support arbitrary reclamation yet.
== code
# instruction effective address register displacement immediate
@ -6,39 +17,54 @@
# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes
Entry: # manual test
# EAX = new-segment(0x1000)
# var ad/ECX : (address allocation-descriptor) = {0, 0}
68/push 0/imm32/limit
68/push 0/imm32/curr
89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX
# new-segment(0x1000, ad)
# . . push args
51/push-ECX
68/push 0x1000/imm32
# . . call
e8/call new-segment/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
# EAX = ad->curr
8b/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy *ECX to EAX
# write to *EAX to check that we have access to the newly-allocated segment
c7 0/subop/copy 0/mod/direct 0/rm32/EAX . . . . . 0x34/imm32 # copy to *EAX
# syscall(exit, EAX)
89/copy 3/mod/direct 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to EBX
b8/copy-to-EAX 1/imm32/exit
cd/syscall 0x80/imm8
new-segment: # len : int -> address
new-segment: # len : int, ad : (address allocation-descriptor)
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# . save registers
50/push-EAX
53/push-EBX
# copy len to _mmap-new-segment->len
# TODO: compute _mmap-new-segment+4 before runtime
8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 8/disp8 . # copy *(EBP+8) to EAX
bb/copy-to-EBX _mmap-new-segment/imm32
89/copy 1/mod/*+disp8 3/rm32/EBX . . . 0/r32/EAX 4/disp8 . # copy EAX to *(EBX+4)
89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX _mmap-new-segment:len/disp32 # copy EAX to *_mmap-new-segment:len
# mmap(_mmap-new-segment)
bb/copy-to-EBX _mmap-new-segment/imm32
b8/copy-to-EAX 0x5a/imm32/mmap
cd/syscall 0x80/imm8
# copy {EAX, EAX+len} to *ad
# . EBX = ad
8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 3/r32/EBX 0xc/disp8 . # copy *(EBP+12) to EBX
# . *EBX = EAX
89/copy 0/mod/indirect 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to *EBX
# . *(EBX+4) = EAX+len
03/add 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 8/disp8 . # add *(EBP+8) to EAX
89/copy 1/mod/*+disp8 3/rm32/EBX . . . 0/r32/EAX 4/disp8 . # copy EAX to *(EBX+4)
$new-segment:end:
# . epilog
# . restore registers
5b/pop-to-EBX
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
@ -49,6 +75,7 @@ $new-segment:end:
_mmap-new-segment: # type mmap_arg_struct
# addr
0/imm32
_mmap-new-segment:len:
# len
0/imm32
# protection flags

View File

@ -16,14 +16,6 @@
# very same 'allocate' helper. They just need a new allocation descriptor for
# their book-keeping.
== data
# The 'global' allocation descriptor. Pass this into 'allocate' to claim a
# hitherto unused bit of memory.
Heap:
Start-of-heap/imm32 # curr
0x0b000000/imm32 # limit; keep sync'd with DATA_SEGMENT + SEGMENT_ALIGNMENT
== code
# instruction effective address register displacement immediate
# . op subop mod rm32 base index scale r32

View File

@ -68,20 +68,21 @@ test-new-stream:
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# var ad/ECX : (address allocation-descriptor) = allocate-region(Heap, 512)
# . EAX = allocate-region(Heap, 512)
# var heap/ECX : (address allocation-descriptor) = {0, 0}
68/push 0/imm32/limit
68/push 0/imm32/curr
89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX
# heap = new-segment(512)
# . . push args
51/push-ECX
68/push 0x200/imm32
68/push Heap/imm32
# . . call
e8/call allocate-region/disp32
e8/call new-segment/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
# . ECX = EAX
89/copy 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # copy EAX to ECX
# var start/EDX = ad->curr
8b/copy 0/mod/indirect 1/rm32/ECX . . . 2/r32/EDX . . # copy *ECX to EDX
# EAX = new-stream(ad, 3, 2)
# EAX = new-stream(heap, 3, 2)
# . . push args
68/push 2/imm32
68/push 3/imm32

View File

@ -917,14 +917,26 @@ test-slice-to-string:
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# var heap/EDX : (address allocation-descriptor) = {0, 0}
68/push 0/imm32/limit
68/push 0/imm32/curr
89/copy 3/mod/direct 2/rm32/EDX . . . 4/r32/ESP . . # copy ESP to EDX
# heap = new-segment(512)
# . . push args
52/push-EDX
68/push 0x200/imm32
# . . call
e8/call new-segment/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
# var slice/ECX = "Abc"
68/push _test-slice-data-3/imm32/end
68/push _test-slice-data-0/imm32/start
89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX
# EAX = slice-to-string(Heap, slice)
# EAX = slice-to-string(heap, slice)
# . . push args
51/push-ECX
68/push Heap/imm32
52/push-EDX
# . . call
e8/call slice-to-string/disp32
# . . discard args

View File

@ -6,7 +6,8 @@
# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes
#? Entry: # run a single test, while debugging
#? e8/call test-print-int32-decimal/disp32
#? e8/call test-print-int32-decimal-negative/disp32
#?
#? # syscall(exit, Num-test-failures)
#? 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX
#? b8/copy-to-EAX 1/imm32/exit
@ -17,27 +18,30 @@ print-int32-decimal: # out : (address stream), n : int32
# to the stack, before popping them one by one into the stream
#
# pseudocode:
# copy ESP to EBX
# EAX = |n|
# push sentinel
# EAX = abs(n)
# while true
# if (EAX == 0) break
# sign-extend EAX into EDX
# EAX, EDX = EAX/10, EAX%10
# EDX += '0'
# push EDX
# if (EAX == 0) break
# if n < 0
# push '-' - '0' = -3
# max/ECX = &out->data[out->length]
# w/EAX = out->write
# curr/EDI = &out->data[out->write]
# push '-'
# w = out->write
# curr = &out->data[out->write]
# max = &out->data[out->length]
# while true
# if (ESP == EBX) break
# pop into EAX
# if (EAX == sentinel) break
# if (curr >= max) abort
# pop into EDX
# EDX += '0' # convert decimal digit to ascii
# *curr = DL
# *curr = AL
# ++curr
# ++w
# out->write = w
# (based on K&R itoa: https://en.wikibooks.org/wiki/C_Programming/stdlib.h/itoa)
# (this pseudocode contains registers because operations like division
# require specific registers in x86)
#
# . prolog
55/push-EBP
@ -48,67 +52,63 @@ print-int32-decimal: # out : (address stream), n : int32
52/push-EDX
53/push-EBX
57/push-EDI
# copy ESP to EBX
89/copy 3/mod/direct 3/rm32/EBX . . . 4/r32/ESP . . # copy ESP to EBX
# ten/ECX = 10
b9/copy-to-ECX 0xa/imm32
# EAX = |n|
# push sentinel
68/push 0/imm32/sentinel
# EAX = abs(n)
8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 0xc/disp8 . # copy *(EBP+12) to EAX
3d/compare-EAX-with 0/imm32
7d/jump-if-greater-or-equal $print-int32-decimal:read-loop/disp8
7d/jump-if-greater-or-equal $print-int32-decimal:read-loop/disp8
$print-int32-decimal:negative:
f7 3/subop/negate 3/mod/direct 0/rm32/EAX . . . . . . # negate EAX
$print-int32-decimal:read-loop:
# if (EAX == 0) break
3d/compare-EAX-and 0/imm32
74/jump-if-equal $print-int32-decimal:read-break/disp8
# sign-extend
# EAX, EDX = EAX / 10, EAX % 10
99/sign-extend-EAX-into-EDX
# EAX, EDX = divide-with-remainder EAX/ten, EAX%ten
f7 7/subop/divide-by 3/mod/direct 1/rm32/ECX . . . . . . # divide EDX:EAX by ECX, storing quotient in EAX and remainder in EDX
f7 7/subop/idiv 3/mod/direct 1/rm32/ECX . . . . . . # divide EDX:EAX by ECX, storing quotient in EAX and remainder in EDX
# EDX += '0'
81 0/subop/add 3/mod/direct 2/rm32/EDX . . . . . 0x30/imm32 # add to EDX
# push EDX
52/push-EDX
eb/jump $print-int32-decimal:read-loop/disp8
# if (EAX == 0) break
3d/compare-EAX-and 0/imm32
7f/jump-if-greater $print-int32-decimal:read-loop/disp8
$print-int32-decimal:read-break:
# if (n < 0) push('-')
81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 0/imm32 # compare *(EBP+12)
7d/jump-if-greater-or-equal $print-int32-decimal:write/disp8
7d/jump-if-greater-or-equal $print-int32-decimal:write/disp8
$print-int32-decimal:push-negative:
68/push -3/imm32/dash-minus-zero
# fall through
68/push 0x2d/imm32/-
$print-int32-decimal:write:
# EDI = out
8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 8/disp8 . # copy *(EBP+8) to EDI
# max/ECX = &out->data[out->length]
8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 1/r32/ECX 8/disp8 . # copy *(EDI+8) to ECX
8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 1/index/ECX . 1/r32/ECX 0xc/disp8 . # copy EDI+ECX+12 to ECX
# w/EAX = out->write
8b/copy 0/mod/indirect 7/rm32/EDI . . . 0/r32/EAX . . # copy *EDI to EAX
# curr/EDI = &out->data[out->write]
8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 0/index/EAX . 7/r32/EDI 0xc/disp8 . # copy EDI+EAX+12 to EDI
# w/EDX = out->write
8b/copy 0/mod/indirect 7/rm32/EDI . . . 2/r32/EDX . . # copy *EDI to EDX
# curr/ECX = &out->data[out->write]
8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 2/index/EDX . 1/r32/ECX 0xc/disp8 . # copy EBX+EDX+12 to ECX
# max/EBX = &out->data[out->length]
8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 3/r32/EBX 8/disp8 . # copy *(EDI+8) to EBX
8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/EDI 3/index/EBX . 3/r32/EBX 0xc/disp8 . # copy EDI+EBX+12 to EBX
$print-int32-decimal:write-loop:
# if (ESP == EBX) break
39/compare 3/mod/direct 4/rm32/ESP . . . 3/r32/EBX . . # compare ESP and EBX
# pop into EAX
58/pop-to-EAX
# if (EAX == sentinel) break
3d/compare-EAX-and 0/imm32/sentinel
74/jump-if-equal $print-int32-decimal:write-break/disp8
# if (curr >= max) abort
39/compare 3/mod/direct 7/rm32/EDI . . . 1/r32/ECX . . # compare EDI and ECX
39/compare 3/mod/direct 1/rm32/ECX . . . 3/r32/EBX . . # compare ECX with EBX
7d/jump-if-greater-or-equal $print-int32-decimal:abort/disp8
# pop into EDX
5a/pop-into-EDX
# EDX += '0'
81 0/subop/add 3/mod/direct 2/rm32/EDX . . . . . 0x30/imm32/zero # add to EDX
$print-int32-decimal:write-char:
# *curr = DL
88/copy-byte 0/mod/indirect 7/rm32/EDI . . . 2/r32/DL . . # copy DL to byte at *ECX
# *curr = AL
88/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy AL to byte at *ECX
# ++curr
47/increment-EDI
41/increment-ECX
# ++w
40/increment-EAX
42/increment-EDX
eb/jump $print-int32-decimal:write-loop/disp8
$print-int32-decimal:write-break:
# out->write = w
8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 8/disp8 . # copy *(EBP+8) to EDI
89/copy 0/mod/indirect 7/rm32/EDI . . . 0/r32/EAX . . # copy EAX to *EDI
89/copy 0/mod/indirect 7/rm32/EDI . . . 2/r32/EDX . . # copy EDX to *EDI
$print-int32-decimal:end:
# . restore registers
5f/pop-to-EDI
@ -166,6 +166,36 @@ test-print-int32-decimal:
# . end
c3/return
test-print-int32-decimal-zero:
# - check that 0 converts correctly
# 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
# print-int32-decimal(_test-stream, 0)
# . . push args
68/push 0/imm32
68/push _test-stream/imm32
# . . call
e8/call print-int32-decimal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
# check-stream-equal(_test-stream, "0", msg)
# . . push args
68/push "F - test-print-int32-decimal-zero"/imm32
68/push "0"/imm32
68/push _test-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-print-int32-decimal-multiple-digits:
# - check that a multi-digit number converts correctly
# setup

Binary file not shown.

View File

@ -26,12 +26,30 @@
Entry: # run tests if necessary, convert stdin if not
# for debugging: run a single test
#? # . Heap = new-segment(4096)
#? # . . push args
#? 68/push Heap/imm32
#? 68/push 0x1000/imm32
#? # . . call
#? e8/call new-segment/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
#? # . test()
#? e8/call test-convert/disp32
#? 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX
#? eb/jump $main:end/disp8
# . prolog
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# initialize heap
# . Heap = new-segment(4096)
# . . push args
68/push Heap/imm32
68/push 0x1000/imm32
# . . call
e8/call new-segment/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
# - if argc > 1 and argv[1] == "test", then return run_tests()
# . argc > 1
81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 0/disp8 1/imm32 # compare *EBP
@ -1316,4 +1334,10 @@ Segment-size:
0x100/imm32
#? 0x1000/imm32/4KB
Heap:
# curr
0/imm32
# limit
0/imm32
# . . vim:nowrap:textwidth=0

Binary file not shown.

Binary file not shown.

View File

@ -22,12 +22,30 @@
Entry: # run tests if necessary, convert stdin if not
# for debugging: run a single test
#? # . Heap = new-segment(4096)
#? # . . push args
#? 68/push Heap/imm32
#? 68/push 0x1000/imm32
#? # . . call
#? e8/call new-segment/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
#? # . test()
#? e8/call test-emit-string-literal-data/disp32
#? 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Num-test-failures/disp32 # copy *Num-test-failures to EBX
#? eb/jump $main:end/disp8
# . prolog
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# initialize heap
# . Heap = new-segment(4096)
# . . push args
68/push Heap/imm32
68/push 0x1000/imm32
# . . call
e8/call new-segment/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
# - if argc > 1 and argv[1] == "test", then return run_tests()
# . argc > 1
81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 0/disp8 1/imm32 # compare *EBP
@ -1692,6 +1710,12 @@ Segment-size:
Next-string-literal: # tracks the next auto-generated variable name
1/imm32
Heap:
# curr
0/imm32
# limit
0/imm32
# length-prefixed string containing just a single space
Space:
# size

Binary file not shown.

Binary file not shown.

View File

@ -79,17 +79,29 @@ test-new:
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# var heap/EDX : (address allocation-descriptor) = {0, 0}
68/push 0/imm32/limit
68/push 0/imm32/curr
89/copy 3/mod/direct 2/rm32/EDX . . . 4/r32/ESP . . # copy ESP to EDX
# heap = new-segment(512)
# . . push args
52/push-EDX
68/push 0x200/imm32
# . . call
e8/call new-segment/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
# *Next-alloc-id = 0x34
c7 0/subop/copy 0/mod/indirect 5/rm32/.disp32 . . . Next-alloc-id/disp32 0x34/imm32 # copy to *Next-alloc-id
# var handle/ECX = {0, 0}
68/push 0/imm32/address
68/push 0/imm32/alloc-id
89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX
# new(Heap, 2, handle/ECX)
# new(heap, 2, handle/ECX)
# . . push args
51/push-ECX
68/push 2/imm32/size
68/push Heap/imm32
52/push-EDX
# . . call
e8/call new/disp32
# . . discard args
@ -234,17 +246,29 @@ test-lookup-success:
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# . save registers
# var heap/EBX : (address allocation-descriptor) = {0, 0}
68/push 0/imm32/limit
68/push 0/imm32/curr
89/copy 3/mod/direct 3/rm32/EBX . . . 4/r32/ESP . . # copy ESP to EBX
# heap = new-segment(512)
# . . push args
53/push-EBX
68/push 0x200/imm32
# . . call
e8/call new-segment/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
# var handle/ECX = {0, 0}
68/push 0/imm32/address
68/push 0/imm32/alloc-id
89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX
# var old_top/EDX = Heap->curr
8b/copy 0/mod/indirect 5/rm32/.disp32 . . 2/r32/EDX Heap/disp32 . # copy *Heap to EDX
# new(Heap, 2, handle)
# var old_top/EDX = heap->curr
8b/copy 0/mod/indirect 3/rm32/EBX . . . 2/r32/EDX . . # copy *EBX to EDX
# new(heap, 2, handle)
# . . push args
51/push-ECX
68/push 2/imm32/size
68/push Heap/imm32
53/push-EBX
# . . call
e8/call new/disp32
# . . discard args
@ -256,7 +280,7 @@ test-lookup-success:
e8/call lookup/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
# EAX contains old top of Heap, except skipping the alloc id in the payload
# EAX contains old top of heap, except skipping the alloc id in the payload
# . check-ints-equal(EAX, old_top+4, msg)
# . . push args
68/push "F - test-lookup-success"/imm32
@ -282,38 +306,46 @@ test-lookup-failure:
# . 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
# var heap/ESI : (address allocation-descriptor) = {0, 0}
68/push 0/imm32/limit
68/push 0/imm32/curr
89/copy 3/mod/direct 6/rm32/ESI . . . 4/r32/ESP . . # copy ESP to ESI
# heap = new-segment(512)
# . . push args
56/push-ESI
68/push 0x200/imm32
# . . call
e8/call new-segment/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
# var h1/ECX = {0, 0}
68/push 0/imm32/address
68/push 0/imm32/alloc-id
89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX
# var old_top/EBX = Heap->curr
8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Heap/disp32 . # copy *Heap to EBX
# var old_top/EBX = heap->curr
8b/copy 0/mod/indirect 6/rm32/ESI . . . 3/r32/EBX . . # copy *ESI to EBX
# first allocation, to h1
# . new(Heap, 2, h1)
# . new(heap, 2, h1)
# . . push args
51/push-ECX
68/push 2/imm32/size
68/push Heap/imm32
56/push-ESI
# . . call
e8/call new/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
# reset Heap->curr to mimic reclamation
89/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/EBX Heap/disp32 . # copy EBX to *Heap
# reset heap->curr to mimic reclamation
89/copy 0/mod/indirect 6/rm32/ESI . . . 3/r32/EBX . . # copy EBX to *ESI
# second allocation that returns the same address as the first
# var h2/EDX = {0, 0}
68/push 0/imm32/address
68/push 0/imm32/alloc-id
89/copy 3/mod/direct 2/rm32/EDX . . . 4/r32/ESP . . # copy ESP to EDX
# . new(Heap, 2, h2)
# . new(heap, 2, h2)
# . . push args
52/push-EDX
68/push 2/imm32/size
68/push Heap/imm32
56/push-ESI
# . . call
e8/call new/disp32
# . . discard args
@ -338,10 +370,6 @@ test-lookup-failure:
# clean up
# . *Next-alloc-id = 1
c7 0/subop/copy 0/mod/indirect 5/rm32/.disp32 . . . Next-alloc-id/disp32 1/imm32 # copy to *Next-alloc-id
# . restore registers
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

Binary file not shown.

Binary file not shown.