Merge pull request #11 from liuk7071/rewrite_interrupts

Fix interrupts and exceptions
This commit is contained in:
liuk707 2023-08-07 22:02:13 +02:00 committed by GitHub
commit 5028e5a954
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 105 additions and 11 deletions

View file

@ -2,6 +2,9 @@
void Interpreter::step(CpuCore* core, Memory* mem, Disassembler* disassembler) {
// Handle interrupts
core->checkInterrupt(mem->interrupt);
CpuCore::Instruction instr = { .raw = mem->read<u32>(core->pc) };
disassembler->disassemble(instr, core);
@ -69,8 +72,11 @@ void Interpreter::step(CpuCore* core, Memory* mem, Disassembler* disassembler) {
break;
}
case CpuOpcodes::SPECIALOpcode::SYSCALL: {
core->pc -= 4;
core->exception(CpuCore::Exception::SysCall);
core->exception(CpuCore::Exception::SysCall, true);
break;
}
case CpuOpcodes::SPECIALOpcode::BREAK: {
core->exception(CpuCore::Exception::Break, true);
break;
}
case CpuOpcodes::SPECIALOpcode::MFHI: {
@ -334,6 +340,15 @@ void Interpreter::step(CpuCore* core, Memory* mem, Disassembler* disassembler) {
gprs[instr.rt] = (u32)(s16)mem->read<u16>(addr);
break;
}
case CpuOpcodes::Opcode::LWL: {
u32 address = gprs[instr.rs] + (u32)(s16)instr.imm;
const int shift = ((address & 3) ^ 3) * 8;
u32 dataTemp = mem->read<u32>(address & ~3);
u32 rtTemp = gprs[instr.rt] & ~(0xffffffff >> shift);
dataTemp >>= shift;
gprs[instr.rt] = dataTemp | rtTemp;
break;
}
case CpuOpcodes::Opcode::LW: {
const u32 addr = gprs[instr.rs] + (u32)(s16)instr.imm;
if (addr & 3) {
@ -355,6 +370,15 @@ void Interpreter::step(CpuCore* core, Memory* mem, Disassembler* disassembler) {
gprs[instr.rt] = mem->read<u16>(addr);
break;
}
case CpuOpcodes::Opcode::LWR: {
u32 address = gprs[instr.rs] + (u32)(s16)instr.imm;
const int shift = (address & 3) * 8;
u32 dataTemp = mem->read<u32>(address & ~3);
u32 rtTemp = gprs[instr.rt] & ~(0xffffffff << shift);
dataTemp <<= shift;
gprs[instr.rt] = dataTemp | rtTemp;
break;
}
case CpuOpcodes::Opcode::SB: {
if (core->cop0.status.isc) return;
const u32 addr = gprs[instr.rs] + (u32)(s16)instr.imm;
@ -370,6 +394,15 @@ void Interpreter::step(CpuCore* core, Memory* mem, Disassembler* disassembler) {
mem->write<u16>(addr, gprs[instr.rt]);
break;
}
case CpuOpcodes::Opcode::SWL: {
u32 address = gprs[instr.rs] + (u32)(s16)instr.imm;
const int shift = ((address & 3) ^ 3) * 8;
u32 dataTemp = mem->read<u32>(address & ~3);
u32 rtTemp = gprs[instr.rt] << shift;
dataTemp &= ~(0xffffffff << shift);
mem->write<u32>(address & ~3, dataTemp | rtTemp);
break;
}
case CpuOpcodes::Opcode::SW: {
if (core->cop0.status.isc) break;
const u32 addr = gprs[instr.rs] + (u32)(s16)instr.imm;
@ -379,9 +412,18 @@ void Interpreter::step(CpuCore* core, Memory* mem, Disassembler* disassembler) {
mem->write<u32>(addr, gprs[instr.rt]);
break;
}
case CpuOpcodes::Opcode::SWR: {
u32 address = gprs[instr.rs] + (u32)(s16)instr.imm;
const int shift = (address & 3) * 8;
u32 dataTemp = mem->read<u32>(address & ~3);
u32 rtTemp = gprs[instr.rt] >> shift;
dataTemp &= ~(0xffffffff >> shift);
mem->write<u32>(address & ~3, dataTemp | rtTemp);
break;
}
default:
Helpers::panic("[FATAL] Unimplemented primary instruction 0x%02x (raw: 0x%08x)\n", instr.primaryOpc.Value(), instr.raw);
}
if (core->isDelaySlot) core->isDelaySlot = false;
core->isDelaySlot = false;
}

View file

@ -11,5 +11,4 @@ void Cpu::switchBackend(Backend backend) {
case Backend::OldInterpreter: stepFunc = &oldInterpreter.step; break;
default: Helpers::panic("Unsupported backend\n"); // Should never be triggered
}
stepFunc = &oldInterpreter.step;
}

View file

@ -3,6 +3,7 @@
#include <helpers.hpp>
#include <BitField.hpp>
#include <logger.hpp>
#include <interrupt.hpp>
struct COP0 {
@ -124,7 +125,8 @@ public:
Overflow = 0xC
};
void exception(Exception exception) {
// If decrementPc is true, epc will be set to pc - 4 instead of pc
void exception(Exception exception, bool decrementPc = false) {
if (isDelaySlot)
cop0.cause.bd = true;
else
@ -143,11 +145,25 @@ public:
cop0.cause.raw &= ~0xff;
cop0.cause.raw |= (u32)exception << 2;
cop0.epc = pc;
if (decrementPc)
cop0.epc -= 4;
if (isDelaySlot)
cop0.epc -= 4;
pc = handler;
nextPc = handler + 4;
}
// Returns true if an interrupt was fired
bool checkInterrupt(Interrupt* interrupt) {
if (interrupt->interruptFired()) {
cop0.cause.raw |= 1 << 10;
if (cop0.status.iec && (cop0.status.raw & (1 << 10))) {
exception(Exception::INT);
return true;
}
}
return false;
}
};
namespace CpuOpcodes {

View file

@ -15,4 +15,12 @@ u16 Interrupt::readImask() {
u16 Interrupt::readIstat() {
return istat;
}
void Interrupt::raiseInterrupt(InterruptType interrupt) {
istat |= 1 << (u32)interrupt;
}
bool Interrupt::interruptFired() {
return istat & imask;
}

View file

@ -11,6 +11,21 @@ public:
u16 readImask();
u16 readIstat();
enum class InterruptType {
VBLANK,
GPU,
CDROM,
DMA,
TMR0, TMR1, TMR2,
PAD,
SIO,
SPU,
IRQ10 // Dunno what to call this one
};
void raiseInterrupt(InterruptType interrupt);
bool interruptFired();
private:
u16 istat = 0;
u16 imask = 0;

View file

@ -3,10 +3,10 @@
#include "playstation.hpp"
#define CLOCK_SPEED (33868800 / 60)
constexpr auto cyclesPerFrame = 33868800 / 60;
int main(int argc, char** argv) {
if (argc < 2) Helpers::panic("Usage: ChonkyStation [bios path]");
if (argc < 2) Helpers::panic("Usage: ChonkyStation [bios path]\n");
printf("ChonkyStation\n");
@ -22,8 +22,17 @@ int main(int argc, char** argv) {
bool running = true;
while (running) {
cycles = 0;
while (cycles++ < CLOCK_SPEED)
while (cycles < cyclesPerFrame) {
playstation.step();
if (!playstation.isInBIOS())
cycles += 2;
else
cycles += 20;
}
// VBLANK interrupt
playstation.VBLANK();
// Handle SDL window events
SDL_Event event;

View file

@ -82,7 +82,8 @@ u16 Memory::read(u32 vaddr) {
u32 paddr = maskAddress(vaddr);
// Interrupt
if (paddr == 0x1f801074) return interrupt->readImask();
if (paddr == 0x1f801070) return interrupt->readIstat();
else if (paddr == 0x1f801074) return interrupt->readImask();
// SPU
else if (Helpers::inRangeSized<u32>(paddr, (u32)MemoryBase::SPU, (u32)MemorySize::SPU)) return 0;
else
@ -105,6 +106,7 @@ u32 Memory::read(u32 vaddr) {
if (paddr == 0x1f801810) return gpu->gpuRead();
else if (paddr == 0x1f801814) return gpu->getStat();
// Interrupt
else if (paddr == 0x1f801070) return interrupt->readIstat();
else if (paddr == 0x1f801074) return interrupt->readImask();
// DMA
else if (Helpers::inRange<u32>(paddr, 0x1f801080, 0x1f8010e8)) {
@ -159,7 +161,8 @@ void Memory::write(u32 vaddr, u16 data) {
u32 paddr = maskAddress(vaddr);
// Interrupt
if (paddr == 0x1f801074) interrupt->writeImask(data);
if (paddr == 0x1f801070) interrupt->writeIstat(data);
else if (paddr == 0x1f801074) interrupt->writeImask(data);
// SPU
else if (Helpers::inRangeSized<u32>(paddr, (u32)MemoryBase::SPU, (u32)MemorySize::SPU)) return;
// Timers

View file

@ -12,7 +12,7 @@ class PlayStation {
public:
PlayStation(const fs::path& biosPath) : interrupt(), gpu(), dma(), mem(&interrupt, &dma, &gpu), cpu(&mem) {
mem.loadBios(biosPath);
cpu.switchBackend(Cpu::Backend::OldInterpreter);
cpu.switchBackend(Cpu::Backend::Interpreter);
}
// Steps the system
@ -23,6 +23,8 @@ public:
u32 getPC() { return cpu.core.pc; }
u8* getRAM() { return mem.ram; }
u8* getVRAM() { return gpu.getVRAM(); }
void VBLANK() { interrupt.raiseInterrupt(Interrupt::InterruptType::VBLANK); }
bool isInBIOS() { return Helpers::inRangeSized<u32>(cpu.core.pc, (u32)Memory::MemoryBase::BIOS, (u32)Memory::MemorySize::BIOS); }
private:
Cpu cpu;