alwaysinline auto SMP::ramRead(uint16 addr) -> uint8 { if(addr >= 0xffc0 && io.iplromEnable) return iplrom[addr & 0x3f]; if(io.ramDisable) return 0x5a; //0xff on mini-SNES return dsp.apuram[addr]; } alwaysinline auto SMP::ramWrite(uint16 addr, uint8 data) -> void { //writes to $ffc0-$ffff always go to apuram, even if the iplrom is enabled if(io.ramWritable && !io.ramDisable) dsp.apuram[addr] = data; } 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::busRead(uint16 addr) -> uint8 { uint result; switch(addr) { 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) result = timer0.stage3; timer0.stage3 = 0; return result; case 0xfe: //T1OUT (4-bit counter value) result = timer1.stage3; timer1.stage3 = 0; return result; case 0xff: //T2OUT (4-bit counter value) result = timer2.stage3; timer2.stage3 = 0; return result; } return ramRead(addr); } auto SMP::busWrite(uint16 addr, uint8 data) -> void { switch(addr) { 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 && data.bit(0)) { timer0.stage2 = 0; timer0.stage3 = 0; } timer0.enable = data.bit(0); if(!timer1.enable && data.bit(1)) { timer1.stage2 = 0; timer1.stage3 = 0; } timer1.enable = data.bit(1); if(!timer2.enable && data.bit(2)) { timer2.stage2 = 0; timer2.stage3 = 0; } timer2.enable = data.bit(2); 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; } ramWrite(addr, data); //all writes, even to I/O registers, appear on bus } auto SMP::idle() -> void { wait(); } auto SMP::read(uint16 addr) -> uint8 { wait(addr); uint8 data = busRead(addr); return data; } auto SMP::write(uint16 addr, uint8 data) -> void { wait(addr); busWrite(addr, data); } auto SMP::readDisassembler(uint16 addr) -> uint8 { if((addr & 0xfff0) == 0x00f0) return 0x00; if(addr >= 0xffc0 && io.iplromEnable) return iplrom[addr & 0x3f]; return dsp.apuram[addr]; }