diff options
Diffstat (limited to 'subx')
-rw-r--r-- | subx/013direct_addressing.cc | 94 | ||||
-rw-r--r-- | subx/opcodes | 2 |
2 files changed, 94 insertions, 2 deletions
diff --git a/subx/013direct_addressing.cc b/subx/013direct_addressing.cc index 2a984dfe..779c7e0a 100644 --- a/subx/013direct_addressing.cc +++ b/subx/013direct_addressing.cc @@ -119,7 +119,7 @@ case 0x29: { // subtract r32 from r/m32 //:: multiply :(before "End Initialize Op Names") -put_new(Name, "f7", "negate/multiply rm32 (with EAX if necessary) depending on subop (neg/mul)"); +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() { @@ -251,6 +251,98 @@ void test_negate_can_overflow() { ); } +//:: divide with remainder + +void test_divide_eax_by_rm32() { + Reg[EAX].u = 7; + Reg[EDX].u = 0; + Reg[ECX].i = 3; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " f7 f9 \n" // multiply EAX by ECX + // ModR/M in binary: 11 (direct mode) 111 (subop idiv) 001 (divisor ECX) + ); + CHECK_TRACE_CONTENTS( + "run: operate on r/m32\n" + "run: r/m32 is ECX\n" + "run: subop: divide EDX:EAX by r/m32, storing quotient in EAX and remainder in EDX\n" + "run: quotient: 0x00000002\n" + "run: remainder: 0x00000001\n" + ); +} + +:(before "End Op f7 Subops") +case 7: { // divide EDX:EAX by r/m32, storing quotient in EAX and remainder in EDX + trace(Callstack_depth+1, "run") << "subop: divide EDX:EAX by r/m32, storing quotient in EAX and remainder in EDX" << end(); + int64_t dividend = static_cast<int64_t>((static_cast<uint64_t>(Reg[EDX].u) << 32) | Reg[EAX].u); + int32_t divisor = *arg1; + assert(divisor != 0); + Reg[EAX].i = dividend/divisor; // quotient + Reg[EDX].i = dividend%divisor; // remainder + 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() { + Reg[EAX].u = 7; + Reg[EDX].u = 0; + Reg[ECX].i = -3; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " f7 f9 \n" // multiply EAX by ECX + // ModR/M in binary: 11 (direct mode) 111 (subop idiv) 001 (divisor ECX) + ); + CHECK_TRACE_CONTENTS( + "run: operate on r/m32\n" + "run: r/m32 is ECX\n" + "run: subop: divide EDX:EAX by r/m32, storing quotient in EAX and remainder in EDX\n" + "run: quotient: 0xfffffffe\n" // -2 + "run: remainder: 0x00000001\n" + ); +} + +void test_divide_negative_eax_by_rm32() { + Reg[EAX].i = -7; + Reg[EDX].i = -1; // sign extend + Reg[ECX].i = 3; + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " f7 f9 \n" // multiply EAX by ECX + // ModR/M in binary: 11 (direct mode) 111 (subop idiv) 001 (divisor ECX) + ); + CHECK_TRACE_CONTENTS( + "run: operate on r/m32\n" + "run: r/m32 is ECX\n" + "run: subop: divide EDX:EAX by r/m32, storing quotient in EAX and remainder in EDX\n" + "run: quotient: 0xfffffffe\n" // -2 + "run: remainder: 0xffffffff\n" // -1, same sign as divident (EDX:EAX) + ); +} + +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) + run( + "== 0x1\n" // code segment + // op ModR/M SIB displacement immediate + " f7 f9 \n" // multiply EAX by ECX + // ModR/M in binary: 11 (direct mode) 111 (subop idiv) 001 (divisor ECX) + ); + CHECK_TRACE_CONTENTS( + "run: operate on r/m32\n" + "run: r/m32 is ECX\n" + "run: subop: divide EDX:EAX by r/m32, storing quotient in EAX and remainder in EDX\n" + "run: quotient: 0xffffffe4\n" // (-7 << 32) / (1 << 30) = -7 << 2 = -28 + "run: remainder: 0x00000000\n" + ); +} + //:: shift left :(before "End Initialize Op Names") diff --git a/subx/opcodes b/subx/opcodes index 0eb0792b..2a11df2b 100644 --- a/subx/opcodes +++ b/subx/opcodes @@ -82,7 +82,7 @@ Opcodes currently supported by SubX: e9: jump disp32 bytes away (jmp) eb: jump disp8 bytes away (jmp) f4: halt (hlt) - f7: negate/multiply rm32 (with EAX if necessary) depending on subop (neg/mul) + f7: negate/multiply/divide rm32 (with EAX and EDX if necessary) depending on subop (neg/mul/idiv) ff: increment/decrement/jump/push/call rm32 based on subop (inc/dec/jmp/push/call) 0f 84: jump disp32 bytes away if equal, if ZF is set (jcc/jz/je) 0f 85: jump disp32 bytes away if not equal, if ZF is not set (jcc/jnz/jne) |