diff options
Diffstat (limited to 'subx')
-rw-r--r-- | subx/010---vm.cc | 37 | ||||
-rw-r--r-- | subx/011run.cc | 31 | ||||
-rw-r--r-- | subx/012elf.cc | 28 | ||||
-rw-r--r-- | subx/013direct_addressing.cc | 308 | ||||
-rw-r--r-- | subx/014indirect_addressing.cc | 334 | ||||
-rw-r--r-- | subx/015immediate_addressing.cc | 526 | ||||
-rw-r--r-- | subx/017jump_disp8.cc | 54 | ||||
-rw-r--r-- | subx/019functions.cc | 19 | ||||
-rw-r--r-- | subx/020syscalls.cc | 19 | ||||
-rw-r--r-- | subx/031check_operands.cc | 4 | ||||
-rw-r--r-- | subx/034compute_segment_address.cc | 2 | ||||
-rw-r--r-- | subx/040---tests.cc | 3 | ||||
-rw-r--r-- | subx/050_write.subx | 11 | ||||
-rw-r--r-- | subx/057write.subx | 2 | ||||
-rw-r--r-- | subx/060read.subx | 2 | ||||
-rw-r--r-- | subx/062write-stream.subx | 22 | ||||
-rw-r--r-- | subx/066print-int.subx (renamed from subx/066print-byte.subx) | 122 | ||||
-rwxr-xr-x | subx/apps/assort | bin | 21961 -> 22287 bytes | |||
-rwxr-xr-x | subx/apps/crenshaw2-1 | bin | 19120 -> 19446 bytes | |||
-rwxr-xr-x | subx/apps/crenshaw2-1b | bin | 19679 -> 20005 bytes | |||
-rwxr-xr-x[-rw-r--r--] | subx/apps/dquotes | bin | 23219 -> 24275 bytes | |||
-rw-r--r-- | subx/apps/dquotes.subx | 231 | ||||
-rwxr-xr-x | subx/apps/factorial | bin | 18036 -> 18362 bytes | |||
-rwxr-xr-x | subx/apps/handle | bin | 18863 -> 19189 bytes | |||
-rwxr-xr-x | subx/apps/hex | bin | 22129 -> 22455 bytes | |||
-rwxr-xr-x | subx/apps/pack | bin | 36721 -> 37047 bytes | |||
-rwxr-xr-x | subx/build | 2 | ||||
-rwxr-xr-x | subx/test_layers | 6 |
28 files changed, 1499 insertions, 264 deletions
diff --git a/subx/010---vm.cc b/subx/010---vm.cc index 31e5608f..18f69035 100644 --- a/subx/010---vm.cc +++ b/subx/010---vm.cc @@ -62,7 +62,10 @@ put_new(Help, "registers", "- the sign flag (SF): usually set if an arithmetic result is negative, or\n" " reset if not.\n" "- the zero flag (ZF): usually set if a result is zero, or reset if not.\n" - "- the overflow flag (OF): usually set if an arithmetic result overflows.\n" + "- the carry flag (CF): usually set if an arithmetic result overflows by just one bit.\n" + " Useful for operating on unsigned numbers.\n" + "- the overflow flag (OF): usually set if an arithmetic result overflows by more than one bit.\n" + " Useful for operating on signed numbers.\n" "The flag bits are read by conditional jumps.\n" "\n" "For complete details on how different instructions update the flags, consult the IA-32\n" @@ -78,36 +81,10 @@ put_new(Help, "registers", // the subset of x86 flag registers we care about bool SF = false; // sign flag bool ZF = false; // zero flag +bool CF = false; // carry flag bool OF = false; // overflow flag :(before "End Reset") -SF = ZF = OF = false; - -//: how the flag registers are updated after each instruction - -:(before "End Includes") -// Combine 'arg1' and 'arg2' with arithmetic operation 'op' and store the -// result in 'arg1', then update flags. -// beware: no side-effects in args -#define BINARY_ARITHMETIC_OP(op, arg1, arg2) { \ - /* arg1 and arg2 must be signed */ \ - int64_t tmp = arg1 op arg2; \ - arg1 = arg1 op arg2; \ - trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << arg1 << end(); \ - SF = (arg1 < 0); \ - ZF = (arg1 == 0); \ - OF = (arg1 != tmp); \ -} - -// Combine 'arg1' and 'arg2' with bitwise operation 'op' and store the result -// in 'arg1', then update flags. -#define BINARY_BITWISE_OP(op, arg1, arg2) { \ - /* arg1 and arg2 must be unsigned */ \ - arg1 = arg1 op arg2; \ - trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << arg1 << end(); \ - SF = (arg1 >> 31); \ - ZF = (arg1 == 0); \ - OF = false; \ -} +SF = ZF = CF = OF = false; //:: simulated RAM @@ -374,7 +351,7 @@ void dump_registers() { if (i > 0) out << "; "; out << " " << i << ": " << std::hex << std::setw(8) << std::setfill('_') << Reg[i].u; } - out << " -- SF: " << SF << "; ZF: " << ZF << "; OF: " << OF; + out << " -- SF: " << SF << "; ZF: " << ZF << "; CF: " << CF << "; OF: " << OF; trace(Callstack_depth+1, "run") << out.str() << end(); } diff --git a/subx/011run.cc b/subx/011run.cc index 6b18ca06..236401b8 100644 --- a/subx/011run.cc +++ b/subx/011run.cc @@ -35,7 +35,7 @@ put_new(Help, "syntax", cerr << " syntax\n"; :(code) -void test_add_imm32_to_eax() { +void test_add_imm32_to_EAX() { // At the lowest level, SubX programs are a series of hex bytes, each // (variable-length) instruction on one line. run( @@ -65,19 +65,18 @@ void test_add_imm32_to_eax() { // opcode ModR/M SIB displacement immediate // instruction mod, reg, Reg/Mem bits scale, index, base // 1-3 bytes 0/1 byte 0/1 byte 0/1/2/4 bytes 0/1/2/4 bytes - " 05 . . . 0a 0b 0c 0d\n" // add 0x0d0c0b0a to EAX + " b8 . . . 0a 0b 0c 0d\n" // copy 0x0d0c0b0a to EAX // The periods are just to help the eye track long gaps between columns, // and are otherwise ignored. ); // This program, when run, causes the following events in the trace: CHECK_TRACE_CONTENTS( - "load: 0x00000001 -> 05\n" + "load: 0x00000001 -> b8\n" "load: 0x00000002 -> 0a\n" "load: 0x00000003 -> 0b\n" "load: 0x00000004 -> 0c\n" "load: 0x00000005 -> 0d\n" - "run: add imm32 0x0d0c0b0a to reg EAX\n" - "run: storing 0x0d0c0b0a\n" + "run: copy imm32 0x0d0c0b0a to EAX\n" ); } @@ -350,18 +349,30 @@ void parse_and_load(const string& text_bytes) { //:: run :(before "End Initialize Op Names") -put_new(Name, "05", "add imm32 to EAX (add)"); +put_new(Name, "b8", "copy imm32 to EAX (mov)"); //: our first opcode + :(before "End Single-Byte Opcodes") -case 0x05: { // add imm32 to EAX - int32_t arg2 = next32(); - trace(Callstack_depth+1, "run") << "add imm32 0x" << HEXWORD << arg2 << " to reg EAX" << end(); - BINARY_ARITHMETIC_OP(+, Reg[EAX].i, arg2); +case 0xb8: { // copy imm32 to EAX + const int32_t src = next32(); + trace(Callstack_depth+1, "run") << "copy imm32 0x" << HEXWORD << src << " to EAX" << end(); + Reg[EAX].i = src; break; } :(code) +void test_copy_imm32_to_EAX() { + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " b8 0a 0b 0c 0d \n" // copy 0x0d0c0b0a to EAX + ); + CHECK_TRACE_CONTENTS( + "run: copy imm32 0x0d0c0b0a to EAX\n" + ); +} + // read a 32-bit int in little-endian order from the instruction stream int32_t next32() { int32_t result = read_mem_i32(EIP); diff --git a/subx/012elf.cc b/subx/012elf.cc index d0a3fbd2..0ae0b108 100644 --- a/subx/012elf.cc +++ b/subx/012elf.cc @@ -90,6 +90,13 @@ void load_elf_contents(uint8_t* elf_contents, size_t size, int argc, char* argv[ void push(uint32_t val) { Reg[ESP].u -= 4; + if (Reg[ESP].u < STACK_SEGMENT) { + raise << "The stack overflowed its segment. " + << "Maybe SPACE_FOR_SEGMENT should be larger? " + << "Or you need to carve out an exception for the stack segment " + << "to be larger.\n" << end(); + exit(1); + } trace(Callstack_depth+1, "run") << "decrementing ESP to 0x" << HEXWORD << Reg[ESP].u << end(); trace(Callstack_depth+1, "run") << "pushing value 0x" << HEXWORD << val << end(); write_mem_u32(Reg[ESP].u, val); @@ -134,18 +141,17 @@ void load_segment_from_program_header(uint8_t* elf_contents, int segment_index, // code: 0x09000000 -> 0x09ffffff (specified in ELF binary) // data: 0x0a000000 -> 0x0affffff (specified in ELF binary) // --- heap gets mmap'd somewhere here --- -// stack: 0x7dffffff -> 0x7d000000 (downward; not in ELF binary) -// argv hack: 0x7f000000 -> 0x7fffffff (not in ELF binary) +// stack: 0xbdffffff -> 0xbd000000 (downward; not in ELF binary) +// argv hack: 0xbf000000 -> 0xbfffffff (not in ELF binary) // -// For now we avoid addresses with the most significant bit set; SubX doesn't -// support unsigned comparison yet (https://github.com/akkartik/mu/issues/30) -// Once we do, we can go up to 0xc0000000; higher addresses are reserved for -// the Linux kernel. -const int CODE_SEGMENT = 0x09000000; -const int DATA_SEGMENT = 0x0a000000; -const int STACK_SEGMENT = 0x7d000000; -const int AFTER_STACK = 0x7e000000; -const int ARGV_DATA_SEGMENT = 0x7f000000; +// Addresses above 0xc0000000 are reserved for the Linux kernel. +const uint32_t CODE_SEGMENT = 0x09000000; +const uint32_t DATA_SEGMENT = 0x0a000000; +const uint32_t START_HEAP = 0x0b000000; +const uint32_t END_HEAP = 0xbd000000; +const uint32_t STACK_SEGMENT = 0xbd000000; +const uint32_t AFTER_STACK = 0xbe000000; +const uint32_t ARGV_DATA_SEGMENT = 0xbf000000; // When updating the above memory map, don't forget to update `mmap`'s // implementation in the 'syscalls' layer. :(before "End Dump Info for Instruction") diff --git a/subx/013direct_addressing.cc b/subx/013direct_addressing.cc index c2dfa911..2914e0dd 100644 --- a/subx/013direct_addressing.cc +++ b/subx/013direct_addressing.cc @@ -25,12 +25,75 @@ case 0x01: { // add r32 to r/m32 uint8_t modrm = next(); uint8_t arg2 = (modrm>>3)&0x7; trace(Callstack_depth+1, "run") << "add " << rname(arg2) << " to r/m32" << end(); - int32_t* arg1 = effective_address(modrm); - BINARY_ARITHMETIC_OP(+, *arg1, Reg[arg2].i); + int32_t* signed_arg1 = effective_address(modrm); + int32_t signed_result = *signed_arg1 + Reg[arg2].i; + SF = (signed_result < 0); + ZF = (signed_result == 0); + int64_t signed_full_result = static_cast<int64_t>(*signed_arg1) + Reg[arg2].i; + OF = (signed_result != signed_full_result); + // set CF + uint32_t unsigned_arg1 = static_cast<uint32_t>(*signed_arg1); + uint32_t unsigned_result = unsigned_arg1 + Reg[arg2].u; + uint64_t unsigned_full_result = static_cast<uint64_t>(unsigned_arg1) + Reg[arg2].u; + CF = (unsigned_result != unsigned_full_result); + trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); + *signed_arg1 = signed_result; + trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); break; } :(code) +void test_add_r32_to_r32_signed_overflow() { + Reg[EAX].i = 0x7fffffff; // largest positive signed integer + Reg[EBX].i = 1; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 01 d8 \n" // add EBX to EAX + // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) + ); + CHECK_TRACE_CONTENTS( + "run: add EBX to r/m32\n" + "run: r/m32 is EAX\n" + "run: SF=1; ZF=0; CF=0; OF=1\n" + "run: storing 0x80000000\n" + ); +} + +void test_add_r32_to_r32_unsigned_overflow() { + Reg[EAX].u = 0xffffffff; // largest unsigned number + Reg[EBX].u = 1; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 01 d8 \n" // add EBX to EAX + // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) + ); + CHECK_TRACE_CONTENTS( + "run: add EBX to r/m32\n" + "run: r/m32 is EAX\n" + "run: SF=0; ZF=1; CF=1; OF=0\n" + "run: storing 0x00000000\n" + ); +} + +void test_add_r32_to_r32_unsigned_and_signed_overflow() { + Reg[EAX].u = Reg[EBX].u = 0x80000000; // smallest negative signed integer + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 01 d8 \n" // add EBX to EAX + // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) + ); + CHECK_TRACE_CONTENTS( + "run: add EBX to r/m32\n" + "run: r/m32 is EAX\n" + "run: SF=0; ZF=1; CF=1; OF=1\n" + "run: storing 0x00000000\n" + ); +} + +:(code) // Implement tables 2-2 and 2-3 in the Intel manual, Volume 2. // We return a pointer so that instructions can write to multiple bytes in // 'Mem' at once. @@ -111,18 +174,82 @@ case 0x29: { // subtract r32 from r/m32 const uint8_t modrm = next(); const uint8_t arg2 = (modrm>>3)&0x7; trace(Callstack_depth+1, "run") << "subtract " << rname(arg2) << " from r/m32" << end(); - int32_t* arg1 = effective_address(modrm); - BINARY_ARITHMETIC_OP(-, *arg1, Reg[arg2].i); + int32_t* signed_arg1 = effective_address(modrm); + int32_t signed_result = *signed_arg1 - Reg[arg2].i; + SF = (signed_result < 0); + ZF = (signed_result == 0); + int64_t signed_full_result = static_cast<int64_t>(*signed_arg1) - Reg[arg2].i; + OF = (signed_result != signed_full_result); + // set CF + uint32_t unsigned_arg1 = static_cast<uint32_t>(*signed_arg1); + uint32_t unsigned_result = unsigned_arg1 - Reg[arg2].u; + uint64_t unsigned_full_result = static_cast<uint64_t>(unsigned_arg1) - Reg[arg2].u; + CF = (unsigned_result != unsigned_full_result); + trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); + *signed_arg1 = signed_result; + trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); break; } +:(code) +void test_subtract_r32_from_r32_signed_overflow() { + Reg[EAX].i = 0x80000000; // smallest negative signed integer + Reg[EBX].i = 0x7fffffff; // largest positive signed integer + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 29 d8 \n" // subtract EBX from EAX + // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) + ); + CHECK_TRACE_CONTENTS( + "run: subtract EBX from r/m32\n" + "run: r/m32 is EAX\n" + "run: SF=0; ZF=0; CF=0; OF=1\n" + "run: storing 0x00000001\n" + ); +} + +void test_subtract_r32_from_r32_unsigned_overflow() { + Reg[EAX].i = 0; + Reg[EBX].i = 1; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 29 d8 \n" // subtract EBX from EAX + // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) + ); + CHECK_TRACE_CONTENTS( + "run: subtract EBX from r/m32\n" + "run: r/m32 is EAX\n" + "run: SF=1; ZF=0; CF=1; OF=0\n" + "run: storing 0xffffffff\n" + ); +} + +void test_subtract_r32_from_r32_signed_and_unsigned_overflow() { + Reg[EAX].i = 0; + Reg[EBX].i = 0x80000000; // smallest negative signed integer + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 29 d8 \n" // subtract EBX from EAX + // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) + ); + CHECK_TRACE_CONTENTS( + "run: subtract EBX from r/m32\n" + "run: r/m32 is EAX\n" + "run: SF=1; ZF=0; CF=1; OF=1\n" + "run: storing 0x80000000\n" + ); +} + //:: multiply :(before "End Initialize Op Names") put_new(Name, "f7", "negate/multiply/divide rm32 (with EAX and EDX if necessary) depending on subop (neg/mul/idiv)"); :(code) -void test_multiply_eax_by_r32() { +void test_multiply_EAX_by_r32() { Reg[EAX].i = 4; Reg[ECX].i = 3; run( @@ -148,7 +275,7 @@ case 0xf7: { switch (subop) { case 4: { // mul unsigned EAX by r/m32 trace(Callstack_depth+1, "run") << "subop: multiply EAX by r/m32" << end(); - const uint64_t result = Reg[EAX].u * static_cast<uint32_t>(*arg1); + const uint64_t result = static_cast<uint64_t>(Reg[EAX].u) * static_cast<uint32_t>(*arg1); Reg[EAX].u = result & 0xffffffff; Reg[EDX].u = result >> 32; OF = (Reg[EDX].u != 0); @@ -179,19 +306,27 @@ void test_multiply_r32_into_r32() { // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) ); CHECK_TRACE_CONTENTS( - "run: multiply r/m32 into EBX\n" + "run: multiply EBX by r/m32\n" "run: r/m32 is EAX\n" "run: storing 0x00000008\n" ); } :(before "End Two-Byte Opcodes Starting With 0f") -case 0xaf: { // multiply r32 into r/m32 +case 0xaf: { // multiply r32 by r/m32 const uint8_t modrm = next(); - const uint8_t arg2 = (modrm>>3)&0x7; - trace(Callstack_depth+1, "run") << "multiply r/m32 into " << rname(arg2) << end(); - const int32_t* arg1 = effective_address(modrm); - BINARY_ARITHMETIC_OP(*, Reg[arg2].i, *arg1); + const uint8_t arg1 = (modrm>>3)&0x7; + trace(Callstack_depth+1, "run") << "multiply " << rname(arg1) << " by r/m32" << end(); + const int32_t* arg2 = effective_address(modrm); + int32_t result = Reg[arg1].i * (*arg2); + SF = (Reg[arg1].i < 0); + ZF = (Reg[arg1].i == 0); + int64_t full_result = static_cast<int64_t>(Reg[arg1].i) * (*arg2); + OF = (Reg[arg1].i != full_result); + CF = OF; + trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); + Reg[arg1].i = result; + trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[arg1].i << end(); break; } @@ -253,7 +388,7 @@ void test_negate_can_overflow() { //:: divide with remainder -void test_divide_eax_by_rm32() { +void test_divide_EAX_by_rm32() { Reg[EAX].u = 7; Reg[EDX].u = 0; Reg[ECX].i = 3; @@ -280,13 +415,14 @@ case 7: { // divide EDX:EAX by r/m32, storing quotient in EAX and remainder in assert(divisor != 0); Reg[EAX].i = dividend/divisor; // quotient Reg[EDX].i = dividend%divisor; // remainder + // flag state undefined trace(Callstack_depth+1, "run") << "quotient: 0x" << HEXWORD << Reg[EAX].i << end(); trace(Callstack_depth+1, "run") << "remainder: 0x" << HEXWORD << Reg[EDX].i << end(); break; } :(code) -void test_divide_eax_by_negative_rm32() { +void test_divide_EAX_by_negative_rm32() { Reg[EAX].u = 7; Reg[EDX].u = 0; Reg[ECX].i = -3; @@ -305,7 +441,7 @@ void test_divide_eax_by_negative_rm32() { ); } -void test_divide_negative_eax_by_rm32() { +void test_divide_negative_EAX_by_rm32() { Reg[EAX].i = -7; Reg[EDX].i = -1; // sign extend Reg[ECX].i = 3; @@ -324,7 +460,7 @@ void test_divide_negative_eax_by_rm32() { ); } -void test_divide_negative_edx_eax_by_rm32() { +void test_divide_negative_EDX_EAX_by_rm32() { Reg[EAX].i = 0; // lower 32 bits are clear Reg[EDX].i = -7; Reg[ECX].i = 0x40000000; // 2^30 (largest positive power of 2) @@ -569,8 +705,16 @@ case 0x21: { // and r32 with r/m32 const uint8_t modrm = next(); const uint8_t arg2 = (modrm>>3)&0x7; trace(Callstack_depth+1, "run") << "and " << rname(arg2) << " with r/m32" << end(); - int32_t* arg1 = effective_address(modrm); - BINARY_BITWISE_OP(&, *arg1, Reg[arg2].u); + // bitwise ops technically operate on unsigned numbers, but it makes no + // difference + int32_t* signed_arg1 = effective_address(modrm); + *signed_arg1 &= Reg[arg2].i; + trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); + SF = (*signed_arg1 >> 31); + ZF = (*signed_arg1 == 0); + CF = false; + OF = false; + trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); break; } @@ -601,8 +745,16 @@ case 0x09: { // or r32 with r/m32 const uint8_t modrm = next(); const uint8_t arg2 = (modrm>>3)&0x7; trace(Callstack_depth+1, "run") << "or " << rname(arg2) << " with r/m32" << end(); - int32_t* arg1 = effective_address(modrm); - BINARY_BITWISE_OP(|, *arg1, Reg[arg2].u); + // bitwise ops technically operate on unsigned numbers, but it makes no + // difference + int32_t* signed_arg1 = effective_address(modrm); + *signed_arg1 |= Reg[arg2].i; + trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); + SF = (*signed_arg1 >> 31); + ZF = (*signed_arg1 == 0); + CF = false; + OF = false; + trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); break; } @@ -633,8 +785,16 @@ case 0x31: { // xor r32 with r/m32 const uint8_t modrm = next(); const uint8_t arg2 = (modrm>>3)&0x7; trace(Callstack_depth+1, "run") << "xor " << rname(arg2) << " with r/m32" << end(); - int32_t* arg1 = effective_address(modrm); - BINARY_BITWISE_OP(^, *arg1, Reg[arg2].u); + // bitwise ops technically operate on unsigned numbers, but it makes no + // difference + int32_t* signed_arg1 = effective_address(modrm); + *signed_arg1 ^= Reg[arg2].i; + trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); + SF = (*signed_arg1 >> 31); + ZF = (*signed_arg1 == 0); + CF = false; + OF = false; + trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); break; } @@ -680,13 +840,13 @@ void test_compare_r32_with_r32_greater() { run( "== 0x1\n" // code segment // op ModR/M SIB displacement immediate - " 39 d8 \n" // compare EBX with EAX + " 39 d8 \n" // compare EAX with EBX // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) ); CHECK_TRACE_CONTENTS( - "run: compare EBX with r/m32\n" + "run: compare r/m32 with EBX\n" "run: r/m32 is EAX\n" - "run: SF=0; ZF=0; OF=0\n" + "run: SF=0; ZF=0; CF=0; OF=0\n" ); } @@ -694,32 +854,84 @@ void test_compare_r32_with_r32_greater() { case 0x39: { // set SF if r/m32 < r32 const uint8_t modrm = next(); const uint8_t reg2 = (modrm>>3)&0x7; - trace(Callstack_depth+1, "run") << "compare " << rname(reg2) << " with r/m32" << end(); - const int32_t* arg1 = effective_address(modrm); - const int32_t arg2 = Reg[reg2].i; - const int32_t tmp1 = *arg1 - arg2; - SF = (tmp1 < 0); - ZF = (tmp1 == 0); - const int64_t tmp2 = *arg1 - arg2; - OF = (tmp1 != tmp2); - trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; OF=" << OF << end(); + trace(Callstack_depth+1, "run") << "compare r/m32 with " << rname(reg2) << end(); + const int32_t* signed_arg1 = effective_address(modrm); + const int32_t signed_difference = *signed_arg1 - Reg[reg2].i; + SF = (signed_difference < 0); + ZF = (signed_difference == 0); + const int64_t signed_full_difference = static_cast<int64_t>(*signed_arg1) - Reg[reg2].i; + OF = (signed_difference != signed_full_difference); + // set CF + const uint32_t unsigned_arg1 = static_cast<uint32_t>(*signed_arg1); + const uint32_t unsigned_difference = unsigned_arg1 - Reg[reg2].u; + const uint64_t unsigned_full_difference = static_cast<uint64_t>(unsigned_arg1) - Reg[reg2].u; + CF = (unsigned_difference != unsigned_full_difference); + trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); break; } :(code) -void test_compare_r32_with_r32_lesser() { +void test_compare_r32_with_r32_lesser_unsigned_and_signed() { Reg[EAX].i = 0x0a0b0c07; Reg[EBX].i = 0x0a0b0c0d; run( "== 0x1\n" // code segment // op ModR/M SIB displacement immediate - " 39 d8 \n" // compare EBX with EAX + " 39 d8 \n" // compare EAX with EBX + // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) + ); + CHECK_TRACE_CONTENTS( + "run: compare r/m32 with EBX\n" + "run: r/m32 is EAX\n" + "run: SF=1; ZF=0; CF=1; OF=0\n" + ); +} + +void test_compare_r32_with_r32_lesser_unsigned_and_signed_due_to_overflow() { + Reg[EAX].i = 0x7fffffff; // largest positive signed integer + Reg[EBX].i = 0x80000000; // smallest negative signed integer + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 39 d8 \n" // compare EAX with EBX + // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) + ); + CHECK_TRACE_CONTENTS( + "run: compare r/m32 with EBX\n" + "run: r/m32 is EAX\n" + "run: SF=1; ZF=0; CF=1; OF=1\n" + ); +} + +void test_compare_r32_with_r32_lesser_signed() { + Reg[EAX].i = 0xffffffff; // -1 + Reg[EBX].i = 0x00000001; // 1 + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 39 d8 \n" // compare EAX with EBX + // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) + ); + CHECK_TRACE_CONTENTS( + "run: compare r/m32 with EBX\n" + "run: r/m32 is EAX\n" + "run: SF=1; ZF=0; CF=0; OF=0\n" + ); +} + +void test_compare_r32_with_r32_lesser_unsigned() { + Reg[EAX].i = 0x00000001; // 1 + Reg[EBX].i = 0xffffffff; // -1 + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 39 d8 \n" // compare EAX with EBX // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) ); CHECK_TRACE_CONTENTS( - "run: compare EBX with r/m32\n" + "run: compare r/m32 with EBX\n" "run: r/m32 is EAX\n" - "run: SF=1; ZF=0; OF=0\n" + "run: SF=0; ZF=0; CF=1; OF=0\n" ); } @@ -729,13 +941,13 @@ void test_compare_r32_with_r32_equal() { run( "== 0x1\n" // code segment // op ModR/M SIB displacement immediate - " 39 d8 \n" // compare EBX with EAX + " 39 d8 \n" // compare EAX and EBX // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) ); CHECK_TRACE_CONTENTS( - "run: compare EBX with r/m32\n" + "run: compare r/m32 with EBX\n" "run: r/m32 is EAX\n" - "run: SF=0; ZF=1; OF=0\n" + "run: SF=0; ZF=1; CF=0; OF=0\n" ); } @@ -971,7 +1183,8 @@ put_new(Name, "57", "push EDI to stack (push)"); :(code) void test_push_r32() { - Reg[ESP].u = 0x64; + Mem.push_back(vma(0xbd000000)); // manually allocate memory + Reg[ESP].u = 0xbd000008; Reg[EBX].i = 0x0000000a; run( "== 0x1\n" // code segment @@ -980,7 +1193,7 @@ void test_push_r32() { ); CHECK_TRACE_CONTENTS( "run: push EBX\n" - "run: decrementing ESP to 0x00000060\n" + "run: decrementing ESP to 0xbd000004\n" "run: pushing value 0x0000000a\n" ); } @@ -1015,9 +1228,9 @@ put_new(Name, "5f", "pop top of stack to EDI (pop)"); :(code) void test_pop_r32() { - Reg[ESP].u = 0x02000000; - Mem.push_back(vma(0x02000000)); // manually allocate memory - write_mem_i32(0x02000000, 0x0000000a); // ..before this write + Mem.push_back(vma(0xbd000000)); // manually allocate memory + Reg[ESP].u = 0xbd000008; + write_mem_i32(0xbd000008, 0x0000000a); // ..before this write run( "== 0x1\n" // code segment // op ModR/M SIB displacement immediate @@ -1028,7 +1241,7 @@ void test_pop_r32() { CHECK_TRACE_CONTENTS( "run: pop into EBX\n" "run: popping value 0x0000000a\n" - "run: incrementing ESP to 0x02000004\n" + "run: incrementing ESP to 0xbd00000c\n" ); } @@ -1054,5 +1267,6 @@ uint32_t pop() { trace(Callstack_depth+1, "run") << "popping value 0x" << HEXWORD << result << end(); Reg[ESP].u += 4; trace(Callstack_depth+1, "run") << "incrementing ESP to 0x" << HEXWORD << Reg[ESP].u << end(); + assert(Reg[ESP].u < AFTER_STACK); return result; } diff --git a/subx/014indirect_addressing.cc b/subx/014indirect_addressing.cc index f591f0cf..a281921f 100644 --- a/subx/014indirect_addressing.cc +++ b/subx/014indirect_addressing.cc @@ -59,11 +59,84 @@ case 0x03: { // add r/m32 to r32 const uint8_t modrm = next(); const uint8_t arg1 = (modrm>>3)&0x7; trace(Callstack_depth+1, "run") << "add r/m32 to " << rname(arg1) << end(); - const int32_t* arg2 = effective_address(modrm); - BINARY_ARITHMETIC_OP(+, Reg[arg1].i, *arg2); + const int32_t* signed_arg2 = effective_address(modrm); + int32_t signed_result = Reg[arg1].i + *signed_arg2; + SF = (signed_result < 0); + ZF = (signed_result == 0); + int64_t signed_full_result = static_cast<int64_t>(Reg[arg1].i) + *signed_arg2; + OF = (signed_result != signed_full_result); + // set CF + uint32_t unsigned_arg2 = static_cast<uint32_t>(*signed_arg2); + uint32_t unsigned_result = Reg[arg1].u + unsigned_arg2; + uint64_t unsigned_full_result = static_cast<uint64_t>(Reg[arg1].u) + unsigned_arg2; + CF = (unsigned_result != unsigned_full_result); + trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); + Reg[arg1].i = signed_result; + trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[arg1].i << end(); break; } +:(code) +void test_add_mem_at_r32_to_r32_signed_overflow() { + Reg[EAX].i = 0x2000; + Reg[EBX].i = 0x7fffffff; // largest positive signed integer + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 03 18 \n" // add *EAX to EBX + // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) + "== 0x2000\n" // data segment + "01 00 00 00\n" // 1 + ); + CHECK_TRACE_CONTENTS( + "run: add r/m32 to EBX\n" + "run: effective address is 0x00002000 (EAX)\n" + "run: effective address contains 1\n" + "run: SF=1; ZF=0; CF=0; OF=1\n" + "run: storing 0x80000000\n" + ); +} + +void test_add_mem_at_r32_to_r32_unsigned_overflow() { + Reg[EAX].u = 0x2000; + Reg[EBX].u = 0xffffffff; // largest unsigned number + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 03 18 \n" // add *EAX to EBX + // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) + "== 0x2000\n" // data segment + "01 00 00 00\n" + ); + CHECK_TRACE_CONTENTS( + "run: add r/m32 to EBX\n" + "run: effective address is 0x00002000 (EAX)\n" + "run: effective address contains 1\n" + "run: SF=0; ZF=1; CF=1; OF=0\n" + "run: storing 0x00000000\n" + ); +} + +void test_add_mem_at_r32_to_r32_unsigned_and_signed_overflow() { + Reg[EAX].u = 0x2000; + Reg[EBX].u = 0x80000000; // smallest negative signed integer + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 03 18 \n" // add *EAX to EBX + // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) + "== 0x2000\n" // data segment + "00 00 00 80\n" // smallest negative signed integer + ); + CHECK_TRACE_CONTENTS( + "run: add r/m32 to EBX\n" + "run: effective address is 0x00002000 (EAX)\n" + "run: effective address contains 80000000\n" + "run: SF=0; ZF=1; CF=1; OF=1\n" + "run: storing 0x00000000\n" + ); +} + //:: subtract :(code) @@ -114,11 +187,84 @@ case 0x2b: { // subtract r/m32 from r32 const uint8_t modrm = next(); const uint8_t arg1 = (modrm>>3)&0x7; trace(Callstack_depth+1, "run") << "subtract r/m32 from " << rname(arg1) << end(); - const int32_t* arg2 = effective_address(modrm); - BINARY_ARITHMETIC_OP(-, Reg[arg1].i, *arg2); + const int32_t* signed_arg2 = effective_address(modrm); + const int32_t signed_result = Reg[arg1].i - *signed_arg2; + SF = (signed_result < 0); + ZF = (signed_result == 0); + int64_t signed_full_result = static_cast<int64_t>(Reg[arg1].i) - *signed_arg2; + OF = (signed_result != signed_full_result); + // set CF + uint32_t unsigned_arg2 = static_cast<uint32_t>(*signed_arg2); + uint32_t unsigned_result = Reg[arg1].u - unsigned_arg2; + uint64_t unsigned_full_result = static_cast<uint64_t>(Reg[arg1].u) - unsigned_arg2; + CF = (unsigned_result != unsigned_full_result); + trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); + Reg[arg1].i = signed_result; + trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[arg1].i << end(); break; } +:(code) +void test_subtract_mem_at_r32_from_r32_signed_overflow() { + Reg[EAX].i = 0x2000; + Reg[EBX].i = 0x80000000; // smallest negative signed integer + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 2b 18 \n" // subtract *EAX from EBX + // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) + "== 0x2000\n" // data segment + "ff ff ff 7f\n" // largest positive signed integer + ); + CHECK_TRACE_CONTENTS( + "run: subtract r/m32 from EBX\n" + "run: effective address is 0x00002000 (EAX)\n" + "run: effective address contains 7fffffff\n" + "run: SF=0; ZF=0; CF=0; OF=1\n" + "run: storing 0x00000001\n" + ); +} + +void test_subtract_mem_at_r32_from_r32_unsigned_overflow() { + Reg[EAX].i = 0x2000; + Reg[EBX].i = 0; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 2b 18 \n" // subtract *EAX from EBX + // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) + "== 0x2000\n" // data segment + "01 00 00 00\n" // 1 + ); + CHECK_TRACE_CONTENTS( + "run: subtract r/m32 from EBX\n" + "run: effective address is 0x00002000 (EAX)\n" + "run: effective address contains 1\n" + "run: SF=1; ZF=0; CF=1; OF=0\n" + "run: storing 0xffffffff\n" + ); +} + +void test_subtract_mem_at_r32_from_r32_signed_and_unsigned_overflow() { + Reg[EAX].i = 0x2000; + Reg[EBX].i = 0; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 2b 18 \n" // subtract *EAX from EBX + // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) + "== 0x2000\n" // data segment + "00 00 00 80\n" // smallest negative signed integer + ); + CHECK_TRACE_CONTENTS( + "run: subtract r/m32 from EBX\n" + "run: effective address is 0x00002000 (EAX)\n" + "run: effective address contains 80000000\n" + "run: SF=1; ZF=0; CF=1; OF=1\n" + "run: storing 0x80000000\n" + ); +} + //:: and :(code) void test_and_r32_with_mem_at_r32() { @@ -168,8 +314,16 @@ case 0x23: { // and r/m32 with r32 const uint8_t modrm = next(); const uint8_t arg1 = (modrm>>3)&0x7; trace(Callstack_depth+1, "run") << "and r/m32 with " << rname(arg1) << end(); - const int32_t* arg2 = effective_address(modrm); - BINARY_BITWISE_OP(&, Reg[arg1].u, *arg2); + // bitwise ops technically operate on unsigned numbers, but it makes no + // difference + const int32_t* signed_arg2 = effective_address(modrm); + Reg[arg1].i &= *signed_arg2; + trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[arg1].i << end(); + SF = (Reg[arg1].i >> 31); + ZF = (Reg[arg1].i == 0); + CF = false; + OF = false; + trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); break; } @@ -223,8 +377,16 @@ case 0x0b: { // or r/m32 with r32 const uint8_t modrm = next(); const uint8_t arg1 = (modrm>>3)&0x7; trace(Callstack_depth+1, "run") << "or r/m32 with " << rname(arg1) << end(); - const int32_t* arg2 = effective_address(modrm); - BINARY_BITWISE_OP(|, Reg[arg1].u, *arg2); + // bitwise ops technically operate on unsigned numbers, but it makes no + // difference + const int32_t* signed_arg2 = effective_address(modrm); + Reg[arg1].i |= *signed_arg2; + trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[arg1].i << end(); + SF = (Reg[arg1].i >> 31); + ZF = (Reg[arg1].i == 0); + CF = false; + OF = false; + trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); break; } @@ -277,8 +439,16 @@ case 0x33: { // xor r/m32 with r32 const uint8_t modrm = next(); const uint8_t arg1 = (modrm>>3)&0x7; trace(Callstack_depth+1, "run") << "xor r/m32 with " << rname(arg1) << end(); - const int32_t* arg2 = effective_address(modrm); - BINARY_BITWISE_OP(|, Reg[arg1].u, *arg2); + // bitwise ops technically operate on unsigned numbers, but it makes no + // difference + const int32_t* signed_arg2 = effective_address(modrm); + Reg[arg1].i |= *signed_arg2; + trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[arg1].i << end(); + SF = (Reg[arg1].i >> 31); + ZF = (Reg[arg1].i == 0); + CF = false; + OF = false; + trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); break; } @@ -312,15 +482,15 @@ void test_compare_mem_at_r32_with_r32_greater() { run( "== 0x1\n" // code segment // op ModR/M SIB displacement immediate - " 39 18 \n" // compare EBX with *EAX + " 39 18 \n" // compare *EAX with EBX // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) "== 0x2000\n" // data segment "0d 0c 0b 0a\n" // 0x0a0b0c0d ); CHECK_TRACE_CONTENTS( - "run: compare EBX with r/m32\n" + "run: compare r/m32 with EBX\n" "run: effective address is 0x00002000 (EAX)\n" - "run: SF=0; ZF=0; OF=0\n" + "run: SF=0; ZF=0; CF=0; OF=0\n" ); } @@ -331,15 +501,15 @@ void test_compare_mem_at_r32_with_r32_lesser() { run( "== 0x1\n" // code segment // op ModR/M SIB displacement immediate - " 39 18 \n" // compare EBX with *EAX + " 39 18 \n" // compare *EAX with EBX // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) "== 0x2000\n" // data segment "07 0c 0b 0a\n" // 0x0a0b0c0d ); CHECK_TRACE_CONTENTS( - "run: compare EBX with r/m32\n" + "run: compare r/m32 with EBX\n" "run: effective address is 0x00002000 (EAX)\n" - "run: SF=1; ZF=0; OF=0\n" + "run: SF=1; ZF=0; CF=1; OF=0\n" ); } @@ -350,15 +520,15 @@ void test_compare_mem_at_r32_with_r32_equal() { run( "== 0x1\n" // code segment // op ModR/M SIB displacement immediate - " 39 18 \n" // compare EBX with *EAX + " 39 18 \n" // compare *EAX and EBX // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) "== 0x2000\n" // data segment "0d 0c 0b 0a\n" // 0x0a0b0c0d ); CHECK_TRACE_CONTENTS( - "run: compare EBX with r/m32\n" + "run: compare r/m32 with EBX\n" "run: effective address is 0x00002000 (EAX)\n" - "run: SF=0; ZF=1; OF=0\n" + "run: SF=0; ZF=1; CF=0; OF=0\n" ); } @@ -374,15 +544,15 @@ void test_compare_r32_with_mem_at_r32_greater() { run( "== 0x1\n" // code segment // op ModR/M SIB displacement immediate - " 3b 18 \n" // compare *EAX with EBX + " 3b 18 \n" // compare EBX with *EAX // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) "== 0x2000\n" // data segment - "07 0c 0b 0a\n" // 0x0a0b0c0d + "07 0c 0b 0a\n" // 0x0a0b0c07 ); CHECK_TRACE_CONTENTS( - "run: compare r/m32 with EBX\n" + "run: compare EBX with r/m32\n" "run: effective address is 0x00002000 (EAX)\n" - "run: SF=0; ZF=0; OF=0\n" + "run: SF=0; ZF=0; CF=0; OF=0\n" ); } @@ -390,59 +560,118 @@ void test_compare_r32_with_mem_at_r32_greater() { case 0x3b: { // set SF if r32 < r/m32 const uint8_t modrm = next(); const uint8_t reg1 = (modrm>>3)&0x7; - trace(Callstack_depth+1, "run") << "compare r/m32 with " << rname(reg1) << end(); - const int32_t arg1 = Reg[reg1].i; - const int32_t* arg2 = effective_address(modrm); - const int32_t tmp1 = arg1 - *arg2; - SF = (tmp1 < 0); - ZF = (tmp1 == 0); - int64_t tmp2 = arg1 - *arg2; - OF = (tmp1 != tmp2); - trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; OF=" << OF << end(); + trace(Callstack_depth+1, "run") << "compare " << rname(reg1) << " with r/m32" << end(); + const int32_t* signed_arg2 = effective_address(modrm); + const int32_t signed_difference = Reg[reg1].i - *signed_arg2; + SF = (signed_difference < 0); + ZF = (signed_difference == 0); + int64_t full_signed_difference = static_cast<int64_t>(Reg[reg1].i) - *signed_arg2; + OF = (signed_difference != full_signed_difference); + const uint32_t unsigned_arg2 = static_cast<uint32_t>(*signed_arg2); + const uint32_t unsigned_difference = Reg[reg1].u - unsigned_arg2; + const uint64_t full_unsigned_difference = static_cast<uint64_t>(Reg[reg1].u) - unsigned_arg2; + CF = (unsigned_difference != full_unsigned_difference); + trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); break; } :(code) -void test_compare_r32_with_mem_at_r32_lesser() { +void test_compare_r32_with_mem_at_r32_lesser_unsigned_and_signed() { Reg[EAX].i = 0x2000; Reg[EBX].i = 0x0a0b0c07; run( "== 0x1\n" // code segment // op ModR/M SIB displacement immediate - " 3b 18 \n" // compare *EAX with EBX - // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) + " 3b 18 \n" // compare EBX with *EAX + // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) "== 0x2000\n" // data segment "0d 0c 0b 0a\n" // 0x0a0b0c0d ); CHECK_TRACE_CONTENTS( - "run: compare r/m32 with EBX\n" + "run: compare EBX with r/m32\n" "run: effective address is 0x00002000 (EAX)\n" - "run: SF=1; ZF=0; OF=0\n" + "run: effective address contains a0b0c0d\n" + "run: SF=1; ZF=0; CF=1; OF=0\n" + ); +} + +void test_compare_r32_with_mem_at_r32_lesser_unsigned_and_signed_due_to_overflow() { + Reg[EAX].i = 0x2000; + Reg[EBX].i = 0x7fffffff; // largest positive signed integer + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 3b 18 \n" // compare EBX with *EAX + // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) + "== 0x2000\n" // data segment + "00 00 00 80\n" // smallest negative signed integer + ); + CHECK_TRACE_CONTENTS( + "run: compare EBX with r/m32\n" + "run: effective address is 0x00002000 (EAX)\n" + "run: effective address contains 80000000\n" + "run: SF=1; ZF=0; CF=1; OF=1\n" + ); +} + +void test_compare_r32_with_mem_at_r32_lesser_signed() { + Reg[EAX].i = 0x2000; + Reg[EBX].i = 0xffffffff; // -1 + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 3b 18 \n" // compare EBX with *EAX + // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) + "== 0x2000\n" // data segment + "01 00 00 00\n" // 1 + ); + CHECK_TRACE_CONTENTS( + "run: compare EBX with r/m32\n" + "run: effective address is 0x00002000 (EAX)\n" + "run: effective address contains 1\n" + "run: SF=1; ZF=0; CF=0; OF=0\n" + ); +} + +void test_compare_r32_with_mem_at_r32_lesser_unsigned() { + Reg[EAX].i = 0x2000; + Reg[EBX].i = 0x00000001; // 1 + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 3b 18 \n" // compare EBX with *EAX + // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) + "== 0x2000\n" // data segment + "ff ff ff ff\n" // -1 + ); + CHECK_TRACE_CONTENTS( + "run: compare EBX with r/m32\n" + "run: effective address is 0x00002000 (EAX)\n" + "run: effective address contains ffffffff\n" + "run: SF=0; ZF=0; CF=1; OF=0\n" ); } -:(code) void test_compare_r32_with_mem_at_r32_equal() { Reg[EAX].i = 0x2000; Reg[EBX].i = 0x0a0b0c0d; run( "== 0x1\n" // code segment // op ModR/M SIB displacement immediate - " 3b 18 \n" // compare *EAX with EBX + " 3b 18 \n" // compare EBX with *EAX // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX) "== 0x2000\n" // data segment "0d 0c 0b 0a\n" // 0x0a0b0c0d ); CHECK_TRACE_CONTENTS( - "run: compare r/m32 with EBX\n" + "run: compare EBX with r/m32\n" "run: effective address is 0x00002000 (EAX)\n" - "run: SF=0; ZF=1; OF=0\n" + "run: SF=0; ZF=1; CF=0; OF=0\n" ); } //:: copy (mov) -:(code) void test_copy_r32_to_mem_at_r32() { Reg[EBX].i = 0xaf; Reg[EAX].i = 0x60; @@ -502,8 +731,8 @@ void test_jump_mem_at_r32() { // op ModR/M SIB displacement immediate " ff 20 \n" // jump to *EAX // ModR/M in binary: 00 (indirect mode) 100 (jump to r/m32) 000 (src EAX) - " 05 00 00 00 01\n" - " 05 00 00 00 02\n" + " b8 00 00 00 01\n" + " b8 00 00 00 02\n" "== 0x2000\n" // data segment "08 00 00 00\n" // 0x00000008 ); @@ -512,9 +741,9 @@ void test_jump_mem_at_r32() { "run: jump to r/m32\n" "run: effective address is 0x00002000 (EAX)\n" "run: jumping to 0x00000008\n" - "run: 0x00000008 opcode: 05\n" + "run: 0x00000008 opcode: b8\n" ); - CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000003 opcode: 05"); + CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000003 opcode: b8"); } :(before "End Op ff Subops") @@ -531,7 +760,8 @@ case 4: { // jump to r/m32 :(code) void test_push_mem_at_r32() { Reg[EAX].i = 0x2000; - Reg[ESP].u = 0x14; + Mem.push_back(vma(0xbd000000)); // manually allocate memory + Reg[ESP].u = 0xbd000014; run( "== 0x1\n" // code segment // op ModR/M SIB displacement immediate @@ -542,7 +772,7 @@ void test_push_mem_at_r32() { CHECK_TRACE_CONTENTS( "run: push r/m32\n" "run: effective address is 0x00002000 (EAX)\n" - "run: decrementing ESP to 0x00000010\n" + "run: decrementing ESP to 0xbd000010\n" "run: pushing value 0x000000af\n" ); } @@ -563,20 +793,20 @@ put_new(Name, "8f", "pop top of stack to rm32 (pop)"); :(code) void test_pop_mem_at_r32() { Reg[EAX].i = 0x60; - Reg[ESP].u = 0x2000; + Mem.push_back(vma(0xbd000000)); // manually allocate memory + Reg[ESP].u = 0xbd000000; + write_mem_i32(0xbd000000, 0x00000030); run( "== 0x1\n" // code segment // op ModR/M SIB displacement immediate " 8f 00 \n" // pop stack into *EAX // ModR/M in binary: 00 (indirect mode) 000 (pop r/m32) 000 (dest EAX) - "== 0x2000\n" // data segment - "30 00 00 00\n" // 0x00000030 ); CHECK_TRACE_CONTENTS( "run: pop into r/m32\n" "run: effective address is 0x00000060 (EAX)\n" "run: popping value 0x00000030\n" - "run: incrementing ESP to 0x00002004\n" + "run: incrementing ESP to 0xbd000004\n" ); } diff --git a/subx/015immediate_addressing.cc b/subx/015immediate_addressing.cc index 18cd5334..4210c024 100644 --- a/subx/015immediate_addressing.cc +++ b/subx/015immediate_addressing.cc @@ -1,6 +1,78 @@ //: instructions that (immediately) contain an argument to act with :(before "End Initialize Op Names") +put_new(Name, "05", "add imm32 to EAX (add)"); + +:(before "End Single-Byte Opcodes") +case 0x05: { // add imm32 to EAX + int32_t signed_arg2 = next32(); + trace(Callstack_depth+1, "run") << "add imm32 0x" << HEXWORD << signed_arg2 << " to EAX" << end(); + int32_t signed_result = Reg[EAX].i + signed_arg2; + SF = (signed_result < 0); + ZF = (signed_result == 0); + int64_t signed_full_result = static_cast<int64_t>(Reg[EAX].i) + signed_arg2; + OF = (signed_result != signed_full_result); + // set CF + uint32_t unsigned_arg2 = static_cast<uint32_t>(signed_arg2); + uint32_t unsigned_result = Reg[EAX].u + unsigned_arg2; + uint64_t unsigned_full_result = static_cast<uint64_t>(Reg[EAX].u) + unsigned_arg2; + CF = (unsigned_result != unsigned_full_result); + trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); + Reg[EAX].i = signed_result; + trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[EAX].i << end(); + break; +} + +:(code) +void test_add_imm32_to_EAX_signed_overflow() { + Reg[EAX].i = 0x7fffffff; // largest positive signed integer + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 05 01 00 00 00 \n" // add 1 to EAX + // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) + ); + CHECK_TRACE_CONTENTS( + "run: add imm32 0x00000001 to EAX\n" + "run: SF=1; ZF=0; CF=0; OF=1\n" + "run: storing 0x80000000\n" + ); +} + +void test_add_imm32_to_EAX_unsigned_overflow() { + Reg[EAX].u = 0xffffffff; // largest unsigned number + Reg[EBX].u = 1; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 05 01 00 00 00 \n" // add 1 to EAX + // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) + ); + CHECK_TRACE_CONTENTS( + "run: add imm32 0x00000001 to EAX\n" + "run: SF=0; ZF=1; CF=1; OF=0\n" + "run: storing 0x00000000\n" + ); +} + +void test_add_imm32_to_EAX_unsigned_and_signed_overflow() { + Reg[EAX].u = 0x80000000; // smallest negative signed integer + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 05 00 00 00 80 \n" // add 0x80000000 to EAX + // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) + ); + CHECK_TRACE_CONTENTS( + "run: add imm32 0x80000000 to EAX\n" + "run: SF=0; ZF=1; CF=1; OF=1\n" + "run: storing 0x00000000\n" + ); +} + +//: + +:(before "End Initialize Op Names") put_new(Name, "81", "combine rm32 with imm32 based on subop (add/sub/and/or/xor/cmp)"); :(code) @@ -10,7 +82,7 @@ void test_add_imm32_to_r32() { "== 0x1\n" // code segment // op ModR/M SIB displacement immediate " 81 c3 0a 0b 0c 0d\n" // add 0x0d0c0b0a to EBX - // ModR/M in binary: 11 (direct mode) 000 (add imm32) 011 (dest EBX) + // ModR/M in binary: 11 (direct mode) 000 (subop add) 011 (dest EBX) ); CHECK_TRACE_CONTENTS( "run: combine imm32 with r/m32\n" @@ -25,15 +97,29 @@ void test_add_imm32_to_r32() { case 0x81: { // combine imm32 with r/m32 trace(Callstack_depth+1, "run") << "combine imm32 with r/m32" << end(); const uint8_t modrm = next(); - int32_t* arg1 = effective_address(modrm); - const int32_t arg2 = next32(); - trace(Callstack_depth+1, "run") << "imm32 is 0x" << HEXWORD << arg2 << end(); + int32_t* signed_arg1 = effective_address(modrm); + const int32_t signed_arg2 = next32(); + trace(Callstack_depth+1, "run") << "imm32 is 0x" << HEXWORD << signed_arg2 << end(); const uint8_t subop = (modrm>>3)&0x7; // middle 3 'reg opcode' bits switch (subop) { - case 0: + case 0: { trace(Callstack_depth+1, "run") << "subop add" << end(); - BINARY_ARITHMETIC_OP(+, *arg1, arg2); + int32_t signed_result = *signed_arg1 + signed_arg2; + SF = (signed_result < 0); + ZF = (signed_result == 0); + int64_t signed_full_result = static_cast<int64_t>(*signed_arg1) + signed_arg2; + OF = (signed_result != signed_full_result); + // set CF + uint32_t unsigned_arg1 = static_cast<uint32_t>(*signed_arg1); + uint32_t unsigned_arg2 = static_cast<uint32_t>(signed_arg2); + uint32_t unsigned_result = unsigned_arg1 + unsigned_arg2; + uint64_t unsigned_full_result = static_cast<uint64_t>(unsigned_arg1) + unsigned_arg2; + CF = (unsigned_result != unsigned_full_result); + trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); + *signed_arg1 = signed_result; + trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); break; + } // End Op 81 Subops default: cerr << "unrecognized subop for opcode 81: " << NUM(subop) << '\n'; @@ -42,6 +128,61 @@ case 0x81: { // combine imm32 with r/m32 break; } +:(code) +void test_add_imm32_to_r32_signed_overflow() { + Reg[EBX].i = 0x7fffffff; // largest positive signed integer + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 81 c3 01 00 00 00\n" // add 1 to EBX + // ModR/M in binary: 11 (direct mode) 000 (subop add) 011 (dest EBX) + ); + CHECK_TRACE_CONTENTS( + "run: combine imm32 with r/m32\n" + "run: r/m32 is EBX\n" + "run: imm32 is 0x00000001\n" + "run: subop add\n" + "run: SF=1; ZF=0; CF=0; OF=1\n" + "run: storing 0x80000000\n" + ); +} + +void test_add_imm32_to_r32_unsigned_overflow() { + Reg[EBX].u = 0xffffffff; // largest unsigned number + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 81 c3 01 00 00 00\n" // add 1 to EBX + // ModR/M in binary: 11 (direct mode) 011 (subop add) 011 (dest EBX) + ); + CHECK_TRACE_CONTENTS( + "run: combine imm32 with r/m32\n" + "run: r/m32 is EBX\n" + "run: imm32 is 0x00000001\n" + "run: subop add\n" + "run: SF=0; ZF=1; CF=1; OF=0\n" + "run: storing 0x00000000\n" + ); +} + +void test_add_imm32_to_r32_unsigned_and_signed_overflow() { + Reg[EBX].u = 0x80000000; // smallest negative signed integer + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 81 c3 00 00 00 80\n" // add 0x80000000 to EBX + // ModR/M in binary: 11 (direct mode) 011 (subop add) 011 (dest EBX) + ); + CHECK_TRACE_CONTENTS( + "run: combine imm32 with r/m32\n" + "run: r/m32 is EBX\n" + "run: imm32 is 0x80000000\n" + "run: subop add\n" + "run: SF=0; ZF=1; CF=1; OF=1\n" + "run: storing 0x00000000\n" + ); +} + //: :(code) @@ -51,7 +192,7 @@ void test_add_imm32_to_mem_at_r32() { "== 0x1\n" // code segment // op ModR/M SIB displacement immediate " 81 03 0a 0b 0c 0d \n" // add 0x0d0c0b0a to *EBX - // ModR/M in binary: 00 (indirect mode) 000 (add imm32) 011 (dest EBX) + // ModR/M in binary: 00 (indirect mode) 000 (subop add) 011 (dest EBX) "== 0x2000\n" // data segment "01 00 00 00\n" // 0x00000001 ); @@ -70,7 +211,7 @@ void test_add_imm32_to_mem_at_r32() { put_new(Name, "2d", "subtract imm32 from EAX (sub)"); :(code) -void test_subtract_imm32_from_eax() { +void test_subtract_imm32_from_EAX() { Reg[EAX].i = 0x0d0c0baa; run( "== 0x1\n" // code segment @@ -85,22 +226,79 @@ void test_subtract_imm32_from_eax() { :(before "End Single-Byte Opcodes") case 0x2d: { // subtract imm32 from EAX - const int32_t arg2 = next32(); - trace(Callstack_depth+1, "run") << "subtract imm32 0x" << HEXWORD << arg2 << " from EAX" << end(); - BINARY_ARITHMETIC_OP(-, Reg[EAX].i, arg2); + const int32_t signed_arg2 = next32(); + trace(Callstack_depth+1, "run") << "subtract imm32 0x" << HEXWORD << signed_arg2 << " from EAX" << end(); + int32_t signed_result = Reg[EAX].i - signed_arg2; + SF = (signed_result < 0); + ZF = (signed_result == 0); + int64_t signed_full_result = static_cast<int64_t>(Reg[EAX].i) - signed_arg2; + OF = (signed_result != signed_full_result); + // set CF + uint32_t unsigned_arg2 = static_cast<uint32_t>(signed_arg2); + uint32_t unsigned_result = Reg[EAX].u - unsigned_arg2; + uint64_t unsigned_full_result = static_cast<uint64_t>(Reg[EAX].u) - unsigned_arg2; + CF = (unsigned_result != unsigned_full_result); + trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); + Reg[EAX].i = signed_result; + trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[EAX].i << end(); break; } +:(code) +void test_subtract_imm32_from_EAX_signed_overflow() { + Reg[EAX].i = 0x80000000; // smallest negative signed integer + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 2d ff ff ff 7f \n" // subtract largest positive signed integer from EAX + // ModR/M in binary: 00 (indirect mode) 101 (subop subtract) 011 (dest EBX) + ); + CHECK_TRACE_CONTENTS( + "run: subtract imm32 0x7fffffff from EAX\n" + "run: SF=0; ZF=0; CF=0; OF=1\n" + "run: storing 0x00000001\n" + ); +} + +void test_subtract_imm32_from_EAX_unsigned_overflow() { + Reg[EAX].i = 0; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 2d 01 00 00 00 \n" // subtract 1 from EAX + // ModR/M in binary: 00 (indirect mode) 101 (subop subtract) 011 (dest EBX) + ); + CHECK_TRACE_CONTENTS( + "run: subtract imm32 0x00000001 from EAX\n" + "run: SF=1; ZF=0; CF=1; OF=0\n" + "run: storing 0xffffffff\n" + ); +} + +void test_subtract_imm32_from_EAX_signed_and_unsigned_overflow() { + Reg[EAX].i = 0; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 2d 00 00 00 80 \n" // subtract smallest negative signed integer from EAX + // ModR/M in binary: 00 (indirect mode) 101 (subop subtract) 011 (dest EBX) + ); + CHECK_TRACE_CONTENTS( + "run: subtract imm32 0x80000000 from EAX\n" + "run: SF=1; ZF=0; CF=1; OF=1\n" + "run: storing 0x80000000\n" + ); +} + //: -:(code) void test_subtract_imm32_from_mem_at_r32() { Reg[EBX].i = 0x2000; run( "== 0x1\n" // code segment // op ModR/M SIB displacement immediate " 81 2b 01 00 00 00 \n" // subtract 1 from *EBX - // ModR/M in binary: 00 (indirect mode) 101 (subtract imm32) 011 (dest EBX) + // ModR/M in binary: 00 (indirect mode) 101 (subop subtract) 011 (dest EBX) "== 0x2000\n" // data segment "0a 00 00 00\n" // 0x0000000a ); @@ -116,20 +314,96 @@ void test_subtract_imm32_from_mem_at_r32() { :(before "End Op 81 Subops") case 5: { trace(Callstack_depth+1, "run") << "subop subtract" << end(); - BINARY_ARITHMETIC_OP(-, *arg1, arg2); + int32_t signed_result = *signed_arg1 - signed_arg2; + SF = (signed_result < 0); + ZF = (signed_result == 0); + int64_t signed_full_result = static_cast<int64_t>(*signed_arg1) - signed_arg2; + OF = (signed_result != signed_full_result); + // set CF + uint32_t unsigned_arg1 = static_cast<uint32_t>(*signed_arg1); + uint32_t unsigned_arg2 = static_cast<uint32_t>(signed_arg2); + uint32_t unsigned_result = unsigned_arg1 - unsigned_arg2; + uint64_t unsigned_full_result = static_cast<uint64_t>(unsigned_arg1) - unsigned_arg2; + CF = (unsigned_result != unsigned_full_result); + trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); + *signed_arg1 = signed_result; + trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); break; } +:(code) +void test_subtract_imm32_from_mem_at_r32_signed_overflow() { + Reg[EBX].i = 0x2000; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 81 2b ff ff ff 7f \n" // subtract largest positive signed integer from *EBX + // ModR/M in binary: 00 (indirect mode) 101 (subop subtract) 011 (dest EBX) + "== 0x2000\n" // data segment + "00 00 00 80\n" // smallest negative signed integer + ); + CHECK_TRACE_CONTENTS( + "run: combine imm32 with r/m32\n" + "run: effective address is 0x00002000 (EBX)\n" + "run: effective address contains 80000000\n" + "run: imm32 is 0x7fffffff\n" + "run: subop subtract\n" + "run: SF=0; ZF=0; CF=0; OF=1\n" + "run: storing 0x00000001\n" + ); +} + +void test_subtract_imm32_from_mem_at_r32_unsigned_overflow() { + Reg[EBX].i = 0x2000; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 81 2b 01 00 00 00 \n" // subtract 1 from *EBX + // ModR/M in binary: 00 (indirect mode) 101 (subop subtract) 011 (dest EBX) + "== 0x2000\n" // data segment + "00 00 00 00\n" // 0 + ); + CHECK_TRACE_CONTENTS( + "run: combine imm32 with r/m32\n" + "run: effective address is 0x00002000 (EBX)\n" + "run: effective address contains 0\n" + "run: imm32 is 0x00000001\n" + "run: subop subtract\n" + "run: SF=1; ZF=0; CF=1; OF=0\n" + "run: storing 0xffffffff\n" + ); +} + +void test_subtract_imm32_from_mem_at_r32_signed_and_unsigned_overflow() { + Reg[EBX].i = 0x2000; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 81 2b 00 00 00 80 \n" // subtract smallest negative signed integer from *EBX + // ModR/M in binary: 00 (indirect mode) 101 (subop subtract) 011 (dest EBX) + "== 0x2000\n" // data segment + "00 00 00 00\n" // 0 + ); + CHECK_TRACE_CONTENTS( + "run: combine imm32 with r/m32\n" + "run: effective address is 0x00002000 (EBX)\n" + "run: effective address contains 0\n" + "run: imm32 is 0x80000000\n" + "run: subop subtract\n" + "run: SF=1; ZF=0; CF=1; OF=1\n" + "run: storing 0x80000000\n" + ); +} + //: -:(code) void test_subtract_imm32_from_r32() { Reg[EBX].i = 10; run( "== 0x1\n" // code segment // op ModR/M SIB displacement immediate " 81 eb 01 00 00 00 \n" // subtract 1 from EBX - // ModR/M in binary: 11 (direct mode) 101 (subtract imm32) 011 (dest EBX) + // ModR/M in binary: 11 (direct mode) 101 (subop subtract) 011 (dest EBX) ); CHECK_TRACE_CONTENTS( "run: combine imm32 with r/m32\n" @@ -338,7 +612,7 @@ void test_shift_right_logical_negative_r32_with_imm8() { put_new(Name, "25", "EAX = bitwise AND of imm32 with EAX (and)"); :(code) -void test_and_imm32_with_eax() { +void test_and_EAX_with_imm32() { Reg[EAX].i = 0xff; run( "== 0x1\n" // code segment @@ -353,9 +627,17 @@ void test_and_imm32_with_eax() { :(before "End Single-Byte Opcodes") case 0x25: { // and imm32 with EAX - const int32_t arg2 = next32(); - trace(Callstack_depth+1, "run") << "and imm32 0x" << HEXWORD << arg2 << " with EAX" << end(); - BINARY_BITWISE_OP(&, Reg[EAX].i, arg2); + // bitwise ops technically operate on unsigned numbers, but it makes no + // difference + const int32_t signed_arg2 = next32(); + trace(Callstack_depth+1, "run") << "and imm32 0x" << HEXWORD << signed_arg2 << " with EAX" << end(); + Reg[EAX].i &= signed_arg2; + trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[EAX].i << end(); + SF = (Reg[EAX].i >> 31); + ZF = (Reg[EAX].i == 0); + CF = false; + OF = false; + trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); break; } @@ -368,7 +650,7 @@ void test_and_imm32_with_mem_at_r32() { "== 0x1\n" // code segment // op ModR/M SIB displacement immediate " 81 23 0a 0b 0c 0d \n" // and 0x0d0c0b0a with *EBX - // ModR/M in binary: 00 (indirect mode) 100 (and imm32) 011 (dest EBX) + // ModR/M in binary: 00 (indirect mode) 100 (subop and) 011 (dest EBX) "== 0x2000\n" // data segment "ff 00 00 00\n" // 0x000000ff ); @@ -384,7 +666,15 @@ void test_and_imm32_with_mem_at_r32() { :(before "End Op 81 Subops") case 4: { trace(Callstack_depth+1, "run") << "subop and" << end(); - BINARY_BITWISE_OP(&, *arg1, arg2); + // bitwise ops technically operate on unsigned numbers, but it makes no + // difference + *signed_arg1 &= signed_arg2; + trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); + SF = (*signed_arg1 >> 31); + ZF = (*signed_arg1 == 0); + CF = false; + OF = false; + trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); break; } @@ -397,7 +687,7 @@ void test_and_imm32_with_r32() { "== 0x1\n" // code segment // op ModR/M SIB displacement immediate " 81 e3 0a 0b 0c 0d \n" // and 0x0d0c0b0a with EBX - // ModR/M in binary: 11 (direct mode) 100 (and imm32) 011 (dest EBX) + // ModR/M in binary: 11 (direct mode) 100 (subop and) 011 (dest EBX) ); CHECK_TRACE_CONTENTS( "run: combine imm32 with r/m32\n" @@ -414,7 +704,7 @@ void test_and_imm32_with_r32() { put_new(Name, "0d", "EAX = bitwise OR of imm32 with EAX (or)"); :(code) -void test_or_imm32_with_eax() { +void test_or_EAX_with_imm32() { Reg[EAX].i = 0xd0c0b0a0; run( "== 0x1\n" // code segment @@ -429,9 +719,17 @@ void test_or_imm32_with_eax() { :(before "End Single-Byte Opcodes") case 0x0d: { // or imm32 with EAX - const int32_t arg2 = next32(); - trace(Callstack_depth+1, "run") << "or imm32 0x" << HEXWORD << arg2 << " with EAX" << end(); - BINARY_BITWISE_OP(|, Reg[EAX].i, arg2); + // bitwise ops technically operate on unsigned numbers, but it makes no + // difference + const int32_t signed_arg2 = next32(); + trace(Callstack_depth+1, "run") << "or imm32 0x" << HEXWORD << signed_arg2 << " with EAX" << end(); + Reg[EAX].i |= signed_arg2; + trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[EAX].i << end(); + SF = (Reg[EAX].i >> 31); + ZF = (Reg[EAX].i == 0); + CF = false; + OF = false; + trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); break; } @@ -444,7 +742,7 @@ void test_or_imm32_with_mem_at_r32() { "== 0x1\n" // code segment // op ModR/M SIB displacement immediate " 81 0b 0a 0b 0c 0d \n" // or 0x0d0c0b0a with *EBX - // ModR/M in binary: 00 (indirect mode) 001 (or imm32) 011 (dest EBX) + // ModR/M in binary: 00 (indirect mode) 001 (subop or) 011 (dest EBX) "== 0x2000\n" // data segment "a0 b0 c0 d0\n" // 0xd0c0b0a0 ); @@ -460,7 +758,15 @@ void test_or_imm32_with_mem_at_r32() { :(before "End Op 81 Subops") case 1: { trace(Callstack_depth+1, "run") << "subop or" << end(); - BINARY_BITWISE_OP(|, *arg1, arg2); + // bitwise ops technically operate on unsigned numbers, but it makes no + // difference + *signed_arg1 |= signed_arg2; \ + trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); \ + SF = (*signed_arg1 >> 31); \ + ZF = (*signed_arg1 == 0); \ + CF = false; \ + OF = false; \ + trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); \ break; } @@ -471,7 +777,7 @@ void test_or_imm32_with_r32() { "== 0x1\n" // code segment // op ModR/M SIB displacement immediate " 81 cb 0a 0b 0c 0d \n" // or 0x0d0c0b0a with EBX - // ModR/M in binary: 11 (direct mode) 001 (or imm32) 011 (dest EBX) + // ModR/M in binary: 11 (direct mode) 001 (subop or) 011 (dest EBX) ); CHECK_TRACE_CONTENTS( "run: combine imm32 with r/m32\n" @@ -488,7 +794,7 @@ void test_or_imm32_with_r32() { put_new(Name, "35", "EAX = bitwise XOR of imm32 with EAX (xor)"); :(code) -void test_xor_imm32_with_eax() { +void test_xor_EAX_with_imm32() { Reg[EAX].i = 0xddccb0a0; run( "== 0x1\n" // code segment @@ -503,9 +809,17 @@ void test_xor_imm32_with_eax() { :(before "End Single-Byte Opcodes") case 0x35: { // xor imm32 with EAX - const int32_t arg2 = next32(); - trace(Callstack_depth+1, "run") << "xor imm32 0x" << HEXWORD << arg2 << " with EAX" << end(); - BINARY_BITWISE_OP(^, Reg[EAX].i, arg2); + // bitwise ops technically operate on unsigned numbers, but it makes no + // difference + const int32_t signed_arg2 = next32(); + trace(Callstack_depth+1, "run") << "xor imm32 0x" << HEXWORD << signed_arg2 << " with EAX" << end(); + Reg[EAX].i ^= signed_arg2; + trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[EAX].i << end(); + SF = (Reg[EAX].i >> 31); + ZF = (Reg[EAX].i == 0); + CF = false; + OF = false; + trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); break; } @@ -518,7 +832,7 @@ void test_xor_imm32_with_mem_at_r32() { "== 0x1\n" // code segment // op ModR/M SIB displacement immediate " 81 33 0a 0b 0c 0d \n" // xor 0x0d0c0b0a with *EBX - // ModR/M in binary: 00 (indirect mode) 110 (xor imm32) 011 (dest EBX) + // ModR/M in binary: 00 (indirect mode) 110 (subop xor) 011 (dest EBX) "== 0x2000\n" // data segment "a0 b0 c0 d0\n" // 0xd0c0b0a0 ); @@ -534,7 +848,15 @@ void test_xor_imm32_with_mem_at_r32() { :(before "End Op 81 Subops") case 6: { trace(Callstack_depth+1, "run") << "subop xor" << end(); - BINARY_BITWISE_OP(^, *arg1, arg2); + // bitwise ops technically operate on unsigned numbers, but it makes no + // difference + *signed_arg1 ^= signed_arg2; + trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); + SF = (*signed_arg1 >> 31); + ZF = (*signed_arg1 == 0); + CF = false; + OF = false; + trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); break; } @@ -545,7 +867,7 @@ void test_xor_imm32_with_r32() { "== 0x1\n" // code segment // op ModR/M SIB displacement immediate " 81 f3 0a 0b 0c 0d \n" // xor 0x0d0c0b0a with EBX - // ModR/M in binary: 11 (direct mode) 110 (xor imm32) 011 (dest EBX) + // ModR/M in binary: 11 (direct mode) 110 (subop xor) 011 (dest EBX) ); CHECK_TRACE_CONTENTS( "run: combine imm32 with r/m32\n" @@ -562,49 +884,96 @@ void test_xor_imm32_with_r32() { put_new(Name, "3d", "compare: set SF if EAX < imm32 (cmp)"); :(code) -void test_compare_imm32_with_eax_greater() { +void test_compare_EAX_with_imm32_greater() { Reg[EAX].i = 0x0d0c0b0a; run( "== 0x1\n" // code segment // op ModR/M SIB displacement immediate - " 3d 07 0b 0c 0d \n" // compare 0x0d0c0b07 with EAX + " 3d 07 0b 0c 0d \n" // compare EAX with 0x0d0c0b07 ); CHECK_TRACE_CONTENTS( - "run: compare EAX and imm32 0x0d0c0b07\n" - "run: SF=0; ZF=0; OF=0\n" + "run: compare EAX with imm32 0x0d0c0b07\n" + "run: SF=0; ZF=0; CF=0; OF=0\n" ); } :(before "End Single-Byte Opcodes") case 0x3d: { // compare EAX with imm32 - const int32_t arg1 = Reg[EAX].i; - const int32_t arg2 = next32(); - trace(Callstack_depth+1, "run") << "compare EAX and imm32 0x" << HEXWORD << arg2 << end(); - const int32_t tmp1 = arg1 - arg2; - SF = (tmp1 < 0); - ZF = (tmp1 == 0); - const int64_t tmp2 = arg1 - arg2; - OF = (tmp1 != tmp2); - trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; OF=" << OF << end(); + const int32_t signed_arg1 = Reg[EAX].i; + const int32_t signed_arg2 = next32(); + trace(Callstack_depth+1, "run") << "compare EAX with imm32 0x" << HEXWORD << signed_arg2 << end(); + const int32_t signed_difference = signed_arg1 - signed_arg2; + SF = (signed_difference < 0); + ZF = (signed_difference == 0); + const int64_t full_signed_difference = static_cast<int64_t>(signed_arg1) - signed_arg2; + OF = (signed_difference != full_signed_difference); + const uint32_t unsigned_arg1 = static_cast<uint32_t>(signed_arg1); + const uint32_t unsigned_arg2 = static_cast<uint32_t>(signed_arg2); + const uint32_t unsigned_difference = unsigned_arg1 - unsigned_arg2; + const uint64_t full_unsigned_difference = static_cast<uint64_t>(unsigned_arg1) - unsigned_arg2; + CF = (unsigned_difference != full_unsigned_difference); + trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); break; } :(code) -void test_compare_imm32_with_eax_lesser() { - Reg[EAX].i = 0x0d0c0b07; +void test_compare_EAX_with_imm32_lesser_unsigned_and_signed() { + Reg[EAX].i = 0x0a0b0c07; run( "== 0x1\n" // code segment // op ModR/M SIB displacement immediate - " 3d 0a 0b 0c 0d \n" // compare 0x0d0c0b0a with EAX + " 3d 0d 0c 0b 0a \n" // compare EAX with imm32 + // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) ); CHECK_TRACE_CONTENTS( - "run: compare EAX and imm32 0x0d0c0b0a\n" - "run: SF=1; ZF=0; OF=0\n" + "run: compare EAX with imm32 0x0a0b0c0d\n" + "run: SF=1; ZF=0; CF=1; OF=0\n" ); } -:(code) -void test_compare_imm32_with_eax_equal() { +void test_compare_EAX_with_imm32_lesser_unsigned_and_signed_due_to_overflow() { + Reg[EAX].i = 0x7fffffff; // largest positive signed integer + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 3d 00 00 00 80\n" // compare EAX with smallest negative signed integer + // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) + ); + CHECK_TRACE_CONTENTS( + "run: compare EAX with imm32 0x80000000\n" + "run: SF=1; ZF=0; CF=1; OF=1\n" + ); +} + +void test_compare_EAX_with_imm32_lesser_signed() { + Reg[EAX].i = 0xffffffff; // -1 + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 3d 01 00 00 00\n" // compare EAX with 1 + // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) + ); + CHECK_TRACE_CONTENTS( + "run: compare EAX with imm32 0x00000001\n" + "run: SF=1; ZF=0; CF=0; OF=0\n" + ); +} + +void test_compare_EAX_with_imm32_lesser_unsigned() { + Reg[EAX].i = 0x00000001; // 1 + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " 3d ff ff ff ff\n" // compare EAX with -1 + // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX) + ); + CHECK_TRACE_CONTENTS( + "run: compare EAX with imm32 0xffffffff\n" + "run: SF=0; ZF=0; CF=1; OF=0\n" + ); +} + +void test_compare_EAX_with_imm32_equal() { Reg[EAX].i = 0x0d0c0b0a; run( "== 0x1\n" // code segment @@ -612,39 +981,38 @@ void test_compare_imm32_with_eax_equal() { " 3d 0a 0b 0c 0d \n" // compare 0x0d0c0b0a with EAX ); CHECK_TRACE_CONTENTS( - "run: compare EAX and imm32 0x0d0c0b0a\n" - "run: SF=0; ZF=1; OF=0\n" + "run: compare EAX with imm32 0x0d0c0b0a\n" + "run: SF=0; ZF=1; CF=0; OF=0\n" ); } //: -:(code) void test_compare_imm32_with_r32_greater() { Reg[EBX].i = 0x0d0c0b0a; run( "== 0x1\n" // code segment // op ModR/M SIB displacement immediate " 81 fb 07 0b 0c 0d \n" // compare 0x0d0c0b07 with EBX - // ModR/M in binary: 11 (direct mode) 111 (compare imm32) 011 (dest EBX) + // ModR/M in binary: 11 (direct mode) 111 (subop compare) 011 (dest EBX) ); CHECK_TRACE_CONTENTS( "run: combine imm32 with r/m32\n" "run: r/m32 is EBX\n" "run: imm32 is 0x0d0c0b07\n" - "run: SF=0; ZF=0; OF=0\n" + "run: SF=0; ZF=0; CF=0; OF=0\n" ); } :(before "End Op 81 Subops") case 7: { trace(Callstack_depth+1, "run") << "subop compare" << end(); - const int32_t tmp1 = *arg1 - arg2; + const int32_t tmp1 = *signed_arg1 - signed_arg2; SF = (tmp1 < 0); ZF = (tmp1 == 0); - const int64_t tmp2 = *arg1 - arg2; + const int64_t tmp2 = static_cast<int64_t>(*signed_arg1) - signed_arg2; OF = (tmp1 != tmp2); - trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; OF=" << OF << end(); + trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); break; } @@ -655,13 +1023,13 @@ void test_compare_imm32_with_r32_lesser() { "== 0x1\n" // code segment // op ModR/M SIB displacement immediate " 81 fb 0a 0b 0c 0d \n" // compare 0x0d0c0b0a with EBX - // ModR/M in binary: 11 (direct mode) 111 (compare imm32) 011 (dest EBX) + // ModR/M in binary: 11 (direct mode) 111 (subop compare) 011 (dest EBX) ); CHECK_TRACE_CONTENTS( "run: combine imm32 with r/m32\n" "run: r/m32 is EBX\n" "run: imm32 is 0x0d0c0b0a\n" - "run: SF=1; ZF=0; OF=0\n" + "run: SF=1; ZF=0; CF=0; OF=0\n" ); } @@ -672,13 +1040,13 @@ void test_compare_imm32_with_r32_equal() { "== 0x1\n" // code segment // op ModR/M SIB displacement immediate " 81 fb 0a 0b 0c 0d \n" // compare 0x0d0c0b0a with EBX - // ModR/M in binary: 11 (direct mode) 111 (compare imm32) 011 (dest EBX) + // ModR/M in binary: 11 (direct mode) 111 (subop compare) 011 (dest EBX) ); CHECK_TRACE_CONTENTS( "run: combine imm32 with r/m32\n" "run: r/m32 is EBX\n" "run: imm32 is 0x0d0c0b0a\n" - "run: SF=0; ZF=1; OF=0\n" + "run: SF=0; ZF=1; CF=0; OF=0\n" ); } @@ -689,7 +1057,7 @@ void test_compare_imm32_with_mem_at_r32_greater() { "== 0x1\n" // code segment // op ModR/M SIB displacement immediate " 81 3b 07 0b 0c 0d \n" // compare 0x0d0c0b07 with *EBX - // ModR/M in binary: 00 (indirect mode) 111 (compare imm32) 011 (dest EBX) + // ModR/M in binary: 00 (indirect mode) 111 (subop compare) 011 (dest EBX) "== 0x2000\n" // data segment "0a 0b 0c 0d\n" // 0x0d0c0b0a ); @@ -697,7 +1065,7 @@ void test_compare_imm32_with_mem_at_r32_greater() { "run: combine imm32 with r/m32\n" "run: effective address is 0x00002000 (EBX)\n" "run: imm32 is 0x0d0c0b07\n" - "run: SF=0; ZF=0; OF=0\n" + "run: SF=0; ZF=0; CF=0; OF=0\n" ); } @@ -708,7 +1076,7 @@ void test_compare_imm32_with_mem_at_r32_lesser() { "== 0x1\n" // code segment // op ModR/M SIB displacement immediate " 81 3b 0a 0b 0c 0d \n" // compare 0x0d0c0b0a with *EBX - // ModR/M in binary: 00 (indirect mode) 111 (compare imm32) 011 (dest EBX) + // ModR/M in binary: 00 (indirect mode) 111 (subop compare) 011 (dest EBX) "== 0x2000\n" // data segment "07 0b 0c 0d\n" // 0x0d0c0b07 ); @@ -716,7 +1084,7 @@ void test_compare_imm32_with_mem_at_r32_lesser() { "run: combine imm32 with r/m32\n" "run: effective address is 0x00002000 (EBX)\n" "run: imm32 is 0x0d0c0b0a\n" - "run: SF=1; ZF=0; OF=0\n" + "run: SF=1; ZF=0; CF=0; OF=0\n" ); } @@ -728,7 +1096,7 @@ void test_compare_imm32_with_mem_at_r32_equal() { "== 0x1\n" // code segment // op ModR/M SIB displacement immediate " 81 3b 0a 0b 0c 0d \n" // compare 0x0d0c0b0a with *EBX - // ModR/M in binary: 00 (indirect mode) 111 (compare imm32) 011 (dest EBX) + // ModR/M in binary: 00 (indirect mode) 111 (subop compare) 011 (dest EBX) "== 0x2000\n" // data segment "0a 0b 0c 0d\n" // 0x0d0c0b0a ); @@ -736,14 +1104,14 @@ void test_compare_imm32_with_mem_at_r32_equal() { "run: combine imm32 with r/m32\n" "run: effective address is 0x00002000 (EBX)\n" "run: imm32 is 0x0d0c0b0a\n" - "run: SF=0; ZF=1; OF=0\n" + "run: SF=0; ZF=1; CF=0; OF=0\n" ); } //:: copy (mov) :(before "End Initialize Op Names") -put_new(Name, "b8", "copy imm32 to EAX (mov)"); +// b8 defined earlier to copy imm32 to EAX put_new(Name, "b9", "copy imm32 to ECX (mov)"); put_new(Name, "ba", "copy imm32 to EDX (mov)"); put_new(Name, "bb", "copy imm32 to EBX (mov)"); @@ -765,7 +1133,6 @@ void test_copy_imm32_to_r32() { } :(before "End Single-Byte Opcodes") -case 0xb8: case 0xb9: case 0xba: case 0xbb: @@ -824,7 +1191,8 @@ put_new(Name, "68", "push imm32 to stack (push)"); :(code) void test_push_imm32() { - Reg[ESP].u = 0x14; + Mem.push_back(vma(0xbd000000)); // manually allocate memory + Reg[ESP].u = 0xbd000014; run( "== 0x1\n" // code segment // op ModR/M SIB displacement immediate @@ -832,7 +1200,7 @@ void test_push_imm32() { ); CHECK_TRACE_CONTENTS( "run: push imm32 0x000000af\n" - "run: ESP is now 0x00000010\n" + "run: ESP is now 0xbd000010\n" "run: contents at ESP: 0x000000af\n" ); } diff --git a/subx/017jump_disp8.cc b/subx/017jump_disp8.cc index 22ae6567..35cc1331 100644 --- a/subx/017jump_disp8.cc +++ b/subx/017jump_disp8.cc @@ -135,7 +135,8 @@ void test_jne_rel8_fail() { //:: jump if greater :(before "End Initialize Op Names") -put_new(Name, "7f", "jump disp8 bytes away if greater, if ZF is unset and SF == OF (jcc/jg/jnle)"); +put_new(Name, "7f", "jump disp8 bytes away if greater (signed), if ZF is unset and SF == OF (jcc/jg/jnle)"); +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() { @@ -158,9 +159,17 @@ void test_jg_rel8_success() { } :(before "End Single-Byte Opcodes") -case 0x7f: { // jump rel8 if !SF and !ZF +case 0x7f: { // jump rel8 if SF == OF and !ZF const int8_t offset = static_cast<int>(next()); - if (!ZF && SF == OF) { + if (SF == OF && !ZF) { + trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); + EIP += offset; + } + break; +} +case 0x77: { // jump rel8 if !CF and !ZF + const int8_t offset = static_cast<int>(next()); + if (!CF && !ZF) { trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); EIP += offset; } @@ -190,7 +199,8 @@ void test_jg_rel8_fail() { //:: jump if greater or equal :(before "End Initialize Op Names") -put_new(Name, "7d", "jump disp8 bytes away if greater or equal, if SF == OF (jcc/jge/jnl)"); +put_new(Name, "7d", "jump disp8 bytes away if greater or equal (signed), if SF == OF (jcc/jge/jnl)"); +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() { @@ -212,7 +222,7 @@ void test_jge_rel8_success() { } :(before "End Single-Byte Opcodes") -case 0x7d: { // jump rel8 if !SF +case 0x7d: { // jump rel8 if SF == OF const int8_t offset = static_cast<int>(next()); if (SF == OF) { trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); @@ -220,6 +230,14 @@ case 0x7d: { // jump rel8 if !SF } break; } +case 0x73: { // jump rel8 if !CF + const int8_t offset = static_cast<int>(next()); + if (!CF) { + trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); + EIP += offset; + } + break; +} :(code) void test_jge_rel8_fail() { @@ -243,7 +261,8 @@ void test_jge_rel8_fail() { //:: jump if lesser :(before "End Initialize Op Names") -put_new(Name, "7c", "jump disp8 bytes away if lesser, if SF != OF (jcc/jl/jnge)"); +put_new(Name, "7c", "jump disp8 bytes away if lesser (signed), if SF != OF (jcc/jl/jnge)"); +put_new(Name, "72", "jump disp8 bytes away if lesser (unsigned), if CF is set (jcc/jb/jnae)"); :(code) void test_jl_rel8_success() { @@ -266,7 +285,7 @@ void test_jl_rel8_success() { } :(before "End Single-Byte Opcodes") -case 0x7c: { // jump rel8 if SF and !ZF +case 0x7c: { // jump rel8 if SF != OF const int8_t offset = static_cast<int>(next()); if (SF != OF) { trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); @@ -274,6 +293,14 @@ case 0x7c: { // jump rel8 if SF and !ZF } break; } +case 0x72: { // jump rel8 if CF + const int8_t offset = static_cast<int>(next()); + if (CF) { + trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); + EIP += offset; + } + break; +} :(code) void test_jl_rel8_fail() { @@ -298,7 +325,8 @@ void test_jl_rel8_fail() { //:: jump if lesser or equal :(before "End Initialize Op Names") -put_new(Name, "7e", "jump disp8 bytes away if lesser or equal, if ZF is set or SF != OF (jcc/jle/jng)"); +put_new(Name, "7e", "jump disp8 bytes away if lesser or equal (signed), if ZF is set or SF != OF (jcc/jle/jng)"); +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() { @@ -341,7 +369,7 @@ void test_jle_rel8_lesser() { } :(before "End Single-Byte Opcodes") -case 0x7e: { // jump rel8 if SF or ZF +case 0x7e: { // jump rel8 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(); @@ -349,6 +377,14 @@ case 0x7e: { // jump rel8 if SF or ZF } break; } +case 0x76: { // jump rel8 if ZF or CF + const int8_t offset = static_cast<int>(next()); + if (ZF || CF) { + trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end(); + EIP += offset; + } + break; +} :(code) void test_jle_rel8_greater() { diff --git a/subx/019functions.cc b/subx/019functions.cc index 7f45167b..00da8397 100644 --- a/subx/019functions.cc +++ b/subx/019functions.cc @@ -5,7 +5,8 @@ put_new(Name, "e8", "call disp32 (call)"); :(code) void test_call_disp32() { - Reg[ESP].u = 0x64; + Mem.push_back(vma(0xbd000000)); // manually allocate memory + Reg[ESP].u = 0xbd000064; run( "== 0x1\n" // code segment // op ModR/M SIB displacement immediate @@ -14,7 +15,7 @@ void test_call_disp32() { ); CHECK_TRACE_CONTENTS( "run: call imm32 0x000000a0\n" - "run: decrementing ESP to 0x00000060\n" + "run: decrementing ESP to 0xbd000060\n" "run: pushing value 0x00000006\n" "run: jumping to 0x000000a6\n" ); @@ -36,7 +37,8 @@ case 0xe8: { // call disp32 relative to next EIP :(code) void test_call_r32() { - Reg[ESP].u = 0x64; + Mem.push_back(vma(0xbd000000)); // manually allocate memory + Reg[ESP].u = 0xbd000064; Reg[EBX].u = 0x000000a0; run( "== 0x1\n" // code segment @@ -47,7 +49,7 @@ void test_call_r32() { CHECK_TRACE_CONTENTS( "run: call to r/m32\n" "run: r/m32 is EBX\n" - "run: decrementing ESP to 0x00000060\n" + "run: decrementing ESP to 0xbd000060\n" "run: pushing value 0x00000003\n" "run: jumping to 0x000000a3\n" ); @@ -66,7 +68,8 @@ case 2: { // call function pointer at r/m32 :(code) void test_call_mem_at_r32() { - Reg[ESP].u = 0x64; + Mem.push_back(vma(0xbd000000)); // manually allocate memory + Reg[ESP].u = 0xbd000064; Reg[EBX].u = 0x2000; run( "== 0x1\n" // code segment @@ -79,7 +82,7 @@ void test_call_mem_at_r32() { CHECK_TRACE_CONTENTS( "run: call to r/m32\n" "run: effective address is 0x00002000 (EBX)\n" - "run: decrementing ESP to 0x00000060\n" + "run: decrementing ESP to 0xbd000060\n" "run: pushing value 0x00000003\n" "run: jumping to 0x000000a3\n" ); @@ -92,7 +95,9 @@ put_new(Name, "c3", "return from most recent unfinished call (ret)"); :(code) void test_ret() { - Reg[ESP].u = 0x2000; + Mem.push_back(vma(0xbd000000)); // manually allocate memory + Reg[ESP].u = 0xbd000064; + write_mem_u32(Reg[ESP].u, 0x10); run( "== 0x1\n" // code segment // op ModR/M SIB displacement immediate diff --git a/subx/020syscalls.cc b/subx/020syscalls.cc index 6b9faa2c..eb8ebcce 100644 --- a/subx/020syscalls.cc +++ b/subx/020syscalls.cc @@ -111,16 +111,19 @@ void check_mode(int reg) { :(before "End Globals") // Very primitive/fixed/insecure mmap segments for now. -// For now we avoid addresses with the most significant bit set; SubX doesn't -// support unsigned comparison yet (https://github.com/akkartik/mu/issues/30) -// Once we do, we can go up to 0xc0000000; higher addresses are reserved for -// the Linux kernel. -uint32_t Next_segment = 0x7c000000; +uint32_t Segments_allocated_above = END_HEAP; const uint32_t SPACE_FOR_SEGMENT = 0x01000000; :(code) +// always allocate multiples of the segment size uint32_t new_segment(uint32_t length) { - uint32_t result = Next_segment; - Mem.push_back(vma(Next_segment, Next_segment+length)); - Next_segment -= SPACE_FOR_SEGMENT; + assert(length > 0); + uint32_t result = (Segments_allocated_above - length) & 0xff000000; + if (result <= START_HEAP) { + raise << "Allocated too many segments; the VM ran out of memory. " + << "Maybe SPACE_FOR_SEGMENT can be smaller?\n" << end(); + exit(1); + } + Mem.push_back(vma(result, result+length)); + Segments_allocated_above = result; return result; } diff --git a/subx/031check_operands.cc b/subx/031check_operands.cc index 9590979f..aad84da6 100644 --- a/subx/031check_operands.cc +++ b/subx/031check_operands.cc @@ -152,8 +152,12 @@ void init_permitted_operands() { // jump put(Permitted_operands, "eb", 0x04); + put(Permitted_operands, "72", 0x04); + put(Permitted_operands, "73", 0x04); put(Permitted_operands, "74", 0x04); put(Permitted_operands, "75", 0x04); + put(Permitted_operands, "76", 0x04); + put(Permitted_operands, "77", 0x04); put(Permitted_operands, "7c", 0x04); put(Permitted_operands, "7d", 0x04); put(Permitted_operands, "7e", 0x04); diff --git a/subx/034compute_segment_address.cc b/subx/034compute_segment_address.cc index a1b7482d..47311219 100644 --- a/subx/034compute_segment_address.cc +++ b/subx/034compute_segment_address.cc @@ -14,7 +14,7 @@ void test_segment_name() { "load: 0x09000056 -> 0b\n" "load: 0x09000057 -> 0c\n" "load: 0x09000058 -> 0d\n" - "run: add imm32 0x0d0c0b0a to reg EAX\n" + "run: add imm32 0x0d0c0b0a to EAX\n" "run: storing 0x0d0c0b0a\n" ); } diff --git a/subx/040---tests.cc b/subx/040---tests.cc index d35cc711..e5949bbd 100644 --- a/subx/040---tests.cc +++ b/subx/040---tests.cc @@ -16,7 +16,8 @@ Transform.push_back(create_test_function); :(code) void test_run_test() { - Reg[ESP].u = 0x100; + Mem.push_back(vma(0xbd000000)); // manually allocate memory + Reg[ESP].u = 0xbd000100; run( "== 0x1\n" // code segment "main:\n" diff --git a/subx/050_write.subx b/subx/050_write.subx index 083adad6..0d6b8152 100644 --- a/subx/050_write.subx +++ b/subx/050_write.subx @@ -32,6 +32,9 @@ _write: # fd : int, s : (address array byte) -> <void> # . syscall b8/copy-to-EAX 4/imm32/write cd/syscall 0x80/imm8 + # if (EAX < 0) abort + 3d/compare-EAX-with 0/imm32 + 0f 8c/jump-if-lesser $_write:abort/disp32 $_write:end: # . restore registers 5b/pop-to-EBX @@ -43,4 +46,12 @@ $_write:end: 5d/pop-to-EBP c3/return +$_write:abort: + # can't write a message here for risk of an infinite loop, so we'll use a special exit code instead + # . syscall(exit, 255) + bb/copy-to-EBX 0xff/imm32 + b8/copy-to-EAX 1/imm32/exit + cd/syscall 0x80/imm8 + # never gets here + # . . vim:nowrap:textwidth=0 diff --git a/subx/057write.subx b/subx/057write.subx index 3135003b..455146ac 100644 --- a/subx/057write.subx +++ b/subx/057write.subx @@ -27,7 +27,7 @@ write: # f : fd or (address stream), s : (address array byte) -> <void> 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP # if (f < 0x08000000) _write(f, s) and return # f can't be a user-mode address, so treat it as a kernel file descriptor 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 0x08000000/imm32 # compare *(EBP+8) - 7d/jump-if-greater-or-equal $write:fake/disp8 + 73/jump-if-greater-unsigned-or-equal $write:fake/disp8 # . . 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) diff --git a/subx/060read.subx b/subx/060read.subx index cedafbf5..d377a1ad 100644 --- a/subx/060read.subx +++ b/subx/060read.subx @@ -51,7 +51,7 @@ read: # f : fd or (address stream), s : (address stream) -> num-bytes-read/EAX 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP # if (f < 0x08000000) return _read(f, s) # f can't be a user-mode address, so treat it as a kernel file descriptor 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 0x08000000/imm32 # compare *(EBP+8) - 7d/jump-if-greater-or-equal $read:fake/disp8 + 73/jump-if-greater-unsigned-or-equal $read:fake/disp8 # . . 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) diff --git a/subx/062write-stream.subx b/subx/062write-stream.subx index 73766fe3..92c67dc2 100644 --- a/subx/062write-stream.subx +++ b/subx/062write-stream.subx @@ -21,7 +21,7 @@ write-stream: # f : fd or (address stream), s : (address stream) -> <void> 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP # if (f < 0x08000000) _write-stream(f, s), return # f can't be a user-mode address, so treat it as a kernel file descriptor 81 7/subop/compare 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 0x08000000/imm32 # compare *(EBP+8) - 7d/jump-if-greater-or-equal $write-stream:fake/disp8 + 73/jump-if-greater-unsigned-or-equal $write-stream:fake/disp8 # . . 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) @@ -102,6 +102,11 @@ _write-stream: # fd : int, s : (address stream) -> <void> # . . syscall b8/copy-to-EAX 4/imm32/write cd/syscall 0x80/imm8 + # if (EAX < 0) abort + 3d/compare-EAX-with 0/imm32 + 0f 8c/jump-if-lesser $_write-stream:abort/disp32 + # s->read += EAX + 01/add 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 4/disp8 . # add EAX to *(ESI+4) # . restore registers 5f/pop-to-EDI 5e/pop-to-ESI @@ -114,6 +119,21 @@ _write-stream: # fd : int, s : (address stream) -> <void> 5d/pop-to-EBP c3/return +$_write-stream:abort: + # . _write(2/stderr, error) + # . . push args + 68/push "_write-stream: failed to write to file"/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-stream-single: # setup # . clear-stream(_test-stream) diff --git a/subx/066print-byte.subx b/subx/066print-int.subx index 80a29a0c..0715b414 100644 --- a/subx/066print-byte.subx +++ b/subx/066print-int.subx @@ -1,10 +1,17 @@ -# Print the (hex) textual representation of the lowest byte of a number. +# Print the (hex) textual representation 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: # run a single test, while debugging +#? e8/call test-print-int32-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 + append-byte-hex: # f : (address stream), n : int -> <void> # . prolog 55/push-EBP @@ -76,6 +83,7 @@ test-append-byte-hex: # . end c3/return +# print the hex representation for the lowest byte of a number print-byte-buffered: # f : (address buffered-file), n : int -> <void> # . prolog 55/push-EBP @@ -163,4 +171,116 @@ test-print-byte-buffered: # . end c3/return +print-int32-buffered: # f : (address buffered-file), n : int -> <void> + # pseudocode: + # ECX = 28 + # while true + # if (ECX < 0) break + # EAX = n >> ECX + # EAX = EAX & 0xf + # write-byte-buffered(f, AL) + # ECX -= 4 + # + # . 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 = 28 + b9/copy-to-ECX 0x1c/imm32 +$print-int32-buffered:loop: + # if (ECX < 0) break + 81 7/subop/compare 3/mod/direct 1/rm32/ECX . . . . . 0/imm32 # compare ECX + 7c/jump-if-lesser $print-int32-buffered:end/disp8 + # EAX = n >> ECX + 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 0xc/disp8 . # copy *(EBP+12) to EAX + d3/>>ECX 5/subop/pad-zeroes 3/mod/direct 0/rm32/EAX . . . . . . # shift EAX right by ECX bits, padding zeroes + # EAX = to-hex-char(AL) + 25/and-EAX 0xf/imm32 + e8/call to-hex-char/disp32 + # write-byte-buffered(f, AL) + # . . push args + 50/push-EAX + ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) + # . . call + e8/call write-byte-buffered/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # ECX -= 4 + 81 5/subop/subtract 3/mod/direct 1/rm32/ECX . . . . . 4/imm32 # subtract from ECX + eb/jump $print-int32-buffered:loop/disp8 +$print-int32-buffered: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 + +test-print-int32-buffered: + # - check that print-int32-buffered prints the hex textual representation + # 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 + # . clear-stream(_test-buffered-file+4) + # . . push args + b8/copy-to-EAX _test-buffered-file/imm32 + 05/add-to-EAX 4/imm32 + 50/push-EAX + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # print-int32-buffered(_test-buffered-file, 0x8899aa) + # . . push args + 68/push 0x8899aa/imm32 + 68/push _test-buffered-file/imm32 + # . . call + e8/call print-int32-buffered/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # flush(_test-buffered-file) + # . . push args + 68/push _test-buffered-file/imm32 + # . . call + e8/call flush/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +#? # dump line {{{ +#? # . write-stream(2/stderr, line) +#? # . . push args +#? 68/push _test-stream/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write-stream/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +#? # . write(2/stderr, "$\n") +#? # . . push args +#? 68/push "$\n"/imm32 +#? 68/push 2/imm32/stderr +#? # . . call +#? e8/call write/disp32 +#? # . . discard args +#? 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +#? # }}} + # check-stream-equal(_test-stream, "008899aa", msg) + # . . push args + 68/push "F - test-print-int32-buffered"/imm32 + 68/push "008899aa"/imm32 + 68/push _test-stream/imm32 + # . . call + e8/call check-stream-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # . end + c3/return + # . . vim:nowrap:textwidth=0 diff --git a/subx/apps/assort b/subx/apps/assort index fb24828b..1cd0b6cf 100755 --- a/subx/apps/assort +++ b/subx/apps/assort Binary files differdiff --git a/subx/apps/crenshaw2-1 b/subx/apps/crenshaw2-1 index 2ffc8a84..03d3d0c3 100755 --- a/subx/apps/crenshaw2-1 +++ b/subx/apps/crenshaw2-1 Binary files differdiff --git a/subx/apps/crenshaw2-1b b/subx/apps/crenshaw2-1b index 6144d591..60521cb0 100755 --- a/subx/apps/crenshaw2-1b +++ b/subx/apps/crenshaw2-1b Binary files differdiff --git a/subx/apps/dquotes b/subx/apps/dquotes index a91f829d..c180c114 100644..100755 --- a/subx/apps/dquotes +++ b/subx/apps/dquotes Binary files differdiff --git a/subx/apps/dquotes.subx b/subx/apps/dquotes.subx index e6b24698..5f98d295 100644 --- a/subx/apps/dquotes.subx +++ b/subx/apps/dquotes.subx @@ -112,6 +112,8 @@ convert: # in : (address buffered-file), out : (address buffered-file) -> <void # process-string-literal(word-slice, out, new-data-segment) # else # write-slice-buffered(out, word-slice) + # write(out, " ") + # write(out, "\n\n") # write-stream-data(out, new-data-segment) # flush(out) # @@ -1223,9 +1225,227 @@ emit-metadata: # out : (address buffered-file), word : (address slice) 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 + + # PSEUDOCODE + # ECX = (char *) word->start + # while true: + # if ECX == word->end: return + # if *(ECX++) == '/': break + # write-slice-buffered(out, {ECX, word->end}) + + # ECX = word + 8b/copy/word 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 0xc/disp8 . # copy *(EBP+12) to ECX + # EDX = word->end + 8b/copy/word->end 1/mod/*+disp8 1/rm32/ECX . . . 2/r32/EDX 4/disp8 . # copy *(ECX+4) to EDX + # ECX = word->start + 8b/copy/word->start 0/mod/indirect 1/rm32/ECX . . . 1/r32/ECX . . # copy *ECX to ECX + + # clear out EAX + b8/copy-to-EAX 0/imm32 + # while *start != '/': +$skip-datum-loop: + # . start == end? + 39/compare-ECX-and 3/mod/direct 2/rm32/EDX . . . 1/r32/ECX . . # EDX == ECX + # . if so, return from function (it's only datum, or empty) + 74/jump-if-equal $emit-metadata:end/disp8 + + # . start++ + 41/increment-ECX # ECX++ + + # . EAX = *start + 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy *ECX to EAX + + # . EAX != '/'? + 3d/compare-EAX-and 0x2f/imm32 + # . if so, continue looping + 75/jump-if-not-equal $skip-datum-loop/disp8 + # end + + # write-slice-buffered(out, &{start, end}) + # . push end + 52/push-EDX + # . push start + 51/push-ECX + # . push &{start, end} + 54/push-ESP + + # . push out + ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) + + e8/call write-slice-buffered/disp32 + # . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . 0x10/imm32 . # add 16 to ESP + $emit-metadata:end: - # . reclaim locals # . restore registers + 5a/pop-to-EDX + 59/pop-to-ECX + 58/pop-to-EAX + # . epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return + +test-emit-metadata: + # . prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # setup + # . clear-stream(_test-output-stream) + # . . push args + 68/push _test-output-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # . clear-stream(_test-output-buffered-file+4) + # . . push args + b8/copy-to-EAX _test-output-buffered-file/imm32 + 05/add-to-EAX 4/imm32 + 50/push-EAX + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # var slice/ECX = "abc/def" + 68/push _test-slice-word-end/imm32 + 68/push _test-slice-word/imm32/start + 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX + # emit-metadata(_test-output-buffered-file, slice) + # . . push args + 51/push-ECX + 68/push _test-output-buffered-file/imm32 + # . . call + e8/call emit-metadata/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # flush(_test-output-buffered-file) + # . . push args + 68/push _test-output-buffered-file/imm32 + # . . call + e8/call flush/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # check-stream-equal(_test-output-stream, "/def", msg) # important that there's no leading space + # . . push args + 68/push "F - test-emit-metadata"/imm32 + 68/push "/def"/imm32 + 68/push _test-output-stream/imm32 + # . . call + e8/call check-stream-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # . epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return + +test-emit-metadata-none: + # . prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # setup + # . clear-stream(_test-output-stream) + # . . push args + 68/push _test-output-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # . clear-stream(_test-output-buffered-file+4) + # . . push args + b8/copy-to-EAX _test-output-buffered-file/imm32 + 05/add-to-EAX 4/imm32 + 50/push-EAX + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # var slice/ECX = "abc" + 68/push _test-slice-word-datum-end/imm32 + 68/push _test-slice-word/imm32/start + 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX + # emit-metadata(_test-output-buffered-file, slice) + # . . push args + 51/push-ECX + 68/push _test-output-buffered-file/imm32 + # . . call + e8/call emit-metadata/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # flush(_test-output-buffered-file) + # . . push args + 68/push _test-output-buffered-file/imm32 + # . . call + e8/call flush/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # check-stream-equal(_test-output-stream, "", msg) + # . . push args + 68/push "F - test-emit-metadata-none"/imm32 + 68/push ""/imm32 + 68/push _test-output-stream/imm32 + # . . call + e8/call check-stream-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + # . epilog + 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 5d/pop-to-EBP + c3/return + +test-emit-metadata-multiple: + # . prolog + 55/push-EBP + 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + # setup + # . clear-stream(_test-output-stream) + # . . push args + 68/push _test-output-stream/imm32 + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # . clear-stream(_test-output-buffered-file+4) + # . . push args + b8/copy-to-EAX _test-output-buffered-file/imm32 + 05/add-to-EAX 4/imm32 + 50/push-EAX + # . . call + e8/call clear-stream/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # var slice/ECX = "abc/def/ghi" + 68/push _test-slice-word-end2/imm32 + 68/push _test-slice-word/imm32/start + 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX + # emit-metadata(_test-output-buffered-file, slice) + # . . push args + 51/push-ECX + 68/push _test-output-buffered-file/imm32 + # . . call + e8/call emit-metadata/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + # flush(_test-output-buffered-file) + # . . push args + 68/push _test-output-buffered-file/imm32 + # . . call + e8/call flush/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + # check-stream-equal(_test-output-stream, "/def/ghi", msg) # important that there's no leading space + # . . push args + 68/push "F - test-emit-metadata-multiple"/imm32 + 68/push "/def/ghi"/imm32 + 68/push _test-output-stream/imm32 + # . . call + e8/call check-stream-equal/disp32 + # . . discard args + 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP # . epilog 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP 5d/pop-to-EBP @@ -1746,4 +1966,13 @@ _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: + # . . vim:nowrap:textwidth=0 diff --git a/subx/apps/factorial b/subx/apps/factorial index d1bd5f17..c18b8bcc 100755 --- a/subx/apps/factorial +++ b/subx/apps/factorial Binary files differdiff --git a/subx/apps/handle b/subx/apps/handle index b77ebba6..4fe1e044 100755 --- a/subx/apps/handle +++ b/subx/apps/handle Binary files differdiff --git a/subx/apps/hex b/subx/apps/hex index dde157e3..59170942 100755 --- a/subx/apps/hex +++ b/subx/apps/hex Binary files differdiff --git a/subx/apps/pack b/subx/apps/pack index 1154be78..f2149b59 100755 --- a/subx/apps/pack +++ b/subx/apps/pack Binary files differdiff --git a/subx/build b/subx/build index 614b07d8..f29518ac 100755 --- a/subx/build +++ b/subx/build @@ -22,7 +22,7 @@ UNTIL_LAYER=${2:-zzz} test "$CXX" || export CXX=c++ test "$CC" || export CC=cc test "$CFLAGS" || export CFLAGS="-g -O3" -export CFLAGS="$CFLAGS -Wall -Wextra -ftrapv -fno-strict-aliasing" +export CFLAGS="$CFLAGS -Wall -Wextra -fno-strict-aliasing" # return 1 if $1 is older than _any_ of the remaining args older_than() { diff --git a/subx/test_layers b/subx/test_layers index a2ed65c6..688a5a99 100755 --- a/subx/test_layers +++ b/subx/test_layers @@ -21,11 +21,11 @@ CFLAGS=$CFLAGS ./build # build optimized by default since we'll be running it r for f in [0-9]*.subx do echo "=== $f" - ./subx translate $(../enumerate/enumerate --until $f |grep '\.subx$') -o foo && ./subx run foo + ./subx translate $(../enumerate/enumerate --until $f |grep '\.subx$') -o a.elf && ./subx run a.elf echo test `uname` = 'Linux' && { - chmod +x foo - ./foo + chmod +x a.elf + ./a.elf echo } || true done |