diff options
Diffstat (limited to 'src/core/arm/interpreter')
-rw-r--r-- | src/core/arm/interpreter/armemu.cpp | 262 | ||||
-rw-r--r-- | src/core/arm/interpreter/armsupp.cpp | 13 |
2 files changed, 197 insertions, 78 deletions
diff --git a/src/core/arm/interpreter/armemu.cpp b/src/core/arm/interpreter/armemu.cpp index 610e04f10..d54dbeac5 100644 --- a/src/core/arm/interpreter/armemu.cpp +++ b/src/core/arm/interpreter/armemu.cpp @@ -1670,7 +1670,7 @@ mainswitch: op1 *= op2; //printf("SMLA_INST:BB,op1=0x%x, op2=0x%x. Rn=0x%x\n", op1, op2, Rn); if (AddOverflow(op1, Rn, op1 + Rn)) - SETS; + SETQ; state->Reg[BITS (16, 19)] = op1 + Rn; break; } @@ -1682,7 +1682,7 @@ mainswitch: ARMword result = op1 + op2; if (AddOverflow(op1, op2, result)) { result = POS (result) ? 0x80000000 : 0x7fffffff; - SETS; + SETQ; } state->Reg[BITS (12, 15)] = result; break; @@ -1724,7 +1724,7 @@ mainswitch: TAKEABORT; } else if ((BITS (0, 11) == 0) && (LHSReg == 15)) { /* MRS CPSR */ UNDEF_MRSPC; - DEST = ECC | EINT | EMODE; + DEST = ARMul_GetCPSR(state); } else { UNDEF_Test; } @@ -1795,7 +1795,7 @@ mainswitch: ARMword Rn = state->Reg[BITS(12, 15)]; if (AddOverflow((ARMword)result, Rn, (ARMword)(result + Rn))) - SETS; + SETQ; result += Rn; } state->Reg[BITS (16, 19)] = (ARMword)result; @@ -1811,7 +1811,7 @@ mainswitch: if (SubOverflow (op1, op2, result)) { result = POS (result) ? 0x80000000 : 0x7fffffff; - SETS; + SETQ; } state->Reg[BITS (12, 15)] = result; @@ -1934,13 +1934,13 @@ mainswitch: if (AddOverflow (op2, op2, op2d)) { - SETS; + SETQ; op2d = POS (op2d) ? 0x80000000 : 0x7fffffff; } result = op1 + op2d; if (AddOverflow(op1, op2d, result)) { - SETS; + SETQ; result = POS (result) ? 0x80000000 : 0x7fffffff; } @@ -2053,13 +2053,13 @@ mainswitch: ARMword result; if (AddOverflow(op2, op2, op2d)) { - SETS; + SETQ; op2d = POS (op2d) ? 0x80000000 : 0x7fffffff; } result = op1 - op2d; if (SubOverflow(op1, op2d, result)) { - SETS; + SETQ; result = POS (result) ? 0x80000000 : 0x7fffffff; } @@ -5863,20 +5863,21 @@ L_stm_s_takeabort: state->Reg[rd_idx] = (lo_result & 0xFFFF) | ((hi_result & 0xFFFF) << 16); if (lo_result >= 0) { - state->Cpsr |= (1 << 16); - state->Cpsr |= (1 << 17); + state->GEFlag |= (1 << 16); + state->GEFlag |= (1 << 17); } else { - state->Cpsr &= ~(1 << 16); - state->Cpsr &= ~(1 << 17); + state->GEFlag &= ~(1 << 16); + state->GEFlag &= ~(1 << 17); } if (hi_result >= 0) { - state->Cpsr |= (1 << 18); - state->Cpsr |= (1 << 19); + state->GEFlag |= (1 << 18); + state->GEFlag |= (1 << 19); } else { - state->Cpsr &= ~(1 << 18); - state->Cpsr &= ~(1 << 19); + state->GEFlag &= ~(1 << 18); + state->GEFlag &= ~(1 << 19); } + return 1; } // SADD8/SSUB8 @@ -5901,24 +5902,24 @@ L_stm_s_takeabort: hi_val2 = (u8)(((rn_val >> 24) & 0xFF) + ((rm_val >> 24) & 0xFF)); if (lo_val1 & 0x80) - state->Cpsr |= (1 << 16); + state->GEFlag |= (1 << 16); else - state->Cpsr &= ~(1 << 16); + state->GEFlag &= ~(1 << 16); if (lo_val2 & 0x80) - state->Cpsr |= (1 << 17); + state->GEFlag |= (1 << 17); else - state->Cpsr &= ~(1 << 17); + state->GEFlag &= ~(1 << 17); if (hi_val1 & 0x80) - state->Cpsr |= (1 << 18); + state->GEFlag |= (1 << 18); else - state->Cpsr &= ~(1 << 18); + state->GEFlag &= ~(1 << 18); if (hi_val2 & 0x80) - state->Cpsr |= (1 << 19); + state->GEFlag |= (1 << 19); else - state->Cpsr &= ~(1 << 19); + state->GEFlag &= ~(1 << 19); } // SSUB8 else { @@ -5928,24 +5929,24 @@ L_stm_s_takeabort: hi_val2 = (u8)(((rn_val >> 24) & 0xFF) - ((rm_val >> 24) & 0xFF)); if (!(lo_val1 & 0x80)) - state->Cpsr |= (1 << 16); + state->GEFlag |= (1 << 16); else - state->Cpsr &= ~(1 << 16); + state->GEFlag &= ~(1 << 16); if (!(lo_val2 & 0x80)) - state->Cpsr |= (1 << 17); + state->GEFlag |= (1 << 17); else - state->Cpsr &= ~(1 << 17); + state->GEFlag &= ~(1 << 17); if (!(hi_val1 & 0x80)) - state->Cpsr |= (1 << 18); + state->GEFlag |= (1 << 18); else - state->Cpsr &= ~(1 << 18); + state->GEFlag &= ~(1 << 18); if (!(hi_val2 & 0x80)) - state->Cpsr |= (1 << 19); + state->GEFlag |= (1 << 19); else - state->Cpsr &= ~(1 << 19); + state->GEFlag &= ~(1 << 19); } state->Reg[rd_idx] = (lo_val1 | lo_val2 << 8 | hi_val1 << 16 | hi_val2 << 24); @@ -6024,15 +6025,32 @@ L_stm_s_takeabort: if ((instr & 0x0F0) == 0x070) { // USUB16 h1 = ((u16)from - (u16)to); h2 = ((u16)(from >> 16) - (u16)(to >> 16)); - if (!(h1 & 0xffff0000)) state->Cpsr |= (3 << 16); - if (!(h2 & 0xffff0000)) state->Cpsr |= (3 << 18); + + if (!(h1 & 0xffff0000)) + state->GEFlag |= (3 << 16); + else + state->GEFlag &= ~(3 << 16); + + if (!(h2 & 0xffff0000)) + state->GEFlag |= (3 << 18); + else + state->GEFlag &= ~(3 << 18); } else { // UADD16 h1 = ((u16)from + (u16)to); h2 = ((u16)(from >> 16) + (u16)(to >> 16)); - if (h1 & 0xffff0000) state->Cpsr |= (3 << 16); - if (h2 & 0xffff0000) state->Cpsr |= (3 << 18); + + if (h1 & 0xffff0000) + state->GEFlag |= (3 << 16); + else + state->GEFlag &= ~(3 << 16); + + if (h2 & 0xffff0000) + state->GEFlag |= (3 << 18); + else + state->GEFlag &= ~(3 << 18); } + state->Reg[rd] = (u32)((h1 & 0xffff) | ((h2 & 0xffff) << 16)); return 1; } @@ -6045,10 +6063,26 @@ L_stm_s_takeabort: b2 = ((u8)(from >> 8) - (u8)(to >> 8)); b3 = ((u8)(from >> 16) - (u8)(to >> 16)); b4 = ((u8)(from >> 24) - (u8)(to >> 24)); - if (!(b1 & 0xffffff00)) state->Cpsr |= (1 << 16); - if (!(b2 & 0xffffff00)) state->Cpsr |= (1 << 17); - if (!(b3 & 0xffffff00)) state->Cpsr |= (1 << 18); - if (!(b4 & 0xffffff00)) state->Cpsr |= (1 << 19); + + if (!(b1 & 0xffffff00)) + state->GEFlag |= (1 << 16); + else + state->GEFlag &= ~(1 << 16); + + if (!(b2 & 0xffffff00)) + state->GEFlag |= (1 << 17); + else + state->GEFlag &= ~(1 << 17); + + if (!(b3 & 0xffffff00)) + state->GEFlag |= (1 << 18); + else + state->GEFlag &= ~(1 << 18); + + if (!(b4 & 0xffffff00)) + state->GEFlag |= (1 << 19); + else + state->GEFlag &= ~(1 << 19); } else { // UADD8 b1 = ((u8)from + (u8)to); @@ -6057,25 +6091,24 @@ L_stm_s_takeabort: b4 = ((u8)(from >> 24) + (u8)(to >> 24)); if (b1 & 0xffffff00) - state->Cpsr |= (1 << 16); + state->GEFlag |= (1 << 16); else - state->Cpsr &= ~(1 << 16); + state->GEFlag &= ~(1 << 16); if (b2 & 0xffffff00) - state->Cpsr |= (1 << 17); + state->GEFlag |= (1 << 17); else - state->Cpsr &= ~(1 << 17); + state->GEFlag &= ~(1 << 17); if (b3 & 0xffffff00) - state->Cpsr |= (1 << 18); + state->GEFlag |= (1 << 18); else - state->Cpsr &= ~(1 << 18); - + state->GEFlag &= ~(1 << 18); if (b4 & 0xffffff00) - state->Cpsr |= (1 << 19); + state->GEFlag |= (1 << 19); else - state->Cpsr &= ~(1 << 19); + state->GEFlag &= ~(1 << 19); } state->Reg[rd] = (u32)(b1 | (b2 & 0xff) << 8 | (b3 & 0xff) << 16 | (b4 & 0xff) << 24); @@ -6116,7 +6149,7 @@ L_stm_s_takeabort: u32 rm = (instr >> 0) & 0xF; u32 from = state->Reg[rn]; u32 to = state->Reg[rm]; - u32 cpsr = state->Cpsr; + u32 cpsr = ARMul_GetCPSR(state); if ((instr & 0xFF0) == 0xFB0) { // SEL u32 result; if (cpsr & (1 << 16)) @@ -6172,15 +6205,21 @@ L_stm_s_takeabort: s16 rn_lo = (state->Reg[rn_idx]); s16 rn_hi = (state->Reg[rn_idx] >> 16); - if (rn_lo > max) + if (rn_lo > max) { rn_lo = max; - else if (rn_lo < min) + SETQ; + } else if (rn_lo < min) { rn_lo = min; + SETQ; + } - if (rn_hi > max) + if (rn_hi > max) { rn_hi = max; - else if (rn_hi < min) + SETQ; + } else if (rn_hi < min) { rn_hi = min; + SETQ; + } state->Reg[rd_idx] = (rn_lo & 0xFFFF) | ((rn_hi & 0xFFFF) << 16); return 1; @@ -6313,16 +6352,22 @@ L_stm_s_takeabort: s16 rn_lo = (state->Reg[rn_idx]); s16 rn_hi = (state->Reg[rn_idx] >> 16); - if (max < rn_lo) + if (max < rn_lo) { rn_lo = max; - else if (rn_lo < 0) + SETQ; + } else if (rn_lo < 0) { rn_lo = 0; + SETQ; + } - if (max < rn_hi) + if (max < rn_hi) { rn_hi = max; - else if (rn_hi < 0) + SETQ; + } else if (rn_hi < 0) { rn_hi = 0; - + SETQ; + } + state->Reg[rd_idx] = (rn_lo & 0xFFFF) | ((rn_hi << 16) & 0xFFFF); return 1; } @@ -6427,30 +6472,95 @@ 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); + 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); } - // SMUSD - else if ((instr & 0xf0d0) == 0xf050) { - state->Reg[rd_idx] = (rn_lo * rm_lo) - (rn_hi * rm_hi); + // SMUSD and SMLSD + else { + state->Reg[rd_idx] = product1 - product2; + + if (BITS(12, 15) != 15) + state->Reg[rd_idx] += state->Reg[ra_idx]; } - // SMLAD - else if ((instr & 0xd0) == 0x10) { - state->Reg[rd_idx] = (rn_lo * rm_lo) + (rn_hi * rm_hi) + (s32)state->Reg[ra_idx]; + + return 1; + } + break; + case 0x74: // SMLALD and SMLSLD + { + const u8 rm_idx = BITS(8, 11); + const u8 rn_idx = BITS(0, 3); + const u8 rdlo_idx = BITS(12, 15); + const u8 rdhi_idx = BITS(16, 19); + const bool do_swap = (BIT(5) == 1); + + const u32 rdlo_val = state->Reg[rdlo_idx]; + const u32 rdhi_val = state->Reg[rdhi_idx]; + const u32 rn_val = state->Reg[rn_idx]; + u32 rm_val = state->Reg[rm_idx]; + + if (do_swap) + rm_val = (((rm_val & 0xFFFF) << 16) | (rm_val >> 16)); + + const s32 product1 = (s16)(rn_val & 0xFFFF) * (s16)(rm_val & 0xFFFF); + const s32 product2 = (s16)((rn_val >> 16) & 0xFFFF) * (s16)((rm_val >> 16) & 0xFFFF); + s64 result; + + // SMLALD + if (BIT(6) == 0) { + result = (product1 + product2) + (s64)(rdlo_val | ((s64)rdhi_val << 32)); } - // SMLSD + // SMLSLD else { - state->Reg[rd_idx] = ((rn_lo * rm_lo) - (rn_hi * rm_hi)) + (s32)state->Reg[ra_idx]; + result = (product1 - product2) + (s64)(rdlo_val | ((s64)rdhi_val << 32)); } + + state->Reg[rdlo_idx] = (result & 0xFFFFFFFF); + state->Reg[rdhi_idx] = ((result >> 32) & 0xFFFFFFFF); return 1; } break; - case 0x74: - printf ("Unhandled v6 insn: smlald/smlsld\n"); - break; - case 0x75: - printf ("Unhandled v6 insn: smmla/smmls/smmul\n"); + case 0x75: // SMMLA, SMMUL, and SMMLS + { + const u8 rm_idx = BITS(8, 11); + const u8 rn_idx = BITS(0, 3); + const u8 ra_idx = BITS(12, 15); + const u8 rd_idx = BITS(16, 19); + const bool do_round = (BIT(5) == 1); + + const u32 rm_val = state->Reg[rm_idx]; + const u32 rn_val = state->Reg[rn_idx]; + + // Assume SMMUL by default. + s64 result = (s64)(s32)rn_val * (s64)(s32)rm_val; + + if (ra_idx != 15) { + const u32 ra_val = state->Reg[ra_idx]; + + // SMMLA, otherwise SMMLS + if (BIT(6) == 0) + result += ((s64)ra_val << 32); + else + result = ((s64)ra_val << 32) - result; + } + + if (do_round) + result += 0x80000000; + + state->Reg[rd_idx] = ((result >> 32) & 0xFFFFFFFF); + return 1; + } break; case 0x78: if (BITS(20, 24) == 0x18) diff --git a/src/core/arm/interpreter/armsupp.cpp b/src/core/arm/interpreter/armsupp.cpp index 30519f216..6774f8a74 100644 --- a/src/core/arm/interpreter/armsupp.cpp +++ b/src/core/arm/interpreter/armsupp.cpp @@ -227,8 +227,9 @@ ARMul_CPSRAltered (ARMul_State * state) //state->Cpsr &= ~CBIT; ASSIGNV ((state->Cpsr & VBIT) != 0); //state->Cpsr &= ~VBIT; - ASSIGNS ((state->Cpsr & SBIT) != 0); - //state->Cpsr &= ~SBIT; + ASSIGNQ ((state->Cpsr & QBIT) != 0); + //state->Cpsr &= ~QBIT; + state->GEFlag = (state->Cpsr & 0x000F0000); #ifdef MODET ASSIGNT ((state->Cpsr & TBIT) != 0); //state->Cpsr &= ~TBIT; @@ -443,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 |