CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Core/MIPS/MIPSInt.cpp
Views: 1401
// Copyright (c) 2012- PPSSPP Project.12// This program is free software: you can redistribute it and/or modify3// it under the terms of the GNU General Public License as published by4// the Free Software Foundation, version 2.0 or later versions.56// This program is distributed in the hope that it will be useful,7// but WITHOUT ANY WARRANTY; without even the implied warranty of8// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9// GNU General Public License 2.0 for more details.1011// A copy of the GPL 2.0 should have been included with the program.12// If not, see http://www.gnu.org/licenses/1314// Official git repository and contact information can be found at15// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.1617#include <cmath>1819#include "Common/Data/Convert/SmallDataConvert.h"20#include "Common/Math/math_util.h"2122#include "Common/BitSet.h"23#include "Common/BitScan.h"24#include "Common/CommonTypes.h"25#include "Core/Config.h"26#include "Core/Core.h"27#include "Core/MemMap.h"28#include "Core/MIPS/MIPS.h"29#include "Core/MIPS/MIPSCodeUtils.h"30#include "Core/MIPS/MIPSInt.h"31#include "Core/MIPS/MIPSTables.h"32#include "Core/Reporting.h"33#include "Core/HLE/HLE.h"34#include "Core/HLE/HLETables.h"35#include "Core/HLE/ReplaceTables.h"36#include "Core/System.h"3738#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)4344#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)5354#define HI currentMIPS->hi55#define LO currentMIPS->lo5657static inline void DelayBranchTo(u32 where)58{59if (!Memory::IsValidAddress(where) || (where & 3) != 0) {60Core_ExecException(where, PC, ExecExceptionType::JUMP);61}62PC += 4;63mipsr4k.nextPC = where;64mipsr4k.inDelaySlot = true;65}6667static inline void SkipLikely() {68MIPSInfo delaySlot = MIPSGetInfo(Memory::Read_Instruction(PC + 4, true));69// Don't actually skip if it is a jump (seen in Brooktown High.)70if (delaySlot & IS_JUMP) {71PC += 4;72} else {73PC += 8;74--mipsr4k.downcount;75}76}7778int MIPS_SingleStep()79{80MIPSOpcode op = Memory::Read_Opcode_JIT(mipsr4k.pc);81if (mipsr4k.inDelaySlot) {82MIPSInterpret(op);83if (mipsr4k.inDelaySlot) {84mipsr4k.pc = mipsr4k.nextPC;85mipsr4k.inDelaySlot = false;86}87} else {88MIPSInterpret(op);89}90return 1;91}9293namespace MIPSInt94{95void Int_Cache(MIPSOpcode op)96{97int imm = SignExtend16ToS32(op);98int rs = _RS;99uint32_t addr = R(rs) + imm;100int func = (op >> 16) & 0x1F;101102// Let's only report this once per run to be safe from impacting perf.103static bool reportedAlignment = false;104105// It appears that a cache line is 0x40 (64) bytes, loops in games106// issue the cache instruction at that interval.107108// These codes might be PSP-specific, they don't match regular MIPS cache codes very well109110// NOTE: If you add support for more, make sure they are handled in the various Jit::Comp_Cache.111switch (func) {112// Icache113case 8:114// Invalidate the instruction cache at this address.115// We assume the CPU won't be reset during this, so no locking.116if (MIPSComp::jit) {117// Let's over invalidate to be super safe.118uint32_t alignedAddr = addr & ~0x3F;119int size = 0x40 + (addr & 0x3F);120MIPSComp::jit->InvalidateCacheAt(alignedAddr, size);121// Using a bool to avoid locking/etc. in case it's slow.122if (!reportedAlignment && (addr & 0x3F) != 0) {123WARN_LOG_REPORT(Log::JIT, "Unaligned icache invalidation of %08x (%08x + %d) at PC=%08x", addr, R(rs), imm, PC);124reportedAlignment = true;125}126if (alignedAddr <= PC + 4 && alignedAddr + size >= PC - 4) {127// This is probably rare so we don't use a static bool.128WARN_LOG_REPORT_ONCE(icacheInvalidatePC, Log::JIT, "Invalidating address near PC: %08x (%08x + %d) at PC=%08x", addr, R(rs), imm, PC);129}130}131break;132133// Dcache134case 24:135// "Create Dirty Exclusive" - for avoiding a cacheline fill before writing to it.136// Will cause garbage on the real machine so we just ignore it, the app will overwrite the cacheline.137break;138case 25: // Hit Invalidate - zaps the line if present in cache. Should not writeback???? scary.139// No need to do anything.140break;141case 27: // D-cube. Hit Writeback Invalidate. Tony Hawk Underground 2142break;143case 30: // GTA LCS, a lot. Fill (prefetch). Tony Hawk Underground 2144break;145146default:147DEBUG_LOG(Log::CPU, "cache instruction affecting %08x : function %i", addr, func);148}149150PC += 4;151}152153void Int_Syscall(MIPSOpcode op)154{155// Need to pre-move PC, as CallSyscall may result in a rescheduling!156// To do this neater, we'll need a little generated kernel loop that syscall can jump to and then RFI from157// but I don't see a need to bother.158if (mipsr4k.inDelaySlot)159{160mipsr4k.pc = mipsr4k.nextPC;161}162else163{164mipsr4k.pc += 4;165}166mipsr4k.inDelaySlot = false;167CallSyscall(op);168}169170void Int_Sync(MIPSOpcode op)171{172//DEBUG_LOG(Log::CPU, "sync");173PC += 4;174}175176void Int_Break(MIPSOpcode op)177{178Reporting::ReportMessage("BREAK instruction hit");179Core_Break(PC);180PC += 4;181}182183void Int_RelBranch(MIPSOpcode op)184{185int imm = _SIMM16_SHL2;186int rs = _RS;187int rt = _RT;188u32 addr = PC + imm + 4;189190switch (op >> 26)191{192case 4: if (R(rt) == R(rs)) DelayBranchTo(addr); else PC += 4; break; //beq193case 5: if (R(rt) != R(rs)) DelayBranchTo(addr); else PC += 4; break; //bne194case 6: if ((s32)R(rs) <= 0) DelayBranchTo(addr); else PC += 4; break; //blez195case 7: if ((s32)R(rs) > 0) DelayBranchTo(addr); else PC += 4; break; //bgtz196197case 20: if (R(rt) == R(rs)) DelayBranchTo(addr); else SkipLikely(); break; //beql198case 21: if (R(rt) != R(rs)) DelayBranchTo(addr); else SkipLikely(); break; //bnel199case 22: if ((s32)R(rs) <= 0) DelayBranchTo(addr); else SkipLikely(); break; //blezl200case 23: if ((s32)R(rs) > 0) DelayBranchTo(addr); else SkipLikely(); break; //bgtzl201202default:203_dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted");204break;205}206}207208void Int_RelBranchRI(MIPSOpcode op)209{210int imm = _SIMM16_SHL2;211int rs = _RS;212u32 addr = PC + imm + 4;213214switch ((op>>16) & 0x1F)215{216case 0: if ((s32)R(rs) < 0) DelayBranchTo(addr); else PC += 4; break;//bltz217case 1: if ((s32)R(rs) >= 0) DelayBranchTo(addr); else PC += 4; break;//bgez218case 2: if ((s32)R(rs) < 0) DelayBranchTo(addr); else SkipLikely(); break;//bltzl219case 3: if ((s32)R(rs) >= 0) DelayBranchTo(addr); else SkipLikely(); break;//bgezl220case 16: R(MIPS_REG_RA) = PC + 8; if ((s32)R(rs) < 0) DelayBranchTo(addr); else PC += 4; break;//bltzal221case 17: R(MIPS_REG_RA) = PC + 8; if ((s32)R(rs) >= 0) DelayBranchTo(addr); else PC += 4; break;//bgezal222case 18: R(MIPS_REG_RA) = PC + 8; if ((s32)R(rs) < 0) DelayBranchTo(addr); else SkipLikely(); break;//bltzall223case 19: R(MIPS_REG_RA) = PC + 8; if ((s32)R(rs) >= 0) DelayBranchTo(addr); else SkipLikely(); break;//bgezall224default:225_dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted");226break;227}228}229230231void Int_VBranch(MIPSOpcode op)232{233int imm = _SIMM16_SHL2;234u32 addr = PC + imm + 4;235236// x, y, z, w, any, all, (invalid), (invalid)237int imm3 = (op>>18)&7;238int val = (currentMIPS->vfpuCtrl[VFPU_CTRL_CC] >> imm3) & 1;239240switch ((op >> 16) & 3)241{242case 0: if (!val) DelayBranchTo(addr); else PC += 4; break; //bvf243case 1: if ( val) DelayBranchTo(addr); else PC += 4; break; //bvt244case 2: if (!val) DelayBranchTo(addr); else SkipLikely(); break; //bvfl245case 3: if ( val) DelayBranchTo(addr); else SkipLikely(); break; //bvtl246}247}248249void Int_FPUBranch(MIPSOpcode op)250{251int imm = _SIMM16_SHL2;252u32 addr = PC + imm + 4;253switch((op>>16)&0x1f)254{255case 0: if (!currentMIPS->fpcond) DelayBranchTo(addr); else PC += 4; break;//bc1f256case 1: if ( currentMIPS->fpcond) DelayBranchTo(addr); else PC += 4; break;//bc1t257case 2: if (!currentMIPS->fpcond) DelayBranchTo(addr); else SkipLikely(); break;//bc1fl258case 3: if ( currentMIPS->fpcond) DelayBranchTo(addr); else SkipLikely(); break;//bc1tl259default:260_dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted");261break;262}263}264265void Int_JumpType(MIPSOpcode op)266{267if (mipsr4k.inDelaySlot)268ERROR_LOG(Log::CPU, "Jump in delay slot :(");269270u32 off = ((op & 0x03FFFFFF) << 2);271u32 addr = (currentMIPS->pc & 0xF0000000) | off;272273switch (op>>26)274{275case 2: //j276if (!mipsr4k.inDelaySlot)277DelayBranchTo(addr);278break;279case 3: //jal280R(MIPS_REG_RA) = PC + 8;281if (!mipsr4k.inDelaySlot)282DelayBranchTo(addr);283break;284default:285_dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted");286break;287}288}289290void Int_JumpRegType(MIPSOpcode op)291{292if (mipsr4k.inDelaySlot)293{294// There's one of these in Star Soldier at 0881808c, which seems benign.295ERROR_LOG(Log::CPU, "Jump in delay slot :(");296}297298int rs = _RS;299int rd = _RD;300u32 addr = R(rs);301switch (op & 0x3f)302{303case 8: //jr304if (!mipsr4k.inDelaySlot)305DelayBranchTo(addr);306break;307case 9: //jalr308if (rd != 0)309R(rd) = PC + 8;310// Update rd, but otherwise do not take the branch if we're branching.311if (!mipsr4k.inDelaySlot)312DelayBranchTo(addr);313break;314}315}316317void Int_IType(MIPSOpcode op)318{319u32 uimm = op & 0xFFFF;320u32 suimm = SignExtend16ToU32(op);321s32 simm = SignExtend16ToS32(op);322323int rt = _RT;324int rs = _RS;325326if (rt == 0) { //destination register is zero register327PC += 4;328return; //nop329}330331switch (op>>26)332{333case 8: R(rt) = R(rs) + simm; break; //addi334case 9: R(rt) = R(rs) + simm; break; //addiu335case 10: R(rt) = (s32)R(rs) < simm; break; //slti336case 11: R(rt) = R(rs) < suimm; break; //sltiu337case 12: R(rt) = R(rs) & uimm; break; //andi338case 13: R(rt) = R(rs) | uimm; break; //ori339case 14: R(rt) = R(rs) ^ uimm; break; //xori340case 15: R(rt) = uimm << 16; break; //lui341default:342_dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted");343break;344}345PC += 4;346}347348void Int_StoreSync(MIPSOpcode op)349{350int imm = (signed short)(op&0xFFFF);351int rt = _RT;352int rs = _RS;353u32 addr = R(rs) + imm;354355switch (op >> 26)356{357case 48: // ll358if (rt != 0) {359R(rt) = Memory::Read_U32(addr);360}361currentMIPS->llBit = 1;362break;363case 56: // sc364if (currentMIPS->llBit) {365Memory::Write_U32(R(rt), addr);366if (rt != 0) {367R(rt) = 1;368}369} else if (rt != 0) {370R(rt) = 0;371}372break;373default:374_dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted");375break;376}377PC += 4;378}379380381void Int_RType3(MIPSOpcode op)382{383int rt = _RT;384int rs = _RS;385int rd = _RD;386387// Don't change $zr.388if (rd == 0)389{390PC += 4;391return;392}393394switch (op & 63)395{396case 10: if (R(rt) == 0) R(rd) = R(rs); break; //movz397case 11: if (R(rt) != 0) R(rd) = R(rs); break; //movn398case 32: R(rd) = R(rs) + R(rt); break; //add (exception on overflow)399case 33: R(rd) = R(rs) + R(rt); break; //addu400case 34: R(rd) = R(rs) - R(rt); break; //sub (exception on overflow)401case 35: R(rd) = R(rs) - R(rt); break; //subu402case 36: R(rd) = R(rs) & R(rt); break; //and403case 37: R(rd) = R(rs) | R(rt); break; //or404case 38: R(rd) = R(rs) ^ R(rt); break; //xor405case 39: R(rd) = ~(R(rs) | R(rt)); break; //nor406case 42: R(rd) = (s32)R(rs) < (s32)R(rt); break; //slt407case 43: R(rd) = R(rs) < R(rt); break; //sltu408case 44: R(rd) = ((s32)R(rs) > (s32)R(rt)) ? R(rs) : R(rt); break; //max409case 45: R(rd) = ((s32)R(rs) < (s32)R(rt)) ? R(rs) : R(rt); break;//min410default:411_dbg_assert_msg_( 0, "Unknown MIPS instruction %08x", op.encoding);412break;413}414PC += 4;415}416417418void Int_ITypeMem(MIPSOpcode op)419{420int imm = (signed short)(op&0xFFFF);421int rt = _RT;422int rs = _RS;423u32 addr = R(rs) + imm;424425if (((op >> 29) & 1) == 0 && rt == 0) {426// Don't load anything into $zr427PC += 4;428return;429}430431switch (op >> 26)432{433case 32: R(rt) = SignExtend8ToU32(Memory::Read_U8(addr)); break; //lb434case 33: R(rt) = SignExtend16ToU32(Memory::Read_U16(addr)); break; //lh435case 35: R(rt) = Memory::Read_U32(addr); break; //lw436case 36: R(rt) = Memory::Read_U8 (addr); break; //lbu437case 37: R(rt) = Memory::Read_U16(addr); break; //lhu438case 40: Memory::Write_U8(R(rt), addr); break; //sb439case 41: Memory::Write_U16(R(rt), addr); break; //sh440case 43: Memory::Write_U32(R(rt), addr); break; //sw441442// When there's an LWL and an LWR together, we should be able to peephole optimize that443// into a single non-alignment-checking LW.444case 34: //lwl445{446u32 shift = (addr & 3) * 8;447u32 mem = Memory::Read_U32(addr & 0xfffffffc);448u32 result = ( u32(R(rt)) & (0x00ffffff >> shift) ) | ( mem << (24 - shift) );449R(rt) = result;450}451break;452453case 38: //lwr454{455u32 shift = (addr & 3) * 8;456u32 mem = Memory::Read_U32(addr & 0xfffffffc);457u32 regval = R(rt);458u32 result = ( regval & (0xffffff00 << (24 - shift)) ) | ( mem >> shift );459R(rt) = result;460}461break;462463case 42: //swl464{465u32 shift = (addr & 3) * 8;466u32 mem = Memory::Read_U32(addr & 0xfffffffc);467u32 result = ( ( u32(R(rt)) >> (24 - shift) ) ) | ( mem & (0xffffff00 << shift) );468Memory::Write_U32(result, (addr & 0xfffffffc));469}470break;471472case 46: //swr473{474u32 shift = (addr & 3) << 3;475u32 mem = Memory::Read_U32(addr & 0xfffffffc);476u32 result = ( ( u32(R(rt)) << shift ) | (mem & (0x00ffffff >> (24 - shift)) ) );477Memory::Write_U32(result, (addr & 0xfffffffc));478}479break;480481default:482_dbg_assert_msg_(false,"Trying to interpret Mem instruction that can't be interpreted");483break;484}485PC += 4;486}487488void Int_FPULS(MIPSOpcode op)489{490s32 offset = (s16)(op&0xFFFF);491int ft = _FT;492int rs = _RS;493u32 addr = R(rs) + offset;494495switch(op >> 26)496{497case 49: FI(ft) = Memory::Read_U32(addr); break; //lwc1498case 57: Memory::Write_U32(FI(ft), addr); break; //swc1499default:500_dbg_assert_msg_(false,"Trying to interpret FPULS instruction that can't be interpreted");501break;502}503PC += 4;504}505506void Int_mxc1(MIPSOpcode op)507{508int fs = _FS;509int rt = _RT;510511switch ((op>>21)&0x1f) {512case 0: //mfc1513if (rt != 0)514R(rt) = FI(fs);515break;516517case 2: //cfc1518if (rt != 0) {519if (fs == 31) {520currentMIPS->fcr31 = (currentMIPS->fcr31 & ~(1<<23)) | ((currentMIPS->fpcond & 1)<<23);521R(rt) = currentMIPS->fcr31;522} else if (fs == 0) {523R(rt) = MIPSState::FCR0_VALUE;524} else {525WARN_LOG_REPORT(Log::CPU, "ReadFCR: Unexpected reg %d", fs);526R(rt) = 0;527}528break;529}530531case 4: //mtc1532FI(fs) = R(rt);533break;534535case 6: //ctc1536{537u32 value = R(rt);538if (fs == 31) {539currentMIPS->fcr31 = value & 0x0181FFFF;540currentMIPS->fpcond = (value >> 23) & 1;541// Don't bother locking, assuming the CPU can't be reset now anyway.542if (MIPSComp::jit) {543// In case of DISABLE, we need to tell jit we updated FCR31.544MIPSComp::jit->UpdateFCR31();545}546} else {547WARN_LOG_REPORT(Log::CPU, "WriteFCR: Unexpected reg %d (value %08x)", fs, value);548}549DEBUG_LOG(Log::CPU, "FCR%i written to, value %08x", fs, value);550break;551}552553default:554_dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted");555break;556}557PC += 4;558}559560void Int_RType2(MIPSOpcode op)561{562int rs = _RS;563int rd = _RD;564565// Don't change $zr.566if (rd == 0)567{568PC += 4;569return;570}571572switch (op & 63)573{574case 22: //clz575R(rd) = clz32(R(rs));576break;577case 23: //clo578R(rd) = clz32(~R(rs));579break;580default:581_dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted");582break;583}584PC += 4;585}586587void Int_MulDivType(MIPSOpcode op)588{589int rt = _RT;590int rs = _RS;591int rd = _RD;592593switch (op & 63)594{595case 24: //mult596{597s64 result = (s64)(s32)R(rs) * (s64)(s32)R(rt);598u64 resultBits = (u64)(result);599LO = (u32)(resultBits);600HI = (u32)(resultBits>>32);601}602break;603case 25: //multu604{605u64 resultBits = (u64)R(rs) * (u64)R(rt);606LO = (u32)(resultBits);607HI = (u32)(resultBits>>32);608}609break;610case 28: //madd611{612u32 a=R(rs),b=R(rt),hi=HI,lo=LO;613u64 origValBits = (u64)lo | ((u64)(hi)<<32);614s64 origVal = (s64)origValBits;615s64 result = origVal + (s64)(s32)a * (s64)(s32)b;616u64 resultBits = (u64)(result);617LO = (u32)(resultBits);618HI = (u32)(resultBits>>32);619}620break;621case 29: //maddu622{623u32 a=R(rs),b=R(rt),hi=HI,lo=LO;624u64 origVal = (u64)lo | ((u64)(hi)<<32);625u64 result = origVal + (u64)a * (u64)b;626LO = (u32)(result);627HI = (u32)(result>>32);628}629break;630case 46: //msub631{632u32 a=R(rs),b=R(rt),hi=HI,lo=LO;633u64 origValBits = (u64)lo | ((u64)(hi)<<32);634s64 origVal = (s64)origValBits;635s64 result = origVal - (s64)(s32)a * (s64)(s32)b;636u64 resultBits = (u64)(result);637LO = (u32)(resultBits);638HI = (u32)(resultBits>>32);639}640break;641case 47: //msubu642{643u32 a=R(rs),b=R(rt),hi=HI,lo=LO;644u64 origVal = (u64)lo | ((u64)(hi)<<32);645u64 result = origVal - (u64)a * (u64)b;646LO = (u32)(result);647HI = (u32)(result>>32);648}649break;650case 16: if (rd != 0) R(rd) = HI; break; //mfhi651case 17: HI = R(rs); break; //mthi652case 18: if (rd != 0) R(rd) = LO; break; //mflo653case 19: LO = R(rs); break; //mtlo654case 26: //div655{656s32 a = (s32)R(rs);657s32 b = (s32)R(rt);658if (a == (s32)0x80000000 && b == -1) {659LO = 0x80000000;660HI = -1;661} else if (b != 0) {662LO = (u32)(a / b);663HI = (u32)(a % b);664} else {665LO = a < 0 ? 1 : -1;666HI = a;667}668}669break;670case 27: //divu671{672u32 a = R(rs);673u32 b = R(rt);674if (b != 0) {675LO = (a/b);676HI = (a%b);677} else {678LO = a <= 0xFFFF ? 0xFFFF : -1;679HI = a;680}681}682break;683684default:685_dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted");686break;687}688PC += 4;689}690691692void Int_ShiftType(MIPSOpcode op)693{694int rt = _RT;695int rs = _RS;696int rd = _RD;697int sa = _FD;698699// Don't change $zr.700if (rd == 0)701{702PC += 4;703return;704}705706switch (op & 0x3f)707{708case 0: R(rd) = R(rt) << sa; break; //sll709case 2:710if (_RS == 0) //srl711{712R(rd) = R(rt) >> sa;713break;714}715else if (_RS == 1) //rotr716{717R(rd) = __rotr(R(rt), sa);718break;719}720else721goto wrong;722723case 3: R(rd) = (u32)(((s32)R(rt)) >> sa); break; //sra724case 4: R(rd) = R(rt) << (R(rs)&0x1F); break; //sllv725case 6:726if (_FD == 0) //srlv727{728R(rd) = R(rt) >> (R(rs)&0x1F);729break;730}731else if (_FD == 1) // rotrv732{733R(rd) = __rotr(R(rt), R(rs));734break;735}736else goto wrong;737case 7: R(rd) = (u32)(((s32)R(rt)) >> (R(rs)&0x1F)); break; //srav738default:739wrong:740_dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted");741break;742}743PC += 4;744}745746void Int_Allegrex(MIPSOpcode op)747{748int rt = _RT;749int rd = _RD;750751// Don't change $zr.752if (rd == 0)753{754PC += 4;755return;756}757758switch((op>>6)&31)759{760case 16: // seb761R(rd) = SignExtend8ToU32(R(rt));762break;763764case 20: // bitrev765{766u32 tmp = 0;767for (int i = 0; i < 32; i++)768{769if (R(rt) & (1 << i))770{771tmp |= (0x80000000 >> i);772}773}774R(rd) = tmp;775}776break;777778case 24: // seh779R(rd) = SignExtend16ToU32(R(rt));780break;781782default:783_dbg_assert_msg_(false,"Trying to interpret ALLEGREX instruction that can't be interpreted");784break;785}786PC += 4;787}788789void Int_Allegrex2(MIPSOpcode op)790{791int rt = _RT;792int rd = _RD;793794// Don't change $zr.795if (rd == 0)796{797PC += 4;798return;799}800801switch (op & 0x3ff)802{803case 0xA0: //wsbh804R(rd) = ((R(rt) & 0xFF00FF00) >> 8) | ((R(rt) & 0x00FF00FF) << 8);805break;806case 0xE0: //wsbw807R(rd) = swap32(R(rt));808break;809default:810_dbg_assert_msg_(false,"Trying to interpret ALLEGREX instruction that can't be interpreted");811break;812}813PC += 4;814}815816void Int_Special2(MIPSOpcode op)817{818static int reported = 0;819switch (op & 0x3F)820{821case 36: // mfic822if (!reported) {823Reporting::ReportMessage("MFIC instruction hit (%08x) at %08x", op.encoding, currentMIPS->pc);824WARN_LOG(Log::CPU,"MFIC Disable/Enable Interrupt CPU instruction");825reported = 1;826}827break;828case 38: // mtic829if (!reported) {830Reporting::ReportMessage("MTIC instruction hit (%08x) at %08x", op.encoding, currentMIPS->pc);831WARN_LOG(Log::CPU,"MTIC Disable/Enable Interrupt CPU instruction");832reported = 1;833}834break;835}836PC += 4;837}838839void Int_Special3(MIPSOpcode op)840{841int rs = _RS;842int rt = _RT;843int pos = _POS;844845// Don't change $zr.846if (rt == 0) {847PC += 4;848return;849}850851switch (op & 0x3f) {852case 0x0: //ext853{854int size = _SIZE + 1;855u32 sourcemask = 0xFFFFFFFFUL >> (32 - size);856R(rt) = (R(rs) >> pos) & sourcemask;857}858break;859case 0x4: //ins860{861int size = (_SIZE + 1) - pos;862u32 sourcemask = 0xFFFFFFFFUL >> (32 - size);863u32 destmask = sourcemask << pos;864R(rt) = (R(rt) & ~destmask) | ((R(rs)&sourcemask) << pos);865}866break;867}868869PC += 4;870}871872void Int_FPU2op(MIPSOpcode op)873{874int fs = _FS;875int fd = _FD;876877switch (op & 0x3f)878{879case 4: F(fd) = sqrtf(F(fs)); break; //sqrt880case 5: F(fd) = fabsf(F(fs)); break; //abs881case 6: F(fd) = F(fs); break; //mov882case 7: F(fd) = -F(fs); break; //neg883case 12:884case 13:885case 14:886case 15:887if (my_isnanorinf(F(fs)))888{889FsI(fd) = my_isinf(F(fs)) && F(fs) < 0.0f ? -2147483648LL : 2147483647LL;890break;891}892switch (op & 0x3f)893{894case 12: FsI(fd) = (int)floorf(F(fs)+0.5f); break; //round.w.s895case 13: //trunc.w.s896if (F(fs) >= 0.0f) {897FsI(fd) = (int)floorf(F(fs));898// Overflow, but it was positive.899if (FsI(fd) == -2147483648LL) {900FsI(fd) = 2147483647LL;901}902} else {903// Overflow happens to be the right value anyway.904FsI(fd) = (int)ceilf(F(fs));905}906break;907case 14: FsI(fd) = (int)ceilf (F(fs)); break; //ceil.w.s908case 15: FsI(fd) = (int)floorf(F(fs)); break; //floor.w.s909}910break;911case 32: F(fd) = (float)FsI(fs); break; //cvt.s.w912913case 36:914if (my_isnanorinf(F(fs)))915{916FsI(fd) = my_isinf(F(fs)) && F(fs) < 0.0f ? -2147483648LL : 2147483647LL;917break;918}919switch (currentMIPS->fcr31 & 3)920{921case 0: FsI(fd) = (int)round_ieee_754(F(fs)); break; // RINT_0922case 1: FsI(fd) = (int)F(fs); break; // CAST_1923case 2: FsI(fd) = (int)ceilf(F(fs)); break; // CEIL_2924case 3: FsI(fd) = (int)floorf(F(fs)); break; // FLOOR_3925}926break; //cvt.w.s927default:928_dbg_assert_msg_(false,"Trying to interpret FPU2Op instruction that can't be interpreted");929break;930}931PC += 4;932}933934void Int_FPUComp(MIPSOpcode op)935{936int fs = _FS;937int ft = _FT;938bool cond;939switch (op & 0xf)940{941case 0: //f942case 8: //sf943cond = false;944break;945946case 1: //un947case 9: //ngle948cond = my_isnan(F(fs)) || my_isnan(F(ft));949break;950951case 2: //eq952case 10: //seq953cond = !my_isnan(F(fs)) && !my_isnan(F(ft)) && (F(fs) == F(ft));954break;955956case 3: //ueq957case 11: //ngl958cond = (F(fs) == F(ft)) || my_isnan(F(fs)) || my_isnan(F(ft));959break;960961case 4: //olt962case 12: //lt963cond = (F(fs) < F(ft));964break;965966case 5: //ult967case 13: //nge968cond = (F(fs) < F(ft)) || my_isnan(F(fs)) || my_isnan(F(ft));969break;970971case 6: //ole972case 14: //le973cond = (F(fs) <= F(ft));974break;975976case 7: //ule977case 15: //ngt978cond = (F(fs) <= F(ft)) || my_isnan(F(fs)) || my_isnan(F(ft));979break;980981default:982_dbg_assert_msg_(false,"Trying to interpret FPUComp instruction that can't be interpreted");983cond = false;984break;985}986currentMIPS->fpcond = cond;987PC += 4;988}989990void Int_FPU3op(MIPSOpcode op)991{992int ft = _FT;993int fs = _FS;994int fd = _FD;995996switch (op & 0x3f)997{998case 0: F(fd) = F(fs) + F(ft); break; // add.s999case 1: F(fd) = F(fs) - F(ft); break; // sub.s1000case 2: // mul.s1001if ((my_isinf(F(fs)) && F(ft) == 0.0f) || (my_isinf(F(ft)) && F(fs) == 0.0f)) {1002// Must be positive NAN, see #12519.1003FI(fd) = 0x7fc00000;1004} else {1005F(fd) = F(fs) * F(ft);1006}1007break;1008case 3: F(fd) = F(fs) / F(ft); break; // div.s1009default:1010_dbg_assert_msg_(false,"Trying to interpret FPU3Op instruction that can't be interpreted");1011break;1012}1013PC += 4;1014}10151016void Int_Interrupt(MIPSOpcode op)1017{1018static int reported = 0;1019switch (op & 1)1020{1021case 0:1022if (!reported) {1023Reporting::ReportMessage("INTERRUPT instruction hit (%08x) at %08x", op.encoding, currentMIPS->pc);1024WARN_LOG(Log::CPU,"Disable/Enable Interrupt CPU instruction");1025reported = 1;1026}1027break;1028}1029PC += 4;1030}10311032void Int_Emuhack(MIPSOpcode op)1033{1034if (((op >> 24) & 3) != EMUOP_CALL_REPLACEMENT) {1035_dbg_assert_msg_(false,"Trying to interpret emuhack instruction that can't be interpreted");1036}1037// It's a replacement func!1038int index = op.encoding & 0xFFFFFF;1039const ReplacementTableEntry *entry = GetReplacementFunc(index);1040if (entry && entry->replaceFunc && (entry->flags & REPFLAG_DISABLED) == 0) {1041int cycles = entry->replaceFunc();10421043if (entry->flags & (REPFLAG_HOOKENTER | REPFLAG_HOOKEXIT)) {1044// Interpret the original instruction under the hook.1045MIPSInterpret(Memory::Read_Instruction(PC, true));1046} else if (cycles < 0) {1047// Leave PC unchanged, call the replacement again (assumes args are modified.)1048currentMIPS->downcount += cycles;1049} else {1050PC = currentMIPS->r[MIPS_REG_RA];1051currentMIPS->downcount -= cycles;1052}1053} else {1054if (!entry || !entry->replaceFunc) {1055ERROR_LOG(Log::CPU, "Bad replacement function index %i", index);1056}1057// Interpret the original instruction under it.1058MIPSInterpret(Memory::Read_Instruction(PC, true));1059}1060}106110621063}106410651066