Merge branch 'master' into dquotes-1
Segfault in this branch is now fixed.
This commit is contained in:
commit
c12e85e103
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
BIN
subx/apps/assort
BIN
subx/apps/assort
Binary file not shown.
|
@ -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.
|
@ -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.
BIN
subx/apps/handle
BIN
subx/apps/handle
Binary file not shown.
|
@ -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
|
||||
|
|
BIN
subx/apps/hex
BIN
subx/apps/hex
Binary file not shown.
BIN
subx/apps/pack
BIN
subx/apps/pack
Binary file not shown.
Loading…
Reference in New Issue
Block a user