Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/MIPS/MIPSInt.cpp
5665 views
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 <cmath>
19
20
#include "Common/Data/Convert/SmallDataConvert.h"
21
#include "Common/Math/math_util.h"
22
23
#include "Common/BitSet.h"
24
#include "Common/BitScan.h"
25
#include "Common/CommonTypes.h"
26
#include "Core/Config.h"
27
#include "Core/Core.h"
28
#include "Core/MemMap.h"
29
#include "Core/MIPS/MIPS.h"
30
#include "Core/MIPS/MIPSCodeUtils.h"
31
#include "Core/MIPS/MIPSInt.h"
32
#include "Core/MIPS/MIPSTables.h"
33
#include "Core/Reporting.h"
34
#include "Core/HLE/HLE.h"
35
#include "Core/HLE/HLETables.h"
36
#include "Core/HLE/ReplaceTables.h"
37
38
#define R(i) (currentMIPS->r[i])
39
#define F(i) (currentMIPS->f[i])
40
#define FI(i) (currentMIPS->fi[i])
41
#define FsI(i) (currentMIPS->fs[i])
42
#define PC (currentMIPS->pc)
43
44
#define _SIMM16_SHL2 ((u32)(s32)(s16)(op & 0xFFFF) << 2)
45
#define _RS ((op>>21) & 0x1F)
46
#define _RT ((op>>16) & 0x1F)
47
#define _RD ((op>>11) & 0x1F)
48
#define _FS ((op>>11) & 0x1F)
49
#define _FT ((op>>16) & 0x1F)
50
#define _FD ((op>>6 ) & 0x1F)
51
#define _POS ((op>>6 ) & 0x1F)
52
#define _SIZE ((op>>11) & 0x1F)
53
54
#define HI currentMIPS->hi
55
#define LO currentMIPS->lo
56
57
static inline void DelayBranchTo(u32 where)
58
{
59
if (!Memory::IsValidAddress(where) || (where & 3) != 0) {
60
Core_ExecException(where, PC, ExecExceptionType::JUMP);
61
}
62
PC += 4;
63
mipsr4k.nextPC = where;
64
mipsr4k.inDelaySlot = true;
65
}
66
67
static inline void SkipLikely() {
68
MIPSInfo delaySlot = MIPSGetInfo(Memory::Read_Instruction(PC + 4, true));
69
// Don't actually skip if it is a jump (seen in Brooktown High.)
70
if (delaySlot & IS_JUMP) {
71
PC += 4;
72
} else {
73
PC += 8;
74
--mipsr4k.downcount;
75
}
76
}
77
78
int MIPS_SingleStep()
79
{
80
MIPSOpcode op = Memory::Read_Opcode_JIT(mipsr4k.pc);
81
if (mipsr4k.inDelaySlot) {
82
MIPSInterpret(op);
83
if (mipsr4k.inDelaySlot) {
84
mipsr4k.pc = mipsr4k.nextPC;
85
mipsr4k.inDelaySlot = false;
86
}
87
} else {
88
MIPSInterpret(op);
89
}
90
return 1;
91
}
92
93
namespace MIPSInt
94
{
95
void Int_Cache(MIPSOpcode op)
96
{
97
int imm = SignExtend16ToS32(op);
98
int rs = _RS;
99
uint32_t addr = R(rs) + imm;
100
int func = (op >> 16) & 0x1F;
101
102
// Let's only report this once per run to be safe from impacting perf.
103
static bool loggedAlignment = false;
104
105
// It appears that a cache line is 0x40 (64) bytes, loops in games
106
// issue the cache instruction at that interval.
107
108
// These codes might be PSP-specific, they don't match regular MIPS cache codes very well
109
110
// NOTE: If you add support for more, make sure they are handled in the various Jit::Comp_Cache.
111
switch (func) {
112
// Icache
113
case 8:
114
// Invalidate the instruction cache at this address.
115
// We assume the CPU won't be reset during this, so no locking.
116
if (MIPSComp::jit) {
117
// Let's over invalidate to be super safe.
118
uint32_t alignedAddr = addr & ~0x3F;
119
int size = 0x40 + (addr & 0x3F);
120
MIPSComp::jit->InvalidateCacheAt(alignedAddr, size);
121
// Using a bool to avoid locking/etc. in case it's slow.
122
if (!loggedAlignment && (addr & 0x3F) != 0) {
123
// These are seen exclusively in Lego games, and are really no big deal. Reporting removed.
124
WARN_LOG(Log::JIT, "Unaligned icache invalidation of %08x (%08x + %d) at PC=%08x", addr, R(rs), imm, PC);
125
loggedAlignment = true;
126
}
127
if (alignedAddr <= PC + 4 && alignedAddr + size >= PC - 4) {
128
// This is probably rare so we don't use a static bool.
129
WARN_LOG_REPORT_ONCE(icacheInvalidatePC, Log::JIT, "Invalidating address near PC: %08x (%08x + %d) at PC=%08x", addr, R(rs), imm, PC);
130
}
131
}
132
break;
133
134
// Dcache
135
case 24:
136
// "Create Dirty Exclusive" - for avoiding a cacheline fill before writing to it.
137
// Will cause garbage on the real machine so we just ignore it, the app will overwrite the cacheline.
138
break;
139
case 25: // Hit Invalidate - zaps the line if present in cache. Should not writeback???? scary.
140
// No need to do anything.
141
break;
142
case 27: // D-cube. Hit Writeback Invalidate. Tony Hawk Underground 2
143
break;
144
case 30: // GTA LCS, a lot. Fill (prefetch). Tony Hawk Underground 2
145
break;
146
147
default:
148
DEBUG_LOG(Log::CPU, "cache instruction affecting %08x : function %i", addr, func);
149
}
150
151
PC += 4;
152
}
153
154
void Int_Syscall(MIPSOpcode op)
155
{
156
// Need to pre-move PC, as CallSyscall may result in a rescheduling!
157
// To do this neater, we'll need a little generated kernel loop that syscall can jump to and then RFI from
158
// but I don't see a need to bother.
159
if (mipsr4k.inDelaySlot)
160
{
161
mipsr4k.pc = mipsr4k.nextPC;
162
}
163
else
164
{
165
mipsr4k.pc += 4;
166
}
167
mipsr4k.inDelaySlot = false;
168
CallSyscall(op);
169
}
170
171
void Int_Sync(MIPSOpcode op)
172
{
173
//DEBUG_LOG(Log::CPU, "sync");
174
PC += 4;
175
}
176
177
void Int_Break(MIPSOpcode op)
178
{
179
Reporting::ReportMessage("BREAK instruction hit");
180
Core_BreakException(PC);
181
PC += 4;
182
}
183
184
void Int_RelBranch(MIPSOpcode op)
185
{
186
int imm = _SIMM16_SHL2;
187
int rs = _RS;
188
int rt = _RT;
189
u32 addr = PC + imm + 4;
190
191
switch (op >> 26)
192
{
193
case 4: if (R(rt) == R(rs)) DelayBranchTo(addr); else PC += 4; break; //beq
194
case 5: if (R(rt) != R(rs)) DelayBranchTo(addr); else PC += 4; break; //bne
195
case 6: if ((s32)R(rs) <= 0) DelayBranchTo(addr); else PC += 4; break; //blez
196
case 7: if ((s32)R(rs) > 0) DelayBranchTo(addr); else PC += 4; break; //bgtz
197
198
case 20: if (R(rt) == R(rs)) DelayBranchTo(addr); else SkipLikely(); break; //beql
199
case 21: if (R(rt) != R(rs)) DelayBranchTo(addr); else SkipLikely(); break; //bnel
200
case 22: if ((s32)R(rs) <= 0) DelayBranchTo(addr); else SkipLikely(); break; //blezl
201
case 23: if ((s32)R(rs) > 0) DelayBranchTo(addr); else SkipLikely(); break; //bgtzl
202
203
default:
204
_dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted");
205
break;
206
}
207
}
208
209
void Int_RelBranchRI(MIPSOpcode op)
210
{
211
int imm = _SIMM16_SHL2;
212
int rs = _RS;
213
u32 addr = PC + imm + 4;
214
215
switch ((op>>16) & 0x1F)
216
{
217
case 0: if ((s32)R(rs) < 0) DelayBranchTo(addr); else PC += 4; break;//bltz
218
case 1: if ((s32)R(rs) >= 0) DelayBranchTo(addr); else PC += 4; break;//bgez
219
case 2: if ((s32)R(rs) < 0) DelayBranchTo(addr); else SkipLikely(); break;//bltzl
220
case 3: if ((s32)R(rs) >= 0) DelayBranchTo(addr); else SkipLikely(); break;//bgezl
221
case 16: R(MIPS_REG_RA) = PC + 8; if ((s32)R(rs) < 0) DelayBranchTo(addr); else PC += 4; break;//bltzal
222
case 17: R(MIPS_REG_RA) = PC + 8; if ((s32)R(rs) >= 0) DelayBranchTo(addr); else PC += 4; break;//bgezal
223
case 18: R(MIPS_REG_RA) = PC + 8; if ((s32)R(rs) < 0) DelayBranchTo(addr); else SkipLikely(); break;//bltzall
224
case 19: R(MIPS_REG_RA) = PC + 8; if ((s32)R(rs) >= 0) DelayBranchTo(addr); else SkipLikely(); break;//bgezall
225
default:
226
_dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted");
227
break;
228
}
229
}
230
231
232
void Int_VBranch(MIPSOpcode op)
233
{
234
int imm = _SIMM16_SHL2;
235
u32 addr = PC + imm + 4;
236
237
// x, y, z, w, any, all, (invalid), (invalid)
238
int imm3 = (op>>18)&7;
239
int val = (currentMIPS->vfpuCtrl[VFPU_CTRL_CC] >> imm3) & 1;
240
241
switch ((op >> 16) & 3)
242
{
243
case 0: if (!val) DelayBranchTo(addr); else PC += 4; break; //bvf
244
case 1: if ( val) DelayBranchTo(addr); else PC += 4; break; //bvt
245
case 2: if (!val) DelayBranchTo(addr); else SkipLikely(); break; //bvfl
246
case 3: if ( val) DelayBranchTo(addr); else SkipLikely(); break; //bvtl
247
}
248
}
249
250
void Int_FPUBranch(MIPSOpcode op)
251
{
252
int imm = _SIMM16_SHL2;
253
u32 addr = PC + imm + 4;
254
switch((op>>16)&0x1f)
255
{
256
case 0: if (!currentMIPS->fpcond) DelayBranchTo(addr); else PC += 4; break;//bc1f
257
case 1: if ( currentMIPS->fpcond) DelayBranchTo(addr); else PC += 4; break;//bc1t
258
case 2: if (!currentMIPS->fpcond) DelayBranchTo(addr); else SkipLikely(); break;//bc1fl
259
case 3: if ( currentMIPS->fpcond) DelayBranchTo(addr); else SkipLikely(); break;//bc1tl
260
default:
261
_dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted");
262
break;
263
}
264
}
265
266
void Int_JumpType(MIPSOpcode op)
267
{
268
if (mipsr4k.inDelaySlot)
269
ERROR_LOG(Log::CPU, "Jump in delay slot :(");
270
271
u32 off = ((op & 0x03FFFFFF) << 2);
272
u32 addr = (currentMIPS->pc & 0xF0000000) | off;
273
274
switch (op>>26)
275
{
276
case 2: //j
277
if (!mipsr4k.inDelaySlot)
278
DelayBranchTo(addr);
279
break;
280
case 3: //jal
281
R(MIPS_REG_RA) = PC + 8;
282
if (!mipsr4k.inDelaySlot)
283
DelayBranchTo(addr);
284
break;
285
default:
286
_dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted");
287
break;
288
}
289
}
290
291
void Int_JumpRegType(MIPSOpcode op)
292
{
293
if (mipsr4k.inDelaySlot)
294
{
295
// There's one of these in Star Soldier at 0881808c, which seems benign.
296
ERROR_LOG(Log::CPU, "Jump in delay slot :(");
297
}
298
299
int rs = _RS;
300
int rd = _RD;
301
u32 addr = R(rs);
302
switch (op & 0x3f)
303
{
304
case 8: //jr
305
if (!mipsr4k.inDelaySlot)
306
DelayBranchTo(addr);
307
break;
308
case 9: //jalr
309
if (rd != 0)
310
R(rd) = PC + 8;
311
// Update rd, but otherwise do not take the branch if we're branching.
312
if (!mipsr4k.inDelaySlot)
313
DelayBranchTo(addr);
314
break;
315
}
316
}
317
318
void Int_IType(MIPSOpcode op)
319
{
320
u32 uimm = op & 0xFFFF;
321
u32 suimm = SignExtend16ToU32(op);
322
s32 simm = SignExtend16ToS32(op);
323
324
int rt = _RT;
325
int rs = _RS;
326
327
if (rt == 0) { //destination register is zero register
328
PC += 4;
329
return; //nop
330
}
331
332
switch (op>>26)
333
{
334
case 8: R(rt) = R(rs) + simm; break; //addi
335
case 9: R(rt) = R(rs) + simm; break; //addiu
336
case 10: R(rt) = (s32)R(rs) < simm; break; //slti
337
case 11: R(rt) = R(rs) < suimm; break; //sltiu
338
case 12: R(rt) = R(rs) & uimm; break; //andi
339
case 13: R(rt) = R(rs) | uimm; break; //ori
340
case 14: R(rt) = R(rs) ^ uimm; break; //xori
341
case 15: R(rt) = uimm << 16; break; //lui
342
default:
343
_dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted");
344
break;
345
}
346
PC += 4;
347
}
348
349
void Int_StoreSync(MIPSOpcode op)
350
{
351
int imm = (signed short)(op&0xFFFF);
352
int rt = _RT;
353
int rs = _RS;
354
u32 addr = R(rs) + imm;
355
356
switch (op >> 26)
357
{
358
case 48: // ll
359
if (rt != 0) {
360
R(rt) = Memory::Read_U32(addr);
361
}
362
currentMIPS->llBit = 1;
363
break;
364
case 56: // sc
365
if (currentMIPS->llBit) {
366
Memory::Write_U32(R(rt), addr);
367
if (rt != 0) {
368
R(rt) = 1;
369
}
370
} else if (rt != 0) {
371
R(rt) = 0;
372
}
373
break;
374
default:
375
_dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted");
376
break;
377
}
378
PC += 4;
379
}
380
381
382
void Int_RType3(MIPSOpcode op)
383
{
384
int rt = _RT;
385
int rs = _RS;
386
int rd = _RD;
387
388
// Don't change $zr.
389
if (rd == 0)
390
{
391
PC += 4;
392
return;
393
}
394
395
switch (op & 63)
396
{
397
case 10: if (R(rt) == 0) R(rd) = R(rs); break; //movz
398
case 11: if (R(rt) != 0) R(rd) = R(rs); break; //movn
399
case 32: R(rd) = R(rs) + R(rt); break; //add (exception on overflow)
400
case 33: R(rd) = R(rs) + R(rt); break; //addu
401
case 34: R(rd) = R(rs) - R(rt); break; //sub (exception on overflow)
402
case 35: R(rd) = R(rs) - R(rt); break; //subu
403
case 36: R(rd) = R(rs) & R(rt); break; //and
404
case 37: R(rd) = R(rs) | R(rt); break; //or
405
case 38: R(rd) = R(rs) ^ R(rt); break; //xor
406
case 39: R(rd) = ~(R(rs) | R(rt)); break; //nor
407
case 42: R(rd) = (s32)R(rs) < (s32)R(rt); break; //slt
408
case 43: R(rd) = R(rs) < R(rt); break; //sltu
409
case 44: R(rd) = ((s32)R(rs) > (s32)R(rt)) ? R(rs) : R(rt); break; //max
410
case 45: R(rd) = ((s32)R(rs) < (s32)R(rt)) ? R(rs) : R(rt); break;//min
411
default:
412
_dbg_assert_msg_( 0, "Unknown MIPS instruction %08x", op.encoding);
413
break;
414
}
415
PC += 4;
416
}
417
418
419
void Int_ITypeMem(MIPSOpcode op)
420
{
421
int imm = (signed short)(op&0xFFFF);
422
int rt = _RT;
423
int rs = _RS;
424
u32 addr = R(rs) + imm;
425
426
if (((op >> 29) & 1) == 0 && rt == 0) {
427
// Don't load anything into $zr
428
PC += 4;
429
return;
430
}
431
432
switch (op >> 26)
433
{
434
case 32: R(rt) = SignExtend8ToU32(Memory::Read_U8(addr)); break; //lb
435
case 33: R(rt) = SignExtend16ToU32(Memory::Read_U16(addr)); break; //lh
436
case 35: R(rt) = Memory::Read_U32(addr); break; //lw
437
case 36: R(rt) = Memory::Read_U8 (addr); break; //lbu
438
case 37: R(rt) = Memory::Read_U16(addr); break; //lhu
439
case 40: Memory::Write_U8(R(rt), addr); break; //sb
440
case 41: Memory::Write_U16(R(rt), addr); break; //sh
441
case 43: Memory::Write_U32(R(rt), addr); break; //sw
442
443
// When there's an LWL and an LWR together, we should be able to peephole optimize that
444
// into a single non-alignment-checking LW.
445
case 34: //lwl
446
{
447
u32 shift = (addr & 3) * 8;
448
u32 mem = Memory::Read_U32(addr & 0xfffffffc);
449
u32 result = ( u32(R(rt)) & (0x00ffffff >> shift) ) | ( mem << (24 - shift) );
450
R(rt) = result;
451
}
452
break;
453
454
case 38: //lwr
455
{
456
u32 shift = (addr & 3) * 8;
457
u32 mem = Memory::Read_U32(addr & 0xfffffffc);
458
u32 regval = R(rt);
459
u32 result = ( regval & (0xffffff00 << (24 - shift)) ) | ( mem >> shift );
460
R(rt) = result;
461
}
462
break;
463
464
case 42: //swl
465
{
466
u32 shift = (addr & 3) * 8;
467
u32 mem = Memory::Read_U32(addr & 0xfffffffc);
468
u32 result = ( ( u32(R(rt)) >> (24 - shift) ) ) | ( mem & (0xffffff00 << shift) );
469
Memory::Write_U32(result, (addr & 0xfffffffc));
470
}
471
break;
472
473
case 46: //swr
474
{
475
u32 shift = (addr & 3) << 3;
476
u32 mem = Memory::Read_U32(addr & 0xfffffffc);
477
u32 result = ( ( u32(R(rt)) << shift ) | (mem & (0x00ffffff >> (24 - shift)) ) );
478
Memory::Write_U32(result, (addr & 0xfffffffc));
479
}
480
break;
481
482
default:
483
_dbg_assert_msg_(false,"Trying to interpret Mem instruction that can't be interpreted");
484
break;
485
}
486
PC += 4;
487
}
488
489
void Int_FPULS(MIPSOpcode op)
490
{
491
s32 offset = (s16)(op&0xFFFF);
492
int ft = _FT;
493
int rs = _RS;
494
u32 addr = R(rs) + offset;
495
496
switch(op >> 26)
497
{
498
case 49: FI(ft) = Memory::Read_U32(addr); break; //lwc1
499
case 57: Memory::Write_U32(FI(ft), addr); break; //swc1
500
default:
501
_dbg_assert_msg_(false,"Trying to interpret FPULS instruction that can't be interpreted");
502
break;
503
}
504
PC += 4;
505
}
506
507
void Int_mxc1(MIPSOpcode op)
508
{
509
int fs = _FS;
510
int rt = _RT;
511
512
switch ((op>>21)&0x1f) {
513
case 0: //mfc1
514
if (rt != 0)
515
R(rt) = FI(fs);
516
break;
517
518
case 2: //cfc1
519
if (rt != 0) {
520
if (fs == 31) {
521
currentMIPS->fcr31 = (currentMIPS->fcr31 & ~(1<<23)) | ((currentMIPS->fpcond & 1)<<23);
522
R(rt) = currentMIPS->fcr31;
523
} else if (fs == 0) {
524
R(rt) = MIPSState::FCR0_VALUE;
525
} else {
526
WARN_LOG_REPORT(Log::CPU, "ReadFCR: Unexpected reg %d", fs);
527
R(rt) = 0;
528
}
529
break;
530
}
531
break;
532
533
case 4: //mtc1
534
FI(fs) = R(rt);
535
break;
536
537
case 6: //ctc1
538
{
539
u32 value = R(rt);
540
if (fs == 31) {
541
currentMIPS->fcr31 = value & 0x0181FFFF;
542
currentMIPS->fpcond = (value >> 23) & 1;
543
// Don't bother locking, assuming the CPU can't be reset now anyway.
544
if (MIPSComp::jit) {
545
// In case of DISABLE, we need to tell jit we updated FCR31.
546
MIPSComp::jit->UpdateFCR31();
547
}
548
} else {
549
WARN_LOG_REPORT(Log::CPU, "WriteFCR: Unexpected reg %d (value %08x)", fs, value);
550
}
551
DEBUG_LOG(Log::CPU, "FCR%i written to, value %08x", fs, value);
552
break;
553
}
554
555
default:
556
_dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted");
557
break;
558
}
559
PC += 4;
560
}
561
562
void Int_RType2(MIPSOpcode op)
563
{
564
int rs = _RS;
565
int rd = _RD;
566
567
// Don't change $zr.
568
if (rd == 0)
569
{
570
PC += 4;
571
return;
572
}
573
574
switch (op & 63)
575
{
576
case 22: //clz
577
R(rd) = clz32(R(rs));
578
break;
579
case 23: //clo
580
R(rd) = clz32(~R(rs));
581
break;
582
default:
583
_dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted");
584
break;
585
}
586
PC += 4;
587
}
588
589
void Int_MulDivType(MIPSOpcode op)
590
{
591
int rt = _RT;
592
int rs = _RS;
593
int rd = _RD;
594
595
switch (op & 63)
596
{
597
case 24: //mult
598
{
599
s64 result = (s64)(s32)R(rs) * (s64)(s32)R(rt);
600
u64 resultBits = (u64)(result);
601
LO = (u32)(resultBits);
602
HI = (u32)(resultBits>>32);
603
}
604
break;
605
case 25: //multu
606
{
607
u64 resultBits = (u64)R(rs) * (u64)R(rt);
608
LO = (u32)(resultBits);
609
HI = (u32)(resultBits>>32);
610
}
611
break;
612
case 28: //madd
613
{
614
u32 a=R(rs),b=R(rt),hi=HI,lo=LO;
615
u64 origValBits = (u64)lo | ((u64)(hi)<<32);
616
s64 origVal = (s64)origValBits;
617
s64 result = origVal + (s64)(s32)a * (s64)(s32)b;
618
u64 resultBits = (u64)(result);
619
LO = (u32)(resultBits);
620
HI = (u32)(resultBits>>32);
621
}
622
break;
623
case 29: //maddu
624
{
625
u32 a=R(rs),b=R(rt),hi=HI,lo=LO;
626
u64 origVal = (u64)lo | ((u64)(hi)<<32);
627
u64 result = origVal + (u64)a * (u64)b;
628
LO = (u32)(result);
629
HI = (u32)(result>>32);
630
}
631
break;
632
case 46: //msub
633
{
634
u32 a=R(rs),b=R(rt),hi=HI,lo=LO;
635
u64 origValBits = (u64)lo | ((u64)(hi)<<32);
636
s64 origVal = (s64)origValBits;
637
s64 result = origVal - (s64)(s32)a * (s64)(s32)b;
638
u64 resultBits = (u64)(result);
639
LO = (u32)(resultBits);
640
HI = (u32)(resultBits>>32);
641
}
642
break;
643
case 47: //msubu
644
{
645
u32 a=R(rs),b=R(rt),hi=HI,lo=LO;
646
u64 origVal = (u64)lo | ((u64)(hi)<<32);
647
u64 result = origVal - (u64)a * (u64)b;
648
LO = (u32)(result);
649
HI = (u32)(result>>32);
650
}
651
break;
652
case 16: if (rd != 0) R(rd) = HI; break; //mfhi
653
case 17: HI = R(rs); break; //mthi
654
case 18: if (rd != 0) R(rd) = LO; break; //mflo
655
case 19: LO = R(rs); break; //mtlo
656
case 26: //div
657
{
658
s32 a = (s32)R(rs);
659
s32 b = (s32)R(rt);
660
if (a == (s32)0x80000000 && b == -1) {
661
LO = 0x80000000;
662
HI = -1;
663
} else if (b != 0) {
664
LO = (u32)(a / b);
665
HI = (u32)(a % b);
666
} else {
667
LO = a < 0 ? 1 : -1;
668
HI = a;
669
}
670
}
671
break;
672
case 27: //divu
673
{
674
u32 a = R(rs);
675
u32 b = R(rt);
676
if (b != 0) {
677
LO = (a/b);
678
HI = (a%b);
679
} else {
680
LO = a <= 0xFFFF ? 0xFFFF : -1;
681
HI = a;
682
}
683
}
684
break;
685
686
default:
687
_dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted");
688
break;
689
}
690
PC += 4;
691
}
692
693
694
void Int_ShiftType(MIPSOpcode op)
695
{
696
int rt = _RT;
697
int rs = _RS;
698
int rd = _RD;
699
int sa = _FD;
700
701
// Don't change $zr.
702
if (rd == 0)
703
{
704
PC += 4;
705
return;
706
}
707
708
switch (op & 0x3f)
709
{
710
case 0: R(rd) = R(rt) << sa; break; //sll
711
case 2:
712
if (_RS == 0) //srl
713
{
714
R(rd) = R(rt) >> sa;
715
break;
716
}
717
else if (_RS == 1) //rotr
718
{
719
R(rd) = __rotr(R(rt), sa);
720
break;
721
}
722
else
723
goto wrong;
724
725
case 3: R(rd) = (u32)(((s32)R(rt)) >> sa); break; //sra
726
case 4: R(rd) = R(rt) << (R(rs)&0x1F); break; //sllv
727
case 6:
728
if (_FD == 0) //srlv
729
{
730
R(rd) = R(rt) >> (R(rs)&0x1F);
731
break;
732
}
733
else if (_FD == 1) // rotrv
734
{
735
R(rd) = __rotr(R(rt), R(rs));
736
break;
737
}
738
else goto wrong;
739
case 7: R(rd) = (u32)(((s32)R(rt)) >> (R(rs)&0x1F)); break; //srav
740
default:
741
wrong:
742
_dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted");
743
break;
744
}
745
PC += 4;
746
}
747
748
void Int_Allegrex(MIPSOpcode op)
749
{
750
int rt = _RT;
751
int rd = _RD;
752
753
// Don't change $zr.
754
if (rd == 0)
755
{
756
PC += 4;
757
return;
758
}
759
760
switch((op>>6)&31)
761
{
762
case 16: // seb
763
R(rd) = SignExtend8ToU32(R(rt));
764
break;
765
766
case 20: // bitrev
767
{
768
u32 tmp = 0;
769
for (int i = 0; i < 32; i++)
770
{
771
if (R(rt) & (1 << i))
772
{
773
tmp |= (0x80000000 >> i);
774
}
775
}
776
R(rd) = tmp;
777
}
778
break;
779
780
case 24: // seh
781
R(rd) = SignExtend16ToU32(R(rt));
782
break;
783
784
default:
785
_dbg_assert_msg_(false,"Trying to interpret ALLEGREX instruction that can't be interpreted");
786
break;
787
}
788
PC += 4;
789
}
790
791
void Int_Allegrex2(MIPSOpcode op)
792
{
793
int rt = _RT;
794
int rd = _RD;
795
796
// Don't change $zr.
797
if (rd == 0)
798
{
799
PC += 4;
800
return;
801
}
802
803
switch (op & 0x3ff)
804
{
805
case 0xA0: //wsbh
806
R(rd) = ((R(rt) & 0xFF00FF00) >> 8) | ((R(rt) & 0x00FF00FF) << 8);
807
break;
808
case 0xE0: //wsbw
809
R(rd) = swap32(R(rt));
810
break;
811
default:
812
_dbg_assert_msg_(false,"Trying to interpret ALLEGREX instruction that can't be interpreted");
813
break;
814
}
815
PC += 4;
816
}
817
818
void Int_Special2(MIPSOpcode op)
819
{
820
static int reported = 0;
821
switch (op & 0x3F)
822
{
823
case 36: // mfic
824
// move from interrupt controller, not implemented
825
// See related report https://report.ppsspp.org/logs/kind/316 for possible locations.
826
// Also see https://forums.ps2dev.org/viewtopic.php?p=32700#p32700 .
827
// TODO: Should we actually implement this?
828
if (!reported) {
829
WARN_LOG(Log::CPU, "MFIC Disable/Enable Interrupt CPU instruction");
830
reported = 1;
831
}
832
break;
833
case 38: // mtic
834
// move to interrupt controller, not implemented
835
if (!reported) {
836
WARN_LOG(Log::CPU, "MTIC Disable/Enable Interrupt CPU instruction");
837
reported = 1;
838
}
839
break;
840
}
841
PC += 4;
842
}
843
844
void Int_Special3(MIPSOpcode op)
845
{
846
int rs = _RS;
847
int rt = _RT;
848
int pos = _POS;
849
850
// Don't change $zr.
851
if (rt == 0) {
852
PC += 4;
853
return;
854
}
855
856
switch (op & 0x3f) {
857
case 0x0: //ext
858
{
859
int size = _SIZE + 1;
860
u32 sourcemask = 0xFFFFFFFFUL >> (32 - size);
861
R(rt) = (R(rs) >> pos) & sourcemask;
862
}
863
break;
864
case 0x4: //ins
865
{
866
int size = (_SIZE + 1) - pos;
867
u32 sourcemask = 0xFFFFFFFFUL >> (32 - size);
868
u32 destmask = sourcemask << pos;
869
R(rt) = (R(rt) & ~destmask) | ((R(rs)&sourcemask) << pos);
870
}
871
break;
872
}
873
874
PC += 4;
875
}
876
877
void Int_FPU2op(MIPSOpcode op)
878
{
879
int fs = _FS;
880
int fd = _FD;
881
882
switch (op & 0x3f)
883
{
884
case 4: F(fd) = sqrtf(F(fs)); break; //sqrt
885
case 5: F(fd) = fabsf(F(fs)); break; //abs
886
case 6: F(fd) = F(fs); break; //mov
887
case 7: F(fd) = -F(fs); break; //neg
888
case 12:
889
case 13:
890
case 14:
891
case 15:
892
if (my_isnanorinf(F(fs)))
893
{
894
FsI(fd) = my_isinf(F(fs)) && F(fs) < 0.0f ? -2147483648LL : 2147483647LL;
895
break;
896
}
897
switch (op & 0x3f)
898
{
899
case 12: FsI(fd) = (int)floorf(F(fs)+0.5f); break; //round.w.s
900
case 13: //trunc.w.s
901
if (F(fs) >= 0.0f) {
902
FsI(fd) = (int)floorf(F(fs));
903
// Overflow, but it was positive.
904
if (FsI(fd) == -2147483648LL) {
905
FsI(fd) = 2147483647LL;
906
}
907
} else {
908
// Overflow happens to be the right value anyway.
909
FsI(fd) = (int)ceilf(F(fs));
910
}
911
break;
912
case 14: FsI(fd) = (int)ceilf (F(fs)); break; //ceil.w.s
913
case 15: FsI(fd) = (int)floorf(F(fs)); break; //floor.w.s
914
}
915
break;
916
case 32: F(fd) = (float)FsI(fs); break; //cvt.s.w
917
918
case 36:
919
if (my_isnanorinf(F(fs)))
920
{
921
FsI(fd) = my_isinf(F(fs)) && F(fs) < 0.0f ? -2147483648LL : 2147483647LL;
922
break;
923
}
924
switch (currentMIPS->fcr31 & 3)
925
{
926
case 0: FsI(fd) = (int)round_ieee_754(F(fs)); break; // RINT_0
927
case 1: FsI(fd) = (int)F(fs); break; // CAST_1
928
case 2: FsI(fd) = (int)ceilf(F(fs)); break; // CEIL_2
929
case 3: FsI(fd) = (int)floorf(F(fs)); break; // FLOOR_3
930
}
931
break; //cvt.w.s
932
default:
933
_dbg_assert_msg_(false,"Trying to interpret FPU2Op instruction that can't be interpreted");
934
break;
935
}
936
PC += 4;
937
}
938
939
void Int_FPUComp(MIPSOpcode op)
940
{
941
int fs = _FS;
942
int ft = _FT;
943
bool cond;
944
switch (op & 0xf)
945
{
946
case 0: //f
947
case 8: //sf
948
cond = false;
949
break;
950
951
case 1: //un
952
case 9: //ngle
953
cond = my_isnan(F(fs)) || my_isnan(F(ft));
954
break;
955
956
case 2: //eq
957
case 10: //seq
958
cond = !my_isnan(F(fs)) && !my_isnan(F(ft)) && (F(fs) == F(ft));
959
break;
960
961
case 3: //ueq
962
case 11: //ngl
963
cond = (F(fs) == F(ft)) || my_isnan(F(fs)) || my_isnan(F(ft));
964
break;
965
966
case 4: //olt
967
case 12: //lt
968
cond = (F(fs) < F(ft));
969
break;
970
971
case 5: //ult
972
case 13: //nge
973
cond = (F(fs) < F(ft)) || my_isnan(F(fs)) || my_isnan(F(ft));
974
break;
975
976
case 6: //ole
977
case 14: //le
978
cond = (F(fs) <= F(ft));
979
break;
980
981
case 7: //ule
982
case 15: //ngt
983
cond = (F(fs) <= F(ft)) || my_isnan(F(fs)) || my_isnan(F(ft));
984
break;
985
986
default:
987
_dbg_assert_msg_(false,"Trying to interpret FPUComp instruction that can't be interpreted");
988
cond = false;
989
break;
990
}
991
currentMIPS->fpcond = cond;
992
PC += 4;
993
}
994
995
void Int_FPU3op(MIPSOpcode op)
996
{
997
int ft = _FT;
998
int fs = _FS;
999
int fd = _FD;
1000
1001
switch (op & 0x3f)
1002
{
1003
case 0: F(fd) = F(fs) + F(ft); break; // add.s
1004
case 1: F(fd) = F(fs) - F(ft); break; // sub.s
1005
case 2: // mul.s
1006
if ((my_isinf(F(fs)) && F(ft) == 0.0f) || (my_isinf(F(ft)) && F(fs) == 0.0f)) {
1007
// Must be positive NAN, see #12519.
1008
FI(fd) = 0x7fc00000;
1009
} else {
1010
F(fd) = F(fs) * F(ft);
1011
}
1012
break;
1013
case 3: F(fd) = F(fs) / F(ft); break; // div.s
1014
default:
1015
_dbg_assert_msg_(false,"Trying to interpret FPU3Op instruction that can't be interpreted");
1016
break;
1017
}
1018
PC += 4;
1019
}
1020
1021
void Int_Interrupt(MIPSOpcode op)
1022
{
1023
static int reported = 0;
1024
switch (op & 1)
1025
{
1026
case 0:
1027
// unlikely to be legitimately used
1028
if (!reported) {
1029
Reporting::ReportMessage("INTERRUPT instruction hit (%08x) at %08x", op.encoding, currentMIPS->pc);
1030
WARN_LOG(Log::CPU, "Disable/Enable Interrupt CPU instruction");
1031
reported = 1;
1032
}
1033
break;
1034
}
1035
PC += 4;
1036
}
1037
1038
void Int_Emuhack(MIPSOpcode op)
1039
{
1040
if (((op >> 24) & 3) != EMUOP_CALL_REPLACEMENT) {
1041
_dbg_assert_msg_(false, "Trying to interpret emuhack instruction that can't be interpreted");
1042
}
1043
1044
_assert_((PC & 3) == 0);
1045
1046
// It's a replacement func!
1047
int index = op.encoding & 0xFFFFFF;
1048
const ReplacementTableEntry *entry = GetReplacementFunc(index);
1049
if (entry && entry->replaceFunc && (entry->flags & REPFLAG_DISABLED) == 0) {
1050
int cycles = entry->replaceFunc();
1051
1052
if (entry->flags & (REPFLAG_HOOKENTER | REPFLAG_HOOKEXIT)) {
1053
// Interpret the original instruction under the hook.
1054
MIPSInterpret(Memory::Read_Instruction(PC, true));
1055
} else if (cycles < 0) {
1056
// Leave PC unchanged, call the replacement again (assumes args are modified.)
1057
currentMIPS->downcount += cycles;
1058
} else {
1059
PC = currentMIPS->r[MIPS_REG_RA];
1060
currentMIPS->downcount -= cycles;
1061
}
1062
} else {
1063
if (!entry || !entry->replaceFunc) {
1064
ERROR_LOG(Log::CPU, "Bad replacement function index %i", index);
1065
}
1066
// Interpret the original instruction under it.
1067
MIPSInterpret(Memory::Read_Instruction(PC, true));
1068
}
1069
}
1070
}
1071
1072