about summary refs log tree commit diff stats
path: root/subx
diff options
context:
space:
mode:
authorKartik Agaram <vc@akkartik.com>2019-05-12 22:41:19 -0700
committerKartik Agaram <vc@akkartik.com>2019-05-12 22:43:31 -0700
commit82fb58e6068f3718fe7cedd1cde1b405308bbbab (patch)
tree4882cf778e3035ee75c6717ed057b9810e9a5d2b /subx
parent4847a5e615bb2393e537295d02a57005a2b2140c (diff)
downloadmu-82fb58e6068f3718fe7cedd1cde1b405308bbbab.tar.gz
CF needs special handling for some arithmetic ops
Inline some macro definitions.
Diffstat (limited to 'subx')
-rw-r--r--subx/010---vm.cc34
-rw-r--r--subx/011run.cc18
-rw-r--r--subx/013direct_addressing.cc86
-rw-r--r--subx/014indirect_addressing.cc66
-rw-r--r--subx/015immediate_addressing.cc130
5 files changed, 248 insertions, 86 deletions
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<uint32_t>(signed_arg1); \
-  uint32_t unsigned_arg2 = static_cast<uint32_t>(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<uint32_t>(Reg[EAX].i);
+  uint32_t unsigned_arg2 = static_cast<uint32_t>(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<uint32_t>(*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<uint32_t>(*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<uint32_t>(*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<uint32_t>(*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<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 = 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<uint32_t>(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<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 = 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;