CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/MIPS/ARM/ArmCompFPU.cpp
Views: 1401
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/Config.h"
22
#include "Core/MemMap.h"
23
#include "Core/MIPS/MIPS.h"
24
#include "Core/MIPS/MIPSCodeUtils.h"
25
#include "Core/MIPS/MIPSTables.h"
26
27
#include "Core/MIPS/ARM/ArmJit.h"
28
#include "Core/MIPS/ARM/ArmRegCache.h"
29
#include "Common/CPUDetect.h"
30
31
#define _RS MIPS_GET_RS(op)
32
#define _RT MIPS_GET_RT(op)
33
#define _RD MIPS_GET_RD(op)
34
#define _FS MIPS_GET_FS(op)
35
#define _FT MIPS_GET_FT(op)
36
#define _FD MIPS_GET_FD(op)
37
#define _SA MIPS_GET_SA(op)
38
#define _POS ((op>> 6) & 0x1F)
39
#define _SIZE ((op>>11) & 0x1F)
40
#define _IMM16 (signed short)(op & 0xFFFF)
41
#define _IMM26 (op & 0x03FFFFFF)
42
43
// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly.
44
// Currently known non working ones should have DISABLE.
45
46
// #define CONDITIONAL_DISABLE(flag) { Comp_Generic(op); return; }
47
#define CONDITIONAL_DISABLE(flag) if (jo.Disabled(JitDisable::flag)) { Comp_Generic(op); return; }
48
#define DISABLE { Comp_Generic(op); return; }
49
50
namespace MIPSComp
51
{
52
using namespace ArmGen;
53
using namespace ArmJitConstants;
54
55
void ArmJit::Comp_FPU3op(MIPSOpcode op)
56
{
57
CONDITIONAL_DISABLE(FPU);
58
59
int ft = _FT;
60
int fs = _FS;
61
int fd = _FD;
62
63
fpr.MapDirtyInIn(fd, fs, ft);
64
switch (op & 0x3f)
65
{
66
case 0: VADD(fpr.R(fd), fpr.R(fs), fpr.R(ft)); break; //F(fd) = F(fs) + F(ft); //add
67
case 1: VSUB(fpr.R(fd), fpr.R(fs), fpr.R(ft)); break; //F(fd) = F(fs) - F(ft); //sub
68
case 2: { //F(fd) = F(fs) * F(ft); //mul
69
MIPSOpcode nextOp = GetOffsetInstruction(1);
70
// Optimization possible if destination is the same
71
if (fd == (int)((nextOp>>6) & 0x1F)) {
72
// VMUL + VNEG -> VNMUL
73
if (!strcmp(MIPSGetName(nextOp), "neg.s")) {
74
if (fd == (int)((nextOp>>11) & 0x1F)) {
75
VNMUL(fpr.R(fd), fpr.R(fs), fpr.R(ft));
76
EatInstruction(nextOp);
77
}
78
return;
79
}
80
}
81
VMUL(fpr.R(fd), fpr.R(fs), fpr.R(ft));
82
break;
83
}
84
case 3: VDIV(fpr.R(fd), fpr.R(fs), fpr.R(ft)); break; //F(fd) = F(fs) / F(ft); //div
85
default:
86
DISABLE;
87
return;
88
}
89
}
90
91
extern int logBlocks;
92
93
void ArmJit::Comp_FPULS(MIPSOpcode op)
94
{
95
CONDITIONAL_DISABLE(LSU_FPU);
96
CheckMemoryBreakpoint();
97
98
s32 offset = SignExtend16ToS32(op & 0xFFFF);
99
int ft = _FT;
100
MIPSGPReg rs = _RS;
101
// u32 addr = R(rs) + offset;
102
// logBlocks = 1;
103
bool doCheck = false;
104
switch(op >> 26)
105
{
106
case 49: //FI(ft) = Memory::Read_U32(addr); break; //lwc1
107
if (!gpr.IsImm(rs) && jo.cachePointers && g_Config.bFastMemory && (offset & 3) == 0 && offset < 0x400 && offset > -0x400) {
108
gpr.MapRegAsPointer(rs);
109
fpr.MapReg(ft, MAP_NOINIT | MAP_DIRTY);
110
VLDR(fpr.R(ft), gpr.RPtr(rs), offset);
111
break;
112
}
113
114
fpr.SpillLock(ft);
115
fpr.MapReg(ft, MAP_NOINIT | MAP_DIRTY);
116
if (gpr.IsImm(rs)) {
117
u32 addr = (offset + gpr.GetImm(rs)) & 0x3FFFFFFF;
118
gpr.SetRegImm(R0, addr + (u32)Memory::base);
119
} else {
120
gpr.MapReg(rs);
121
if (g_Config.bFastMemory) {
122
SetR0ToEffectiveAddress(rs, offset);
123
} else {
124
SetCCAndR0ForSafeAddress(rs, offset, SCRATCHREG2);
125
doCheck = true;
126
}
127
ADD(R0, R0, MEMBASEREG);
128
}
129
#ifdef __ARM_ARCH_7S__
130
FixupBranch skip;
131
if (doCheck) {
132
skip = B_CC(CC_EQ);
133
}
134
VLDR(fpr.R(ft), R0, 0);
135
if (doCheck) {
136
SetJumpTarget(skip);
137
}
138
#else
139
VLDR(fpr.R(ft), R0, 0);
140
if (doCheck) {
141
SetCC(CC_EQ);
142
MOVI2R(R0, 0);
143
VMOV(fpr.R(ft), R0);
144
SetCC(CC_AL);
145
}
146
#endif
147
fpr.ReleaseSpillLocksAndDiscardTemps();
148
break;
149
150
case 57: //Memory::Write_U32(FI(ft), addr); break; //swc1
151
if (!gpr.IsImm(rs) && jo.cachePointers && g_Config.bFastMemory && (offset & 3) == 0 && offset < 0x400 && offset > -0x400) {
152
gpr.MapRegAsPointer(rs);
153
fpr.MapReg(ft, 0);
154
VSTR(fpr.R(ft), gpr.RPtr(rs), offset);
155
break;
156
}
157
158
fpr.MapReg(ft);
159
if (gpr.IsImm(rs)) {
160
u32 addr = (offset + gpr.GetImm(rs)) & 0x3FFFFFFF;
161
gpr.SetRegImm(R0, addr + (u32)Memory::base);
162
} else {
163
gpr.MapReg(rs);
164
if (g_Config.bFastMemory) {
165
SetR0ToEffectiveAddress(rs, offset);
166
} else {
167
SetCCAndR0ForSafeAddress(rs, offset, SCRATCHREG2);
168
doCheck = true;
169
}
170
ADD(R0, R0, MEMBASEREG);
171
}
172
#ifdef __ARM_ARCH_7S__
173
FixupBranch skip2;
174
if (doCheck) {
175
skip2 = B_CC(CC_EQ);
176
}
177
VSTR(fpr.R(ft), R0, 0);
178
if (doCheck) {
179
SetJumpTarget(skip2);
180
}
181
#else
182
VSTR(fpr.R(ft), R0, 0);
183
if (doCheck) {
184
SetCC(CC_AL);
185
}
186
#endif
187
break;
188
189
default:
190
Comp_Generic(op);
191
return;
192
}
193
}
194
195
void ArmJit::Comp_FPUComp(MIPSOpcode op) {
196
CONDITIONAL_DISABLE(FPU_COMP);
197
198
int opc = op & 0xF;
199
if (opc >= 8) opc -= 8; // alias
200
if (opc == 0) { // f, sf (signalling false)
201
gpr.SetImm(MIPS_REG_FPCOND, 0);
202
return;
203
}
204
205
int fs = _FS;
206
int ft = _FT;
207
gpr.MapReg(MIPS_REG_FPCOND, MAP_DIRTY | MAP_NOINIT);
208
fpr.MapInIn(fs, ft);
209
VCMP(fpr.R(fs), fpr.R(ft));
210
VMRS_APSR(); // Move FP flags from FPSCR to APSR (regular flags).
211
switch(opc)
212
{
213
case 1: // un, ngle (unordered)
214
SetCC(CC_VS);
215
MOVI2R(gpr.R(MIPS_REG_FPCOND), 1);
216
SetCC(CC_VC);
217
break;
218
case 2: // eq, seq (equal, ordered)
219
SetCC(CC_EQ);
220
MOVI2R(gpr.R(MIPS_REG_FPCOND), 1);
221
SetCC(CC_NEQ);
222
break;
223
case 3: // ueq, ngl (equal, unordered)
224
SetCC(CC_EQ);
225
MOVI2R(gpr.R(MIPS_REG_FPCOND), 1);
226
SetCC(CC_NEQ);
227
MOVI2R(gpr.R(MIPS_REG_FPCOND), 0);
228
SetCC(CC_VS);
229
MOVI2R(gpr.R(MIPS_REG_FPCOND), 1);
230
SetCC(CC_AL);
231
return;
232
case 4: // olt, lt (less than, ordered)
233
SetCC(CC_LO);
234
MOVI2R(gpr.R(MIPS_REG_FPCOND), 1);
235
SetCC(CC_HS);
236
break;
237
case 5: // ult, nge (less than, unordered)
238
SetCC(CC_LT);
239
MOVI2R(gpr.R(MIPS_REG_FPCOND), 1);
240
SetCC(CC_GE);
241
break;
242
case 6: // ole, le (less equal, ordered)
243
SetCC(CC_LS);
244
MOVI2R(gpr.R(MIPS_REG_FPCOND), 1);
245
SetCC(CC_HI);
246
break;
247
case 7: // ule, ngt (less equal, unordered)
248
SetCC(CC_LE);
249
MOVI2R(gpr.R(MIPS_REG_FPCOND), 1);
250
SetCC(CC_GT);
251
break;
252
default:
253
Comp_Generic(op);
254
return;
255
}
256
MOVI2R(gpr.R(MIPS_REG_FPCOND), 0);
257
SetCC(CC_AL);
258
}
259
260
void ArmJit::Comp_FPU2op(MIPSOpcode op) {
261
CONDITIONAL_DISABLE(FPU);
262
263
int fs = _FS;
264
int fd = _FD;
265
266
switch (op & 0x3f) {
267
case 4: //F(fd) = sqrtf(F(fs)); break; //sqrt
268
fpr.MapDirtyIn(fd, fs);
269
VSQRT(fpr.R(fd), fpr.R(fs));
270
break;
271
case 5: //F(fd) = fabsf(F(fs)); break; //abs
272
fpr.MapDirtyIn(fd, fs);
273
VABS(fpr.R(fd), fpr.R(fs));
274
break;
275
case 6: //F(fd) = F(fs); break; //mov
276
fpr.MapDirtyIn(fd, fs);
277
VMOV(fpr.R(fd), fpr.R(fs));
278
break;
279
case 7: //F(fd) = -F(fs); break; //neg
280
fpr.MapDirtyIn(fd, fs);
281
VNEG(fpr.R(fd), fpr.R(fs));
282
break;
283
case 12: //FsI(fd) = (int)floorf(F(fs)+0.5f); break; //round.w.s
284
RestoreRoundingMode();
285
fpr.MapDirtyIn(fd, fs);
286
VCVT(fpr.R(fd), fpr.R(fs), TO_INT | IS_SIGNED);
287
break;
288
case 13: //FsI(fd) = Rto0(F(fs))); break; //trunc.w.s
289
fpr.MapDirtyIn(fd, fs);
290
VCMP(fpr.R(fs), fpr.R(fs));
291
VCVT(fpr.R(fd), fpr.R(fs), TO_INT | IS_SIGNED | ROUND_TO_ZERO);
292
VMRS_APSR(); // Move FP flags from FPSCR to APSR (regular flags).
293
SetCC(CC_VS);
294
MOVIU2F(fpr.R(fd), 0x7FFFFFFF, SCRATCHREG1);
295
SetCC(CC_AL);
296
break;
297
case 14: //FsI(fd) = (int)ceilf (F(fs)); break; //ceil.w.s
298
{
299
RestoreRoundingMode();
300
fpr.MapDirtyIn(fd, fs);
301
VMRS(SCRATCHREG2);
302
// Assume we're always in round-to-nearest mode.
303
ORR(SCRATCHREG1, SCRATCHREG2, AssumeMakeOperand2(1 << 22));
304
VMSR(SCRATCHREG1);
305
VCMP(fpr.R(fs), fpr.R(fs));
306
VCVT(fpr.R(fd), fpr.R(fs), TO_INT | IS_SIGNED);
307
VMRS_APSR(); // Move FP flags from FPSCR to APSR (regular flags).
308
SetCC(CC_VS);
309
MOVIU2F(fpr.R(fd), 0x7FFFFFFF, SCRATCHREG1);
310
SetCC(CC_AL);
311
// Set the rounding mode back. TODO: Keep it? Dirty?
312
VMSR(SCRATCHREG2);
313
break;
314
}
315
case 15: //FsI(fd) = (int)floorf(F(fs)); break; //floor.w.s
316
{
317
RestoreRoundingMode();
318
fpr.MapDirtyIn(fd, fs);
319
VMRS(SCRATCHREG2);
320
// Assume we're always in round-to-nearest mode.
321
ORR(SCRATCHREG1, SCRATCHREG2, AssumeMakeOperand2(2 << 22));
322
VMSR(SCRATCHREG1);
323
VCMP(fpr.R(fs), fpr.R(fs));
324
VCVT(fpr.R(fd), fpr.R(fs), TO_INT | IS_SIGNED);
325
VMRS_APSR(); // Move FP flags from FPSCR to APSR (regular flags).
326
SetCC(CC_VS);
327
MOVIU2F(fpr.R(fd), 0x7FFFFFFF, SCRATCHREG1);
328
SetCC(CC_AL);
329
// Set the rounding mode back. TODO: Keep it? Dirty?
330
VMSR(SCRATCHREG2);
331
break;
332
}
333
case 32: //F(fd) = (float)FsI(fs); break; //cvt.s.w
334
fpr.MapDirtyIn(fd, fs);
335
VCVT(fpr.R(fd), fpr.R(fs), TO_FLOAT | IS_SIGNED);
336
break;
337
case 36: //FsI(fd) = (int) F(fs); break; //cvt.w.s
338
fpr.MapDirtyIn(fd, fs);
339
VCMP(fpr.R(fs), fpr.R(fs));
340
VCVT(fpr.R(fd), fpr.R(fs), TO_INT | IS_SIGNED);
341
VMRS_APSR(); // Move FP flags from FPSCR to APSR (regular flags).
342
SetCC(CC_VS);
343
MOVIU2F(fpr.R(fd), 0x7FFFFFFF, SCRATCHREG1);
344
SetCC(CC_AL);
345
break;
346
default:
347
DISABLE;
348
}
349
}
350
351
void ArmJit::Comp_mxc1(MIPSOpcode op)
352
{
353
CONDITIONAL_DISABLE(FPU_XFER);
354
355
int fs = _FS;
356
MIPSGPReg rt = _RT;
357
358
switch ((op >> 21) & 0x1f)
359
{
360
case 0: // R(rt) = FI(fs); break; //mfc1
361
if (rt == MIPS_REG_ZERO) {
362
return;
363
}
364
gpr.MapReg(rt, MAP_DIRTY | MAP_NOINIT);
365
if (fpr.IsMapped(fs)) {
366
VMOV(gpr.R(rt), fpr.R(fs));
367
} else {
368
LDR(gpr.R(rt), CTXREG, fpr.GetMipsRegOffset(fs));
369
}
370
return;
371
372
case 2: //cfc1
373
if (rt == MIPS_REG_ZERO) {
374
return;
375
}
376
if (fs == 31) {
377
if (gpr.IsImm(MIPS_REG_FPCOND)) {
378
gpr.MapReg(rt, MAP_DIRTY | MAP_NOINIT);
379
LDR(gpr.R(rt), CTXREG, offsetof(MIPSState, fcr31));
380
if (gpr.GetImm(MIPS_REG_FPCOND) & 1) {
381
ORI2R(gpr.R(rt), gpr.R(rt), 0x1 << 23, SCRATCHREG2);
382
} else {
383
ANDI2R(gpr.R(rt), gpr.R(rt), ~(0x1 << 23), SCRATCHREG2);
384
}
385
} else {
386
gpr.MapDirtyIn(rt, MIPS_REG_FPCOND);
387
LDR(gpr.R(rt), CTXREG, offsetof(MIPSState, fcr31));
388
#if PPSSPP_ARCH(ARMV7)
389
BFI(gpr.R(rt), gpr.R(MIPS_REG_FPCOND), 23, 1);
390
#else
391
AND(SCRATCHREG1, gpr.R(MIPS_REG_FPCOND), Operand2(1)); // Just in case
392
ANDI2R(gpr.R(rt), gpr.R(rt), ~(0x1 << 23), SCRATCHREG2); // SCRATCHREG2 won't be used, this turns into a simple BIC.
393
ORR(gpr.R(rt), gpr.R(rt), Operand2(SCRATCHREG1, ST_LSL, 23));
394
#endif
395
}
396
} else if (fs == 0) {
397
gpr.SetImm(rt, MIPSState::FCR0_VALUE);
398
} else {
399
// Unsupported regs are always 0.
400
gpr.SetImm(rt, 0);
401
}
402
return;
403
404
case 4: //FI(fs) = R(rt); break; //mtc1
405
if (gpr.IsImm(rt) && gpr.GetImm(rt) == 0) {
406
fpr.MapReg(fs, MAP_NOINIT);
407
MOVI2F(fpr.R(fs), 0.0f, R0);
408
} else {
409
gpr.MapReg(rt);
410
fpr.MapReg(fs, MAP_NOINIT);
411
VMOV(fpr.R(fs), gpr.R(rt));
412
}
413
return;
414
415
case 6: //ctc1
416
if (fs == 31) {
417
// Must clear before setting, since ApplyRoundingMode() assumes it was cleared.
418
RestoreRoundingMode();
419
bool wasImm = gpr.IsImm(rt);
420
u32 immVal = -1;
421
if (wasImm) {
422
immVal = gpr.GetImm(rt);
423
gpr.SetImm(MIPS_REG_FPCOND, (immVal >> 23) & 1);
424
gpr.MapReg(rt);
425
} else {
426
gpr.MapDirtyIn(MIPS_REG_FPCOND, rt);
427
}
428
429
// Update MIPS state
430
// TODO: Technically, should mask by 0x0181FFFF. Maybe just put all of FCR31 in the reg?
431
STR(gpr.R(rt), CTXREG, offsetof(MIPSState, fcr31));
432
if (!wasImm) {
433
#if PPSSPP_ARCH(ARMV7)
434
UBFX(gpr.R(MIPS_REG_FPCOND), gpr.R(rt), 23, 1);
435
#else
436
MOV(SCRATCHREG1, Operand2(gpr.R(rt), ST_LSR, 23));
437
AND(gpr.R(MIPS_REG_FPCOND), SCRATCHREG1, Operand2(1));
438
#endif
439
UpdateRoundingMode();
440
} else {
441
UpdateRoundingMode(immVal);
442
}
443
ApplyRoundingMode();
444
} else {
445
Comp_Generic(op);
446
}
447
return;
448
}
449
}
450
451
} // namespace MIPSComp
452
453
#endif // PPSSPP_ARCH(ARM)
454
455