pureikyubu/src/gekkoc.cpp
2024-04-19 10:03:36 +03:00

5187 lines
130 KiB
C++
Raw Permalink Blame History

#include "pch.h"
using namespace Debug;
// Branch Instructions
namespace Gekko
{
// info.Imm.Address is always pre-calculated, there is no need to perform operations on the `pc`, just write a new address there.
#define BO(n) ((bo >> (4-n)) & 1)
void Interpreter::BranchCheck()
{
if (core->intFlag && (core->regs.msr & MSR_EE))
{
core->Exception(Gekko::Exception::EXCEPTION_EXTERNAL_INTERRUPT);
core->exception = false;
return;
}
// modify CPU counters (possible CPU_EXCEPTION_DECREMENTER)
core->Tick();
if (core->decreq && (core->regs.msr & MSR_EE))
{
core->decreq = false;
core->Exception(Gekko::Exception::EXCEPTION_DECREMENTER);
}
}
// PC = PC + EXTS(LI || 0b00)
void Interpreter::b()
{
core->regs.pc = info.Imm.Address;
BranchCheck();
}
// PC = EXTS(LI || 0b00)
void Interpreter::ba()
{
core->regs.pc = info.Imm.Address;
BranchCheck();
}
// LR = PC + 4, PC = PC + EXTS(LI || 0b00)
void Interpreter::bl()
{
core->regs.spr[SPR::LR] = core->regs.pc + 4;
core->regs.pc = info.Imm.Address;
BranchCheck();
}
// LR = PC + 4, PC = EXTS(LI || 0b00)
void Interpreter::bla()
{
core->regs.spr[SPR::LR] = core->regs.pc + 4;
core->regs.pc = info.Imm.Address;
BranchCheck();
}
// calculation of conditional branch
bool Interpreter::BcTest()
{
bool ctr_ok, cond_ok;
size_t bo = info.paramBits[0], bi = info.paramBits[1];
if (BO(2) == 0)
{
core->regs.spr[SPR::CTR]--;
if (BO(3)) ctr_ok = (core->regs.spr[SPR::CTR] == 0);
else ctr_ok = (core->regs.spr[SPR::CTR] != 0);
}
else ctr_ok = true;
if (BO(0) == 0)
{
if (BO(1)) cond_ok = ((core->regs.cr << bi) & 0x80000000) != 0;
else cond_ok = ((core->regs.cr << bi) & 0x80000000) == 0;
}
else cond_ok = true;
return (ctr_ok && cond_ok);
}
// if ~BO2 then CTR = CTR - 1
// ctr_ok = BO2 | ((CTR != 0) ^ BO3)
// cond_ok = BO0 | (CR[BI] EQV BO1)
// if ctr_ok & cond_ok then
// if LK = 1
// LR = PC + 4
// if AA = 1
// then PC = EXTS(BD || 0b00)
// else PC = PC + EXTS(BD || 0b00)
void Interpreter::bc()
{
if (BcTest())
{
core->regs.pc = info.Imm.Address;
BranchCheck();
}
else
{
core->regs.pc += 4;
}
}
void Interpreter::bca()
{
if (BcTest())
{
core->regs.pc = info.Imm.Address;
BranchCheck();
}
else
{
core->regs.pc += 4;
}
}
void Interpreter::bcl()
{
if (BcTest())
{
core->regs.spr[SPR::LR] = core->regs.pc + 4; // LK
core->regs.pc = info.Imm.Address;
BranchCheck();
}
else
{
core->regs.pc += 4;
}
}
void Interpreter::bcla()
{
if (BcTest())
{
core->regs.spr[SPR::LR] = core->regs.pc + 4; // LK
core->regs.pc = info.Imm.Address;
BranchCheck();
}
else
{
core->regs.pc += 4;
}
}
// calculation of conditional to count register branch
bool Interpreter::BctrTest()
{
bool cond_ok;
size_t bo = info.paramBits[0], bi = info.paramBits[1];
if (BO(0) == 0)
{
if (BO(1)) cond_ok = ((core->regs.cr << bi) & 0x80000000) != 0;
else cond_ok = ((core->regs.cr << bi) & 0x80000000) == 0;
}
else cond_ok = true;
return cond_ok;
}
// cond_ok = BO0 | (CR[BI] EQV BO1)
// if cond_ok
// then
// PC = CTR || 0b00
void Interpreter::bcctr()
{
if (BctrTest())
{
core->regs.pc = core->regs.spr[SPR::CTR] & ~3;
BranchCheck();
}
else
{
core->regs.pc += 4;
}
}
// cond_ok = BO0 | (CR[BI] EQV BO1)
// if cond_ok
// then
// LR = PC + 4
// PC = CTR || 0b00
void Interpreter::bcctrl()
{
if (BctrTest())
{
core->regs.spr[SPR::LR] = core->regs.pc + 4;
core->regs.pc = core->regs.spr[SPR::CTR] & ~3;
BranchCheck();
}
else
{
core->regs.pc += 4;
}
}
// if ~BO2 then CTR = CTR - 1
// ctr_ok = BO2 | ((CTR != 0) ^ BO3)
// cond_ok = BO0 | (CR[BI] EQV BO1)
// if ctr_ok & cond_ok then
// PC = LR[0-29] || 0b00
void Interpreter::bclr()
{
if (BcTest())
{
core->regs.pc = core->regs.spr[SPR::LR] & ~3;
BranchCheck();
}
else
{
core->regs.pc += 4;
}
}
// if ~BO2 then CTR = CTR - 1
// ctr_ok = BO2 | ((CTR != 0) ^ BO3)
// cond_ok = BO0 | (CR[BI] EQV BO1)
// if ctr_ok & cond_ok then
// NLR = PC + 4
// PC = LR[0-29] || 0b00
// LR = NLR
void Interpreter::bclrl()
{
if (BcTest())
{
uint32_t lr = core->regs.pc + 4;
core->regs.pc = core->regs.spr[SPR::LR] & ~3;
core->regs.spr[SPR::LR] = lr;
BranchCheck();
}
else
{
core->regs.pc += 4;
}
}
}
// Integer Compare Instructions
namespace Gekko
{
#define SET_CR_LT(n) (core->regs.cr |= (1 << (3 + 4 * (7 - n))))
#define SET_CR_GT(n) (core->regs.cr |= (1 << (2 + 4 * (7 - n))))
#define SET_CR_EQ(n) (core->regs.cr |= (1 << (1 + 4 * (7 - n))))
#define SET_CR_SO(n) (core->regs.cr |= (1 << ( 4 * (7 - n))))
#define RESET_CR_LT(n) (core->regs.cr &= ~(1 << (3 + 4 * (7 - n))))
#define RESET_CR_GT(n) (core->regs.cr &= ~(1 << (2 + 4 * (7 - n))))
#define RESET_CR_EQ(n) (core->regs.cr &= ~(1 << (1 + 4 * (7 - n))))
#define RESET_CR_SO(n) (core->regs.cr &= ~(1 << ( 4 * (7 - n))))
// if a < b
// then c = 0b100
// else if a > b
// then c = 0b010
// else c = 0b001
// CR[4*crf..4*crf+3] = c || XER[SO]
template <typename T>
inline void Interpreter::CmpCommon(size_t crfd, T a, T b)
{
// TODO: Optimize
if (a < b) SET_CR_LT(crfd); else RESET_CR_LT(crfd);
if (a > b) SET_CR_GT(crfd); else RESET_CR_GT(crfd);
if (a == b) SET_CR_EQ(crfd); else RESET_CR_EQ(crfd);
if (IS_XER_SO) SET_CR_SO(crfd); else RESET_CR_SO(crfd);
core->regs.pc += 4;
}
// a = ra (signed)
// b = SIMM
void Interpreter::cmpi()
{
int32_t a = core->regs.gpr[info.paramBits[1]];
int32_t b = (int32_t)info.Imm.Signed;
CmpCommon<int32_t>(info.paramBits[0], a, b);
}
// a = ra (signed)
// b = rb (signed)
void Interpreter::cmp()
{
int32_t a = core->regs.gpr[info.paramBits[1]];
int32_t b = core->regs.gpr[info.paramBits[2]];
CmpCommon<int32_t>(info.paramBits[0], a, b);
}
// a = ra (unsigned)
// b = 0x0000 || UIMM
void Interpreter::cmpli()
{
uint32_t a = core->regs.gpr[info.paramBits[1]];
uint32_t b = info.Imm.Unsigned;
CmpCommon<uint32_t>(info.paramBits[0], a, b);
}
// a = ra (unsigned)
// b = rb (unsigned)
void Interpreter::cmpl()
{
uint32_t a = core->regs.gpr[info.paramBits[1]];
uint32_t b = core->regs.gpr[info.paramBits[2]];
CmpCommon<uint32_t>(info.paramBits[0], a, b);
}
}
// Condition Register Logical Instructions
namespace Gekko
{
// CR[crbd] = CR[crba] & CR[crbb]
void Interpreter::crand()
{
size_t crbd = info.paramBits[0], crba = info.paramBits[1], crbb = info.paramBits[2];
uint32_t a = (core->regs.cr >> (31 - crba)) & 1;
uint32_t b = (core->regs.cr >> (31 - crbb)) & 1;
uint32_t d = (a & b) << (31 - crbd); // <- crop is here
uint32_t m = ~(1 << (31 - crbd));
core->regs.cr = (core->regs.cr & m) | d;
core->regs.pc += 4;
}
// CR[crbd] = CR[crba] & ~CR[crbb]
void Interpreter::crandc()
{
size_t crbd = info.paramBits[0], crba = info.paramBits[1], crbb = info.paramBits[2];
uint32_t a = (core->regs.cr >> (31 - crba)) & 1;
uint32_t b = (core->regs.cr >> (31 - crbb)) & 1;
uint32_t d = (a & (~b)) << (31 - crbd); // <- crop is here
uint32_t m = ~(1 << (31 - crbd));
core->regs.cr = (core->regs.cr & m) | d;
core->regs.pc += 4;
}
// CR[crbd] = CR[crba] EQV CR[crbb]
void Interpreter::creqv()
{
size_t crbd = info.paramBits[0], crba = info.paramBits[1], crbb = info.paramBits[2];
uint32_t a = (core->regs.cr >> (31 - crba)) & 1;
uint32_t b = (core->regs.cr >> (31 - crbb)) & 1;
uint32_t d = (!(a ^ b)) << (31 - crbd); // <- crop is here
uint32_t m = ~(1 << (31 - crbd));
core->regs.cr = (core->regs.cr & m) | d;
core->regs.pc += 4;
}
// CR[crbd] = !(CR[crba] & CR[crbb])
void Interpreter::crnand()
{
size_t crbd = info.paramBits[0], crba = info.paramBits[1], crbb = info.paramBits[2];
uint32_t a = (core->regs.cr >> (31 - crba)) & 1;
uint32_t b = (core->regs.cr >> (31 - crbb)) & 1;
uint32_t d = (!(a & b)) << (31 - crbd); // <- crop is here
uint32_t m = ~(1 << (31 - crbd));
core->regs.cr = (core->regs.cr & m) | d;
core->regs.pc += 4;
}
// CR[crbd] = !(CR[crba] | CR[crbb])
void Interpreter::crnor()
{
size_t crbd = info.paramBits[0], crba = info.paramBits[1], crbb = info.paramBits[2];
uint32_t a = (core->regs.cr >> (31 - crba)) & 1;
uint32_t b = (core->regs.cr >> (31 - crbb)) & 1;
uint32_t d = (!(a | b)) << (31 - crbd); // <- crop is here
uint32_t m = ~(1 << (31 - crbd));
core->regs.cr = (core->regs.cr & m) | d;
core->regs.pc += 4;
}
// CR[crbd] = CR[crba] | CR[crbb]
void Interpreter::cror()
{
size_t crbd = info.paramBits[0], crba = info.paramBits[1], crbb = info.paramBits[2];
uint32_t a = (core->regs.cr >> (31 - crba)) & 1;
uint32_t b = (core->regs.cr >> (31 - crbb)) & 1;
uint32_t d = (a | b) << (31 - crbd); // <- crop is here
uint32_t m = ~(1 << (31 - crbd));
core->regs.cr = (core->regs.cr & m) | d;
core->regs.pc += 4;
}
// CR[crbd] = CR[crba] | ~CR[crbb]
void Interpreter::crorc()
{
size_t crbd = info.paramBits[0], crba = info.paramBits[1], crbb = info.paramBits[2];
uint32_t a = (core->regs.cr >> (31 - crba)) & 1;
uint32_t b = (core->regs.cr >> (31 - crbb)) & 1;
uint32_t d = (a | (~b)) << (31 - crbd); // <- crop is here
uint32_t m = ~(1 << (31 - crbd));
core->regs.cr = (core->regs.cr & m) | d;
core->regs.pc += 4;
}
// CR[crbd] = CR[crba] ^ CR[crbb]
void Interpreter::crxor()
{
size_t crbd = info.paramBits[0], crba = info.paramBits[1], crbb = info.paramBits[2];
uint32_t a = (core->regs.cr >> (31 - crba)) & 1;
uint32_t b = (core->regs.cr >> (31 - crbb)) & 1;
uint32_t d = (a ^ b) << (31 - crbd); // <- crop is here
uint32_t m = ~(1 << (31 - crbd));
core->regs.cr = (core->regs.cr & m) | d;
core->regs.pc += 4;
}
// CR[4*crfd .. 4*crfd + 3] = CR[4*crfs .. 4*crfs + 3]
void Interpreter::mcrf()
{
size_t crfd = 4 * (7 - info.paramBits[0]), crfs = 4 * (7 - info.paramBits[1]);
core->regs.cr = (core->regs.cr & (~(0xf << crfd))) | (((core->regs.cr >> crfs) & 0xf) << crfd);
core->regs.pc += 4;
}
}
// Floating-Point Instructions
namespace Gekko
{
void Interpreter::fadd()
{
if (core->regs.msr & MSR_FP)
{
FPRD(info.paramBits[0]) = FPRD(info.paramBits[1]) + FPRD(info.paramBits[2]);
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::fadd_d()
{
fadd();
if (!core->exception) COMPUTE_CR1();
}
void Interpreter::fadds()
{
if (core->regs.msr & MSR_FP)
{
FPRD(info.paramBits[0]) = (float)(FPRD(info.paramBits[1]) + FPRD(info.paramBits[2]));
if (core->regs.spr[SPR::HID2] & HID2_PSE) PS1(info.paramBits[0]) = PS0(info.paramBits[0]);
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::fadds_d()
{
fadds();
if (!core->exception) COMPUTE_CR1();
}
void Interpreter::fdiv()
{
if (core->regs.msr & MSR_FP)
{
FPRD(info.paramBits[0]) = FPRD(info.paramBits[1]) / FPRD(info.paramBits[2]);
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::fdiv_d()
{
fdiv();
if (!core->exception) COMPUTE_CR1();
}
void Interpreter::fdivs()
{
if (core->regs.msr & MSR_FP)
{
FPRD(info.paramBits[0]) = (float)(FPRD(info.paramBits[1]) / FPRD(info.paramBits[2]));
if (core->regs.spr[SPR::HID2] & HID2_PSE) PS1(info.paramBits[0]) = PS0(info.paramBits[0]);
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::fdivs_d()
{
fdivs();
if (!core->exception) COMPUTE_CR1();
}
void Interpreter::fmul()
{
if (core->regs.msr & MSR_FP)
{
FPRD(info.paramBits[0]) = FPRD(info.paramBits[1]) * FPRD(info.paramBits[2]);
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::fmul_d()
{
fmul();
if (!core->exception) COMPUTE_CR1();
}
void Interpreter::fmuls()
{
if (core->regs.msr & MSR_FP)
{
FPRD(info.paramBits[0]) = (float)(FPRD(info.paramBits[1]) * FPRD(info.paramBits[2]));
if (core->regs.spr[SPR::HID2] & HID2_PSE) PS1(info.paramBits[0]) = PS0(info.paramBits[0]);
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::fmuls_d()
{
fmuls();
if (!core->exception) COMPUTE_CR1();
}
void Interpreter::fres()
{
if (core->regs.msr & MSR_FP)
{
FPRD(info.paramBits[0]) = 1.0 / FPRD(info.paramBits[1]);
if (core->regs.spr[SPR::HID2] & HID2_PSE) PS1(info.paramBits[0]) = PS0(info.paramBits[0]);
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::fres_d()
{
fres();
if (!core->exception) COMPUTE_CR1();
}
void Interpreter::frsqrte()
{
if (core->regs.msr & MSR_FP)
{
FPRD(info.paramBits[0]) = 1.0 / sqrt(FPRD(info.paramBits[1]));
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::frsqrte_d()
{
frsqrte();
if (!core->exception) COMPUTE_CR1();
}
void Interpreter::fsub()
{
if (core->regs.msr & MSR_FP)
{
FPRD(info.paramBits[0]) = FPRD(info.paramBits[1]) - FPRD(info.paramBits[2]);
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::fsub_d()
{
fsub();
if (!core->exception) COMPUTE_CR1();
}
void Interpreter::fsubs()
{
if (core->regs.msr & MSR_FP)
{
FPRD(info.paramBits[0]) = (float)(FPRD(info.paramBits[1]) - FPRD(info.paramBits[2]));
if (core->regs.spr[SPR::HID2] & HID2_PSE) PS1(info.paramBits[0]) = PS0(info.paramBits[0]);
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::fsubs_d()
{
fsubs();
if (!core->exception) COMPUTE_CR1();
}
void Interpreter::fsel()
{
if (core->regs.msr & MSR_FP)
{
FPRD(info.paramBits[0]) = (FPRD(info.paramBits[1]) >= 0.0) ? (FPRD(info.paramBits[2])) : (FPRD(info.paramBits[3]));
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::fsel_d()
{
fsel();
if (!core->exception) COMPUTE_CR1();
}
void Interpreter::fmadd()
{
if (core->regs.msr & MSR_FP)
{
FPRD(info.paramBits[0]) = FPRD(info.paramBits[1]) * FPRD(info.paramBits[2]) + FPRD(info.paramBits[3]);
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::fmadd_d()
{
fmadd();
if (!core->exception) COMPUTE_CR1();
}
void Interpreter::fmadds()
{
if (core->regs.msr & MSR_FP)
{
FPRD(info.paramBits[0]) = (float)(FPRD(info.paramBits[1]) * FPRD(info.paramBits[2]) + FPRD(info.paramBits[3]));
if (core->regs.spr[SPR::HID2] & HID2_PSE) PS1(info.paramBits[0]) = PS0(info.paramBits[0]);
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::fmadds_d()
{
fmadds();
if (!core->exception) COMPUTE_CR1();
}
void Interpreter::fmsub()
{
if (core->regs.msr & MSR_FP)
{
FPRD(info.paramBits[0]) = FPRD(info.paramBits[1]) * FPRD(info.paramBits[2]) - FPRD(info.paramBits[3]);
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::fmsub_d()
{
fmsub();
if (!core->exception) COMPUTE_CR1();
}
void Interpreter::fmsubs()
{
if (core->regs.msr & MSR_FP)
{
FPRD(info.paramBits[0]) = (float)(FPRD(info.paramBits[1]) * FPRD(info.paramBits[2]) - FPRD(info.paramBits[3]));
if (core->regs.spr[SPR::HID2] & HID2_PSE) PS1(info.paramBits[0]) = PS0(info.paramBits[0]);
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::fmsubs_d()
{
fmsubs();
if (!core->exception) COMPUTE_CR1();
}
void Interpreter::fnmadd()
{
if (core->regs.msr & MSR_FP)
{
FPRD(info.paramBits[0]) = -(FPRD(info.paramBits[1]) * FPRD(info.paramBits[2]) + FPRD(info.paramBits[3]));
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::fnmadd_d()
{
fnmadd();
if (!core->exception) COMPUTE_CR1();
}
void Interpreter::fnmadds()
{
if (core->regs.msr & MSR_FP)
{
FPRD(info.paramBits[0]) = (float)(-(FPRD(info.paramBits[1]) * FPRD(info.paramBits[2]) + FPRD(info.paramBits[3])));
if (core->regs.spr[SPR::HID2] & HID2_PSE) PS1(info.paramBits[0]) = PS0(info.paramBits[0]);
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::fnmadds_d()
{
fnmadds();
if (!core->exception) COMPUTE_CR1();
}
void Interpreter::fnmsub()
{
if (core->regs.msr & MSR_FP)
{
FPRD(info.paramBits[0]) = -(FPRD(info.paramBits[1]) * FPRD(info.paramBits[2]) - FPRD(info.paramBits[3]));
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::fnmsub_d()
{
fnmsub();
if (!core->exception) COMPUTE_CR1();
}
void Interpreter::fnmsubs()
{
if (core->regs.msr & MSR_FP)
{
FPRD(info.paramBits[0]) = (float)(-(FPRD(info.paramBits[1]) * FPRD(info.paramBits[2]) - FPRD(info.paramBits[3])));
if (core->regs.spr[SPR::HID2] & HID2_PSE) PS1(info.paramBits[0]) = PS0(info.paramBits[0]);
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::fnmsubs_d()
{
fnmsubs();
if (!core->exception) COMPUTE_CR1();
}
void Interpreter::fctiw()
{
if (core->regs.msr & MSR_FP)
{
FPRU(info.paramBits[0]) = (uint64_t)(uint32_t)(int32_t)FPRD(info.paramBits[1]);
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::fctiw_d()
{
fctiw();
if (!core->exception) COMPUTE_CR1();
}
void Interpreter::fctiwz()
{
if (core->regs.msr & MSR_FP)
{
FPRU(info.paramBits[0]) = (uint64_t)(uint32_t)(int32_t)FPRD(info.paramBits[1]);
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::fctiwz_d()
{
fctiwz();
if (!core->exception) COMPUTE_CR1();
}
void Interpreter::frsp()
{
if (core->regs.msr & MSR_FP)
{
if (core->regs.spr[SPR::HID2] & HID2_PSE)
{
PS0(info.paramBits[0]) = (float)FPRD(info.paramBits[1]);
PS1(info.paramBits[0]) = PS0(info.paramBits[0]);
}
else FPRD(info.paramBits[0]) = (float)FPRD(info.paramBits[1]);
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::frsp_d()
{
frsp();
if (!core->exception) COMPUTE_CR1();
}
void Interpreter::fcmpo()
{
if (core->regs.msr & MSR_FP)
{
int32_t n = (int32_t)info.paramBits[0];
double a = FPRD(info.paramBits[1]), b = FPRD(info.paramBits[2]);
uint64_t da = FPRU(info.paramBits[1]), db = FPRU(info.paramBits[2]);
uint32_t c;
if (IS_NAN(da) || IS_NAN(db)) c = 1;
else if (a < b) c = 8;
else if (a > b) c = 4;
else c = 2;
core->regs.fpscr = (core->regs.fpscr & 0xffff0fff) | (c << 12);
core->regs.cr = (core->regs.cr & (~(0xf << ((7 - n) * 4)))) | (c << ((7 - n) * 4));
if (IS_SNAN(da) || IS_SNAN(db))
{
core->regs.fpscr = core->regs.fpscr | 0x01000000;
}
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::fcmpu()
{
if (core->regs.msr & MSR_FP)
{
int32_t n = (int32_t)info.paramBits[0];
double a = FPRD(info.paramBits[1]), b = FPRD(info.paramBits[2]);
uint64_t da = FPRU(info.paramBits[1]), db = FPRU(info.paramBits[2]);
uint32_t c;
if (IS_NAN(da) || IS_NAN(db)) c = 1;
else if (a < b) c = 8;
else if (a > b) c = 4;
else c = 2;
core->regs.fpscr = (core->regs.fpscr & 0xffff0fff) | (c << 12);
core->regs.cr = (core->regs.cr & (~(0xf << ((7 - n) * 4)))) | (c << ((7 - n) * 4));
if (IS_SNAN(da) || IS_SNAN(db))
{
core->regs.fpscr = core->regs.fpscr | 0x01000000;
}
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::fabs()
{
if (core->regs.msr & MSR_FP)
{
FPRU(info.paramBits[0]) = FPRU(info.paramBits[1]) & ~0x8000000000000000;
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::fabs_d()
{
fabs();
if (!core->exception) COMPUTE_CR1();
}
// fd = fb
void Interpreter::fmr()
{
if (core->regs.msr & MSR_FP)
{
FPRU(info.paramBits[0]) = FPRU(info.paramBits[1]);
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::fmr_d()
{
fmr();
if (!core->exception) COMPUTE_CR1();
}
void Interpreter::fnabs()
{
if (core->regs.msr & MSR_FP)
{
FPRU(info.paramBits[0]) = FPRU(info.paramBits[1]) | 0x8000000000000000;
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::fnabs_d()
{
fnabs();
if (!core->exception) COMPUTE_CR1();
}
void Interpreter::fneg()
{
if (core->regs.msr & MSR_FP)
{
FPRU(info.paramBits[0]) = FPRU(info.paramBits[1]) ^ 0x8000000000000000;
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::fneg_d()
{
fneg();
if (!core->exception) COMPUTE_CR1();
}
// CR[crfD] = FPSCR[crfS]
void Interpreter::mcrfs()
{
uint32_t fp = (core->regs.fpscr >> (28 - info.paramBits[1])) & 0xf;
core->regs.cr &= ~(0xf0000000 >> info.paramBits[0]);
core->regs.cr |= fp << (28 - info.paramBits[0]);
core->regs.pc += 4;
}
// fd[32-63] = FPSCR
void Interpreter::mffs()
{
if (core->regs.msr & MSR_FP)
{
FPRU(info.paramBits[0]) = (uint64_t)core->regs.fpscr;
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
// fd[32-63] = FPSCR, CR1
void Interpreter::mffs_d()
{
mffs();
if (!core->exception) COMPUTE_CR1();
}
// FPSCR(crbD) = 0 (clear bit)
void Interpreter::mtfsb0()
{
uint32_t m = 1 << (31 - info.paramBits[0]);
core->regs.fpscr &= ~m;
core->regs.pc += 4;
}
// FPSCR(crbD) = 0 (clear bit), CR1
void Interpreter::mtfsb0_d()
{
mtfsb0();
COMPUTE_CR1();
}
// FPSCR(crbD) = 1 (set bit)
void Interpreter::mtfsb1()
{
uint32_t m = 1 << (31 - info.paramBits[0]);
core->regs.fpscr = (core->regs.fpscr & ~m) | m;
core->regs.pc += 4;
}
// FPSCR(crbD) = 1 (set bit), CR1
void Interpreter::mtfsb1_d()
{
mtfsb1();
COMPUTE_CR1();
}
// mask = (4)FM[0] || (4)FM[1] || ... || (4)FM[7]
// FPSCR = (fb & mask) | (FPSCR & ~mask)
void Interpreter::mtfsf()
{
uint32_t m = 0, fm = (uint32_t)info.paramBits[0];
for (int i = 7; i >= 0; i--)
{
if ((fm >> i) & 1)
{
m |= 0xf;
}
m <<= 4;
}
core->regs.fpscr = ((uint32_t)FPRU(info.paramBits[1]) & m) | (core->regs.fpscr & ~m);
core->regs.pc += 4;
}
// mask = (4)FM[0] || (4)FM[1] || ... || (4)FM[7]
// FPSCR = (fb & mask) | (FPSCR & ~mask)
void Interpreter::mtfsf_d()
{
mtfsf();
COMPUTE_CR1();
}
// FPSCR[crfD] = IMM.
void Interpreter::mtfsfi()
{
int crf = info.paramBits[0] & 7;
int imm = info.paramBits[1] & 0xf;
uint32_t oldFpscr = core->regs.fpscr;
core->regs.fpscr &= ~(0xf << (7 - crf));
if (crf == 0)
{
// When FPSCR[0<>3] is specified, bits 0 (FX) and 3 (OX) are set to the values of IMM[0] and IMM[3]
core->regs.fpscr |= ((imm & 0b1001) << (7 - crf));
core->regs.fpscr |= oldFpscr & 0x60000000;
}
else
{
core->regs.fpscr |= (imm << (7 - crf));
}
core->regs.pc += 4;
}
void Interpreter::mtfsfi_d()
{
mtfsfi();
COMPUTE_CR1();
}
}
// Floating-Point Load and Store Instructions
namespace Gekko
{
// ea = (ra | 0) + SIMM
// fd = MEM(ea, 8)
void Interpreter::lfd()
{
if (core->regs.msr & MSR_FP)
{
if (info.paramBits[1]) core->ReadDouble(core->regs.gpr[info.paramBits[1]] + (int32_t)info.Imm.Signed, &FPRU(info.paramBits[0]));
else core->ReadDouble((int32_t)info.Imm.Signed, &FPRU(info.paramBits[0]));
if (core->exception) return;
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
// ea = ra + SIMM
// fd = MEM(ea, 8)
// ra = ea
void Interpreter::lfdu()
{
if (core->regs.msr & MSR_FP)
{
uint32_t ea = core->regs.gpr[info.paramBits[1]] + (int32_t)info.Imm.Signed;
core->ReadDouble(ea, &FPRU(info.paramBits[0]));
if (core->exception) return;
core->regs.gpr[info.paramBits[1]] = ea;
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
// ea = ra + rb
// fd = MEM(ea, 8)
// ra = ea
void Interpreter::lfdux()
{
if (core->regs.msr & MSR_FP)
{
uint32_t ea = core->regs.gpr[info.paramBits[1]] + core->regs.gpr[info.paramBits[2]];
core->ReadDouble(ea, &FPRU(info.paramBits[0]));
if (core->exception) return;
core->regs.gpr[info.paramBits[1]] = ea;
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
// ea = (ra | 0) + rb
// fd = MEM(ea, 8)
void Interpreter::lfdx()
{
if (core->regs.msr & MSR_FP)
{
if (info.paramBits[1]) core->ReadDouble(core->regs.gpr[info.paramBits[1]] + core->regs.gpr[info.paramBits[2]], &FPRU(info.paramBits[0]));
else core->ReadDouble(core->regs.gpr[info.paramBits[2]], &FPRU(info.paramBits[0]));
if (core->exception) return;
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
// ea = (ra | 0) + SIMM
// if HID2[PSE] = 0
// then fd = DOUBLE(MEM(ea, 4))
// else fd(ps0) = SINGLE(MEM(ea, 4))
// fd(ps1) = SINGLE(MEM(ea, 4))
void Interpreter::lfs()
{
if (core->regs.msr & MSR_FP)
{
float res;
if (info.paramBits[1]) core->ReadWord(core->regs.gpr[info.paramBits[1]] + (int32_t)info.Imm.Signed, (uint32_t*)&res);
else core->ReadWord((int32_t)info.Imm.Signed, (uint32_t*)&res);
if (core->exception) return;
if (core->regs.spr[SPR::HID2] & HID2_PSE) PS0(info.paramBits[0]) = PS1(info.paramBits[0]) = (double)res;
else FPRD(info.paramBits[0]) = (double)res;
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
// ea = ra + SIMM
// if HID2[PSE] = 0
// then fd = DOUBLE(MEM(ea, 4))
// else fd(ps0) = SINGLE(MEM(ea, 4))
// fd(ps1) = SINGLE(MEM(ea, 4))
// ra = ea
void Interpreter::lfsu()
{
if (core->regs.msr & MSR_FP)
{
uint32_t ea = core->regs.gpr[info.paramBits[1]] + (int32_t)info.Imm.Signed;
float res;
core->ReadWord(ea, (uint32_t*)&res);
if (core->exception) return;
if (core->regs.spr[SPR::HID2] & HID2_PSE) PS0(info.paramBits[0]) = PS1(info.paramBits[0]) = (double)res;
else FPRD(info.paramBits[0]) = (double)res;
core->regs.gpr[info.paramBits[1]] = ea;
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
// ea = ra + rb
// if HID2[PSE] = 0
// then fd = DOUBLE(MEM(ea, 4))
// else fd(ps0) = SINGLE(MEM(ea, 4))
// fd(ps1) = SINGLE(MEM(ea, 4))
// ra = ea
void Interpreter::lfsux()
{
if (core->regs.msr & MSR_FP)
{
uint32_t ea = core->regs.gpr[info.paramBits[1]] + core->regs.gpr[info.paramBits[2]];
float res;
core->ReadWord(ea, (uint32_t*)&res);
if (core->exception) return;
if (core->regs.spr[SPR::HID2] & HID2_PSE) PS0(info.paramBits[0]) = PS1(info.paramBits[0]) = (double)res;
else FPRD(info.paramBits[0]) = (double)res;
core->regs.gpr[info.paramBits[1]] = ea;
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
// ea = (ra | 0) + rb
// if HID2[PSE] = 0
// then fd = DOUBLE(MEM(ea, 4))
// else fd(ps0) = SINGLE(MEM(ea, 4))
// fd(ps1) = SINGLE(MEM(ea, 4))
void Interpreter::lfsx()
{
if (core->regs.msr & MSR_FP)
{
float res;
if (info.paramBits[1]) core->ReadWord(core->regs.gpr[info.paramBits[1]] + core->regs.gpr[info.paramBits[2]], (uint32_t*)&res);
else core->ReadWord(core->regs.gpr[info.paramBits[2]], (uint32_t*)&res);
if (core->exception) return;
if (core->regs.spr[SPR::HID2] & HID2_PSE) PS0(info.paramBits[0]) = PS1(info.paramBits[0]) = (double)res;
else FPRD(info.paramBits[0]) = (double)res;
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
// ea = (ra | 0) + SIMM
// MEM(ea, 8) = fs
void Interpreter::stfd()
{
if (core->regs.msr & MSR_FP)
{
if (info.paramBits[1]) core->WriteDouble(core->regs.gpr[info.paramBits[1]] + (int32_t)info.Imm.Signed, &FPRU(info.paramBits[0]));
else core->WriteDouble((int32_t)info.Imm.Signed, &FPRU(info.paramBits[0]));
if (core->exception) return;
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
// ea = ra + SIMM
// MEM(ea, 8) = fs
// ra = ea
void Interpreter::stfdu()
{
if (core->regs.msr & MSR_FP)
{
uint32_t ea = core->regs.gpr[info.paramBits[1]] + (int32_t)info.Imm.Signed;
core->WriteDouble(ea, &FPRU(info.paramBits[0]));
if (core->exception) return;
core->regs.gpr[info.paramBits[1]] = ea;
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
// ea = ra + rb
// MEM(ea, 8) = fs
// ra = ea
void Interpreter::stfdux()
{
if (core->regs.msr & MSR_FP)
{
uint32_t ea = core->regs.gpr[info.paramBits[1]] + core->regs.gpr[info.paramBits[2]];
core->WriteDouble(ea, &FPRU(info.paramBits[0]));
if (core->exception) return;
core->regs.gpr[info.paramBits[1]] = ea;
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
// ea = (ra | 0) + rb
// MEM(ea, 8) = fs
void Interpreter::stfdx()
{
if (core->regs.msr & MSR_FP)
{
if (info.paramBits[1]) core->WriteDouble(core->regs.gpr[info.paramBits[1]] + core->regs.gpr[info.paramBits[2]], &FPRU(info.paramBits[0]));
else core->WriteDouble(core->regs.gpr[info.paramBits[2]], &FPRU(info.paramBits[0]));
if (core->exception) return;
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
// ea = (ra | 0) + rb
// MEM(ea, 4) = fs[32-63]
void Interpreter::stfiwx()
{
if (core->regs.msr & MSR_FP)
{
uint32_t val = (uint32_t)(FPRU(info.paramBits[0]) & 0x00000000ffffffff);
if (info.paramBits[1]) core->WriteWord(core->regs.gpr[info.paramBits[1]] + core->regs.gpr[info.paramBits[2]], val);
else core->WriteWord(core->regs.gpr[info.paramBits[2]], val);
if (core->exception) return;
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
// ea = (ra | 0) + SIMM
// MEM(ea, 4) = SINGLE(fs)
void Interpreter::stfs()
{
if (core->regs.msr & MSR_FP)
{
float data = (float)FPRD(info.paramBits[0]);
if (info.paramBits[1]) core->WriteWord(core->regs.gpr[info.paramBits[1]] + (int32_t)info.Imm.Signed, *(uint32_t*)&data);
else core->WriteWord((int32_t)info.Imm.Signed, *(uint32_t*)&data);
if (core->exception) return;
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
// ea = ra + SIMM
// MEM(ea, 4) = SINGLE(fs)
// ra = ea
void Interpreter::stfsu()
{
if (core->regs.msr & MSR_FP)
{
float data = (float)FPRD(info.paramBits[0]);
uint32_t ea = core->regs.gpr[info.paramBits[1]] + (int32_t)info.Imm.Signed;
core->WriteWord(ea, *(uint32_t*)&data);
if (core->exception) return;
core->regs.gpr[info.paramBits[1]] = ea;
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
// ea = ra + rb
// MEM(ea, 4) = SINGLE(fs)
// ra = ea
void Interpreter::stfsux()
{
if (core->regs.msr & MSR_FP)
{
float data = (float)FPRD(info.paramBits[0]);
uint32_t ea = core->regs.gpr[info.paramBits[1]] + core->regs.gpr[info.paramBits[2]];
core->WriteWord(ea, *(uint32_t*)&data);
if (core->exception) return;
core->regs.gpr[info.paramBits[1]] = ea;
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
// ea = (ra | 0) + rb
// MEM(ea, 4) = SINGLE(fs)
void Interpreter::stfsx()
{
if (core->regs.msr & MSR_FP)
{
float data = (float)FPRD(info.paramBits[0]);
if (info.paramBits[1]) core->WriteWord(core->regs.gpr[info.paramBits[1]] + core->regs.gpr[info.paramBits[2]], *(uint32_t*)&data);
else core->WriteWord(core->regs.gpr[info.paramBits[2]], *(uint32_t*)&data);
if (core->exception) return;
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
}
// Integer Instructions
namespace Gekko
{
// We use macro programming to compress the source code.
// Now I am not very willing to use such things.
#define GPR(n) (core->regs.gpr[info.paramBits[(n)]])
// rd = ra + rb
void Interpreter::add()
{
GPR(0) = GPR(1) + GPR(2);
core->regs.pc += 4;
}
// rd = ra + rb, CR0
void Interpreter::add_d()
{
add();
COMPUTE_CR0(GPR(0));
}
// rd = ra + rb, XER
void Interpreter::addo()
{
CarryBit = 0;
GPR(0) = FullAdder(GPR(1), GPR(2));
if (OverflowBit) { SET_XER_OV(); SET_XER_SO(); }
else RESET_XER_OV();
core->regs.pc += 4;
}
// rd = ra + rb, CR0, XER
void Interpreter::addo_d()
{
addo();
COMPUTE_CR0(GPR(0));
}
// rd = ra + rb, XER[CA]
void Interpreter::addc()
{
CarryBit = 0;
GPR(0) = FullAdder(GPR(1), GPR(2));
if (CarryBit) SET_XER_CA(); else RESET_XER_CA();
core->regs.pc += 4;
}
// rd = ra + rb, XER[CA], CR0
void Interpreter::addc_d()
{
addc();
COMPUTE_CR0(GPR(0));
}
// rd = ra + rb, XER[CA], XER[OV]
void Interpreter::addco()
{
CarryBit = 0;
GPR(0) = FullAdder(GPR(1), GPR(2));
if (CarryBit) SET_XER_CA(); else RESET_XER_CA();
if (OverflowBit) { SET_XER_OV(); SET_XER_SO(); }
else RESET_XER_OV();
core->regs.pc += 4;
}
// rd = ra + rb, XER[CA], XER[OV], CR0
void Interpreter::addco_d()
{
addco();
COMPUTE_CR0(GPR(0));
}
// rd = ra + rb + XER[CA], XER
void Interpreter::adde()
{
CarryBit = IS_XER_CA ? 1 : 0;
GPR(0) = FullAdder(GPR(1), GPR(2));
if (CarryBit) SET_XER_CA(); else RESET_XER_CA();
core->regs.pc += 4;
}
// rd = ra + rb + XER[CA], CR0, XER
void Interpreter::adde_d()
{
adde();
COMPUTE_CR0(GPR(0));
}
void Interpreter::addeo()
{
CarryBit = IS_XER_CA ? 1 : 0;
GPR(0) = FullAdder(GPR(1), GPR(2));
if (CarryBit) SET_XER_CA(); else RESET_XER_CA();
if (OverflowBit) { SET_XER_OV(); SET_XER_SO(); }
else RESET_XER_OV();
core->regs.pc += 4;
}
void Interpreter::addeo_d()
{
addeo();
COMPUTE_CR0(GPR(0));
}
// rd = (ra | 0) + SIMM
void Interpreter::addi()
{
if (info.paramBits[1]) GPR(0) = GPR(1) + (int32_t)info.Imm.Signed;
else GPR(0) = (int32_t)info.Imm.Signed;
core->regs.pc += 4;
}
// rd = ra + SIMM, XER
void Interpreter::addic()
{
CarryBit = 0;
GPR(0) = FullAdder(GPR(1), (int32_t)info.Imm.Signed);
if (CarryBit) SET_XER_CA(); else RESET_XER_CA();
core->regs.pc += 4;
}
// rd = ra + SIMM, CR0, XER
void Interpreter::addic_d()
{
addic();
COMPUTE_CR0(GPR(0));
}
// rd = (ra | 0) + (SIMM || 0x0000)
void Interpreter::addis()
{
if (info.paramBits[1]) GPR(0) = GPR(1) + ((int32_t)info.Imm.Signed << 16);
else GPR(0) = (int32_t)info.Imm.Signed << 16;
core->regs.pc += 4;
}
// rd = ra + XER[CA] - 1 (0xffffffff), XER
void Interpreter::addme()
{
CarryBit = IS_XER_CA ? 1 : 0;
GPR(0) = FullAdder(GPR(1), -1);
if (CarryBit) SET_XER_CA(); else RESET_XER_CA();
core->regs.pc += 4;
}
// rd = ra + XER[CA] - 1 (0xffffffff), CR0, XER
void Interpreter::addme_d()
{
addme();
COMPUTE_CR0(GPR(0));
}
void Interpreter::addmeo()
{
CarryBit = IS_XER_CA ? 1 : 0;
GPR(0) = FullAdder(GPR(1), -1);
if (CarryBit) SET_XER_CA(); else RESET_XER_CA();
if (OverflowBit) { SET_XER_OV(); SET_XER_SO(); }
else RESET_XER_OV();
core->regs.pc += 4;
}
void Interpreter::addmeo_d()
{
addmeo();
COMPUTE_CR0(GPR(0));
}
// rd = ra + XER[CA], XER
void Interpreter::addze()
{
CarryBit = IS_XER_CA ? 1 : 0;
GPR(0) = FullAdder(GPR(1), 0);
if (CarryBit) SET_XER_CA(); else RESET_XER_CA();
core->regs.pc += 4;
}
// rd = ra + XER[CA], CR0, XER
void Interpreter::addze_d()
{
addze();
COMPUTE_CR0(GPR(0));
}
void Interpreter::addzeo()
{
CarryBit = IS_XER_CA ? 1 : 0;
GPR(0) = FullAdder(GPR(1), 0);
if (CarryBit) SET_XER_CA(); else RESET_XER_CA();
if (OverflowBit) { SET_XER_OV(); SET_XER_SO(); }
else RESET_XER_OV();
core->regs.pc += 4;
}
void Interpreter::addzeo_d()
{
addzeo();
COMPUTE_CR0(GPR(0));
}
// rd = ra / rb (signed)
void Interpreter::divw()
{
int32_t a = GPR(1), b = GPR(2);
if (b) GPR(0) = a / b;
core->regs.pc += 4;
}
// rd = ra / rb (signed), CR0
void Interpreter::divw_d()
{
int32_t a = GPR(1), b = GPR(2), res;
if (b)
{
res = a / b;
GPR(0) = res;
COMPUTE_CR0(res);
}
core->regs.pc += 4;
}
void Interpreter::divwo()
{
Halt("divwo\n");
}
void Interpreter::divwo_d()
{
divwo();
COMPUTE_CR0(GPR(0));
}
// rd = ra / rb (unsigned)
void Interpreter::divwu()
{
uint32_t a = GPR(1), b = GPR(2);
if (b) GPR(0) = a / b;
core->regs.pc += 4;
}
// rd = ra / rb (unsigned), CR0
void Interpreter::divwu_d()
{
uint32_t a = GPR(1), b = GPR(2), res;
if (b)
{
res = a / b;
GPR(0) = res;
COMPUTE_CR0(res);
}
core->regs.pc += 4;
}
void Interpreter::divwuo()
{
Halt("divwuo\n");
}
void Interpreter::divwuo_d()
{
divwuo();
COMPUTE_CR0(GPR(0));
}
// prod[0-63] = ra * rb
// rd = prod[0-31]
void Interpreter::mulhw()
{
int64_t a = (int32_t)GPR(1), b = (int32_t)GPR(2), res = a * b;
res = (res >> 32);
GPR(0) = (int32_t)res;
core->regs.pc += 4;
}
// prod[0-63] = ra * rb
// rd = prod[0-31]
// CR0
void Interpreter::mulhw_d()
{
mulhw();
COMPUTE_CR0(GPR(0));
}
// prod[0-63] = ra * rb
// rd = prod[0-31]
void Interpreter::mulhwu()
{
uint64_t a = GPR(1), b = GPR(2), res = a * b;
res = (res >> 32);
GPR(0) = (uint32_t)res;
core->regs.pc += 4;
}
// prod[0-63] = ra * rb
// rd = prod[0-31]
// CR0
void Interpreter::mulhwu_d()
{
mulhwu();
COMPUTE_CR0(GPR(0));
}
// prod[0-48] = ra * SIMM
// rd = prod[16-48]
void Interpreter::mulli()
{
GPR(0) = GPR(1) * (int32_t)info.Imm.Signed;
core->regs.pc += 4;
}
// prod[0-48] = ra * rb
// rd = prod[16-48]
void Interpreter::mullw()
{
int32_t a = GPR(1), b = GPR(2);
int64_t res = (int64_t)a * (int64_t)b;
GPR(0) = (int32_t)(res & 0x00000000ffffffff);
core->regs.pc += 4;
}
// prod[0-48] = ra * rb
// rd = prod[16-48]
// CR0
void Interpreter::mullw_d()
{
mullw();
COMPUTE_CR0(GPR(0));
}
void Interpreter::mullwo()
{
Halt("mullwo\n");
}
void Interpreter::mullwo_d()
{
mullwo();
COMPUTE_CR0(GPR(0));
}
// rd = ~ra + 1
void Interpreter::neg()
{
GPR(0) = ~GPR(1) + 1;
core->regs.pc += 4;
}
// rd = ~ra + 1, CR0
void Interpreter::neg_d()
{
neg();
COMPUTE_CR0(GPR(0));
}
void Interpreter::nego()
{
CarryBit = 0;
GPR(0) = FullAdder(~GPR(1), 1);
if (OverflowBit) { SET_XER_OV(); SET_XER_SO(); }
else RESET_XER_OV();
core->regs.pc += 4;
}
void Interpreter::nego_d()
{
nego();
COMPUTE_CR0(GPR(0));
}
// rd = ~ra + rb + 1
void Interpreter::subf()
{
GPR(0) = ~GPR(1) + GPR(2) + 1;
core->regs.pc += 4;
}
// rd = ~ra + rb + 1, CR0
void Interpreter::subf_d()
{
subf();
COMPUTE_CR0(GPR(0));
}
// rd = ~ra + rb + 1, XER
void Interpreter::subfo()
{
CarryBit = 1;
GPR(0) = FullAdder(~GPR(1), GPR(2));
if (OverflowBit) { SET_XER_OV(); SET_XER_SO(); }
else RESET_XER_OV();
core->regs.pc += 4;
}
// rd = ~ra + rb + 1, CR0, XER
void Interpreter::subfo_d()
{
subfo();
COMPUTE_CR0(GPR(0));
}
// rd = ~ra + rb + 1, XER[CA]
void Interpreter::subfc()
{
CarryBit = 1;
GPR(0) = FullAdder(~GPR(1), GPR(2));
if (CarryBit) SET_XER_CA(); else RESET_XER_CA();
core->regs.pc += 4;
}
// rd = ~ra + rb + 1, XER[CA], CR0
void Interpreter::subfc_d()
{
subfc();
COMPUTE_CR0(GPR(0));
}
void Interpreter::subfco()
{
CarryBit = 1;
GPR(0) = FullAdder(~GPR(1), GPR(2));
if (CarryBit) SET_XER_CA(); else RESET_XER_CA();
if (OverflowBit) { SET_XER_OV(); SET_XER_SO(); }
else RESET_XER_OV();
core->regs.pc += 4;
}
void Interpreter::subfco_d()
{
subfco();
COMPUTE_CR0(GPR(0));
}
// rd = ~ra + rb + XER[CA], XER
void Interpreter::subfe()
{
CarryBit = IS_XER_CA ? 1 : 0;
GPR(0) = FullAdder(~GPR(1), GPR(2));
if (CarryBit) SET_XER_CA(); else RESET_XER_CA();
core->regs.pc += 4;
}
// rd = ~ra + rb + XER[CA], CR0, XER
void Interpreter::subfe_d()
{
subfe();
COMPUTE_CR0(GPR(0));
}
void Interpreter::subfeo()
{
CarryBit = IS_XER_CA ? 1 : 0;
GPR(0) = FullAdder(~GPR(1), GPR(2));
if (CarryBit) SET_XER_CA(); else RESET_XER_CA();
if (OverflowBit) { SET_XER_OV(); SET_XER_SO(); }
else RESET_XER_OV();
core->regs.pc += 4;
}
void Interpreter::subfeo_d()
{
subfeo();
COMPUTE_CR0(GPR(0));
}
// rd = ~RRA + SIMM + 1, XER
void Interpreter::subfic()
{
CarryBit = 1;
GPR(0) = FullAdder(~GPR(1), (int32_t)info.Imm.Signed);
if (CarryBit) SET_XER_CA(); else RESET_XER_CA();
core->regs.pc += 4;
}
// rd = ~ra + XER[CA] - 1, XER
void Interpreter::subfme()
{
CarryBit = IS_XER_CA ? 1 : 0;
GPR(0) = FullAdder(~GPR(1), -1);
if (CarryBit) SET_XER_CA(); else RESET_XER_CA();
core->regs.pc += 4;
}
// rd = ~ra + XER[CA] - 1, CR0, XER
void Interpreter::subfme_d()
{
subfme();
COMPUTE_CR0(GPR(0));
}
void Interpreter::subfmeo()
{
CarryBit = IS_XER_CA ? 1 : 0;
GPR(0) = FullAdder(~GPR(1), -1);
if (CarryBit) SET_XER_CA(); else RESET_XER_CA();
if (OverflowBit) { SET_XER_OV(); SET_XER_SO(); }
else RESET_XER_OV();
core->regs.pc += 4;
}
void Interpreter::subfmeo_d()
{
subfmeo();
COMPUTE_CR0(GPR(0));
}
// rd = ~ra + XER[CA], XER
void Interpreter::subfze()
{
CarryBit = IS_XER_CA ? 1 : 0;
GPR(0) = FullAdder(~GPR(1), 0);
if (CarryBit) SET_XER_CA(); else RESET_XER_CA();
core->regs.pc += 4;
}
// rd = ~ra + XER[CA], CR0, XER
void Interpreter::subfze_d()
{
subfze();
COMPUTE_CR0(GPR(0));
}
void Interpreter::subfzeo()
{
CarryBit = IS_XER_CA ? 1 : 0;
GPR(0) = FullAdder(~GPR(1), 0);
if (CarryBit) SET_XER_CA(); else RESET_XER_CA();
if (OverflowBit) { SET_XER_OV(); SET_XER_SO(); }
else RESET_XER_OV();
core->regs.pc += 4;
}
void Interpreter::subfzeo_d()
{
subfzeo();
COMPUTE_CR0(GPR(0));
}
}
// Integer Load and Store Instructions
namespace Gekko
{
// ea = (ra | 0) + SIMM
// rd = 0x000000 || MEM(ea, 1)
void Interpreter::lbz()
{
if (info.paramBits[1]) core->ReadByte(core->regs.gpr[info.paramBits[1]] + (int32_t)info.Imm.Signed, &core->regs.gpr[info.paramBits[0]]);
else core->ReadByte((int32_t)info.Imm.Signed, &core->regs.gpr[info.paramBits[0]]);
if (core->exception) return;
core->regs.pc += 4;
}
// ea = ra + SIMM
// rd = 0x000000 || MEM(ea, 1)
// ra = ea
void Interpreter::lbzu()
{
uint32_t ea = core->regs.gpr[info.paramBits[1]] + (int32_t)info.Imm.Signed;
core->ReadByte(ea, &core->regs.gpr[info.paramBits[0]]);
if (core->exception) return;
core->regs.gpr[info.paramBits[1]] = ea;
core->regs.pc += 4;
}
// ea = ra + rb
// rd = 0x000000 || MEM(ea, 1)
// ra = ea
void Interpreter::lbzux()
{
uint32_t ea = core->regs.gpr[info.paramBits[1]] + core->regs.gpr[info.paramBits[2]];
core->ReadByte(ea, &core->regs.gpr[info.paramBits[0]]);
if (core->exception) return;
core->regs.gpr[info.paramBits[1]] = ea;
core->regs.pc += 4;
}
// ea = (ra | 0) + rb
// rd = 0x000000 || MEM(ea, 1)
void Interpreter::lbzx()
{
if (info.paramBits[1]) core->ReadByte(core->regs.gpr[info.paramBits[1]] + core->regs.gpr[info.paramBits[2]], &core->regs.gpr[info.paramBits[0]]);
else core->ReadByte(core->regs.gpr[info.paramBits[2]], &core->regs.gpr[info.paramBits[0]]);
if (core->exception) return;
core->regs.pc += 4;
}
// ea = (ra | 0) + SIMM
// rd = (signed)MEM(ea, 2)
void Interpreter::lha()
{
if (info.paramBits[1]) core->ReadHalf(core->regs.gpr[info.paramBits[1]] + (int32_t)info.Imm.Signed, &core->regs.gpr[info.paramBits[0]]);
else core->ReadHalf((int32_t)info.Imm.Signed, &core->regs.gpr[info.paramBits[0]]);
if (core->exception) return;
if (core->regs.gpr[info.paramBits[0]] & 0x8000) core->regs.gpr[info.paramBits[0]] |= 0xffff0000;
core->regs.pc += 4;
}
// ea = ra + SIMM
// rd = (signed)MEM(ea, 2)
// ra = ea
void Interpreter::lhau()
{
uint32_t ea = core->regs.gpr[info.paramBits[1]] + (int32_t)info.Imm.Signed;
core->ReadHalf(ea, &core->regs.gpr[info.paramBits[0]]);
if (core->exception) return;
if (core->regs.gpr[info.paramBits[0]] & 0x8000) core->regs.gpr[info.paramBits[0]] |= 0xffff0000;
core->regs.gpr[info.paramBits[1]] = ea;
core->regs.pc += 4;
}
// ea = ra + rb
// rd = (signed)MEM(ea, 2)
// ra = ea
void Interpreter::lhaux()
{
uint32_t ea = core->regs.gpr[info.paramBits[1]] + core->regs.gpr[info.paramBits[2]];
core->ReadHalf(ea, &core->regs.gpr[info.paramBits[0]]);
if (core->exception) return;
if (core->regs.gpr[info.paramBits[0]] & 0x8000) core->regs.gpr[info.paramBits[0]] |= 0xffff0000;
core->regs.gpr[info.paramBits[1]] = ea;
core->regs.pc += 4;
}
// ea = (ra | 0) + rb
// rd = (signed)MEM(ea, 2)
void Interpreter::lhax()
{
if (info.paramBits[1]) core->ReadHalf(core->regs.gpr[info.paramBits[1]] + core->regs.gpr[info.paramBits[2]], &core->regs.gpr[info.paramBits[0]]);
else core->ReadHalf(core->regs.gpr[info.paramBits[2]], &core->regs.gpr[info.paramBits[0]]);
if (core->exception) return;
if (core->regs.gpr[info.paramBits[0]] & 0x8000) core->regs.gpr[info.paramBits[0]] |= 0xffff0000;
core->regs.pc += 4;
}
// ea = (ra | 0) + SIMM
// rd = 0x0000 || MEM(ea, 2)
void Interpreter::lhz()
{
if (info.paramBits[1]) core->ReadHalf(core->regs.gpr[info.paramBits[1]] + (int32_t)info.Imm.Signed, &core->regs.gpr[info.paramBits[0]]);
else core->ReadHalf((int32_t)info.Imm.Signed, &core->regs.gpr[info.paramBits[0]]);
if (core->exception) return;
core->regs.pc += 4;
}
// ea = ra + SIMM
// rd = 0x0000 || MEM(ea, 2)
// ra = ea
void Interpreter::lhzu()
{
uint32_t ea = core->regs.gpr[info.paramBits[1]] + (int32_t)info.Imm.Signed;
core->ReadHalf(ea, &core->regs.gpr[info.paramBits[0]]);
if (core->exception) return;
core->regs.gpr[info.paramBits[1]] = ea;
core->regs.pc += 4;
}
// ea = ra + rb
// rd = 0x0000 || MEM(ea, 2)
// ra = ea
void Interpreter::lhzux()
{
uint32_t ea = core->regs.gpr[info.paramBits[1]] + core->regs.gpr[info.paramBits[2]];
core->ReadHalf(ea, &core->regs.gpr[info.paramBits[0]]);
if (core->exception) return;
core->regs.gpr[info.paramBits[1]] = ea;
core->regs.pc += 4;
}
// ea = (ra | 0) + rb
// rd = 0x0000 || MEM(ea, 2)
void Interpreter::lhzx()
{
if (info.paramBits[1]) core->ReadHalf(core->regs.gpr[info.paramBits[1]] + core->regs.gpr[info.paramBits[2]], &core->regs.gpr[info.paramBits[0]]);
else core->ReadHalf(core->regs.gpr[info.paramBits[2]], &core->regs.gpr[info.paramBits[0]]);
if (core->exception) return;
core->regs.pc += 4;
}
// ea = (ra | 0) + SIMM
// rd = MEM(ea, 4)
void Interpreter::lwz()
{
if (info.paramBits[1]) core->ReadWord(core->regs.gpr[info.paramBits[1]] + (int32_t)info.Imm.Signed, &core->regs.gpr[info.paramBits[0]]);
else core->ReadWord((int32_t)info.Imm.Signed, &core->regs.gpr[info.paramBits[0]]);
if (core->exception) return;
core->regs.pc += 4;
}
// ea = ra + SIMM
// rd = MEM(ea, 4)
// ra = ea
void Interpreter::lwzu()
{
uint32_t ea = core->regs.gpr[info.paramBits[1]] + (int32_t)info.Imm.Signed;
core->ReadWord(ea, &core->regs.gpr[info.paramBits[0]]);
if (core->exception) return;
core->regs.gpr[info.paramBits[1]] = ea;
core->regs.pc += 4;
}
// ea = ra + rb
// rd = MEM(ea, 4)
// ra = ea
void Interpreter::lwzux()
{
uint32_t ea = core->regs.gpr[info.paramBits[1]] + core->regs.gpr[info.paramBits[2]];
core->ReadWord(ea, &core->regs.gpr[info.paramBits[0]]);
if (core->exception) return;
core->regs.gpr[info.paramBits[1]] = ea;
core->regs.pc += 4;
}
// ea = (ra | 0) + rb
// rd = MEM(ea, 4)
void Interpreter::lwzx()
{
if (info.paramBits[1]) core->ReadWord(core->regs.gpr[info.paramBits[1]] + core->regs.gpr[info.paramBits[2]], &core->regs.gpr[info.paramBits[0]]);
else core->ReadWord(core->regs.gpr[info.paramBits[2]], &core->regs.gpr[info.paramBits[0]]);
if (core->exception) return;
core->regs.pc += 4;
}
// ea = (ra | 0) + SIMM
// MEM(ea, 1) = rs[24-31]
void Interpreter::stb()
{
if (info.paramBits[1]) core->WriteByte(core->regs.gpr[info.paramBits[1]] + (int32_t)info.Imm.Signed, core->regs.gpr[info.paramBits[0]]);
else core->WriteByte((int32_t)info.Imm.Signed, core->regs.gpr[info.paramBits[0]]);
if (core->exception) return;
core->regs.pc += 4;
}
// ea = ra + SIMM
// MEM(ea, 1) = rs[24-31]
// ra = ea
void Interpreter::stbu()
{
uint32_t ea = core->regs.gpr[info.paramBits[1]] + (int32_t)info.Imm.Signed;
core->WriteByte(ea, core->regs.gpr[info.paramBits[0]]);
if (core->exception) return;
core->regs.gpr[info.paramBits[1]] = ea;
core->regs.pc += 4;
}
// ea = ra + rb
// MEM(ea, 1) = rs[24-31]
// ra = ea
void Interpreter::stbux()
{
uint32_t ea = core->regs.gpr[info.paramBits[1]] + core->regs.gpr[info.paramBits[2]];
core->WriteByte(ea, core->regs.gpr[info.paramBits[0]]);
if (core->exception) return;
core->regs.gpr[info.paramBits[1]] = ea;
core->regs.pc += 4;
}
// ea = (ra | 0) + rb
// MEM(ea, 1) = rs[24-31]
void Interpreter::stbx()
{
if (info.paramBits[1]) core->WriteByte(core->regs.gpr[info.paramBits[1]] + core->regs.gpr[info.paramBits[2]], core->regs.gpr[info.paramBits[0]]);
else core->WriteByte(core->regs.gpr[info.paramBits[2]], core->regs.gpr[info.paramBits[0]]);
if (core->exception) return;
core->regs.pc += 4;
}
// ea = (ra | 0) + SIMM
// MEM(ea, 2) = rs[16-31]
void Interpreter::sth()
{
if (info.paramBits[1]) core->WriteHalf(core->regs.gpr[info.paramBits[1]] + (int32_t)info.Imm.Signed, core->regs.gpr[info.paramBits[0]]);
else core->WriteHalf((int32_t)info.Imm.Signed, core->regs.gpr[info.paramBits[0]]);
if (core->exception) return;
core->regs.pc += 4;
}
// ea = ra + SIMM
// MEM(ea, 2) = rs[16-31]
// ra = ea
void Interpreter::sthu()
{
uint32_t ea = core->regs.gpr[info.paramBits[1]] + (int32_t)info.Imm.Signed;
core->WriteHalf(ea, core->regs.gpr[info.paramBits[0]]);
if (core->exception) return;
core->regs.gpr[info.paramBits[1]] = ea;
core->regs.pc += 4;
}
// ea = ra + rb
// MEM(ea, 2) = rs[16-31]
// ra = ea
void Interpreter::sthux()
{
uint32_t ea = core->regs.gpr[info.paramBits[1]] + core->regs.gpr[info.paramBits[2]];
core->WriteHalf(ea, core->regs.gpr[info.paramBits[0]]);
if (core->exception) return;
core->regs.gpr[info.paramBits[1]] = ea;
core->regs.pc += 4;
}
// ea = (ra | 0) + rb
// MEM(ea, 2) = rs[16-31]
void Interpreter::sthx()
{
if (info.paramBits[1]) core->WriteHalf(core->regs.gpr[info.paramBits[1]] + core->regs.gpr[info.paramBits[2]], core->regs.gpr[info.paramBits[0]]);
else core->WriteHalf(core->regs.gpr[info.paramBits[2]], core->regs.gpr[info.paramBits[0]]);
if (core->exception) return;
core->regs.pc += 4;
}
// ea = (ra | 0) + SIMM
// MEM(ea, 4) = rs
void Interpreter::stw()
{
if (info.paramBits[1]) core->WriteWord(core->regs.gpr[info.paramBits[1]] + (int32_t)info.Imm.Signed, core->regs.gpr[info.paramBits[0]]);
else core->WriteWord((int32_t)info.Imm.Signed, core->regs.gpr[info.paramBits[0]]);
if (core->exception) return;
core->regs.pc += 4;
}
// ea = ra + SIMM
// MEM(ea, 4) = rs
// ra = ea
void Interpreter::stwu()
{
uint32_t ea = core->regs.gpr[info.paramBits[1]] + (int32_t)info.Imm.Signed;
core->WriteWord(ea, core->regs.gpr[info.paramBits[0]]);
if (core->exception) return;
core->regs.gpr[info.paramBits[1]] = ea;
core->regs.pc += 4;
}
// ea = ra + rb
// MEM(ea, 4) = rs
// ra = ea
void Interpreter::stwux()
{
uint32_t ea = core->regs.gpr[info.paramBits[1]] + core->regs.gpr[info.paramBits[2]];
core->WriteWord(ea, core->regs.gpr[info.paramBits[0]]);
if (core->exception) return;
core->regs.gpr[info.paramBits[1]] = ea;
core->regs.pc += 4;
}
// ea = (ra | 0) + rb
// MEM(ea, 4) = rs
void Interpreter::stwx()
{
if (info.paramBits[1]) core->WriteWord(core->regs.gpr[info.paramBits[1]] + core->regs.gpr[info.paramBits[2]], core->regs.gpr[info.paramBits[0]]);
else core->WriteWord(core->regs.gpr[info.paramBits[2]], core->regs.gpr[info.paramBits[0]]);
if (core->exception) return;
core->regs.pc += 4;
}
// ea = (ra | 0) + rb
// rd = 0x0000 || MEM(ea+1, 1) || MEM(EA, 1)
void Interpreter::lhbrx()
{
uint32_t val;
if (info.paramBits[1]) core->ReadHalf(core->regs.gpr[info.paramBits[1]] + core->regs.gpr[info.paramBits[2]], &val);
else core->ReadHalf(core->regs.gpr[info.paramBits[2]], &val);
if (core->exception) return;
core->regs.gpr[info.paramBits[0]] = _BYTESWAP_UINT16((uint16_t)val);
core->regs.pc += 4;
}
// ea = (ra | 0) + rb
// rd = MEM(ea+3, 1) || MEM(ea+2, 1) || MEM(ea+1, 1) || MEM(ea, 1)
void Interpreter::lwbrx()
{
uint32_t val;
if (info.paramBits[1]) core->ReadWord(core->regs.gpr[info.paramBits[1]] + core->regs.gpr[info.paramBits[2]], &val);
else core->ReadWord(core->regs.gpr[info.paramBits[2]], &val);
if (core->exception) return;
core->regs.gpr[info.paramBits[0]] = _BYTESWAP_UINT32(val);
core->regs.pc += 4;
}
// ea = (ra | 0) + rb
// MEM(ea, 2) = rs[24-31] || rs[16-23]
void Interpreter::sthbrx()
{
if (info.paramBits[1]) core->WriteHalf(core->regs.gpr[info.paramBits[1]] + core->regs.gpr[info.paramBits[2]], _BYTESWAP_UINT16((uint16_t)core->regs.gpr[info.paramBits[0]]));
else core->WriteHalf(core->regs.gpr[info.paramBits[2]], _BYTESWAP_UINT16((uint16_t)core->regs.gpr[info.paramBits[0]]));
if (core->exception) return;
core->regs.pc += 4;
}
// ea = (ra | 0) + rb
// MEM(ea, 4) = rs[24-31] || rs[16-23] || rs[8-15] || rs[0-7]
void Interpreter::stwbrx()
{
if (info.paramBits[1]) core->WriteWord(core->regs.gpr[info.paramBits[1]] + core->regs.gpr[info.paramBits[2]], _BYTESWAP_UINT32(core->regs.gpr[info.paramBits[0]]));
else core->WriteWord(core->regs.gpr[info.paramBits[2]], _BYTESWAP_UINT32(core->regs.gpr[info.paramBits[0]]));
if (core->exception) return;
core->regs.pc += 4;
}
// ea = (ra | 0) + SIMM
// r = rd
// while r <= 31
// GPR(r) = MEM(ea, 4)
// r = r + 1
// ea = ea + 4
void Interpreter::lmw()
{
uint32_t ea;
if (info.paramBits[1]) ea = core->regs.gpr[info.paramBits[1]] + (int32_t)info.Imm.Signed;
else ea = (int32_t)info.Imm.Signed;
for (size_t r = info.paramBits[0]; r < 32; r++, ea += 4)
{
core->ReadWord(ea, &core->regs.gpr[r]);
if (core->exception) return;
}
core->regs.pc += 4;
}
// ea = (ra | 0) + SIMM
// r = rs
// while r <= 31
// MEM(ea, 4) = GPR(r)
// r = r + 1
// ea = ea + 4
void Interpreter::stmw()
{
uint32_t ea;
if (info.paramBits[1]) ea = core->regs.gpr[info.paramBits[1]] + (int32_t)info.Imm.Signed;
else ea = (int32_t)info.Imm.Signed;
for (size_t r = info.paramBits[0]; r < 32; r++, ea += 4)
{
core->WriteWord(ea, core->regs.gpr[r]);
if (core->exception) return;
}
core->regs.pc += 4;
}
// ea = (ra | 0)
// n = NB ? NB : 32
// r = rd - 1
// i = 0
// while n > 0
// if i = 0 then
// r = (r + 1) % 32
// GPR(r) = 0
// GPR(r)[i...i+7] = MEM(ea, 1)
// i = i + 8
// if i = 32 then i = 0
// ea = ea + 1
// n = n -1
void Interpreter::lswi()
{
int32_t rd = info.paramBits[0], n = (info.paramBits[2]) ? (info.paramBits[2]) : 32, i = 4;
uint32_t ea = (info.paramBits[1]) ? (core->regs.gpr[info.paramBits[1]]) : 0;
uint32_t r = 0, val;
while (n > 0)
{
if (i == 0)
{
i = 4;
core->regs.gpr[rd] = r;
rd++;
rd %= 32;
r = 0;
}
core->ReadByte(ea, &val);
if (core->exception) return;
r <<= 8;
r |= (uint8_t)val;
ea++;
i--;
n--;
}
if (i != 0)
{
while (i)
{
r <<= 8;
i--;
}
core->regs.gpr[rd] = r;
}
core->regs.pc += 4;
}
// ea = (ra | 0) + rb
// n = XER[25-31]
// r = rd - 1
// i = 0
// while n > 0
// if i = 0 then
// r = (r + 1) % 32
// GPR(r) = 0
// GPR(r)[i...i+7] = MEM(ea, 1)
// i = i + 8
// if i = 32 then i = 0
// ea = ea + 1
// n = n -1
void Interpreter::lswx()
{
int32_t rd = info.paramBits[0], n = core->regs.spr[SPR::XER] & 0x7f, i = 4;
uint32_t ea = ((info.paramBits[1]) ? (core->regs.gpr[info.paramBits[1]]) : 0) + core->regs.gpr[info.paramBits[2]];
uint32_t r = 0, val;
while (n > 0)
{
if (i == 0)
{
i = 4;
core->regs.gpr[rd] = r;
rd++;
rd %= 32;
r = 0;
}
core->ReadByte(ea, &val);
if (core->exception) return;
r <<= 8;
r |= (uint8_t)val;
ea++;
i--;
n--;
}
if (i != 0)
{
while (i)
{
r <<= 8;
i--;
}
core->regs.gpr[rd] = r;
}
core->regs.pc += 4;
}
// ea = (ra | 0)
// n = NB ? NB : 32
// r = rs - 1
// i = 0
// while n > 0
// if i = 0 then r = (r + 1) % 32
// MEM(ea, 1) = GPR(r)[i...i+7]
// i = i + 8
// if i = 32 then i = 0;
// ea = ea + 1
// n = n -1
void Interpreter::stswi()
{
int32_t rs = info.paramBits[0], n = (info.paramBits[2]) ? (info.paramBits[2]) : 32, i = 0;
uint32_t ea = (info.paramBits[1]) ? (core->regs.gpr[info.paramBits[1]]) : 0;
uint32_t r = 0;
while (n > 0)
{
if (i == 0)
{
r = core->regs.gpr[rs];
rs++;
rs %= 32;
i = 4;
}
core->WriteByte(ea, r >> 24);
if (core->exception) return;
r <<= 8;
ea++;
i--;
n--;
}
core->regs.pc += 4;
}
// ea = (ra | 0)
// n = XER[25-31]
// r = rs - 1
// i = 0
// while n > 0
// if i = 0 then r = (r + 1) % 32
// MEM(ea, 1) = GPR(r)[i...i+7]
// i = i + 8
// if i = 32 then i = 0;
// ea = ea + 1
// n = n -1
void Interpreter::stswx()
{
int32_t rs = info.paramBits[0], n = core->regs.spr[SPR::XER] & 0x7f, i = 0;
uint32_t ea = ((info.paramBits[1]) ? (core->regs.gpr[info.paramBits[1]]) : 0) + core->regs.gpr[info.paramBits[2]];
uint32_t r = 0;
while (n > 0)
{
if (i == 0)
{
r = core->regs.gpr[rs];
rs++;
rs %= 32;
i = 4;
}
core->WriteByte(ea, r >> 24);
if (core->exception) return;
r <<= 8;
ea++;
i--;
n--;
}
core->regs.pc += 4;
}
}
// Logical Instructions
namespace Gekko
{
// ra = rs & rb
void Interpreter::_and()
{
GPR(0) = GPR(1) & GPR(2);
core->regs.pc += 4;
}
// ra = rs & rb, CR0
void Interpreter::and_d()
{
_and();
COMPUTE_CR0(GPR(0));
}
// ra = rs & ~rb
void Interpreter::andc()
{
GPR(0) = GPR(1) & (~GPR(2));
core->regs.pc += 4;
}
// ra = rs & ~rb, CR0
void Interpreter::andc_d()
{
andc();
COMPUTE_CR0(GPR(0));
}
// ra = rs & UIMM, CR0
void Interpreter::andi_d()
{
uint32_t res = GPR(1) & (uint32_t)info.Imm.Unsigned;
GPR(0) = res;
COMPUTE_CR0(res);
core->regs.pc += 4;
}
// ra = rs & (UIMM || 0x0000), CR0
void Interpreter::andis_d()
{
uint32_t res = GPR(1) & ((uint32_t)info.Imm.Unsigned << 16);
GPR(0) = res;
COMPUTE_CR0(res);
core->regs.pc += 4;
}
// n = 0
// while n < 32
// if rs[n] = 1 then leave
// n = n + 1
// ra = n
void Interpreter::cntlzw()
{
uint32_t n, m, rs = GPR(1);
for (n = 0, m = 1 << 31; n < 32; n++, m >>= 1)
{
if (rs & m) break;
}
GPR(0) = n;
core->regs.pc += 4;
}
// n = 0
// while n < 32
// if rs[n] = 1 then leave
// n = n + 1
// ra = n
// CR0
void Interpreter::cntlzw_d()
{
cntlzw();
COMPUTE_CR0(GPR(0));
}
// ra = rs EQV rb
void Interpreter::eqv()
{
GPR(0) = ~(GPR(1) ^ GPR(2));
core->regs.pc += 4;
}
// ra = rs EQV rb, CR0
void Interpreter::eqv_d()
{
eqv();
COMPUTE_CR0(GPR(0));
}
// sign = rs[24]
// ra[24-31] = rs[24-31]
// ra[0-23] = (24)sign
void Interpreter::extsb()
{
GPR(0) = (uint32_t)(int32_t)(int8_t)(uint8_t)GPR(1);
core->regs.pc += 4;
}
// sign = rs[24]
// ra[24-31] = rs[24-31]
// ra[0-23] = (24)sign
// CR0
void Interpreter::extsb_d()
{
extsb();
COMPUTE_CR0(GPR(0));
}
// sign = rs[16]
// ra[16-31] = rs[16-31]
// ra[0-15] = (16)sign
void Interpreter::extsh()
{
GPR(0) = (uint32_t)(int32_t)(int16_t)(uint16_t)GPR(1);
core->regs.pc += 4;
}
// sign = rs[16]
// ra[16-31] = rs[16-31]
// ra[0-15] = (16)sign
// CR0
void Interpreter::extsh_d()
{
extsh();
COMPUTE_CR0(GPR(0));
}
// ra = ~(rs & rb)
void Interpreter::nand()
{
GPR(0) = ~(GPR(1) & GPR(2));
core->regs.pc += 4;
}
// ra = ~(rs & rb), CR0
void Interpreter::nand_d()
{
nand();
COMPUTE_CR0(GPR(0));
}
// ra = ~(rs | rb)
void Interpreter::nor()
{
GPR(0) = ~(GPR(1) | GPR(2));
core->regs.pc += 4;
}
// ra = ~(rs | rb), CR0
void Interpreter::nor_d()
{
nor();
COMPUTE_CR0(GPR(0));
}
// ra = rs | rb
void Interpreter::_or()
{
GPR(0) = GPR(1) | GPR(2);
core->regs.pc += 4;
}
// ra = rs | rb, CR0
void Interpreter::or_d()
{
_or();
COMPUTE_CR0(GPR(0));
}
// ra = rs | ~rb
void Interpreter::orc()
{
GPR(0) = GPR(1) | (~GPR(2));
core->regs.pc += 4;
}
// ra = rs | ~rb, CR0
void Interpreter::orc_d()
{
orc();
COMPUTE_CR0(GPR(0));
}
// ra = rs | (0x0000 || UIMM)
void Interpreter::ori()
{
GPR(0) = GPR(1) | (uint32_t)info.Imm.Unsigned;
core->regs.pc += 4;
}
// ra = rs | (UIMM || 0x0000)
void Interpreter::oris()
{
GPR(0) = GPR(1) | ((uint32_t)info.Imm.Unsigned << 16);
core->regs.pc += 4;
}
// ra = rs ^ rb
void Interpreter::_xor()
{
GPR(0) = GPR(1) ^ GPR(2);
core->regs.pc += 4;
}
// ra = rs ^ rb, CR0
void Interpreter::xor_d()
{
_xor();
COMPUTE_CR0(GPR(0));
}
// ra = rs ^ (0x0000 || UIMM)
void Interpreter::xori()
{
GPR(0) = GPR(1) ^ (uint32_t)info.Imm.Unsigned;
core->regs.pc += 4;
}
// ra = rs ^ (UIMM || 0x0000)
void Interpreter::xoris()
{
GPR(0) = GPR(1) ^ ((uint32_t)info.Imm.Unsigned << 16);
core->regs.pc += 4;
}
}
// Paired Single Instructions
namespace Gekko
{
// Paired-Single Floating Point Arithmetic Instructions
void Interpreter::ps_div()
{
if (core->regs.msr & MSR_FP)
{
PS0(info.paramBits[0]) = PS0(info.paramBits[1]) / PS0(info.paramBits[2]);
PS1(info.paramBits[0]) = PS1(info.paramBits[1]) / PS1(info.paramBits[2]);
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::ps_div_d()
{
ps_div();
if (!core->exception) COMPUTE_CR1();
}
void Interpreter::ps_sub()
{
if (core->regs.msr & MSR_FP)
{
PS0(info.paramBits[0]) = PS0(info.paramBits[1]) - PS0(info.paramBits[2]);
PS1(info.paramBits[0]) = PS1(info.paramBits[1]) - PS1(info.paramBits[2]);
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::ps_sub_d()
{
ps_sub();
if (!core->exception) COMPUTE_CR1();
}
void Interpreter::ps_add()
{
if (core->regs.msr & MSR_FP)
{
PS0(info.paramBits[0]) = PS0(info.paramBits[1]) + PS0(info.paramBits[2]);
PS1(info.paramBits[0]) = PS1(info.paramBits[1]) + PS1(info.paramBits[2]);
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::ps_add_d()
{
ps_add();
if (!core->exception) COMPUTE_CR1();
}
void Interpreter::ps_sel()
{
if (core->regs.msr & MSR_FP)
{
PS0(info.paramBits[0]) = (PS0(info.paramBits[1]) >= 0.0) ? (PS0(info.paramBits[2])) : (PS0(info.paramBits[3]));
PS1(info.paramBits[0]) = (PS1(info.paramBits[1]) >= 0.0) ? (PS1(info.paramBits[2])) : (PS1(info.paramBits[3]));
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::ps_sel_d()
{
ps_sel();
if (!core->exception) COMPUTE_CR1();
}
void Interpreter::ps_res()
{
if (core->regs.msr & MSR_FP)
{
PS0(info.paramBits[0]) = 1.0f / PS0(info.paramBits[1]);
PS1(info.paramBits[0]) = 1.0f / PS1(info.paramBits[1]);
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::ps_res_d()
{
ps_res();
if (!core->exception) COMPUTE_CR1();
}
void Interpreter::ps_mul()
{
if (core->regs.msr & MSR_FP)
{
PS0(info.paramBits[0]) = PS0(info.paramBits[1]) * PS0(info.paramBits[2]);
PS1(info.paramBits[0]) = PS1(info.paramBits[1]) * PS1(info.paramBits[2]);
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::ps_mul_d()
{
ps_mul();
if (!core->exception) COMPUTE_CR1();
}
void Interpreter::ps_rsqrte()
{
if (core->regs.msr & MSR_FP)
{
PS0(info.paramBits[0]) = 1.0f / sqrt(PS0(info.paramBits[1]));
PS1(info.paramBits[0]) = 1.0f / sqrt(PS1(info.paramBits[1]));
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::ps_rsqrte_d()
{
ps_rsqrte();
if (!core->exception) COMPUTE_CR1();
}
void Interpreter::ps_msub()
{
if (core->regs.msr & MSR_FP)
{
double a = PS0(info.paramBits[1]);
double b = PS0(info.paramBits[3]);
double c = PS0(info.paramBits[2]);
PS0(info.paramBits[0]) = (a * c) - b;
a = PS1(info.paramBits[1]);
b = PS1(info.paramBits[3]);
c = PS1(info.paramBits[2]);
PS1(info.paramBits[0]) = (a * c) - b;
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::ps_msub_d()
{
ps_msub();
if (!core->exception) COMPUTE_CR1();
}
void Interpreter::ps_madd()
{
if (core->regs.msr & MSR_FP)
{
double a = PS0(info.paramBits[1]);
double b = PS0(info.paramBits[3]);
double c = PS0(info.paramBits[2]);
PS0(info.paramBits[0]) = (a * c) + b;
a = PS1(info.paramBits[1]);
b = PS1(info.paramBits[3]);
c = PS1(info.paramBits[2]);
PS1(info.paramBits[0]) = (a * c) + b;
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::ps_madd_d()
{
ps_madd();
if (!core->exception) COMPUTE_CR1();
}
void Interpreter::ps_nmsub()
{
if (core->regs.msr & MSR_FP)
{
double a = PS0(info.paramBits[1]);
double b = PS0(info.paramBits[3]);
double c = PS0(info.paramBits[2]);
PS0(info.paramBits[0]) = -((a * c) - b);
a = PS1(info.paramBits[1]);
b = PS1(info.paramBits[3]);
c = PS1(info.paramBits[2]);
PS1(info.paramBits[0]) = -((a * c) - b);
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::ps_nmsub_d()
{
ps_nmsub();
if (!core->exception) COMPUTE_CR1();
}
void Interpreter::ps_nmadd()
{
if (core->regs.msr & MSR_FP)
{
double a = PS0(info.paramBits[1]);
double b = PS0(info.paramBits[3]);
double c = PS0(info.paramBits[2]);
PS0(info.paramBits[0]) = -((a * c) + b);
a = PS1(info.paramBits[1]);
b = PS1(info.paramBits[3]);
c = PS1(info.paramBits[2]);
PS1(info.paramBits[0]) = -((a * c) + b);
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::ps_nmadd_d()
{
ps_nmadd();
if (!core->exception) COMPUTE_CR1();
}
void Interpreter::ps_neg()
{
if (core->regs.msr & MSR_FP)
{
PS0(info.paramBits[0]) = -PS0(info.paramBits[1]);
PS1(info.paramBits[0]) = -PS1(info.paramBits[1]);
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::ps_neg_d()
{
ps_neg();
if (!core->exception) COMPUTE_CR1();
}
void Interpreter::ps_mr()
{
if (core->regs.msr & MSR_FP)
{
double p0 = PS0(info.paramBits[1]), p1 = PS1(info.paramBits[1]);
PS0(info.paramBits[0]) = p0, PS1(info.paramBits[0]) = p1;
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::ps_mr_d()
{
ps_mr();
if (!core->exception) COMPUTE_CR1();
}
void Interpreter::ps_nabs()
{
if (core->regs.msr & MSR_FP)
{
PS0U(info.paramBits[0]) = PS0U(info.paramBits[1]) | 0x8000000000000000;
PS1U(info.paramBits[0]) = PS1U(info.paramBits[1]) | 0x8000000000000000;
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::ps_nabs_d()
{
ps_nabs();
if (!core->exception) COMPUTE_CR1();
}
void Interpreter::ps_abs()
{
if (core->regs.msr & MSR_FP)
{
PS0U(info.paramBits[0]) = PS0U(info.paramBits[1]) & ~0x8000000000000000;
PS1U(info.paramBits[0]) = PS1U(info.paramBits[1]) & ~0x8000000000000000;
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::ps_abs_d()
{
ps_abs();
if (!core->exception) COMPUTE_CR1();
}
// Miscellaneous Paired-Single Instructions
void Interpreter::ps_sum0()
{
if (core->regs.msr & MSR_FP)
{
double s0 = PS0(info.paramBits[1]) + PS1(info.paramBits[3]);
double s1 = PS1(info.paramBits[2]);
PS0(info.paramBits[0]) = s0;
PS1(info.paramBits[0]) = s1;
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::ps_sum0_d()
{
ps_sum0();
if (!core->exception) COMPUTE_CR1();
}
void Interpreter::ps_sum1()
{
if (core->regs.msr & MSR_FP)
{
double s0 = PS0(info.paramBits[2]);
double s1 = PS0(info.paramBits[1]) + PS1(info.paramBits[3]);
PS0(info.paramBits[0]) = s0;
PS1(info.paramBits[0]) = s1;
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::ps_sum1_d()
{
ps_sum1();
if (!core->exception) COMPUTE_CR1();
}
void Interpreter::ps_muls0()
{
if (core->regs.msr & MSR_FP)
{
double m0 = PS0(info.paramBits[1]) * PS0(info.paramBits[2]);
double m1 = PS1(info.paramBits[1]) * PS0(info.paramBits[2]);
PS0(info.paramBits[0]) = m0;
PS1(info.paramBits[0]) = m1;
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::ps_muls0_d()
{
ps_muls0();
if (!core->exception) COMPUTE_CR1();
}
void Interpreter::ps_muls1()
{
if (core->regs.msr & MSR_FP)
{
double m0 = PS0(info.paramBits[1]) * PS1(info.paramBits[2]);
double m1 = PS1(info.paramBits[1]) * PS1(info.paramBits[2]);
PS0(info.paramBits[0]) = m0;
PS1(info.paramBits[0]) = m1;
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::ps_muls1_d()
{
ps_muls1();
if (!core->exception) COMPUTE_CR1();
}
void Interpreter::ps_madds0()
{
if (core->regs.msr & MSR_FP)
{
double s0 = (PS0(info.paramBits[1]) * PS0(info.paramBits[2])) + PS0(info.paramBits[3]);
double s1 = (PS1(info.paramBits[1]) * PS0(info.paramBits[2])) + PS1(info.paramBits[3]);
PS0(info.paramBits[0]) = s0;
PS1(info.paramBits[0]) = s1;
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::ps_madds0_d()
{
ps_madds0();
if (!core->exception) COMPUTE_CR1();
}
void Interpreter::ps_madds1()
{
if (core->regs.msr & MSR_FP)
{
double s0 = (PS0(info.paramBits[1]) * PS1(info.paramBits[2])) + PS0(info.paramBits[3]);
double s1 = (PS1(info.paramBits[1]) * PS1(info.paramBits[2])) + PS1(info.paramBits[3]);
PS0(info.paramBits[0]) = s0;
PS1(info.paramBits[0]) = s1;
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::ps_madds1_d()
{
ps_madds1();
if (!core->exception) COMPUTE_CR1();
}
void Interpreter::ps_cmpu0()
{
if (core->regs.msr & MSR_FP)
{
size_t n = info.paramBits[0];
double a = PS0(info.paramBits[1]), b = PS0(info.paramBits[2]);
uint64_t da, db;
uint32_t c;
da = *(uint64_t*)&a;
db = *(uint64_t*)&b;
if (IS_NAN(da) || IS_NAN(db)) c = 1;
else if (a < b) c = 8;
else if (a > b) c = 4;
else c = 2;
SET_CRF(n, c);
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::ps_cmpo0()
{
if (core->regs.msr & MSR_FP)
{
size_t n = info.paramBits[0];
double a = PS0(info.paramBits[1]), b = PS0(info.paramBits[2]);
uint64_t da, db;
uint32_t c;
da = *(uint64_t*)&a;
db = *(uint64_t*)&b;
if (IS_NAN(da) || IS_NAN(db)) c = 1;
else if (a < b) c = 8;
else if (a > b) c = 4;
else c = 2;
SET_CRF(n, c);
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::ps_cmpu1()
{
if (core->regs.msr & MSR_FP)
{
size_t n = info.paramBits[0];
double a = PS1(info.paramBits[1]), b = PS1(info.paramBits[2]);
uint64_t da, db;
uint32_t c;
da = *(uint64_t*)&a;
db = *(uint64_t*)&b;
if (IS_NAN(da) || IS_NAN(db)) c = 1;
else if (a < b) c = 8;
else if (a > b) c = 4;
else c = 2;
SET_CRF(n, c);
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::ps_cmpo1()
{
if (core->regs.msr & MSR_FP)
{
size_t n = info.paramBits[0];
double a = PS1(info.paramBits[1]), b = PS1(info.paramBits[2]);
uint64_t da, db;
uint32_t c;
da = *(uint64_t*)&a;
db = *(uint64_t*)&b;
if (IS_NAN(da) || IS_NAN(db)) c = 1;
else if (a < b) c = 8;
else if (a > b) c = 4;
else c = 2;
SET_CRF(n, c);
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::ps_merge00()
{
if (core->regs.msr & MSR_FP)
{
double a = PS0(info.paramBits[1]);
double b = PS0(info.paramBits[2]);
PS0(info.paramBits[0]) = a;
PS1(info.paramBits[0]) = b;
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::ps_merge00_d()
{
ps_merge00();
if (!core->exception) COMPUTE_CR1();
}
void Interpreter::ps_merge01()
{
if (core->regs.msr & MSR_FP)
{
double a = PS0(info.paramBits[1]);
double b = PS1(info.paramBits[2]);
PS0(info.paramBits[0]) = a;
PS1(info.paramBits[0]) = b;
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::ps_merge01_d()
{
ps_merge01();
if (!core->exception) COMPUTE_CR1();
}
void Interpreter::ps_merge10()
{
if (core->regs.msr & MSR_FP)
{
double a = PS1(info.paramBits[1]);
double b = PS0(info.paramBits[2]);
PS0(info.paramBits[0]) = a;
PS1(info.paramBits[0]) = b;
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::ps_merge10_d()
{
ps_merge10();
if (!core->exception) COMPUTE_CR1();
}
void Interpreter::ps_merge11()
{
if (core->regs.msr & MSR_FP)
{
double a = PS1(info.paramBits[1]);
double b = PS1(info.paramBits[2]);
PS0(info.paramBits[0]) = a;
PS1(info.paramBits[0]) = b;
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::ps_merge11_d()
{
ps_merge11();
if (!core->exception) COMPUTE_CR1();
}
}
// Paired Single Load and Store Instructions
// used for fast type casting and matrix transfers.
namespace Gekko
{
#define LD_SCALE(n) ((core->regs.spr[SPR::GQRs + n] >> 24) & 0x3f)
#define LD_TYPE(n) (GEKKO_QUANT_TYPE)((core->regs.spr[SPR::GQRs + n] >> 16) & 7)
#define ST_SCALE(n) ((core->regs.spr[SPR::GQRs + n] >> 8) & 0x3f)
#define ST_TYPE(n) (GEKKO_QUANT_TYPE)((core->regs.spr[SPR::GQRs + n] ) & 7)
// INT -> float (F = I * 2 ** -S)
float Interpreter::dequantize(uint32_t data, GEKKO_QUANT_TYPE type, uint8_t scale)
{
float flt;
switch (type)
{
case GEKKO_QUANT_TYPE::U8: flt = (float)(uint8_t)data; break;
case GEKKO_QUANT_TYPE::U16: flt = (float)(uint16_t)data; break;
case GEKKO_QUANT_TYPE::S8:
if (data & 0x80) data |= 0xffffff00;
flt = (float)(int8_t)data; break;
case GEKKO_QUANT_TYPE::S16:
if (data & 0x8000) data |= 0xffff0000;
flt = (float)(int16_t)data; break;
case GEKKO_QUANT_TYPE::SINGLE_FLOAT:
default: flt = *((float*)&data); break;
}
return flt * core->interp->ldScale[scale];
}
// float -> INT (I = ROUND(F * 2 ** S))
uint32_t Interpreter::quantize(float data, GEKKO_QUANT_TYPE type, uint8_t scale)
{
uint32_t uval;
data *= core->interp->stScale[scale];
switch (type)
{
case GEKKO_QUANT_TYPE::U8:
if (data < 0) data = 0;
if (data > 255) data = 255;
uval = (uint8_t)(uint32_t)data; break;
case GEKKO_QUANT_TYPE::U16:
if (data < 0) data = 0;
if (data > 65535) data = 65535;
uval = (uint16_t)(uint32_t)data; break;
case GEKKO_QUANT_TYPE::S8:
if (data < -128) data = -128;
if (data > 127) data = 127;
uval = (int8_t)(uint8_t)(int32_t)(uint32_t)data; break;
case GEKKO_QUANT_TYPE::S16:
if (data < -32768) data = -32768;
if (data > 32767) data = 32767;
uval = (int16_t)(uint16_t)(int32_t)(uint32_t)data; break;
case GEKKO_QUANT_TYPE::SINGLE_FLOAT:
default: *((float*)&uval) = data; break;
}
return uval;
}
void Interpreter::psq_lx()
{
if ((core->regs.spr[SPR::HID2] & HID2_PSE) == 0 ||
(core->regs.spr[SPR::HID2] & HID2_LSQE) == 0)
{
core->PrCause = PrivilegedCause::IllegalInstruction;
core->Exception(Exception::EXCEPTION_PROGRAM);
return;
}
if (core->regs.msr & MSR_FP)
{
size_t i = info.paramBits[4];
uint32_t EA = core->regs.gpr[info.paramBits[2]], data0, data1;
int32_t d = info.paramBits[0];
uint8_t scale = (uint8_t)LD_SCALE(i);
GEKKO_QUANT_TYPE type = LD_TYPE(i);
if (info.paramBits[1]) EA += core->regs.gpr[info.paramBits[1]];
if (info.paramBits[3] /* W */)
{
if ((type == GEKKO_QUANT_TYPE::U8) || (type == GEKKO_QUANT_TYPE::S8)) core->ReadByte(EA, &data0);
else if ((type == GEKKO_QUANT_TYPE::U16) || (type == GEKKO_QUANT_TYPE::S16)) core->ReadHalf(EA, &data0);
else core->ReadWord(EA, &data0);
if (core->exception) return;
PS0(d) = (double)dequantize(data0, type, scale);
PS1(d) = 1.0f;
}
else
{
if ((type == GEKKO_QUANT_TYPE::U8) || (type == GEKKO_QUANT_TYPE::S8)) core->ReadByte(EA, &data0);
else if ((type == GEKKO_QUANT_TYPE::U16) || (type == GEKKO_QUANT_TYPE::S16)) core->ReadHalf(EA, &data0);
else core->ReadWord(EA, &data0);
if (core->exception) return;
if ((type == GEKKO_QUANT_TYPE::U8) || (type == GEKKO_QUANT_TYPE::S8)) core->ReadByte(EA + 1, &data1);
else if ((type == GEKKO_QUANT_TYPE::U16) || (type == GEKKO_QUANT_TYPE::S16)) core->ReadHalf(EA + 2, &data1);
else core->ReadWord(EA + 4, &data1);
if (core->exception) return;
PS0(d) = (double)dequantize(data0, type, scale);
PS1(d) = (double)dequantize(data1, type, scale);
}
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::psq_stx()
{
if ((core->regs.spr[SPR::HID2] & HID2_PSE) == 0 ||
(core->regs.spr[SPR::HID2] & HID2_LSQE) == 0)
{
core->PrCause = PrivilegedCause::IllegalInstruction;
core->Exception(Exception::EXCEPTION_PROGRAM);
return;
}
if (core->regs.msr & MSR_FP)
{
size_t i = info.paramBits[4];
uint32_t EA = core->regs.gpr[info.paramBits[2]];
int32_t d = info.paramBits[0];
uint8_t scale = (uint8_t)ST_SCALE(i);
GEKKO_QUANT_TYPE type = ST_TYPE(i);
if (info.paramBits[1]) EA += core->regs.gpr[info.paramBits[1]];
if (info.paramBits[3] /* W */)
{
if ((type == GEKKO_QUANT_TYPE::U8) || (type == GEKKO_QUANT_TYPE::S8)) core->WriteByte(EA, quantize((float)PS0(d), type, scale));
else if ((type == GEKKO_QUANT_TYPE::U16) || (type == GEKKO_QUANT_TYPE::S16)) core->WriteHalf(EA, quantize((float)PS0(d), type, scale));
else core->WriteWord(EA, quantize((float)PS0(d), type, scale));
}
else
{
if ((type == GEKKO_QUANT_TYPE::U8) || (type == GEKKO_QUANT_TYPE::S8)) core->WriteByte(EA, quantize((float)PS0(d), type, scale));
else if ((type == GEKKO_QUANT_TYPE::U16) || (type == GEKKO_QUANT_TYPE::S16)) core->WriteHalf(EA, quantize((float)PS0(d), type, scale));
else core->WriteWord(EA, quantize((float)PS0(d), type, scale));
if (core->exception) return;
if ((type == GEKKO_QUANT_TYPE::U8) || (type == GEKKO_QUANT_TYPE::S8)) core->WriteByte(EA + 1, quantize((float)PS1(d), type, scale));
else if ((type == GEKKO_QUANT_TYPE::U16) || (type == GEKKO_QUANT_TYPE::S16)) core->WriteHalf(EA + 2, quantize((float)PS1(d), type, scale));
else core->WriteWord(EA + 4, quantize((float)PS1(d), type, scale));
}
if (core->exception) return;
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::psq_lux()
{
if ((core->regs.spr[SPR::HID2] & HID2_PSE) == 0 ||
(core->regs.spr[SPR::HID2] & HID2_LSQE) == 0 ||
info.paramBits[1] == 0)
{
core->PrCause = PrivilegedCause::IllegalInstruction;
core->Exception(Exception::EXCEPTION_PROGRAM);
return;
}
if (core->regs.msr & MSR_FP)
{
size_t i = info.paramBits[4];
uint32_t EA = core->regs.gpr[info.paramBits[2]], data0, data1;
int32_t d = info.paramBits[0];
uint8_t scale = (uint8_t)LD_SCALE(i);
GEKKO_QUANT_TYPE type = LD_TYPE(i);
EA += core->regs.gpr[info.paramBits[1]];
if (info.paramBits[3] /* W */)
{
if ((type == GEKKO_QUANT_TYPE::U8) || (type == GEKKO_QUANT_TYPE::S8)) core->ReadByte(EA, &data0);
else if ((type == GEKKO_QUANT_TYPE::U16) || (type == GEKKO_QUANT_TYPE::S16)) core->ReadHalf(EA, &data0);
else core->ReadWord(EA, &data0);
if (core->exception) return;
PS0(d) = (double)dequantize(data0, type, scale);
PS1(d) = 1.0f;
}
else
{
if ((type == GEKKO_QUANT_TYPE::U8) || (type == GEKKO_QUANT_TYPE::S8)) core->ReadByte(EA, &data0);
else if ((type == GEKKO_QUANT_TYPE::U16) || (type == GEKKO_QUANT_TYPE::S16)) core->ReadHalf(EA, &data0);
else core->ReadWord(EA, &data0);
if (core->exception) return;
if ((type == GEKKO_QUANT_TYPE::U8) || (type == GEKKO_QUANT_TYPE::S8)) core->ReadByte(EA + 1, &data1);
else if ((type == GEKKO_QUANT_TYPE::U16) || (type == GEKKO_QUANT_TYPE::S16)) core->ReadHalf(EA + 2, &data1);
else core->ReadWord(EA + 4, &data1);
if (core->exception) return;
PS0(d) = (double)dequantize(data0, type, scale);
PS1(d) = (double)dequantize(data1, type, scale);
}
core->regs.gpr[info.paramBits[1]] = EA;
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::psq_stux()
{
if ((core->regs.spr[SPR::HID2] & HID2_PSE) == 0 ||
(core->regs.spr[SPR::HID2] & HID2_LSQE) == 0 ||
info.paramBits[1] == 0)
{
core->PrCause = PrivilegedCause::IllegalInstruction;
core->Exception(Exception::EXCEPTION_PROGRAM);
return;
}
if (core->regs.msr & MSR_FP)
{
size_t i = info.paramBits[4];
uint32_t EA = core->regs.gpr[info.paramBits[2]];
int32_t d = info.paramBits[0];
uint8_t scale = (uint8_t)ST_SCALE(i);
GEKKO_QUANT_TYPE type = ST_TYPE(i);
EA += core->regs.gpr[info.paramBits[1]];
if (info.paramBits[3] /* W */)
{
if ((type == GEKKO_QUANT_TYPE::U8) || (type == GEKKO_QUANT_TYPE::S8)) core->WriteByte(EA, quantize((float)PS0(d), type, scale));
else if ((type == GEKKO_QUANT_TYPE::U16) || (type == GEKKO_QUANT_TYPE::S16)) core->WriteHalf(EA, quantize((float)PS0(d), type, scale));
else core->WriteWord(EA, quantize((float)PS0(d), type, scale));
}
else
{
if ((type == GEKKO_QUANT_TYPE::U8) || (type == GEKKO_QUANT_TYPE::S8)) core->WriteByte(EA, quantize((float)PS0(d), type, scale));
else if ((type == GEKKO_QUANT_TYPE::U16) || (type == GEKKO_QUANT_TYPE::S16)) core->WriteHalf(EA, quantize((float)PS0(d), type, scale));
else core->WriteWord(EA, quantize((float)PS0(d), type, scale));
if (core->exception) return;
if ((type == GEKKO_QUANT_TYPE::U8) || (type == GEKKO_QUANT_TYPE::S8)) core->WriteByte(EA + 1, quantize((float)PS1(d), type, scale));
else if ((type == GEKKO_QUANT_TYPE::U16) || (type == GEKKO_QUANT_TYPE::S16)) core->WriteHalf(EA + 2, quantize((float)PS1(d), type, scale));
else core->WriteWord(EA + 4, quantize((float)PS1(d), type, scale));
}
if (core->exception) return;
core->regs.gpr[info.paramBits[1]] = EA;
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::psq_l()
{
if ((core->regs.spr[SPR::HID2] & HID2_PSE) == 0 ||
(core->regs.spr[SPR::HID2] & HID2_LSQE) == 0)
{
core->PrCause = PrivilegedCause::IllegalInstruction;
core->Exception(Exception::EXCEPTION_PROGRAM);
return;
}
if (core->regs.msr & MSR_FP)
{
uint32_t EA = info.Imm.Signed & 0xfff, data0, data1;
int32_t d = info.paramBits[0];
uint8_t scale = (uint8_t)LD_SCALE(info.paramBits[3]);
GEKKO_QUANT_TYPE type = LD_TYPE(info.paramBits[3]);
if (EA & 0x800) EA |= 0xfffff000;
if (info.paramBits[1]) EA += core->regs.gpr[info.paramBits[1]];
if (info.paramBits[2])
{
if ((type == GEKKO_QUANT_TYPE::U8) || (type == GEKKO_QUANT_TYPE::S8)) core->ReadByte(EA, &data0);
else if ((type == GEKKO_QUANT_TYPE::U16) || (type == GEKKO_QUANT_TYPE::S16)) core->ReadHalf(EA, &data0);
else core->ReadWord(EA, &data0);
if (core->exception) return;
PS0(d) = (double)dequantize(data0, type, scale);
PS1(d) = 1.0f;
}
else
{
if ((type == GEKKO_QUANT_TYPE::U8) || (type == GEKKO_QUANT_TYPE::S8)) core->ReadByte(EA, &data0);
else if ((type == GEKKO_QUANT_TYPE::U16) || (type == GEKKO_QUANT_TYPE::S16)) core->ReadHalf(EA, &data0);
else core->ReadWord(EA, &data0);
if (core->exception) return;
if ((type == GEKKO_QUANT_TYPE::U8) || (type == GEKKO_QUANT_TYPE::S8)) core->ReadByte(EA + 1, &data1);
else if ((type == GEKKO_QUANT_TYPE::U16) || (type == GEKKO_QUANT_TYPE::S16)) core->ReadHalf(EA + 2, &data1);
else core->ReadWord(EA + 4, &data1);
if (core->exception) return;
PS0(d) = (double)dequantize(data0, type, scale);
PS1(d) = (double)dequantize(data1, type, scale);
}
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::psq_lu()
{
if ((core->regs.spr[SPR::HID2] & HID2_PSE) == 0 ||
(core->regs.spr[SPR::HID2] & HID2_LSQE) == 0 ||
info.paramBits[1] == 0)
{
core->PrCause = PrivilegedCause::IllegalInstruction;
core->Exception(Exception::EXCEPTION_PROGRAM);
return;
}
if (core->regs.msr & MSR_FP)
{
uint32_t EA = info.Imm.Signed & 0xfff, data0, data1;
int32_t d = info.paramBits[0];
uint8_t scale = (uint8_t)LD_SCALE(info.paramBits[3]);
GEKKO_QUANT_TYPE type = LD_TYPE(info.paramBits[3]);
if (EA & 0x800) EA |= 0xfffff000;
EA += core->regs.gpr[info.paramBits[1]];
if (info.paramBits[2])
{
if ((type == GEKKO_QUANT_TYPE::U8) || (type == GEKKO_QUANT_TYPE::S8)) core->ReadByte(EA, &data0);
else if ((type == GEKKO_QUANT_TYPE::U16) || (type == GEKKO_QUANT_TYPE::S16)) core->ReadHalf(EA, &data0);
else core->ReadWord(EA, &data0);
if (core->exception) return;
PS0(d) = (double)dequantize(data0, type, scale);
PS1(d) = 1.0f;
}
else
{
if ((type == GEKKO_QUANT_TYPE::U8) || (type == GEKKO_QUANT_TYPE::S8)) core->ReadByte(EA, &data0);
else if ((type == GEKKO_QUANT_TYPE::U16) || (type == GEKKO_QUANT_TYPE::S16)) core->ReadHalf(EA, &data0);
else core->ReadWord(EA, &data0);
if (core->exception) return;
if ((type == GEKKO_QUANT_TYPE::U8) || (type == GEKKO_QUANT_TYPE::S8)) core->ReadByte(EA + 1, &data1);
else if ((type == GEKKO_QUANT_TYPE::U16) || (type == GEKKO_QUANT_TYPE::S16)) core->ReadHalf(EA + 2, &data1);
else core->ReadWord(EA + 4, &data1);
if (core->exception) return;
PS0(d) = (double)dequantize(data0, type, scale);
PS1(d) = (double)dequantize(data1, type, scale);
}
core->regs.gpr[info.paramBits[1]] = EA;
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::psq_st()
{
if ((core->regs.spr[SPR::HID2] & HID2_PSE) == 0 ||
(core->regs.spr[SPR::HID2] & HID2_LSQE) == 0)
{
core->PrCause = PrivilegedCause::IllegalInstruction;
core->Exception(Exception::EXCEPTION_PROGRAM);
return;
}
if (core->regs.msr & MSR_FP)
{
uint32_t EA = info.Imm.Signed & 0xfff;
int32_t d = info.paramBits[0];
uint8_t scale = (uint8_t)ST_SCALE(info.paramBits[3]);
GEKKO_QUANT_TYPE type = ST_TYPE(info.paramBits[3]);
if (EA & 0x800) EA |= 0xfffff000;
if (info.paramBits[1]) EA += core->regs.gpr[info.paramBits[1]];
if (info.paramBits[2])
{
if ((type == GEKKO_QUANT_TYPE::U8) || (type == GEKKO_QUANT_TYPE::S8)) core->WriteByte(EA, quantize((float)PS0(d), type, scale));
else if ((type == GEKKO_QUANT_TYPE::U16) || (type == GEKKO_QUANT_TYPE::S16)) core->WriteHalf(EA, quantize((float)PS0(d), type, scale));
else core->WriteWord(EA, quantize((float)PS0(d), type, scale));
}
else
{
if ((type == GEKKO_QUANT_TYPE::U8) || (type == GEKKO_QUANT_TYPE::S8)) core->WriteByte(EA, quantize((float)PS0(d), type, scale));
else if ((type == GEKKO_QUANT_TYPE::U16) || (type == GEKKO_QUANT_TYPE::S16)) core->WriteHalf(EA, quantize((float)PS0(d), type, scale));
else core->WriteWord(EA, quantize((float)PS0(d), type, scale));
if (core->exception) return;
if ((type == GEKKO_QUANT_TYPE::U8) || (type == GEKKO_QUANT_TYPE::S8)) core->WriteByte(EA + 1, quantize((float)PS1(d), type, scale));
else if ((type == GEKKO_QUANT_TYPE::U16) || (type == GEKKO_QUANT_TYPE::S16)) core->WriteHalf(EA + 2, quantize((float)PS1(d), type, scale));
else core->WriteWord(EA + 4, quantize((float)PS1(d), type, scale));
}
if (core->exception) return;
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
void Interpreter::psq_stu()
{
if ((core->regs.spr[SPR::HID2] & HID2_PSE) == 0 ||
(core->regs.spr[SPR::HID2] & HID2_LSQE) == 0 ||
info.paramBits[1] == 0)
{
core->PrCause = PrivilegedCause::IllegalInstruction;
core->Exception(Exception::EXCEPTION_PROGRAM);
return;
}
if (core->regs.msr & MSR_FP)
{
uint32_t EA = info.Imm.Signed & 0xfff;
int32_t d = info.paramBits[0];
uint8_t scale = (uint8_t)ST_SCALE(info.paramBits[3]);
GEKKO_QUANT_TYPE type = ST_TYPE(info.paramBits[3]);
if (EA & 0x800) EA |= 0xfffff000;
EA += core->regs.gpr[info.paramBits[1]];
if (info.paramBits[2])
{
if ((type == GEKKO_QUANT_TYPE::U8) || (type == GEKKO_QUANT_TYPE::S8)) core->WriteByte(EA, quantize((float)PS0(d), type, scale));
else if ((type == GEKKO_QUANT_TYPE::U16) || (type == GEKKO_QUANT_TYPE::S16)) core->WriteHalf(EA, quantize((float)PS0(d), type, scale));
else core->WriteWord(EA, quantize((float)PS0(d), type, scale));
}
else
{
if ((type == GEKKO_QUANT_TYPE::U8) || (type == GEKKO_QUANT_TYPE::S8)) core->WriteByte(EA, quantize((float)PS0(d), type, scale));
else if ((type == GEKKO_QUANT_TYPE::U16) || (type == GEKKO_QUANT_TYPE::S16)) core->WriteHalf(EA, quantize((float)PS0(d), type, scale));
else core->WriteWord(EA, quantize((float)PS0(d), type, scale));
if (core->exception) return;
if ((type == GEKKO_QUANT_TYPE::U8) || (type == GEKKO_QUANT_TYPE::S8)) core->WriteByte(EA + 1, quantize((float)PS1(d), type, scale));
else if ((type == GEKKO_QUANT_TYPE::U16) || (type == GEKKO_QUANT_TYPE::S16)) core->WriteHalf(EA + 2, quantize((float)PS1(d), type, scale));
else core->WriteWord(EA + 4, quantize((float)PS1(d), type, scale));
}
if (core->exception) return;
core->regs.gpr[info.paramBits[1]] = EA;
core->regs.pc += 4;
}
else core->Exception(Gekko::Exception::EXCEPTION_FP_UNAVAILABLE);
}
}
// Integer Rotate Instructions
namespace Gekko
{
// n = SH
// r = ROTL(rs, n)
// m = MASK(mb, me)
// ra = (r & m) | (ra & ~m)
// CR0 (if .)
void Interpreter::rlwimi()
{
uint32_t m = rotmask[info.paramBits[3]][info.paramBits[4]];
uint32_t r = Rotl32(info.paramBits[2], GPR(1));
GPR(0) = (r & m) | (core->regs.gpr[info.paramBits[0]] & ~m);
core->regs.pc += 4;
}
void Interpreter::rlwimi_d()
{
rlwimi();
COMPUTE_CR0(GPR(0));
}
// n = SH
// r = ROTL(rs, n)
// m = MASK(MB, ME)
// ra = r & m
// CR0 (if .)
void Interpreter::rlwinm()
{
uint32_t m = rotmask[info.paramBits[3]][info.paramBits[4]];
uint32_t r = Rotl32(info.paramBits[2], GPR(1));
GPR(0) = r & m;
core->regs.pc += 4;
}
void Interpreter::rlwinm_d()
{
rlwinm();
COMPUTE_CR0(GPR(0));
}
// n = rb[27-31]
// r = ROTL(rs, n)
// m = MASK(MB, ME)
// ra = r & m
void Interpreter::rlwnm()
{
uint32_t m = rotmask[info.paramBits[3]][info.paramBits[4]];
uint32_t r = Rotl32(GPR(2) & 0x1f, GPR(1));
GPR(0) = r & m;
core->regs.pc += 4;
}
void Interpreter::rlwnm_d()
{
rlwnm();
COMPUTE_CR0(GPR(0));
}
}
// Integer Shift Instructions
namespace Gekko
{
// n = rb[27-31]
// r = ROTL(rs, n)
// if rb[26] = 0
// then m = MASK(0, 31-n)
// else m = (32)0
// ra = r & m
// (simply : ra = rs << rb, or ra = 0, if rb[26] = 1)
void Interpreter::slw()
{
uint32_t n = GPR(2);
uint32_t res;
if (n & 0x20) res = 0;
else res = GPR(1) << (n & 0x1f);
GPR(0) = res;
core->regs.pc += 4;
}
void Interpreter::slw_d()
{
slw();
COMPUTE_CR0(GPR(0));
}
// n = rb[27-31]
// r = ROTL(rs, 32-n)
// if rb[26] = 0
// then m = MASK(n, 31)
// else m = (32)0
// S = rs(0)
// ra = r & m | (32)S & ~m
// XER[CA] = S & (r & ~m[0-31] != 0)
void Interpreter::sraw()
{
uint32_t n = GPR(2);
int32_t res;
int32_t src = GPR(1);
if (n == 0)
{
res = src;
RESET_XER_CA();
}
else if (n & 0x20)
{
if (src < 0)
{
res = 0xffffffff;
if (src & 0x7fffffff) SET_XER_CA(); else RESET_XER_CA();
}
else
{
res = 0;
RESET_XER_CA();
}
}
else
{
n = n & 0x1f;
res = (int32_t)src >> n;
if (src < 0 && (src << (32 - n)) != 0) SET_XER_CA(); else RESET_XER_CA();
}
GPR(0) = res;
core->regs.pc += 4;
}
void Interpreter::sraw_d()
{
sraw();
COMPUTE_CR0(GPR(0));
}
// n = SH
// r = ROTL(rs, 32 - n)
// m = MASK(n, 31)
// sign = rs[0]
// ra = r & m | (32)sign & ~m
// XER[CA] = sign(0) & ((r & ~m) != 0)
void Interpreter::srawi()
{
uint32_t n = (uint32_t)info.paramBits[2];
int32_t res;
int32_t src = GPR(1);
if (n == 0)
{
res = src;
RESET_XER_CA();
}
else
{
res = src >> n;
if (src < 0 && (src << (32 - n)) != 0) SET_XER_CA(); else RESET_XER_CA();
}
GPR(0) = res;
core->regs.pc += 4;
}
void Interpreter::srawi_d()
{
srawi();
COMPUTE_CR0(GPR(0));
}
// n = rb[27-31]
// r = ROTL(rs, 32-n)
// if rb[26] = 0
// then m = MASK(n, 31)
// else m = (32)0
// ra = r & m
// (simply : ra = rs >> rb, or ra = 0, if rb[26] = 1)
void Interpreter::srw()
{
uint32_t n = GPR(2);
uint32_t res;
if (n & 0x20) res = 0;
else res = GPR(1) >> (n & 0x1f);
GPR(0) = res;
core->regs.pc += 4;
}
void Interpreter::srw_d()
{
srw();
COMPUTE_CR0(GPR(0));
}
}
// System Instructions
namespace Gekko
{
void Interpreter::eieio()
{
core->regs.pc += 4;
}
// instruction synchronize.
void Interpreter::isync()
{
core->regs.pc += 4;
}
// ea = (ra | 0) + rb
// RESERVE = 1
// RESERVE_ADDR = physical(ea)
// rd = MEM(ea, 4)
void Interpreter::lwarx()
{
int WIMG;
uint32_t ea = core->regs.gpr[info.paramBits[2]];
if (info.paramBits[1]) ea += core->regs.gpr[info.paramBits[1]];
core->RESERVE = true;
core->RESERVE_ADDR = core->EffectiveToPhysical(ea, Gekko::MmuAccess::Read, WIMG);
core->ReadWord(ea, &core->regs.gpr[info.paramBits[0]]);
if (core->exception) return;
core->regs.pc += 4;
}
// ea = (ra | 0) + rb
// if RESERVE
// then
// MEM(ea, 4) = rs
// CR0 = 0b00 || 0b1 || XER[SO]
// RESERVE = 0
// else
// CR0 = 0b00 || 0b0 || XER[SO]
void Interpreter::stwcx_d()
{
uint32_t ea = core->regs.gpr[info.paramBits[2]];
if (info.paramBits[1]) ea += core->regs.gpr[info.paramBits[1]];
core->regs.cr &= 0x0fffffff;
if (core->RESERVE)
{
core->WriteWord(ea, core->regs.gpr[info.paramBits[0]]);
if (core->exception) return;
core->regs.cr |= GEKKO_CR0_EQ;
core->RESERVE = false;
}
if (IS_XER_SO) core->regs.cr |= GEKKO_CR0_SO;
core->regs.pc += 4;
}
void Interpreter::sync()
{
core->regs.pc += 4;
}
// return from exception
void Interpreter::rfi()
{
core->regs.msr &= ~(0x87C0FF73 | 0x00040000);
core->regs.msr |= core->regs.spr[SPR::SRR1] & 0x87C0FF73;
core->regs.pc = core->regs.spr[SPR::SRR0] & ~3;
}
// syscall
void Interpreter::sc()
{
// pseudo-branch (to resume from next instruction after 'rfi')
core->regs.pc += 4;
core->Exception(Gekko::Exception::EXCEPTION_SYSTEM_CALL);
}
void Interpreter::tw()
{
int32_t a = core->regs.gpr[info.paramBits[1]], b = core->regs.gpr[info.paramBits[2]];
int32_t to = info.paramBits[0];
if (((a < b) && (to & 0x10)) ||
((a > b) && (to & 0x08)) ||
((a == b) && (to & 0x04)) ||
(((uint32_t)a < (uint32_t)b) && (to & 0x02)) ||
(((uint32_t)a > (uint32_t)b) && (to & 0x01)))
{
// pseudo-branch (to resume from next instruction after 'rfi')
core->regs.pc += 4;
core->PrCause = PrivilegedCause::Trap;
core->Exception(Gekko::Exception::EXCEPTION_PROGRAM);
}
else
{
core->regs.pc += 4;
}
}
void Interpreter::twi()
{
int32_t a = core->regs.gpr[info.paramBits[1]], b = (int32_t)info.Imm.Signed;
int32_t to = info.paramBits[0];
if (((a < b) && (to & 0x10)) ||
((a > b) && (to & 0x08)) ||
((a == b) && (to & 0x04)) ||
(((uint32_t)a < (uint32_t)b) && (to & 0x02)) ||
(((uint32_t)a > (uint32_t)b) && (to & 0x01)))
{
// pseudo-branch (to resume from next instruction after 'rfi')
core->regs.pc += 4;
core->PrCause = PrivilegedCause::Trap;
core->Exception(Gekko::Exception::EXCEPTION_PROGRAM);
}
else
{
core->regs.pc += 4;
}
}
// CR[4 * crfD .. 4 * crfd + 3] = XER[0-3]
// XER[0..3] = 0b0000
void Interpreter::mcrxr()
{
uint32_t mask = 0xf0000000 >> (4 * info.paramBits[0]);
core->regs.cr &= ~mask;
core->regs.cr |= (core->regs.spr[SPR::XER] & 0xf0000000) >> (4 * info.paramBits[0]);
core->regs.spr[SPR::XER] &= ~0xf0000000;
core->regs.pc += 4;
}
// rd = cr
void Interpreter::mfcr()
{
core->regs.gpr[info.paramBits[0]] = core->regs.cr;
core->regs.pc += 4;
}
// rd = msr
void Interpreter::mfmsr()
{
if (core->regs.msr & MSR_PR)
{
core->PrCause = PrivilegedCause::Privileged;
core->Exception(Exception::EXCEPTION_PROGRAM);
return;
}
core->regs.gpr[info.paramBits[0]] = core->regs.msr;
core->regs.pc += 4;
}
// We do not support access rights to SPRs, since all applications on the emulated system are executed with OEA rights.
// A detailed study of all SPRs in all modes is in Docs\HW\SPR.txt. If necessary, it will be possible to wind the rights properly.
// rd = spr
void Interpreter::mfspr()
{
int spr = info.paramBits[1];
uint32_t value;
switch (spr)
{
case SPR::WPAR:
value = (core->regs.spr[spr] & ~0x1f) | (core->gatherBuffer->NotEmpty() ? 1 : 0);
break;
case SPR::HID1:
// Gekko PLL_CFG = 0b1000
value = 0x8000'0000;
break;
default:
value = core->regs.spr[spr];
break;
}
core->regs.gpr[info.paramBits[0]] = value;
core->regs.pc += 4;
}
// rd = tbr
void Interpreter::mftb()
{
int tbr = info.paramBits[1];
if (tbr == (int)TBR::TBL)
{
core->regs.gpr[info.paramBits[0]] = core->regs.tb.Part.l;
}
else if (tbr == (int)TBR::TBU)
{
core->regs.gpr[info.paramBits[0]] = core->regs.tb.Part.u;
}
core->regs.pc += 4;
}
// mask = (4)CRM[0] || (4)CRM[1] || ... || (4)CRM[7]
// CR = (rs & mask) | (CR & ~mask)
void Interpreter::mtcrf()
{
uint32_t crm = info.paramBits[0], d = core->regs.gpr[info.paramBits[1]];
for (int i = 0; i < 8; i++)
{
if ((crm >> i) & 1)
{
uint32_t a = (d >> (i << 2)) & 0xf;
uint32_t m = (0xf << (i << 2));
core->regs.cr = (core->regs.cr & ~m) | (a << (i << 2));
}
}
core->regs.pc += 4;
}
// msr = rs
void Interpreter::mtmsr()
{
if (core->regs.msr & MSR_PR)
{
core->PrCause = PrivilegedCause::Privileged;
core->Exception(Exception::EXCEPTION_PROGRAM);
return;
}
uint32_t oldMsr = core->regs.msr;
core->regs.msr = core->regs.gpr[info.paramBits[0]];
if ((oldMsr & MSR_IR) != (core->regs.msr & MSR_IR))
{
core->itlb.InvalidateAll();
}
if ((oldMsr & MSR_DR) != (core->regs.msr & MSR_DR))
{
core->dtlb.InvalidateAll();
}
core->regs.pc += 4;
}
// spr = rs
void Interpreter::mtspr()
{
int spr = info.paramBits[0];
// Diagnostic output when the BAT registers are changed.
if (spr >= SPR::IBAT0U && spr <= SPR::DBAT3L)
{
static const char* bat[] = {
"IBAT0U", "IBAT0L", "IBAT1U", "IBAT1L",
"IBAT2U", "IBAT2L", "IBAT3U", "IBAT3L",
"DBAT0U", "DBAT0L", "DBAT1U", "DBAT1L",
"DBAT2U", "DBAT2L", "DBAT3U", "DBAT3L"
};
bool msr_ir = (core->regs.msr & MSR_IR) ? true : false;
bool msr_dr = (core->regs.msr & MSR_DR) ? true : false;
Report(Channel::CPU, "%s <- %08X (IR:%i DR:%i pc:%08X)\n",
bat[spr - SPR::IBAT0U], core->regs.gpr[info.paramBits[1]], msr_ir, msr_dr, core->regs.pc);
}
switch (spr)
{
// decrementer
case SPR::DEC:
//DBReport2(DbgChannel::CPU, "set decrementer (OS alarm) to %08X\n", RRS);
break;
// page table base
case SPR::SDR1:
{
bool msr_ir = (core->regs.msr & MSR_IR) ? true : false;
bool msr_dr = (core->regs.msr & MSR_DR) ? true : false;
Report(Channel::CPU, "SDR1 <- %08X (IR:%i DR:%i pc:%08X)\n",
core->regs.gpr[info.paramBits[1]], msr_ir, msr_dr, core->regs.pc);
core->dtlb.InvalidateAll();
core->itlb.InvalidateAll();
}
break;
case SPR::TBL:
core->regs.tb.Part.l = core->regs.gpr[info.paramBits[1]];
Report(Channel::CPU, "Set TBL: 0x%08X\n", core->regs.tb.Part.l);
break;
case SPR::TBU:
core->regs.tb.Part.u = core->regs.gpr[info.paramBits[1]];
Report(Channel::CPU, "Set TBU: 0x%08X\n", core->regs.tb.Part.u);
break;
// write gathering buffer
case SPR::WPAR:
// A mtspr to WPAR invalidates the data.
core->gatherBuffer->Reset();
break;
case SPR::HID0:
{
uint32_t bits = core->regs.gpr[info.paramBits[1]];
core->cache->Enable((bits & HID0_DCE) ? true : false);
core->icache->Enable((bits & HID0_ICE) ? true : false);
core->cache->Freeze((bits & HID0_DLOCK) ? true : false);
if (bits & HID0_DCFI)
{
bits &= ~HID0_DCFI;
Report(Channel::CPU, "Data Cache Flash Invalidate\n");
core->cache->FlashInvalidate();
}
if (bits & HID0_ICFI)
{
bits &= ~HID0_ICFI;
Report(Channel::CPU, "Instruction Cache Flash Invalidate\n");
core->icache->FlashInvalidate();
}
core->regs.spr[spr] = bits;
core->regs.pc += 4;
return;
}
break;
case SPR::HID1:
// Read only
core->regs.pc += 4;
return;
case SPR::HID2:
{
uint32_t bits = core->regs.gpr[info.paramBits[1]];
core->cache->LockedEnable((bits & HID2_LCE) ? true : false);
}
break;
// Locked cache DMA
case SPR::DMAU:
if (core->trace_locked_dma_regs) {
Report(Channel::CPU, "DMAU: 0x%08X\n", core->regs.gpr[info.paramBits[1]]);
}
break;
case SPR::DMAL:
{
core->regs.spr[spr] = core->regs.gpr[info.paramBits[1]];
if (core->trace_locked_dma_regs) {
Report(Channel::CPU, "DMAL: 0x%08X\n", core->regs.spr[spr]);
}
if (core->regs.spr[SPR::DMAL] & GEKKO_DMAL_DMA_T)
{
uint32_t maddr = core->regs.spr[SPR::DMAU] & GEKKO_DMAU_MEM_ADDR;
uint32_t lcaddr = core->regs.spr[SPR::DMAL] & GEKKO_DMAL_LC_ADDR;
size_t length = ((core->regs.spr[SPR::DMAU] & GEKKO_DMAU_DMA_LEN_U) << GEKKO_DMA_LEN_SHIFT) |
((core->regs.spr[SPR::DMAL] >> GEKKO_DMA_LEN_SHIFT) & GEKKO_DMAL_DMA_LEN_L);
if (length == 0) length = 128;
if (core->cache->IsLockedEnable())
{
core->cache->LockedCacheDma(
(core->regs.spr[SPR::DMAL] & GEKKO_DMAL_DMA_LD) ? true : false,
maddr,
lcaddr,
length);
}
}
// It makes no sense to implement such a small Queue. We make all transactions instant.
core->regs.spr[spr] &= ~(GEKKO_DMAL_DMA_T | GEKKO_DMAL_DMA_F);
core->regs.pc += 4;
return;
}
break;
case SPR::GQR0:
case SPR::GQR1:
case SPR::GQR2:
case SPR::GQR3:
case SPR::GQR4:
case SPR::GQR5:
case SPR::GQR6:
case SPR::GQR7:
// In the sense of Dolphin OS, registers GQR1-7 are constantly reloaded when switching threads via `OSLoadContext`.
// GQR0 is always 0 and is not reloaded when switching threads.
break;
case SPR::IBAT0U:
case SPR::IBAT0L:
case SPR::IBAT1U:
case SPR::IBAT1L:
case SPR::IBAT2U:
case SPR::IBAT2L:
case SPR::IBAT3U:
case SPR::IBAT3L:
core->itlb.InvalidateAll();
break;
case SPR::DBAT0U:
case SPR::DBAT0L:
case SPR::DBAT1U:
case SPR::DBAT1L:
case SPR::DBAT2U:
case SPR::DBAT2L:
case SPR::DBAT3U:
case SPR::DBAT3L:
core->dtlb.InvalidateAll();
break;
case SPR::L2CR:
{
uint32_t bits = core->regs.gpr[info.paramBits[1]];
//if (bits & GEKKO_L2CR_L2E) {
// if (bits & GEKKO_L2CR_L2I) {
// core->cache->FlashInvalidate();
// Report(Channel::CPU, "L2 Cache Global Invalidate\n");
// }
//}
}
break;
}
// default
core->regs.spr[spr] = core->regs.gpr[info.paramBits[1]];
core->regs.pc += 4;
}
void Interpreter::dcbf()
{
int WIMG;
uint32_t ea = info.paramBits[0] ? core->regs.gpr[info.paramBits[0]] + core->regs.gpr[info.paramBits[1]] : core->regs.gpr[info.paramBits[1]];
uint32_t pa = core->EffectiveToPhysical(ea, MmuAccess::Read, WIMG);
if (pa != Gekko::BadAddress)
{
core->cache->Flush(pa);
}
else
{
core->regs.spr[Gekko::SPR::DAR] = ea;
core->Exception(Exception::EXCEPTION_DSI);
return;
}
core->regs.pc += 4;
}
void Interpreter::dcbi()
{
int WIMG;
uint32_t ea = info.paramBits[0] ? core->regs.gpr[info.paramBits[0]] + core->regs.gpr[info.paramBits[1]] : core->regs.gpr[info.paramBits[1]];
if (core->regs.msr & MSR_PR)
{
core->PrCause = PrivilegedCause::Privileged;
core->Exception(Exception::EXCEPTION_PROGRAM);
return;
}
uint32_t pa = core->EffectiveToPhysical(ea, MmuAccess::Write, WIMG);
if (pa != Gekko::BadAddress)
{
core->cache->Invalidate(pa);
}
else
{
// TODO
// In Gekko manual it is written that when translation is enabled it is necessary to generate DSI if the address could not be translated.
// But games do dcbi 0xE0000000 BEFORE enabling LCEnable, which obviously shouldn't work, according to the manual.
// It's not clear what the fuck is going on here, but I'm going to disable DSI generation with this instruction for now until further investigation.
//core->regs.spr[Gekko::SPR::DAR] = ea;
//core->Exception(Exception::EXCEPTION_DSI);
//return;
}
core->regs.pc += 4;
}
void Interpreter::dcbst()
{
int WIMG;
uint32_t ea = info.paramBits[0] ? core->regs.gpr[info.paramBits[0]] + core->regs.gpr[info.paramBits[1]] : core->regs.gpr[info.paramBits[1]];
uint32_t pa = core->EffectiveToPhysical(ea, MmuAccess::Read, WIMG);
if (pa != Gekko::BadAddress)
{
core->cache->Store(pa);
}
else
{
core->regs.spr[Gekko::SPR::DAR] = ea;
core->Exception(Exception::EXCEPTION_DSI);
return;
}
core->regs.pc += 4;
}
void Interpreter::dcbt()
{
int WIMG;
if (core->regs.spr[Gekko::SPR::HID0] & HID0_NOOPTI || core->cache->IsLockedEnable())
{
core->regs.pc += 4;
return;
}
uint32_t ea = info.paramBits[0] ? core->regs.gpr[info.paramBits[0]] + core->regs.gpr[info.paramBits[1]] : core->regs.gpr[info.paramBits[1]];
uint32_t pa = core->EffectiveToPhysical(ea, MmuAccess::Read, WIMG);
if (pa != Gekko::BadAddress)
{
core->cache->Touch(pa);
}
core->regs.pc += 4;
}
void Interpreter::dcbtst()
{
int WIMG;
if (core->regs.spr[Gekko::SPR::HID0] & HID0_NOOPTI || core->cache->IsLockedEnable())
{
core->regs.pc += 4;
return;
}
uint32_t ea = info.paramBits[0] ? core->regs.gpr[info.paramBits[0]] + core->regs.gpr[info.paramBits[1]] : core->regs.gpr[info.paramBits[1]];
// TouchForStore is also made architecturally as a Read operation so that the MMU does not set the "Changed" bit for PTE.
uint32_t pa = core->EffectiveToPhysical(ea, MmuAccess::Read, WIMG);
if (pa != Gekko::BadAddress)
{
core->cache->TouchForStore(pa);
}
core->regs.pc += 4;
}
void Interpreter::dcbz()
{
int WIMG;
uint32_t ea = info.paramBits[0] ? core->regs.gpr[info.paramBits[0]] + core->regs.gpr[info.paramBits[1]] : core->regs.gpr[info.paramBits[1]];
uint32_t pa = core->EffectiveToPhysical(ea, MmuAccess::Write, WIMG);
if (pa != Gekko::BadAddress)
{
core->cache->Zero(pa);
}
else
{
core->regs.spr[Gekko::SPR::DAR] = ea;
core->Exception(Exception::EXCEPTION_DSI);
return;
}
core->regs.pc += 4;
}
// DCBZ_L is used for the alien Locked Cache address mapping mechanism.
// For example, calling dcbz_l 0xE0000000 will make this address be associated with Locked Cache for subsequent Load/Store operations.
// Locked Cache is saved in RAM by another alien mechanism (DMA).
void Interpreter::dcbz_l()
{
int WIMG;
if (!core->cache->IsLockedEnable())
{
core->PrCause = PrivilegedCause::IllegalInstruction;
core->Exception(Exception::EXCEPTION_PROGRAM);
return;
}
uint32_t ea = info.paramBits[0] ? core->regs.gpr[info.paramBits[0]] + core->regs.gpr[info.paramBits[1]] : core->regs.gpr[info.paramBits[1]];
uint32_t pa = core->EffectiveToPhysical(ea, MmuAccess::Write, WIMG);
if (pa != Gekko::BadAddress)
{
Report(Channel::CPU, "Locked cache is assigned to physical address: 0x%08X\n", pa);
core->cache->ZeroLocked(pa);
}
else
{
core->regs.spr[Gekko::SPR::DAR] = ea;
core->Exception(Exception::EXCEPTION_DSI);
return;
}
core->regs.pc += 4;
}
void Interpreter::icbi()
{
int WIMG;
uint32_t address = info.paramBits[0] ? core->regs.gpr[info.paramBits[0]] + core->regs.gpr[info.paramBits[1]] : core->regs.gpr[info.paramBits[1]];
address &= ~0x1f;
if (core->regs.msr & MSR_PR)
{
core->PrCause = PrivilegedCause::Privileged;
core->Exception(Exception::EXCEPTION_PROGRAM);
return;
}
uint32_t pa = core->EffectiveToPhysical(address, MmuAccess::Read, WIMG);
if (pa != Gekko::BadAddress)
{
core->icache->Invalidate(pa);
}
else
{
core->Exception(Exception::EXCEPTION_ISI);
return;
}
core->regs.pc += 4;
}
// rd = sr[a]
void Interpreter::mfsr()
{
if (core->regs.msr & MSR_PR)
{
core->PrCause = PrivilegedCause::Privileged;
core->Exception(Exception::EXCEPTION_PROGRAM);
return;
}
core->regs.gpr[info.paramBits[0]] = core->regs.sr[info.paramBits[1]];
core->regs.pc += 4;
}
// rd = sr[rb]
void Interpreter::mfsrin()
{
if (core->regs.msr & MSR_PR)
{
core->PrCause = PrivilegedCause::Privileged;
core->Exception(Exception::EXCEPTION_PROGRAM);
return;
}
core->regs.gpr[info.paramBits[0]] = core->regs.sr[core->regs.gpr[info.paramBits[1]] >> 28];
core->regs.pc += 4;
}
// sr[a] = rs
void Interpreter::mtsr()
{
if (core->regs.msr & MSR_PR)
{
core->PrCause = PrivilegedCause::Privileged;
core->Exception(Exception::EXCEPTION_PROGRAM);
return;
}
Report(Channel::CPU, "mtsr: sr[%d] = 0x%08X, pc: 0x%08X\n", info.paramBits[0], core->regs.gpr[info.paramBits[1]], core->regs.pc);
core->regs.sr[info.paramBits[0]] = core->regs.gpr[info.paramBits[1]];
core->regs.pc += 4;
}
// sr[rb] = rs
void Interpreter::mtsrin()
{
if (core->regs.msr & MSR_PR)
{
core->PrCause = PrivilegedCause::Privileged;
core->Exception(Exception::EXCEPTION_PROGRAM);
return;
}
Report(Channel::CPU, "mtsrin: sr[%d] = 0x%08X, pc: 0x%08X\n", core->regs.gpr[info.paramBits[1]] >> 28, core->regs.gpr[info.paramBits[0]], core->regs.pc);
core->regs.sr[core->regs.gpr[info.paramBits[1]] >> 28] = core->regs.gpr[info.paramBits[0]];
core->regs.pc += 4;
}
void Interpreter::tlbie()
{
core->dtlb.Invalidate(core->regs.gpr[info.paramBits[0]]);
core->itlb.Invalidate(core->regs.gpr[info.paramBits[0]]);
core->regs.pc += 4;
}
void Interpreter::tlbsync()
{
core->regs.pc += 4;
}
void Interpreter::eciwx()
{
Halt("eciwx\n");
}
void Interpreter::ecowx()
{
Halt("ecowx\n");
}
}
// Gekko interpreter
namespace Gekko
{
Interpreter::Interpreter(GekkoCore* _core)
{
core = _core;
// build rotate mask table
for (int mb = 0; mb < 32; mb++)
{
for (int me = 0; me < 32; me++)
{
uint32_t mask = ((uint32_t)-1 >> mb) ^ ((me >= 31) ? 0 : ((uint32_t)-1) >> (me + 1));
rotmask[mb][me] = (mb > me) ? (~mask) : (mask);
}
}
// build paired-single load scale
for (uint8_t scale = 0; scale < 64; scale++)
{
int factor;
if (scale & 0x20) // -32 ... -1
{
factor = -32 + (scale & 0x1f);
}
else // 0 ... 31
{
factor = 0 + (scale & 0x1f);
}
ldScale[scale] = powf(2, -1.0f * (float)factor);
}
// build paired-single store scale
for (uint8_t scale = 0; scale < 64; scale++)
{
int factor;
if (scale & 0x20) // -32 ... -1
{
factor = -32 + (scale & 0x1f);
}
else // 0 ... 31
{
factor = 0 + (scale & 0x1f);
}
stScale[scale] = powf(2, +1.0f * (float)factor);
}
}
/// <summary>
/// Result = a + b + CarryIn. Return carry flag in CarryBit. Return overflow flag in OverflowBit.
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
uint32_t Interpreter::FullAdder(uint32_t a, uint32_t b)
{
uint64_t res = (uint64_t)a + (uint64_t)b + (uint64_t)(CarryBit != 0 ? 1 : 0);
//A human need only remember that, when doing signed math, adding
//two numbers of the same sign must produce a result of the same sign,
//otherwise overflow happened.
bool msb = (res & 0x8000'0000) != 0 ? true : false;
bool aMsb = (a & 0x8000'0000) != 0 ? true : false;
bool bMsb = (b & 0x8000'0000) != 0 ? true : false;
OverflowBit = 0;
if (aMsb == bMsb)
{
OverflowBit = (aMsb != msb) ? 1 : 0;
}
CarryBit = ((res & 0xffffffff'00000000) != 0) ? 1 : 0;
return (uint32_t)res;
}
/// <summary>
/// Rotate 32 bit left
/// </summary>
/// <param name="sa">Rotate bits amount</param>
/// <param name="data">Source</param>
/// <returns>Result</returns>
uint32_t Interpreter::Rotl32(size_t sa, uint32_t data)
{
return (data << sa) | (data >> ((32 - sa) & 31));
}
// parse and execute single opcode
void Interpreter::ExecuteOpcode()
{
uint32_t instr = 0;
// Hack for the first time.
// Since we are using an "infinite" cache, the old data from the memory check (0x55555555) remains in it after BS2 is loaded;
// So when entering BS2, you just need to invalidate the DCache.
// Need to figure out why this is happening / come up with a better place to do it
if (core->regs.pc == 0x81300000) {
Core->cache->FlashInvalidate();
}
// Fetch instruction
core->Fetch(core->regs.pc, &instr);
// ISI
if (core->exception)
{
core->exception = false;
return;
}
// Decode instruction and dispatch
Decoder::DecodeFast(core->regs.pc, instr, &info);
Dispatch();
core->ops++;
if (core->resetInstructionCounter)
{
core->resetInstructionCounter = false;
core->ops = 0;
}
// DSI, ALIGN, PROGRAM, FPUNA, SC
if (core->exception)
{
core->exception = false;
return;
}
core->Tick();
core->exception = false;
}
void Interpreter::Dispatch()
{
switch (info.instr)
{
case Instruction::b: b(); break;
case Instruction::ba: ba(); break;
case Instruction::bl: bl(); break;
case Instruction::bla: bla(); break;
case Instruction::bc: bc(); break;
case Instruction::bca: bca(); break;
case Instruction::bcl: bcl(); break;
case Instruction::bcla: bcla(); break;
case Instruction::bcctr: bcctr(); break;
case Instruction::bcctrl: bcctrl(); break;
case Instruction::bclr: bclr(); break;
case Instruction::bclrl: bclrl(); break;
case Instruction::cmpi: cmpi(); break;
case Instruction::cmp: cmp(); break;
case Instruction::cmpli: cmpli(); break;
case Instruction::cmpl: cmpl(); break;
case Instruction::crand: crand(); break;
case Instruction::crandc: crandc(); break;
case Instruction::creqv: creqv(); break;
case Instruction::crnand: crnand(); break;
case Instruction::crnor: crnor(); break;
case Instruction::cror: cror(); break;
case Instruction::crorc: crorc(); break;
case Instruction::crxor: crxor(); break;
case Instruction::mcrf: mcrf(); break;
case Instruction::fadd: fadd(); break;
case Instruction::fadd_d: fadd_d(); break;
case Instruction::fadds: fadds(); break;
case Instruction::fadds_d: fadds_d(); break;
case Instruction::fdiv: fdiv(); break;
case Instruction::fdiv_d: fdiv_d(); break;
case Instruction::fdivs: fdivs(); break;
case Instruction::fdivs_d: fdivs_d(); break;
case Instruction::fmul: fmul(); break;
case Instruction::fmul_d: fmul_d(); break;
case Instruction::fmuls: fmuls(); break;
case Instruction::fmuls_d: fmuls_d(); break;
case Instruction::fres: fres(); break;
case Instruction::fres_d: fres_d(); break;
case Instruction::frsqrte: frsqrte(); break;
case Instruction::frsqrte_d: frsqrte_d(); break;
case Instruction::fsub: fsub(); break;
case Instruction::fsub_d: fsub_d(); break;
case Instruction::fsubs: fsubs(); break;
case Instruction::fsubs_d: fsubs_d(); break;
case Instruction::fsel: fsel(); break;
case Instruction::fsel_d: fsel_d(); break;
case Instruction::fmadd: fmadd(); break;
case Instruction::fmadd_d: fmadd_d(); break;
case Instruction::fmadds: fmadds(); break;
case Instruction::fmadds_d: fmadds_d(); break;
case Instruction::fmsub: fmsub(); break;
case Instruction::fmsub_d: fmsub_d(); break;
case Instruction::fmsubs: fmsubs(); break;
case Instruction::fmsubs_d: fmsubs_d(); break;
case Instruction::fnmadd: fnmadd(); break;
case Instruction::fnmadd_d: fnmadd_d(); break;
case Instruction::fnmadds: fnmadds(); break;
case Instruction::fnmadds_d: fnmadds_d(); break;
case Instruction::fnmsub: fnmsub(); break;
case Instruction::fnmsub_d: fnmsub_d(); break;
case Instruction::fnmsubs: fnmsubs(); break;
case Instruction::fnmsubs_d: fnmsubs_d(); break;
case Instruction::fctiw: fctiw(); break;
case Instruction::fctiw_d: fctiw_d(); break;
case Instruction::fctiwz: fctiwz(); break;
case Instruction::fctiwz_d: fctiwz_d(); break;
case Instruction::frsp: frsp(); break;
case Instruction::frsp_d: frsp_d(); break;
case Instruction::fcmpo: fcmpo(); break;
case Instruction::fcmpu: fcmpu(); break;
case Instruction::fabs: fabs(); break;
case Instruction::fabs_d: fabs_d(); break;
case Instruction::fmr: fmr(); break;
case Instruction::fmr_d: fmr_d(); break;
case Instruction::fnabs: fnabs(); break;
case Instruction::fnabs_d: fnabs_d(); break;
case Instruction::fneg: fneg(); break;
case Instruction::fneg_d: fneg_d(); break;
case Instruction::mcrfs: mcrfs(); break;
case Instruction::mffs: mffs(); break;
case Instruction::mffs_d: mffs_d(); break;
case Instruction::mtfsb0: mtfsb0(); break;
case Instruction::mtfsb0_d: mtfsb0_d(); break;
case Instruction::mtfsb1: mtfsb1(); break;
case Instruction::mtfsb1_d: mtfsb1_d(); break;
case Instruction::mtfsf: mtfsf(); break;
case Instruction::mtfsf_d: mtfsf_d(); break;
case Instruction::mtfsfi: mtfsfi(); break;
case Instruction::mtfsfi_d: mtfsfi_d(); break;
case Instruction::lfd: lfd(); break;
case Instruction::lfdu: lfdu(); break;
case Instruction::lfdux: lfdux(); break;
case Instruction::lfdx: lfdx(); break;
case Instruction::lfs: lfs(); break;
case Instruction::lfsu: lfsu(); break;
case Instruction::lfsux: lfsux(); break;
case Instruction::lfsx: lfsx(); break;
case Instruction::stfd: stfd(); break;
case Instruction::stfdu: stfdu(); break;
case Instruction::stfdux: stfdux(); break;
case Instruction::stfdx: stfdx(); break;
case Instruction::stfiwx: stfiwx(); break;
case Instruction::stfs: stfs(); break;
case Instruction::stfsu: stfsu(); break;
case Instruction::stfsux: stfsux(); break;
case Instruction::stfsx: stfsx(); break;
case Instruction::add: add(); break;
case Instruction::add_d: add_d(); break;
case Instruction::addo: addo(); break;
case Instruction::addo_d: addo_d(); break;
case Instruction::addc: addc(); break;
case Instruction::addc_d: addc_d(); break;
case Instruction::addco: addco(); break;
case Instruction::addco_d: addco_d(); break;
case Instruction::adde: adde(); break;
case Instruction::adde_d: adde_d(); break;
case Instruction::addeo: addeo(); break;
case Instruction::addeo_d: addeo_d(); break;
case Instruction::addi: addi(); break;
case Instruction::addic: addic(); break;
case Instruction::addic_d: addic_d(); break;
case Instruction::addis: addis(); break;
case Instruction::addme: addme(); break;
case Instruction::addme_d: addme_d(); break;
case Instruction::addmeo: addmeo(); break;
case Instruction::addmeo_d: addmeo_d(); break;
case Instruction::addze: addze(); break;
case Instruction::addze_d: addze_d(); break;
case Instruction::addzeo: addzeo(); break;
case Instruction::addzeo_d: addzeo_d(); break;
case Instruction::divw: divw(); break;
case Instruction::divw_d: divw_d(); break;
case Instruction::divwo: divwo(); break;
case Instruction::divwo_d: divwo_d(); break;
case Instruction::divwu: divwu(); break;
case Instruction::divwu_d: divwu_d(); break;
case Instruction::divwuo: divwuo(); break;
case Instruction::divwuo_d: divwuo_d(); break;
case Instruction::mulhw: mulhw(); break;
case Instruction::mulhw_d: mulhw_d(); break;
case Instruction::mulhwu: mulhwu(); break;
case Instruction::mulhwu_d: mulhwu_d(); break;
case Instruction::mulli: mulli(); break;
case Instruction::mullw: mullw(); break;
case Instruction::mullw_d: mullw_d(); break;
case Instruction::mullwo: mullwo(); break;
case Instruction::mullwo_d: mullwo_d(); break;
case Instruction::neg: neg(); break;
case Instruction::neg_d: neg_d(); break;
case Instruction::nego: nego(); break;
case Instruction::nego_d: nego_d(); break;
case Instruction::subf: subf(); break;
case Instruction::subf_d: subf_d(); break;
case Instruction::subfo: subfo(); break;
case Instruction::subfo_d: subfo_d(); break;
case Instruction::subfc: subfc(); break;
case Instruction::subfc_d: subfc_d(); break;
case Instruction::subfco: subfco(); break;
case Instruction::subfco_d: subfco_d(); break;
case Instruction::subfe: subfe(); break;
case Instruction::subfe_d: subfe_d(); break;
case Instruction::subfeo: subfeo(); break;
case Instruction::subfeo_d: subfeo_d(); break;
case Instruction::subfic: subfic(); break;
case Instruction::subfme: subfme(); break;
case Instruction::subfme_d: subfme_d(); break;
case Instruction::subfmeo: subfmeo(); break;
case Instruction::subfmeo_d: subfmeo_d(); break;
case Instruction::subfze: subfze(); break;
case Instruction::subfze_d: subfze_d(); break;
case Instruction::subfzeo: subfzeo(); break;
case Instruction::subfzeo_d: subfzeo_d(); break;
case Instruction::lbz: lbz(); break;
case Instruction::lbzu: lbzu(); break;
case Instruction::lbzux: lbzux(); break;
case Instruction::lbzx: lbzx(); break;
case Instruction::lha: lha(); break;
case Instruction::lhau: lhau(); break;
case Instruction::lhaux: lhaux(); break;
case Instruction::lhax: lhax(); break;
case Instruction::lhz: lhz(); break;
case Instruction::lhzu: lhzu(); break;
case Instruction::lhzux: lhzux(); break;
case Instruction::lhzx: lhzx(); break;
case Instruction::lwz: lwz(); break;
case Instruction::lwzu: lwzu(); break;
case Instruction::lwzux: lwzux(); break;
case Instruction::lwzx: lwzx(); break;
case Instruction::stb: stb(); break;
case Instruction::stbu: stbu(); break;
case Instruction::stbux: stbux(); break;
case Instruction::stbx: stbx(); break;
case Instruction::sth: sth(); break;
case Instruction::sthu: sthu(); break;
case Instruction::sthux: sthux(); break;
case Instruction::sthx: sthx(); break;
case Instruction::stw: stw(); break;
case Instruction::stwu: stwu(); break;
case Instruction::stwux: stwux(); break;
case Instruction::stwx: stwx(); break;
case Instruction::lhbrx: lhbrx(); break;
case Instruction::lwbrx: lwbrx(); break;
case Instruction::sthbrx: sthbrx(); break;
case Instruction::stwbrx: stwbrx(); break;
case Instruction::lmw: lmw(); break;
case Instruction::stmw: stmw(); break;
case Instruction::lswi: lswi(); break;
case Instruction::lswx: lswx(); break;
case Instruction::stswi: stswi(); break;
case Instruction::stswx: stswx(); break;
case Instruction::_and: _and(); break;
case Instruction::and_d: and_d(); break;
case Instruction::andc: andc(); break;
case Instruction::andc_d: andc_d(); break;
case Instruction::andi_d: andi_d(); break;
case Instruction::andis_d: andis_d(); break;
case Instruction::cntlzw: cntlzw(); break;
case Instruction::cntlzw_d: cntlzw_d(); break;
case Instruction::eqv: eqv(); break;
case Instruction::eqv_d: eqv_d(); break;
case Instruction::extsb: extsb(); break;
case Instruction::extsb_d: extsb_d(); break;
case Instruction::extsh: extsh(); break;
case Instruction::extsh_d: extsh_d(); break;
case Instruction::nand: nand(); break;
case Instruction::nand_d: nand_d(); break;
case Instruction::nor: nor(); break;
case Instruction::nor_d: nor_d(); break;
case Instruction::_or: _or(); break;
case Instruction::or_d: or_d(); break;
case Instruction::orc: orc(); break;
case Instruction::orc_d: orc_d(); break;
case Instruction::ori: ori(); break;
case Instruction::oris: oris(); break;
case Instruction::_xor: _xor(); break;
case Instruction::xor_d: xor_d(); break;
case Instruction::xori: xori(); break;
case Instruction::xoris: xoris(); break;
case Instruction::ps_div: ps_div(); break;
case Instruction::ps_div_d: ps_div_d(); break;
case Instruction::ps_sub: ps_sub(); break;
case Instruction::ps_sub_d: ps_sub_d(); break;
case Instruction::ps_add: ps_add(); break;
case Instruction::ps_add_d: ps_add_d(); break;
case Instruction::ps_sel: ps_sel(); break;
case Instruction::ps_sel_d: ps_sel_d(); break;
case Instruction::ps_res: ps_res(); break;
case Instruction::ps_res_d: ps_res_d(); break;
case Instruction::ps_mul: ps_mul(); break;
case Instruction::ps_mul_d: ps_mul_d(); break;
case Instruction::ps_rsqrte: ps_rsqrte(); break;
case Instruction::ps_rsqrte_d: ps_rsqrte_d(); break;
case Instruction::ps_msub: ps_msub(); break;
case Instruction::ps_msub_d: ps_msub_d(); break;
case Instruction::ps_madd: ps_madd(); break;
case Instruction::ps_madd_d: ps_madd_d(); break;
case Instruction::ps_nmsub: ps_nmsub(); break;
case Instruction::ps_nmsub_d: ps_nmsub_d(); break;
case Instruction::ps_nmadd: ps_nmadd(); break;
case Instruction::ps_nmadd_d: ps_nmadd_d(); break;
case Instruction::ps_neg: ps_neg(); break;
case Instruction::ps_neg_d: ps_neg_d(); break;
case Instruction::ps_mr: ps_mr(); break;
case Instruction::ps_mr_d: ps_mr_d(); break;
case Instruction::ps_nabs: ps_nabs(); break;
case Instruction::ps_nabs_d: ps_nabs_d(); break;
case Instruction::ps_abs: ps_abs(); break;
case Instruction::ps_abs_d: ps_abs_d(); break;
case Instruction::ps_sum0: ps_sum0(); break;
case Instruction::ps_sum0_d: ps_sum0_d(); break;
case Instruction::ps_sum1: ps_sum1(); break;
case Instruction::ps_sum1_d: ps_sum1_d(); break;
case Instruction::ps_muls0: ps_muls0(); break;
case Instruction::ps_muls0_d: ps_muls0_d(); break;
case Instruction::ps_muls1: ps_muls1(); break;
case Instruction::ps_muls1_d: ps_muls1_d(); break;
case Instruction::ps_madds0: ps_madds0(); break;
case Instruction::ps_madds0_d: ps_madds0_d(); break;
case Instruction::ps_madds1: ps_madds1(); break;
case Instruction::ps_madds1_d: ps_madds1_d(); break;
case Instruction::ps_cmpu0: ps_cmpu0(); break;
case Instruction::ps_cmpo0: ps_cmpo0(); break;
case Instruction::ps_cmpu1: ps_cmpu1(); break;
case Instruction::ps_cmpo1: ps_cmpo1(); break;
case Instruction::ps_merge00: ps_merge00(); break;
case Instruction::ps_merge00_d: ps_merge00_d(); break;
case Instruction::ps_merge01: ps_merge01(); break;
case Instruction::ps_merge01_d: ps_merge01_d(); break;
case Instruction::ps_merge10: ps_merge10(); break;
case Instruction::ps_merge10_d: ps_merge10_d(); break;
case Instruction::ps_merge11: ps_merge11(); break;
case Instruction::ps_merge11_d: ps_merge11_d(); break;
case Instruction::psq_lx: psq_lx(); break;
case Instruction::psq_stx: psq_stx(); break;
case Instruction::psq_lux: psq_lux(); break;
case Instruction::psq_stux: psq_stux(); break;
case Instruction::psq_l: psq_l(); break;
case Instruction::psq_lu: psq_lu(); break;
case Instruction::psq_st: psq_st(); break;
case Instruction::psq_stu: psq_stu(); break;
case Instruction::rlwimi: rlwimi(); break;
case Instruction::rlwimi_d: rlwimi_d(); break;
case Instruction::rlwinm: rlwinm(); break;
case Instruction::rlwinm_d: rlwinm_d(); break;
case Instruction::rlwnm: rlwnm(); break;
case Instruction::rlwnm_d: rlwnm_d(); break;
case Instruction::slw: slw(); break;
case Instruction::slw_d: slw_d(); break;
case Instruction::sraw: sraw(); break;
case Instruction::sraw_d: sraw_d(); break;
case Instruction::srawi: srawi(); break;
case Instruction::srawi_d: srawi_d(); break;
case Instruction::srw: srw(); break;
case Instruction::srw_d: srw_d(); break;
case Instruction::eieio: eieio(); break;
case Instruction::isync: isync(); break;
case Instruction::lwarx: lwarx(); break;
case Instruction::stwcx_d: stwcx_d(); break;
case Instruction::sync: sync(); break;
case Instruction::rfi: rfi(); break;
case Instruction::sc: sc(); break;
case Instruction::tw: tw(); break;
case Instruction::twi: twi(); break;
case Instruction::mcrxr: mcrxr(); break;
case Instruction::mfcr: mfcr(); break;
case Instruction::mfmsr: mfmsr(); break;
case Instruction::mfspr: mfspr(); break;
case Instruction::mftb: mftb(); break;
case Instruction::mtcrf: mtcrf(); break;
case Instruction::mtmsr: mtmsr(); break;
case Instruction::mtspr: mtspr(); break;
case Instruction::dcbf: dcbf(); break;
case Instruction::dcbi: dcbi(); break;
case Instruction::dcbst: dcbst(); break;
case Instruction::dcbt: dcbt(); break;
case Instruction::dcbtst: dcbtst(); break;
case Instruction::dcbz: dcbz(); break;
case Instruction::dcbz_l: dcbz_l(); break;
case Instruction::icbi: icbi(); break;
case Instruction::mfsr: mfsr(); break;
case Instruction::mfsrin: mfsrin(); break;
case Instruction::mtsr: mtsr(); break;
case Instruction::mtsrin: mtsrin(); break;
case Instruction::tlbie: tlbie(); break;
case Instruction::tlbsync: tlbsync(); break;
case Instruction::eciwx: eciwx(); break;
case Instruction::ecowx: ecowx(); break;
// TODO: CallVM opcode.
default:
Halt("** CPU ERROR **\n"
"unimplemented opcode : %08X\n", core->regs.pc);
core->PrCause = PrivilegedCause::IllegalInstruction;
core->Exception(Exception::EXCEPTION_PROGRAM);
return;
}
if (core->opcodeStatsEnabled)
{
core->opcodeStats[(size_t)info.instr]++;
}
}
uint32_t Interpreter::GetRotMask(int mb, int me)
{
return rotmask[mb][me];
}
// high level call
void Interpreter::callvm()
{
// module base should be specified as 0x400000 in project properties
//void (*pcall)() = (void (*)())((void*)(uint64_t)op);
//if (op == 0)
//{
// Halt(
// "Something goes wrong in interpreter, \n"
// "program is trying to execute NULL opcode.\n\n"
// "pc:%08X", core->regs.pc);
// return;
//}
//pcall();
Halt("callvm: Temporary not implemented!\n");
}
}