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/ARM/ArmCompALU.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 "ppsspp_config.h"18#if PPSSPP_ARCH(ARM)1920#include <algorithm>2122#include "Common/BitSet.h"23#include "Common/CPUDetect.h"24#include "Common/Data/Convert/SmallDataConvert.h"25#include "Core/MIPS/MIPS.h"26#include "Core/MIPS/MIPSAnalyst.h"27#include "Core/MIPS/MIPSCodeUtils.h"28#include "Core/MIPS/ARM/ArmJit.h"29#include "Core/MIPS/ARM/ArmRegCache.h"3031using namespace MIPSAnalyst;3233#define _RS MIPS_GET_RS(op)34#define _RT MIPS_GET_RT(op)35#define _RD MIPS_GET_RD(op)36#define _FS MIPS_GET_FS(op)37#define _FT MIPS_GET_FT(op)38#define _FD MIPS_GET_FD(op)39#define _SA MIPS_GET_SA(op)40#define _POS ((op>> 6) & 0x1F)41#define _SIZE ((op>>11) & 0x1F)42#define _IMM16 (signed short)(op & 0xFFFF)43#define _IMM26 (op & 0x03FFFFFF)4445// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly.46// Currently known non working ones should have DISABLE.4748// #define CONDITIONAL_DISABLE(flag) { Comp_Generic(op); return; }49#define CONDITIONAL_DISABLE(flag) if (jo.Disabled(JitDisable::flag)) { Comp_Generic(op); return; }50#define DISABLE { Comp_Generic(op); return; }5152namespace MIPSComp53{54using namespace ArmGen;55using namespace ArmJitConstants;5657static u32 EvalOr(u32 a, u32 b) { return a | b; }58static u32 EvalEor(u32 a, u32 b) { return a ^ b; }59static u32 EvalAnd(u32 a, u32 b) { return a & b; }60static u32 EvalAdd(u32 a, u32 b) { return a + b; }61static u32 EvalSub(u32 a, u32 b) { return a - b; }6263void ArmJit::CompImmLogic(MIPSGPReg rs, MIPSGPReg rt, u32 uimm, void (ARMXEmitter::*arith)(ARMReg dst, ARMReg src, Operand2 op2), bool (ARMXEmitter::*tryArithI2R)(ARMReg dst, ARMReg src, u32 val), u32 (*eval)(u32 a, u32 b))64{65if (gpr.IsImm(rs)) {66gpr.SetImm(rt, (*eval)(gpr.GetImm(rs), uimm));67} else {68gpr.MapDirtyIn(rt, rs);69if (!(this->*tryArithI2R)(gpr.R(rt), gpr.R(rs), uimm)) {70gpr.SetRegImm(SCRATCHREG1, uimm);71(this->*arith)(gpr.R(rt), gpr.R(rs), SCRATCHREG1);72}73}74}7576void ArmJit::Comp_IType(MIPSOpcode op)77{78CONDITIONAL_DISABLE(ALU_IMM);79u32 uimm = op & 0xFFFF;80s32 simm = SignExtend16ToS32(op);81u32 suimm = SignExtend16ToU32(op);8283MIPSGPReg rt = _RT;84MIPSGPReg rs = _RS;8586// noop, won't write to ZERO.87if (rt == 0)88return;8990switch (op >> 26)91{92case 8: // same as addiu?93case 9: // R(rt) = R(rs) + simm; break; //addiu94CompImmLogic(rs, rt, simm, &ARMXEmitter::ADD, &ARMXEmitter::TryADDI2R, &EvalAdd);95break;9697case 12: CompImmLogic(rs, rt, uimm, &ARMXEmitter::AND, &ARMXEmitter::TryANDI2R, &EvalAnd); break;98case 13: CompImmLogic(rs, rt, uimm, &ARMXEmitter::ORR, &ARMXEmitter::TryORI2R, &EvalOr); break;99case 14: CompImmLogic(rs, rt, uimm, &ARMXEmitter::EOR, &ARMXEmitter::TryEORI2R, &EvalEor); break;100101case 10: // R(rt) = (s32)R(rs) < simm; break; //slti102{103if (gpr.IsImm(rs)) {104gpr.SetImm(rt, (s32)gpr.GetImm(rs) < simm ? 1 : 0);105break;106} else if (simm == 0) {107gpr.MapDirtyIn(rt, rs);108// Shift to get the sign bit only (for < 0.)109LSR(gpr.R(rt), gpr.R(rs), 31);110break;111}112gpr.MapDirtyIn(rt, rs);113if (!TryCMPI2R(gpr.R(rs), simm)) {114gpr.SetRegImm(SCRATCHREG1, simm);115CMP(gpr.R(rs), SCRATCHREG1);116}117SetCC(CC_LT);118MOVI2R(gpr.R(rt), 1);119SetCC(CC_GE);120MOVI2R(gpr.R(rt), 0);121SetCC(CC_AL);122}123break;124125case 11: // R(rt) = R(rs) < suimm; break; //sltiu126{127if (gpr.IsImm(rs)) {128gpr.SetImm(rt, gpr.GetImm(rs) < suimm ? 1 : 0);129break;130}131gpr.MapDirtyIn(rt, rs);132if (!TryCMPI2R(gpr.R(rs), suimm)) {133gpr.SetRegImm(SCRATCHREG1, suimm);134CMP(gpr.R(rs), SCRATCHREG1);135}136SetCC(CC_LO);137MOVI2R(gpr.R(rt), 1);138SetCC(CC_HS);139MOVI2R(gpr.R(rt), 0);140SetCC(CC_AL);141}142break;143144case 15: // R(rt) = uimm << 16; //lui145gpr.SetImm(rt, uimm << 16);146break;147148default:149Comp_Generic(op);150break;151}152}153154void ArmJit::Comp_RType2(MIPSOpcode op)155{156CONDITIONAL_DISABLE(ALU_BIT);157MIPSGPReg rs = _RS;158MIPSGPReg rd = _RD;159160// Don't change $zr.161if (rd == 0)162return;163164switch (op & 63)165{166case 22: //clz167if (gpr.IsImm(rs)) {168u32 value = gpr.GetImm(rs);169int x = 31;170int count = 0;171while (x >= 0 && !(value & (1 << x))) {172count++;173x--;174}175gpr.SetImm(rd, count);176break;177}178gpr.MapDirtyIn(rd, rs);179CLZ(gpr.R(rd), gpr.R(rs));180break;181case 23: //clo182if (gpr.IsImm(rs)) {183u32 value = gpr.GetImm(rs);184int x = 31;185int count = 0;186while (x >= 0 && (value & (1 << x))) {187count++;188x--;189}190gpr.SetImm(rd, count);191break;192}193gpr.MapDirtyIn(rd, rs);194MVN(SCRATCHREG1, gpr.R(rs));195CLZ(gpr.R(rd), SCRATCHREG1);196break;197default:198DISABLE;199}200}201202void ArmJit::CompType3(MIPSGPReg rd, MIPSGPReg rs, MIPSGPReg rt, void (ARMXEmitter::*arith)(ARMReg dst, ARMReg rm, Operand2 rn), bool (ARMXEmitter::*tryArithI2R)(ARMReg dst, ARMReg rm, u32 val), u32 (*eval)(u32 a, u32 b), bool symmetric)203{204if (gpr.IsImm(rs) && gpr.IsImm(rt)) {205gpr.SetImm(rd, (*eval)(gpr.GetImm(rs), gpr.GetImm(rt)));206return;207}208209if (gpr.IsImm(rt) || (gpr.IsImm(rs) && symmetric)) {210MIPSGPReg lhs = gpr.IsImm(rs) ? rt : rs;211MIPSGPReg rhs = gpr.IsImm(rs) ? rs : rt;212u32 rhsImm = gpr.GetImm(rhs);213gpr.MapDirtyIn(rd, lhs);214if ((this->*tryArithI2R)(gpr.R(rd), gpr.R(lhs), rhsImm)) {215return;216}217// If rd is rhs, we may have lost it in the MapDirtyIn(). lhs was kept.218// This means the rhsImm value was never flushed to rhs, and would be garbage.219if (rd == rhs) {220// Luckily, it was just an imm.221gpr.SetImm(rhs, rhsImm);222}223} else if (gpr.IsImm(rs) && !symmetric) {224Operand2 op2;225// For SUB, we can use RSB as a reverse operation.226if (eval == &EvalSub && TryMakeOperand2(gpr.GetImm(rs), op2)) {227gpr.MapDirtyIn(rd, rt);228RSB(gpr.R(rd), gpr.R(rt), op2);229return;230}231}232233// Generic solution. If it's an imm, better to flush at this point.234gpr.MapDirtyInIn(rd, rs, rt);235(this->*arith)(gpr.R(rd), gpr.R(rs), gpr.R(rt));236}237238void ArmJit::Comp_RType3(MIPSOpcode op)239{240CONDITIONAL_DISABLE(ALU);241MIPSGPReg rt = _RT;242MIPSGPReg rs = _RS;243MIPSGPReg rd = _RD;244245// noop, won't write to ZERO.246if (rd == 0)247return;248249switch (op & 63)250{251case 10: //if (!R(rt)) R(rd) = R(rs); break; //movz252if (rd == rs || (gpr.IsImm(rd) && gpr.IsImm(rs) && gpr.GetImm(rd) == gpr.GetImm(rs)))253break;254if (!gpr.IsImm(rt)) {255Operand2 op2;256// Avoid flushing the imm if possible.257if (gpr.IsImm(rs) && TryMakeOperand2(gpr.GetImm(rs), op2)) {258gpr.MapDirtyIn(rd, rt, false);259} else {260gpr.MapDirtyInIn(rd, rt, rs, false);261op2 = gpr.R(rs);262}263CMP(gpr.R(rt), Operand2(0));264SetCC(CC_EQ);265MOV(gpr.R(rd), op2);266SetCC(CC_AL);267} else if (gpr.GetImm(rt) == 0) {268// Yes, this actually happens.269if (gpr.IsImm(rs)) {270gpr.SetImm(rd, gpr.GetImm(rs));271} else {272gpr.MapDirtyIn(rd, rs);273MOV(gpr.R(rd), gpr.R(rs));274}275}276break;277case 11:// if (R(rt)) R(rd) = R(rs); break; //movn278if (rd == rs || (gpr.IsImm(rd) && gpr.IsImm(rs) && gpr.GetImm(rd) == gpr.GetImm(rs)))279break;280if (!gpr.IsImm(rt)) {281Operand2 op2;282// Avoid flushing the imm if possible.283if (gpr.IsImm(rs) && TryMakeOperand2(gpr.GetImm(rs), op2)) {284gpr.MapDirtyIn(rd, rt, false);285} else {286gpr.MapDirtyInIn(rd, rt, rs, false);287op2 = gpr.R(rs);288}289CMP(gpr.R(rt), Operand2(0));290SetCC(CC_NEQ);291MOV(gpr.R(rd), op2);292SetCC(CC_AL);293} else if (gpr.GetImm(rt) != 0) {294// Yes, this actually happens.295if (gpr.IsImm(rs)) {296gpr.SetImm(rd, gpr.GetImm(rs));297} else {298gpr.MapDirtyIn(rd, rs);299MOV(gpr.R(rd), gpr.R(rs));300}301}302break;303304case 32: //R(rd) = R(rs) + R(rt); break; //add305case 33: //R(rd) = R(rs) + R(rt); break; //addu306// We optimize out 0 as an operand2 ADD.307CompType3(rd, rs, rt, &ARMXEmitter::ADD, &ARMXEmitter::TryADDI2R, &EvalAdd, true);308break;309310case 34: //R(rd) = R(rs) - R(rt); break; //sub311case 35: //R(rd) = R(rs) - R(rt); break; //subu312CompType3(rd, rs, rt, &ARMXEmitter::SUB, &ARMXEmitter::TrySUBI2R, &EvalSub, false);313break;314case 36: //R(rd) = R(rs) & R(rt); break; //and315CompType3(rd, rs, rt, &ARMXEmitter::AND, &ARMXEmitter::TryANDI2R, &EvalAnd, true);316break;317case 37: //R(rd) = R(rs) | R(rt); break; //or318CompType3(rd, rs, rt, &ARMXEmitter::ORR, &ARMXEmitter::TryORI2R, &EvalOr, true);319break;320case 38: //R(rd) = R(rs) ^ R(rt); break; //xor/eor321CompType3(rd, rs, rt, &ARMXEmitter::EOR, &ARMXEmitter::TryEORI2R, &EvalEor, true);322break;323324case 39: // R(rd) = ~(R(rs) | R(rt)); break; //nor325if (gpr.IsImm(rs) && gpr.IsImm(rt)) {326gpr.SetImm(rd, ~(gpr.GetImm(rs) | gpr.GetImm(rt)));327} else if (gpr.IsImm(rs) || gpr.IsImm(rt)) {328MIPSGPReg lhs = gpr.IsImm(rs) ? rt : rs;329MIPSGPReg rhs = gpr.IsImm(rs) ? rs : rt;330u32 rhsImm = gpr.GetImm(rhs);331Operand2 op2;332if (TryMakeOperand2(rhsImm, op2)) {333gpr.MapDirtyIn(rd, lhs);334} else {335gpr.MapDirtyInIn(rd, rs, rt);336op2 = gpr.R(rhs);337}338if (rhsImm == 0) {339MVN(gpr.R(rd), gpr.R(lhs));340} else {341ORR(gpr.R(rd), gpr.R(lhs), op2);342MVN(gpr.R(rd), gpr.R(rd));343}344} else {345gpr.MapDirtyInIn(rd, rs, rt);346ORR(gpr.R(rd), gpr.R(rs), gpr.R(rt));347MVN(gpr.R(rd), gpr.R(rd));348}349break;350351case 42: //R(rd) = (int)R(rs) < (int)R(rt); break; //slt352if (gpr.IsImm(rs) && gpr.IsImm(rt)) {353gpr.SetImm(rd, (s32)gpr.GetImm(rs) < (s32)gpr.GetImm(rt));354} else {355CCFlags caseOne = CC_LT;356CCFlags caseZero = CC_GE;357Operand2 op2;358bool negated;359if (gpr.IsImm(rs) && TryMakeOperand2_AllowNegation(gpr.GetImm(rs), op2, &negated)) {360gpr.MapDirtyIn(rd, rt);361if (!negated)362CMP(gpr.R(rt), op2);363else364CMN(gpr.R(rt), op2);365366// Swap the condition since we swapped the arguments.367caseOne = CC_GT;368caseZero = CC_LE;369} else if (gpr.IsImm(rt) && TryMakeOperand2_AllowNegation(gpr.GetImm(rt), op2, &negated)) {370gpr.MapDirtyIn(rd, rs);371if (!negated)372CMP(gpr.R(rs), op2);373else374CMN(gpr.R(rs), op2);375} else {376gpr.MapDirtyInIn(rd, rs, rt);377CMP(gpr.R(rs), gpr.R(rt));378}379380SetCC(caseOne);381MOVI2R(gpr.R(rd), 1);382SetCC(caseZero);383MOVI2R(gpr.R(rd), 0);384SetCC(CC_AL);385}386break;387388case 43: //R(rd) = R(rs) < R(rt); break; //sltu389if (gpr.IsImm(rs) && gpr.IsImm(rt)) {390gpr.SetImm(rd, gpr.GetImm(rs) < gpr.GetImm(rt));391} else {392CCFlags caseOne = CC_LO;393CCFlags caseZero = CC_HS;394Operand2 op2;395bool negated;396if (gpr.IsImm(rs) && TryMakeOperand2_AllowNegation(gpr.GetImm(rs), op2, &negated)) {397gpr.MapDirtyIn(rd, rt);398if (!negated)399CMP(gpr.R(rt), op2);400else401CMN(gpr.R(rt), op2);402403// Swap the condition since we swapped the arguments.404caseOne = CC_HI;405caseZero = CC_LS;406} else if (gpr.IsImm(rt) && TryMakeOperand2_AllowNegation(gpr.GetImm(rt), op2, &negated)) {407gpr.MapDirtyIn(rd, rs);408if (!negated)409CMP(gpr.R(rs), op2);410else411CMN(gpr.R(rs), op2);412} else {413gpr.MapDirtyInIn(rd, rs, rt);414CMP(gpr.R(rs), gpr.R(rt));415}416417SetCC(caseOne);418MOVI2R(gpr.R(rd), 1);419SetCC(caseZero);420MOVI2R(gpr.R(rd), 0);421SetCC(CC_AL);422}423break;424425case 44: //R(rd) = max(R(rs), R(rt); break; //max426if (gpr.IsImm(rs) && gpr.IsImm(rt)) {427gpr.SetImm(rd, std::max(gpr.GetImm(rs), gpr.GetImm(rt)));428break;429}430gpr.MapDirtyInIn(rd, rs, rt);431CMP(gpr.R(rs), gpr.R(rt));432SetCC(CC_GT);433if (rd != rs)434MOV(gpr.R(rd), gpr.R(rs));435SetCC(CC_LE);436if (rd != rt)437MOV(gpr.R(rd), gpr.R(rt));438SetCC(CC_AL);439break;440441case 45: //R(rd) = min(R(rs), R(rt)); break; //min442if (gpr.IsImm(rs) && gpr.IsImm(rt)) {443gpr.SetImm(rd, std::min(gpr.GetImm(rs), gpr.GetImm(rt)));444break;445}446gpr.MapDirtyInIn(rd, rs, rt);447CMP(gpr.R(rs), gpr.R(rt));448SetCC(CC_LT);449if (rd != rs)450MOV(gpr.R(rd), gpr.R(rs));451SetCC(CC_GE);452if (rd != rt)453MOV(gpr.R(rd), gpr.R(rt));454SetCC(CC_AL);455break;456457default:458Comp_Generic(op);459break;460}461}462463void ArmJit::CompShiftImm(MIPSOpcode op, ArmGen::ShiftType shiftType, int sa)464{465MIPSGPReg rd = _RD;466MIPSGPReg rt = _RT;467468if (gpr.IsImm(rt)) {469switch (shiftType) {470case ST_LSL:471gpr.SetImm(rd, gpr.GetImm(rt) << sa);472break;473case ST_LSR:474gpr.SetImm(rd, gpr.GetImm(rt) >> sa);475break;476case ST_ASR:477gpr.SetImm(rd, (int)gpr.GetImm(rt) >> sa);478break;479case ST_ROR:480gpr.SetImm(rd, (gpr.GetImm(rt) >> sa) | (gpr.GetImm(rt) << (32 - sa)));481break;482default:483DISABLE;484}485} else {486gpr.MapDirtyIn(rd, rt);487MOV(gpr.R(rd), Operand2(gpr.R(rt), shiftType, sa));488}489}490491void ArmJit::CompShiftVar(MIPSOpcode op, ArmGen::ShiftType shiftType)492{493MIPSGPReg rd = _RD;494MIPSGPReg rt = _RT;495MIPSGPReg rs = _RS;496if (gpr.IsImm(rs)) {497int sa = gpr.GetImm(rs) & 0x1F;498CompShiftImm(op, shiftType, sa);499return;500}501gpr.MapDirtyInIn(rd, rs, rt);502AND(SCRATCHREG1, gpr.R(rs), Operand2(0x1F));503MOV(gpr.R(rd), Operand2(gpr.R(rt), shiftType, SCRATCHREG1));504}505506void ArmJit::Comp_ShiftType(MIPSOpcode op)507{508CONDITIONAL_DISABLE(ALU);509MIPSGPReg rs = _RS;510MIPSGPReg rd = _RD;511int fd = _FD;512int sa = _SA;513514// noop, won't write to ZERO.515if (rd == 0)516return;517518// WARNING : ROTR519switch (op & 0x3f) {520case 0: CompShiftImm(op, ST_LSL, sa); break; //sll521case 2: CompShiftImm(op, rs == 1 ? ST_ROR : ST_LSR, sa); break; //srl522case 3: CompShiftImm(op, ST_ASR, sa); break; //sra523case 4: CompShiftVar(op, ST_LSL); break; //sllv524case 6: CompShiftVar(op, fd == 1 ? ST_ROR : ST_LSR); break; //srlv525case 7: CompShiftVar(op, ST_ASR); break; //srav526527default:528Comp_Generic(op);529break;530}531}532533void ArmJit::Comp_Special3(MIPSOpcode op)534{535CONDITIONAL_DISABLE(ALU_BIT);536537MIPSGPReg rs = _RS;538MIPSGPReg rt = _RT;539540int pos = _POS;541int size = _SIZE + 1;542u32 mask = 0xFFFFFFFFUL >> (32 - size);543544// Don't change $zr.545if (rt == 0)546return;547548switch (op & 0x3f) {549case 0x0: //ext550if (gpr.IsImm(rs)) {551gpr.SetImm(rt, (gpr.GetImm(rs) >> pos) & mask);552return;553}554555gpr.MapDirtyIn(rt, rs);556#if PPSSPP_ARCH(ARMV7)557UBFX(gpr.R(rt), gpr.R(rs), pos, size);558#else559MOV(gpr.R(rt), Operand2(gpr.R(rs), ST_LSR, pos));560ANDI2R(gpr.R(rt), gpr.R(rt), mask, SCRATCHREG1);561#endif562break;563564case 0x4: //ins565{566u32 sourcemask = mask >> pos;567u32 destmask = ~(sourcemask << pos);568if (gpr.IsImm(rs)) {569u32 inserted = (gpr.GetImm(rs) & sourcemask) << pos;570if (gpr.IsImm(rt)) {571gpr.SetImm(rt, (gpr.GetImm(rt) & destmask) | inserted);572return;573}574575gpr.MapReg(rt, MAP_DIRTY);576ANDI2R(gpr.R(rt), gpr.R(rt), destmask, SCRATCHREG1);577if (inserted != 0) {578ORI2R(gpr.R(rt), gpr.R(rt), inserted, SCRATCHREG1);579}580} else {581gpr.MapDirtyIn(rt, rs, false);582#if PPSSPP_ARCH(ARMV7)583BFI(gpr.R(rt), gpr.R(rs), pos, size - pos);584#else585ANDI2R(SCRATCHREG1, gpr.R(rs), sourcemask, SCRATCHREG2);586ANDI2R(gpr.R(rt), gpr.R(rt), destmask, SCRATCHREG2);587ORR(gpr.R(rt), gpr.R(rt), Operand2(SCRATCHREG1, ST_LSL, pos));588#endif589}590}591break;592}593}594595void ArmJit::Comp_Allegrex(MIPSOpcode op)596{597CONDITIONAL_DISABLE(ALU_BIT);598MIPSGPReg rt = _RT;599MIPSGPReg rd = _RD;600// Don't change $zr.601if (rd == 0)602return;603604switch ((op >> 6) & 31) {605case 16: // seb // R(rd) = SignExtend8ToU32(R(rt));606if (gpr.IsImm(rt)) {607gpr.SetImm(rd, SignExtend8ToU32(gpr.GetImm(rt)));608return;609}610gpr.MapDirtyIn(rd, rt);611SXTB(gpr.R(rd), gpr.R(rt));612break;613614case 24: // seh615if (gpr.IsImm(rt)) {616gpr.SetImm(rd, SignExtend16ToU32(gpr.GetImm(rt)));617return;618}619gpr.MapDirtyIn(rd, rt);620SXTH(gpr.R(rd), gpr.R(rt));621break;622623case 20: //bitrev624if (gpr.IsImm(rt)) {625// http://graphics.stanford.edu/~seander/bithacks.html#ReverseParallel626u32 v = gpr.GetImm(rt);627v = ((v >> 1) & 0x55555555) | ((v & 0x55555555) << 1); // odd<->even628v = ((v >> 2) & 0x33333333) | ((v & 0x33333333) << 2); // pair<->pair629v = ((v >> 4) & 0x0F0F0F0F) | ((v & 0x0F0F0F0F) << 4); // nibb<->nibb630v = ((v >> 8) & 0x00FF00FF) | ((v & 0x00FF00FF) << 8); // byte<->byte631v = ( v >> 16 ) | ( v << 16); // hword<->hword632gpr.SetImm(rd, v);633return;634}635636#if PPSSPP_ARCH(ARMV7)637gpr.MapDirtyIn(rd, rt);638RBIT(gpr.R(rd), gpr.R(rt));639#else640Comp_Generic(op);641#endif642break;643default:644Comp_Generic(op);645return;646}647}648649void ArmJit::Comp_Allegrex2(MIPSOpcode op)650{651CONDITIONAL_DISABLE(ALU_BIT);652MIPSGPReg rt = _RT;653MIPSGPReg rd = _RD;654// Don't change $zr.655if (rd == 0)656return;657658switch (op & 0x3ff) {659case 0xA0: //wsbh660if (gpr.IsImm(rt)) {661gpr.SetImm(rd, ((gpr.GetImm(rt) & 0xFF00FF00) >> 8) | ((gpr.GetImm(rt) & 0x00FF00FF) << 8));662} else {663gpr.MapDirtyIn(rd, rt);664REV16(gpr.R(rd), gpr.R(rt));665}666break;667case 0xE0: //wsbw668if (gpr.IsImm(rt)) {669gpr.SetImm(rd, swap32(gpr.GetImm(rt)));670} else {671gpr.MapDirtyIn(rd, rt);672REV(gpr.R(rd), gpr.R(rt));673}674break;675default:676Comp_Generic(op);677break;678}679}680681void ArmJit::Comp_MulDivType(MIPSOpcode op)682{683CONDITIONAL_DISABLE(MULDIV);684MIPSGPReg rt = _RT;685MIPSGPReg rs = _RS;686MIPSGPReg rd = _RD;687688switch (op & 63) {689case 16: // R(rd) = HI; //mfhi690if (rd != MIPS_REG_ZERO) {691if (gpr.IsImm(MIPS_REG_HI)) {692gpr.SetImm(rd, gpr.GetImm(MIPS_REG_HI));693break;694}695gpr.MapDirtyIn(rd, MIPS_REG_HI);696MOV(gpr.R(rd), gpr.R(MIPS_REG_HI));697}698break;699700case 17: // HI = R(rs); //mthi701if (gpr.IsImm(rs)) {702gpr.SetImm(MIPS_REG_HI, gpr.GetImm(rs));703break;704}705gpr.MapDirtyIn(MIPS_REG_HI, rs);706MOV(gpr.R(MIPS_REG_HI), gpr.R(rs));707break;708709case 18: // R(rd) = LO; break; //mflo710if (rd != MIPS_REG_ZERO) {711if (gpr.IsImm(MIPS_REG_LO)) {712gpr.SetImm(rd, gpr.GetImm(MIPS_REG_LO));713break;714}715gpr.MapDirtyIn(rd, MIPS_REG_LO);716MOV(gpr.R(rd), gpr.R(MIPS_REG_LO));717}718break;719720case 19: // LO = R(rs); break; //mtlo721if (gpr.IsImm(rs)) {722gpr.SetImm(MIPS_REG_LO, gpr.GetImm(rs));723break;724}725gpr.MapDirtyIn(MIPS_REG_LO, rs);726MOV(gpr.R(MIPS_REG_LO), gpr.R(rs));727break;728729case 24: //mult (the most popular one). lo,hi = signed mul (rs * rt)730if (gpr.IsImm(rs) && gpr.IsImm(rt)) {731s64 result = (s64)(s32)gpr.GetImm(rs) * (s64)(s32)gpr.GetImm(rt);732u64 resultBits = (u64)result;733gpr.SetImm(MIPS_REG_LO, (u32)(resultBits >> 0));734gpr.SetImm(MIPS_REG_HI, (u32)(resultBits >> 32));735break;736}737gpr.MapDirtyDirtyInIn(MIPS_REG_LO, MIPS_REG_HI, rs, rt);738SMULL(gpr.R(MIPS_REG_LO), gpr.R(MIPS_REG_HI), gpr.R(rs), gpr.R(rt));739break;740741case 25: //multu (2nd) lo,hi = unsigned mul (rs * rt)742if (gpr.IsImm(rs) && gpr.IsImm(rt)) {743u64 resultBits = (u64)gpr.GetImm(rs) * (u64)gpr.GetImm(rt);744gpr.SetImm(MIPS_REG_LO, (u32)(resultBits >> 0));745gpr.SetImm(MIPS_REG_HI, (u32)(resultBits >> 32));746break;747}748gpr.MapDirtyDirtyInIn(MIPS_REG_LO, MIPS_REG_HI, rs, rt);749UMULL(gpr.R(MIPS_REG_LO), gpr.R(MIPS_REG_HI), gpr.R(rs), gpr.R(rt));750break;751752case 26: //div753if (cpu_info.bIDIVa) {754// TODO: Does this handle INT_MAX, 0, etc. correctly?755gpr.MapDirtyDirtyInIn(MIPS_REG_LO, MIPS_REG_HI, rs, rt);756757CMPI2R(gpr.R(rt), 0, SCRATCHREG1);758FixupBranch skipZero = B_CC(CC_NEQ);759MOV(gpr.R(MIPS_REG_HI), gpr.R(rs));760MOVI2R(gpr.R(MIPS_REG_LO), -1);761CMPI2R(gpr.R(rs), 0, SCRATCHREG1);762SetCC(CC_LT);763MOVI2R(gpr.R(MIPS_REG_LO), 1);764SetCC(CC_AL);765FixupBranch skipDiv = B();766767SetJumpTarget(skipZero);768SDIV(gpr.R(MIPS_REG_LO), gpr.R(rs), gpr.R(rt));769SetJumpTarget(skipDiv);770MUL(SCRATCHREG1, gpr.R(rt), gpr.R(MIPS_REG_LO));771SUB(gpr.R(MIPS_REG_HI), gpr.R(rs), Operand2(SCRATCHREG1));772} else {773DISABLE;774}775break;776777case 27: //divu778// Do we have a known power-of-two denominator? Yes, this happens.779if (gpr.IsImm(rt) && (gpr.GetImm(rt) & (gpr.GetImm(rt) - 1)) == 0 && gpr.GetImm(rt) != 0) {780u32 denominator = gpr.GetImm(rt);781gpr.MapDirtyDirtyIn(MIPS_REG_LO, MIPS_REG_HI, rs);782// Remainder is just an AND, neat.783ANDI2R(gpr.R(MIPS_REG_HI), gpr.R(rs), denominator - 1, SCRATCHREG1);784int shift = 0;785while (denominator != 0) {786++shift;787denominator >>= 1;788}789// The shift value is one too much for the divide by the same value.790if (shift > 1) {791LSR(gpr.R(MIPS_REG_LO), gpr.R(rs), shift - 1);792} else {793MOV(gpr.R(MIPS_REG_LO), gpr.R(rs));794}795} else if (cpu_info.bIDIVa) {796// TODO: Does this handle INT_MAX, 0, etc. correctly?797gpr.MapDirtyDirtyInIn(MIPS_REG_LO, MIPS_REG_HI, rs, rt);798799CMPI2R(gpr.R(rt), 0, SCRATCHREG1);800FixupBranch skipZero = B_CC(CC_NEQ);801MOV(gpr.R(MIPS_REG_HI), gpr.R(rs));802MOVI2R(SCRATCHREG1, 0xFFFF);803CMP(gpr.R(rs), SCRATCHREG1);804MOVI2R(gpr.R(MIPS_REG_LO), -1);805SetCC(CC_LS);806MOV(gpr.R(MIPS_REG_LO), SCRATCHREG1);807SetCC(CC_AL);808FixupBranch skipDiv = B();809810SetJumpTarget(skipZero);811UDIV(gpr.R(MIPS_REG_LO), gpr.R(rs), gpr.R(rt));812SetJumpTarget(skipDiv);813MUL(SCRATCHREG1, gpr.R(rt), gpr.R(MIPS_REG_LO));814SUB(gpr.R(MIPS_REG_HI), gpr.R(rs), Operand2(SCRATCHREG1));815} else {816// If rt is 0, we either caught it above, or it's not an imm.817gpr.MapDirtyDirtyInIn(MIPS_REG_LO, MIPS_REG_HI, rs, rt);818MOV(SCRATCHREG1, gpr.R(rt));819// We start at rs for the remainder and subtract out.820MOV(gpr.R(MIPS_REG_HI), gpr.R(rs));821822CMP(gpr.R(rt), 0);823FixupBranch skipper = B_CC(CC_EQ);824825// Double SCRATCHREG1 until it would be (but isn't) bigger than the numerator.826CMP(SCRATCHREG1, Operand2(gpr.R(rs), ST_LSR, 1));827const u8 *doubleLoop = GetCodePtr();828SetCC(CC_LS);829MOV(SCRATCHREG1, Operand2(SCRATCHREG1, ST_LSL, 1));830SetCC(CC_AL);831CMP(SCRATCHREG1, Operand2(gpr.R(rs), ST_LSR, 1));832B_CC(CC_LS, doubleLoop);833834MOV(gpr.R(MIPS_REG_LO), 0);835836// Subtract and halve SCRATCHREG1 (doubling and adding the result) until it's below the denominator.837const u8 *subLoop = GetCodePtr();838CMP(gpr.R(MIPS_REG_HI), SCRATCHREG1);839SetCC(CC_HS);840SUB(gpr.R(MIPS_REG_HI), gpr.R(MIPS_REG_HI), SCRATCHREG1);841SetCC(CC_AL);842// Carry will be set if we subtracted.843ADC(gpr.R(MIPS_REG_LO), gpr.R(MIPS_REG_LO), gpr.R(MIPS_REG_LO));844MOV(SCRATCHREG1, Operand2(SCRATCHREG1, ST_LSR, 1));845CMP(SCRATCHREG1, gpr.R(rt));846B_CC(CC_HS, subLoop);847848// We didn't change rt. If it was 0, then clear HI and LO.849FixupBranch zeroSkip = B();850SetJumpTarget(skipper);851MOVI2R(SCRATCHREG1, 0xFFFF);852CMP(gpr.R(rs), SCRATCHREG1);853MOVI2R(gpr.R(MIPS_REG_LO), -1);854SetCC(CC_LS);855MOV(gpr.R(MIPS_REG_LO), SCRATCHREG1);856SetCC(CC_AL);857SetJumpTarget(zeroSkip);858}859break;860861case 28: //madd862gpr.MapDirtyDirtyInIn(MIPS_REG_LO, MIPS_REG_HI, rs, rt, false);863SMLAL(gpr.R(MIPS_REG_LO), gpr.R(MIPS_REG_HI), gpr.R(rs), gpr.R(rt));864break;865866case 29: //maddu867gpr.MapDirtyDirtyInIn(MIPS_REG_LO, MIPS_REG_HI, rs, rt, false);868UMLAL(gpr.R(MIPS_REG_LO), gpr.R(MIPS_REG_HI), gpr.R(rs), gpr.R(rt));869break;870871case 46: // msub872gpr.MapDirtyDirtyInIn(MIPS_REG_LO, MIPS_REG_HI, rs, rt, false);873SMULL(SCRATCHREG1, SCRATCHREG2, gpr.R(rs), gpr.R(rt));874SUBS(gpr.R(MIPS_REG_LO), gpr.R(MIPS_REG_LO), SCRATCHREG1);875SBC(gpr.R(MIPS_REG_HI), gpr.R(MIPS_REG_HI), SCRATCHREG2);876break;877878case 47: // msubu879gpr.MapDirtyDirtyInIn(MIPS_REG_LO, MIPS_REG_HI, rs, rt, false);880UMULL(SCRATCHREG1, SCRATCHREG2, gpr.R(rs), gpr.R(rt));881SUBS(gpr.R(MIPS_REG_LO), gpr.R(MIPS_REG_LO), SCRATCHREG1);882SBC(gpr.R(MIPS_REG_HI), gpr.R(MIPS_REG_HI), SCRATCHREG2);883break;884885default:886DISABLE;887}888}889890}891892#endif // PPSSPP_ARCH(ARM)893894895