mirror of
https://github.com/liuk7071/ChonkyStation.git
synced 2024-05-20 12:57:52 -04:00
Merge pull request #11 from liuk7071/rewrite_interrupts
Fix interrupts and exceptions
This commit is contained in:
commit
5028e5a954
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
|
|
15
src/main.cpp
15
src/main.cpp
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue