mirror of
https://github.com/google0101-ryan/Emotional.git
synced 2024-06-22 14:12:36 -04:00
More opcodes
This commit is contained in:
parent
4aa328c107
commit
079564dce8
|
@ -128,9 +128,9 @@ void System::Run()
|
|||
{
|
||||
while (1)
|
||||
{
|
||||
// size_t cycles = Scheduler::GetNextTimestamp();
|
||||
size_t cycles = Scheduler::GetNextTimestamp();
|
||||
|
||||
int true_cycles = EmotionEngine::Clock(32);
|
||||
int true_cycles = EmotionEngine::Clock(cycles);
|
||||
IOP_MANAGEMENT::Clock(true_cycles / 2);
|
||||
|
||||
Scheduler::CheckScheduler(true_cycles);
|
||||
|
|
|
@ -33,7 +33,154 @@ void EmitPrologue()
|
|||
curBlock->instructions.push_back(instr);
|
||||
}
|
||||
|
||||
// 0x05
|
||||
void EmitSLL(Opcode op)
|
||||
{
|
||||
IRValue rd(IRValue::Reg);
|
||||
rd.SetReg(op.r_type.rd);
|
||||
|
||||
IRValue rt(IRValue::Reg);
|
||||
rt.SetReg(op.r_type.rt);
|
||||
|
||||
IRValue sa(IRValue::Imm);
|
||||
sa.SetImm32Unsigned(op.r_type.sa);
|
||||
|
||||
IRInstruction instr = IRInstruction::Build({rd, rt, sa}, SHIFT);
|
||||
instr.is_logical = true;
|
||||
instr.direction = IRInstruction::Direction::Left;
|
||||
curBlock->instructions.push_back(instr);
|
||||
|
||||
printf("sll %s,%s,%d\n", EmotionEngine::Reg(op.r_type.rd), EmotionEngine::Reg(op.r_type.rt), op.r_type.sa);
|
||||
}
|
||||
|
||||
void EmitJR(Opcode op)
|
||||
{
|
||||
IRValue reg(IRValue::Reg);
|
||||
reg.SetReg(op.r_type.rs);
|
||||
|
||||
auto instr = IRInstruction::Build({reg}, JUMP);
|
||||
instr.should_link = false;
|
||||
curBlock->instructions.push_back(instr);
|
||||
|
||||
printf("jr %s\n", EmotionEngine::Reg(reg.GetReg()));
|
||||
}
|
||||
|
||||
void EmitJalr(Opcode op)
|
||||
{
|
||||
IRValue rd(IRValue::Reg);
|
||||
rd.SetReg(op.r_type.rd);
|
||||
IRValue rs(IRValue::Reg);
|
||||
rs.SetReg(op.r_type.rs);
|
||||
|
||||
auto instr = IRInstruction::Build({rd, rs}, JUMP);
|
||||
instr.should_link = true;
|
||||
curBlock->instructions.push_back(instr);
|
||||
|
||||
printf("jalr %s,%s\n", EmotionEngine::Reg(op.r_type.rd), EmotionEngine::Reg(op.r_type.rs));
|
||||
}
|
||||
|
||||
// 0x18
|
||||
void EmitMULT(Opcode op)
|
||||
{
|
||||
IRValue rd(IRValue::Reg);
|
||||
rd.SetReg(op.r_type.rt);
|
||||
IRValue rt(IRValue::Reg);
|
||||
rt.SetReg(op.r_type.rt);
|
||||
IRValue rs(IRValue::Reg);
|
||||
rs.SetReg(op.r_type.rs);
|
||||
|
||||
auto instr = IRInstruction::Build({rd, rs, rt}, MULT);
|
||||
instr.is_unsigned = true;
|
||||
instr.size = IRInstruction::InstrSize::Size32;
|
||||
instr.is_mmi_divmul = false;
|
||||
curBlock->instructions.push_back(instr);
|
||||
|
||||
printf("mult %s,%s,%s\n", EmotionEngine::Reg(op.r_type.rd), EmotionEngine::Reg(op.r_type.rs), EmotionEngine::Reg(op.r_type.rt));
|
||||
}
|
||||
|
||||
// 0x25
|
||||
void EmitOR(Opcode op)
|
||||
{
|
||||
IRValue rt(IRValue::Reg);
|
||||
rt.SetReg(op.r_type.rt);
|
||||
IRValue rd(IRValue::Reg);
|
||||
rd.SetReg(op.r_type.rd);
|
||||
IRValue rs(IRValue::Reg);
|
||||
rs.SetReg(op.r_type.rs);
|
||||
|
||||
auto instr = IRInstruction::Build({rd, rs, rt}, OR);
|
||||
instr.is_unsigned = true;
|
||||
instr.size = IRInstruction::InstrSize::Size64;
|
||||
|
||||
curBlock->instructions.push_back(instr);
|
||||
|
||||
printf("or %s,%s,%s\n", EmotionEngine::Reg(rd.GetReg()), EmotionEngine::Reg(rs.GetReg()), EmotionEngine::Reg(rt.GetReg()));
|
||||
}
|
||||
|
||||
// 0x2d
|
||||
void EmitDADDU(Opcode op)
|
||||
{
|
||||
IRValue rt(IRValue::Reg);
|
||||
rt.SetReg(op.r_type.rt);
|
||||
IRValue rd(IRValue::Reg);
|
||||
rd.SetReg(op.r_type.rd);
|
||||
IRValue rs(IRValue::Reg);
|
||||
rs.SetReg(op.r_type.rs);
|
||||
|
||||
auto instr = IRInstruction::Build({rd, rs, rt}, ADD);
|
||||
instr.is_unsigned = true;
|
||||
instr.size = IRInstruction::InstrSize::Size64;
|
||||
|
||||
curBlock->instructions.push_back(instr);
|
||||
|
||||
printf("daddu %s,%s,%s\n", EmotionEngine::Reg(rd.GetReg()), EmotionEngine::Reg(rs.GetReg()), EmotionEngine::Reg(rt.GetReg()));
|
||||
}
|
||||
|
||||
// 0x00
|
||||
void EmitSpecial(Opcode op)
|
||||
{
|
||||
switch (op.r_type.func)
|
||||
{
|
||||
case 0x00:
|
||||
EmitSLL(op);
|
||||
break;
|
||||
case 0x08:
|
||||
EmitJR(op);
|
||||
break;
|
||||
case 0x09:
|
||||
EmitJalr(op);
|
||||
break;
|
||||
case 0x0f:
|
||||
printf("sync\n");
|
||||
break;
|
||||
case 0x18:
|
||||
EmitMULT(op);
|
||||
break;
|
||||
case 0x25:
|
||||
EmitOR(op);
|
||||
break;
|
||||
case 0x2D:
|
||||
EmitDADDU(op);
|
||||
break;
|
||||
default:
|
||||
printf("[EEJIT]: Cannot emit unknown special opcode 0x%02x (0x%08x)\n", op.r_type.func, op.full);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// 0x03
|
||||
void EmitJAL(Opcode op)
|
||||
{
|
||||
IRValue imm(IRValue::Imm);
|
||||
imm.SetImm32Unsigned(op.j_type.target << 2);
|
||||
|
||||
auto instr = IRInstruction::Build({imm}, JUMP);
|
||||
instr.should_link = true;
|
||||
curBlock->instructions.push_back(instr);
|
||||
|
||||
printf("jal 0x%08x\n", (EmotionEngine::GetState()->pc & 0xF0000000) | imm.GetImm());
|
||||
}
|
||||
|
||||
// 0x04
|
||||
void EmitBEQ(Opcode op)
|
||||
{
|
||||
IRValue imm(IRValue::Imm);
|
||||
|
@ -46,12 +193,54 @@ void EmitBEQ(Opcode op)
|
|||
rs.SetReg(op.r_type.rs);
|
||||
|
||||
auto instr = IRInstruction::Build({rs, rt, imm}, IRInstrs::BRANCH);
|
||||
instr.b_type = IRInstruction::BranchType::EQ;
|
||||
if (op.r_type.rt == op.r_type.rs == 0)
|
||||
instr.b_type = IRInstruction::BranchType::AL;
|
||||
else
|
||||
instr.b_type = IRInstruction::BranchType::EQ;
|
||||
curBlock->instructions.push_back(instr);
|
||||
|
||||
printf("beq %s,%s,pc+%d\n", EmotionEngine::Reg(op.i_type.rs), EmotionEngine::Reg(op.i_type.rt), (int32_t)imm.GetImm());
|
||||
}
|
||||
|
||||
// 0x05
|
||||
void EmitBNE(Opcode op)
|
||||
{
|
||||
IRValue imm(IRValue::Imm);
|
||||
imm.SetImm32(op.i_type.imm << 2);
|
||||
|
||||
IRValue rt(IRValue::Reg);
|
||||
rt.SetReg(op.r_type.rt);
|
||||
|
||||
IRValue rs(IRValue::Reg);
|
||||
rs.SetReg(op.r_type.rs);
|
||||
|
||||
auto instr = IRInstruction::Build({rs, rt, imm}, IRInstrs::BRANCH);
|
||||
instr.b_type = IRInstruction::BranchType::NE;
|
||||
curBlock->instructions.push_back(instr);
|
||||
|
||||
printf("bne %s,%s,pc+%d\n", EmotionEngine::Reg(op.i_type.rs), EmotionEngine::Reg(op.i_type.rt), (int32_t)imm.GetImm());
|
||||
}
|
||||
|
||||
// 0x09
|
||||
void EmitADDIU(Opcode op)
|
||||
{
|
||||
IRValue imm(IRValue::Imm);
|
||||
imm.SetImm64(op.i_type.imm);
|
||||
|
||||
IRValue src(IRValue::Reg);
|
||||
src.SetReg(op.i_type.rs);
|
||||
|
||||
IRValue dst(IRValue::Reg);
|
||||
dst.SetReg(op.i_type.rt);
|
||||
|
||||
auto instr = IRInstruction::Build({dst, src, imm}, IRInstrs::ADD);
|
||||
instr.is_unsigned = true;
|
||||
instr.size = IRInstruction::Size32;
|
||||
curBlock->instructions.push_back(instr);
|
||||
|
||||
printf("addiu %s,%s,0x%08lx\n", EmotionEngine::Reg(op.i_type.rt), EmotionEngine::Reg(op.i_type.rs), (int64_t)(int16_t)op.i_type.imm);
|
||||
}
|
||||
|
||||
// 0x0A
|
||||
void EmitSLTI(Opcode op)
|
||||
{
|
||||
|
@ -71,6 +260,42 @@ void EmitSLTI(Opcode op)
|
|||
printf("slti %s,%s,0x%08lx\n", EmotionEngine::Reg(op.i_type.rt), EmotionEngine::Reg(op.i_type.rs), (int64_t)(int16_t)op.i_type.imm);
|
||||
}
|
||||
|
||||
// 0x0C
|
||||
void EmitANDI(Opcode op)
|
||||
{
|
||||
IRValue imm(IRValue::Imm);
|
||||
imm.SetImmUnsigned(op.i_type.imm);
|
||||
|
||||
IRValue src(IRValue::Reg);
|
||||
src.SetReg(op.i_type.rs);
|
||||
|
||||
IRValue dst(IRValue::Reg);
|
||||
dst.SetReg(op.i_type.rt);
|
||||
|
||||
auto instr = IRInstruction::Build({dst, src, imm}, IRInstrs::AND);
|
||||
curBlock->instructions.push_back(instr);
|
||||
|
||||
printf("andi %s,%s,0x%08lx\n", EmotionEngine::Reg(dst.GetReg()), EmotionEngine::Reg(src.GetReg()), imm.GetImm64());
|
||||
}
|
||||
|
||||
// 0x0D
|
||||
void EmitOri(Opcode op)
|
||||
{
|
||||
IRValue imm(IRValue::Imm);
|
||||
imm.SetImmUnsigned(op.i_type.imm);
|
||||
|
||||
IRValue src(IRValue::Reg);
|
||||
src.SetReg(op.i_type.rs);
|
||||
|
||||
IRValue dst(IRValue::Reg);
|
||||
dst.SetReg(op.i_type.rt);
|
||||
|
||||
auto instr = IRInstruction::Build({dst, src, imm}, IRInstrs::OR);
|
||||
curBlock->instructions.push_back(instr);
|
||||
|
||||
printf("ori %s,%s,0x%08lx\n", EmotionEngine::Reg(dst.GetReg()), EmotionEngine::Reg(src.GetReg()), imm.GetImm64());
|
||||
}
|
||||
|
||||
// 0x0F
|
||||
void EmitLUI(Opcode op)
|
||||
{
|
||||
|
@ -93,10 +318,10 @@ void EmitMFC0(Opcode op)
|
|||
int src = op.r_type.rd;
|
||||
|
||||
IRValue dst_val(IRValue::Cop0Reg);
|
||||
dst_val.SetReg(dest);
|
||||
dst_val.SetReg(src);
|
||||
|
||||
IRValue src_val(IRValue::Reg);
|
||||
src_val.SetReg(src);
|
||||
src_val.SetReg(dest);
|
||||
|
||||
auto instr = IRInstruction::Build({src_val, dst_val}, IRInstrs::MOVE);
|
||||
curBlock->instructions.push_back(instr);
|
||||
|
@ -104,24 +329,107 @@ void EmitMFC0(Opcode op)
|
|||
printf("mfc0 %s,r%d\n", EmotionEngine::Reg(dest), src);
|
||||
}
|
||||
|
||||
// 0x10 0x04
|
||||
void EmitMTC0(Opcode op)
|
||||
{
|
||||
int dest = op.r_type.rt;
|
||||
int src = op.r_type.rd;
|
||||
|
||||
IRValue src_val(IRValue::Cop0Reg);
|
||||
src_val.SetReg(src);
|
||||
|
||||
IRValue dst_val(IRValue::Reg);
|
||||
dst_val.SetReg(dest);
|
||||
|
||||
auto instr = IRInstruction::Build({src_val, dst_val}, IRInstrs::MOVE);
|
||||
curBlock->instructions.push_back(instr);
|
||||
|
||||
printf("mtc0 %s,r%d\n", EmotionEngine::Reg(dest), src);
|
||||
}
|
||||
|
||||
// 0x10
|
||||
void EmitCOP0(Opcode op)
|
||||
{
|
||||
switch (op.r_type.func)
|
||||
switch (op.r_type.rs)
|
||||
{
|
||||
case 0x00:
|
||||
EmitMFC0(op);
|
||||
break;
|
||||
case 0x04:
|
||||
EmitMTC0(op);
|
||||
break;
|
||||
case 0x10:
|
||||
{
|
||||
switch (op.r_type.func)
|
||||
{
|
||||
case 0x02:
|
||||
printf("tlbwi\n");
|
||||
break;
|
||||
default:
|
||||
printf("Unknown COP0 TLB instruction 0x%02x\n", op.r_type.func);
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
printf("[EEJIT]: Cannot emit unknown cop0 opcode 0x%08x\n", op.r_type.func);
|
||||
printf("[EEJIT]: Cannot emit unknown cop0 opcode 0x%08x\n", op.r_type.rs);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// 0x2B
|
||||
void EmitSW(Opcode op)
|
||||
{
|
||||
IRValue imm(IRValue::Imm);
|
||||
imm.SetImm64(op.i_type.imm);
|
||||
|
||||
IRValue src(IRValue::Reg);
|
||||
src.SetReg(op.i_type.rs);
|
||||
|
||||
IRValue dst(IRValue::Reg);
|
||||
dst.SetReg(op.i_type.rt);
|
||||
|
||||
auto instr = IRInstruction::Build({dst, imm, src}, IRInstrs::STORE);
|
||||
instr.access_size = IRInstruction::AccessSize::U32;
|
||||
curBlock->instructions.push_back(instr);
|
||||
|
||||
printf("sw %s, %d(%s)\n", EmotionEngine::Reg(op.i_type.rt), (int16_t)op.i_type.imm, EmotionEngine::Reg(op.i_type.rs));
|
||||
}
|
||||
|
||||
// 0x3F
|
||||
void EmitSD(Opcode op)
|
||||
{
|
||||
IRValue imm(IRValue::Imm);
|
||||
imm.SetImm64(op.i_type.imm);
|
||||
|
||||
IRValue src(IRValue::Reg);
|
||||
src.SetReg(op.i_type.rs);
|
||||
|
||||
IRValue dst(IRValue::Reg);
|
||||
dst.SetReg(op.i_type.rt);
|
||||
|
||||
auto instr = IRInstruction::Build({dst, imm, src}, IRInstrs::STORE);
|
||||
instr.access_size = IRInstruction::AccessSize::U64;
|
||||
curBlock->instructions.push_back(instr);
|
||||
|
||||
printf("sd %s, %d(%s)\n", EmotionEngine::Reg(op.i_type.rt), (int16_t)op.i_type.imm, EmotionEngine::Reg(op.i_type.rs));
|
||||
}
|
||||
|
||||
bool IsBranch(Opcode op)
|
||||
{
|
||||
switch (op.opcode)
|
||||
{
|
||||
case 0x00:
|
||||
switch (op.r_type.func)
|
||||
{
|
||||
case 0x08:
|
||||
case 0x09:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 0x03:
|
||||
case 0x05:
|
||||
return true;
|
||||
}
|
||||
|
@ -135,78 +443,111 @@ bool branchDelayed = false;
|
|||
// If we encounter a branch, return that as the true number of cycles
|
||||
int EEJit::Clock(int cycles)
|
||||
{
|
||||
// Create a new block
|
||||
curBlock = new Block();
|
||||
curBlock->addr = EmotionEngine::GetState()->pc;
|
||||
curBlock->instructions.clear();
|
||||
|
||||
uint32_t start = curBlock->addr;
|
||||
|
||||
EmitPrologue();
|
||||
|
||||
int cycle = 0;
|
||||
for (; cycle < cycles; cycle++)
|
||||
curBlock = EEJitX64::GetBlockForAddr(EmotionEngine::GetState()->pc);
|
||||
if (curBlock)
|
||||
{
|
||||
printf("0x%08x:\t", start);
|
||||
// TODO: Move this into its own assembly routine
|
||||
uint32_t instr = Bus::Read32(start);
|
||||
start += 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create a new block
|
||||
curBlock = new Block();
|
||||
curBlock->addr = EmotionEngine::GetState()->pc;
|
||||
curBlock->instructions.clear();
|
||||
curBlock->cycles = 0;
|
||||
|
||||
Opcode op;
|
||||
op.full = instr;
|
||||
uint32_t start = curBlock->addr;
|
||||
|
||||
if (!instr)
|
||||
EmitPrologue();
|
||||
|
||||
int cycle = 0;
|
||||
int instrs = 0;
|
||||
for (; cycle < cycles && instrs < 12; cycle++, instrs++, curBlock->cycles++)
|
||||
{
|
||||
printf("nop\n");
|
||||
printf("0x%08x:\t", start);
|
||||
// TODO: Move this into its own assembly routine
|
||||
uint32_t instr = Bus::Read32(start);
|
||||
start += 4;
|
||||
|
||||
Opcode op;
|
||||
op.full = instr;
|
||||
|
||||
if (!instr)
|
||||
{
|
||||
printf("nop\n");
|
||||
curBlock->instructions.push_back(IRInstruction::Build({}, NOP));
|
||||
if (branchDelayed)
|
||||
{
|
||||
branchDelayed = false;
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (op.opcode)
|
||||
{
|
||||
case 0x00:
|
||||
EmitSpecial(op);
|
||||
break;
|
||||
case 0x03:
|
||||
EmitJAL(op);
|
||||
break;
|
||||
case 0x04:
|
||||
EmitBEQ(op);
|
||||
break;
|
||||
case 0x05:
|
||||
EmitBNE(op);
|
||||
break;
|
||||
case 0x09:
|
||||
EmitADDIU(op);
|
||||
break;
|
||||
case 0x0A:
|
||||
EmitSLTI(op);
|
||||
break;
|
||||
case 0x0C:
|
||||
EmitANDI(op);
|
||||
break;
|
||||
case 0x0D:
|
||||
EmitOri(op);
|
||||
break;
|
||||
case 0x0F:
|
||||
EmitLUI(op);
|
||||
break;
|
||||
case 0x10:
|
||||
EmitCOP0(op);
|
||||
break;
|
||||
case 0x2B:
|
||||
EmitSW(op);
|
||||
break;
|
||||
case 0x3F:
|
||||
EmitSD(op);
|
||||
break;
|
||||
default:
|
||||
printf("[EEJIT]: Cannot emit unknown opcode 0x%02x (0x%08x)\n", op.opcode, op.full);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (branchDelayed)
|
||||
{
|
||||
branchDelayed = false;
|
||||
cycle++;
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
|
||||
branchDelayed = IsBranch(op);
|
||||
}
|
||||
|
||||
switch (op.opcode)
|
||||
{
|
||||
case 0x05:
|
||||
EmitBEQ(op);
|
||||
break;
|
||||
case 0x0A:
|
||||
EmitSLTI(op);
|
||||
break;
|
||||
case 0x0F:
|
||||
EmitLUI(op);
|
||||
break;
|
||||
case 0x10:
|
||||
EmitCOP0(op);
|
||||
break;
|
||||
default:
|
||||
printf("[EEJIT]: Cannot emit unknown opcode 0x%02x (0x%08x)\n", op.opcode, op.full);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (branchDelayed)
|
||||
{
|
||||
branchDelayed = false;
|
||||
cycle++;
|
||||
break;
|
||||
}
|
||||
|
||||
branchDelayed = IsBranch(op);
|
||||
}
|
||||
|
||||
// Emit epilogue
|
||||
curBlock->instructions.push_back(IRInstruction::Build({}, IRInstrs::EPILOGUE));
|
||||
// JIT the block into host code
|
||||
// Emit epilogue
|
||||
curBlock->instructions.push_back(IRInstruction::Build({}, IRInstrs::EPILOGUE));
|
||||
// JIT the block into host code
|
||||
#if EE_JIT == 64
|
||||
EEJitX64::TranslateBlock(curBlock);
|
||||
EEJitX64::TranslateBlock(curBlock);
|
||||
#endif
|
||||
// Cache the block
|
||||
// Cache the block
|
||||
EEJitX64::CacheBlock(curBlock);
|
||||
}
|
||||
// Run it
|
||||
curBlock->entryPoint(EmotionEngine::GetState(), start);
|
||||
curBlock->entryPoint(EmotionEngine::GetState(), curBlock->addr);
|
||||
|
||||
return cycle;
|
||||
return curBlock->cycles;
|
||||
}
|
||||
|
||||
void EEJit::Initialize()
|
||||
|
|
|
@ -5,11 +5,19 @@
|
|||
|
||||
enum IRInstrs
|
||||
{
|
||||
NOP,
|
||||
PROLOGUE,
|
||||
EPILOGUE,
|
||||
MOVE, // General purpose move (COP0, r<-imm, etc)
|
||||
SLT, // Compare and set register (can be immediate or reg, signed or unsigned)
|
||||
BRANCH, // PC-Relative branch on condition = true
|
||||
OR, // Do a logical or between a register and an immediate or two registers
|
||||
JUMP, // Either a jump to an imm, or else a jump to a register
|
||||
ADD, // Add two pieces of data (reg+imm, reg+reg)
|
||||
STORE, // Store memory op
|
||||
AND, // Bitwise AND
|
||||
SHIFT, // Shift logical, arithmetic, left, right, etc...
|
||||
MULT, // Multiply
|
||||
};
|
||||
|
||||
struct IRValue
|
||||
|
@ -31,7 +39,7 @@ private:
|
|||
float fp_value;
|
||||
int cop_regnum;
|
||||
} value;
|
||||
|
||||
public:
|
||||
Type type;
|
||||
public:
|
||||
IRValue(Type type)
|
||||
|
@ -86,6 +94,7 @@ struct IRInstruction
|
|||
LE = 3,
|
||||
GT = 4,
|
||||
LT = 5,
|
||||
AL = 6,
|
||||
} b_type;
|
||||
|
||||
// MOVN, MOVZ
|
||||
|
@ -127,7 +136,7 @@ typedef void (*blockEntry)(void* statePtr, uint32_t blockPC);
|
|||
|
||||
struct Block
|
||||
{
|
||||
uint32_t addr;
|
||||
uint32_t addr, cycles;
|
||||
std::vector<IRInstruction> instructions;
|
||||
blockEntry entryPoint;
|
||||
};
|
||||
|
|
|
@ -25,14 +25,15 @@ void Reset()
|
|||
#endif
|
||||
memset(&state, 0, sizeof(state));
|
||||
|
||||
state.pc = 0xBFC00000;
|
||||
state.next_pc = 0xBFC00004;
|
||||
state.cop0_regs[15] = 0x2E20;
|
||||
GetState()->pc = 0xBFC00000;
|
||||
GetState()->next_pc = 0xBFC00004;
|
||||
GetState()->cop0_regs[15] = 0x2E20;
|
||||
}
|
||||
|
||||
int Clock(int cycles)
|
||||
{
|
||||
#ifdef EE_JIT
|
||||
GetState()->cop0_regs[9] += cycles;
|
||||
return EEJit::Clock(cycles);
|
||||
#else
|
||||
#error TODO: EE Interpreter clock
|
||||
|
@ -107,17 +108,20 @@ bool IsBranch(uint32_t instr)
|
|||
void Dump()
|
||||
{
|
||||
for (int i = 0; i < 32; i++)
|
||||
printf("%s\t->\t0x%lx%016lx\n", Reg(i), state.regs[i].u64[1], state.regs[i].u64[0]);
|
||||
printf("pc\t->\t0x%08x\n", state.pc);
|
||||
printf("pc_at\t->\t0x%08x\n", state.pc_at);
|
||||
printf("hi\t->\t0x%08lx\n", state.hi);
|
||||
printf("lo\t->\t0x%08lx\n", state.lo);
|
||||
printf("hi1\t->\t0x%08lx\n", state.hi1);
|
||||
printf("lo1\t->\t0x%08lx\n", state.lo1);
|
||||
printf("status\t->\t0x%08x\n", state.cop0_regs[12]);
|
||||
printf("cause\t->\t0x%08x\n", state.cop0_regs[13]);
|
||||
printf("%s\t->\t0x%lx%016lx\n", Reg(i), GetState()->regs[i].u64[1], GetState()->regs[i].u64[0]);
|
||||
printf("pc\t->\t0x%08x\n", GetState()->pc);
|
||||
printf("pc_at\t->\t0x%08x\n", GetState()->pc_at);
|
||||
printf("hi\t->\t0x%08lx\n", GetState()->hi);
|
||||
printf("lo\t->\t0x%08lx\n", GetState()->lo);
|
||||
printf("hi1\t->\t0x%08lx\n", GetState()->hi1);
|
||||
printf("lo1\t->\t0x%08lx\n", GetState()->lo1);
|
||||
printf("COP0:");
|
||||
for (int i = 0; i < 32; i++)
|
||||
printf("f%d\t->\t%f\n", i, convert(state.fprs[i].i));
|
||||
printf("\tr%d\t->\t0x%08x\n", i, GetState()->cop0_regs[i]);
|
||||
for (int i = 0; i < 32; i++)
|
||||
printf("f%d\t->\t%f\n", i, convert(GetState()->fprs[i].i));
|
||||
|
||||
printf("0x%08lx\n", offsetof(EmotionEngine::ProcessorState, cop0_regs[12]));
|
||||
|
||||
#ifdef EE_JIT
|
||||
EEJit::Dump();
|
||||
|
@ -190,7 +194,7 @@ void Exception(uint8_t code)
|
|||
|
||||
if (code == 0x08)
|
||||
{
|
||||
int syscall_number = std::abs((int)EmotionEngine::state.regs[3].u32[0]);
|
||||
int syscall_number = std::abs((int)EmotionEngine::GetState()->regs[3].u32[0]);
|
||||
if (syscall_number != 122)
|
||||
printf("Syscall %d\n", syscall_number);
|
||||
if (syscall_number == 0x02)
|
||||
|
@ -249,8 +253,8 @@ void Exception(uint8_t code)
|
|||
|
||||
COP0CAUSE cause;
|
||||
COP0Status status;
|
||||
status.value = state.cop0_regs[12];
|
||||
cause.value = state.cop0_regs[13];
|
||||
status.value = GetState()->cop0_regs[12];
|
||||
cause.value = GetState()->cop0_regs[13];
|
||||
|
||||
cause.excode = code;
|
||||
|
||||
|
@ -258,7 +262,7 @@ void Exception(uint8_t code)
|
|||
|
||||
if (!status.exl)
|
||||
{
|
||||
state.cop0_regs[14] = state.pc-4;
|
||||
GetState()->cop0_regs[14] = GetState()->pc-4;
|
||||
|
||||
switch (code)
|
||||
{
|
||||
|
@ -273,11 +277,11 @@ void Exception(uint8_t code)
|
|||
status.exl = true;
|
||||
}
|
||||
|
||||
state.pc = exception_addr[status.bev] + vector;
|
||||
state.next_pc = state.pc + 4;
|
||||
GetState()->pc = exception_addr[status.bev] + vector;
|
||||
GetState()->next_pc = GetState()->pc + 4;
|
||||
|
||||
state.cop0_regs[12] = status.value;
|
||||
state.cop0_regs[13] = cause.value;
|
||||
GetState()->cop0_regs[12] = status.value;
|
||||
GetState()->cop0_regs[13] = cause.value;
|
||||
}
|
||||
|
||||
void SetIp1Pending()
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
#define MOV(a, b) generator->mov(a, b)
|
||||
#define MOV(a, b) generator->mov(a, b)
|
||||
#define ADD(a, b) generator->add(a, b)
|
|
@ -2,6 +2,7 @@
|
|||
#include "RegAllocator.h"
|
||||
#include <emu/cpu/ee/EEJit.h>
|
||||
#include <emu/cpu/ee/EmotionEngine.h>
|
||||
#include <emu/memory/Bus.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
@ -20,12 +21,38 @@ void EEJitX64::JitStoreReg(GuestRegister reg)
|
|||
{
|
||||
auto offs = reg_alloc.GetRegOffset(reg);
|
||||
|
||||
generator->mov(generator->qword[generator->rbp + offs], Xbyak::Reg64(reg_alloc.GetHostReg(reg)));
|
||||
if (reg >= COP0_OFFS && reg < COP0_OFFS+32)
|
||||
MOV(generator->dword[generator->rbp + offs], Xbyak::Reg32(reg_alloc.GetHostReg(reg)));
|
||||
else
|
||||
MOV(generator->qword[generator->rbp + offs], Xbyak::Reg64(reg_alloc.GetHostReg(reg)));
|
||||
}
|
||||
|
||||
void EEJitX64::JitLoadReg(GuestRegister reg, int hostReg)
|
||||
{
|
||||
generator->mov(Xbyak::Reg64(hostReg), generator->qword[generator->rbp + reg_alloc.GetRegOffset(reg)]);
|
||||
if (reg >= COP0_OFFS && reg < COP0_OFFS+32)
|
||||
MOV(Xbyak::Reg32(hostReg), generator->dword[generator->rbp + reg_alloc.GetRegOffset(reg)]);
|
||||
else
|
||||
MOV(Xbyak::Reg64(hostReg), generator->qword[generator->rbp + reg_alloc.GetRegOffset(reg)]);
|
||||
}
|
||||
|
||||
void SaveHostRegisters()
|
||||
{
|
||||
generator->push(generator->rbx);
|
||||
generator->push(generator->rdx);
|
||||
generator->push(generator->r8);
|
||||
generator->push(generator->r9);
|
||||
generator->push(generator->r10);
|
||||
generator->push(generator->r11);
|
||||
}
|
||||
|
||||
void RestoreHostRegisters()
|
||||
{
|
||||
generator->pop(generator->r11);
|
||||
generator->pop(generator->r10);
|
||||
generator->pop(generator->r9);
|
||||
generator->pop(generator->r8);
|
||||
generator->pop(generator->rdx);
|
||||
generator->pop(generator->rbx);
|
||||
}
|
||||
|
||||
void JitPrologue()
|
||||
|
@ -44,9 +71,9 @@ void JitEpilogue()
|
|||
reg_alloc.DoWriteback();
|
||||
|
||||
// Write R8 back to pc
|
||||
generator->mov(generator->qword[generator->rbp + offsetof(EmotionEngine::ProcessorState, pc)], generator->r8);
|
||||
generator->mov(generator->qword[generator->rbp + offsetof(EmotionEngine::ProcessorState, next_pc)], generator->r8);
|
||||
generator->add(generator->qword[generator->rbp + offsetof(EmotionEngine::ProcessorState, next_pc)], 4);
|
||||
MOV(generator->qword[generator->rbp + offsetof(EmotionEngine::ProcessorState, pc)], generator->r8);
|
||||
MOV(generator->qword[generator->rbp + offsetof(EmotionEngine::ProcessorState, next_pc)], generator->r8);
|
||||
ADD(generator->qword[generator->rbp + offsetof(EmotionEngine::ProcessorState, next_pc)], 4);
|
||||
|
||||
// Now restore all host registers
|
||||
for (int i = 15; i >= 0; i--)
|
||||
|
@ -66,11 +93,17 @@ void JitMov(IRInstruction& i)
|
|||
}
|
||||
else
|
||||
{
|
||||
auto src = Xbyak::Reg64(reg_alloc.GetHostReg((GuestRegister)(i.args[0].GetReg()+COP0_OFFS)));
|
||||
auto dst = Xbyak::Reg64(reg_alloc.GetHostReg((GuestRegister)i.args[1].GetReg(), true));
|
||||
generator->mov(dst, src);
|
||||
auto src = Xbyak::Reg32(reg_alloc.GetHostReg((GuestRegister)(i.args[1].GetReg()+COP0_OFFS)));
|
||||
auto dst = Xbyak::Reg32(reg_alloc.GetHostReg((GuestRegister)i.args[0].GetReg(), true));
|
||||
MOV(dst, src);
|
||||
}
|
||||
}
|
||||
else if (i.args[1].IsReg() && i.args[0].IsCop0())
|
||||
{
|
||||
auto src = Xbyak::Reg32(reg_alloc.GetHostReg((GuestRegister)i.args[1].GetReg()));
|
||||
auto dst = Xbyak::Reg32(reg_alloc.GetHostReg((GuestRegister)(i.args[0].GetReg()+COP0_OFFS), true));
|
||||
MOV(dst, src);
|
||||
}
|
||||
else if (i.args[0].IsReg() && i.args[1].IsImm())
|
||||
{
|
||||
if (i.args[1].GetReg() == 0)
|
||||
|
@ -81,7 +114,7 @@ void JitMov(IRInstruction& i)
|
|||
else
|
||||
{
|
||||
auto dst = Xbyak::Reg64(reg_alloc.GetHostReg((GuestRegister)(i.args[0].GetReg()), true));
|
||||
generator->mov(dst, i.args[1].GetImm64());
|
||||
MOV(dst, i.args[1].GetImm64());
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -104,12 +137,23 @@ void JitSlt(IRInstruction& i)
|
|||
else
|
||||
{
|
||||
auto dst = Xbyak::Reg8(reg_alloc.GetHostReg((GuestRegister)i.args[0].GetReg(), true));
|
||||
auto src = Xbyak::Reg64(reg_alloc.GetHostReg((GuestRegister)i.args[1].GetReg()));
|
||||
int32_t imm = i.args[2].GetImm();
|
||||
if (i.args[1].GetReg() == 0)
|
||||
{
|
||||
int32_t imm = i.args[2].GetImm();
|
||||
MOV(generator->rdi, 0);
|
||||
generator->cmp(generator->rdi, imm);
|
||||
generator->setl(dst);
|
||||
generator->movzx(Xbyak::Reg64(reg_alloc.GetHostReg((GuestRegister)i.args[0].GetReg(), true)), dst);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto src = Xbyak::Reg64(reg_alloc.GetHostReg((GuestRegister)i.args[1].GetReg()));
|
||||
int32_t imm = i.args[2].GetImm();
|
||||
|
||||
generator->cmp(src, imm);
|
||||
generator->setl(dst);
|
||||
generator->movzx(Xbyak::Reg64(reg_alloc.GetHostReg((GuestRegister)i.args[0].GetReg(), true)), dst);
|
||||
generator->cmp(src, imm);
|
||||
generator->setl(dst);
|
||||
generator->movzx(Xbyak::Reg64(reg_alloc.GetHostReg((GuestRegister)i.args[0].GetReg(), true)), dst);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -129,58 +173,400 @@ void JitBranch(IRInstruction& i)
|
|||
{
|
||||
case IRInstruction::BranchType::EQ:
|
||||
{
|
||||
auto op2 = Xbyak::Reg64(reg_alloc.GetHostReg((GuestRegister)i.args[1].GetReg()));
|
||||
generator->cmp(op1, op2);
|
||||
generator->jne(cond_failed);
|
||||
if (i.args[1].GetReg() == 0)
|
||||
{
|
||||
generator->cmp(op1, 0);
|
||||
generator->jne(cond_failed);
|
||||
}
|
||||
else if (i.args[0].GetReg() == 0)
|
||||
{
|
||||
auto op2 = Xbyak::Reg64(reg_alloc.GetHostReg((GuestRegister)i.args[1].GetReg()));
|
||||
MOV(generator->rdi, 0);
|
||||
generator->cmp(generator->rdi, i.args[1].GetImm());
|
||||
generator->jne(cond_failed);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto op2 = Xbyak::Reg64(reg_alloc.GetHostReg((GuestRegister)i.args[1].GetReg()));
|
||||
generator->cmp(op1, op2);
|
||||
generator->jne(cond_failed);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IRInstruction::BranchType::NE:
|
||||
{
|
||||
if (i.args[1].GetReg() == 0)
|
||||
{
|
||||
generator->cmp(op1, 0);
|
||||
generator->je(cond_failed);
|
||||
}
|
||||
else if (i.args[0].GetReg() == 0)
|
||||
{
|
||||
auto op2 = Xbyak::Reg64(reg_alloc.GetHostReg((GuestRegister)i.args[1].GetReg()));
|
||||
MOV(generator->rdi, 0);
|
||||
generator->cmp(generator->rdi, i.args[1].GetImm());
|
||||
generator->je(cond_failed);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto op2 = Xbyak::Reg64(reg_alloc.GetHostReg((GuestRegister)i.args[1].GetReg()));
|
||||
generator->cmp(op1, op2);
|
||||
generator->je(cond_failed);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IRInstruction::BranchType::AL:
|
||||
break;
|
||||
default:
|
||||
printf("Unknown branch condition %d\n", i.b_type);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
generator->add(generator->r8, i.args[2].GetImm());
|
||||
Xbyak::Label done;
|
||||
|
||||
ADD(generator->r8, i.args[2].GetImm()-4);
|
||||
generator->jmp(done);
|
||||
generator->L(cond_failed);
|
||||
ADD(generator->r8, 4);
|
||||
generator->L(done);
|
||||
}
|
||||
|
||||
void JitOr(IRInstruction& i)
|
||||
{
|
||||
if (i.args[2].IsImm())
|
||||
{
|
||||
if (i.args[1].GetReg() == i.args[0].GetReg())
|
||||
{
|
||||
auto src = Xbyak::Reg32(reg_alloc.GetHostReg((GuestRegister)i.args[1].GetReg()));
|
||||
generator->or_(src, i.args[2].GetImm());
|
||||
return;
|
||||
}
|
||||
auto src = Xbyak::Reg64(reg_alloc.GetHostReg((GuestRegister)i.args[1].GetReg()));
|
||||
auto dst = Xbyak::Reg64(reg_alloc.GetHostReg((GuestRegister)i.args[0].GetReg(), true));
|
||||
|
||||
MOV(generator->rdi, src);
|
||||
generator->or_(generator->rdi, i.args[2].GetImm());
|
||||
MOV(dst, generator->rdi);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (i.args[1].GetReg() == 0)
|
||||
{
|
||||
auto src = Xbyak::Reg64(reg_alloc.GetHostReg((GuestRegister)i.args[2].GetReg()));
|
||||
auto dst = Xbyak::Reg64(reg_alloc.GetHostReg((GuestRegister)i.args[0].GetReg(), true));
|
||||
MOV(dst, src);
|
||||
}
|
||||
else if (i.args[2].GetReg() == 0)
|
||||
{
|
||||
auto src = Xbyak::Reg64(reg_alloc.GetHostReg((GuestRegister)i.args[1].GetReg()));
|
||||
auto dst = Xbyak::Reg64(reg_alloc.GetHostReg((GuestRegister)i.args[0].GetReg(), true));
|
||||
MOV(dst, src);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto src1 = Xbyak::Reg64(reg_alloc.GetHostReg((GuestRegister)i.args[1].GetReg()));
|
||||
auto src2 = Xbyak::Reg64(reg_alloc.GetHostReg((GuestRegister)i.args[2].GetReg()));
|
||||
auto dst = Xbyak::Reg64(reg_alloc.GetHostReg((GuestRegister)i.args[0].GetReg(), true));
|
||||
generator->or_(src1, src2);
|
||||
MOV(dst, src1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void JitJump(IRInstruction& i)
|
||||
{
|
||||
if (i.args[0].IsReg() && i.args.size() == 1)
|
||||
{
|
||||
if (i.should_link)
|
||||
{
|
||||
auto lr = Xbyak::Reg64(reg_alloc.GetHostReg(REG_RA, true));
|
||||
MOV(lr, generator->r8);
|
||||
}
|
||||
|
||||
auto src = Xbyak::Reg64(reg_alloc.GetHostReg((GuestRegister)i.args[0].GetReg()));
|
||||
MOV(generator->r8, src);
|
||||
}
|
||||
else if (i.args[0].IsReg() && i.args.size() == 2)
|
||||
{
|
||||
assert(i.should_link);
|
||||
auto lr = Xbyak::Reg64(reg_alloc.GetHostReg((GuestRegister)i.args[0].GetReg(), true));
|
||||
MOV(lr, generator->r8);
|
||||
|
||||
auto src = Xbyak::Reg32(reg_alloc.GetHostReg((GuestRegister)i.args[1].GetReg()));
|
||||
generator->mov(generator->r8d, src);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (i.should_link)
|
||||
{
|
||||
auto lr = Xbyak::Reg64(reg_alloc.GetHostReg(REG_RA, true));
|
||||
MOV(lr, generator->r8);
|
||||
}
|
||||
|
||||
generator->and_(generator->r8, 0xF0000000);
|
||||
generator->or_(generator->r8, i.args[0].GetImm());
|
||||
}
|
||||
}
|
||||
|
||||
void JitAdd(IRInstruction instr)
|
||||
{
|
||||
if (instr.args[1].IsReg() && instr.args[2].IsImm() && instr.size == IRInstruction::Size32)
|
||||
{
|
||||
if (instr.args[1].GetReg() == 0)
|
||||
{
|
||||
auto dst = Xbyak::Reg32(reg_alloc.GetHostReg((GuestRegister)instr.args[0].GetReg(), true));
|
||||
auto imm = instr.args[2].GetImm();
|
||||
MOV(dst, imm);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto src = Xbyak::Reg32(reg_alloc.GetHostReg((GuestRegister)instr.args[1].GetReg()));
|
||||
auto dst = Xbyak::Reg32(reg_alloc.GetHostReg((GuestRegister)instr.args[0].GetReg(), true));
|
||||
auto imm = instr.args[2].GetImm();
|
||||
|
||||
MOV(generator->rdi, src);
|
||||
ADD(generator->rdi, imm);
|
||||
MOV(dst, generator->rdi);
|
||||
}
|
||||
}
|
||||
else if (instr.args[1].IsReg() && instr.args[2].IsReg() && instr.size == IRInstruction::Size64)
|
||||
{
|
||||
if (instr.args[1].GetReg() == 0 && instr.args[2].GetReg() == 0)
|
||||
{
|
||||
auto dst = Xbyak::Reg64(reg_alloc.GetHostReg((GuestRegister)instr.args[0].GetReg(), true));
|
||||
MOV(dst, 0);
|
||||
}
|
||||
else if (instr.args[1].GetReg() == 0)
|
||||
{
|
||||
auto dst = Xbyak::Reg64(reg_alloc.GetHostReg((GuestRegister)instr.args[0].GetReg(), true));
|
||||
auto imm = Xbyak::Reg64(reg_alloc.GetHostReg((GuestRegister)instr.args[2].GetReg()));
|
||||
MOV(dst, imm);
|
||||
}
|
||||
else if (instr.args[2].GetReg() == 0)
|
||||
{
|
||||
auto dst = Xbyak::Reg64(reg_alloc.GetHostReg((GuestRegister)instr.args[0].GetReg(), true));
|
||||
auto imm = Xbyak::Reg64(reg_alloc.GetHostReg((GuestRegister)instr.args[1].GetReg()));
|
||||
MOV(dst, imm);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto src = Xbyak::Reg64(reg_alloc.GetHostReg((GuestRegister)instr.args[1].GetReg()));
|
||||
auto dst = Xbyak::Reg64(reg_alloc.GetHostReg((GuestRegister)instr.args[0].GetReg(), true));
|
||||
auto src2 = Xbyak::Reg64(reg_alloc.GetHostReg((GuestRegister)instr.args[2].GetReg()));
|
||||
|
||||
MOV(generator->rdi, src);
|
||||
ADD(generator->rdi, src2);
|
||||
MOV(dst, generator->rdi);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("%d, %d\n", instr.args[1].type, instr.args[2].type);
|
||||
assert(0 && "Unknown add combo");
|
||||
}
|
||||
}
|
||||
|
||||
void JitStore(IRInstruction instr)
|
||||
{
|
||||
if (instr.access_size == IRInstruction::U32)
|
||||
{
|
||||
auto src = Xbyak::Reg32(reg_alloc.GetHostReg((GuestRegister)instr.args[2].GetReg()));
|
||||
auto dst = Xbyak::Reg32(reg_alloc.GetHostReg((GuestRegister)instr.args[0].GetReg()));
|
||||
auto imm = instr.args[1].GetImm();
|
||||
|
||||
SaveHostRegisters();
|
||||
|
||||
MOV(generator->rdi, src);
|
||||
ADD(generator->rdi, imm);
|
||||
MOV(generator->rsi, dst);
|
||||
MOV(generator->rcx, reinterpret_cast<uint64_t>(Bus::Write32));
|
||||
generator->call(generator->rcx);
|
||||
|
||||
RestoreHostRegisters();
|
||||
}
|
||||
else if (instr.access_size == IRInstruction::U64)
|
||||
{
|
||||
auto src = Xbyak::Reg32(reg_alloc.GetHostReg((GuestRegister)instr.args[2].GetReg()));
|
||||
auto dst = Xbyak::Reg64(reg_alloc.GetHostReg((GuestRegister)instr.args[0].GetReg()));
|
||||
auto imm = instr.args[1].GetImm();
|
||||
|
||||
SaveHostRegisters();
|
||||
|
||||
MOV(generator->rdi, src);
|
||||
ADD(generator->rdi, imm);
|
||||
MOV(generator->rsi, dst);
|
||||
MOV(generator->rcx, reinterpret_cast<uint64_t>(Bus::Write64));
|
||||
generator->call(generator->rcx);
|
||||
|
||||
RestoreHostRegisters();
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Unknown store access size %d\n", (int)instr.access_size);
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
void JitAnd(IRInstruction& i)
|
||||
{
|
||||
if (i.args[2].IsImm())
|
||||
{
|
||||
if (i.args[1].GetReg() == i.args[0].GetReg())
|
||||
{
|
||||
auto src = Xbyak::Reg64(reg_alloc.GetHostReg((GuestRegister)i.args[1].GetReg()));
|
||||
generator->and_(src, i.args[2].GetImm());
|
||||
return;
|
||||
}
|
||||
auto src = Xbyak::Reg32(reg_alloc.GetHostReg((GuestRegister)i.args[1].GetReg()));
|
||||
auto dst = Xbyak::Reg64(reg_alloc.GetHostReg((GuestRegister)i.args[0].GetReg(), true));
|
||||
|
||||
MOV(generator->edi, src);
|
||||
generator->and_(generator->edi, i.args[2].GetImm());
|
||||
generator->movsxd(dst, generator->edi);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("TODO: and\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void JitShift(IRInstruction i)
|
||||
{
|
||||
// SLL
|
||||
if (i.args[2].IsImm() && i.is_logical && i.direction == IRInstruction::Direction::Left)
|
||||
{
|
||||
auto src = Xbyak::Reg32(reg_alloc.GetHostReg((GuestRegister)i.args[1].GetReg()));
|
||||
auto dst = Xbyak::Reg32(reg_alloc.GetHostReg((GuestRegister)i.args[0].GetReg(), true));
|
||||
|
||||
MOV(generator->cl, i.args[2].GetImm());
|
||||
generator->shl(src, generator->cl);
|
||||
generator->mov(dst, src);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Invalid shift combo %d/%d/%d\n", i.args[2].type, i.is_logical, i.direction);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void JitMULT(IRInstruction i)
|
||||
{
|
||||
GuestRegister lo_reg = i.is_mmi_divmul ? GuestRegister::LO1 : GuestRegister::LO;
|
||||
GuestRegister hi_reg = i.is_mmi_divmul ? GuestRegister::HI1 : GuestRegister::HI;
|
||||
|
||||
auto src = Xbyak::Reg32(reg_alloc.GetHostReg((GuestRegister)i.args[1].GetReg()));
|
||||
auto src2 = Xbyak::Reg32(reg_alloc.GetHostReg((GuestRegister)i.args[2].GetReg()));
|
||||
auto lo = Xbyak::Reg64(reg_alloc.GetHostReg(lo_reg));
|
||||
auto hi = Xbyak::Reg64(reg_alloc.GetHostReg(hi_reg));
|
||||
|
||||
reg_alloc.InvalidateRegister(HostRegisters::RDX);
|
||||
|
||||
if (i.is_unsigned)
|
||||
{
|
||||
generator->movsxd(generator->rax, src);
|
||||
generator->mul(src2);
|
||||
generator->movsxd(lo, generator->eax);
|
||||
generator->movsxd(hi, generator->edx);
|
||||
|
||||
if (i.args[0].GetReg())
|
||||
{
|
||||
auto dst = Xbyak::Reg32(reg_alloc.GetHostReg((GuestRegister)i.args[0].GetReg()));
|
||||
MOV(dst, lo);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("TODO: MULT");
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
void JitIncPC()
|
||||
{
|
||||
generator->add(generator->r8, 4);
|
||||
ADD(generator->r8, 4);
|
||||
}
|
||||
|
||||
void EEJitX64::TranslateBlock(Block *block)
|
||||
{
|
||||
printf("Translating block at 0x%08x\n", block->addr);
|
||||
reg_alloc.Reset();
|
||||
|
||||
block->entryPoint = (blockEntry)generator->getCurr();
|
||||
|
||||
IRInstruction prev;
|
||||
|
||||
for (auto& i : block->instructions)
|
||||
{
|
||||
if (i.instr != 0x00)
|
||||
if (i.instr != PROLOGUE && i.instr != EPILOGUE && i.instr != BRANCH && prev.instr != JUMP)
|
||||
JitIncPC();
|
||||
|
||||
|
||||
switch (i.instr)
|
||||
{
|
||||
case 0x00:
|
||||
case NOP:
|
||||
generator->nop();
|
||||
break;
|
||||
case PROLOGUE:
|
||||
JitPrologue();
|
||||
break;
|
||||
case 0x01:
|
||||
case EPILOGUE:
|
||||
JitEpilogue();
|
||||
break;
|
||||
case 0x02:
|
||||
case MOVE:
|
||||
JitMov(i);
|
||||
break;
|
||||
case 0x03:
|
||||
case SLT:
|
||||
JitSlt(i);
|
||||
break;
|
||||
case 0x04:
|
||||
case BRANCH:
|
||||
JitBranch(i);
|
||||
break;
|
||||
case OR:
|
||||
JitOr(i);
|
||||
break;
|
||||
case JUMP:
|
||||
JitJump(i);
|
||||
break;
|
||||
case ADD:
|
||||
JitAdd(i);
|
||||
break;
|
||||
case STORE:
|
||||
JitStore(i);
|
||||
break;
|
||||
case AND:
|
||||
JitAnd(i);
|
||||
break;
|
||||
case SHIFT:
|
||||
JitShift(i);
|
||||
break;
|
||||
case MULT:
|
||||
JitMULT(i);
|
||||
break;
|
||||
default:
|
||||
printf("[EEJIT_X64]: Cannot emit unknown IR instruction 0x%02x\n", i.instr);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
prev = i;
|
||||
}
|
||||
}
|
||||
|
||||
void EEJitX64::CacheBlock(Block *block)
|
||||
{
|
||||
// TODO: Actually cache the block
|
||||
|
||||
if ((generator->getCurr() - generator->getCode()) >= (128*1024*1024))
|
||||
{
|
||||
delete[] generator;
|
||||
generator = new Xbyak::CodeGenerator(0xffffffff, (void*)base);
|
||||
}
|
||||
}
|
||||
|
||||
Block *EEJitX64::GetBlockForAddr(uint32_t addr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void EEJitX64::Initialize()
|
||||
{
|
||||
base = (uint8_t*)mmap(nullptr, 0xffffffff, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "GuestRegister.h"
|
||||
#include <cstdint>
|
||||
|
||||
struct Block;
|
||||
|
||||
|
@ -13,6 +14,8 @@ void JitLoadReg(GuestRegister reg, int hostReg);
|
|||
// Translate a JIT block from IR to host code
|
||||
// Modifies the `entry` pointer in the block
|
||||
void TranslateBlock(Block* block);
|
||||
void CacheBlock(Block* block);
|
||||
Block* GetBlockForAddr(uint32_t addr);
|
||||
|
||||
void Initialize();
|
||||
|
||||
|
|
|
@ -36,14 +36,15 @@ enum GuestRegister
|
|||
REG_SP,
|
||||
REG_FP,
|
||||
REG_RA,
|
||||
COP0_OFFS = REG_RA,
|
||||
REG_COP0_INDEX,
|
||||
COP0_OFFS,
|
||||
REG_COP0_INDEX = COP0_OFFS,
|
||||
REG_COP0_RANDOM,
|
||||
REG_COP0_ENTRYLO0,
|
||||
REG_COP0_ENTRYLO1,
|
||||
REG_COP0_CONTEXT,
|
||||
REG_COP0_PAGEMASK,
|
||||
REG_COP0_WIRED,
|
||||
_REG_UNUSED7,
|
||||
REG_COP0_BADVADDR,
|
||||
REG_COP0_COUNT,
|
||||
REG_COP0_ENTRYHI,
|
||||
|
@ -52,4 +53,9 @@ enum GuestRegister
|
|||
REG_COP0_CAUSE,
|
||||
REG_COP0_EPC,
|
||||
REG_COP0_PRID,
|
||||
REG_COP0_CONFIG,
|
||||
LO,
|
||||
HI,
|
||||
LO1,
|
||||
HI1,
|
||||
};
|
|
@ -2,26 +2,6 @@
|
|||
#include "EEJitx64.h"
|
||||
#include <emu/cpu/ee/EmotionEngine.h>
|
||||
|
||||
enum HostRegisters
|
||||
{
|
||||
RAX,
|
||||
RCX,
|
||||
RDX,
|
||||
RBX,
|
||||
RSP,
|
||||
RBP,
|
||||
RSI,
|
||||
RDI,
|
||||
R8,
|
||||
R9,
|
||||
R10,
|
||||
R11,
|
||||
R12,
|
||||
R13,
|
||||
R14,
|
||||
R15
|
||||
};
|
||||
|
||||
struct HostRegister
|
||||
{
|
||||
bool allocated;
|
||||
|
@ -64,9 +44,14 @@ size_t RegAllocatorX64::GetRegOffset(GuestRegister reg)
|
|||
case GuestRegister::REG_SP: return offsetof(EmotionEngine::ProcessorState, regs[29]);
|
||||
case GuestRegister::REG_FP: return offsetof(EmotionEngine::ProcessorState, regs[30]);
|
||||
case GuestRegister::REG_RA: return offsetof(EmotionEngine::ProcessorState, regs[31]);
|
||||
case GuestRegister::REG_COP0_PRID: return offsetof(EmotionEngine::ProcessorState, cop0_regs[15]);
|
||||
case GuestRegister::REG_COP0_INDEX ... GuestRegister::REG_COP0_CONFIG:
|
||||
return offsetof(EmotionEngine::ProcessorState, cop0_regs)+((reg - COP0_OFFS) * sizeof(uint32_t));
|
||||
case LO: return offsetof(EmotionEngine::ProcessorState, lo);
|
||||
case HI: return offsetof(EmotionEngine::ProcessorState, hi);
|
||||
case LO1: return offsetof(EmotionEngine::ProcessorState, lo1);
|
||||
case HI1: return offsetof(EmotionEngine::ProcessorState, hi1);
|
||||
default:
|
||||
printf("[REGALLOC_X64]: Couldn't find offset of unknown guest register %d\n", (int)reg);
|
||||
printf("[REGALLOC_X64]: Couldn't find offset of unknown guest register %d (%d)\n", (int)reg, (int)reg - 32);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
@ -104,7 +89,6 @@ int RegAllocatorX64::GetHostReg(GuestRegister reg, bool dest)
|
|||
// Find the least used register
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
printf("%d\n", regs[i].used);
|
||||
if (regs[i].used > hitsLeast)
|
||||
{
|
||||
hitsLeast = regs[i].used;
|
||||
|
@ -136,7 +120,18 @@ void RegAllocatorX64::DoWriteback()
|
|||
}
|
||||
}
|
||||
|
||||
RegAllocatorX64::RegAllocatorX64()
|
||||
void RegAllocatorX64::InvalidateRegister(HostRegisters reg)
|
||||
{
|
||||
if (regs[reg].allocated && regs[reg].mapping != GuestRegister::NONE)
|
||||
{
|
||||
EEJitX64::JitStoreReg(regs[reg].mapping);
|
||||
regs[reg].allocated = false;
|
||||
regs[reg].mapping = GuestRegister::NONE;
|
||||
regs[reg].used = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void RegAllocatorX64::Reset()
|
||||
{
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
|
@ -145,12 +140,13 @@ RegAllocatorX64::RegAllocatorX64()
|
|||
regs[i].used = 0;
|
||||
}
|
||||
|
||||
// Mark RSP, RBP, RAX, RDI, RSI, and R8 as used
|
||||
// Mark RSP, RBP, RAX, RDI, RSI, RCX, and R8 as used
|
||||
// RSP is used for the stack (and it should never be overwritten)
|
||||
// RBP is used to point to the processor state
|
||||
// RAX is used for return values
|
||||
// RDI and RSI are used for passing values to functions
|
||||
// R8 holds the current value of pc
|
||||
// RCX is for function pointers
|
||||
regs[RSP].allocated = true;
|
||||
regs[RSP].mapping = GuestRegister::NONE;
|
||||
regs[RSP].used = -1;
|
||||
|
@ -163,6 +159,10 @@ RegAllocatorX64::RegAllocatorX64()
|
|||
regs[RAX].mapping = GuestRegister::NONE;
|
||||
regs[RAX].used = -1;
|
||||
|
||||
regs[RCX].allocated = true;
|
||||
regs[RCX].mapping = GuestRegister::NONE;
|
||||
regs[RCX].used = -1;
|
||||
|
||||
regs[RDI].allocated = true;
|
||||
regs[RDI].mapping = GuestRegister::NONE;
|
||||
regs[RDI].used = -1;
|
||||
|
@ -175,3 +175,8 @@ RegAllocatorX64::RegAllocatorX64()
|
|||
regs[R8].mapping = GuestRegister::NONE;
|
||||
regs[R8].used = -1;
|
||||
}
|
||||
|
||||
RegAllocatorX64::RegAllocatorX64()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
|
|
@ -5,6 +5,26 @@
|
|||
|
||||
#include "GuestRegister.h"
|
||||
|
||||
enum HostRegisters
|
||||
{
|
||||
RAX,
|
||||
RCX,
|
||||
RDX,
|
||||
RBX,
|
||||
RSP,
|
||||
RBP,
|
||||
RSI,
|
||||
RDI,
|
||||
R8,
|
||||
R9,
|
||||
R10,
|
||||
R11,
|
||||
R12,
|
||||
R13,
|
||||
R14,
|
||||
R15
|
||||
};
|
||||
|
||||
class RegAllocatorX64
|
||||
{
|
||||
public:
|
||||
|
@ -13,4 +33,6 @@ public:
|
|||
int GetHostReg(GuestRegister reg, bool dest = false);
|
||||
size_t GetRegOffset(GuestRegister reg);
|
||||
void DoWriteback();
|
||||
void InvalidateRegister(HostRegisters reg);
|
||||
void Reset();
|
||||
};
|
|
@ -441,6 +441,8 @@ void Bus::Write32(uint32_t addr, uint32_t data)
|
|||
|
||||
addr = Translate(addr);
|
||||
|
||||
printf("Wrote 0x%08x to 0x%08x\n", data, addr);
|
||||
|
||||
if (addr == 0x10c0 && data == 0 && !firstTime)
|
||||
{
|
||||
printf("Writing 0 to 0x10c0\n");
|
||||
|
|
Loading…
Reference in a new issue