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