bsnes/higan/sfc/cpu/irq.cpp

76 lines
2.0 KiB
C++

//called once every four clock cycles;
//as NMI steps by scanlines (divisible by 4) and IRQ by PPU 4-cycle dots.
//
//ppu.(vh)counter(n) returns the value of said counters n-clocks before current time;
//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;
//NMI test
if(status.nmiValid.flip(vcounter(2) >= ppu.vdisp())) {
if(status.nmiLine = status.nmiValid) status.nmiHold = true; //hold /NMI for four cycles
}
//IRQ hold
status.irqHold = false;
if(status.irqLine && io.irqEnable) status.irqTransition = true;
//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
}
auto CPU::nmitimenUpdate(uint8 data) -> void {
io.hirqEnable = data.bit(4);
io.virqEnable = data.bit(5);
io.irqEnable = io.hirqEnable || io.virqEnable;
if(io.virqEnable && !io.hirqEnable && status.irqLine) {
status.irqTransition = true;
} else if(!io.irqEnable) {
status.irqLine = false;
status.irqTransition = false;
}
if(io.nmiEnable.raise(data.bit(7)) && status.nmiLine) {
status.nmiTransition = true;
}
status.irqLock = true;
}
auto CPU::rdnmi() -> bool {
bool result = status.nmiLine;
if(!status.nmiHold) {
status.nmiLine = false;
}
return result;
}
auto CPU::timeup() -> bool {
bool result = status.irqLine;
if(!status.irqHold) {
status.irqLine = false;
status.irqTransition = false;
}
return result;
}
auto CPU::nmiTest() -> bool {
if(!status.nmiTransition) return false;
status.nmiTransition = false;
r.wai = false;
return true;
}
auto CPU::irqTest() -> bool {
if(!status.irqTransition && !r.irq) return false;
status.irqTransition = false;
r.wai = false;
return !r.p.i;
}