Skip to content
Permalink
Browse files

cpu: simplified LoadDelaySlot logic

  • Loading branch information
JaCzekanski committed Nov 21, 2019
1 parent 00fdbc1 commit bb08f6dacdf6b7c771f3bc2773df72a35a03d5c1
Showing with 60 additions and 69 deletions.
  1. +1 βˆ’1 src/bios/functions.cpp
  2. +5 βˆ’21 src/cpu/cpu.cpp
  3. +14 βˆ’7 src/cpu/cpu.h
  4. +33 βˆ’33 src/cpu/instructions.cpp
  5. +1 βˆ’1 src/state/state.cpp
  6. +3 βˆ’3 src/system.cpp
  7. +3 βˆ’3 src/utils/psf.cpp
@@ -83,7 +83,7 @@ inline bool haltSystem(System* sys) {
inline bool forceTTYOn(System* sys) {
(void)sys;
// Force TTY redirection (requires proper DUART implementation)
sys->cpu->reg[4] = 1;
sys->cpu->setReg(4, 1);
return true;
}

@@ -12,30 +12,14 @@ CPU::CPU(System* sys) : sys(sys), _opcode(0) {
hi = 0;
lo = 0;

for (auto& slot : slots) slot.reg = 0;
for (auto& line : icache) line.tag = 0;
for (auto& slot : slots) slot = {DUMMY_REG, 0};
for (auto& line : icache) line = {0, 0};
}

void CPU::loadDelaySlot(uint32_t r, uint32_t data) {
assert(r < REGISTER_COUNT);
if (r == 0) return;
if (r == slots[0].reg) slots[0].reg = 0; // Override previous write to same register

slots[1].reg = r;
slots[1].data = data;
slots[1].prevData = reg[r];
}

void CPU::moveLoadDelaySlots() {
if (slots[0].reg != 0) {
// If register contents has been changed during delay slot - ignore it
if (reg[slots[0].reg] == slots[0].prevData) {
reg[slots[0].reg] = slots[0].data;
}
}

INLINE void CPU::moveLoadDelaySlots() {
reg[slots[0].reg] = slots[0].data;
slots[0] = slots[1];
slots[1].reg = 0; // cancel
slots[1].reg = DUMMY_REG; // invalidate
}

void CPU::saveStateForException() {
@@ -43,11 +43,10 @@ r31 ra - return address
struct LoadSlot {
uint32_t reg;
uint32_t data;
uint32_t prevData;

template <class Archive>
void serialize(Archive& ar) {
ar(reg, data, prevData);
ar(reg, data);
}
};

@@ -65,6 +64,7 @@ struct CacheLine {

struct CPU {
inline static const int REGISTER_COUNT = 32;
#define DUMMY_REG 32 // Used as dummy Load Delay slot

// Saved state for exception handling
uint32_t exceptionPC;
@@ -77,7 +77,7 @@ struct CPU {
bool branchTaken; // If CPU is in Branch Delay slot, was the branch taken
LoadSlot slots[2]; // Load Delay slots

uint32_t reg[REGISTER_COUNT];
uint32_t reg[REGISTER_COUNT + 1];
uint32_t hi, lo;
COP0 cop0;
GTE gte;
@@ -89,15 +89,22 @@ struct CPU {

CPU(System* sys);
void checkForInterrupts();
void loadDelaySlot(uint32_t r, uint32_t data);
void moveLoadDelaySlots();
INLINE void loadAndInvalidate(uint32_t r, uint32_t data) {
INLINE void moveLoadDelaySlots();
INLINE void loadDelaySlot(uint32_t r, uint32_t data) {
if (r == 0) return;
if (r == slots[0].reg) {
slots[0].reg = DUMMY_REG; // Override previous write to same register
}

slots[1] = {r, data};
}
INLINE void setReg(uint32_t r, uint32_t data) {
if (r == 0) return;
reg[r] = data;

// Invalidate
if (slots[0].reg == r) {
slots[0].reg = 0;
slots[0].reg = DUMMY_REG;
}
}
INLINE void jump(uint32_t address) {
@@ -223,27 +223,27 @@ void special(CPU *cpu, Opcode i) {

// Shift Word Left Logical
// SLL rd, rt, a
void op_sll(CPU *cpu, Opcode i) { cpu->loadAndInvalidate(i.rd, cpu->reg[i.rt] << i.sh); }
void op_sll(CPU *cpu, Opcode i) { cpu->setReg(i.rd, cpu->reg[i.rt] << i.sh); }

// Shift Word Right Logical
// SRL rd, rt, a
void op_srl(CPU *cpu, Opcode i) { cpu->loadAndInvalidate(i.rd, cpu->reg[i.rt] >> i.sh); }
void op_srl(CPU *cpu, Opcode i) { cpu->setReg(i.rd, cpu->reg[i.rt] >> i.sh); }

// Shift Word Right Arithmetic
// SRA rd, rt, a
void op_sra(CPU *cpu, Opcode i) { cpu->loadAndInvalidate(i.rd, ((int32_t)cpu->reg[i.rt]) >> i.sh); }
void op_sra(CPU *cpu, Opcode i) { cpu->setReg(i.rd, ((int32_t)cpu->reg[i.rt]) >> i.sh); }

// Shift Word Left Logical Variable
// SLLV rd, rt, rs
void op_sllv(CPU *cpu, Opcode i) { cpu->loadAndInvalidate(i.rd, cpu->reg[i.rt] << (cpu->reg[i.rs] & 0x1f)); }
void op_sllv(CPU *cpu, Opcode i) { cpu->setReg(i.rd, cpu->reg[i.rt] << (cpu->reg[i.rs] & 0x1f)); }

// Shift Word Right Logical Variable
// SRLV rd, rt, a
void op_srlv(CPU *cpu, Opcode i) { cpu->loadAndInvalidate(i.rd, cpu->reg[i.rt] >> (cpu->reg[i.rs] & 0x1f)); }
void op_srlv(CPU *cpu, Opcode i) { cpu->setReg(i.rd, cpu->reg[i.rt] >> (cpu->reg[i.rs] & 0x1f)); }

// Shift Word Right Arithmetic Variable
// SRAV rd, rt, rs
void op_srav(CPU *cpu, Opcode i) { cpu->loadAndInvalidate(i.rd, ((int32_t)cpu->reg[i.rt]) >> (cpu->reg[i.rs] & 0x1f)); }
void op_srav(CPU *cpu, Opcode i) { cpu->setReg(i.rd, ((int32_t)cpu->reg[i.rt]) >> (cpu->reg[i.rs] & 0x1f)); }

// Jump Register
// JR rs
@@ -263,7 +263,7 @@ void op_jr(CPU *cpu, Opcode i) {
void op_jalr(CPU *cpu, Opcode i) {
uint32_t addr = cpu->reg[i.rs];
cpu->inBranchDelay = true;
cpu->loadAndInvalidate(i.rd, cpu->nextPC);
cpu->setReg(i.rd, cpu->nextPC);
if (unlikely(addr & 3)) {
cpu->cop0.bada = addr;
exception(cpu, COP0::CAUSE::Exception::addressErrorLoad);
@@ -291,15 +291,15 @@ void op_break(CPU *cpu, Opcode i) {

// Move From Hi
// MFHI rd
void op_mfhi(CPU *cpu, Opcode i) { cpu->loadAndInvalidate(i.rd, cpu->hi); }
void op_mfhi(CPU *cpu, Opcode i) { cpu->setReg(i.rd, cpu->hi); }

// Move To Hi
// MTHI rd
void op_mthi(CPU *cpu, Opcode i) { cpu->hi = cpu->reg[i.rs]; }

// Move From Lo
// MFLO rd
void op_mflo(CPU *cpu, Opcode i) { cpu->loadAndInvalidate(i.rd, cpu->lo); }
void op_mflo(CPU *cpu, Opcode i) { cpu->setReg(i.rd, cpu->lo); }

// Move To Lo
// MTLO rd
@@ -362,12 +362,12 @@ void op_add(CPU *cpu, Opcode i) {
exception(cpu, COP0::CAUSE::Exception::arithmeticOverflow);
return;
}
cpu->loadAndInvalidate(i.rd, result);
cpu->setReg(i.rd, result);
}

// Add unsigned
// addu rd, rs, rt
void op_addu(CPU *cpu, Opcode i) { cpu->loadAndInvalidate(i.rd, cpu->reg[i.rs] + cpu->reg[i.rt]); }
void op_addu(CPU *cpu, Opcode i) { cpu->setReg(i.rd, cpu->reg[i.rs] + cpu->reg[i.rt]); }

// Subtract
// sub rd, rs, rt
@@ -380,45 +380,45 @@ void op_sub(CPU *cpu, Opcode i) {
exception(cpu, COP0::CAUSE::Exception::arithmeticOverflow);
return;
}
cpu->loadAndInvalidate(i.rd, result);
cpu->setReg(i.rd, result);
}

// Subtract unsigned
// subu rd, rs, rt
void op_subu(CPU *cpu, Opcode i) { cpu->loadAndInvalidate(i.rd, cpu->reg[i.rs] - cpu->reg[i.rt]); }
void op_subu(CPU *cpu, Opcode i) { cpu->setReg(i.rd, cpu->reg[i.rs] - cpu->reg[i.rt]); }

// And
// and rd, rs, rt
void op_and(CPU *cpu, Opcode i) { cpu->loadAndInvalidate(i.rd, cpu->reg[i.rs] & cpu->reg[i.rt]); }
void op_and(CPU *cpu, Opcode i) { cpu->setReg(i.rd, cpu->reg[i.rs] & cpu->reg[i.rt]); }

// Or
// OR rd, rs, rt
void op_or(CPU *cpu, Opcode i) { cpu->loadAndInvalidate(i.rd, cpu->reg[i.rs] | cpu->reg[i.rt]); }
void op_or(CPU *cpu, Opcode i) { cpu->setReg(i.rd, cpu->reg[i.rs] | cpu->reg[i.rt]); }

// Xor
// XOR rd, rs, rt
void op_xor(CPU *cpu, Opcode i) { cpu->loadAndInvalidate(i.rd, cpu->reg[i.rs] ^ cpu->reg[i.rt]); }
void op_xor(CPU *cpu, Opcode i) { cpu->setReg(i.rd, cpu->reg[i.rs] ^ cpu->reg[i.rt]); }

// Nor
// NOR rd, rs, rt
void op_nor(CPU *cpu, Opcode i) { cpu->loadAndInvalidate(i.rd, ~(cpu->reg[i.rs] | cpu->reg[i.rt])); }
void op_nor(CPU *cpu, Opcode i) { cpu->setReg(i.rd, ~(cpu->reg[i.rs] | cpu->reg[i.rt])); }

// Set On Less Than Signed
// SLT rd, rs, rt
void op_slt(CPU *cpu, Opcode i) {
if ((int32_t)cpu->reg[i.rs] < (int32_t)cpu->reg[i.rt])
cpu->loadAndInvalidate(i.rd, 1);
cpu->setReg(i.rd, 1);
else
cpu->loadAndInvalidate(i.rd, 0);
cpu->setReg(i.rd, 0);
}

// Set On Less Than Unsigned
// SLTU rd, rs, rt
void op_sltu(CPU *cpu, Opcode i) {
if (cpu->reg[i.rs] < cpu->reg[i.rt])
cpu->loadAndInvalidate(i.rd, 1);
cpu->setReg(i.rd, 1);
else
cpu->loadAndInvalidate(i.rd, 0);
cpu->setReg(i.rd, 0);
}

/**
@@ -444,7 +444,7 @@ void branch(CPU *cpu, Opcode i) {
condition = (int32_t)cpu->reg[i.rs] >= 0;
}

if (link) cpu->reg[31] = cpu->nextPC;
if (link) cpu->setReg(31, cpu->nextPC);

cpu->inBranchDelay = true;
if (condition) {
@@ -463,7 +463,7 @@ void op_j(CPU *cpu, Opcode i) {
// JAL target
void op_jal(CPU *cpu, Opcode i) {
cpu->inBranchDelay = true;
cpu->reg[31] = cpu->nextPC;
cpu->setReg(31, cpu->nextPC);
cpu->jump((cpu->nextPC & 0xf0000000) | (i.target * 4));
}

@@ -514,46 +514,46 @@ void op_addi(CPU *cpu, Opcode i) {
exception(cpu, COP0::CAUSE::Exception::arithmeticOverflow);
return;
}
cpu->loadAndInvalidate(i.rt, result);
cpu->setReg(i.rt, result);
}

// Add Immediate Unsigned Word
// ADDIU rt, rs, imm
void op_addiu(CPU *cpu, Opcode i) { cpu->loadAndInvalidate(i.rt, cpu->reg[i.rs] + i.offset); }
void op_addiu(CPU *cpu, Opcode i) { cpu->setReg(i.rt, cpu->reg[i.rs] + i.offset); }

// Set On Less Than Immediate
// SLTI rd, rs, rt
void op_slti(CPU *cpu, Opcode i) {
if ((int32_t)cpu->reg[i.rs] < (int32_t)i.offset)
cpu->loadAndInvalidate(i.rt, 1);
cpu->setReg(i.rt, 1);
else
cpu->loadAndInvalidate(i.rt, 0);
cpu->setReg(i.rt, 0);
}

// Set On Less Than Immediate Unsigned
// SLTIU rd, rs, rt
void op_sltiu(CPU *cpu, Opcode i) {
if (cpu->reg[i.rs] < (uint32_t)i.offset)
cpu->loadAndInvalidate(i.rt, 1);
cpu->setReg(i.rt, 1);
else
cpu->loadAndInvalidate(i.rt, 0);
cpu->setReg(i.rt, 0);
}

// And Immediate
// ANDI rt, rs, imm
void op_andi(CPU *cpu, Opcode i) { cpu->loadAndInvalidate(i.rt, cpu->reg[i.rs] & i.imm); }
void op_andi(CPU *cpu, Opcode i) { cpu->setReg(i.rt, cpu->reg[i.rs] & i.imm); }

// Or Immediete
// ORI rt, rs, imm
void op_ori(CPU *cpu, Opcode i) { cpu->loadAndInvalidate(i.rt, cpu->reg[i.rs] | i.imm); }
void op_ori(CPU *cpu, Opcode i) { cpu->setReg(i.rt, cpu->reg[i.rs] | i.imm); }

// Xor Immediate
// XORI rt, rs, imm
void op_xori(CPU *cpu, Opcode i) { cpu->loadAndInvalidate(i.rt, cpu->reg[i.rs] ^ i.imm); }
void op_xori(CPU *cpu, Opcode i) { cpu->setReg(i.rt, cpu->reg[i.rs] ^ i.imm); }

// Load Upper Immediate
// LUI rt, imm
void op_lui(CPU *cpu, Opcode i) { cpu->loadAndInvalidate(i.rt, i.imm << 16); }
void op_lui(CPU *cpu, Opcode i) { cpu->setReg(i.rt, i.imm << 16); }

// Coprocessor zero
void op_cop0(CPU *cpu, Opcode i) {
@@ -21,7 +21,7 @@ const std::string basePath = "data/state/";
const std::string lastSavePath = basePath + "last.state";

struct StateMetadata {
inline static const uint32_t SAVESTATE_VERSION = 2;
inline static const uint32_t SAVESTATE_VERSION = 3;

uint32_t version = SAVESTATE_VERSION;
std::string biosPath;
@@ -430,11 +430,11 @@ bool System::loadExeFile(const std::vector<uint8_t>& _exe) {
}

cpu->setPC(exe.pc0);
cpu->reg[28] = exe.gp0;
cpu->setReg(28, exe.gp0);

if (exe.s_addr != 0) {
cpu->reg[29] = exe.s_addr + exe.s_size;
cpu->reg[30] = exe.s_addr + exe.s_size;
cpu->setReg(29, exe.s_addr + exe.s_size);
cpu->setReg(30, exe.s_addr + exe.s_size);
}

cpu->inBranchDelay = false;
@@ -35,10 +35,10 @@ bool loadExe(System* sys, const std::vector<uint8_t>& _exe, PsfType type) {
}

if (type == PsfType::Main || type == PsfType::MainLib) {
for (int i = 0; i < 32; i++) sys->cpu->reg[i] = 0;
for (int i = 0; i < 32; i++) sys->cpu->setReg(i, 0);
sys->cpu->setPC(exe.pc0);
sys->cpu->reg[28] = exe.gp0;
sys->cpu->reg[29] = exe.s_addr;
sys->cpu->setReg(28, exe.gp0);
sys->cpu->setReg(29, exe.s_addr);
if (sys->cpu->reg[29] == 0) sys->cpu->reg[29] = 0x801ffff0;

sys->cpu->inBranchDelay = false;

0 comments on commit bb08f6d

Please sign in to comment.
You can’t perform that action at this time.