1 // Copyright (c) 2012- PPSSPP Project. 2 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU General Public License as published by 5 // the Free Software Foundation, version 2.0 or later versions. 6 7 // This program is distributed in the hope that it will be useful, 8 // but WITHOUT ANY WARRANTY; without even the implied warranty of 9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 // GNU General Public License 2.0 for more details. 11 12 // A copy of the GPL 2.0 should have been included with the program. 13 // If not, see http://www.gnu.org/licenses/ 14 15 // Official git repository and contact information can be found at 16 // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. 17 18 #include "ppsspp_config.h" 19 #if PPSSPP_ARCH(ARM) 20 21 #include "Core/MemMap.h" 22 #include "Core/Config.h" 23 #include "Core/MIPS/MIPS.h" 24 #include "Core/MIPS/MIPSAnalyst.h" 25 #include "Core/MIPS/MIPSCodeUtils.h" 26 #include "Core/MIPS/ARM/ArmJit.h" 27 #include "Core/MIPS/ARM/ArmRegCache.h" 28 29 #define _RS MIPS_GET_RS(op) 30 #define _RT MIPS_GET_RT(op) 31 #define _RD MIPS_GET_RD(op) 32 #define _FS MIPS_GET_FS(op) 33 #define _FT MIPS_GET_FT(op) 34 #define _FD MIPS_GET_FD(op) 35 #define _SA MIPS_GET_SA(op) 36 #define _POS ((op>> 6) & 0x1F) 37 #define _SIZE ((op>>11) & 0x1F) 38 #define _IMM16 (signed short)(op & 0xFFFF) 39 #define _IMM26 (op & 0x03FFFFFF) 40 41 // All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly. 42 // Currently known non working ones should have DISABLE. 43 44 // #define CONDITIONAL_DISABLE(flag) { Comp_Generic(op); return; } 45 #define CONDITIONAL_DISABLE(flag) if (jo.Disabled(JitDisable::flag)) { Comp_Generic(op); return; } 46 #define DISABLE { Comp_Generic(op); return; } 47 48 namespace MIPSComp 49 { 50 using namespace ArmGen; 51 using namespace ArmJitConstants; 52 SetR0ToEffectiveAddress(MIPSGPReg rs,s16 offset)53 void ArmJit::SetR0ToEffectiveAddress(MIPSGPReg rs, s16 offset) { 54 Operand2 op2; 55 if (offset) { 56 bool negated; 57 if (TryMakeOperand2_AllowNegation(offset, op2, &negated)) { 58 if (!negated) 59 ADD(R0, gpr.R(rs), op2); 60 else 61 SUB(R0, gpr.R(rs), op2); 62 } else { 63 // Try to avoid using MOVT 64 if (offset < 0) { 65 gpr.SetRegImm(R0, (u32)(-offset)); 66 SUB(R0, gpr.R(rs), R0); 67 } else { 68 gpr.SetRegImm(R0, (u32)offset); 69 ADD(R0, gpr.R(rs), R0); 70 } 71 } 72 BIC(R0, R0, Operand2(0xC0, 4)); // &= 0x3FFFFFFF 73 } else { 74 BIC(R0, gpr.R(rs), Operand2(0xC0, 4)); // &= 0x3FFFFFFF 75 } 76 } 77 SetCCAndR0ForSafeAddress(MIPSGPReg rs,s16 offset,ARMReg tempReg,bool reverse)78 void ArmJit::SetCCAndR0ForSafeAddress(MIPSGPReg rs, s16 offset, ARMReg tempReg, bool reverse) { 79 SetR0ToEffectiveAddress(rs, offset); 80 81 // There are three valid ranges. Each one gets a bit. 82 const u32 BIT_SCRATCH = 1, BIT_RAM = 2, BIT_VRAM = 4; 83 MOVI2R(tempReg, BIT_SCRATCH | BIT_RAM | BIT_VRAM); 84 85 CMP(R0, AssumeMakeOperand2(PSP_GetScratchpadMemoryBase())); 86 SetCC(CC_LO); 87 BIC(tempReg, tempReg, BIT_SCRATCH); 88 SetCC(CC_HS); 89 CMP(R0, AssumeMakeOperand2(PSP_GetScratchpadMemoryEnd())); 90 BIC(tempReg, tempReg, BIT_SCRATCH); 91 92 // If it was in that range, later compares don't matter. 93 CMP(R0, AssumeMakeOperand2(PSP_GetVidMemBase())); 94 SetCC(CC_LO); 95 BIC(tempReg, tempReg, BIT_VRAM); 96 SetCC(CC_HS); 97 CMP(R0, AssumeMakeOperand2(PSP_GetVidMemEnd())); 98 BIC(tempReg, tempReg, BIT_VRAM); 99 100 CMP(R0, AssumeMakeOperand2(PSP_GetKernelMemoryBase())); 101 SetCC(CC_LO); 102 BIC(tempReg, tempReg, BIT_RAM); 103 SetCC(CC_HS); 104 CMP(R0, AssumeMakeOperand2(PSP_GetUserMemoryEnd())); 105 BIC(tempReg, tempReg, BIT_RAM); 106 107 // If we left any bit set, the address is OK. 108 SetCC(CC_AL); 109 CMP(tempReg, 0); 110 SetCC(reverse ? CC_EQ : CC_GT); 111 } 112 Comp_ITypeMemLR(MIPSOpcode op,bool load)113 void ArmJit::Comp_ITypeMemLR(MIPSOpcode op, bool load) { 114 CONDITIONAL_DISABLE(LSU); 115 CheckMemoryBreakpoint(); 116 int offset = (signed short)(op & 0xFFFF); 117 MIPSGPReg rt = _RT; 118 MIPSGPReg rs = _RS; 119 int o = op >> 26; 120 121 if (!js.inDelaySlot && !jo.Disabled(JitDisable::LSU_UNALIGNED)) { 122 // Optimisation: Combine to single unaligned load/store 123 bool isLeft = (o == 34 || o == 42); 124 CheckMemoryBreakpoint(1); 125 MIPSOpcode nextOp = GetOffsetInstruction(1); 126 // Find a matching shift in opposite direction with opposite offset. 127 if (nextOp == (isLeft ? (op.encoding + (4<<26) - 3) 128 : (op.encoding - (4<<26) + 3))) 129 { 130 EatInstruction(nextOp); 131 nextOp = MIPSOpcode(((load ? 35 : 43) << 26) | ((isLeft ? nextOp : op) & 0x03FFFFFF)); //lw, sw 132 Comp_ITypeMem(nextOp); 133 return; 134 } 135 } 136 137 u32 iaddr = gpr.IsImm(rs) ? offset + gpr.GetImm(rs) : 0xFFFFFFFF; 138 bool doCheck = false; 139 FixupBranch skip; 140 141 if (gpr.IsImm(rs) && Memory::IsValidAddress(iaddr)) { 142 u32 addr = iaddr & 0x3FFFFFFF; 143 // Need to initialize since this only loads part of the register. 144 // But rs no longer matters (even if rs == rt) since we have the address. 145 gpr.MapReg(rt, load ? MAP_DIRTY : 0); 146 gpr.SetRegImm(R0, addr & ~3); 147 148 u8 shift = (addr & 3) * 8; 149 150 switch (o) { 151 case 34: // lwl 152 LDR(R0, MEMBASEREG, R0); 153 ANDI2R(gpr.R(rt), gpr.R(rt), 0x00ffffff >> shift, SCRATCHREG2); 154 ORR(gpr.R(rt), gpr.R(rt), Operand2(R0, ST_LSL, 24 - shift)); 155 break; 156 157 case 38: // lwr 158 LDR(R0, MEMBASEREG, R0); 159 ANDI2R(gpr.R(rt), gpr.R(rt), 0xffffff00 << (24 - shift), SCRATCHREG2); 160 ORR(gpr.R(rt), gpr.R(rt), Operand2(R0, ST_LSR, shift)); 161 break; 162 163 case 42: // swl 164 LDR(SCRATCHREG2, MEMBASEREG, R0); 165 // Don't worry, can't use temporary. 166 ANDI2R(SCRATCHREG2, SCRATCHREG2, 0xffffff00 << shift, R0); 167 ORR(SCRATCHREG2, SCRATCHREG2, Operand2(gpr.R(rt), ST_LSR, 24 - shift)); 168 STR(SCRATCHREG2, MEMBASEREG, R0); 169 break; 170 171 case 46: // swr 172 LDR(SCRATCHREG2, MEMBASEREG, R0); 173 // Don't worry, can't use temporary. 174 ANDI2R(SCRATCHREG2, SCRATCHREG2, 0x00ffffff >> (24 - shift), R0); 175 ORR(SCRATCHREG2, SCRATCHREG2, Operand2(gpr.R(rt), ST_LSL, shift)); 176 STR(SCRATCHREG2, MEMBASEREG, R0); 177 break; 178 } 179 return; 180 } 181 182 // This gets hit in a few games, as a result of never-taken delay slots (some branch types 183 // conditionally execute the delay slot instructions). Ignore in those cases. 184 if (!js.inDelaySlot) { 185 _dbg_assert_msg_(!gpr.IsImm(rs), "Invalid immediate address %08x? CPU bug?", iaddr); 186 } 187 188 if (load) { 189 gpr.MapDirtyIn(rt, rs, false); 190 } else { 191 gpr.MapInIn(rt, rs); 192 } 193 194 if (!g_Config.bFastMemory && rs != MIPS_REG_SP) { 195 SetCCAndR0ForSafeAddress(rs, offset, SCRATCHREG2, true); 196 doCheck = true; 197 } else { 198 SetR0ToEffectiveAddress(rs, offset); 199 } 200 if (doCheck) { 201 skip = B(); 202 } 203 SetCC(CC_AL); 204 205 // Need temp regs. TODO: Get from the regcache? 206 static const ARMReg LR_SCRATCHREG3 = R9; 207 static const ARMReg LR_SCRATCHREG4 = R10; 208 if (load) { 209 PUSH(1, LR_SCRATCHREG3); 210 } else { 211 PUSH(2, LR_SCRATCHREG3, LR_SCRATCHREG4); 212 } 213 214 // Here's our shift amount. 215 AND(SCRATCHREG2, R0, 3); 216 LSL(SCRATCHREG2, SCRATCHREG2, 3); 217 218 // Now align the address for the actual read. 219 BIC(R0, R0, 3); 220 221 switch (o) { 222 case 34: // lwl 223 MOVI2R(LR_SCRATCHREG3, 0x00ffffff); 224 LDR(R0, MEMBASEREG, R0); 225 AND(gpr.R(rt), gpr.R(rt), Operand2(LR_SCRATCHREG3, ST_LSR, SCRATCHREG2)); 226 RSB(SCRATCHREG2, SCRATCHREG2, 24); 227 ORR(gpr.R(rt), gpr.R(rt), Operand2(R0, ST_LSL, SCRATCHREG2)); 228 break; 229 230 case 38: // lwr 231 MOVI2R(LR_SCRATCHREG3, 0xffffff00); 232 LDR(R0, MEMBASEREG, R0); 233 LSR(R0, R0, SCRATCHREG2); 234 RSB(SCRATCHREG2, SCRATCHREG2, 24); 235 AND(gpr.R(rt), gpr.R(rt), Operand2(LR_SCRATCHREG3, ST_LSL, SCRATCHREG2)); 236 ORR(gpr.R(rt), gpr.R(rt), R0); 237 break; 238 239 case 42: // swl 240 MOVI2R(LR_SCRATCHREG3, 0xffffff00); 241 LDR(LR_SCRATCHREG4, MEMBASEREG, R0); 242 AND(LR_SCRATCHREG4, LR_SCRATCHREG4, Operand2(LR_SCRATCHREG3, ST_LSL, SCRATCHREG2)); 243 RSB(SCRATCHREG2, SCRATCHREG2, 24); 244 ORR(LR_SCRATCHREG4, LR_SCRATCHREG4, Operand2(gpr.R(rt), ST_LSR, SCRATCHREG2)); 245 STR(LR_SCRATCHREG4, MEMBASEREG, R0); 246 break; 247 248 case 46: // swr 249 MOVI2R(LR_SCRATCHREG3, 0x00ffffff); 250 LDR(LR_SCRATCHREG4, MEMBASEREG, R0); 251 RSB(SCRATCHREG2, SCRATCHREG2, 24); 252 AND(LR_SCRATCHREG4, LR_SCRATCHREG4, Operand2(LR_SCRATCHREG3, ST_LSR, SCRATCHREG2)); 253 RSB(SCRATCHREG2, SCRATCHREG2, 24); 254 ORR(LR_SCRATCHREG4, LR_SCRATCHREG4, Operand2(gpr.R(rt), ST_LSL, SCRATCHREG2)); 255 STR(LR_SCRATCHREG4, MEMBASEREG, R0); 256 break; 257 } 258 259 if (load) { 260 POP(1, LR_SCRATCHREG3); 261 } else { 262 POP(2, LR_SCRATCHREG3, LR_SCRATCHREG4); 263 } 264 265 if (doCheck) { 266 SetJumpTarget(skip); 267 } 268 } 269 Comp_ITypeMem(MIPSOpcode op)270 void ArmJit::Comp_ITypeMem(MIPSOpcode op) 271 { 272 CONDITIONAL_DISABLE(LSU); 273 CheckMemoryBreakpoint(); 274 int offset = (signed short)(op&0xFFFF); 275 bool load = false; 276 MIPSGPReg rt = _RT; 277 MIPSGPReg rs = _RS; 278 int o = op>>26; 279 if (((op >> 29) & 1) == 0 && rt == MIPS_REG_ZERO) { 280 // Don't load anything into $zr 281 return; 282 } 283 284 u32 iaddr = gpr.IsImm(rs) ? offset + gpr.GetImm(rs) : 0xFFFFFFFF; 285 bool doCheck = false; 286 ARMReg addrReg = R0; 287 288 switch (o) { 289 case 32: //lb 290 case 33: //lh 291 case 35: //lw 292 case 36: //lbu 293 case 37: //lhu 294 load = true; 295 case 40: //sb 296 case 41: //sh 297 case 43: //sw 298 // Map base register as pointer and go from there - if the displacement isn't too big. 299 // This is faster if there are multiple loads from the same pointer. Need to hook up the MIPS analyzer.. 300 if (jo.cachePointers && g_Config.bFastMemory) { 301 // ARM has smaller load/store immediate displacements than MIPS, 12 bits - and some memory ops only have 8 bits. 302 int offsetRange = 0x3ff; 303 if (o == 41 || o == 33 || o == 37 || o == 32) 304 offsetRange = 0xff; // 8 bit offset only 305 if (!gpr.IsImm(rs) && rs != rt && (offset <= offsetRange) && offset >= -offsetRange) { 306 gpr.SpillLock(rs, rt); 307 gpr.MapRegAsPointer(rs); 308 gpr.MapReg(rt, load ? MAP_NOINIT : 0); 309 switch (o) { 310 case 35: LDR (gpr.R(rt), gpr.RPtr(rs), Operand2(offset, TYPE_IMM)); break; 311 case 37: LDRH (gpr.R(rt), gpr.RPtr(rs), Operand2(offset, TYPE_IMM)); break; 312 case 33: LDRSH(gpr.R(rt), gpr.RPtr(rs), Operand2(offset, TYPE_IMM)); break; 313 case 36: LDRB (gpr.R(rt), gpr.RPtr(rs), Operand2(offset, TYPE_IMM)); break; 314 case 32: LDRSB(gpr.R(rt), gpr.RPtr(rs), Operand2(offset, TYPE_IMM)); break; 315 // Store 316 case 43: STR (gpr.R(rt), gpr.RPtr(rs), Operand2(offset, TYPE_IMM)); break; 317 case 41: STRH (gpr.R(rt), gpr.RPtr(rs), Operand2(offset, TYPE_IMM)); break; 318 case 40: STRB (gpr.R(rt), gpr.RPtr(rs), Operand2(offset, TYPE_IMM)); break; 319 } 320 gpr.ReleaseSpillLocks(); 321 break; 322 } 323 } 324 325 if (gpr.IsImm(rs) && Memory::IsValidAddress(iaddr)) { 326 // TODO: Avoid mapping a register for the "zero" register, use R0 instead. 327 328 // We can compute the full address at compile time. Kickass. 329 u32 addr = iaddr & 0x3FFFFFFF; 330 331 if (addr == iaddr && offset == 0) { 332 // It was already safe. Let's shove it into a reg and use it directly. 333 load ? gpr.MapDirtyIn(rt, rs) : gpr.MapInIn(rt, rs); 334 addrReg = gpr.R(rs); 335 } else { 336 // In this case, only map rt. rs+offset will be in R0. 337 gpr.MapReg(rt, load ? MAP_NOINIT : 0); 338 gpr.SetRegImm(R0, addr); 339 addrReg = R0; 340 } 341 } else { 342 _dbg_assert_msg_(!gpr.IsImm(rs), "Invalid immediate address? CPU bug?"); 343 load ? gpr.MapDirtyIn(rt, rs) : gpr.MapInIn(rt, rs); 344 345 if (!g_Config.bFastMemory && rs != MIPS_REG_SP) { 346 SetCCAndR0ForSafeAddress(rs, offset, SCRATCHREG2); 347 doCheck = true; 348 } else { 349 SetR0ToEffectiveAddress(rs, offset); 350 } 351 addrReg = R0; 352 } 353 354 switch (o) 355 { 356 // Load 357 case 35: LDR (gpr.R(rt), MEMBASEREG, addrReg); break; 358 case 37: LDRH (gpr.R(rt), MEMBASEREG, addrReg); break; 359 case 33: LDRSH(gpr.R(rt), MEMBASEREG, addrReg); break; 360 case 36: LDRB (gpr.R(rt), MEMBASEREG, addrReg); break; 361 case 32: LDRSB(gpr.R(rt), MEMBASEREG, addrReg); break; 362 // Store 363 case 43: STR (gpr.R(rt), MEMBASEREG, addrReg); break; 364 case 41: STRH (gpr.R(rt), MEMBASEREG, addrReg); break; 365 case 40: STRB (gpr.R(rt), MEMBASEREG, addrReg); break; 366 } 367 if (doCheck) { 368 if (load) { 369 SetCC(CC_EQ); 370 MOVI2R(gpr.R(rt), 0); 371 } 372 SetCC(CC_AL); 373 } 374 break; 375 case 34: //lwl 376 case 38: //lwr 377 load = true; 378 case 42: //swl 379 case 46: //swr 380 Comp_ITypeMemLR(op, load); 381 break; 382 default: 383 Comp_Generic(op); 384 return; 385 } 386 } 387 Comp_Cache(MIPSOpcode op)388 void ArmJit::Comp_Cache(MIPSOpcode op) { 389 CONDITIONAL_DISABLE(LSU); 390 391 int func = (op >> 16) & 0x1F; 392 393 // See Int_Cache for the definitions. 394 switch (func) { 395 case 24: break; 396 case 25: break; 397 case 27: break; 398 case 30: break; 399 default: 400 // Fall back to the interpreter. 401 DISABLE; 402 } 403 } 404 } 405 406 #endif // PPSSPP_ARCH(ARM) 407