Simplifications to CPU interrupt handling.

This commit is contained in:
byuu 2019-08-02 11:23:31 +09:00
parent db988d9588
commit b577e6d5d0
12 changed files with 99 additions and 90 deletions

View File

@ -19,6 +19,9 @@ struct WDC65816 {
virtual auto readDisassembler(uint addr) -> uint8 { return 0; }
inline auto irq() const -> bool { return r.irq; }
virtual inline auto irq(bool line) -> void { r.irq = line; }
using r8 = uint8;
union r16 {

View File

@ -23,7 +23,7 @@ auto HitachiDSP::step(uint clocks) -> void {
auto HitachiDSP::halt() -> void {
HG51B::halt();
if(io.irq == 0) r.i = 1, cpu.r.irq = 1;
if(io.irq == 0) cpu.irq(r.i = 1);
}
auto HitachiDSP::unload() -> void {

View File

@ -223,7 +223,7 @@ auto HitachiDSP::writeIO(uint address, uint8 data) -> void {
case 0x7f51:
io.irq = data & 1;
if(io.irq == 1) r.i = 0, cpu.r.irq = 0;
if(io.irq == 1) cpu.irq(r.i = 0);
return;
case 0x7f52:

View File

@ -51,7 +51,7 @@ auto SA1::dmaCC1() -> void {
mmio.chdma_irqfl = true;
if(mmio.chdma_irqen) {
mmio.chdma_irqcl = 0;
cpu.r.irq = 1;
cpu.irq(1);
}
}

View File

@ -136,14 +136,14 @@ auto SA1::writeIOCPU(uint address, uint8 data) -> void {
if(!mmio.cpu_irqen && (data & 0x80)) {
if(mmio.cpu_irqfl) {
mmio.cpu_irqcl = 0;
cpu.r.irq = 1;
cpu.irq(1);
}
}
if(!mmio.chdma_irqen && (data & 0x20)) {
if(mmio.chdma_irqfl) {
mmio.chdma_irqcl = 0;
cpu.r.irq = 1;
cpu.irq(1);
}
}
@ -160,7 +160,7 @@ auto SA1::writeIOCPU(uint address, uint8 data) -> void {
if(mmio.cpu_irqcl ) mmio.cpu_irqfl = false;
if(mmio.chdma_irqcl) mmio.chdma_irqfl = false;
if(!mmio.cpu_irqfl && !mmio.chdma_irqfl) cpu.r.irq = 0;
if(!mmio.cpu_irqfl && !mmio.chdma_irqfl) cpu.irq(0);
return;
}
@ -251,7 +251,7 @@ auto SA1::writeIOSA1(uint address, uint8 data) -> void {
mmio.cpu_irqfl = true;
if(mmio.cpu_irqen) {
mmio.cpu_irqcl = 0;
cpu.r.irq = 1;
cpu.irq(1);
}
}

View File

@ -1,5 +1,5 @@
auto SuperFX::stop() -> void {
cpu.r.irq = 1;
cpu.irq(1);
}
auto SuperFX::color(uint8 source) -> uint8 {

View File

@ -18,7 +18,7 @@ auto SuperFX::readIO(uint addr, uint8) -> uint8 {
case 0x3031: {
uint8 r = regs.sfr >> 8;
regs.sfr.irq = 0;
cpu.r.irq = 0;
cpu.irq(0);
return r;
}

View File

@ -33,34 +33,34 @@ auto CPU::Enter() -> void {
}
}
auto CPU::boot() -> void {
}
auto CPU::main() -> void {
if(r.wai) return instructionWait();
if(r.stp) return instructionStop();
if(!status.interruptPending) return instruction();
if(status.interruptPending) {
status.interruptPending = false;
if(status.nmiPending) {
status.nmiPending = false;
r.vector = r.e ? 0xfffa : 0xffea;
interrupt();
} else if(status.irqPending) {
status.irqPending = false;
r.vector = r.e ? 0xfffe : 0xffee;
interrupt();
} else if(status.resetPending) {
status.resetPending = false;
for(uint repeat : range(22)) step<6,0>(); //step(132);
r.vector = 0xfffc;
interrupt();
} else if(status.powerPending) {
status.powerPending = false;
for(uint repeat : range(31)) step<6,0>(); //step(186);
r.pc.l = bus.read(0xfffc, r.mdr);
r.pc.h = bus.read(0xfffd, r.mdr);
}
if(status.nmiPending) {
status.nmiPending = 0;
r.vector = r.e ? 0xfffa : 0xffea;
return interrupt();
}
instruction();
if(status.irqPending) {
status.irqPending = 0;
r.vector = r.e ? 0xfffe : 0xffee;
return interrupt();
}
if(status.resetPending) {
status.resetPending = 0;
for(uint repeat : range(22)) step<6,0>(); //step(132);
r.vector = 0xfffc;
return interrupt();
}
status.interruptPending = 0;
}
auto CPU::load() -> bool {
@ -109,13 +109,11 @@ auto CPU::power(bool reset) -> void {
alu = {};
status = {};
status.lineClocks = hperiod();
status.dramRefreshPosition = (version == 1 ? 530 : 538);
status.hdmaSetupPosition = (version == 1 ? 12 + 8 - dmaCounter() : 12 + dmaCounter());
status.hdmaPosition = 1104;
status.powerPending = reset == 0;
status.resetPending = reset == 1;
status.interruptPending = true;
status.resetPending = 1;
status.interruptPending = 1;
}
}

View File

@ -9,6 +9,7 @@ struct CPU : Processor::WDC65816, Thread, PPUcounter {
auto synchronizePPU() -> void;
auto synchronizeCoprocessors() -> void;
static auto Enter() -> void;
auto boot() -> void;
auto main() -> void;
auto load() -> bool;
auto power(bool reset) -> void;
@ -51,9 +52,9 @@ struct CPU : Processor::WDC65816, Thread, PPUcounter {
alwaysinline auto aluEdge() -> void;
alwaysinline auto dmaEdge() -> void;
alwaysinline auto lastCycle() -> void;
//irq.cpp
auto irq(bool line) -> void override;
alwaysinline auto pollInterrupts() -> void;
auto nmitimenUpdate(uint8 data) -> void;
auto rdnmi() -> bool;
@ -61,6 +62,7 @@ struct CPU : Processor::WDC65816, Thread, PPUcounter {
alwaysinline auto nmiTest() -> bool;
alwaysinline auto irqTest() -> bool;
alwaysinline auto lastCycle() -> void;
//joypad.cpp
auto joypadEdge() -> void;
@ -86,18 +88,17 @@ private:
struct Status {
uint clockCount = 0;
uint lineClocks = 0;
bool irqLock = false;
bool irqLock = 0;
uint dramRefreshPosition = 0;
uint dramRefresh = 0; //0 = not refreshed; 1 = refresh active; 2 = refresh inactive
uint hdmaSetupPosition = 0;
bool hdmaSetupTriggered = false;
bool hdmaSetupTriggered = 0;
uint hdmaPosition = 0;
bool hdmaTriggered = false;
bool hdmaTriggered = 0;
boolean nmiValid = 0;
boolean nmiLine = 0;
@ -111,18 +112,16 @@ private:
boolean irqPending = 0;
boolean irqHold = 0;
bool powerPending = false;
bool resetPending = false;
bool resetPending = 0;
bool interruptPending = 0;
bool interruptPending = false;
bool dmaActive = false;
bool dmaPending = false;
bool hdmaPending = false;
bool dmaActive = 0;
bool dmaPending = 0;
bool hdmaPending = 0;
bool hdmaMode = 0; //0 = init, 1 = run
bool autoJoypadActive = false;
bool autoJoypadLatch = false;
bool autoJoypadActive = 0;
bool autoJoypadLatch = 0;
uint autoJoypadCounter = 0;
} status;

View File

@ -1,3 +1,12 @@
//external interrupt line changed.
auto CPU::irq(bool line) -> void {
WDC65816::irq(line);
if(line) {
status.irqTransition = 1;
r.wai = 0;
}
}
//called once every four clock cycles;
//as NMI steps by scanlines (divisible by 4) and IRQ by PPU 4-cycle dots.
//
@ -5,48 +14,56 @@
//it is used to emulate hardware communication delay between opcode and interrupt units.
auto CPU::pollInterrupts() -> void {
//NMI hold
if(status.nmiHold.lower() && io.nmiEnable) status.nmiTransition = true;
if(status.nmiHold.lower() && io.nmiEnable) {
status.nmiTransition = 1;
r.wai = 0;
}
//NMI test
if(status.nmiValid.flip(vcounter(2) >= ppu.vdisp())) {
if(status.nmiLine = status.nmiValid) status.nmiHold = true; //hold /NMI for four cycles
if(status.nmiLine = status.nmiValid) status.nmiHold = 1; //hold /NMI for four cycles
}
//IRQ hold
status.irqHold = false;
if(status.irqLine && io.irqEnable) status.irqTransition = true;
status.irqHold = 0;
if(status.irqLine && io.irqEnable) {
status.irqTransition = 1;
r.wai = 0;
}
//IRQ test
if(status.irqValid.raise(io.irqEnable
&& (!io.virqEnable || vcounter(10) == io.vtime)
&& (!io.hirqEnable || hcounter(10) == io.htime)
&& (vcounter(6) || hcounter(6)) //IRQs cannot trigger on last dot of fields
)) status.irqLine = status.irqHold = true; //hold /IRQ for four cycles
)) status.irqLine = status.irqHold = 1; //hold /IRQ for four cycles
}
auto CPU::nmitimenUpdate(uint8 data) -> void {
io.hirqEnable = bit1(data,4);
io.virqEnable = bit1(data,5);
io.hirqEnable = data & 0x10;
io.virqEnable = data & 0x20;
io.irqEnable = io.hirqEnable || io.virqEnable;
if(io.virqEnable && !io.hirqEnable && status.irqLine) {
status.irqTransition = true;
status.irqTransition = 1;
r.wai = 0;
} else if(!io.irqEnable) {
status.irqLine = false;
status.irqTransition = false;
status.irqLine = 0;
status.irqTransition = 0;
}
if(io.nmiEnable.raise(bit1(data,7)) && status.nmiLine) {
status.nmiTransition = true;
if(io.nmiEnable.raise(data & 0x80) && status.nmiLine) {
status.nmiTransition = 1;
r.wai = 0;
}
status.irqLock = true;
status.irqLock = 1;
}
auto CPU::rdnmi() -> bool {
bool result = status.nmiLine;
if(!status.nmiHold) {
status.nmiLine = false;
status.nmiLine = 0;
}
return result;
}
@ -54,22 +71,32 @@ auto CPU::rdnmi() -> bool {
auto CPU::timeup() -> bool {
bool result = status.irqLine;
if(!status.irqHold) {
status.irqLine = false;
status.irqTransition = false;
status.irqLine = 0;
status.irqTransition = 0;
}
return result;
}
auto CPU::nmiTest() -> bool {
if(!status.nmiTransition) return false;
status.nmiTransition = false;
r.wai = false;
return true;
if(!status.nmiTransition) return 0;
status.nmiTransition = 0;
return 1;
}
auto CPU::irqTest() -> bool {
if(!status.irqTransition && !r.irq) return false;
status.irqTransition = false;
r.wai = false;
if(!status.irqTransition) return 0;
status.irqTransition = 0;
return !r.p.i;
}
//used to test for NMI/IRQ, which can trigger on the edge of every opcode.
//test one cycle early to simulate two-stage pipeline of the 65816 CPU.
//
//status.irqLock is used to simulate hardware delay before interrupts can
//trigger during certain events (immediately after DMA, writes to $4200, etc)
auto CPU::lastCycle() -> void {
if(!status.irqLock) {
if(nmiTest()) status.nmiPending = 1, status.interruptPending = 1;
if(irqTest()) status.irqPending = 1, status.interruptPending = 1;
}
}

View File

@ -11,7 +11,6 @@ auto CPU::serialize(serializer& s) -> void {
s.integer(counter.dma);
s.integer(status.clockCount);
s.integer(status.lineClocks);
s.integer(status.irqLock);
@ -36,9 +35,7 @@ auto CPU::serialize(serializer& s) -> void {
s.boolean(status.irqPending);
s.boolean(status.irqHold);
s.integer(status.powerPending);
s.integer(status.resetPending);
s.integer(status.interruptPending);
s.integer(status.dmaActive);

View File

@ -82,8 +82,6 @@ auto CPU::step(uint clocks) -> void {
//called by ppu.tick() when Hcounter=0
auto CPU::scanline() -> void {
status.lineClocks = hperiod();
//forcefully sync S-CPU to other processors, in case chips are not communicating
synchronizeSMP();
synchronizePPU();
@ -232,16 +230,3 @@ auto CPU::joypadEdge() -> void {
status.autoJoypadCounter++;
}
}
//used to test for NMI/IRQ, which can trigger on the edge of every opcode.
//test one cycle early to simulate two-stage pipeline of x816 CPU.
//
//status.irq_lock is used to simulate hardware delay before interrupts can
//trigger during certain events (immediately after DMA, writes to $4200, etc)
auto CPU::lastCycle() -> void {
if(!status.irqLock) {
if(nmiTest()) status.nmiPending = true;
if(irqTest()) status.irqPending = true;
status.interruptPending = status.nmiPending || status.irqPending;
}
}