From 520b64e711b34b47d7ce32a61b328dda6eb7eb85 Mon Sep 17 00:00:00 2001
From: Connor McLaughlin <stenzek@gmail.com>
Date: Sun, 28 Feb 2021 15:03:12 +1000
Subject: [PATCH] CPU/Recompiler: Don't zero read-only bits in mtc0

Fixes memory card error in Digimon Digital Card Battle.
---
 src/core/cpu_code_cache.cpp                |  6 ++-
 src/core/cpu_recompiler_code_generator.cpp | 55 ++++++++++++++++++++++
 src/core/cpu_recompiler_code_generator.h   |  1 +
 3 files changed, 61 insertions(+), 1 deletion(-)

diff --git a/src/core/cpu_code_cache.cpp b/src/core/cpu_code_cache.cpp
index cb2a23c19..c25d3d969 100644
--- a/src/core/cpu_code_cache.cpp
+++ b/src/core/cpu_code_cache.cpp
@@ -319,10 +319,14 @@ void ExecuteRecompiler()
 
     while (g_state.pending_ticks < g_state.downcount)
     {
+#if 0
+      LogCurrentState();
+#endif
+
       const u32 pc = g_state.regs.pc;
       g_state.current_instruction_pc = pc;
       const u32 fast_map_index = GetFastMapIndex(pc);
-      s_single_block_asm_dispatcher[fast_map_index]();
+      s_single_block_asm_dispatcher(s_fast_map[fast_map_index]);
     }
 
     TimingEvents::RunEvents();
diff --git a/src/core/cpu_recompiler_code_generator.cpp b/src/core/cpu_recompiler_code_generator.cpp
index 3ba83b682..b5450317f 100644
--- a/src/core/cpu_recompiler_code_generator.cpp
+++ b/src/core/cpu_recompiler_code_generator.cpp
@@ -684,6 +684,54 @@ Value CodeGenerator::OrValues(const Value& lhs, const Value& rhs)
   return res;
 }
 
+void CodeGenerator::OrValueInPlace(Value& lhs, const Value& rhs)
+{
+  DebugAssert(lhs.size == rhs.size);
+  if (lhs.IsConstant() && rhs.IsConstant())
+  {
+    // compile-time
+    u64 new_cv = lhs.constant_value | rhs.constant_value;
+    switch (lhs.size)
+    {
+      case RegSize_8:
+        lhs = Value::FromConstantU8(Truncate8(new_cv));
+        break;
+
+      case RegSize_16:
+        lhs = Value::FromConstantU16(Truncate16(new_cv));
+        break;
+
+      case RegSize_32:
+        lhs = Value::FromConstantU32(Truncate32(new_cv));
+        break;
+
+      case RegSize_64:
+        lhs = Value::FromConstantU64(new_cv);
+        break;
+
+      default:
+        lhs = Value();
+        break;
+    }
+  }
+
+  // unlikely
+  if (rhs.HasConstantValue(0))
+    return;
+
+  if (lhs.IsInHostRegister())
+  {
+    EmitOr(lhs.host_reg, lhs.host_reg, rhs);
+  }
+  else
+  {
+    Value new_lhs = m_register_cache.AllocateScratch(lhs.size);
+    EmitCopyValue(new_lhs.host_reg, lhs);
+    EmitOr(new_lhs.host_reg, new_lhs.host_reg, rhs);
+    lhs = std::move(new_lhs);
+  }
+}
+
 Value CodeGenerator::AndValues(const Value& lhs, const Value& rhs)
 {
   DebugAssert(lhs.size == rhs.size);
@@ -2397,6 +2445,13 @@ bool CodeGenerator::Compile_cop0(const CodeBlockInstruction& cbi)
             {
               // need to adjust the mask
               Value masked_value = AndValues(value, Value::FromConstantU32(write_mask));
+              {
+                Value old_value = m_register_cache.AllocateScratch(RegSize_32);
+                EmitLoadCPUStructField(old_value.GetHostRegister(), RegSize_32, offset);
+                EmitAnd(old_value.GetHostRegister(), old_value.GetHostRegister(), Value::FromConstantU32(~write_mask));
+                OrValueInPlace(masked_value, old_value);
+              }
+
               if (g_settings.UsingPGXPCPUMode())
               {
                 EmitFunctionCall(nullptr, &PGXP::CPU_MTC0, Value::FromConstantU32(cbi.instruction.bits), masked_value,
diff --git a/src/core/cpu_recompiler_code_generator.h b/src/core/cpu_recompiler_code_generator.h
index e6db0e7dd..e07470e79 100644
--- a/src/core/cpu_recompiler_code_generator.h
+++ b/src/core/cpu_recompiler_code_generator.h
@@ -163,6 +163,7 @@ public:
   Value ShrValues(const Value& lhs, const Value& rhs, bool assume_amount_masked = true);
   Value SarValues(const Value& lhs, const Value& rhs, bool assume_amount_masked = true);
   Value OrValues(const Value& lhs, const Value& rhs);
+  void OrValueInPlace(Value& lhs, const Value& rhs);
   Value AndValues(const Value& lhs, const Value& rhs);
   void AndValueInPlace(Value& lhs, const Value& rhs);
   Value XorValues(const Value& lhs, const Value& rhs);