More opcodes

This commit is contained in:
google0101-ryan 2024-01-15 01:25:00 -05:00
parent 4aa328c107
commit 079564dce8
11 changed files with 921 additions and 142 deletions

View file

@ -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);

View file

@ -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()

View file

@ -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;
};

View file

@ -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()

View file

@ -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)

View file

@ -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);

View file

@ -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();

View file

@ -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,
};

View file

@ -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();
}

View file

@ -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();
};

View file

@ -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");