Newer
Older
#include <cpu/ee.hpp>
#include <common/manager.hpp>
#include <bitset>
#include <iostream>
EmotionEngine::EmotionEngine(ComponentManager* parent)
{
}
void EmotionEngine::tick()
{
/* Fetch next instruction. */
}
void EmotionEngine::reset_state()
{
/* Reset PC. */
pc = 0xbfc00000;
instr.value = instr.pc = 0;
next_instr.value = next_instr.pc = 0;
}
void EmotionEngine::fetch_instruction()
{
/* Handle branch delay slots by prefetching the next one */
instr = next_instr;
next_instr.value = read<uint32_t>(pc);
next_instr.pc = pc;
/* Update PC */
fmt::print("PC: {:#x} instruction: {:#x} ", instr.pc, instr.value);
if (skip_branch_delay)
{
skip_branch_delay = false;
std::cout << "SKIPPED delay slot\n";
return;
}
switch(instr.opcode)
{
case COP0_OPCODE: op_cop0(); break;
case SPECIAL_OPCODE: op_special(); break;
case 0b101011: op_sw(); break;
case 0b001010: op_slti(); break;
case 0b001101: op_ori(); break;
case 0b001000: op_addi(); break;
case 0b011110: op_lq(); break;
case 0b001111: op_lui(); break;
case 0b001001: op_addiu(); break;
case 0b011100: op_mmi(); break;
case 0b111111: op_sd(); break;
case 0b000011: op_jal(); break;
case 0b000001: op_regimm(); break;
case 0b001100: op_andi(); break;
case 0b000100: op_beq(); break;
case 0b010100: op_beql(); break;
case 0b001011: op_sltiu(); break;
case 0b010101: op_bnel(); break;
fmt::print("[ERROR] Unimplemented opcode: {:#06b}\n", instr.opcode & 0x3F);
template <typename T>
T EmotionEngine::read(uint32_t addr)
{
if (addr >= 0x70000000 && addr < 0x70004000) /* Read from scratchpad */
return *(uint32_t*)&scratchpad[addr & 0x3FFC];
else
return manager->read<T>(addr);
}
template <typename T>
void EmotionEngine::write(uint32_t addr, T data)
{
if (addr >= 0x70000000 && addr < 0x70004000)
*(uint32_t*)&scratchpad[addr & 0x3FFC] = data;
else
manager->write<T>(addr, data);
}
void EmotionEngine::op_cop0()
{
/* The manual says that COP0 is only accessible in kernel mode
There are exceptions but we will deal with those later. */
if (cop0.get_operating_mode() != OperatingMode::KERNEL_MODE)
return;
uint16_t type = instr.value >> 21 & 0x1F;
switch (type)
switch(instr.value & 0x7)
{
case 0b000: op_mfc0(); break;
default:
fmt::print("[ERROR] Unimplemented COP0 MF0 instruction: {:#03b}\n", instr.value & 0x7);
std::exit(1);
}
break;
case COP0_MT0:
switch (instr.value & 0x7)
{
case 0b000: op_mtc0(); break;
default:
fmt::print("[ERROR] Unimplemented COP0 MT0 instruction: {:#03b}\n", instr.value & 0x7);
std::exit(1);
}
break;
case COP0_TLB:
if ((instr.value & 0x3F) == 0b000010) op_tlbwi();
break;
fmt::print("[ERROR] Unimplemented COP0 instruction: {:#05b}\n", type);
std::exit(1);
}
}
void EmotionEngine::op_mfc0()
{
uint16_t rd = (instr.value >> 11) & 0x1F;
uint16_t rt = (instr.value >> 16) & 0x1F;
fmt::print("MFC0: GPR[{:d}] = COP0_REG[{:d}] ({:#x})\n", rt, rd, cop0.regs[rd]);
}
void EmotionEngine::op_special()
{
switch(instr.r_type.funct)
{
case 0b000000: op_sll(); break;
case 0b001000: op_jr(); break;
case 0b001001: op_jalr(); break;
case 0b000011: op_sra(); break;
case 0b100001: op_addu(); break;
case 0b101101: op_daddu(); break;
case 0b100101: op_or(); break;
case 0b011000: op_mult(); break;
case 0b011011: op_divu(); break;
case 0b010010: op_mflo(); break;
fmt::print("[ERROR] Unimplemented SPECIAL instruction: {:#06b}\n", (uint16_t)instr.r_type.funct);
void EmotionEngine::op_regimm()
{
uint16_t type = (instr.value >> 16) & 0x1F;
switch(type)
{
case 0b00001: op_bgez(); break;
default:
fmt::print("[ERROR] Unimplemented REGIMM instruction: {:#05b}\n", type);
void EmotionEngine::op_sw()
{
uint16_t base = instr.i_type.rs;
uint16_t rt = instr.i_type.rt;
int16_t offset = (int16_t)instr.i_type.immediate;
uint32_t data = gpr[rt].word[0];
if ((vaddr & 0b11) != 0)
fmt::print("[ERROR] SW: Address {:#x} is not aligned\n", vaddr);
std::exit(1); /* NOTE: SignalException (AddressError) */
}
fmt::print("SW: Writing GPR[{:d}] ({:#x}) to address {:#x} = GPR[{:d}] ({:#x}) + {:d}\n", rt, data, vaddr, base, gpr[base].word[0], offset);
}
void EmotionEngine::op_sll()
{
uint16_t rt = instr.r_type.rt;
uint16_t rd = instr.r_type.rd;
gpr[rd].dword[0] = (uint64_t)(int32_t)(gpr[rt].word[0] << sa);
if (instr.value == 0) fmt::print("NOP\n");
else fmt::print("SLL: GPR[{:d}] = GPR[{:d}] ({:#x}) << {:d}\n", rd, rt, gpr[rd].dword[0], sa);
}
void EmotionEngine::op_slti()
{
uint16_t rs = instr.i_type.rs;
uint16_t rt = instr.i_type.rt;
int64_t imm = (int64_t)(int16_t)instr.i_type.immediate;
gpr[rt].dword[0] = (int64_t)gpr[rs].dword[0] < imm;
fmt::print("SLTI: GPR[{:d}] = GPR[{:d}] ({:#x}) < {:d}\n", rt, rs, gpr[rs].dword[0], imm);
}
void EmotionEngine::op_bne()
{
uint16_t rt = instr.i_type.rt;
uint16_t rs = instr.i_type.rs;
int32_t imm = (int16_t)instr.i_type.immediate;
int32_t offset = imm << 2;
fmt::print("BNE: IF GPR[{:d}] ({:#x}) != GPR[{:d}] ({:#x}) THEN PC += {:#x}\n", rt, gpr[rt].dword[0], rs, gpr[rs].dword[0], offset);
}
void EmotionEngine::op_ori()
{
uint16_t rs = instr.i_type.rs;
uint16_t rt = instr.i_type.rt;
fmt::print("ORI: GPR[{:d}] = GPR[{:d}] ({:#x}) | {:#x}\n", rt, rs, gpr[rs].dword[0], imm);
}
void EmotionEngine::op_addi()
{
uint16_t rs = instr.i_type.rs;
uint16_t rt = instr.i_type.rt;
int16_t imm = (int16_t)instr.i_type.immediate;
/* TODO: Overflow detection */
fmt::print("ADDI: GPR[{:d}] = GPR[{:d}] ({:#x}) + {:#x}\n", rt, rs, gpr[rs].dword[0], imm);
}
void EmotionEngine::op_lq()
{
uint16_t rt = instr.i_type.rt;
uint16_t base = instr.i_type.rs;
int16_t imm = (int16_t)instr.i_type.immediate;
uint32_t vaddr = (gpr[base].word[0] + imm) & 0b0000;
gpr[rt].dword[0] = read<uint64_t>(vaddr);
gpr[rt].dword[1] = read<uint64_t>(vaddr + 8);
fmt::print("LQ: GPR[{:d}] = {:#x} from address {:#x} = GPR[{:d}] ({:#x} + {:#x}\n", rt, gpr[rt].dword[0], vaddr, base, gpr[base].word[0], imm);
}
void EmotionEngine::op_lui()
{
uint16_t rt = instr.i_type.rt;
uint32_t imm = instr.i_type.immediate;
gpr[rt].dword[0] = (int64_t)(int32_t)(imm << 16);
fmt::print("LUI: GPR[{:d}] = {:#x}\n", rt, imm);
}
void EmotionEngine::op_jr()
{
uint16_t rs = instr.i_type.rs;
pc = gpr[rs].word[0];
fmt::print("JR: Jumped to GPR[{:d}] = {:#x}\n", rs, pc);
}
void EmotionEngine::op_sync()
{
fmt::print("SYNC\n");
}
void EmotionEngine::op_addiu()
{
uint16_t rt = instr.i_type.rt;
uint16_t rs = instr.i_type.rs;
int16_t imm = (int16_t)instr.i_type.immediate;
int64_t temp = gpr[rs].dword[0] + (int64_t)imm;
gpr[rt].dword[0] = (int64_t)(int32_t)(temp & 0xFFFFFFFF);
fmt::print("ADDIU: GPR[{:d}] = GPR[{:d}] ({:#x}) + {:#x}\n", rt, rs, gpr[rs].dword[0], imm);
}
void EmotionEngine::op_tlbwi()
{
}
void EmotionEngine::op_mtc0()
{
uint16_t rt = (instr.value >> 16) & 0x1F;
uint16_t rd = (instr.value >> 11) & 0x1F;
cop0.regs[rd] = gpr[rt].word[0];
fmt::print("MTC0: COP0[{:d}] = GPR[{:d}] ({:#x})\n", rd, rt, gpr[rt].word[0]);
}
void EmotionEngine::op_mmi()
{
switch(instr.r_type.sa)
{
case 0b10000: op_madd1(); break;
default:
fmt::print("[ERROR] Unimplemented MMI instruction: {:#05b}\n", (uint16_t)instr.r_type.sa);
std::exit(1);
}
}
void EmotionEngine::op_madd1()
{
uint16_t rd = instr.r_type.rd;
uint16_t rs = instr.r_type.rs;
uint16_t rt = instr.r_type.rt;
uint64_t lo = lo1 & 0xFFFFFFFF;
uint64_t hi = hi1 & 0xFFFFFFFF;
int64_t result = (hi << 32 | lo) + (int64_t)gpr[rs].word[0] * (int64_t)gpr[rt].word[0];
lo1 = (int64_t)(int32_t)(result & 0xFFFFFFFF);
hi1 = (int64_t)(int32_t)(result >> 32);
fmt::print("MADD1: GPR[{:d}] = LO1 = {:#x} and HI1 = {:#x}\n", lo1, hi1);
}
void EmotionEngine::op_jalr()
{
uint16_t rs = instr.r_type.rs;
uint16_t rd = instr.r_type.rd;
gpr[rd].dword[0] = pc + 4; /* Normally this should be PC + 8 but we compensate for the prefetching with -4 */
pc = gpr[rs].word[0];
fmt::print("JALR: Jumping to PC = GPR[{:d}] ({:#x}) with link address {:#x}\n", rs, pc, gpr[rs].dword[0]);
}
void EmotionEngine::op_sd()
{
uint16_t base = instr.i_type.rs;
uint16_t rt = instr.i_type.rt;
int16_t offset = (int16_t)instr.i_type.immediate;
uint32_t vaddr = offset + gpr[base].word[0];
fmt::print("[ERROR] SD: Address {:#x} is not aligned\n", vaddr);
std::exit(1); /* NOTE: SignalException (AddressError) */
}
fmt::print("SD: Writing GPR[{:d}] ({:#x}) to address {:#x} = GPR[{:d}] ({:#x}) + {:#x}\n", rt, data, vaddr, base, gpr[base].word[0], offset);
void EmotionEngine::op_jal()
{
uint32_t instr_index = instr.j_type.target;
pc = (pc & 0xF0000000) | (instr_index << 2);
fmt::print("JAL: Jumping to PC = {:#x}\n", pc);
}
void EmotionEngine::op_sra()
{
uint16_t sa = instr.r_type.sa;
uint16_t rd = instr.r_type.rd;
uint16_t rt = instr.r_type.rt;
gpr[rd].dword[0] = (int64_t)(gpr[rt].word[0] >> sa);
fmt::print("SRA: GPR[{:d}] = GPR[{:d}] ({:#x}) >> {:d}\n", rd, rt, gpr[rt].word[0], sa);
}
void EmotionEngine::op_bgez()
{
fmt::print("BGEZ: IF GPR[{:d}] ({:#x}) > 0 THEN PC += {:#x}\n", rs, gpr[rs].word[0], offset);
}
void EmotionEngine::op_addu()
{
uint16_t rt = instr.r_type.rt;
uint16_t rs = instr.r_type.rs;
uint16_t rd = instr.r_type.rd;
gpr[rd].dword[0] = (int32_t)(gpr[rs].dword[0] + gpr[rt].dword[0]);
fmt::print("ADDU: GPR[{:d}] = GPR[{:d}] ({:#x}) + GPR[{:d}] ({:#x})\n", rd, rs, gpr[rs].dword[0], rt, gpr[rt].dword[0]);
}
void EmotionEngine::op_daddu()
{
uint16_t rt = instr.r_type.rt;
uint16_t rs = instr.r_type.rs;
uint16_t rd = instr.r_type.rd;
gpr[rd].dword[0] = gpr[rs].dword[0] + gpr[rt].dword[0];
fmt::print("DADDU: GPR[{:d}] = GPR[{:d}] ({:#x}) + GPR[{:d}] ({:#x})\n", rd, rs, gpr[rs].dword[0], rt, gpr[rt].dword[0]);
}
void EmotionEngine::op_andi()
{
uint16_t rt = instr.i_type.rt;
uint16_t rs = instr.i_type.rs;
uint64_t imm = instr.i_type.immediate;
gpr[rt].dword[0] = gpr[rs].dword[0] & imm;
fmt::print("ANDI: GPR[{:d}] = GPR[{:d}] ({:#x}) & {:#x}\n", rt, rs, gpr[rs].dword[0], imm);
}
void EmotionEngine::op_beq()
{
uint16_t rt = instr.i_type.rt;
uint16_t rs = instr.i_type.rs;
uint32_t imm = instr.i_type.immediate;
int32_t offset = (int32_t)(imm << 2);
if (gpr[rs].dword[0] == gpr[rt].dword[0])
pc += offset;
fmt::print("BEQ: IF GPR[{:d}] ({:#x}) == GPR[{:d}] ({:#x}) THEN PC += {:#x}\n", rt, gpr[rt].dword[0], rs, gpr[rs].dword[0], offset);
}
void EmotionEngine::op_or()
{
uint16_t rt = instr.r_type.rt;
uint16_t rs = instr.r_type.rs;
uint16_t rd = instr.r_type.rd;
gpr[rd].dword[0] = gpr[rs].dword[0] | gpr[rt].dword[0];
fmt::print("ORI: GPR[{:d}] = GPR[{:d}] ({:#x}) | GPR[{:d}] ({:#x})\n", rd, rs, gpr[rs].dword[0], rt, gpr[rt].dword[0]);
}
void EmotionEngine::op_mult()
{
uint16_t rt = instr.r_type.rt;
uint16_t rs = instr.r_type.rs;
uint16_t rd = instr.r_type.rd;
int64_t result = (int64_t)gpr[rs].word[0] * (int64_t)gpr[rt].word[0];
gpr[rd].dword[0] = lo0 = (int64_t)(int32_t)(result & 0xFFFFFFFF);
hi0 = (int64_t)(int32_t)(result >> 32);
fmt::print("MULT: GPR[{:d}] = LO0 = {:#x} and HI0 = {:#x}\n", rd, lo0, hi0);
}
void EmotionEngine::op_divu()
{
uint16_t rt = instr.i_type.rt;
uint16_t rs = instr.i_type.rs;
if (gpr[rt].word[0] != 0)
{
lo0 = gpr[rs].word[0] / gpr[rt].word[0];
hi0 = gpr[rs].word[0] % gpr[rt].word[0];
fmt::print("DIVU: LO0 = GPR[{:d}] ({:#x}) / GPR[{:d}] ({:#x})\n", rs, gpr[rs].word[0], rt, gpr[rt].word[0]);
std::abort();
}
}
void EmotionEngine::op_beql()
{
uint16_t rt = instr.i_type.rt;
uint16_t rs = instr.i_type.rs;
uint32_t imm = instr.i_type.immediate;
int32_t offset = (int32_t)(imm << 2);
if (gpr[rs].dword[0] == gpr[rt].dword[0])
pc += offset;
else
skip_branch_delay = true;
fmt::print("BEQL: IF GPR[{:d}] ({:#x}) == GPR[{:d}] ({:#x}) THEN PC += {:#x}\n", rs, gpr[rs].dword[0], rt, gpr[rt].dword[0], offset);
}
void EmotionEngine::op_mflo()
{
uint16_t rd = instr.r_type.rd;
gpr[rd].dword[0] = lo0;
fmt::print("MFLO: GPR[{:d}] = LO0 ({:#x})\n", rd, lo0);
}
void EmotionEngine::op_sltiu()
{
uint16_t rt = instr.i_type.rt;
uint16_t rs = instr.i_type.rs;
uint64_t imm = instr.i_type.immediate;
gpr[rt].dword[0] = (gpr[rs].dword[0] < imm);
fmt::print("SLTIU: GPR[{:d}] = GPR[{:d}] ({:#x}) < {:#x}\n", rt, rs, gpr[rs].dword[0], imm);
}
void EmotionEngine::op_bnel()
{
uint16_t rt = instr.i_type.rt;
uint16_t rs = instr.i_type.rs;
uint32_t imm = instr.i_type.immediate;
int32_t offset = (int32_t)(imm << 2);
if (gpr[rs].dword[0] != gpr[rt].dword[0])
pc += offset;
else
skip_branch_delay = true;
fmt::print("BNEL: IF GPR[{:d}] ({:#x}) != GPR[{:d}] ({:#x}) THEN PC += {:#x}\n", rs, gpr[rs].dword[0], rt, gpr[rt].dword[0], offset);
/* Template definitions. */
template uint32_t EmotionEngine::read<uint32_t>(uint32_t);
template uint64_t EmotionEngine::read<uint64_t>(uint32_t);
template void EmotionEngine::write<uint32_t>(uint32_t, uint32_t);
template void EmotionEngine::write<uint64_t>(uint32_t, uint64_t);