Merge pull request #34 from akkartik/survey

SubX in SubX: computing addresses for labels
This commit is contained in:
Kartik Agaram 2019-07-13 19:50:49 -07:00 committed by GitHub
commit c4aa819a5d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
53 changed files with 9263 additions and 2739 deletions

View File

@ -273,6 +273,7 @@ int main(int argc, char* argv[]) {
search(Current_search_pattern, opposite(Current_search_direction));
}
}
tb_clear();
tb_shutdown();
return 0;
}

View File

@ -203,6 +203,7 @@ inline uint8_t* mem_addr_u8(uint32_t addr) {
if (result == NULL) {
if (Trace_file) Trace_file.flush();
raise << "Tried to access uninitialized memory at address 0x" << HEXWORD << addr << '\n' << end();
exit(1);
}
return result;
}
@ -293,7 +294,6 @@ void run_one_instruction() {
// End Two-Byte Opcodes Starting With 0f
default:
cerr << "unrecognized second opcode after 0f: " << HEXBYTE << NUM(op2) << '\n';
DUMP("");
exit(1);
}
break;
@ -305,13 +305,11 @@ void run_one_instruction() {
// End Three-Byte Opcodes Starting With f2 0f
default:
cerr << "unrecognized third opcode after f2 0f: " << HEXBYTE << NUM(op3) << '\n';
DUMP("");
exit(1);
}
break;
default:
cerr << "unrecognized second opcode after f2: " << HEXBYTE << NUM(op2) << '\n';
DUMP("");
exit(1);
}
break;
@ -323,19 +321,16 @@ void run_one_instruction() {
// End Three-Byte Opcodes Starting With f3 0f
default:
cerr << "unrecognized third opcode after f3 0f: " << HEXBYTE << NUM(op3) << '\n';
DUMP("");
exit(1);
}
break;
default:
cerr << "unrecognized second opcode after f3: " << HEXBYTE << NUM(op2) << '\n';
DUMP("");
exit(1);
}
break;
default:
cerr << "unrecognized opcode: " << HEXBYTE << NUM(op) << '\n';
DUMP("");
exit(1);
}
}

View File

@ -1103,7 +1103,6 @@ case 0xff: {
}
default:
cerr << "unrecognized subop for ff: " << HEXBYTE << NUM(subop) << '\n';
DUMP("");
exit(1);
// End Op ff Subops
}

View File

@ -874,7 +874,7 @@ void test_add_r32_to_mem_at_r32_plus_disp8() {
}
:(before "End Mod Special-cases(addr)")
case 1: // indirect + disp8 addressing
case 1: { // indirect + disp8 addressing
switch (rm) {
default:
addr = Reg[rm].u;
@ -882,11 +882,16 @@ case 1: // indirect + disp8 addressing
break;
// End Mod 1 Special-cases(addr)
}
int8_t displacement = static_cast<int8_t>(next());
if (addr > 0) {
addr += static_cast<int8_t>(next());
addr += displacement;
trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << addr << " (after adding disp8)" << end();
}
else {
trace(Callstack_depth+1, "run") << "null address; skipping displacement" << end();
}
break;
}
:(code)
void test_add_r32_to_mem_at_r32_plus_negative_disp8() {

View File

@ -6,7 +6,7 @@
put_new(Name, "eb", "jump disp8 bytes away (jmp)");
:(code)
void test_jump_rel8() {
void test_jump_disp8() {
run(
"== code 0x1\n"
// op ModR/M SIB displacement immediate
@ -23,7 +23,7 @@ void test_jump_rel8() {
}
:(before "End Single-Byte Opcodes")
case 0xeb: { // jump rel8
case 0xeb: { // jump disp8
int8_t offset = static_cast<int>(next());
trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end();
EIP += offset;
@ -36,7 +36,7 @@ case 0xeb: { // jump rel8
put_new(Name, "74", "jump disp8 bytes away if equal, if ZF is set (jcc/jz/je)");
:(code)
void test_je_rel8_success() {
void test_je_disp8_success() {
ZF = true;
run(
"== code 0x1\n"
@ -54,7 +54,7 @@ void test_je_rel8_success() {
}
:(before "End Single-Byte Opcodes")
case 0x74: { // jump rel8 if ZF
case 0x74: { // jump disp8 if ZF
const int8_t offset = static_cast<int>(next());
if (ZF) {
trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end();
@ -64,7 +64,7 @@ case 0x74: { // jump rel8 if ZF
}
:(code)
void test_je_rel8_fail() {
void test_je_disp8_fail() {
ZF = false;
run(
"== code 0x1\n"
@ -87,7 +87,7 @@ void test_je_rel8_fail() {
put_new(Name, "75", "jump disp8 bytes away if not equal, if ZF is not set (jcc/jnz/jne)");
:(code)
void test_jne_rel8_success() {
void test_jne_disp8_success() {
ZF = false;
run(
"== code 0x1\n"
@ -105,7 +105,7 @@ void test_jne_rel8_success() {
}
:(before "End Single-Byte Opcodes")
case 0x75: { // jump rel8 unless ZF
case 0x75: { // jump disp8 unless ZF
const int8_t offset = static_cast<int>(next());
if (!ZF) {
trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end();
@ -115,7 +115,7 @@ case 0x75: { // jump rel8 unless ZF
}
:(code)
void test_jne_rel8_fail() {
void test_jne_disp8_fail() {
ZF = true;
run(
"== code 0x1\n"
@ -139,7 +139,7 @@ put_new(Name, "7f", "jump disp8 bytes away if greater (signed), if ZF is unset a
put_new(Name, "77", "jump disp8 bytes away if greater (unsigned), if ZF is unset and CF is unset (jcc/ja/jnbe)");
:(code)
void test_jg_rel8_success() {
void test_jg_disp8_success() {
ZF = false;
SF = false;
OF = false;
@ -159,7 +159,7 @@ void test_jg_rel8_success() {
}
:(before "End Single-Byte Opcodes")
case 0x7f: { // jump rel8 if SF == OF and !ZF
case 0x7f: { // jump disp8 if SF == OF and !ZF
const int8_t offset = static_cast<int>(next());
if (SF == OF && !ZF) {
trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end();
@ -167,7 +167,7 @@ case 0x7f: { // jump rel8 if SF == OF and !ZF
}
break;
}
case 0x77: { // jump rel8 if !CF and !ZF
case 0x77: { // jump disp8 if !CF and !ZF
const int8_t offset = static_cast<int>(next());
if (!CF && !ZF) {
trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end();
@ -177,7 +177,7 @@ case 0x77: { // jump rel8 if !CF and !ZF
}
:(code)
void test_jg_rel8_fail() {
void test_jg_disp8_fail() {
ZF = false;
SF = true;
OF = false;
@ -203,7 +203,7 @@ put_new(Name, "7d", "jump disp8 bytes away if greater or equal (signed), if SF =
put_new(Name, "73", "jump disp8 bytes away if greater or equal (unsigned), if CF is unset (jcc/jae/jnb)");
:(code)
void test_jge_rel8_success() {
void test_jge_disp8_success() {
SF = false;
OF = false;
run(
@ -222,7 +222,7 @@ void test_jge_rel8_success() {
}
:(before "End Single-Byte Opcodes")
case 0x7d: { // jump rel8 if SF == OF
case 0x7d: { // jump disp8 if SF == OF
const int8_t offset = static_cast<int>(next());
if (SF == OF) {
trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end();
@ -230,7 +230,7 @@ case 0x7d: { // jump rel8 if SF == OF
}
break;
}
case 0x73: { // jump rel8 if !CF
case 0x73: { // jump disp8 if !CF
const int8_t offset = static_cast<int>(next());
if (!CF) {
trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end();
@ -240,7 +240,7 @@ case 0x73: { // jump rel8 if !CF
}
:(code)
void test_jge_rel8_fail() {
void test_jge_disp8_fail() {
SF = true;
OF = false;
run(
@ -265,7 +265,7 @@ put_new(Name, "7c", "jump disp8 bytes away if lesser (signed), if SF != OF (jcc/
put_new(Name, "72", "jump disp8 bytes away if lesser (unsigned), if CF is set (jcc/jb/jnae)");
:(code)
void test_jl_rel8_success() {
void test_jl_disp8_success() {
ZF = false;
SF = true;
OF = false;
@ -285,7 +285,7 @@ void test_jl_rel8_success() {
}
:(before "End Single-Byte Opcodes")
case 0x7c: { // jump rel8 if SF != OF
case 0x7c: { // jump disp8 if SF != OF
const int8_t offset = static_cast<int>(next());
if (SF != OF) {
trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end();
@ -293,7 +293,7 @@ case 0x7c: { // jump rel8 if SF != OF
}
break;
}
case 0x72: { // jump rel8 if CF
case 0x72: { // jump disp8 if CF
const int8_t offset = static_cast<int>(next());
if (CF) {
trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end();
@ -303,7 +303,7 @@ case 0x72: { // jump rel8 if CF
}
:(code)
void test_jl_rel8_fail() {
void test_jl_disp8_fail() {
ZF = false;
SF = false;
OF = false;
@ -329,7 +329,7 @@ put_new(Name, "7e", "jump disp8 bytes away if lesser or equal (signed), if ZF is
put_new(Name, "76", "jump disp8 bytes away if lesser or equal (unsigned), if ZF is set or CF is set (jcc/jbe/jna)");
:(code)
void test_jle_rel8_equal() {
void test_jle_disp8_equal() {
ZF = true;
SF = false;
OF = false;
@ -349,7 +349,7 @@ void test_jle_rel8_equal() {
}
:(code)
void test_jle_rel8_lesser() {
void test_jle_disp8_lesser() {
ZF = false;
SF = true;
OF = false;
@ -369,7 +369,7 @@ void test_jle_rel8_lesser() {
}
:(before "End Single-Byte Opcodes")
case 0x7e: { // jump rel8 if ZF or SF != OF
case 0x7e: { // jump disp8 if ZF or SF != OF
const int8_t offset = static_cast<int>(next());
if (ZF || SF != OF) {
trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end();
@ -377,7 +377,7 @@ case 0x7e: { // jump rel8 if ZF or SF != OF
}
break;
}
case 0x76: { // jump rel8 if ZF or CF
case 0x76: { // jump disp8 if ZF or CF
const int8_t offset = static_cast<int>(next());
if (ZF || CF) {
trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end();
@ -387,7 +387,7 @@ case 0x76: { // jump rel8 if ZF or CF
}
:(code)
void test_jle_rel8_greater() {
void test_jle_disp8_greater() {
ZF = false;
SF = false;
OF = false;

View File

@ -57,7 +57,7 @@ void test_je_disp32_success() {
case 0x84: { // jump disp32 if ZF
const int32_t offset = next32();
if (ZF) {
trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end();
trace(Callstack_depth+1, "run") << "jump " << offset << end();
EIP += offset;
}
break;
@ -108,7 +108,7 @@ void test_jne_disp32_success() {
case 0x85: { // jump disp32 unless ZF
const int32_t offset = next32();
if (!ZF) {
trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end();
trace(Callstack_depth+1, "run") << "jump " << offset << end();
EIP += offset;
}
break;
@ -135,7 +135,8 @@ void test_jne_disp32_fail() {
//:: jump if greater
:(before "End Initialize Op Names")
put_new(Name_0f, "8f", "jump disp32 bytes away if greater, if ZF is unset and SF == OF (jcc/jg/jnle)");
put_new(Name_0f, "8f", "jump disp32 bytes away if greater (signed), if ZF is unset and SF == OF (jcc/jg/jnle)");
put_new(Name_0f, "87", "jump disp32 bytes away if greater (unsigned), if ZF is unset and CF is unset (jcc/ja/jnbe)");
:(code)
void test_jg_disp32_success() {
@ -161,7 +162,15 @@ void test_jg_disp32_success() {
case 0x8f: { // jump disp32 if !SF and !ZF
const int32_t offset = next32();
if (!ZF && SF == OF) {
trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end();
trace(Callstack_depth+1, "run") << "jump " << offset << end();
EIP += offset;
}
break;
}
case 0x87: { // jump disp32 if !CF and !ZF
const int32_t offset = next();
if (!CF && !ZF) {
trace(Callstack_depth+1, "run") << "jump " << offset << end();
EIP += offset;
}
break;
@ -190,7 +199,8 @@ void test_jg_disp32_fail() {
//:: jump if greater or equal
:(before "End Initialize Op Names")
put_new(Name_0f, "8d", "jump disp32 bytes away if greater or equal, if SF == OF (jcc/jge/jnl)");
put_new(Name_0f, "8d", "jump disp32 bytes away if greater or equal (signed), if SF == OF (jcc/jge/jnl)");
put_new(Name_0f, "83", "jump disp32 bytes away if greater or equal (unsigned), if CF is unset (jcc/jae/jnb)");
:(code)
void test_jge_disp32_success() {
@ -215,7 +225,15 @@ void test_jge_disp32_success() {
case 0x8d: { // jump disp32 if !SF
const int32_t offset = next32();
if (SF == OF) {
trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end();
trace(Callstack_depth+1, "run") << "jump " << offset << end();
EIP += offset;
}
break;
}
case 0x83: { // jump disp32 if !CF
const int32_t offset = next32();
if (!CF) {
trace(Callstack_depth+1, "run") << "jump " << offset << end();
EIP += offset;
}
break;
@ -243,7 +261,8 @@ void test_jge_disp32_fail() {
//:: jump if lesser
:(before "End Initialize Op Names")
put_new(Name_0f, "8c", "jump disp32 bytes away if lesser, if SF != OF (jcc/jl/jnge)");
put_new(Name_0f, "8c", "jump disp32 bytes away if lesser (signed), if SF != OF (jcc/jl/jnge)");
put_new(Name_0f, "82", "jump disp32 bytes away if lesser (unsigned), if CF is set (jcc/jb/jnae)");
:(code)
void test_jl_disp32_success() {
@ -269,7 +288,15 @@ void test_jl_disp32_success() {
case 0x8c: { // jump disp32 if SF and !ZF
const int32_t offset = next32();
if (SF != OF) {
trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end();
trace(Callstack_depth+1, "run") << "jump " << offset << end();
EIP += offset;
}
break;
}
case 0x72: { // jump disp32 if CF
const int32_t offset = next32();
if (CF) {
trace(Callstack_depth+1, "run") << "jump " << offset << end();
EIP += offset;
}
break;
@ -298,7 +325,8 @@ void test_jl_disp32_fail() {
//:: jump if lesser or equal
:(before "End Initialize Op Names")
put_new(Name_0f, "8e", "jump disp32 bytes away if lesser or equal, if ZF is set or SF != OF (jcc/jle/jng)");
put_new(Name_0f, "8e", "jump disp32 bytes away if lesser or equal (signed), if ZF is set or SF != OF (jcc/jle/jng)");
put_new(Name_0f, "86", "jump disp8 bytes away if lesser or equal (unsigned), if ZF is set or CF is set (jcc/jbe/jna)");
:(code)
void test_jle_disp32_equal() {
@ -344,7 +372,15 @@ void test_jle_disp32_lesser() {
case 0x8e: { // jump disp32 if SF or ZF
const int32_t offset = next32();
if (ZF || SF != OF) {
trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end();
trace(Callstack_depth+1, "run") << "jump " << offset << end();
EIP += offset;
}
break;
}
case 0x86: { // jump disp32 if ZF or CF
const int32_t offset = next32();
if (ZF || CF) {
trace(Callstack_depth+1, "run") << "jump " << offset << end();
EIP += offset;
}
break;

View File

@ -162,7 +162,7 @@ case 0xc6: { // copy imm8 to r/m8
const uint8_t modrm = next();
const uint8_t src = next();
trace(Callstack_depth+1, "run") << "copy imm8 to r8/m8-at-r32" << end();
trace(Callstack_depth+1, "run") << "imm8 is 0x" << HEXWORD << src << end();
trace(Callstack_depth+1, "run") << "imm8 is 0x" << HEXWORD << NUM(src) << end();
const uint8_t subop = (modrm>>3)&0x7; // middle 3 'reg opcode' bits
if (subop != 0) {
cerr << "unrecognized subop for opcode c6: " << NUM(subop) << " (only 0/copy currently implemented)\n";

View File

@ -225,12 +225,11 @@ void init_permitted_operands() {
put(Permitted_operands, "87", 0x01);
// copy address (lea)
put(Permitted_operands, "8d", 0x01);
// pop
put(Permitted_operands, "8f", 0x01);
//// Class N: op, ModR/M and subop (not r32)
// imm32 imm8 disp32 |disp16 disp8 subop modrm
// 0 0 0 |0 0 1 1
put(Permitted_operands, "8f", 0x03); // pop
put(Permitted_operands, "d3", 0x03); // shift
put(Permitted_operands, "f7", 0x03); // test/not/mul/div
put(Permitted_operands, "ff", 0x03); // jump/push/call
@ -627,8 +626,12 @@ map</*op*/string, /*bitvector*/uint8_t> Permitted_operands_0f;
//// Class D: just op and disp32
// imm32 imm8 disp32 |disp16 disp8 subop modrm
// 0 0 1 |0 0 0 0
put_new(Permitted_operands_0f, "82", 0x10);
put_new(Permitted_operands_0f, "83", 0x10);
put_new(Permitted_operands_0f, "84", 0x10);
put_new(Permitted_operands_0f, "85", 0x10);
put_new(Permitted_operands_0f, "86", 0x10);
put_new(Permitted_operands_0f, "87", 0x10);
put_new(Permitted_operands_0f, "8c", 0x10);
put_new(Permitted_operands_0f, "8d", 0x10);
put_new(Permitted_operands_0f, "8e", 0x10);

View File

@ -74,9 +74,9 @@ Watch_points.clear();
:(code)
void dump_watch_points() {
if (Watch_points.empty()) return;
dbg << "watch points:" << end();
trace(Callstack_depth, "dbg") << "watch points:" << end();
for (map<string, uint32_t>::iterator p = Watch_points.begin(); p != Watch_points.end(); ++p)
dbg << " " << p->first << ": " << HEXWORD << p->second << " -> " << HEXWORD << read_mem_u32(p->second) << end();
trace(Callstack_depth, "dbg") << " " << p->first << ": " << HEXWORD << p->second << " -> " << HEXWORD << read_mem_u32(p->second) << end();
}
:(before "End Globals")
@ -91,6 +91,20 @@ if (!Watch_this_effective_address.empty()) {
put(Watch_points, Watch_this_effective_address, addr);
}
//: Special label that dumps regions of memory.
//: Not a general mechanism; by the time you get here you're willing to hack
//: on the emulator.
:(after "Run One Instruction")
if (contains_key(Symbol_name, EIP) && get(Symbol_name, EIP) == "$dump-stream-at-EAX")
dump_stream_at(Reg[EAX].u);
:(code)
void dump_stream_at(uint32_t stream_start) {
int32_t stream_length = read_mem_i32(stream_start + 8);
dbg << "stream length: " << std::dec << stream_length << end();
for (int i = 0; i < stream_length + 12; ++i)
dbg << "0x" << HEXWORD << (stream_start+i) << ": " << HEXBYTE << NUM(read_mem_u8(stream_start+i)) << end();
}
//: helpers
:(code)

View File

@ -15,22 +15,20 @@ Entry: # run all tests
string-equal?: # s : (address string), benchmark : (address string) -> EAX : boolean
# pseudocode:
# lens = s->length
# if (lens != benchmark->length) return false
# i = 0
# if (s->length != benchmark->length) return false
# currs = s->data
# currb = benchmark->data
# while i < s->length
# maxs = s->data + s->length
# while currs < maxs
# c1 = *currs
# c2 = *currb
# if (c1 != c2) return false
# ++i, ++currs, ++currb
# ++currs, ++currb
# return true
#
# registers:
# i: ECX
# lens: EDX
# currs: ESI
# maxs: ECX
# currb: EDI
# c1: EAX
# c2: EBX
@ -41,40 +39,38 @@ string-equal?: # s : (address string), benchmark : (address string) -> EAX : bo
# . save registers
51/push-ECX
52/push-EDX
53/push-EBX
56/push-ESI
57/push-EDI
# ESI = s
8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI
# EDI = benchmark
8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI
# lens/EDX = s->length
8b/copy 0/mod/indirect 6/rm32/ESI . . . 2/r32/EDX . . # copy *ESI to EDX
# ECX = s->length
8b/copy 0/mod/indirect 6/rm32/ESI . . . 1/r32/ECX . . # copy *ESI to ECX
$string-equal?:lengths:
# if (lens != benchmark->length) return false
39/compare 0/mod/indirect 7/rm32/EDI . . . 2/r32/EDX . . # compare *EDI and EDX
# if (ECX != benchmark->length) return false
39/compare 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # compare *EDI and ECX
75/jump-if-not-equal $string-equal?:false/disp8
# currs/ESI = s->data
81 0/subop/add 3/mod/direct 6/rm32/ESI . . . . . 4/imm32 # add to ESI
# maxs/ECX = s->data + s->length
01/add 3/mod/direct 1/rm32/ECX . . . 6/r32/ESI . . # add ESI to ECX
# currb/EDI = benchmark->data
81 0/subop/add 3/mod/direct 7/rm32/EDI . . . . . 4/imm32 # add to EDI
# i/ECX = c1/EAX = c2/EBX = 0
31/xor 3/mod/direct 1/rm32/ECX . . . 1/r32/ECX . . # clear ECX
# c1/EAX = c2/EDX = 0
31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX
31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX
31/xor 3/mod/direct 2/rm32/EDX . . . 2/r32/EDX . . # clear EDX
$string-equal?:loop:
# if (i >= lens) return true
39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX
7d/jump-if-greater-or-equal $string-equal?:true/disp8
# if (currs >= maxs) return true
39/compare 3/mod/direct 6/rm32/ESI . . . 1/r32/ECX . . # compare ESI with ECX
73/jump-if-greater-or-equal-unsigned $string-equal?:true/disp8
# c1 = *currs
8a/copy-byte 0/mod/indirect 6/rm32/ESI . . . 0/r32/AL . . # copy byte at *ESI to AL
# c2 = *currb
8a/copy-byte 0/mod/indirect 7/rm32/EDI . . . 3/r32/BL . . # copy byte at *EDI to BL
8a/copy-byte 0/mod/indirect 7/rm32/EDI . . . 2/r32/DL . . # copy byte at *EDI to DL
# if (c1 != c2) return false
39/compare 3/mod/direct 0/rm32/EAX . . . 3/r32/EBX . . # compare EAX and EBX
39/compare 3/mod/direct 0/rm32/EAX . . . 2/r32/EDX . . # compare EAX and EDX
75/jump-if-not-equal $string-equal?:false/disp8
# ++i
41/increment-ECX
# ++currs
46/increment-ESI
# ++currb
@ -89,7 +85,6 @@ $string-equal?:end:
# . restore registers
5f/pop-to-EDI
5e/pop-to-ESI
5b/pop-to-EBX
5a/pop-to-EDX
59/pop-to-ECX
# . epilog
@ -179,4 +174,57 @@ test-compare-inequal-strings-equal-lengths:
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
c3/return
# helper for later tests
check-string-equal: # s : (address string), expected : (address string), msg : (address string)
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# . save registers
50/push-EAX
# EAX = string-equal?(s, expected)
# . . push args
ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12)
ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8)
# . . call
e8/call string-equal?/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
ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16)
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
$check-string-equal:end:
# . restore registers
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 the helper
test-check-string-equal:
# check-string-equal?("Abc", "Abc")
# . . push args
68/push "Abc"/imm32
68/push "Abc"/imm32
# . . call
e8/call check-string-equal/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-check-string-equal"/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
c3/return
# . . vim:nowrap:textwidth=0

View File

@ -37,7 +37,7 @@ clear-stream: # f : (address stream) -> <void>
$clear-stream:loop:
# if (EAX >= ECX) break
39/compare 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # compare EAX with ECX
7d/jump-if-greater-or-equal $clear-stream:end/disp8
73/jump-if-greater-or-equal-unsigned $clear-stream:end/disp8
# *EAX = 0
c6 0/subop/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm8 # copy byte to *EAX
# ++EAX

View File

@ -4,19 +4,13 @@
# write : int # index at which writes go
# read : int # index that we've read until
# data : (array byte) # prefixed by length as usual
# In a real trace the data will be in a special segment set aside for the purpose.
# Usually the trace stream will be in a separate segment set aside for the purpose.
#
# primitives for operating on traces:
# - initialize-trace-stream (update global variable)
# - trace: stream, string
# - die: stream (exit(1) if using real trace)
# - check-trace-contains: stream, string/line, string/message (scans only from stream's read pointer, prints message to stderr on failure, updates stream's read pointer)
# - scan-to-next-line: stream (advance read pointer past next newline)
#
# Traces are very fundamental, so many of the helpers we create here won't be
# used elsewhere; we'll switch to more bounds-checked variants. But here we get
# bounds-checking for free; we allocate a completely disjoint segment for trace
# data, and overflowing it will generate a page fault.
# primitives for operating on traces (arguments in quotes):
# - initialize-trace-stream: populates Trace-stream with a new segment of the given 'size'
# - trace: adds a 'line' to Trace-stream
# - check-trace-contains: scans from Trace-stream's start for a matching 'line', prints a 'message' to stderr on failure
# - check-trace-scans-to: scans from Trace-stream's read pointer for a matching 'line', prints a 'message' to stderr on failure
== data
@ -24,6 +18,10 @@
Trace-stream:
0/imm32
Trace-segment:
0/imm32/curr
0/imm32/limit
# Fake trace-stream for tests.
# Also illustrates the layout of the real trace-stream (segment).
_test-trace-stream:
@ -43,23 +41,45 @@ _test-trace-stream:
# Allocate a new segment for the trace stream, initialize its length, and save its address to Trace-stream.
# The Trace-stream segment will consist of variable-length lines separated by newlines (0x0a)
initialize-trace-stream:
# EAX = new-segment(0x1000)
initialize-trace-stream: # n : 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
# ECX = n
8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX
# Trace-segment = new-segment(n)
# . . push args
68/push 0x1000/imm32/N
68/push Trace-segment/imm32
51/push-ECX
# . . call
e8/call new-segment/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
# copy EAX to *Trace-stream
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
# copy Trace-segment->curr to *Trace-stream
8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Trace-segment/disp32 # copy *Trace-segment to EAX
# watch point to catch Trace-stream leaks
#? $watch-1:
89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Trace-stream/disp32 # copy EAX to *Trace-stream
# Trace-stream->length = 0x1000/N - 12
c7 0/subop/copy 1/mod/*+disp8 0/rm32/EAX . . . . 8/disp8 0xff4/imm32 # copy 0xff4 to *(EAX+8)
# Trace-stream->length = n - 12
# . ECX -= 12
81 5/subop/subtract 3/mod/direct 1/rm32/ECX . . . . . 0xc/imm32 # subtract from ECX
# . Trace-stream->length = ECX
89/copy 1/mod/*+disp8 0/rm32/EAX . . . 1/r32/ECX 8/disp8 . # copy ECX to *(EAX+8)
$initialize-trace-stream:end:
# . restore registers
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
# Append a string to the given trace stream.
# Silently give up if it's already full. Or truncate the string if there isn't enough room.
trace: # t : (address trace-stream), line : string
trace: # line : (address string)
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
@ -70,10 +90,10 @@ trace: # t : (address trace-stream), line : string
53/push-EBX
56/push-ESI
57/push-EDI
# EDI = t
8b/copy 1/mod/*+disp8 5/rm32/EBP . . 7/r32/EDI 8/disp8 . # copy *(EBP+8) to EDI
# EDI = *Trace-stream
8b/copy 0/mod/indirect 5/rm32/.disp32 . . 7/r32/EDI Trace-stream/disp32 # copy *Trace-stream to EDI
# ESI = line
8b/copy 1/mod/*+disp8 5/rm32/EBP . . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI
8b/copy 1/mod/*+disp8 5/rm32/EBP . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI
# ECX = t->write
8b/copy 0/mod/indirect 7/rm32/EDI . . . 1/r32/ECX . . # copy *EDI to ECX
# EDX = t->length
@ -126,15 +146,707 @@ $trace:end:
5d/pop-to-EBP
c3/return
clear-trace-stream: # t : (address trace-stream)
test-trace-single:
# push *Trace-stream
ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream
# *Trace-stream = _test-trace-stream
b8/copy-to-EAX _test-trace-stream/imm32
89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Trace-stream/disp32 # copy EAX to *Trace-stream
# clear-trace-stream()
e8/call clear-trace-stream/disp32
# trace("Ab")
# . . push args
68/push "Ab"/imm32
# . . call
e8/call trace/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
# check-ints-equal(*_test-trace-stream->data, 41/A 62/b 0a/newline 00, msg)
# . . push args
68/push "F - test-trace-single"/imm32
68/push 0x0a6241/imm32/Ab-newline
# . . push *_test-trace-stream->data
b8/copy-to-EAX _test-trace-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
# pop into *Trace-stream
8f 0/subop/pop 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # pop into *Trace-stream
# end
c3/return
test-trace-appends:
# push *Trace-stream
ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream
# *Trace-stream = _test-trace-stream
b8/copy-to-EAX _test-trace-stream/imm32
89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Trace-stream/disp32 # copy EAX to *Trace-stream
# clear-trace-stream()
e8/call clear-trace-stream/disp32
# trace("C")
# . . push args
68/push "C"/imm32
# . . call
e8/call trace/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
# trace("D")
# . . push args
68/push "D"/imm32
# . . call
e8/call trace/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
# check-ints-equal(*_test-trace-stream->data, 43/C 0a/newline 44/D 0a/newline, msg)
# . . push args
68/push "F - test-trace-appends"/imm32
68/push 0x0a440a43/imm32/C-newline-D-newline
# . . push *_test-trace-stream->data
b8/copy-to-EAX _test-trace-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
# pop into *Trace-stream
8f 0/subop/pop 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # pop into *Trace-stream
# end
c3/return
test-trace-empty-line:
# push *Trace-stream
ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream
# *Trace-stream = _test-trace-stream
b8/copy-to-EAX _test-trace-stream/imm32
89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Trace-stream/disp32 # copy EAX to *Trace-stream
# clear-trace-stream()
e8/call clear-trace-stream/disp32
# trace("")
# . . push args
68/push ""/imm32
# . . call
e8/call trace/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
# check-ints-equal(*_test-trace-stream->data, 0, msg)
# . . push args
68/push "F - test-trace-empty-line"/imm32
68/push 0/imm32
# . . push *_test-trace-stream->data
b8/copy-to-EAX _test-trace-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
# pop into *Trace-stream
8f 0/subop/pop 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # pop into *Trace-stream
# end
c3/return
check-trace-contains: # line : (address string), msg : (address string)
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# rewind-stream(*Trace-stream)
# . . push args
ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream
# . . call
e8/call rewind-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
# check-trace-scans-to(line, msg)
# . . push args
ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12)
ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8)
# . . call
e8/call check-trace-scans-to/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
$check-trace-contains:end:
# . epilog
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
5d/pop-to-EBP
c3/return
check-trace-scans-to: # line : (address string), msg : (address string)
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# . save registers
50/push-EAX
# EAX = trace-scan(line)
# . . push args
ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8)
# . . call
e8/call trace-scan/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
ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12)
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
$check-trace-scans-to:end:
# . restore registers
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
# Start scanning from Trace-stream->read for 'line'. If found, update Trace-stream->read and return true.
trace-scan: # line : (address string) -> result/EAX : boolean
# pseudocode:
# push Trace-stream->read
# while true:
# if Trace-stream->read >= Trace-stream->write
# break
# if next-line-matches?(Trace-stream, line)
# skip-next-line(Trace-stream)
# dump saved copy of Trace-stream->read
# return true
# skip-next-line(Trace-stream)
# pop saved copy of Trace-stream->read
# 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
56/push-ESI
# ESI = *Trace-stream
8b/copy 0/mod/indirect 5/rm32/.disp32 . . 6/r32/ESI Trace-stream/disp32 # copy *Trace-stream to ESI
# ECX = Trace-stream->write
8b/copy 0/mod/indirect 6/rm32/ESI . . . 1/r32/ECX . # copy *ESI to ECX
# push Trace-stream->read
ff 6/subop/push 1/mod/*+disp8 6/rm32/ESI . . . . 4/disp8 . # push *(ESI+4)
$trace-scan:loop:
# if (Trace-stream->read >= Trace-stream->write) return false
39/compare 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # compare ECX with *(ESI+4)
7d/jump-if-greater-or-equal $trace-scan:false/disp8
# EAX = next-line-matches?(Trace-stream, line)
# . . push args
ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8)
56/push-ESI
# . . call
e8/call next-line-matches?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
# if (EAX == 0) continue
3d/compare-EAX-and 0/imm32
74/jump-if-equal $trace-scan:continue/disp8
$trace-scan:true:
# skip-next-line(Trace-stream)
# . . push args
56/push-ESI
# . . call
e8/call skip-next-line/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
# dump saved copy of Trace-stream->read
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
# return true
b8/copy-to-EAX 1/imm32/true
eb/jump $trace-scan:end/disp8
$trace-scan:continue:
# skip-next-line(Trace-stream)
# . . push args
56/push-ESI
# . . call
e8/call skip-next-line/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
eb/jump $trace-scan:loop/disp8
$trace-scan:false:
# restore saved copy of Trace-stream->read
8f 0/subop/pop 1/mod/*+disp8 6/rm32/ESI . . . . 4/disp8 . # pop to *(ESI+4)
# return false
b8/copy-to-EAX 0/imm32/false
$trace-scan: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-trace-scan-first:
# push *Trace-stream
ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream
# setup
# . *Trace-stream = _test-trace-stream
b8/copy-to-EAX _test-trace-stream/imm32
89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Trace-stream/disp32 # copy EAX to *Trace-stream
# . clear-trace-stream()
e8/call clear-trace-stream/disp32
# . trace("Ab")
# . . push args
68/push "Ab"/imm32
# . . call
e8/call trace/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
# EAX = trace-scan("Ab")
# . . push args
68/push "Ab"/imm32
# . . call
e8/call trace-scan/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-trace-scan-first"/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
# pop into *Trace-stream
8f 0/subop/pop 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # pop into *Trace-stream
# . end
c3/return
test-trace-scan-skips-lines-until-found:
# push *Trace-stream
ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream
# setup
# . *Trace-stream = _test-trace-stream
b8/copy-to-EAX _test-trace-stream/imm32
89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Trace-stream/disp32 # copy EAX to *Trace-stream
# . clear-trace-stream()
e8/call clear-trace-stream/disp32
# . trace("Ab")
# . . push args
68/push "Ab"/imm32
# . . call
e8/call trace/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
# . trace("cd")
# . . push args
68/push "cd"/imm32
# . . call
e8/call trace/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
# EAX = trace-scan("cd")
# . . push args
68/push "cd"/imm32
# . . call
e8/call trace-scan/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-trace-scan-skips-lines-until-found"/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
# pop into *Trace-stream
8f 0/subop/pop 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # pop into *Trace-stream
# . end
c3/return
test-trace-second-scan-starts-where-first-left-off:
# push *Trace-stream
ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream
# setup
# . *Trace-stream = _test-trace-stream
b8/copy-to-EAX _test-trace-stream/imm32
89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Trace-stream/disp32 # copy EAX to *Trace-stream
# . clear-trace-stream()
e8/call clear-trace-stream/disp32
# . trace("Ab")
# . . push args
68/push "Ab"/imm32
# . . call
e8/call trace/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
# . EAX = trace-scan("Ab")
# . . push args
68/push "Ab"/imm32
# . . call
e8/call trace-scan/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
# second scan fails
# . EAX = trace-scan("Ab")
# . . push args
68/push "Ab"/imm32
# . . call
e8/call trace-scan/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-trace-second-scan-starts-where-first-left-off"/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
# pop into *Trace-stream
8f 0/subop/pop 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # pop into *Trace-stream
# . end
c3/return
test-trace-scan-failure-leaves-read-index-untouched:
# push *Trace-stream
ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream
# setup
# . *Trace-stream = _test-trace-stream
b8/copy-to-EAX _test-trace-stream/imm32
89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Trace-stream/disp32 # copy EAX to *Trace-stream
# . clear-trace-stream()
e8/call clear-trace-stream/disp32
# . trace("Ab")
# . . push args
68/push "Ab"/imm32
# . . call
e8/call trace/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
# . check-ints-equal(_test-trace-stream->read, 0, msg)
# . . push args
68/push "F - test-trace-second-scan-starts-where-first-left-off/precondition-failure"/imm32
68/push 0/imm32
b8/copy-to-EAX _test-trace-stream/imm32
ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4)
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
# perform a failing scan
# . EAX = trace-scan("Ax")
# . . push args
68/push "Ax"/imm32
# . . call
e8/call trace-scan/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
# no change in read index
# . check-ints-equal(_test-trace-stream->read, 0, msg)
# . . push args
68/push "F - test-trace-second-scan-starts-where-first-left-off"/imm32
68/push 0/imm32
b8/copy-to-EAX _test-trace-stream/imm32
ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4)
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
# pop into *Trace-stream
8f 0/subop/pop 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # pop into *Trace-stream
# . end
c3/return
next-line-matches?: # t : (address stream), line : (address string) -> result/EAX : boolean
# pseudocode:
# while true:
# if (currl >= maxl) break
# if (currt >= maxt) return false
# if (*currt != *currl) return false
# ++currt
# ++currl
# return *currt == '\n'
#
# . 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
53/push-EBX
56/push-ESI
57/push-EDI
# EDX = line
8b/copy 1/mod/*+disp8 5/rm32/EBP . . 2/r32/EDX 0xc/disp8 . # copy *(EBP+12) to EDX
# currl/ESI = line->data
# . ESI = line/EDX->data
8d/copy-address 1/mod/*+disp8 2/rm32/EDX . . . 6/r32/ESI 4/disp8 . # copy EDX+4 to ESI
# maxl/ECX = line->data + line->size
# . EAX = line/EDX->size
8b/copy 0/mod/indirect 2/rm32/EDX . . 0/r32/EAX . . # copy *EDX to EAX
# . maxl/ECX = line->data/ESI + line->size/EAX
8d/copy-address 0/mod/indirect 4/rm32/sib 6/base/ESI 0/index/EAX . 1/r32/ECX . . # copy EDX+EAX to ECX
# EDI = t
8b/copy 1/mod/*+disp8 5/rm32/EBP . . 7/r32/EDI 8/disp8 . # copy *(EBP+8) to EDI
# EBX = t->data
8d/copy-address 1/mod/*+disp8 7/rm32/EDI . . . 3/r32/EBX 0xc/disp8 . # copy EDI+12 to EBX
# maxt/EDX = t->data + t->write
# . EAX = t->write
8b/copy 0/mod/indirect 7/rm32/EDI . . 0/r32/EAX . . # copy *EDI to EAX
# . maxt/EDX = t->data/EBX + t->write/EAX
8d/copy-address 0/mod/indirect 4/rm32/sib 3/base/EBX 0/index/EAX . 2/r32/EDX . . # copy EBX+EAX to EDX
# currt/EDI = t->data + t->read
# . EAX = t/EDI->read
8b/copy 1/mod/*+disp8 7/rm32/EDI . . 0/r32/EAX 4/disp8 . # copy *(EDI+4) to EAX
# . currt/EDI = t->data/EBX + t->read/EAX
8d/copy-address 0/mod/indirect 4/rm32/sib 3/base/EBX 0/index/EAX . 7/r32/EDI . . # copy EBX+EAX to EDI
$next-line-matches?:loop:
# if (currl/ESI >= maxl/ECX) break
39/compare 3/mod/direct 6/rm32/ESI . . . 1/r32/ECX . . # compare ESI and ECX
73/jump-if-greater-or-equal-unsigned $next-line-matches?:break/disp8
# if (currt/EDI >= maxt/EDX) return false
# . EAX = false
b8/copy-to-EAX 0/imm32/false
39/compare 3/mod/direct 7/rm32/EDI . . . 2/r32/EDX . . # compare EDI and EDX
73/jump-if-greater-or-equal-unsigned $next-line-matches?:end/disp8
# if (*currt/EDI != *currl/ESI) return false
31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX
31/xor 3/mod/direct 3/rm32/EAX . . . 3/r32/EAX . . # clear EBX
# . EAX = (char) *currt/EDI
8a/copy-byte 0/mod/indirect 7/rm32/EDI . . 0/r32/EAX . . # copy *EDI to EAX
# . EBX = (char) *currl/ESI
8a/copy-byte 0/mod/indirect 6/rm32/ESI . . 3/r32/EBX . . # copy *ESI to EBX
# . EAX >= EBX
39/compare 3/mod/direct 0/rm32/EAX . . . 3/r32/EBX . . # compare EAX and EBX
# . EAX = false
b8/copy-to-EAX 0/imm32/false
75/jump-if-not-equal $next-line-matches?:end/disp8
# ++currt/EDI
47/increment-EDI
# ++currl/ESI
46/increment-ESI
eb/jump $next-line-matches?:loop/disp8
$next-line-matches?:break:
# return *currt == '\n'
31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX
# . EAX = (char) *currt
8a/copy-byte 0/mod/indirect 7/rm32/EDI . . 0/r32/EAX . . # copy *EDI to EAX
3d/compare-EAX-and 0xa/imm32/newline
# . EAX = false
b8/copy-to-EAX 1/imm32/true
74/jump-if-equal $next-line-matches?:end/disp8
b8/copy-to-EAX 0/imm32/true
$next-line-matches?:end:
# . restore registers
5f/pop-to-EDI
5e/pop-to-ESI
5b/pop-to-EBX
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-next-line-matches?-no-match-1:
# next line of "ABABA" does not match "blah blah"
# . EAX = next-line-matches?(_test-stream-line-ABABA, "blah blah")
# . . push args
68/push "blah blah"/imm32
68/push _test-stream-line-ABABA/imm32
# . . call
e8/call next-line-matches?/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-next-line-matches?-no-match-1"/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
c3/return
test-next-line-matches?-no-match-2:
# next line of "ABABA" does not match ""
# . EAX = next-line-matches?(_test-stream-line-ABABA, "")
# . . push args
68/push ""/imm32
68/push _test-stream-line-ABABA/imm32
# . . call
e8/call next-line-matches?/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-next-line-matches?-no-match-2"/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
c3/return
test-next-line-matches?-no-match-3:
# next line of "ABABA" does not match "AA"
# . EAX = next-line-matches?(_test-stream-line-ABABA, "AA")
# . . push args
68/push "AA"/imm32
68/push _test-stream-line-ABABA/imm32
# . . call
e8/call next-line-matches?/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-next-line-matches?-no-match-3"/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
c3/return
test-next-line-matches?-match:
# next line of "ABABA" matches "ABABA"
# . EAX = next-line-matches?(_test-stream-line-ABABA, "ABABA")
# . . push args
68/push "ABABA"/imm32
68/push _test-stream-line-ABABA/imm32
# . . call
e8/call next-line-matches?/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-next-line-matches?-match"/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
c3/return
# move t->read to _after_ next newline
skip-next-line: # t : (address stream)
# pseudocode:
# max = t->data + t->write
# i = t->read
# curr = t->data + t->read
# while true
# if (curr >= max) break
# ++i
# if (*curr == '\n') break
# ++curr
# t->read = i
#
# . 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
# EAX = t
8b/copy 1/mod/*+disp8 5/rm32/EBP . . 0/r32/EAX 8/disp8 . # copy *(EBP+8) to EAX
52/push-EDX
53/push-EBX
# ECX = t
8b/copy 1/mod/*+disp8 5/rm32/EBP . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX
# EDX = t/ECX->data
8d/copy-address 1/mod/*+disp8 1/rm32/ECX . . . 2/r32/EDX 0xc/disp8 . # copy ECX+12 to EDX
# EAX = t/ECX->write
8b/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy *ECX to EAX
# max/EBX = t->data/EDX + t->write/EAX
8d/copy-address 0/mod/indirect 4/rm32/sib 2/base/EDX 0/index/EAX . 3/r32/EBX . . # copy EDX+EAX to EBX
# EAX = t/ECX->read
8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy *(ECX+4) to EDX
# curr/ECX = t->data/EDX + t->read/EAX
8d/copy-address 0/mod/indirect 4/rm32/sib 2/base/EDX 0/index/EAX . 1/r32/ECX . . # copy EDX+EAX to ECX
# i/EDX = EAX
8b/copy 3/mod/direct 0/rm32/EAX . . . 2/r32/EDX . . # copy EAX to EDX
$skip-next-line:loop:
# if (curr/ECX >= max/EBX) break
39/compare 3/mod/direct 1/rm32/ECX . . . 3/r32/EBX . . # compare ECX and EBX
73/jump-if-greater-or-equal-unsigned $skip-next-line:end/disp8
# ++i/EDX
42/increment-EDX
# if (*curr/ECX == '\n') break
31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX
8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy *ECX to EAX
3d/compare-EAX-and 0a/imm32/newline
74/jump-if-equal $skip-next-line:end/disp8
# ++curr/ECX
41/increment-ECX
# loop
eb/jump $skip-next-line:loop/disp8
$skip-next-line:end:
# ECX = t
8b/copy 1/mod/*+disp8 5/rm32/EBP . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX
# t/ECX->read = i/EDX
89/copy 1/mod/*+disp8 1/rm32/ECX . . . 2/r32/EDX 4/disp8 . # copy EDX to *(ECX+4)
# . restore registers
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-skip-next-line-empty:
# skipping next line in empty stream leaves read pointer at 0
# . skip-next-line(_test-stream-empty)
# . . push args
68/push _test-stream-empty/imm32
# . . call
e8/call skip-next-line/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
# . check-ints-equal(_test-stream-empty->read, 0, msg)
# . . push args
68/push "F - test-skip-next-line-empty"/imm32
68/push 0/imm32
b8/copy-to-EAX _test-stream-empty/imm32
8b/copy 1/mod/*+disp8 0/rm32/EAX . . . 0/r32/EAX 4/disp8 . # copy *(EAX+4) to 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
c3/return
test-skip-next-line-filled:
# skipping next line increments read pointer by length of line + 1 (for newline)
# . skip-next-line(_test-stream-filled)
# . . push args
68/push _test-stream-filled/imm32
# . . call
e8/call skip-next-line/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
# . check-ints-equal(_test-stream-filled->read, 5, msg)
# . . push args
68/push "F - test-skip-next-line-filled"/imm32
68/push 5/imm32
b8/copy-to-EAX _test-stream-filled/imm32
8b/copy 1/mod/*+disp8 0/rm32/EAX . . . 0/r32/EAX 4/disp8 . # copy *(EAX+4) to 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
c3/return
clear-trace-stream:
# . 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
# EAX = *Trace-stream
8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/EAX Trace-stream/disp32 # copy *Trace-stream to EAX
# ECX = t->length
8b/copy 1/mod/*+disp8 0/rm32/EAX . . . 1/r32/ECX 8/disp8 . # copy *(EAX+8) to ECX
# ECX = &t->data[t->length]
@ -148,7 +860,7 @@ clear-trace-stream: # t : (address trace-stream)
$clear-trace-stream:loop:
# if (EAX >= ECX) break
39/compare 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # compare EAX with ECX
7d/jump-if-greater-or-equal $clear-trace-stream:end/disp8
73/jump-if-greater-or-equal-unsigned $clear-trace-stream:end/disp8
# *EAX = 0
c7 0/subop/copy 0/mod/direct 0/rm32/EAX . . . . . 0/imm32 # copy to *EAX
# EAX += 4
@ -163,106 +875,6 @@ $clear-trace-stream:end:
5d/pop-to-EBP
c3/return
# - tests
test-trace-single:
# clear-trace-stream(_test-trace-stream)
# . . push args
68/push _test-trace-stream/imm32
# . . call
e8/call clear-trace-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
# trace(_test-trace-stream, "Ab")
# . . push args
68/push "Ab"/imm32
68/push _test-trace-stream/imm32
# . . call
e8/call trace/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
# check-ints-equal(*_test-trace-stream->data, 41/A 62/b 0a/newline 00, msg)
# . . push args
68/push "F - test-trace-single"/imm32
68/push 0x0a6241/imm32/Ab-newline
# . . push *_test-trace-stream->data
b8/copy-to-EAX _test-trace-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-trace-appends:
# clear-trace-stream(_test-trace-stream)
# . . push args
68/push _test-trace-stream/imm32
# . . call
e8/call clear-trace-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
# trace(_test-trace-stream, "C")
# . . push args
68/push "C"/imm32
68/push _test-trace-stream/imm32
# . . call
e8/call trace/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
# trace(_test-trace-stream, "D")
# . . push args
68/push "D"/imm32
68/push _test-trace-stream/imm32
# . . call
e8/call trace/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
# check-ints-equal(*_test-trace-stream->data, 43/C 0a/newline 44/D 0a/newline, msg)
# . . push args
68/push "F - test-trace-appends"/imm32
68/push 0x0a440a43/imm32/C-newline-D-newline
# . . push *_test-trace-stream->data
b8/copy-to-EAX _test-trace-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-trace-empty-line:
# clear-trace-stream(_test-trace-stream)
# . . push args
68/push _test-trace-stream/imm32
# . . call
e8/call clear-trace-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
# trace(_test-trace-stream, "")
# . . push args
68/push ""/imm32
68/push _test-trace-stream/imm32
# . . call
e8/call trace/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
# check-ints-equal(*_test-trace-stream->data, 0, msg)
# . . push args
68/push "F - test-trace-empty-line"/imm32
68/push 0/imm32
# . . push *_test-trace-stream->data
b8/copy-to-EAX _test-trace-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
# - helpers
# 3-argument variant of _append
@ -321,10 +933,10 @@ _append-4: # out : address, outend : address, in : address, inend : address ->
$_append-4:loop:
# if (in >= inend) break
39/compare 3/mod/direct 6/rm32/ESI . . . 1/r32/ECX . . # compare ESI with ECX
7d/jump-if-greater-or-equal $_append-4:end/disp8
73/jump-if-greater-or-equal-unsigned $_append-4:end/disp8
# if (out >= outend) abort # just to catch test failures fast
39/compare 3/mod/direct 7/rm32/EDI . . . 2/r32/EDX . . # compare EDI with EDX
7d/jump-if-greater-or-equal $_append-4:abort/disp8
73/jump-if-greater-or-equal-unsigned $_append-4:abort/disp8
# *out = *in
8a/copy-byte 0/mod/indirect 6/rm32/ESI . . . 3/r32/BL . . # copy byte at *ESI to BL
88/copy-byte 0/mod/indirect 7/rm32/EDI . . . 3/r32/BL . . # copy byte at BL to *EDI
@ -362,4 +974,36 @@ $_append-4:abort:
cd/syscall 0x80/imm8
# never gets here
== data
_test-stream-line-ABABA:
# write
8/imm32
# read
0/imm32
# length
8/imm32
# data
41 42 41 42 41 0A 00 00 # 8 bytes
_test-stream-empty:
# write
0/imm32
# read
0/imm32
# length
8/imm32
# data
00 00 00 00 00 00 00 00 # 8 bytes
_test-stream-filled:
# write
8/imm32
# read
0/imm32
# length
8/imm32
# data
41 41 41 41 0A 41 41 41 # 8 bytes
# . . vim:nowrap:textwidth=0

View File

@ -5,13 +5,6 @@
# . op subop mod rm32 base index scale r32
# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes
#? Entry: # run a single test, while debugging
#? e8/call test-next-stream-line-equal-stops-at-newline/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
#? cd/syscall 0x80/imm8
# compare all the data in a stream (ignoring the read pointer)
stream-data-equal?: # f : (address stream), s : (address string) -> EAX : boolean
# . prolog
@ -26,7 +19,7 @@ stream-data-equal?: # f : (address stream), s : (address string) -> EAX : boole
8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI
# EAX = f->write
8b/copy 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # copy *ESI to EAX
# max/EDX = f->data + f->write
# maxf/EDX = f->data + f->write
8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/ESI 0/index/EAX . 2/r32/EDX 0xc/disp8 . # copy ESI+EAX+12 to EDX
# currf/ESI = f->data
81 0/subop/add 3/mod/direct 6/rm32/ESI . . . . . 0xc/imm32 # add to ESI
@ -42,9 +35,9 @@ $stream-data-equal?:compare-lengths:
31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX
31/xor 3/mod/direct 1/rm32/ECX . . . 1/r32/ECX . . # clear ECX
$stream-data-equal?:loop:
# if (curr >= max) return true
# if (currf >= maxf) return true
39/compare 3/mod/direct 6/rm32/ESI . . . 2/r32/EDX . . # compare ESI with EDX
7d/jump-if-greater-or-equal $stream-data-equal?:true/disp8
73/jump-if-greater-or-equal-unsigned $stream-data-equal?:true/disp8
# AL = *currs
8a/copy-byte 0/mod/indirect 6/rm32/ESI . . . 0/r32/AL . . # copy byte at *ESI to AL
# CL = *curr

View File

@ -37,13 +37,6 @@
# . op subop mod rm32 base index scale r32
# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes
#? Entry: # run a single test, while debugging
#? e8/call test-stop-skips-returns-on-exit/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
#? cd/syscall 0x80/imm8
# Configure an exit-descriptor for a call pushing 'nbytes' bytes of args to
# the stack.
# Ugly that we need to know the size of args, but so it goes.

View File

@ -165,10 +165,10 @@ _buffer-4: # out : address, outend : address, in : address, inend : address ->
$_buffer-4:loop:
# if (in >= inend) break
39/compare 3/mod/direct 6/rm32/ESI . . . 1/r32/ECX . . # compare ESI with ECX
7d/jump-if-greater-or-equal $_buffer-4:end/disp8
73/jump-if-greater-or-equal-unsigned $_buffer-4:end/disp8
# if (out >= outend) break # for now silently ignore filled up buffer
39/compare 3/mod/direct 7/rm32/EDI . . . 2/r32/EDX . . # compare EDI with EDX
7d/jump-if-greater-or-equal $_buffer-4:end/disp8
73/jump-if-greater-or-equal-unsigned $_buffer-4:end/disp8
# *out = *in
8a/copy-byte 0/mod/indirect 6/rm32/ESI . . . 3/r32/BL . . # copy byte at *ESI to BL
88/copy-byte 0/mod/indirect 7/rm32/EDI . . . 3/r32/BL . . # copy byte at BL to *EDI

View File

@ -31,14 +31,6 @@ Stdin:
# . op subop mod rm32 base index scale r32
# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes
#? Entry: # run a single test, while debugging
#? e8/call test-read-byte-buffered-multiple/disp32
#? e8/call test-read-byte-buffered-refills-buffer/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
#? cd/syscall 0x80/imm8
# return next byte value in EAX, with top 3 bytes cleared.
# On reaching end of file, return 0xffffffff (Eof).
read-byte-buffered: # f : (address buffered-file) -> byte-or-Eof/EAX

View File

@ -23,7 +23,7 @@ is-hex-int?: # in : (address slice) -> EAX : boolean
# if s is empty return false
b8/copy-to-EAX 0/imm32/false
39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX
7d/jump-if-greater-or-equal $is-hex-int?:end/disp8
73/jump-if-greater-or-equal-unsigned $is-hex-int?:end/disp8
# skip past leading '-'
# . if (*curr == '-') ++curr
31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX
@ -44,7 +44,7 @@ $is-hex-int?:initial-0:
$is-hex-int?:initial-0x:
# . if (curr >= in->end) return true
39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX
7d/jump-if-greater-or-equal $is-hex-int?:true/disp8
73/jump-if-greater-or-equal-unsigned $is-hex-int?:true/disp8
# . if (*curr != 'x') jump to loop # the previous '0' is still valid so doesn't need to be checked again
31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX
8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 3/r32/BL . . # copy byte at *ECX to BL
@ -55,7 +55,7 @@ $is-hex-int?:initial-0x:
$is-hex-int?:loop:
# if (curr >= in->end) return true
39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX
7d/jump-if-greater-or-equal $is-hex-int?:true/disp8
73/jump-if-greater-or-equal-unsigned $is-hex-int?:true/disp8
# EAX = is-hex-digit?(*curr)
# . . push args
8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL
@ -88,9 +88,14 @@ test-is-hex-int:
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# var slice/ECX = "34"
68/push _test-slice-hex-int-end/imm32
68/push _test-slice-hex-int/imm32
# (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-hex-int?(slice)
# . . push args
@ -117,9 +122,14 @@ test-is-hex-int-handles-letters:
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# var slice/ECX = "34a"
68/push _test-slice-hex-int-letters-end/imm32
68/push _test-slice-hex-int-letters/imm32
# (EAX..ECX) = "34a"
b8/copy-to-EAX "34a"/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-hex-int?(slice)
# . . push args
@ -146,9 +156,14 @@ test-is-hex-int-with-trailing-char:
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# var slice/ECX = "34q"
68/push _test-slice-digits-and-char-end/imm32
68/push _test-slice-digits-and-char/imm32
# (EAX..ECX) = "34q"
b8/copy-to-EAX "34q"/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-hex-int?(slice)
# . . push args
@ -175,9 +190,14 @@ test-is-hex-int-with-leading-char:
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# var slice/ECX = "q34"
68/push _test-slice-char-and-digits-end/imm32
68/push _test-slice-char-and-digits/imm32
# (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-hex-int?(slice)
# . . push args
@ -205,8 +225,8 @@ test-is-hex-int-empty:
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# var slice/ECX = ""
68/push _test-slice-empty-end/imm32
68/push _test-slice-empty/imm32
68/push 0/imm32
68/push 0/imm32
89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX
# EAX = is-hex-int?(slice)
# . . push args
@ -233,9 +253,14 @@ test-is-hex-int-handles-0x-prefix:
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# var slice/ECX = "0x3a"
68/push _test-slice-hex-int-with-0x-prefix-end/imm32
68/push _test-slice-hex-int-with-0x-prefix/imm32
# (EAX..ECX) = "0x3a"
b8/copy-to-EAX "0x3a"/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-hex-int?(slice)
# . . push args
@ -262,9 +287,14 @@ test-is-hex-int-handles-negative:
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# var slice/ECX = "-34a"
68/push _test-slice-hex-int-letters-end/imm32
68/push _test-slice-hex-int-letters-negative/imm32
# (EAX..ECX) = "-34a"
b8/copy-to-EAX "-34a"/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-hex-int?(slice)
# . . push args
@ -291,9 +321,14 @@ test-is-hex-int-handles-negative-0x-prefix:
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# var slice/ECX = "-0x3a"
68/push _test-slice-hex-int-with-0x-prefix-end/imm32
68/push _test-slice-hex-int-with-0x-prefix-negative/imm32
# (EAX..ECX) = "-0x3a"
b8/copy-to-EAX "-0x3a"/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-hex-int?(slice)
# . . push args
@ -356,7 +391,7 @@ $parse-hex-int:initial-0:
$parse-hex-int:initial-0x:
# . if (curr >= in->end) return result
39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX
7d/jump-if-greater-or-equal $parse-hex-int:end/disp8
73/jump-if-greater-or-equal-unsigned $parse-hex-int:end/disp8
# . if (*curr != 'x') jump to loop # the previous '0' is still valid so doesn't need to be checked again
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
@ -367,7 +402,7 @@ $parse-hex-int:initial-0x:
$parse-hex-int:loop:
# if (curr >= in->end) break
39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX
7d/jump-if-greater-or-equal $parse-hex-int:negate/disp8
73/jump-if-greater-or-equal-unsigned $parse-hex-int:negate/disp8
# EAX = from-hex-char(*curr)
# . . copy arg to EAX
8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL
@ -400,9 +435,14 @@ test-parse-hex-int-single-digit:
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# var slice/ECX = "a"
68/push _test-slice-hex-int-single-letter-end/imm32
68/push _test-slice-hex-int-single-letter/imm32
# (EAX..ECX) = "a"
b8/copy-to-EAX "a"/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 = parse-hex-int(slice)
# . . push args
@ -429,9 +469,14 @@ test-parse-hex-int-multi-digit:
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# var slice/ECX = "34a"
68/push _test-slice-hex-int-letters-end/imm32
68/push _test-slice-hex-int-letters/imm32
# (EAX..ECX) = "34a"
b8/copy-to-EAX "34a"/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 = parse-hex-int(slice)
# . . push args
@ -458,9 +503,14 @@ test-parse-hex-int-0x-prefix:
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# var slice/ECX = "0x34"
68/push _test-slice-hex-int-with-0x-prefix-end/imm32
68/push _test-slice-hex-int-with-0x-prefix/imm32
# (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 = parse-hex-int(slice)
# . . push args
@ -487,9 +537,14 @@ test-parse-hex-int-zero:
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# var slice/ECX = "0"
68/push _test-slice-hex-int-zero-end/imm32
68/push _test-slice-hex-int-zero/imm32
# (EAX..ECX) = "0"
b8/copy-to-EAX "0"/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 = parse-hex-int(slice)
# . . push args
@ -516,9 +571,14 @@ test-parse-hex-int-0-prefix:
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# var slice/ECX = "03"
68/push _test-slice-hex-int-with-0-prefix-end/imm32
68/push _test-slice-hex-int-with-0-prefix/imm32
# (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 = parse-hex-int(slice)
# . . push args
@ -545,9 +605,14 @@ test-parse-hex-int-negative:
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# var slice/ECX = "-03"
68/push _test-slice-hex-int-negative-with-0-prefix-end/imm32
68/push _test-slice-hex-int-negative-with-0-prefix/imm32
# (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 = parse-hex-int(slice)
# . . push args
@ -729,7 +794,7 @@ test-hex-above-f:
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
c3/return
from-hex-char: # in/EAX : byte -> out/EAX : num
from-hex-char: # in/EAX : byte -> out/EAX : nibble
# no error checking; accepts argument in EAX
# if (EAX <= '9') return EAX - '0'
3d/compare-EAX-with 0x39/imm32/9
@ -753,50 +818,4 @@ $to-hex-char:else:
05/add-to-EAX 0x57/imm32/a-10
c3/return
== data
_test-slice-empty:
# nothing
_test-slice-empty-end:
_test-slice-hex-int:
33/3 34/4
_test-slice-hex-int-end:
_test-slice-hex-int-letters-negative:
2d/-
_test-slice-hex-int-letters:
33/3 34/4 61/a
_test-slice-hex-int-letters-end:
_test-slice-hex-int-single-letter:
61/a
_test-slice-hex-int-single-letter-end:
_test-slice-char-and-digits:
71/q 33/3 34/4
_test-slice-char-and-digits-end:
_test-slice-digits-and-char:
33/3 34/4 71/q
_test-slice-digits-and-char-end:
_test-slice-hex-int-with-0x-prefix-negative:
2d/-
_test-slice-hex-int-with-0x-prefix:
30/0 78/x 33/3 34/4
_test-slice-hex-int-with-0x-prefix-end:
_test-slice-hex-int-zero:
30/0
_test-slice-hex-int-zero-end:
_test-slice-hex-int-with-0-prefix:
30/0 33/3
_test-slice-hex-int-with-0-prefix-end:
_test-slice-hex-int-negative-with-0-prefix:
2d/- 30/0 33/3
_test-slice-hex-int-negative-with-0-prefix-end:
# . . vim:nowrap:textwidth=0

View File

@ -5,14 +5,6 @@
# . op subop mod rm32 base index scale r32
# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes
#? Entry: # run a single test, while debugging
#? e8/call test-write-buffered/disp32
#? e8/call test-write-buffered-with-intermediate-flush/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
#? cd/syscall 0x80/imm8
write-buffered: # f : (address buffered-file), msg : (address array byte) -> <void>
# pseudocode:
# in = msg->data
@ -60,7 +52,7 @@ write-buffered: # f : (address buffered-file), msg : (address array byte) -> <v
$write-buffered:loop:
# if (in >= inend) break
39/compare 3/mod/direct 6/rm32/ESI . . . 1/r32/ECX . . # compare ESI with ECX
7d/jump-if-greater-or-equal $write-buffered:loop-end/disp8
73/jump-if-greater-or-equal-unsigned $write-buffered:loop-end/disp8
# if (f->write >= f->length) flush and clear f's stream
39/compare 3/mod/direct 3/rm32/EBX . . . 2/r32/EDX . . # compare EBX with EDX
7c/jump-if-lesser $write-buffered:to-stream/disp8

View File

@ -5,13 +5,6 @@
# . op subop mod rm32 base index scale r32
# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes
#? Entry: # run a single test, while debugging
#? e8/call test-print-int32/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
#? cd/syscall 0x80/imm8
append-byte-hex: # f : (address stream), n : int -> <void>
# . prolog
55/push-EBP
@ -259,6 +252,23 @@ test-print-int32:
# . end
c3/return
# TODO: append to string
check-ints-equal2: # (a : int, b : int, msg : (address array byte))
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16)
ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12)
ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8)
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8)
# . epilog
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
5d/pop-to-EBP
c3/return
print-int32-buffered: # f : (address buffered-file), n : int -> <void>
# pseudocode:
# write-buffered(f, "0x")

View File

@ -3,13 +3,6 @@
# . op subop mod rm32 base index scale r32
# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes
#? Entry: # run a single test, while debugging
#? e8/call test-read-line-buffered/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
#? cd/syscall 0x80/imm8
# read bytes from 'f' until (and including) a newline and store them into 's'
# 's' fails to grow if and only if no data found
# just abort if 's' is too small

View File

@ -6,13 +6,6 @@
# . op subop mod rm32 base index scale r32
# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes
#? Entry: # run a single test, while debugging
#? e8/call test-slice-to-string/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
#? cd/syscall 0x80/imm8
slice-empty?: # s : (address slice) -> EAX : boolean
# . prolog
55/push-EBP
@ -154,7 +147,7 @@ $slice-equal?:nonnull-string:
$slice-equal?:loop:
# if (currs >= maxs) return true
39/compare 3/mod/direct 2/rm32/EDX . . . 6/r32/ESI . . # compare EDX with ESI
7d/jump-if-greater-or-equal $slice-equal?:true/disp8
73/jump-if-greater-or-equal-unsigned $slice-equal?:true/disp8
# AL = *currp
8a/copy-byte 0/mod/indirect 3/rm32/EBX . . . 0/r32/AL . . # copy byte at *EBX to AL
# CL = *currs
@ -188,9 +181,14 @@ test-slice-equal:
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# var slice/ECX
68/push _test-slice-data-3/imm32/end
68/push _test-slice-data-0/imm32/start
# (EAX..ECX) = "Abc"
b8/copy-to-EAX "Abc"/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 = slice-equal?(ECX, "Abc")
# . . push args
@ -219,9 +217,14 @@ test-slice-equal-false:
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# var slice/ECX
68/push _test-slice-data-4/imm32/end
68/push _test-slice-data-1/imm32/start
# (EAX..ECX) = "bcd"
b8/copy-to-EAX "bcd"/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 = slice-equal?(ECX, "Abc")
# . . push args
@ -250,9 +253,14 @@ test-slice-equal-too-long:
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# var slice/ECX
68/push _test-slice-data-4/imm32/end
68/push _test-slice-data-0/imm32/start
# (EAX..ECX) = "Abcd"
b8/copy-to-EAX "Abcd"/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 = slice-equal?(ECX, "Abc")
# . . push args
@ -281,9 +289,14 @@ test-slice-equal-too-short:
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# var slice/ECX
68/push _test-slice-data-1/imm32/end
68/push _test-slice-data-0/imm32/start
# (EAX..ECX) = "A"
b8/copy-to-EAX "A"/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 = slice-equal?(ECX, "Abc")
# . . push args
@ -313,8 +326,8 @@ test-slice-equal-empty:
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# var slice/ECX
68/push _test-slice-data-0/imm32/end
68/push _test-slice-data-0/imm32/start
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
# EAX = slice-equal?(ECX, "Abc")
# . . push args
@ -343,9 +356,14 @@ test-slice-equal-with-empty:
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# var slice/ECX
68/push _test-slice-data-2/imm32/end
68/push _test-slice-data-0/imm32/start
# (EAX..ECX) = "Ab"
b8/copy-to-EAX "Ab"/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 = slice-equal?(ECX, "")
# . . push args
@ -375,8 +393,8 @@ test-slice-equal-empty-with-empty:
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# var slice/ECX
68/push _test-slice-data-0/imm32/end
68/push _test-slice-data-0/imm32/start
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
# EAX = slice-equal?(ECX, "")
# . . push args
@ -405,9 +423,14 @@ test-slice-equal-with-null:
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# var slice/ECX
68/push _test-slice-data-2/imm32/end
68/push _test-slice-data-0/imm32/start
# (EAX..ECX) = "Ab"
b8/copy-to-EAX "Ab"/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 = slice-equal?(ECX, 0)
# . . push args
@ -523,9 +546,14 @@ test-slice-starts-with-single-character:
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# var slice/ECX
68/push _test-slice-data-3/imm32/end
68/push _test-slice-data-0/imm32/start
# (EAX..ECX) = "Abc"
b8/copy-to-EAX "Abc"/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 = slice-starts-with?(ECX, "A")
# . . push args
@ -554,9 +582,14 @@ test-slice-starts-with-empty-string:
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# var slice/ECX
68/push _test-slice-data-3/imm32/end
68/push _test-slice-data-0/imm32/start
# (EAX..ECX) = "Abc"
b8/copy-to-EAX "Abc"/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 = slice-starts-with?(ECX, "")
# . . push args
@ -585,9 +618,14 @@ test-slice-starts-with-multiple-characters:
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# var slice/ECX
68/push _test-slice-data-3/imm32/end
68/push _test-slice-data-0/imm32/start
# (EAX..ECX) = "Abc"
b8/copy-to-EAX "Abc"/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 = slice-starts-with?(ECX, "Ab")
# . . push args
@ -616,9 +654,14 @@ test-slice-starts-with-entire-string:
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# var slice/ECX
68/push _test-slice-data-3/imm32/end
68/push _test-slice-data-0/imm32/start
# (EAX..ECX) = "Abc"
b8/copy-to-EAX "Abc"/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 = slice-starts-with?(ECX, "Abc")
# . . push args
@ -647,9 +690,14 @@ test-slice-starts-with-fails:
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# var slice/ECX
68/push _test-slice-data-3/imm32/end
68/push _test-slice-data-0/imm32/start
# (EAX..ECX) = "Abc"
b8/copy-to-EAX "Abc"/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 = slice-starts-with?(ECX, "Abd")
# . . push args
@ -678,9 +726,14 @@ test-slice-starts-with-fails-2:
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# var slice/ECX
68/push _test-slice-data-3/imm32/end
68/push _test-slice-data-0/imm32/start
# (EAX..ECX) = "Abc"
b8/copy-to-EAX "Abc"/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 = slice-starts-with?(ECX, "Ac")
# . . push args
@ -704,6 +757,124 @@ test-slice-starts-with-fails-2:
5d/pop-to-EBP
c3/return
# write a slice to a stream
# abort if the stream doesn't have enough space
write-slice: # out : (address stream), s : (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
52/push-EDX
53/push-EBX
56/push-ESI
57/push-EDI
# ESI = s
8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI
# curr/ECX = s->start
8b/copy 0/mod/indirect 6/rm32/ESI . . . 1/r32/ECX . . # copy *ESI to ECX
# max/ESI = s->end
8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 6/r32/ESI 4/disp8 . # copy *(ESI+4) to ESI
# EDI = out
8b/copy 1/mod/*+disp8 5/rm32/EBP . . 7/r32/EDI 8/disp8 . # copy *(EBP+8) to EDI
# EDX = out->length
8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 2/r32/EDX 8/disp8 . # copy *(EDI+8) to EDX
# EBX = out->write
8b/copy 0/mod/indirect 7/rm32/EDI . . . 3/r32/EBX . . # copy *EDI to EBX
$write-slice:loop:
# if (curr >= max) break
39/compare 3/mod/direct 1/rm32/ECX . . . 6/r32/ESI . . # compare ECX with ESI
73/jump-if-greater-or-equal-unsigned $write-slice:loop-end/disp8
# if (out->write >= out->length) abort
39/compare 3/mod/direct 3/rm32/EBX . . . 2/r32/EDX . . # compare EBX with EDX
7d/jump-if-greater-or-equal $write-slice:abort/disp8
# out->data[out->write] = *in
# . AL = *in
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
# . out->data[out->write] = AL
88/copy-byte 1/mod/*+disp8 4/rm32/sib 7/base/EDI 3/index/EBX . 0/r32/AL 0xc/disp8 . # copy AL to *(EDI+EBX+12)
# ++out->write
43/increment-EBX
# ++in
41/increment-ECX
eb/jump $write-slice:loop/disp8
$write-slice:loop-end:
# persist out->write
89/copy 0/mod/indirect 7/rm32/EDI . . . 3/r32/EBX . . # copy EBX to *EDI
$write-slice:end:
# . restore registers
5f/pop-to-EDI
5e/pop-to-ESI
5b/pop-to-EBX
5a/pop-to-EDX
59/pop-to-ECX
58/pop-to-EAX
# . epilog
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
5d/pop-to-EBP
c3/return
$write-slice:abort:
# . _write(2/stderr, error)
# . . push args
68/push "write-slice: out of space"/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-write-slice:
# . 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
# (EAX..ECX) = "Abc"
b8/copy-to-EAX "Abc"/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
# write-slice(_test-stream, slice)
# . . push args
51/push-ECX
68/push _test-stream/imm32
# . . call
e8/call write-slice/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
# check-stream-equal(_test-stream, "Abc", msg)
# . . push args
68/push "F - test-write-slice"/imm32
68/push "Abc"/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
# . epilog
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
5d/pop-to-EBP
c3/return
# write a slice to a buffered-file
write-slice-buffered: # out : (address buffered-file), s : (address slice)
# . prolog
55/push-EBP
@ -730,7 +901,7 @@ write-slice-buffered: # out : (address buffered-file), s : (address slice)
$write-slice-buffered:loop:
# if (curr >= max) break
39/compare 3/mod/direct 1/rm32/ECX . . . 6/r32/ESI . . # compare ECX with ESI
7d/jump-if-greater-or-equal $write-slice-buffered:loop-end/disp8
73/jump-if-greater-or-equal-unsigned $write-slice-buffered:loop-end/disp8
# if (out->write >= out->length) flush and clear out's stream
39/compare 3/mod/direct 3/rm32/EBX . . . 2/r32/EDX . . # compare EBX with EDX
7c/jump-if-lesser $write-slice-buffered:to-stream/disp8
@ -802,9 +973,14 @@ test-write-slice-buffered:
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 = "Abc"
68/push _test-slice-data-3/imm32/end
68/push _test-slice-data-0/imm32/start
# (EAX..ECX) = "Abc"
b8/copy-to-EAX "Abc"/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
# write-slice-buffered(_test-buffered-file, slice)
# . . push args
@ -929,9 +1105,14 @@ test-slice-to-string:
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
# (EAX..ECX) = "Abc"
b8/copy-to-EAX "Abc"/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 = slice-to-string(heap, slice)
# . . push args
@ -989,16 +1170,4 @@ test-slice-to-string:
5d/pop-to-EBP
c3/return
== data
_test-slice-data-0:
41/A
_test-slice-data-1:
62/b
_test-slice-data-2:
63/c
_test-slice-data-3:
64/d
_test-slice-data-4:
# . . vim:nowrap:textwidth=0

View File

@ -1,15 +1,10 @@
# Some tokenization primitives.
== code
# instruction effective address register displacement immediate
# . op subop mod rm32 base index scale r32
# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes
#? Entry: # run a single test, while debugging
#? e8/call test-next-token-from-slice/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
#? cd/syscall 0x80/imm8
# extract the next run of characters that are different from a given 'delimiter' (skipping multiple delimiters if necessary)
# on reaching end of file, return an empty interval
next-token: # in : (address stream), delimiter : byte, out : (address slice)
@ -698,7 +693,7 @@ skip-chars-matching-in-slice: # curr : (address byte), end : (address byte), de
$skip-chars-matching-in-slice:loop:
# if (curr >= end) break
39/compare 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # compare EAX with ECX
7d/jump-if-greater-or-equal $skip-chars-matching-in-slice:end/disp8
73/jump-if-greater-or-equal-unsigned $skip-chars-matching-in-slice:end/disp8
# if (*curr != delimiter) break
8a/copy-byte 0/mod/indirect 0/rm32/EAX . . . 3/r32/BL . . # copy byte at *EAX to BL
39/compare 3/mod/direct 3/rm32/EBX . . . 2/r32/EDX . . # compare EBX and EDX
@ -724,7 +719,7 @@ test-skip-chars-matching-in-slice:
05/add-to-EAX 4/imm32
# EAX = skip-chars-matching-in-slice(EAX, ECX, 0x20/space)
# . . push args
68/push 0x20/imm32
68/push 0x20/imm32/space
51/push-ECX
50/push-EAX
# . . call
@ -753,7 +748,7 @@ test-skip-chars-matching-in-slice-none:
05/add-to-EAX 4/imm32
# EAX = skip-chars-matching-in-slice(EAX, ECX, 0x20/space)
# . . push args
68/push 0x20/imm32
68/push 0x20/imm32/space
51/push-ECX
50/push-EAX
# . . call
@ -794,7 +789,7 @@ skip-chars-not-matching-in-slice: # curr : (address byte), end : (address byte)
$skip-chars-not-matching-in-slice:loop:
# if (curr >= end) break
39/compare 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # compare EAX with ECX
7d/jump-if-greater-or-equal $skip-chars-not-matching-in-slice:end/disp8
73/jump-if-greater-or-equal-unsigned $skip-chars-not-matching-in-slice:end/disp8
# if (*curr == delimiter) break
8a/copy-byte 0/mod/indirect 0/rm32/EAX . . . 3/r32/BL . . # copy byte at *EAX to BL
39/compare 3/mod/direct 3/rm32/EBX . . . 2/r32/EDX . . # compare EBX and EDX
@ -820,7 +815,7 @@ test-skip-chars-not-matching-in-slice:
05/add-to-EAX 4/imm32
# EAX = skip-chars-not-matching-in-slice(EAX, ECX, 0x20/space)
# . . push args
68/push 0x20/imm32
68/push 0x20/imm32/space
51/push-ECX
50/push-EAX
# . . call
@ -849,7 +844,7 @@ test-skip-chars-not-matching-in-slice-none:
05/add-to-EAX 4/imm32
# EAX = skip-chars-not-matching-in-slice(EAX, ECX, 0x20/space)
# . . push args
68/push 0x20/imm32
68/push 0x20/imm32/space
51/push-ECX
50/push-EAX
# . . call
@ -878,7 +873,7 @@ test-skip-chars-not-matching-in-slice-all:
05/add-to-EAX 4/imm32
# EAX = skip-chars-not-matching-in-slice(EAX, ECX, 0x20/space)
# . . push args
68/push 0x20/imm32
68/push 0x20/imm32/space
51/push-ECX
50/push-EAX
# . . call

View File

@ -5,14 +5,6 @@
# . op subop mod rm32 base index scale r32
# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes
#? Entry: # run a single test, while debugging
#? 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
#? cd/syscall 0x80/imm8
print-int32-decimal: # out : (address stream), n : int32
# works by generating characters from lowest to highest and pushing them
# to the stack, before popping them one by one into the stream
@ -97,7 +89,7 @@ $print-int32-decimal:write-loop:
74/jump-if-equal $print-int32-decimal:write-break/disp8
# if (curr >= max) abort
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
73/jump-if-greater-or-equal-unsigned $print-int32-decimal:abort/disp8
$print-int32-decimal:write-char:
# *curr = AL
88/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy AL to byte at *ECX

629
subx/075array-equal.subx Normal file
View File

@ -0,0 +1,629 @@
# Comparing arrays of numbers.
== code
# instruction effective address register displacement immediate
# . op subop mod rm32 base index scale r32
# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes
Entry:
# initialize heap
# . Heap = new-segment(64KB)
# . . push args
68/push Heap/imm32
68/push 0x10000/imm32/64KB
# . . call
e8/call new-segment/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
$array-equal-main:end:
# 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
cd/syscall 0x80/imm8
array-equal?: # a : (address array int), b : (address array int) -> EAX : boolean
# pseudocode:
# lena = a->length
# if (lena != b->length) return false
# i = 0
# curra = a->data
# currb = b->data
# while i < lena
# i1 = *curra
# i2 = *currb
# if (c1 != c2) return false
# i+=4, curra+=4, currb+=4
# return true
#
# registers:
# i: ECX
# lena: EDX
# curra: ESI
# currb: EDI
# i1: EAX
# i2: EBX
#
# . 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
53/push-EBX
56/push-ESI
57/push-EDI
# ESI = a
8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI
# EDI = b
8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 0xc/disp8 . # copy *(EBP+12) to EDI
# lena/EDX = a->length
8b/copy 0/mod/indirect 6/rm32/ESI . . . 2/r32/EDX . . # copy *ESI to EDX
$array-equal?:lengths:
# if (lena != b->length) return false
39/compare 0/mod/indirect 7/rm32/EDI . . . 2/r32/EDX . . # compare *EDI and EDX
75/jump-if-not-equal $array-equal?:false/disp8
# curra/ESI = a->data
81 0/subop/add 3/mod/direct 6/rm32/ESI . . . . . 4/imm32 # add to ESI
# currb/EDI = b->data
81 0/subop/add 3/mod/direct 7/rm32/EDI . . . . . 4/imm32 # add to EDI
# i/ECX = i1/EAX = i2/EBX = 0
31/xor 3/mod/direct 1/rm32/ECX . . . 1/r32/ECX . . # clear ECX
$array-equal?:loop:
# if (i >= lena) return true
39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX
7d/jump-if-greater-or-equal $array-equal?:true/disp8
# i1 = *curra
8b/copy 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # copy *ESI to EAX
# i2 = *currb
8b/copy 0/mod/indirect 7/rm32/EDI . . . 3/r32/EBX . . # copy *EDI to EBX
# if (i1 != i2) return false
39/compare 3/mod/direct 0/rm32/EAX . . . 3/r32/EBX . . # compare EAX and EBX
75/jump-if-not-equal $array-equal?:false/disp8
# i += 4
81 0/subop/add 3/mod/direct 1/rm32/ECX . . . . . 4/imm32 # add to ECX
# currs += 4
81 0/subop/add 3/mod/direct 6/rm32/ESI . . . . . 4/imm32 # add to ESI
# currb += 4
81 0/subop/add 3/mod/direct 7/rm32/EDI . . . . . 4/imm32 # add to EDI
eb/jump $array-equal?:loop/disp8
$array-equal?:true:
b8/copy-to-EAX 1/imm32
eb/jump $array-equal?:end/disp8
$array-equal?:false:
b8/copy-to-EAX 0/imm32
$array-equal?:end:
# . restore registers
5f/pop-to-EDI
5e/pop-to-ESI
5b/pop-to-EBX
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-compare-empty-with-empty-array:
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# var ECX = []
68/push 0/imm32/size
89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX
# var EDX = []
68/push 0/imm32/size
89/copy 3/mod/direct 2/rm32/EDX . . . 4/r32/ESP . . # copy ESP to EDX
# EAX = array-equal?(ECX, EDX)
# . . push args
52/push-EDX
51/push-ECX
# . . call
e8/call array-equal?/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-compare-empty-with-empty-array"/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-compare-empty-with-non-empty-array: # also checks length-mismatch code path
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# var ECX = [1]
68/push 1/imm32
68/push 4/imm32/size
89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX
# var EDX = []
68/push 0/imm32/size
89/copy 3/mod/direct 2/rm32/EDX . . . 4/r32/ESP . . # copy ESP to EDX
# EAX = array-equal?(ECX, EDX)
# . . push args
52/push-EDX
51/push-ECX
# . . call
e8/call array-equal?/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-compare-empty-with-non-empty-array"/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-compare-equal-arrays:
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# var ECX = [1, 2, 3]
68/push 3/imm32
68/push 2/imm32
68/push 1/imm32
68/push 0xc/imm32/size
89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX
# var EDX = [1, 2, 3]
68/push 3/imm32
68/push 2/imm32
68/push 1/imm32
68/push 0xc/imm32/size
89/copy 3/mod/direct 2/rm32/EDX . . . 4/r32/ESP . . # copy ESP to EDX
# EAX = array-equal?(ECX, EDX)
# . . push args
52/push-EDX
51/push-ECX
# . . call
e8/call array-equal?/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-compare-equal-arrays"/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-compare-inequal-arrays-equal-lengths:
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# var ECX = [1, 4, 3]
68/push 3/imm32
68/push 4/imm32
68/push 1/imm32
68/push 0xc/imm32/size
89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX
# var EDX = [1, 2, 3]
68/push 3/imm32
68/push 2/imm32
68/push 1/imm32
68/push 0xc/imm32/size
89/copy 3/mod/direct 2/rm32/EDX . . . 4/r32/ESP . . # copy ESP to EDX
# EAX = array-equal?(ECX, EDX)
# . . push args
52/push-EDX
51/push-ECX
# . . call
e8/call array-equal?/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-compare-inequal-arrays-equal-lengths"/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
parse-array-of-ints: # ad : (address allocation-descriptor), s : (address string) -> result/EAX : (address array int)
# pseudocode
# end = s->data + s->length
# curr = s->data
# size = 0
# while true
# if (curr >= end) break
# curr = skip-chars-matching-in-slice(curr, end, ' ')
# if (curr >= end) break
# curr = skip-chars-not-matching-in-slice(curr, end, ' ')
# ++size
# result = allocate(ad, (size+1)*4)
# result->size = (size+1)*4
# var slice = {s->data, 0}
# out = result->data
# while true
# if (slice->start >= end) break
# slice->start = skip-chars-matching-in-slice(slice->start, end, ' ')
# if (slice->start >= end) break
# slice->end = skip-chars-not-matching-in-slice(slice->start, end, ' ')
# *out = parse-hex-int(slice)
# out += 4
# slice->start = slice->end
# return result
#
# . 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
53/push-EBX
56/push-ESI
57/push-EDI
# ESI = s
8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI
# curr/ECX = s->data
8d/copy-address 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # copy ESI+4 to ECX
# end/EDX = s->data + s->length
# . EDX = s->length
8b/copy 0/mod/indirect 6/rm32/ESI . . . 2/r32/EDX . . # copy *ESI to EDX
# . EDX += curr
01/add 3/mod/direct 2/rm32/EDX . . . 1/r32/ECX . . # add ECX to EDX
# size/EBX = 0
31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX
$parse-array-of-ints:loop1:
# if (curr >= end) break
39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX
73/jump-if-greater-or-equal-unsigned $parse-array-of-ints:break1/disp8
# curr = skip-chars-matching-in-slice(curr, end, ' ')
# . EAX = skip-chars-matching-in-slice(curr, end, ' ')
# . . push args
68/push 0x20/imm32/space
52/push-EDX
51/push-ECX
# . . call
e8/call skip-chars-matching-in-slice/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
# . ECX = EAX
89/copy 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # copy EAX to ECX
# if (curr >= end) break
39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX
73/jump-if-greater-or-equal-unsigned $parse-array-of-ints:break1/disp8
# curr = skip-chars-not-matching-in-slice(curr, end, ' ')
# . EAX = skip-chars-not-matching-in-slice(curr, end, ' ')
# . . push args
68/push 0x20/imm32/space
52/push-EDX
51/push-ECX
# . . call
e8/call skip-chars-not-matching-in-slice/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
# . ECX = EAX
89/copy 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # copy EAX to ECX
# size += 4
81 0/subop/add 3/mod/direct 3/rm32/EBX . . . . . 4/imm32 # add to EBX
eb/jump $parse-array-of-ints:loop1/disp8
$parse-array-of-ints:break1:
# result/EDI = allocate(ad, size+4)
# . EAX = allocate(ad, size+4)
# . . push args
89/copy 3/mod/direct 0/rm32/EAX . . . 3/r32/EBX . . # copy EBX to EAX
05/add-to-EAX 4/imm32
50/push-EAX
ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8)
# . . call
e8/call allocate/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
# . EDI = EAX
89/copy 3/mod/direct 7/rm32/EDI . . . 0/r32/EAX . . # copy EAX to EDI
# result->size = size
89/copy 0/mod/indirect 0/rm32/EAX . . . 3/r32/EBX . . # copy EBX to *EAX
$parse-array-of-ints:pass2:
# var slice/ECX = {s->data, 0}
# . push 0
68/push 0/imm32/end
# . push s->data
8d/copy-address 1/mod/*+disp8 6/rm32/ESI . . . 1/r32/ECX 4/disp8 . # copy ESI+4 to ECX
51/push-ECX
# . bookmark
89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX
# out/EBX = result->data
8d/copy-address 1/mod/*+disp8 0/rm32/EAX . . . 3/r32/EBX 4/disp8 . # copy EAX+4 to EBX
$parse-array-of-ints:loop2:
# if (slice->start >= end) break
39/compare 0/mod/indirect 1/rm32/ECX . . . 2/r32/EDX . . # compare *ECX with EDX
73/jump-if-greater-or-equal-unsigned $parse-array-of-ints:end/disp8
# slice->start = skip-chars-matching-in-slice(slice->start, end, ' ')
# . EAX = skip-chars-matching-in-slice(slice->start, end, ' ')
# . . push args
68/push 0x20/imm32/space
52/push-EDX
ff 6/subop/push 0/mod/indirect 1/rm32/ECX . . . . . . # push *ECX
# . . call
e8/call skip-chars-matching-in-slice/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
# . slice->start = EAX
89/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy EAX to *ECX
# if (slice->start >= end) break
39/compare 0/mod/indirect 1/rm32/ECX . . . 2/r32/EDX . . # compare *ECX with EDX
73/jump-if-greater-or-equal-unsigned $parse-array-of-ints:end/disp8
# slice->end = skip-chars-not-matching-in-slice(slice->start, end, ' ')
# . EAX = skip-chars-not-matching-in-slice(curr, end, ' ')
# . . push args
68/push 0x20/imm32/space
52/push-EDX
50/push-EAX
# . . call
e8/call skip-chars-not-matching-in-slice/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
# . slice->end = EAX
89/copy 1/mod/direct 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy EAX to *(ECX+4)
# *out = parse-hex-int(slice)
# . EAX = parse-hex-int(slice)
# . . push args
51/push-ECX
# . . call
e8/call parse-hex-int/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
# *out = EAX
89/copy 0/mod/indirect 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to *EBX
# out += 4
81 0/subop/add 3/mod/direct 3/rm32/EBX . . . . . 4/imm32 # add to EBX
# slice->start = slice->end
8b/copy 1/mod/direct 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy *(ECX+4) to EAX
89/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy EAX to *ECX
81 0/subop/add 3/mod/direct 1/rm32/ECX . . . . . 4/imm32 # add to ECX
eb/jump $parse-array-of-ints:loop2/disp8
$parse-array-of-ints:end:
# return EDI
89/copy 3/mod/direct 0/rm32/EAX . . . 7/r32/EDI . . # copy EDI to EAX
# . 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
5b/pop-to-EBX
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-parse-array-of-ints:
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# var ECX = [1, 2, 3]
68/push 3/imm32
68/push 2/imm32
68/push 1/imm32
68/push 0xc/imm32/size
89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX
# EAX = parse-array-of-ints(Heap, "1 2 3")
# . . push args
68/push "1 2 3"/imm32
68/push Heap/imm32
# . . call
e8/call parse-array-of-ints/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
# EAX = array-equal?(ECX, EAX)
# . . push args
50/push-EAX
51/push-ECX
# . . call
e8/call array-equal?/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-parse-array-of-ints"/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-parse-array-of-ints-empty:
# - empty string = empty array
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# EAX = parse-array-of-ints(Heap, "")
# . . push args
68/push ""/imm32
68/push Heap/imm32
# . . call
e8/call parse-array-of-ints/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-parse-array-of-ints-empty"/imm32
68/push 0/imm32/size
ff 6/subop/push 0/mod/indirect 0/rm32/EAX . . . . . . # 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-parse-array-of-ints-just-whitespace:
# - just whitespace = empty array
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# EAX = parse-array-of-ints(Heap, " ")
# . . push args
68/push " "/imm32
68/push Heap/imm32
# . . call
e8/call parse-array-of-ints/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-parse-array-of-ints-empty"/imm32
68/push 0/imm32/size
ff 6/subop/push 0/mod/indirect 0/rm32/EAX . . . . . . # 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-parse-array-of-ints-extra-whitespace:
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# var ECX = [1, 2, 3]
68/push 3/imm32
68/push 2/imm32
68/push 1/imm32
68/push 0xc/imm32/size
89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX
# EAX = parse-array-of-ints(Heap, " 1 2 3 ")
# . . push args
68/push " 1 2 3 "/imm32
68/push Heap/imm32
# . . call
e8/call parse-array-of-ints/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
# EAX = array-equal?(ECX, EAX)
# . . push args
50/push-EAX
51/push-ECX
# . . call
e8/call array-equal?/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-parse-array-of-ints-extra-whitespace"/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
# helper for later tests
# compare an array with a string representation of an array literal
check-array-equal: # a : (address array int), expected : (address string), msg : (address string)
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# . save registers
50/push-EAX
# var b/ECX = parse-array-of-ints(Heap, expected)
# . EAX = parse-array-of-ints(Heap, expected)
# . . push args
ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12)
68/push Heap/imm32
# . . call
e8/call parse-array-of-ints/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
# . b = EAX
89/copy 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # copy EAX to ECX
# EAX = array-equal?(a, b)
# . . push args
51/push-ECX
ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8)
# . . call
e8/call array-equal?/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
ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16)
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
$check-array-equal:end:
# . restore registers
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-check-array-equal:
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# var ECX = [1, 2, 3]
68/push 3/imm32
68/push 2/imm32
68/push 1/imm32
68/push 0xc/imm32/size
89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX
# check-array-equal(ECX, "1 2 3", "msg")
# . . push args
68/push "F - test-check-array-equal"/imm32
68/push "1 2 3"/imm32
51/push-ECX
# . . call
e8/call check-array-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/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
Heap:
# curr
0/imm32
# limit
0/imm32
# . . vim:nowrap:textwidth=0

84
subx/076zero-out.subx Normal file
View File

@ -0,0 +1,84 @@
# Fill a region of memory with zeroes.
== 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
zero-out: # start : address, len : int
# pseudocode:
# curr/ESI = start
# i/ECX = 0
# while true
# if (i >= len) break
# *curr = 0
# ++curr
# ++i
#
# . 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
56/push-ESI
# curr/ESI = start
8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI
# i/ECX = 0
31/xor 3/mod/direct 1/rm32/ECX . . . 1/r32/ECX . . # clear ECX
# EDX = len
8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 2/r32/EDX 0xc/disp8 . # copy *(EBP+12) to EDX
$zero-out:loop:
# if (i >= len) break
39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX
7d/jump-if-greater-or-equal $zero-out:end/disp8
# *curr = 0
c6 0/subop/copy 0/mod/direct 6/rm32/ESI . . . . . 0/imm8 # copy byte to *ESI
# ++curr
46/increment-ESI
# ++i
41/increment-ECX
eb/jump $zero-out:loop/disp8
$zero-out:end:
# . restore registers
5e/pop-to-ESI
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-zero-out:
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# region/ECX = 34, 35, 36, 37
68/push 0x37363534/imm32
89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX
# zero-out(ECX, 3)
# . . push args
68/push 3/imm32/len
51/push-ECX
# . . call
e8/call zero-out/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
# first 3 bytes cleared, fourth left alone
# . check-ints-equal(*ECX, 0x37000000, msg)
# . . push args
68/push "F - test-zero-out"/imm32
68/push 0x37000000/imm32
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
# . epilog
89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP
5d/pop-to-EBP
c3/return
# . . vim:nowrap:textwidth=0

View File

@ -627,18 +627,26 @@ allocated memory for it.)_
#### writing to disk
* `write`: string -> file
- Can also be used to cat a string into a stream.
- Will abort the entire program if there isn't enough room.
- Will abort the entire program if destination is a stream and doesn't have
enough room.
* `write-stream`: stream -> file
- Can also be used to cat one stream into another.
- Will abort the entire program if there isn't enough room.
- Will abort the entire program if destination is a stream and doesn't have
enough room.
* `write-slice`: slice -> stream
- Will abort the entire program if there isn't enough room in the
destination stream.
* `append-byte`: int -> stream
- Will abort the entire program if there isn't enough room.
- Will abort the entire program if there isn't enough room in the
destination stream.
* `append-byte-hex`: int -> stream
- textual representation in hex, no '0x' prefix
- Will abort the entire program if there isn't enough room.
- Will abort the entire program if there isn't enough room in the
destination stream.
* `print-int32`: int -> stream
- textual representation in hex, including '0x' prefix
- Will abort the entire program if there isn't enough room.
- Will abort the entire program if there isn't enough room in the
destination stream.
* `write-buffered`: string -> buffered-file
* `write-slice-buffered`: slice -> buffered-file
* `flush`: buffered-file

Binary file not shown.

View File

@ -34,11 +34,6 @@ Entry:
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
# for debugging: run a single 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
# run tests if necessary, convert stdin if not
# . prolog
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
@ -86,8 +81,8 @@ $main:end:
cd/syscall 0x80/imm8
# data structure:
# row: pair of (address array byte) and (address stream byte)
# table: (address stream row)
# table: (address stream {string, (address stream byte)}) (8 bytes per row)
# inefficient; uses sequential search for looking up segments by name
convert: # in : (address buffered-file), out : (address buffered-file) -> <void>
# pseudocode:
@ -439,7 +434,8 @@ test-convert:
5d/pop-to-EBP
c3/return
read-segments: # in : (address buffered-file), table : (address stream row)
# beware: leaks memory (one name per segment read)
read-segments: # in : (address buffered-file), table : (address stream {string, (address stream byte)})
# pseudocode:
# var curr-segment = null
# var line = new-stream(512, 1)
@ -454,13 +450,14 @@ read-segments: # in : (address buffered-file), table : (address stream row)
# continue
# if slice-equal?(word-slice, "==")
# var segment-name = next-word(line)
# curr-segment = get-or-insert-segment(table, segment-name, Segment-size)
# if curr-segment->write == 0
# rewind-stream(line)
# write-stream(curr-segment, line)
# else
# rewind-stream(line)
# write-stream(curr-segment, line) # abort if curr-segment overflows
# segment-slot = leaky-get-or-insert-slice(table, segment-name, row-size=8)
# curr-segment = *segment-slot
# if curr-segment != 0
# continue
# curr-segment = new-stream(Segment-size)
# *segment-slot = curr-segment
# rewind-stream(line)
# write-stream(curr-segment, line) # abort if curr-segment overflows
#
# word-slice and segment-name are both slices with disjoint lifetimes, so
# we'll use the same address for them.
@ -489,7 +486,7 @@ read-segments: # in : (address buffered-file), table : (address stream row)
89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX
# var word-slice/EDX = {0, 0}
68/push 0/imm32/end
68/push 0/imm32/curr
68/push 0/imm32/start
89/copy 3/mod/direct 2/rm32/EDX . . . 4/r32/ESP . . # copy ESP to EDX
$read-segments:loop:
# clear-stream(line)
@ -610,10 +607,7 @@ $read-segments:check-for-segment-header:
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
#? # }}}
# if slice-equal?(word-slice, "==")
# segment-name = next-word(line)
# curr-segment = get-or-insert(table, segment-name)
# if (curr-segment->write > 0) continue
# if !slice-equal?(word-slice, "==") goto next check
# . EAX = slice-equal?(word-slice, "==")
# . . push args
68/push "=="/imm32
@ -625,7 +619,7 @@ $read-segments:check-for-segment-header:
# . if (EAX == 0) goto check3
3d/compare-EAX-and 0/imm32
0f 84/jump-if-equal $read-segments:regular-line/disp32
# . next-word(line, segment-name)
# segment-name = next-word(line)
# . . push args
52/push-EDX
51/push-ECX
@ -675,21 +669,38 @@ $read-segments:check-for-segment-header:
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
#? # }}}
# . EAX = get-or-insert-segment(table, segment-name, Segment-size)
# segment-slot/EAX = leaky-get-or-insert-slice(table, segment-name, row-size=8)
# . . push args
ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Segment-size/disp32 # push *Segment-size
68/push 8/imm32/row-size
52/push-EDX
ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12)
# . . call
e8/call get-or-insert-segment/disp32
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
# curr-segment = *segment-slot
8b/copy 0/mod/indirect 0/rm32/EAX . . . 3/r32/EBX . . # copy *EAX to EBX
# if (curr-segment != 0) continue
81 7/subop/compare 3/mod/direct 3/rm32/EBX . . . . . 0/imm32 # compare EBX
0f 85/jump-if-not-equal $read-segments:loop/disp32
# curr-segment = new-stream(Heap, Segment-size, 1)
# . save segment-slot
50/push-EAX
# . EAX = new-stream(Heap, Segment-size, 1)
# . . push args
68/push 1/imm32
ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Segment-size/disp32 # push *Segment-size
68/push Heap/imm32
# . . call
e8/call new-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
# . curr-segment = EAX
89/copy 3/mod/direct 3/rm32/EBX . . . 0/r32/EAX . . # copy EAX to EBX
# . if (curr-segment->write > 0) continue
8b/copy 0/mod/indirect 0/rm32/EAX . . . 0/r32/EAX . . # copy *EAX to EAX
3d/compare-EAX-and 0/imm32
0f 8f/jump-if-greater $read-segments:loop/disp32
# . restore segment-slot
58/pop-to-EAX
# *segment-slot = curr-segment
89/copy 0/mod/indirect 0/rm32/EAX . . . 3/r32/EBX . . # copy EBX to *EAX
# fall through
$read-segments:regular-line:
# rewind-stream(line)
@ -724,7 +735,7 @@ $read-segments:end:
5d/pop-to-EBP
c3/return
write-segments: # out : (address buffered-file), table : (address stream row)
write-segments: # out : (address buffered-file), table : (address stream {string, (address stream byte)})
# pseudocode:
# var curr = table->data
# var max = table->data + table->write
@ -752,7 +763,7 @@ write-segments: # out : (address buffered-file), table : (address stream row)
$write-segments:loop:
# if (curr >= max) break
39/compare 3/mod/direct 6/rm32/ESI . . . 2/r32/EDX . . # compare ESI with EDX
7d/jump-if-greater-or-equal $write-segments:break/disp8
73/jump-if-greater-or-equal-unsigned $write-segments:break/disp8
# stream/EAX = table[i].stream
8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 4/disp8 . # copy *(ESI+4) to EAX
# write-stream-data(out, stream)
@ -785,534 +796,9 @@ $write-segments:end:
5d/pop-to-EBP
c3/return
## helpers
# TODO: pass in an allocation descriptor
get-or-insert-segment: # table : (address stream row), s : (address slice), n : int -> EAX : (address stream)
# pseudocode:
# curr = table->data
# max = &table->data[table->write]
# while curr < max
# if slice-equal?(s, *curr)
# return *(curr+4)
# curr += 8
# if table->write < table->length
# *max = slice-to-string(Heap, s)
# result = new-stream(Heap, n, 1)
# *(max+4) = result
# table->write += 8
# return result
# return 0
#
# . 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-segment:search-loop:
# if (curr >= max) break
39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX
7d/jump-if-greater-or-equal $get-or-insert-segment:not-found/disp8
# if (slice-equal?(s, *curr)) return *(curr+4)
# . EAX = slice-equal?(s, *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-or-insert-segment:mismatch/disp8
8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy *(ECX+4) to EAX
eb/jump $get-or-insert-segment:end/disp8
$get-or-insert-segment:mismatch:
# curr += 8
81 0/subop/add 3/mod/direct 1/rm32/ECX . . . . . 8/imm32 # add to ECX
# loop
eb/jump $get-or-insert-segment:search-loop/disp8
$get-or-insert-segment: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 $get-or-insert-segment:abort/disp8
# *max = slice-to-string(Heap, s)
# . EAX = slice-to-string(Heap, s)
# . . 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
# result/EAX = new-stream(Heap, n, 1)
# . . push args
68/push 1/imm32
ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16)
68/push Heap/imm32
# . . call
e8/call new-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
# *(max+4) = result
89/copy 1/mod/*+disp8 2/rm32/EDX . . . 0/r32/EAX 4/disp8 . # copy EAX to *(EDX+4)
# table->write += 8
81 0/subop/add 0/mod/indirect 6/rm32/ESI . . . . . 8/imm32 # add to *ESI
$get-or-insert-segment: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-segment:abort:
# . _write(2/stderr, error)
# . . push args
68/push "get-or-insert-segment: too many segments\n"/imm32
68/push 2/imm32/stderr
# . . call
e8/call _write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
# . 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-segment:
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
# var table/ECX : (address stream byte) = stream(2 * 8)
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
# EDX : (address slice) = "code"
68/push _test-code-segment-end/imm32/end
68/push _test-code-segment/imm32/start
89/copy 3/mod/direct 2/rm32/EDX . . . 4/r32/ESP . . # copy ESP to EDX
$test-get-or-insert-segment:first-call:
# - start with an empty table, insert one segment, verify that it was inserted
# segment/EAX = get-or-insert-segment(table, "code" slice, 10)
# . . push args
68/push 0xa/imm32/segment-length
52/push-EDX
51/push-ECX
# . . call
e8/call get-or-insert-segment/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
# save segment
50/push-EAX
# if (segment != 0) goto next check
3d/compare-EAX-and 0/imm32
75/jump-if-not-equal $test-get-or-insert-segment:check1/disp8
# fail test
# . _write(2/stderr, msg)
# . . push args
68/push "F - test-get-or-insert-segment/0\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
# . increment Num-test-failures
ff 0/subop/increment 0/mod/indirect 5/rm32/.disp32 . . . Num-test-failures/disp32 # increment *Num-test-failures
e9/jump $test-get-or-insert-segment:end/disp32
$test-get-or-insert-segment:check1:
# check-ints-equal(segment->length, 10, msg)
# . . push args
68/push "F - test-get-or-insert-segment/1"/imm32
68/push 0xa/imm32/segment-length
ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 8/disp8 . # push *(EAX+8)
# . . 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-segment:check2:
# check-ints-equal(table->write, rowsize = 8, msg)
# . . push args
68/push "F - test-get-or-insert-segment/2"/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
# EAX = string-equal?(*table->data, "code")
# . . push args
68/push "code"/imm32
ff 6/subop/push 1/mod/*+disp8 1/rm32/ECX . . . . 0xc/disp8 . # push *(ECX+12)
# . . call
e8/call string-equal?/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-get-or-insert-segment/3"/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-get-or-insert-segment:check3:
# stream/EAX = *(table->data+4)
8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 0x10/disp8 . # copy *(ECX+16) to EAX
# check-ints-equal(stream->length, 10, msg)
# . . push args
68/push "F - test-get-or-insert-segment/4"/imm32
68/push 0xa/imm32/segment-size
ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 8/disp8 . # push *(EAX+8)
# . . 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-segment:second-call:
# - insert the same segment name again, verify that it was reused
# segment2/EAX = get-or-insert-segment(table, "code" slice, 8)
# . . push args
68/push 8/imm32/segment-length
52/push-EDX
51/push-ECX
# . . call
e8/call get-or-insert-segment/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP
# restore old segment1
5a/pop-to-EDX
# check-ints-equal(segment2/EAX, segment1/EDX, msg)
# . . push args
68/push "F - test-get-or-insert-segment/5"/imm32
52/push-EDX
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 change to table size
# . check-ints-equal(table->write, rowsize = 8, msg)
# . . push args
68/push "F - test-get-or-insert-segment/6"/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
$test-get-or-insert-segment:third-call:
# - insert a new segment name, verify that it was inserted
# EDX : (address slice) = "data"
c7 0/subop/copy 0/mod/indirect 2/rm32/EDX . . . . . _test-data-segment/imm32 # copy to *EDX
c7 0/subop/copy 1/mod/*+disp8 2/rm32/EDX . . . . 4/disp8 _test-data-segment-end/imm32 # copy to *(EDX+4)
# segment2/EAX = get-or-insert-segment(table, "data" slice, 8)
# . . push args
68/push 8/imm32/segment-length
52/push-EDX
51/push-ECX
# . . call
e8/call get-or-insert-segment/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(table->write, 2 rows = 16, msg)
# . . push args
68/push "F - test-get-or-insert-segment/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
$test-get-or-insert-segment: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
== data
_test-code-segment:
63/c 6f/o 64/d 65/e
_test-code-segment-end:
_test-data-segment:
64/d 61/a 74/t 61/a
_test-data-segment-end:
Segment-size:
0x1000/imm32/4KB
Heap:
# curr
0/imm32
# limit
0/imm32
# . . vim:nowrap:textwidth=0

Binary file not shown.

View File

@ -31,10 +31,15 @@
# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes
Entry: # run tests if necessary, call 'compile' if not
#? # for debugging: run a single test; don't bother setting status code
#? e8/call test-get-num-aborts-on-non-digit-in-Look/disp32
#? eb/jump $main:end/disp8
# initialize heap
# . Heap = new-segment(64KB)
# . . push args
68/push Heap/imm32
68/push 0x10000/imm32/64KB
# . . call
e8/call new-segment/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
# . prolog
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP

Binary file not shown.

View File

@ -31,10 +31,15 @@
# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes
Entry: # run tests if necessary, call 'compile' if not
#? # for debugging: run a single test; don't bother setting status code
#? e8/call test-get-num-reads-single-digit/disp32
#? eb/jump $main:end/disp8
# initialize heap
# . Heap = new-segment(64KB)
# . . push args
68/push Heap/imm32
68/push 0x10000/imm32/64KB
# . . call
e8/call new-segment/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
# . prolog
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP

Binary file not shown.

View File

@ -30,11 +30,6 @@ Entry:
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
# for debugging: run a single test
#? e8/call test-string-length-at-start-of-slice-escaped/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
# run tests if necessary, convert stdin if not
# . prolog
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
@ -95,7 +90,7 @@ convert: # in : (address buffered-file), out : (address buffered-file) -> <void
# read-line-buffered(in, line)
# if (line->write == 0) break # end of file
# while true
# var word-slice = next-word(line)
# var word-slice = next-word-or-string(line)
# if slice-empty?(word-slice) # end of line
# break
# if slice-starts-with?(word-slice, "#") # comment
@ -127,7 +122,7 @@ convert: # in : (address buffered-file), out : (address buffered-file) -> <void
89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX
# var word-slice/EDX = {0, 0}
68/push 0/imm32/end
68/push 0/imm32/curr
68/push 0/imm32/start
89/copy 3/mod/direct 2/rm32/EDX . . . 4/r32/ESP . . # copy ESP to EDX
# new-data-segment/EDI = new-stream(Heap, Segment-size, 1)
# . EAX = new-stream(Heap, Segment-size, 1)
@ -170,12 +165,12 @@ $convert:check0:
81 7/subop/compare 0/mod/indirect 1/rm32/ECX . . . . . 0/imm32 # compare *ECX
0f 84/jump-if-equal $convert:break/disp32
$convert:word-loop:
# next-word(line, word-slice)
# next-word-or-string(line, word-slice)
# . . push args
52/push-EDX
51/push-ECX
# . . call
e8/call next-word/disp32
e8/call next-word-or-string/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
$convert:check1:
@ -913,7 +908,7 @@ $emit-string-literal-data:loop-init:
$emit-string-literal-data:loop:
# if (curr >= max) break
39/compare 3/mod/direct 2/rm32/EDX . . . 6/r32/ESI . . # compare EDX with ESI
7d/jump-if-greater-or-equal $emit-string-literal-data:end/disp8
73/jump-if-greater-or-equal-unsigned $emit-string-literal-data:end/disp8
# CL = *curr
8a/copy-byte 0/mod/indirect 2/rm32/EDX . . . 1/r32/CL . . # copy byte at *EDX to CL
# if (ECX == '"') break
@ -926,7 +921,7 @@ $emit-string-literal-data:loop:
42/increment-EDX
# . if (curr >= max) break
39/compare 3/mod/direct 2/rm32/EDX . . . 6/r32/ESI . . # compare EDX with ESI
7d/jump-if-greater-or-equal $emit-string-literal-data:end/disp8
73/jump-if-greater-or-equal-unsigned $emit-string-literal-data:end/disp8
# . CL = *curr
8a/copy-byte 0/mod/indirect 2/rm32/EDX . . . 1/r32/CL . . # copy byte at *EDX to CL
$emit-string-literal-data:emit:
@ -1037,7 +1032,7 @@ test-emit-string-literal-data:
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
# var slice/ECX = '"abc"/d'
68/push _test-slice-abc-metadata-end/imm32
68/push _test-slice-abc-limit/imm32
68/push _test-slice-abc/imm32
89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX
# emit-string-literal-data(_test-output-stream, slice)
@ -1101,8 +1096,8 @@ test-emit-string-literal-data-empty:
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
# var slice/ECX = '""'
68/push _test-slice-empty-string-literal-end/imm32
68/push _test-slice-empty-string-literal/imm32
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
# emit-string-literal-data(_test-output-stream, slice)
# . . push args
@ -1166,7 +1161,7 @@ test-emit-string-literal-data-no-metadata-for-non-alphanumerics:
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
# var slice/ECX = '"a b"'
68/push _test-slice-a-space-b-end/imm32
68/push _test-slice-a-space-b-limit/imm32
68/push _test-slice-a-space-b/imm32
89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX
# emit-string-literal-data(_test-output-stream, slice)
@ -1230,7 +1225,7 @@ test-emit-string-literal-data-handles-escape-sequences:
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
# var slice/ECX = '"a\"b"'
68/push _test-slice-a-dquote-b-end/imm32
68/push _test-slice-a-dquote-b-limit/imm32
68/push _test-slice-a-dquote-b/imm32
89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX
# emit-string-literal-data(_test-output-stream, slice)
@ -1295,7 +1290,7 @@ emit-metadata: # out : (address buffered-file), word : (address slice)
# if *curr == '/'
# break
# ++curr
# slice->curr = curr
# slice->start = curr
# write-slice-buffered(out, slice)
#
# . prolog
@ -1349,7 +1344,7 @@ $emit-metadata:skip-datum-loop:
41/increment-ECX
eb/jump $emit-metadata:skip-datum-loop/disp8
$emit-metadata:emit:
# slice->curr = ECX
# slice->start = ECX
89/copy 0/mod/indirect 3/rm32/EBX . . . 1/r32/ECX . . # copy ECX to *EBX
# write-slice-buffered(out, slice)
# . . push args
@ -1394,9 +1389,14 @@ test-emit-metadata:
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 = "abc/def"
68/push _test-slice-word-end/imm32
68/push _test-slice-word/imm32/start
# (EAX..ECX) = "abc/def"
b8/copy-to-EAX "abc/def"/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-metadata(_test-output-buffered-file, slice)
# . . push args
@ -1448,9 +1448,14 @@ test-emit-metadata-none:
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 = "abc"
68/push _test-slice-word-datum-end/imm32
68/push _test-slice-word/imm32/start
# (EAX..ECX) = "abc"
b8/copy-to-EAX "abc"/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-metadata(_test-output-buffered-file, slice)
# . . push args
@ -1502,9 +1507,14 @@ test-emit-metadata-multiple:
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 = "abc/def/ghi"
68/push _test-slice-word-end2/imm32
68/push _test-slice-word/imm32/start
# (EAX..ECX) = "abc/def/ghi"
b8/copy-to-EAX "abc/def/ghi"/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-metadata(_test-output-buffered-file, slice)
# . . push args
@ -1618,7 +1628,7 @@ test-emit-metadata-in-string-literal:
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
# var slice/ECX = "\"abc/def\"/ghi"
68/push _test-slice-literal-string-with-metadata-end/imm32
68/push _test-slice-literal-string-with-limit/imm32
68/push _test-slice-literal-string/imm32/start
89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX
# emit-metadata(_test-output-buffered-file, slice)
@ -1678,7 +1688,7 @@ test-emit-metadata-in-string-literal:
# (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)
next-word-or-string: # 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
@ -1699,18 +1709,18 @@ next-word: # line : (address stream byte), out : (address slice)
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:
$next-word-or-string: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
7c/jump-if-lesser $next-word-or-string: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:
eb/jump $next-word-or-string:end/disp8
$next-word-or-string: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
@ -1721,8 +1731,8 @@ $next-word:check-for-comment:
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:check-for-string-literal/disp8
$next-word:comment:
75/jump-if-not-equal $next-word-or-string:check-for-string-literal/disp8
$next-word-or-string: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
@ -1731,16 +1741,16 @@ $next-word:comment:
8b/copy 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # copy *ESI to EAX
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:check-for-string-literal:
eb/jump $next-word-or-string:end/disp8
$next-word-or-string:check-for-string-literal:
# if line->data[line->read] == '"'
# . 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 0x22/imm32/dquote
75/jump-if-not-equal $next-word:regular-word/disp8
$next-word:string-literal:
75/jump-if-not-equal $next-word-or-string:regular-word/disp8
$next-word-or-string:string-literal:
# skip-string(line)
# . . push args
56/push-ESI
@ -1749,7 +1759,7 @@ $next-word:string-literal:
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
# fall through
$next-word:regular-word:
$next-word-or-string:regular-word:
# 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)
@ -1761,7 +1771,7 @@ $next-word:regular-word:
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:
$next-word-or-string:end:
# . restore registers
5f/pop-to-EDI
5e/pop-to-ESI
@ -1772,7 +1782,7 @@ $next-word:end:
5d/pop-to-EBP
c3/return
test-next-word:
test-next-word-or-string:
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
@ -1796,17 +1806,17 @@ test-next-word:
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
# next-word(_test-input-stream, slice)
# next-word-or-string(_test-input-stream, slice)
# . . push args
51/push-ECX
68/push _test-input-stream/imm32
# . . call
e8/call next-word/disp32
e8/call next-word-or-string/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
# check-ints-equal(_test-input-stream->read, 4, msg)
# . . push args
68/push "F - test-next-word/updates-stream-read-correctly"/imm32
68/push "F - test-next-word-or-string/updates-stream-read-correctly"/imm32
68/push 4/imm32
b8/copy-to-EAX _test-input-stream/imm32
ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4)
@ -1817,7 +1827,7 @@ test-next-word:
# check-ints-equal(slice->start - _test-input-stream->data, 2, msg)
# . check-ints-equal(slice->start - _test-input-stream, 14, msg)
# . . push args
68/push "F - test-next-word: start"/imm32
68/push "F - test-next-word-or-string: start"/imm32
68/push 0xe/imm32
# . . push slice->start - _test-input-stream
8b/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy *ECX to EAX
@ -1830,7 +1840,7 @@ test-next-word:
# check-ints-equal(slice->end - _test-input-stream->data, 4, msg)
# . check-ints-equal(slice->end - _test-input-stream, 16, msg)
# . . push args
68/push "F - test-next-word: end"/imm32
68/push "F - test-next-word-or-string: end"/imm32
68/push 0x10/imm32
# . . push slice->end - _test-input-stream
8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy *(ECX+4) to EAX
@ -1845,7 +1855,7 @@ test-next-word:
5d/pop-to-EBP
c3/return
test-next-word-returns-whole-comment:
test-next-word-or-string-returns-whole-comment:
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
@ -1869,17 +1879,17 @@ test-next-word-returns-whole-comment:
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
# next-word(_test-input-stream, slice)
# next-word-or-string(_test-input-stream, slice)
# . . push args
51/push-ECX
68/push _test-input-stream/imm32
# . . call
e8/call next-word/disp32
e8/call next-word-or-string/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
# check-ints-equal(_test-input-stream->read, 5, msg)
# . . push args
68/push "F - test-next-word-returns-whole-comment/updates-stream-read-correctly"/imm32
68/push "F - test-next-word-or-string-returns-whole-comment/updates-stream-read-correctly"/imm32
68/push 5/imm32
b8/copy-to-EAX _test-input-stream/imm32
ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4)
@ -1890,7 +1900,7 @@ test-next-word-returns-whole-comment:
# check-ints-equal(slice->start - _test-input-stream->data, 2, msg)
# . check-ints-equal(slice->start - _test-input-stream, 14, msg)
# . . push args
68/push "F - test-next-word-returns-whole-comment: start"/imm32
68/push "F - test-next-word-or-string-returns-whole-comment: start"/imm32
68/push 0xe/imm32
# . . push slice->start - _test-input-stream
8b/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy *ECX to EAX
@ -1903,7 +1913,7 @@ test-next-word-returns-whole-comment:
# check-ints-equal(slice->end - _test-input-stream->data, 5, msg)
# . check-ints-equal(slice->end - _test-input-stream, 17, msg)
# . . push args
68/push "F - test-next-word-returns-whole-comment: end"/imm32
68/push "F - test-next-word-or-string-returns-whole-comment: end"/imm32
68/push 0x11/imm32
# . . push slice->end - _test-input-stream
8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy *(ECX+4) to EAX
@ -1918,7 +1928,7 @@ test-next-word-returns-whole-comment:
5d/pop-to-EBP
c3/return
test-next-word-returns-empty-string-on-eof:
test-next-word-or-string-returns-empty-string-on-eof:
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
@ -1935,17 +1945,17 @@ test-next-word-returns-empty-string-on-eof:
68/push 0/imm32/start
89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX
# write nothing to _test-input-stream
# next-word(_test-input-stream, slice)
# next-word-or-string(_test-input-stream, slice)
# . . push args
51/push-ECX
68/push _test-input-stream/imm32
# . . call
e8/call next-word/disp32
e8/call next-word-or-string/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 "F - test-next-word-or-string-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
@ -1960,7 +1970,7 @@ test-next-word-returns-empty-string-on-eof:
5d/pop-to-EBP
c3/return
test-next-word-returns-whole-string:
test-next-word-or-string-returns-whole-string:
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
@ -1984,18 +1994,18 @@ test-next-word-returns-whole-string:
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
# next-word(_test-input-stream, slice)
# next-word-or-string(_test-input-stream, slice)
# . . push args
51/push-ECX
68/push _test-input-stream/imm32
# . . call
e8/call next-word/disp32
e8/call next-word-or-string/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
# check-ints-equal(slice->start - _test-input-stream->data, 1, msg)
# . check-ints-equal(slice->start - _test-input-stream, 13, msg)
# . . push args
68/push "F - test-next-word-returns-whole-string: start"/imm32
68/push "F - test-next-word-or-string-returns-whole-string: start"/imm32
68/push 0xd/imm32
# . . push slice->start - _test-input-stream
8b/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy *ECX to EAX
@ -2008,7 +2018,7 @@ test-next-word-returns-whole-string:
# check-ints-equal(slice->end - _test-input-stream->data, 12, msg)
# . check-ints-equal(slice->end - _test-input-stream, 24, msg)
# . . push args
68/push "F - test-next-word-returns-whole-string: end"/imm32
68/push "F - test-next-word-or-string-returns-whole-string: end"/imm32
68/push 0x18/imm32
# . . push slice->end - _test-input-stream
8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy *(ECX+4) to EAX
@ -2023,7 +2033,7 @@ test-next-word-returns-whole-string:
5d/pop-to-EBP
c3/return
test-next-word-returns-string-with-escapes:
test-next-word-or-string-returns-string-with-escapes:
# . prolog
55/push-EBP
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP
@ -2047,18 +2057,18 @@ test-next-word-returns-string-with-escapes:
e8/call write/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
# next-word(_test-input-stream, slice)
# next-word-or-string(_test-input-stream, slice)
# . . push args
51/push-ECX
68/push _test-input-stream/imm32
# . . call
e8/call next-word/disp32
e8/call next-word-or-string/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
# check-ints-equal(slice->start - _test-input-stream->data, 1, msg)
# . check-ints-equal(slice->start - _test-input-stream, 13, msg)
# . . push args
68/push "F - test-next-word-returns-string-with-escapes: start"/imm32
68/push "F - test-next-word-or-string-returns-string-with-escapes: start"/imm32
68/push 0xd/imm32
# . . push slice->start - _test-input-stream
8b/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy *ECX to EAX
@ -2071,7 +2081,7 @@ test-next-word-returns-string-with-escapes:
# check-ints-equal(slice->end - _test-input-stream->data, 9, msg)
# . check-ints-equal(slice->end - _test-input-stream, 21, msg)
# . . push args
68/push "F - test-next-word-returns-string-with-escapes: end"/imm32
68/push "F - test-next-word-or-string-returns-string-with-escapes: end"/imm32
68/push 0x15/imm32
# . . push slice->end - _test-input-stream
8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy *(ECX+4) to EAX
@ -2634,12 +2644,6 @@ 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
@ -2656,30 +2660,16 @@ Slash:
_test-slice-abc:
22/dquote 61/a 62/b 63/c 22/dquote # "abc"
_test-slice-abc-end:
2f/slash 64/d
_test-slice-abc-metadata-end:
_test-slice-empty-string-literal:
22/dquote 22/dquote # ""
_test-slice-empty-string-literal-end:
_test-slice-abc-limit:
_test-slice-a-space-b:
22/dquote 61/a 20/space 62/b 22/dquote # "a b"
_test-slice-a-space-b-end:
_test-slice-a-space-b-limit:
_test-slice-a-dquote-b:
22/dquote 61/a 5c/backslash 22/dquote 62/b 22/dquote # "a\"b"
_test-slice-a-dquote-b-end:
# abc/def/ghi
_test-slice-word:
61/a 62/b 63/c # abc
_test-slice-word-datum-end:
2f/slash 64/d 65/e 66/f # /def
_test-slice-word-end:
2f/slash 67/g 68/h 69/i # /ghi
_test-slice-word-end2:
_test-slice-a-dquote-b-limit:
# "abc/def"/ghi
_test-slice-literal-string:
@ -2687,8 +2677,7 @@ _test-slice-literal-string:
61/a 62/b 63/c # abc
2f/slash 64/d 65/e 66/f # /def
22/dquote
_test-slice-literal-string-end:
2f/slash 67/g 68/h 69/i # /ghi
_test-slice-literal-string-with-metadata-end:
_test-slice-literal-string-with-limit:
# . . vim:nowrap:textwidth=0

Binary file not shown.

View File

@ -19,10 +19,15 @@
# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes
Entry: # run tests if necessary, compute `factorial(5)` if not
#? # for debugging: run a single test; don't bother setting status code
#? e8/call test-get-num-reads-single-digit/disp32
#? eb/jump $main:end/disp8
# initialize heap
# . Heap = new-segment(64KB)
# . . push args
68/push Heap/imm32
68/push 0x10000/imm32/64KB
# . . call
e8/call new-segment/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
# . prolog
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP

Binary file not shown.

Binary file not shown.

View File

@ -18,11 +18,15 @@
# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes
Entry: # run tests if necessary, convert stdin if not
#? # for debugging: run a single test
#? e8/call test-convert-next-octet-aborts-on-single-hex-byte/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
# initialize heap
# . Heap = new-segment(64KB)
# . . push args
68/push Heap/imm32
68/push 0x10000/imm32/64KB
# . . call
e8/call new-segment/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
# . prolog
89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

BIN
subx/apps/survey Executable file

Binary file not shown.

3993
subx/apps/survey.subx Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,28 +0,0 @@
#!/bin/sh
# Little helper to quickly build SubX programs in 'debug mode' from the commandline.
# Only works for programs in some standard places the repo knows about.
if [ $# -eq 0 ]
then
echo "Usage: $0 <file root without subdirectory or .subx extension>"
echo
echo "Naming convention: Files starting with 'ex' will be assumed to live in examples/ and be self-contained."
echo "Other files will be assumed to live in apps/ and need the standard library."
exit 1
fi
# Build in debug mode since the common case at the moment is building small
# files. To override, calling scripts should do their own builds to ensure
# subx_bin is up to date.
export CFLAGS=-g
case $1 in
ex*)
./subx --debug translate examples/$1.subx -o examples/`echo $1 |sed 's/\..*//'`
exit $?
;;
*)
./subx --debug translate *.subx apps/$1.subx -o apps/`echo $1 |sed 's/\..*//'`
exit $?
;;
esac

View File

@ -1,22 +0,0 @@
#!/usr/bin/env zsh
# Run commonly-used SubX programs using the SubX VM in 'debug mode'.
if [ $# -eq 0 ]
then
echo "Usage: $0 <binary name without directory> <args>"
echo
echo "Naming convention: Binaries starting with 'ex' will be assumed to live in examples/"
echo "Other binaries will be assumed to live in apps/"
exit 1
fi
case $1 in
ex*)
./subx --debug --trace run examples/$*
exit $?
;;
*)
./subx --debug --trace run apps/$*
exit $?
;;
esac

View File

@ -1,28 +0,0 @@
#!/bin/sh
# Little helper to quickly build SubX programs from the commandline.
# Only works for programs in some standard places the repo knows about.
if [ $# -eq 0 ]
then
echo "Usage: $0 <file root without subdirectory or .subx extension>"
echo
echo "Naming convention: Files starting with 'ex' will be assumed to live in examples/ and be self-contained."
echo "Other files will be assumed to live in apps/ and need the standard library."
exit 1
fi
# Build in debug mode since the common case at the moment is building small
# files. To override, calling scripts should do their own builds to ensure
# subx_bin is up to date.
export CFLAGS=-g
case $1 in
ex*)
./subx translate examples/$1.subx -o examples/`echo $1 |sed 's/\..*//'`
exit $?
;;
*)
./subx translate *.subx apps/$1.subx -o apps/`echo $1 |sed 's/\..*//'`
exit $?
;;
esac

View File

@ -1,22 +0,0 @@
#!/usr/bin/env zsh
# Run commonly-used SubX programs using the SubX VM.
if [ $# -eq 0 ]
then
echo "Usage: $0 <binary name without directory> <args>"
echo
echo "Naming convention: Binaries starting with 'ex' will be assumed to live in examples/"
echo "Other binaries will be assumed to live in apps/"
exit 1
fi
case $1 in
ex*)
./subx run examples/$*
exit $?
;;
*)
./subx run apps/$*
exit $?
;;
esac

22
subx/run_one_test.sh Executable file
View File

@ -0,0 +1,22 @@
#!/usr/bin/env zsh
# Either run the test with the given name, or rerun the most recently run test.
# Intended to be called from within Vim. Check out the vimrc.vim file.
if [[ $2 == 'test-'* ]]
then
TEST_NAME=$2 envsubst '$TEST_NAME' < run_one_test.subx > /tmp/run_one_test.subx
FILES=$(ls [0-9]*.subx apps/subx-common.subx $1 |sort |uniq)
echo $FILES > /tmp/last_run_files
elif [[ -e /tmp/last_run_files ]]
then
FILES=`cat /tmp/last_run_files`
else
echo "no test found"
exit 0 # don't open trace
fi
set -e
# turn newlines into spaces
CFLAGS=$CFLAGS ./subx --debug translate $(echo $FILES) /tmp/run_one_test.subx -o /tmp/a.elf
./subx --debug --trace run /tmp/a.elf

30
subx/run_one_test.subx Normal file
View File

@ -0,0 +1,30 @@
# run a single test
== code
# instruction effective address register displacement immediate
# . op subop mod rm32 base index scale r32
# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes
Entry:
# Heap = new-segment(64KB)
# . . push args
68/push Heap/imm32
68/push 0x10000/imm32/64KB
# . . call
e8/call new-segment/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP
# initialize-trace-stream(256KB)
# . . push args
68/push 0x40000/imm32/256KB
# . . call
e8/call initialize-trace-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP
# for debugging: run a single test
e8/call $TEST_NAME/disp32
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
cd/syscall 0x80/imm8
# . . vim:nowrap:textwidth=0

View File

@ -184,6 +184,16 @@ test `uname` = 'Linux' && {
echo
}
echo survey
./subx translate 0*.subx apps/subx-common.subx apps/survey.subx -o apps/survey
[ "$1" != record ] && git diff --exit-code apps/survey
./subx run apps/survey test
echo
test `uname` = 'Linux' && {
apps/survey test
echo
}
echo pack
./subx translate 0*.subx apps/subx-common.subx apps/pack.subx -o apps/pack
[ "$1" != record ] && git diff --exit-code apps/pack

View File

@ -1 +0,0 @@
../vimrc.vim

95
subx/vimrc.vim Normal file
View File

@ -0,0 +1,95 @@
" Highlighting literate directives in C++ sources.
function! HighlightTangledFile()
" Tangled comments only make sense in the sources and are stripped out of
" the generated .cc file. They're highlighted same as regular comments.
syntax match tangledComment /\/\/:.*/ | highlight link tangledComment Comment
syntax match tangledSalientComment /\/\/::.*/ | highlight link tangledSalientComment SalientComment
set comments-=://
set comments-=n://
set comments+=n://:,n://
" Inside tangle scenarios.
syntax region tangleDirective start=+:(+ skip=+".*"+ end=+)+
highlight link tangleDirective Delimiter
syntax match traceContains /^+.*/
highlight traceContains ctermfg=22
syntax match traceAbsent /^-.*/
highlight traceAbsent ctermfg=darkred
syntax match tangleScenarioSetup /^\s*% .*/ | highlight link tangleScenarioSetup SpecialChar
highlight Special ctermfg=160
syntax match subxString %"[^"]*"% | highlight link subxString Constant
" match globals but not registers like 'EAX'
syntax match subxGlobal %\<[A-Z][a-z0-9_-]*\>% | highlight link subxGlobal SpecialChar
endfunction
augroup LocalVimrc
autocmd BufRead,BufNewFile *.cc call HighlightTangledFile()
autocmd BufRead,BufNewFile *.subx set ft=subx
augroup END
" Scenarios considered:
" opening or starting vim with a new or existing file without an extension (should interpret as C++)
" opening or starting vim with a new or existing file with a .mu extension
" starting vim or opening a buffer without a file name (ok to do nothing)
" opening a second file in a new or existing window (shouldn't mess up existing highlighting)
" reloading an existing file (shouldn't mess up existing highlighting)
" assumes CWD is subx/
command! -nargs=1 E call EditSubx("edit", <f-args>)
if exists("&splitvertical")
command! -nargs=1 S call EditSubx("vert split", <f-args>)
command! -nargs=1 H call EditSubx("hor split", <f-args>)
else
command! -nargs=1 S call EditSubx("vert split", <f-args>)
command! -nargs=1 H call EditSubx("split", <f-args>)
endif
function! EditSubx(cmd, arg)
exec "silent! " . a:cmd . " " . SubxPath(a:arg)
endfunction
function! SubxPath(arg)
if a:arg =~ "^ex"
return "examples/" . a:arg . ".subx"
else
return "apps/" . a:arg . ".subx"
endif
endfunction
" we often want to crib lines of machine code from other files
function! GrepSubX(regex)
" https://github.com/mtth/scratch.vim
Scratch!
silent exec "r !grep -h '".a:regex."' *.subx */*.subx"
endfunction
command! -nargs=1 G call GrepSubX(<q-args>)
if exists("&splitvertical")
command! -nargs=0 P hor split opcodes
else
command! -nargs=0 P split opcodes
endif
" useful for inspecting just the control flow in a trace
" see https://github.com/akkartik/mu/blob/master/subx/Readme.md#a-few-hints-for-debugging
" the '-a' is because traces can sometimes contain unprintable characters that bother grep
command! -nargs=0 L exec "%!grep -a label |grep -v clear-stream:loop"
" run test cursor around cursor
" if test fails, open trace in split window
" if test passes, just show output and wait for <CR>
" don't move cursor in original window
" this solution is unfortunate, but seems forced:
" can't put initial cursor movement inside function because we rely on <C-r><C-w> to grab word at cursor
" can't put final cursor movement out of function because that disables the wait for <CR> prompt; function must be final operation of map
" can't avoid the function because that disables the wait for <CR> prompt
" known issue:
" cursor on '#' causes error
noremap <Leader>t {j0:call RunTestMoveCursorAndMaybeOpenTrace("<C-r><C-w>")<CR>
function RunTestMoveCursorAndMaybeOpenTrace(arg)
exec "!run_one_test.sh ".expand("%")." ".a:arg
exec "normal \<C-o>"
if v:shell_error
noautocmd vertical split last_run
endif
endfunction

View File

@ -16,16 +16,10 @@ function! HighlightTangledFile()
syntax match traceAbsent /^-.*/
highlight traceAbsent ctermfg=darkred
syntax match tangleScenarioSetup /^\s*% .*/ | highlight link tangleScenarioSetup SpecialChar
highlight Special ctermfg=160
syntax match subxString %"[^"]*"% | highlight link subxString Constant
" match globals but not registers like 'EAX'
syntax match subxGlobal %\<[A-Z][a-z0-9_-]*\>% | highlight link subxGlobal SpecialChar
endfunction
augroup LocalVimrc
autocmd BufRead,BufNewFile *.cc call HighlightTangledFile()
autocmd BufRead,BufNewFile *.subx set ft=subx
autocmd BufRead,BufNewFile *.mu set ft=mu
augroup END
@ -35,44 +29,3 @@ augroup END
" starting vim or opening a buffer without a file name (ok to do nothing)
" opening a second file in a new or existing window (shouldn't mess up existing highlighting)
" reloading an existing file (shouldn't mess up existing highlighting)
" assumes CWD is subx/
command! -nargs=1 E call EditSubx("edit", <f-args>)
if exists("&splitvertical")
command! -nargs=1 S call EditSubx("vert split", <f-args>)
command! -nargs=1 H call EditSubx("hor split", <f-args>)
else
command! -nargs=1 S call EditSubx("vert split", <f-args>)
command! -nargs=1 H call EditSubx("split", <f-args>)
endif
function! EditSubx(cmd, arg)
exec "silent! " . a:cmd . " " . SubxPath(a:arg)
endfunction
function! SubxPath(arg)
if a:arg =~ "^ex"
return "examples/" . a:arg . ".subx"
else
return "apps/" . a:arg . ".subx"
endif
endfunction
" we often want to crib lines of machine code from other files
function! GrepSubX(regex)
" https://github.com/mtth/scratch.vim
Scratch!
silent exec "r !grep -h '".a:regex."' *.subx */*.subx"
endfunction
command! -nargs=1 G call GrepSubX(<q-args>)
if exists("&splitvertical")
command! -nargs=0 P hor split opcodes
else
command! -nargs=0 P split opcodes
endif
" useful for inspecting just the control flow in a trace
" see https://github.com/akkartik/mu/blob/master/subx/Readme.md#a-few-hints-for-debugging
" the '-a' is because traces can sometimes contain unprintable characters that bother grep
command! -nargs=0 L exec "%!grep -a label |grep -v clear-stream:loop"