bsnes/higan/sfc/smp/io.cpp

183 lines
3.6 KiB
C++
Raw Normal View History

Update to v106r49 release. byuu says: This is a fairly radical WIP with extreme changes to lots of very important parts. The result is a ~7% emulation speedup (with bsnes, unsure how much it helps higan), but it's quite possible there are regressions. As such, I would really appreciate testing as many games as possible ... especially the old finnicky games that had issues with DMA and/or interrupts. One thing to note is that I removed an edge case test that suppresses IRQs from firing on the very last dot of every field, which is a behavior I've verified on real hardware in the past. I feel that the main interrupt polling function (the hottest portion of the entire emulator) is not the appropriate place for it, and I should instead factor it into assignment of NMITIMEN/VTIME/HTIME using the new io.irqEnable (==virqEnable||hirqEnable) flag. But since I haven't done that yet ... there's an old IRQ test ROM of mine that'll fail for this WIP. No commercial games will ever rely on this, so it's fine for testing. Changelog: - sfc/cpu.smp: inlined the global status functions - sfc/cpu: added readRAM, writeRAM to use a function pointer instead of a lambda for WRAM access - sfc/cpu,smp,ppu/counter: updated reset functionality to new style using class inline initializers - sfc/cpu: fixed power(false) to invoke the reset vector properly - sfc/cpu: completely rewrote DMA handling to have per-channel functions - sfc/cpu: removed unused joylatch(), io.joypadStrobeLatch - sfc/cpu: cleaned up io.cpp handlers - sfc/cpu: simplified interrupt polling code using nall::boolean::flip(),raise(),lower() functions - sfc/ppu/counter: cleaned up the class significantly and also optimized things for efficiency - sfc/ppu/counter: emulated PAL 1368-clock long scanline when interlace=1, field=1, vcounter=311 - sfc/smp: factored out the I/O and port handlers to io.cpp
2018-07-19 09:01:44 +00:00
auto SMP::portRead(uint2 port) const -> uint8 {
if(port == 0) return io.cpu0;
if(port == 1) return io.cpu1;
if(port == 2) return io.cpu2;
if(port == 3) return io.cpu3;
unreachable;
}
auto SMP::portWrite(uint2 port, uint8 data) -> void {
if(port == 0) io.apu0 = data;
if(port == 1) io.apu1 = data;
if(port == 2) io.apu2 = data;
if(port == 3) io.apu3 = data;
}
auto SMP::readIO(uint16 address) -> uint8 {
uint8 data;
switch(address) {
case 0xf0: //TEST (write-only register)
return 0x00;
case 0xf1: //CONTROL (write-only register)
return 0x00;
case 0xf2: //DSPADDR
return io.dspAddr;
case 0xf3: //DSPDATA
//0x80-0xff are read-only mirrors of 0x00-0x7f
return dsp.read(io.dspAddr & 0x7f);
case 0xf4: //CPUIO0
synchronize(cpu);
return io.apu0;
case 0xf5: //CPUIO1
synchronize(cpu);
return io.apu1;
case 0xf6: //CPUIO2
synchronize(cpu);
return io.apu2;
case 0xf7: //CPUIO3
synchronize(cpu);
return io.apu3;
case 0xf8: //AUXIO4
return io.aux4;
case 0xf9: //AUXIO5
return io.aux5;
case 0xfa: //T0TARGET
case 0xfb: //T1TARGET
case 0xfc: //T2TARGET (write-only registers)
return 0x00;
case 0xfd: //T0OUT (4-bit counter value)
data = timer0.stage3;
timer0.stage3 = 0;
return data;
case 0xfe: //T1OUT (4-bit counter value)
data = timer1.stage3;
timer1.stage3 = 0;
return data;
case 0xff: //T2OUT (4-bit counter value)
data = timer2.stage3;
timer2.stage3 = 0;
return data;
}
return data; //unreachable
}
auto SMP::writeIO(uint16 address, uint8 data) -> void {
switch(address) {
case 0xf0: //TEST
if(r.p.p) break; //writes only valid when P flag is clear
io.timersDisable = data.bit (0);
io.ramWritable = data.bit (1);
io.ramDisable = data.bit (2);
io.timersEnable = data.bit (3);
io.externalWaitStates = data.bits(4,5);
io.internalWaitStates = data.bits(6,7);
timer0.synchronizeStage1();
timer1.synchronizeStage1();
timer2.synchronizeStage1();
break;
case 0xf1: //CONTROL
//0->1 transistion resets timers
if(timer0.enable.raise(data.bit(0))) {
timer0.stage2 = 0;
timer0.stage3 = 0;
}
if(timer1.enable.raise(data.bit(1))) {
timer1.stage2 = 0;
timer1.stage3 = 0;
}
if(!timer2.enable.raise(data.bit(2))) {
timer2.stage2 = 0;
timer2.stage3 = 0;
}
if(data.bit(4)) {
synchronize(cpu);
io.apu0 = 0x00;
io.apu1 = 0x00;
}
if(data.bit(5)) {
synchronize(cpu);
io.apu2 = 0x00;
io.apu3 = 0x00;
}
io.iplromEnable = data.bit(7);
break;
case 0xf2: //DSPADDR
io.dspAddr = data;
break;
case 0xf3: //DSPDATA
if(io.dspAddr & 0x80) break; //0x80-0xff are read-only mirrors of 0x00-0x7f
dsp.write(io.dspAddr & 0x7f, data);
break;
case 0xf4: //CPUIO0
synchronize(cpu);
io.cpu0 = data;
break;
case 0xf5: //CPUIO1
synchronize(cpu);
io.cpu1 = data;
break;
case 0xf6: //CPUIO2
synchronize(cpu);
io.cpu2 = data;
break;
case 0xf7: //CPUIO3
synchronize(cpu);
io.cpu3 = data;
break;
case 0xf8: //AUXIO4
io.aux4 = data;
break;
case 0xf9: //AUXIO5
io.aux5 = data;
break;
case 0xfa: //T0TARGET
timer0.target = data;
break;
case 0xfb: //T1TARGET
timer1.target = data;
break;
case 0xfc: //T2TARGET
timer2.target = data;
break;
case 0xfd: //T0OUT
case 0xfe: //T1OUT
case 0xff: //T2OUT (read-only registers)
break;
}
}