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/MIPSInt.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 <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
#include "Core/System.h"
38
39
#define R(i) (currentMIPS->r[i])
40
#define F(i) (currentMIPS->f[i])
41
#define FI(i) (currentMIPS->fi[i])
42
#define FsI(i) (currentMIPS->fs[i])
43
#define PC (currentMIPS->pc)
44
45
#define _SIMM16_SHL2 ((u32)(s32)(s16)(op & 0xFFFF) << 2)
46
#define _RS ((op>>21) & 0x1F)
47
#define _RT ((op>>16) & 0x1F)
48
#define _RD ((op>>11) & 0x1F)
49
#define _FS ((op>>11) & 0x1F)
50
#define _FT ((op>>16) & 0x1F)
51
#define _FD ((op>>6 ) & 0x1F)
52
#define _POS ((op>>6 ) & 0x1F)
53
#define _SIZE ((op>>11) & 0x1F)
54
55
#define HI currentMIPS->hi
56
#define LO currentMIPS->lo
57
58
static inline void DelayBranchTo(u32 where)
59
{
60
if (!Memory::IsValidAddress(where) || (where & 3) != 0) {
61
Core_ExecException(where, PC, ExecExceptionType::JUMP);
62
}
63
PC += 4;
64
mipsr4k.nextPC = where;
65
mipsr4k.inDelaySlot = true;
66
}
67
68
static inline void SkipLikely() {
69
MIPSInfo delaySlot = MIPSGetInfo(Memory::Read_Instruction(PC + 4, true));
70
// Don't actually skip if it is a jump (seen in Brooktown High.)
71
if (delaySlot & IS_JUMP) {
72
PC += 4;
73
} else {
74
PC += 8;
75
--mipsr4k.downcount;
76
}
77
}
78
79
int MIPS_SingleStep()
80
{
81
MIPSOpcode op = Memory::Read_Opcode_JIT(mipsr4k.pc);
82
if (mipsr4k.inDelaySlot) {
83
MIPSInterpret(op);
84
if (mipsr4k.inDelaySlot) {
85
mipsr4k.pc = mipsr4k.nextPC;
86
mipsr4k.inDelaySlot = false;
87
}
88
} else {
89
MIPSInterpret(op);
90
}
91
return 1;
92
}
93
94
namespace MIPSInt
95
{
96
void Int_Cache(MIPSOpcode op)
97
{
98
int imm = SignExtend16ToS32(op);
99
int rs = _RS;
100
uint32_t addr = R(rs) + imm;
101
int func = (op >> 16) & 0x1F;
102
103
// Let's only report this once per run to be safe from impacting perf.
104
static bool reportedAlignment = false;
105
106
// It appears that a cache line is 0x40 (64) bytes, loops in games
107
// issue the cache instruction at that interval.
108
109
// These codes might be PSP-specific, they don't match regular MIPS cache codes very well
110
111
// NOTE: If you add support for more, make sure they are handled in the various Jit::Comp_Cache.
112
switch (func) {
113
// Icache
114
case 8:
115
// Invalidate the instruction cache at this address.
116
// We assume the CPU won't be reset during this, so no locking.
117
if (MIPSComp::jit) {
118
// Let's over invalidate to be super safe.
119
uint32_t alignedAddr = addr & ~0x3F;
120
int size = 0x40 + (addr & 0x3F);
121
MIPSComp::jit->InvalidateCacheAt(alignedAddr, size);
122
// Using a bool to avoid locking/etc. in case it's slow.
123
if (!reportedAlignment && (addr & 0x3F) != 0) {
124
WARN_LOG_REPORT(Log::JIT, "Unaligned icache invalidation of %08x (%08x + %d) at PC=%08x", addr, R(rs), imm, PC);
125
reportedAlignment = 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_Break(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
532
case 4: //mtc1
533
FI(fs) = R(rt);
534
break;
535
536
case 6: //ctc1
537
{
538
u32 value = R(rt);
539
if (fs == 31) {
540
currentMIPS->fcr31 = value & 0x0181FFFF;
541
currentMIPS->fpcond = (value >> 23) & 1;
542
// Don't bother locking, assuming the CPU can't be reset now anyway.
543
if (MIPSComp::jit) {
544
// In case of DISABLE, we need to tell jit we updated FCR31.
545
MIPSComp::jit->UpdateFCR31();
546
}
547
} else {
548
WARN_LOG_REPORT(Log::CPU, "WriteFCR: Unexpected reg %d (value %08x)", fs, value);
549
}
550
DEBUG_LOG(Log::CPU, "FCR%i written to, value %08x", fs, value);
551
break;
552
}
553
554
default:
555
_dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted");
556
break;
557
}
558
PC += 4;
559
}
560
561
void Int_RType2(MIPSOpcode op)
562
{
563
int rs = _RS;
564
int rd = _RD;
565
566
// Don't change $zr.
567
if (rd == 0)
568
{
569
PC += 4;
570
return;
571
}
572
573
switch (op & 63)
574
{
575
case 22: //clz
576
R(rd) = clz32(R(rs));
577
break;
578
case 23: //clo
579
R(rd) = clz32(~R(rs));
580
break;
581
default:
582
_dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted");
583
break;
584
}
585
PC += 4;
586
}
587
588
void Int_MulDivType(MIPSOpcode op)
589
{
590
int rt = _RT;
591
int rs = _RS;
592
int rd = _RD;
593
594
switch (op & 63)
595
{
596
case 24: //mult
597
{
598
s64 result = (s64)(s32)R(rs) * (s64)(s32)R(rt);
599
u64 resultBits = (u64)(result);
600
LO = (u32)(resultBits);
601
HI = (u32)(resultBits>>32);
602
}
603
break;
604
case 25: //multu
605
{
606
u64 resultBits = (u64)R(rs) * (u64)R(rt);
607
LO = (u32)(resultBits);
608
HI = (u32)(resultBits>>32);
609
}
610
break;
611
case 28: //madd
612
{
613
u32 a=R(rs),b=R(rt),hi=HI,lo=LO;
614
u64 origValBits = (u64)lo | ((u64)(hi)<<32);
615
s64 origVal = (s64)origValBits;
616
s64 result = origVal + (s64)(s32)a * (s64)(s32)b;
617
u64 resultBits = (u64)(result);
618
LO = (u32)(resultBits);
619
HI = (u32)(resultBits>>32);
620
}
621
break;
622
case 29: //maddu
623
{
624
u32 a=R(rs),b=R(rt),hi=HI,lo=LO;
625
u64 origVal = (u64)lo | ((u64)(hi)<<32);
626
u64 result = origVal + (u64)a * (u64)b;
627
LO = (u32)(result);
628
HI = (u32)(result>>32);
629
}
630
break;
631
case 46: //msub
632
{
633
u32 a=R(rs),b=R(rt),hi=HI,lo=LO;
634
u64 origValBits = (u64)lo | ((u64)(hi)<<32);
635
s64 origVal = (s64)origValBits;
636
s64 result = origVal - (s64)(s32)a * (s64)(s32)b;
637
u64 resultBits = (u64)(result);
638
LO = (u32)(resultBits);
639
HI = (u32)(resultBits>>32);
640
}
641
break;
642
case 47: //msubu
643
{
644
u32 a=R(rs),b=R(rt),hi=HI,lo=LO;
645
u64 origVal = (u64)lo | ((u64)(hi)<<32);
646
u64 result = origVal - (u64)a * (u64)b;
647
LO = (u32)(result);
648
HI = (u32)(result>>32);
649
}
650
break;
651
case 16: if (rd != 0) R(rd) = HI; break; //mfhi
652
case 17: HI = R(rs); break; //mthi
653
case 18: if (rd != 0) R(rd) = LO; break; //mflo
654
case 19: LO = R(rs); break; //mtlo
655
case 26: //div
656
{
657
s32 a = (s32)R(rs);
658
s32 b = (s32)R(rt);
659
if (a == (s32)0x80000000 && b == -1) {
660
LO = 0x80000000;
661
HI = -1;
662
} else if (b != 0) {
663
LO = (u32)(a / b);
664
HI = (u32)(a % b);
665
} else {
666
LO = a < 0 ? 1 : -1;
667
HI = a;
668
}
669
}
670
break;
671
case 27: //divu
672
{
673
u32 a = R(rs);
674
u32 b = R(rt);
675
if (b != 0) {
676
LO = (a/b);
677
HI = (a%b);
678
} else {
679
LO = a <= 0xFFFF ? 0xFFFF : -1;
680
HI = a;
681
}
682
}
683
break;
684
685
default:
686
_dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted");
687
break;
688
}
689
PC += 4;
690
}
691
692
693
void Int_ShiftType(MIPSOpcode op)
694
{
695
int rt = _RT;
696
int rs = _RS;
697
int rd = _RD;
698
int sa = _FD;
699
700
// Don't change $zr.
701
if (rd == 0)
702
{
703
PC += 4;
704
return;
705
}
706
707
switch (op & 0x3f)
708
{
709
case 0: R(rd) = R(rt) << sa; break; //sll
710
case 2:
711
if (_RS == 0) //srl
712
{
713
R(rd) = R(rt) >> sa;
714
break;
715
}
716
else if (_RS == 1) //rotr
717
{
718
R(rd) = __rotr(R(rt), sa);
719
break;
720
}
721
else
722
goto wrong;
723
724
case 3: R(rd) = (u32)(((s32)R(rt)) >> sa); break; //sra
725
case 4: R(rd) = R(rt) << (R(rs)&0x1F); break; //sllv
726
case 6:
727
if (_FD == 0) //srlv
728
{
729
R(rd) = R(rt) >> (R(rs)&0x1F);
730
break;
731
}
732
else if (_FD == 1) // rotrv
733
{
734
R(rd) = __rotr(R(rt), R(rs));
735
break;
736
}
737
else goto wrong;
738
case 7: R(rd) = (u32)(((s32)R(rt)) >> (R(rs)&0x1F)); break; //srav
739
default:
740
wrong:
741
_dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted");
742
break;
743
}
744
PC += 4;
745
}
746
747
void Int_Allegrex(MIPSOpcode op)
748
{
749
int rt = _RT;
750
int rd = _RD;
751
752
// Don't change $zr.
753
if (rd == 0)
754
{
755
PC += 4;
756
return;
757
}
758
759
switch((op>>6)&31)
760
{
761
case 16: // seb
762
R(rd) = SignExtend8ToU32(R(rt));
763
break;
764
765
case 20: // bitrev
766
{
767
u32 tmp = 0;
768
for (int i = 0; i < 32; i++)
769
{
770
if (R(rt) & (1 << i))
771
{
772
tmp |= (0x80000000 >> i);
773
}
774
}
775
R(rd) = tmp;
776
}
777
break;
778
779
case 24: // seh
780
R(rd) = SignExtend16ToU32(R(rt));
781
break;
782
783
default:
784
_dbg_assert_msg_(false,"Trying to interpret ALLEGREX instruction that can't be interpreted");
785
break;
786
}
787
PC += 4;
788
}
789
790
void Int_Allegrex2(MIPSOpcode op)
791
{
792
int rt = _RT;
793
int rd = _RD;
794
795
// Don't change $zr.
796
if (rd == 0)
797
{
798
PC += 4;
799
return;
800
}
801
802
switch (op & 0x3ff)
803
{
804
case 0xA0: //wsbh
805
R(rd) = ((R(rt) & 0xFF00FF00) >> 8) | ((R(rt) & 0x00FF00FF) << 8);
806
break;
807
case 0xE0: //wsbw
808
R(rd) = swap32(R(rt));
809
break;
810
default:
811
_dbg_assert_msg_(false,"Trying to interpret ALLEGREX instruction that can't be interpreted");
812
break;
813
}
814
PC += 4;
815
}
816
817
void Int_Special2(MIPSOpcode op)
818
{
819
static int reported = 0;
820
switch (op & 0x3F)
821
{
822
case 36: // mfic
823
if (!reported) {
824
Reporting::ReportMessage("MFIC instruction hit (%08x) at %08x", op.encoding, currentMIPS->pc);
825
WARN_LOG(Log::CPU,"MFIC Disable/Enable Interrupt CPU instruction");
826
reported = 1;
827
}
828
break;
829
case 38: // mtic
830
if (!reported) {
831
Reporting::ReportMessage("MTIC instruction hit (%08x) at %08x", op.encoding, currentMIPS->pc);
832
WARN_LOG(Log::CPU,"MTIC Disable/Enable Interrupt CPU instruction");
833
reported = 1;
834
}
835
break;
836
}
837
PC += 4;
838
}
839
840
void Int_Special3(MIPSOpcode op)
841
{
842
int rs = _RS;
843
int rt = _RT;
844
int pos = _POS;
845
846
// Don't change $zr.
847
if (rt == 0) {
848
PC += 4;
849
return;
850
}
851
852
switch (op & 0x3f) {
853
case 0x0: //ext
854
{
855
int size = _SIZE + 1;
856
u32 sourcemask = 0xFFFFFFFFUL >> (32 - size);
857
R(rt) = (R(rs) >> pos) & sourcemask;
858
}
859
break;
860
case 0x4: //ins
861
{
862
int size = (_SIZE + 1) - pos;
863
u32 sourcemask = 0xFFFFFFFFUL >> (32 - size);
864
u32 destmask = sourcemask << pos;
865
R(rt) = (R(rt) & ~destmask) | ((R(rs)&sourcemask) << pos);
866
}
867
break;
868
}
869
870
PC += 4;
871
}
872
873
void Int_FPU2op(MIPSOpcode op)
874
{
875
int fs = _FS;
876
int fd = _FD;
877
878
switch (op & 0x3f)
879
{
880
case 4: F(fd) = sqrtf(F(fs)); break; //sqrt
881
case 5: F(fd) = fabsf(F(fs)); break; //abs
882
case 6: F(fd) = F(fs); break; //mov
883
case 7: F(fd) = -F(fs); break; //neg
884
case 12:
885
case 13:
886
case 14:
887
case 15:
888
if (my_isnanorinf(F(fs)))
889
{
890
FsI(fd) = my_isinf(F(fs)) && F(fs) < 0.0f ? -2147483648LL : 2147483647LL;
891
break;
892
}
893
switch (op & 0x3f)
894
{
895
case 12: FsI(fd) = (int)floorf(F(fs)+0.5f); break; //round.w.s
896
case 13: //trunc.w.s
897
if (F(fs) >= 0.0f) {
898
FsI(fd) = (int)floorf(F(fs));
899
// Overflow, but it was positive.
900
if (FsI(fd) == -2147483648LL) {
901
FsI(fd) = 2147483647LL;
902
}
903
} else {
904
// Overflow happens to be the right value anyway.
905
FsI(fd) = (int)ceilf(F(fs));
906
}
907
break;
908
case 14: FsI(fd) = (int)ceilf (F(fs)); break; //ceil.w.s
909
case 15: FsI(fd) = (int)floorf(F(fs)); break; //floor.w.s
910
}
911
break;
912
case 32: F(fd) = (float)FsI(fs); break; //cvt.s.w
913
914
case 36:
915
if (my_isnanorinf(F(fs)))
916
{
917
FsI(fd) = my_isinf(F(fs)) && F(fs) < 0.0f ? -2147483648LL : 2147483647LL;
918
break;
919
}
920
switch (currentMIPS->fcr31 & 3)
921
{
922
case 0: FsI(fd) = (int)round_ieee_754(F(fs)); break; // RINT_0
923
case 1: FsI(fd) = (int)F(fs); break; // CAST_1
924
case 2: FsI(fd) = (int)ceilf(F(fs)); break; // CEIL_2
925
case 3: FsI(fd) = (int)floorf(F(fs)); break; // FLOOR_3
926
}
927
break; //cvt.w.s
928
default:
929
_dbg_assert_msg_(false,"Trying to interpret FPU2Op instruction that can't be interpreted");
930
break;
931
}
932
PC += 4;
933
}
934
935
void Int_FPUComp(MIPSOpcode op)
936
{
937
int fs = _FS;
938
int ft = _FT;
939
bool cond;
940
switch (op & 0xf)
941
{
942
case 0: //f
943
case 8: //sf
944
cond = false;
945
break;
946
947
case 1: //un
948
case 9: //ngle
949
cond = my_isnan(F(fs)) || my_isnan(F(ft));
950
break;
951
952
case 2: //eq
953
case 10: //seq
954
cond = !my_isnan(F(fs)) && !my_isnan(F(ft)) && (F(fs) == F(ft));
955
break;
956
957
case 3: //ueq
958
case 11: //ngl
959
cond = (F(fs) == F(ft)) || my_isnan(F(fs)) || my_isnan(F(ft));
960
break;
961
962
case 4: //olt
963
case 12: //lt
964
cond = (F(fs) < F(ft));
965
break;
966
967
case 5: //ult
968
case 13: //nge
969
cond = (F(fs) < F(ft)) || my_isnan(F(fs)) || my_isnan(F(ft));
970
break;
971
972
case 6: //ole
973
case 14: //le
974
cond = (F(fs) <= F(ft));
975
break;
976
977
case 7: //ule
978
case 15: //ngt
979
cond = (F(fs) <= F(ft)) || my_isnan(F(fs)) || my_isnan(F(ft));
980
break;
981
982
default:
983
_dbg_assert_msg_(false,"Trying to interpret FPUComp instruction that can't be interpreted");
984
cond = false;
985
break;
986
}
987
currentMIPS->fpcond = cond;
988
PC += 4;
989
}
990
991
void Int_FPU3op(MIPSOpcode op)
992
{
993
int ft = _FT;
994
int fs = _FS;
995
int fd = _FD;
996
997
switch (op & 0x3f)
998
{
999
case 0: F(fd) = F(fs) + F(ft); break; // add.s
1000
case 1: F(fd) = F(fs) - F(ft); break; // sub.s
1001
case 2: // mul.s
1002
if ((my_isinf(F(fs)) && F(ft) == 0.0f) || (my_isinf(F(ft)) && F(fs) == 0.0f)) {
1003
// Must be positive NAN, see #12519.
1004
FI(fd) = 0x7fc00000;
1005
} else {
1006
F(fd) = F(fs) * F(ft);
1007
}
1008
break;
1009
case 3: F(fd) = F(fs) / F(ft); break; // div.s
1010
default:
1011
_dbg_assert_msg_(false,"Trying to interpret FPU3Op instruction that can't be interpreted");
1012
break;
1013
}
1014
PC += 4;
1015
}
1016
1017
void Int_Interrupt(MIPSOpcode op)
1018
{
1019
static int reported = 0;
1020
switch (op & 1)
1021
{
1022
case 0:
1023
if (!reported) {
1024
Reporting::ReportMessage("INTERRUPT instruction hit (%08x) at %08x", op.encoding, currentMIPS->pc);
1025
WARN_LOG(Log::CPU,"Disable/Enable Interrupt CPU instruction");
1026
reported = 1;
1027
}
1028
break;
1029
}
1030
PC += 4;
1031
}
1032
1033
void Int_Emuhack(MIPSOpcode op)
1034
{
1035
if (((op >> 24) & 3) != EMUOP_CALL_REPLACEMENT) {
1036
_dbg_assert_msg_(false,"Trying to interpret emuhack instruction that can't be interpreted");
1037
}
1038
// It's a replacement func!
1039
int index = op.encoding & 0xFFFFFF;
1040
const ReplacementTableEntry *entry = GetReplacementFunc(index);
1041
if (entry && entry->replaceFunc && (entry->flags & REPFLAG_DISABLED) == 0) {
1042
int cycles = entry->replaceFunc();
1043
1044
if (entry->flags & (REPFLAG_HOOKENTER | REPFLAG_HOOKEXIT)) {
1045
// Interpret the original instruction under the hook.
1046
MIPSInterpret(Memory::Read_Instruction(PC, true));
1047
} else if (cycles < 0) {
1048
// Leave PC unchanged, call the replacement again (assumes args are modified.)
1049
currentMIPS->downcount += cycles;
1050
} else {
1051
PC = currentMIPS->r[MIPS_REG_RA];
1052
currentMIPS->downcount -= cycles;
1053
}
1054
} else {
1055
if (!entry || !entry->replaceFunc) {
1056
ERROR_LOG(Log::CPU, "Bad replacement function index %i", index);
1057
}
1058
// Interpret the original instruction under it.
1059
MIPSInterpret(Memory::Read_Instruction(PC, true));
1060
}
1061
}
1062
1063
1064
}
1065
1066