Merge pull request #964 from achurch/ppu-interpreter-fixes

Implement missing and fix broken PPU instructions in the interpreter.
This commit is contained in:
B1ackDaemon 2015-01-18 01:11:06 +02:00
commit 10150957ff
8 changed files with 981 additions and 597 deletions

View file

@ -1675,6 +1675,10 @@ private:
{
DisAsm_V1_R2("stvlx", vs, ra, rb);
}
void STDBRX(u32 rs, u32 ra, u32 rb)
{
DisAsm_R3("stdbrx", rs, ra, rb);
}
void STSWX(u32 rs, u32 ra, u32 rb)
{
DisAsm_R3("swswx", rs, ra, rb);

View file

@ -226,6 +226,9 @@ namespace PPU_instr
#define bind_instr(list, name, ...) \
static const auto& name = make_instr<PPU_opcodes::name>(list, #name, &PPUOpcodes::name, ##__VA_ARGS__)
#define bind_instr_oe(list, name, ...) \
bind_instr(list, name, ##__VA_ARGS__); \
static const auto& name##O = make_instr<PPU_opcodes::name##O>(list, #name "O", &PPUOpcodes::name, ##__VA_ARGS__)
bind_instr(main_list, TDI, TO, RA, simm16);
bind_instr(main_list, TWI, TO, RA, simm16);
@ -456,9 +459,9 @@ namespace PPU_instr
/*0x004*/bind_instr(g1f_list, TW, TO, RA, RB);
/*0x006*/bind_instr(g1f_list, LVSL, VD, RA, RB);
/*0x007*/bind_instr(g1f_list, LVEBX, VD, RA, RB);
/*0x008*/bind_instr(g1f_list, SUBFC, RD, RA, RB, OE, RC);
/*0x008*/bind_instr_oe(g1f_list, SUBFC, RD, RA, RB, OE, RC);
/*0x009*/bind_instr(g1f_list, MULHDU, RD, RA, RB, RC);
/*0x00a*/bind_instr(g1f_list, ADDC, RD, RA, RB, OE, RC);
/*0x00a*/bind_instr_oe(g1f_list, ADDC, RD, RA, RB, OE, RC);
/*0x00b*/bind_instr(g1f_list, MULHWU, RD, RA, RB, RC);
/*0x013*/bind_instr(g1f_list, MFOCRF, L_11, RD, CRM);
/*0x014*/bind_instr(g1f_list, LWARX, RD, RA, RB);
@ -471,7 +474,7 @@ namespace PPU_instr
/*0x020*/bind_instr(g1f_list, CMPL, CRFD, L_10, RA, RB);
/*0x026*/bind_instr(g1f_list, LVSR, VD, RA, RB);
/*0x027*/bind_instr(g1f_list, LVEHX, VD, RA, RB);
/*0x028*/bind_instr(g1f_list, SUBF, RD, RA, RB, OE, RC);
/*0x028*/bind_instr_oe(g1f_list, SUBF, RD, RA, RB, OE, RC);
/*0x035*/bind_instr(g1f_list, LDUX, RD, RA, RB);
/*0x036*/bind_instr(g1f_list, DCBST, RA, RB);
/*0x037*/bind_instr(g1f_list, LWZUX, RD, RA, RB);
@ -485,12 +488,12 @@ namespace PPU_instr
/*0x056*/bind_instr(g1f_list, DCBF, RA, RB);
/*0x057*/bind_instr(g1f_list, LBZX, RD, RA, RB);
/*0x067*/bind_instr(g1f_list, LVX, VD, RA, RB);
/*0x068*/bind_instr(g1f_list, NEG, RD, RA, OE, RC);
/*0x068*/bind_instr_oe(g1f_list, NEG, RD, RA, OE, RC);
/*0x077*/bind_instr(g1f_list, LBZUX, RD, RA, RB);
/*0x07c*/bind_instr(g1f_list, NOR, RA, RS, RB, RC);
/*0x087*/bind_instr(g1f_list, STVEBX, VS, RA, RB);
/*0x088*/bind_instr(g1f_list, SUBFE, RD, RA, RB, OE, RC);
/*0x08a*/bind_instr(g1f_list, ADDE, RD, RA, RB, OE, RC);
/*0x088*/bind_instr_oe(g1f_list, SUBFE, RD, RA, RB, OE, RC);
/*0x08a*/bind_instr_oe(g1f_list, ADDE, RD, RA, RB, OE, RC);
/*0x090*/bind_instr(g1f_list, MTOCRF, L_11, CRM, RS);
/*0x095*/bind_instr(g1f_list, STDX, RS, RA, RB);
/*0x096*/bind_instr(g1f_list, STWCX_, RS, RA, RB);
@ -499,18 +502,18 @@ namespace PPU_instr
/*0x0b5*/bind_instr(g1f_list, STDUX, RS, RA, RB);
/*0x0b7*/bind_instr(g1f_list, STWUX, RS, RA, RB);
/*0x0c7*/bind_instr(g1f_list, STVEWX, VS, RA, RB);
/*0x0c8*/bind_instr(g1f_list, SUBFZE, RD, RA, OE, RC);
/*0x0ca*/bind_instr(g1f_list, ADDZE, RD, RA, OE, RC);
/*0x0c8*/bind_instr_oe(g1f_list, SUBFZE, RD, RA, OE, RC);
/*0x0ca*/bind_instr_oe(g1f_list, ADDZE, RD, RA, OE, RC);
/*0x0d6*/bind_instr(g1f_list, STDCX_, RS, RA, RB);
/*0x0d7*/bind_instr(g1f_list, STBX, RS, RA, RB);
/*0x0e7*/bind_instr(g1f_list, STVX, VS, RA, RB);
/*0x0e8*/bind_instr(g1f_list, SUBFME, RD, RA, OE, RC);
/*0x0e9*/bind_instr(g1f_list, MULLD, RD, RA, RB, OE, RC);
/*0x0ea*/bind_instr(g1f_list, ADDME, RD, RA, OE, RC);
/*0x0eb*/bind_instr(g1f_list, MULLW, RD, RA, RB, OE, RC);
/*0x0e8*/bind_instr_oe(g1f_list, SUBFME, RD, RA, OE, RC);
/*0x0e9*/bind_instr_oe(g1f_list, MULLD, RD, RA, RB, OE, RC);
/*0x0ea*/bind_instr_oe(g1f_list, ADDME, RD, RA, OE, RC);
/*0x0eb*/bind_instr_oe(g1f_list, MULLW, RD, RA, RB, OE, RC);
/*0x0f6*/bind_instr(g1f_list, DCBTST, RA, RB, TH);
/*0x0f7*/bind_instr(g1f_list, STBUX, RS, RA, RB);
/*0x10a*/bind_instr(g1f_list, ADD, RD, RA, RB, OE, RC);
/*0x10a*/bind_instr_oe(g1f_list, ADD, RD, RA, RB, OE, RC);
/*0x116*/bind_instr(g1f_list, DCBT, RA, RB, TH);
/*0x117*/bind_instr(g1f_list, LHZX, RD, RA, RB);
/*0x11c*/bind_instr(g1f_list, EQV, RA, RS, RB, RC);
@ -531,15 +534,21 @@ namespace PPU_instr
/*0x1b6*/bind_instr(g1f_list, ECOWX, RS, RA, RB);
/*0x1b7*/bind_instr(g1f_list, STHUX, RS, RA, RB);
/*0x1bc*/bind_instr(g1f_list, OR, RA, RS, RB, RC);
/*0x1c9*/bind_instr(g1f_list, DIVDU, RD, RA, RB, OE, RC);
/*0x1cb*/bind_instr(g1f_list, DIVWU, RD, RA, RB, OE, RC);
/*0x1c9*/bind_instr_oe(g1f_list, DIVDU, RD, RA, RB, OE, RC);
/*0x1cb*/bind_instr_oe(g1f_list, DIVWU, RD, RA, RB, OE, RC);
/*0x1d3*/bind_instr(g1f_list, MTSPR, SPR, RS);
/*0x1d6*///DCBI
/*0x1dc*/bind_instr(g1f_list, NAND, RA, RS, RB, RC);
/*0x1e7*/bind_instr(g1f_list, STVXL, VS, RA, RB);
/*0x1e9*/bind_instr(g1f_list, DIVD, RD, RA, RB, OE, RC);
/*0x1eb*/bind_instr(g1f_list, DIVW, RD, RA, RB, OE, RC);
/*0x1e9*/bind_instr_oe(g1f_list, DIVD, RD, RA, RB, OE, RC);
/*0x1eb*/bind_instr_oe(g1f_list, DIVW, RD, RA, RB, OE, RC);
/*0x207*/bind_instr(g1f_list, LVLX, VD, RA, RB);
// MULH{D|DU|W|WU} don't use OE, but a real Cell accepts
// opcodes with OE=1 and Rc=0, behaving as if OE was not set.
// OE=1 and Rc=1 causes an invalid instruction exception, but
// we don't worry about that.
static const auto& MULHDUO = make_instr<0x209>(g1f_list, "MULHDUO", &PPUOpcodes::MULHDU, RD, RA, RB, RC);
static const auto& MULHWUO = make_instr<0x20b>(g1f_list, "MULHWUO", &PPUOpcodes::MULHWU, RD, RA, RB, RC);
/*0x214*/bind_instr(g1f_list, LDBRX, RD, RA, RB);
/*0x215*/bind_instr(g1f_list, LSWX, RD, RA, RB);
/*0x216*/bind_instr(g1f_list, LWBRX, RD, RA, RB);
@ -548,11 +557,14 @@ namespace PPU_instr
/*0x21b*/bind_instr(g1f_list, SRD, RA, RS, RB, RC);
/*0x227*/bind_instr(g1f_list, LVRX, VD, RA, RB);
/*0x237*/bind_instr(g1f_list, LFSUX, FRD, RA, RB);
static const auto& MULHDO = make_instr<0x249>(g1f_list, "MULHDO", &PPUOpcodes::MULHD, RD, RA, RB, RC);
static const auto& MULHWO = make_instr<0x24b>(g1f_list, "MULHWO", &PPUOpcodes::MULHW, RD, RA, RB, RC);
/*0x255*/bind_instr(g1f_list, LSWI, RD, RA, NB);
/*0x256*/bind_instr(g1f_list, SYNC, L_9_10);
/*0x257*/bind_instr(g1f_list, LFDX, FRD, RA, RB);
/*0x277*/bind_instr(g1f_list, LFDUX, FRD, RA, RB);
/*0x287*/bind_instr(g1f_list, STVLX, VS, RA, RB);
/*0x294*/bind_instr(g1f_list, STDBRX, RD, RA, RB);
/*0x296*/bind_instr(g1f_list, STSWX, RS, RA, RB);
/*0x296*/bind_instr(g1f_list, STWBRX, RS, RA, RB);
/*0x297*/bind_instr(g1f_list, STFSX, FRS, RA, RB);

File diff suppressed because it is too large Load diff

View file

@ -3311,7 +3311,7 @@ void Compiler::MFSPR(u32 rd, u32 spr) {
rd_i64 = GetCtr();
break;
case 0x100:
rd_i64 = GetUsprg0();
rd_i64 = GetVrsave();
break;
case 0x10C:
rd_i64 = Call<u64>("get_time", get_time);
@ -3503,7 +3503,7 @@ void Compiler::MTSPR(u32 spr, u32 rs) {
SetCtr(rs_i64);
break;
case 0x100:
SetUsprg0(rs_i64);
SetVrsave(rs_i64);
break;
default:
assert(0);
@ -3746,6 +3746,16 @@ void Compiler::STVLX(u32 vs, u32 ra, u32 rb) {
addr_i8_ptr, vs_i8_ptr, size_i64, m_ir_builder->getInt32(1), m_ir_builder->getInt1(false));
}
void Compiler::STDBRX(u32 rs, u32 ra, u32 rb) {
auto addr_i64 = GetGpr(rb);
if (ra) {
auto ra_i64 = GetGpr(ra);
addr_i64 = m_ir_builder->CreateAdd(ra_i64, addr_i64);
}
WriteMemory(addr_i64, GetGpr(rs), 0, false);
}
void Compiler::STSWX(u32 rs, u32 ra, u32 rb) {
CompilationError("STSWX");
}
@ -5268,17 +5278,19 @@ void Compiler::SetXerSo(Value * so) {
SetXer(xer_i64);
}
Value * Compiler::GetUsprg0() {
auto usrpg0_i8_ptr = m_ir_builder->CreateConstGEP1_32(m_state.args[CompileTaskState::Args::State], (unsigned int)offsetof(PPUThread, USPRG));
auto usprg0_i64_ptr = m_ir_builder->CreateBitCast(usrpg0_i8_ptr, m_ir_builder->getInt64Ty()->getPointerTo());
return m_ir_builder->CreateAlignedLoad(usprg0_i64_ptr, 8);
Value * Compiler::GetVrsave() {
auto vrsave_i8_ptr = m_ir_builder->CreateConstGEP1_32(m_state.args[CompileTaskState::Args::State], (unsigned int)offsetof(PPUThread, VRSAVE));
auto vrsave_i32_ptr = m_ir_builder->CreateBitCast(vrsave_i8_ptr, m_ir_builder->getInt32Ty()->getPointerTo());
auto val_i32 = m_ir_builder->CreateAlignedLoad(vrsave_i32_ptr, 4);
return m_ir_builder->CreateZExtOrTrunc(val_i32, m_ir_builder->getInt64Ty());
}
void Compiler::SetUsprg0(Value * val_x64) {
void Compiler::SetVrsave(Value * val_x64) {
auto val_i64 = m_ir_builder->CreateBitCast(val_x64, m_ir_builder->getInt64Ty());
auto usprg0_i8_ptr = m_ir_builder->CreateConstGEP1_32(m_state.args[CompileTaskState::Args::State], (unsigned int)offsetof(PPUThread, USPRG));
auto usprg0_i64_ptr = m_ir_builder->CreateBitCast(usprg0_i8_ptr, m_ir_builder->getInt64Ty()->getPointerTo());
m_ir_builder->CreateAlignedStore(val_i64, usprg0_i64_ptr, 8);
auto val_i32 = m_ir_builder->CreateZExtOrTrunc(val_i64, m_ir_builder->getInt32Ty());
auto vrsave_i8_ptr = m_ir_builder->CreateConstGEP1_32(m_state.args[CompileTaskState::Args::State], (unsigned int)offsetof(PPUThread, VRSAVE));
auto vrsave_i32_ptr = m_ir_builder->CreateBitCast(vrsave_i8_ptr, m_ir_builder->getInt32Ty()->getPointerTo());
m_ir_builder->CreateAlignedStore(val_i32, vrsave_i32_ptr, 8);
}
Value * Compiler::GetFpscr() {

View file

@ -598,6 +598,7 @@ namespace ppu_recompiler_llvm {
void LFDX(u32 frd, u32 ra, u32 rb) override;
void LFDUX(u32 frd, u32 ra, u32 rb) override;
void STVLX(u32 vs, u32 ra, u32 rb) override;
void STDBRX(u32 rd, u32 ra, u32 rb) override;
void STSWX(u32 rs, u32 ra, u32 rb) override;
void STWBRX(u32 rs, u32 ra, u32 rb) override;
void STFSX(u32 frs, u32 ra, u32 rb) override;
@ -859,11 +860,11 @@ namespace ppu_recompiler_llvm {
/// Set the SO bit of XER
void SetXerSo(llvm::Value * so);
/// Get USPRG0
llvm::Value * GetUsprg0();
/// Get VRSAVE
llvm::Value * GetVrsave();
/// Set USPRG0
void SetUsprg0(llvm::Value * val_x64);
/// Set VRSAVE
void SetVrsave(llvm::Value * val_x64);
/// Load FPSCR
llvm::Value * GetFpscr();

View file

@ -344,6 +344,8 @@ namespace PPU_opcodes
DIVD = 0x1e9,
DIVW = 0x1eb,
LVLX = 0x207, //Load Vector Left Indexed
SUBFCO = 0x208,
ADDCO = 0x20a,
LDBRX = 0x214,
LSWX = 0x215,
LWBRX = 0x216,
@ -351,21 +353,33 @@ namespace PPU_opcodes
SRW = 0x218,
SRD = 0x21b,
LVRX = 0x227, //Load Vector Right Indexed
SUBFO = 0x228,
LFSUX = 0x237,
LSWI = 0x255,
SYNC = 0x256,
LFDX = 0x257,
NEGO = 0x268,
LFDUX = 0x277,
STVLX = 0x287, //Store Vector Left Indexed
SUBFEO = 0x288,
ADDEO = 0x28a,
STDBRX = 0x294,
STSWX = 0x295,
STWBRX = 0x296,
STFSX = 0x297,
STVRX = 0x2a7, //Store Vector Right Indexed
STFSUX = 0x2b7,
SUBFZEO= 0x2c8,
ADDZEO = 0x2ca,
STSWI = 0x2d5,
STFDX = 0x2d7, //Store Floating-Point Double Indexed
SUBFMEO= 0x2e8,
MULLDO = 0x2e9,
ADDMEO = 0x2ea,
MULLWO = 0x2eb,
STFDUX = 0x2f7,
LVLXL = 0x307, //Load Vector Left Indexed Last
ADDO = 0x30a,
LHBRX = 0x316,
SRAW = 0x318,
SRAD = 0x31a,
@ -380,9 +394,13 @@ namespace PPU_opcodes
EXTSH = 0x39a,
STVRXL = 0x3a7, //Store Vector Right Indexed Last
EXTSB = 0x3ba,
DIVDUO = 0x3c9,
DIVWUO = 0x3cb,
STFIWX = 0x3d7,
EXTSW = 0x3da,
ICBI = 0x3d6, //Instruction Cache Block Invalidate
DIVDO = 0x3e9,
DIVWO = 0x3eb,
DCBZ = 0x3f6, //Data Cache Block Set to Zero
};
@ -759,6 +777,7 @@ public:
virtual void LFDX(u32 frd, u32 ra, u32 rb) = 0;
virtual void LFDUX(u32 frd, u32 ra, u32 rb) = 0;
virtual void STVLX(u32 vs, u32 ra, u32 rb) = 0;
virtual void STDBRX(u32 rs, u32 ra, u32 rb) = 0;
virtual void STSWX(u32 rs, u32 ra, u32 rb) = 0;
virtual void STWBRX(u32 rs, u32 ra, u32 rb) = 0;
virtual void STFSX(u32 frs, u32 ra, u32 rb) = 0;

View file

@ -43,7 +43,6 @@ void PPUThread::DoReset()
memset(FPR, 0, sizeof(FPR));
memset(GPR, 0, sizeof(GPR));
memset(SPRG, 0, sizeof(SPRG));
memset(USPRG, 0, sizeof(USPRG));
CR.CR = 0;
LR = 0;
@ -52,6 +51,7 @@ void PPUThread::DoReset()
XER.XER = 0;
FPSCR.FPSCR = 0;
VSCR.VSCR = 0;
VRSAVE = 0;
cycle = 0;
}

View file

@ -53,6 +53,8 @@ enum FPSCR_EXP
FPSCR_VXSOFT = 0x00000400,
FPSCR_VXSQRT = 0x00000200,
FPSCR_VXCVI = 0x00000100,
FPSCR_VX_ALL = FPSCR_VXSNAN | FPSCR_VXISI | FPSCR_VXIDI | FPSCR_VXZDZ | FPSCR_VXIMZ | FPSCR_VXVC | FPSCR_VXSOFT | FPSCR_VXSQRT | FPSCR_VXCVI,
};
enum FPSCR_RN
@ -441,18 +443,22 @@ struct PPCdouble
PPCdouble() : _u64(0)
{
type = UpdateType();
}
PPCdouble(double val) : _double(val)
{
type = UpdateType();
}
PPCdouble(u64 val) : _u64(val)
{
type = UpdateType();
}
PPCdouble(u32 val) : _u64(val)
{
type = UpdateType();
}
};
@ -531,20 +537,12 @@ public:
u64 LR; //SPR 0x008 : Link Register
u64 CTR; //SPR 0x009 : Count Register
u64 USPRG[8]; //SPR 0x100 - 0x107: User-SPR General-Purpose Registers
u32 VRSAVE; //SPR 0x100: VR Save/Restore Register (32 bits)
u64 SPRG[8]; //SPR 0x110 - 0x117 : SPR General-Purpose Registers
//TBR : Time-Base Registers
union
{
u64 TB; //TBR 0x10C - 0x10D
struct
{
u32 TBH;
u32 TBL;
};
};
u64 TB; //TBR 0x10C - 0x10D
u64 cycle;
@ -652,7 +650,7 @@ public:
UpdateCRn<T>(0, val, 0);
}
template<typename T> void UpdateCR1()
void UpdateCR1()
{
SetCR_LT(1, FPSCR.FX);
SetCR_GT(1, FPSCR.FEX);
@ -670,10 +668,37 @@ public:
bool IsCarry(const u64 a, const u64 b) { return (a + b) < a; }
bool IsCarry(const u64 a, const u64 b, const u64 c) { return IsCarry(a, b) || IsCarry(a + b, c); }
void SetOV(const bool set)
{
XER.OV = set;
XER.SO |= set;
}
void UpdateFPSCR_FEX()
{
const u32 exceptions = (FPSCR.FPSCR >> 25) & 0x1F;
const u32 enabled = (FPSCR.FPSCR >> 3) & 0x1F;
if (exceptions & enabled) FPSCR.FEX = 1;
}
void UpdateFPSCR_VX()
{
if (FPSCR.FPSCR & FPSCR_VX_ALL) FPSCR.VX = 1;
}
void SetFPSCR(const u32 val)
{
FPSCR.FPSCR = val & ~(FPSCR_FEX | FPSCR_VX);
UpdateFPSCR_VX();
UpdateFPSCR_FEX();
}
void SetFPSCRException(const FPSCR_EXP mask)
{
if ((FPSCR.FPSCR & mask) != mask) FPSCR.FX = 1;
FPSCR.FPSCR |= mask;
UpdateFPSCR_VX();
UpdateFPSCR_FEX();
}
void SetFPSCR_FI(const u32 val)