From 8c1a69089bcb0be8ee869a3f83c3151a7083e27c Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Sun, 12 May 2019 07:36:18 -0700 Subject: snapshot of carry flag implementation Tests failing. This approach seems wrong. I'm not sure even the tests are correct. Also, some open questions: 1. Should setting the overflow flag always set the carry flag? 2. Should the carry flag only be set on add/subtract/compare, or by all arithmetic ops? 3. Had to turn off the -ftrapv flag in `build`. Is there a way to detect overflow without actually causing overflow? Once we start setting CF correctly we have to implement jump above/below instructions (8- and 32-bit displacement variants). https://github.com/akkartik/mu/issues/30 --- subx/010---vm.cc | 48 +++++++++++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 17 deletions(-) (limited to 'subx/010---vm.cc') diff --git a/subx/010---vm.cc b/subx/010---vm.cc index 31e5608f..00a2dee4 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,9 +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; +SF = ZF = CF = OF = false; //: how the flag registers are updated after each instruction @@ -88,25 +92,35 @@ SF = ZF = OF = false; // 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); \ +#define BINARY_ARITHMETIC_OP(op, signed_arg1, signed_arg2) { \ + cerr << signed_arg1 << " vs " << signed_arg2 << '\n'; \ + int64_t signed_full_result = signed_arg1 op signed_arg2; \ + signed_arg1 = signed_arg1 op signed_arg2; \ + trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << signed_arg1 << end(); \ + SF = (signed_arg1 < 0); \ + ZF = (signed_arg1 == 0); \ + OF = (signed_arg1 != signed_full_result); \ + /* CF is more complex */ \ + uint32_t unsigned_arg1 = static_cast(signed_arg1); \ + uint32_t unsigned_arg2 = static_cast(signed_arg2); \ + cerr << unsigned_arg1 << " vs " << unsigned_arg2 << '\n'; \ + uint32_t unsigned_result = unsigned_arg1 op unsigned_arg2; \ + cerr << "result: " << unsigned_result << '\n'; \ + uint64_t unsigned_full_result = unsigned_arg1 op unsigned_arg2; \ + CF = (unsigned_result != unsigned_full_result); \ + trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); \ } // 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); \ +#define BINARY_BITWISE_OP(op, unsigned_arg1, unsigned_arg2) { \ + unsigned_arg1 = unsigned_arg1 op unsigned_arg2; \ + trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << unsigned_arg1 << end(); \ + SF = (unsigned_arg1 >> 31); \ + ZF = (unsigned_arg1 == 0); \ + CF = false; \ OF = false; \ + trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); \ } //:: simulated RAM @@ -374,7 +388,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(); } -- cgit 1.4.1-2-gfad0 From 4847a5e615bb2393e537295d02a57005a2b2140c Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Sun, 12 May 2019 13:27:14 -0700 Subject: . Drop some prints as a first step to straightening things out. --- subx/010---vm.cc | 3 --- subx/013direct_addressing.cc | 4 ---- 2 files changed, 7 deletions(-) (limited to 'subx/010---vm.cc') diff --git a/subx/010---vm.cc b/subx/010---vm.cc index 00a2dee4..379c441d 100644 --- a/subx/010---vm.cc +++ b/subx/010---vm.cc @@ -93,7 +93,6 @@ SF = ZF = CF = OF = false; // result in 'arg1', then update flags. // beware: no side-effects in args #define BINARY_ARITHMETIC_OP(op, signed_arg1, signed_arg2) { \ - cerr << signed_arg1 << " vs " << signed_arg2 << '\n'; \ int64_t signed_full_result = signed_arg1 op signed_arg2; \ signed_arg1 = signed_arg1 op signed_arg2; \ trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << signed_arg1 << end(); \ @@ -103,9 +102,7 @@ SF = ZF = CF = OF = false; /* CF is more complex */ \ uint32_t unsigned_arg1 = static_cast(signed_arg1); \ uint32_t unsigned_arg2 = static_cast(signed_arg2); \ - cerr << unsigned_arg1 << " vs " << unsigned_arg2 << '\n'; \ uint32_t unsigned_result = unsigned_arg1 op unsigned_arg2; \ - cerr << "result: " << unsigned_result << '\n'; \ uint64_t unsigned_full_result = unsigned_arg1 op unsigned_arg2; \ CF = (unsigned_result != unsigned_full_result); \ trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); \ diff --git a/subx/013direct_addressing.cc b/subx/013direct_addressing.cc index ef5ac752..c94a152d 100644 --- a/subx/013direct_addressing.cc +++ b/subx/013direct_addressing.cc @@ -733,7 +733,6 @@ case 0x39: { // set SF if r/m32 < r32 trace(Callstack_depth+1, "run") << "compare " << rname(reg2) << " with r/m32" << end(); const int32_t* signed_arg1 = effective_address(modrm); const int32_t signed_arg2 = Reg[reg2].i; - cerr << *signed_arg1 << " vs " << signed_arg2 << '\n'; const int32_t signed_difference = *signed_arg1 - signed_arg2; SF = (signed_difference < 0); ZF = (signed_difference == 0); @@ -741,11 +740,8 @@ case 0x39: { // set SF if r/m32 < r32 OF = (signed_difference != signed_full_difference); const uint32_t unsigned_arg1 = static_cast(*signed_arg1); const uint32_t unsigned_arg2 = static_cast(signed_arg2); - cerr << unsigned_arg1 << " vs " << unsigned_arg2 << '\n'; const uint32_t unsigned_difference = unsigned_arg1 - unsigned_arg2; - cerr << "result: " << unsigned_difference << '\n'; const uint64_t unsigned_full_difference = unsigned_arg1 - unsigned_arg2; - cerr << "full result: " << unsigned_full_difference << '\n'; CF = (unsigned_difference != unsigned_full_difference); trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); break; -- cgit 1.4.1-2-gfad0 From 82fb58e6068f3718fe7cedd1cde1b405308bbbab Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Sun, 12 May 2019 22:41:19 -0700 Subject: CF needs special handling for some arithmetic ops Inline some macro definitions. --- subx/010---vm.cc | 34 ----------- subx/011run.cc | 18 +++++- subx/013direct_addressing.cc | 86 +++++++++++++++++++++----- subx/014indirect_addressing.cc | 66 ++++++++++++++++---- subx/015immediate_addressing.cc | 130 +++++++++++++++++++++++++++++++++------- 5 files changed, 248 insertions(+), 86 deletions(-) (limited to 'subx/010---vm.cc') diff --git a/subx/010---vm.cc b/subx/010---vm.cc index 379c441d..18f69035 100644 --- a/subx/010---vm.cc +++ b/subx/010---vm.cc @@ -86,40 +86,6 @@ bool OF = false; // overflow flag :(before "End Reset") SF = ZF = CF = 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, signed_arg1, signed_arg2) { \ - int64_t signed_full_result = signed_arg1 op signed_arg2; \ - signed_arg1 = signed_arg1 op signed_arg2; \ - trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << signed_arg1 << end(); \ - SF = (signed_arg1 < 0); \ - ZF = (signed_arg1 == 0); \ - OF = (signed_arg1 != signed_full_result); \ - /* CF is more complex */ \ - uint32_t unsigned_arg1 = static_cast(signed_arg1); \ - uint32_t unsigned_arg2 = static_cast(signed_arg2); \ - uint32_t unsigned_result = unsigned_arg1 op unsigned_arg2; \ - uint64_t unsigned_full_result = unsigned_arg1 op unsigned_arg2; \ - CF = (unsigned_result != unsigned_full_result); \ - trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); \ -} - -// Combine 'arg1' and 'arg2' with bitwise operation 'op' and store the result -// in 'arg1', then update flags. -#define BINARY_BITWISE_OP(op, unsigned_arg1, unsigned_arg2) { \ - unsigned_arg1 = unsigned_arg1 op unsigned_arg2; \ - trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << unsigned_arg1 << end(); \ - SF = (unsigned_arg1 >> 31); \ - ZF = (unsigned_arg1 == 0); \ - CF = false; \ - OF = false; \ - trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); \ -} - //:: simulated RAM :(before "End Types") diff --git a/subx/011run.cc b/subx/011run.cc index 6b18ca06..ccc891c3 100644 --- a/subx/011run.cc +++ b/subx/011run.cc @@ -355,9 +355,21 @@ put_new(Name, "05", "add imm32 to EAX (add)"); //: 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); + int32_t signed_arg2 = next32(); + trace(Callstack_depth+1, "run") << "add imm32 0x" << HEXWORD << signed_arg2 << " to reg EAX" << end(); + int64_t signed_full_result = Reg[EAX].i + signed_arg2; + Reg[EAX].i += signed_arg2; + trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[EAX].i << end(); + SF = (Reg[EAX].i < 0); + ZF = (Reg[EAX].i == 0); + OF = (Reg[EAX].i != signed_full_result); + // set CF + uint32_t unsigned_arg1 = static_cast(Reg[EAX].i); + uint32_t unsigned_arg2 = static_cast(signed_arg2); + uint32_t unsigned_result = unsigned_arg1 + unsigned_arg2; + uint64_t unsigned_full_result = unsigned_arg1 + unsigned_arg2; + CF = (unsigned_result != unsigned_full_result); + trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); break; } diff --git a/subx/013direct_addressing.cc b/subx/013direct_addressing.cc index c94a152d..87983e3d 100644 --- a/subx/013direct_addressing.cc +++ b/subx/013direct_addressing.cc @@ -25,8 +25,19 @@ 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); + int64_t signed_full_result = *signed_arg1 + Reg[arg2].i; + *signed_arg1 += Reg[arg2].i; + trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); + SF = (*signed_arg1 < 0); + ZF = (*signed_arg1 == 0); + OF = (*signed_arg1 != signed_full_result); + // set CF + uint32_t unsigned_arg1 = static_cast(*signed_arg1); + uint32_t unsigned_result = unsigned_arg1 + Reg[arg2].u; + uint64_t unsigned_full_result = 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(); break; } @@ -129,8 +140,19 @@ 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); + int64_t signed_full_result = *signed_arg1 - Reg[arg2].i; + *signed_arg1 -= Reg[arg2].i; + trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); + SF = (*signed_arg1 < 0); + ZF = (*signed_arg1 == 0); + OF = (*signed_arg1 != signed_full_result); + // set CF + uint32_t unsigned_arg1 = static_cast(*signed_arg1); + uint32_t unsigned_result = unsigned_arg1 - Reg[arg2].u; + uint64_t unsigned_full_result = 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(); break; } @@ -215,19 +237,26 @@ 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); + int64_t full_result = Reg[arg1].i * (*arg2); + Reg[arg1].i *= (*arg2); + trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[arg1].i << end(); + SF = (Reg[arg1].i < 0); + ZF = (Reg[arg1].i == 0); + OF = (Reg[arg1].i != full_result); + CF = OF; + trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); break; } @@ -316,6 +345,7 @@ 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; @@ -605,8 +635,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; } @@ -637,8 +675,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; } @@ -669,8 +715,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; } diff --git a/subx/014indirect_addressing.cc b/subx/014indirect_addressing.cc index b8b3d6ce..4b891f99 100644 --- a/subx/014indirect_addressing.cc +++ b/subx/014indirect_addressing.cc @@ -59,8 +59,19 @@ 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); + int64_t signed_full_result = Reg[arg1].i + *signed_arg2; + Reg[arg1].i += *signed_arg2; + trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[arg1].i << end(); + SF = (Reg[arg1].i < 0); + ZF = (Reg[arg1].i == 0); + OF = (Reg[arg1].i != signed_full_result); + // set CF + uint32_t unsigned_arg2 = static_cast(*signed_arg2); + uint32_t unsigned_result = Reg[arg1].u + unsigned_arg2; + uint64_t unsigned_full_result = 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(); break; } @@ -114,8 +125,19 @@ 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); + int64_t signed_full_result = Reg[arg1].i - *signed_arg2; + Reg[arg1].i -= *signed_arg2; + trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[arg1].i << end(); + SF = (Reg[arg1].i < 0); + ZF = (Reg[arg1].i == 0); + OF = (Reg[arg1].i != signed_full_result); + // set CF + uint32_t unsigned_arg2 = static_cast(*signed_arg2); + uint32_t unsigned_result = Reg[arg1].u - unsigned_arg2; + uint64_t unsigned_full_result = 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(); break; } @@ -168,8 +190,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 +253,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 +315,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; } diff --git a/subx/015immediate_addressing.cc b/subx/015immediate_addressing.cc index 97c868bd..8a0ba020 100644 --- a/subx/015immediate_addressing.cc +++ b/subx/015immediate_addressing.cc @@ -25,15 +25,28 @@ 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); + int64_t signed_full_result = *signed_arg1 + signed_arg2; + *signed_arg1 += signed_arg2; + trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); + SF = (*signed_arg1 < 0); + ZF = (*signed_arg1 == 0); + OF = (*signed_arg1 != signed_full_result); + // set CF + uint32_t unsigned_arg1 = static_cast(*signed_arg1); + uint32_t unsigned_arg2 = static_cast(signed_arg2); + uint32_t unsigned_result = unsigned_arg1 + unsigned_arg2; + uint64_t unsigned_full_result = unsigned_arg1 + unsigned_arg2; + CF = (unsigned_result != unsigned_full_result); + trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); break; + } // End Op 81 Subops default: cerr << "unrecognized subop for opcode 81: " << NUM(subop) << '\n'; @@ -85,9 +98,20 @@ 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(); + int64_t signed_full_result = Reg[EAX].i - signed_arg2; + Reg[EAX].i -= signed_arg2; + trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[EAX].i << end(); + SF = (Reg[EAX].i < 0); + ZF = (Reg[EAX].i == 0); + OF = (Reg[EAX].i != signed_full_result); + // set CF + uint32_t unsigned_arg2 = static_cast(signed_arg2); + uint32_t unsigned_result = Reg[EAX].u - unsigned_arg2; + uint64_t unsigned_full_result = 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(); break; } @@ -116,7 +140,19 @@ 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); + int64_t signed_full_result = *signed_arg1 - signed_arg2; + *signed_arg1 -= signed_arg2; + trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end(); + SF = (*signed_arg1 < 0); + ZF = (*signed_arg1 == 0); + OF = (*signed_arg1 != signed_full_result); + // set CF + uint32_t unsigned_arg1 = static_cast(*signed_arg1); + uint32_t unsigned_arg2 = static_cast(signed_arg2); + uint32_t unsigned_result = unsigned_arg1 - unsigned_arg2; + uint64_t unsigned_full_result = unsigned_arg1 - unsigned_arg2; + CF = (unsigned_result != unsigned_full_result); + trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); break; } @@ -353,9 +389,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; } @@ -384,7 +428,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; } @@ -429,9 +481,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; } @@ -460,7 +520,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; } @@ -503,9 +571,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; } @@ -534,7 +610,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; } @@ -644,10 +728,10 @@ void test_compare_imm32_with_r32_greater() { :(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 = *signed_arg1 - signed_arg2; OF = (tmp1 != tmp2); trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end(); break; -- cgit 1.4.1-2-gfad0