mirror of https://github.com/bsnes-emu/bsnes.git
Simplifications to CPU interrupt handling.
This commit is contained in:
parent
db988d9588
commit
b577e6d5d0
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
auto SuperFX::stop() -> void {
|
||||
cpu.r.irq = 1;
|
||||
cpu.irq(1);
|
||||
}
|
||||
|
||||
auto SuperFX::color(uint8 source) -> uint8 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue