Jelajahi Sumber

armemu: Set the Q flag properly for SMLAD/SMUAD

Lioncash 11 tahun lalu
induk
melakukan
79a7a432c5

+ 19 - 13
src/core/arm/interpreter/armemu.cpp

@@ -6478,22 +6478,28 @@ L_stm_s_takeabort:
                 const s16 rn_lo = (rn_val & 0xFFFF);
                 const s16 rn_hi = ((rn_val >> 16) & 0xFFFF);
 
-                // SMUAD
-                if ((instr & 0xf0d0) == 0xf010) {
-                    state->Reg[rd_idx] = (rn_lo * rm_lo) + (rn_hi * rm_hi);
-                }
-                // SMUSD
-                else if ((instr & 0xf0d0) == 0xf050) {
-                    state->Reg[rd_idx] = (rn_lo * rm_lo) - (rn_hi * rm_hi);
-                }
-                // SMLAD
-                else if ((instr & 0xd0) == 0x10) {
-                    state->Reg[rd_idx] = (rn_lo * rm_lo) + (rn_hi * rm_hi) + (s32)state->Reg[ra_idx];
+                const u32 product1 = (rn_lo * rm_lo);
+                const u32 product2 = (rn_hi * rm_hi);
+
+                // SMUAD and SMLAD
+                if (BIT(6) == 0) {
+                    state->Reg[rd_idx] = product1 + product2;
+
+                    if (BITS(12, 15) != 15) {
+                        state->Reg[rd_idx] += state->Reg[ra_idx];
+                        ARMul_AddOverflowQ(state, product1 + product2, state->Reg[ra_idx]);
+                    }
+
+                    ARMul_AddOverflowQ(state, product1, product2);
                 }
-                // SMLSD
+                // SMUSD and SMLSD
                 else {
-                    state->Reg[rd_idx] = ((rn_lo * rm_lo) - (rn_hi * rm_hi)) + (s32)state->Reg[ra_idx];
+                    state->Reg[rd_idx] = product1 - product2;
+                    
+                    if (BITS(12, 15) != 15)
+                        state->Reg[rd_idx] += state->Reg[ra_idx];
                 }
+
                 return 1;
             }
             break;

+ 8 - 0
src/core/arm/interpreter/armsupp.cpp

@@ -444,6 +444,14 @@ ARMul_AddOverflow (ARMul_State * state, ARMword a, ARMword b, ARMword result)
     ASSIGNV (AddOverflow (a, b, result));
 }
 
+/* Assigns the Q flag if the given result is considered an overflow from the addition of a and b  */
+void ARMul_AddOverflowQ(ARMul_State* state, ARMword a, ARMword b)
+{
+    u32 result = a + b;
+    if (((result ^ a) & (u32)0x80000000) && ((a ^ b) & (u32)0x80000000) == 0)
+        SETQ;
+}
+
 /* Assigns the C flag after an subtraction of a and b to give result.  */
 
 void

+ 1 - 0
src/core/arm/skyeye_common/armemu.h

@@ -602,6 +602,7 @@ extern ARMword ARMul_SwitchMode (ARMul_State *, ARMword, ARMword);
 extern void ARMul_MSRCpsr (ARMul_State *, ARMword, ARMword);
 extern void ARMul_SubOverflow (ARMul_State *, ARMword, ARMword, ARMword);
 extern void ARMul_AddOverflow (ARMul_State *, ARMword, ARMword, ARMword);
+extern void ARMul_AddOverflowQ(ARMul_State*, ARMword, ARMword);
 extern void ARMul_SubCarry (ARMul_State *, ARMword, ARMword, ARMword);
 extern void ARMul_AddCarry (ARMul_State *, ARMword, ARMword, ARMword);
 extern tdstate ARMul_ThumbDecode (ARMul_State *, ARMword, ARMword, ARMword *);