diff --git a/ares/fc/apu/apu.hpp b/ares/fc/apu/apu.hpp index 5c09b8da4..1065c2631 100644 --- a/ares/fc/apu/apu.hpp +++ b/ares/fc/apu/apu.hpp @@ -148,13 +148,14 @@ struct APU : Thread { auto clock() -> n8; auto power(bool reset) -> void; + auto setDMABuffer(n8 data) -> void; + auto dmaAddress() -> n16 const { return 0x8000 | readAddress; } //serialization.cpp auto serialize(serializer&) -> void; n16 lengthCounter; n16 periodCounter; - n16 dmaDelayCounter; n1 irqPending; n4 period; n1 irqEnable; diff --git a/ares/fc/apu/dmc.cpp b/ares/fc/apu/dmc.cpp index 0cb4a7953..32a9825f0 100644 --- a/ares/fc/apu/dmc.cpp +++ b/ares/fc/apu/dmc.cpp @@ -2,44 +2,19 @@ auto APU::DMC::start() -> void { if(lengthCounter == 0) { readAddress = 0x4000 + (addressLatch << 6); lengthCounter = (lengthLatch << 4) + 1; + + if (!dmaBufferValid) + cpu.dmcDMAPending(); } } auto APU::DMC::stop() -> void { lengthCounter = 0; - dmaDelayCounter = 0; - cpu.rdyLine(1); - cpu.rdyAddress(false); } auto APU::DMC::clock() -> n8 { n8 result = dacLatch; - if(dmaDelayCounter > 0) { - dmaDelayCounter--; - - if(dmaDelayCounter == 1) { - cpu.rdyAddress(true, 0x8000 | readAddress); - } else if(dmaDelayCounter == 0) { - cpu.rdyLine(1); - cpu.rdyAddress(false); - - dmaBuffer = cpu.io.openBus; - dmaBufferValid = true; - lengthCounter--; - readAddress++; - - if(lengthCounter == 0) { - if(loopMode) { - start(); - } else if(irqEnable) { - irqPending = true; - apu.setIRQ(); - } - } - } - } - if(--periodCounter == 0) { if(sampleValid) { s32 delta = (((sample >> bitCounter) & 1) << 2) - 2; @@ -52,6 +27,9 @@ auto APU::DMC::clock() -> n8 { sampleValid = true; sample = dmaBuffer; dmaBufferValid = false; + + if (lengthCounter > 0) + cpu.dmcDMAPending(); } else { sampleValid = false; } @@ -60,18 +38,12 @@ auto APU::DMC::clock() -> n8 { periodCounter = Region::PAL() ? dmcPeriodTablePAL[period] : dmcPeriodTableNTSC[period]; } - if(lengthCounter > 0 && !dmaBufferValid && dmaDelayCounter == 0) { - cpu.rdyLine(0); - dmaDelayCounter = 4; - } - return result; } auto APU::DMC::power(bool reset) -> void { lengthCounter = 0; periodCounter = Region::PAL() ? dmcPeriodTablePAL[0] : dmcPeriodTableNTSC[0]; - dmaDelayCounter = 0; irqPending = 0; period = 0; irqEnable = 0; @@ -86,3 +58,19 @@ auto APU::DMC::power(bool reset) -> void { sampleValid = 0; sample = 0; } + +auto APU::DMC::setDMABuffer(n8 data) -> void { + dmaBuffer = data; + dmaBufferValid = true; + lengthCounter--; + readAddress++; + + if (lengthCounter == 0) { + if (loopMode) { + start(); + } else if (irqEnable) { + irqPending = true; + apu.setIRQ(); + } + } +} diff --git a/ares/fc/apu/serialization.cpp b/ares/fc/apu/serialization.cpp index c143ec550..fc766c8b1 100644 --- a/ares/fc/apu/serialization.cpp +++ b/ares/fc/apu/serialization.cpp @@ -70,7 +70,6 @@ auto APU::Noise::serialize(serializer& s) -> void { auto APU::DMC::serialize(serializer& s) -> void { s(lengthCounter); s(periodCounter); - s(dmaDelayCounter); s(irqPending); s(period); s(irqEnable); diff --git a/ares/fc/cpu/cpu.cpp b/ares/fc/cpu/cpu.cpp index e2a5f51ef..192c1d01e 100644 --- a/ares/fc/cpu/cpu.cpp +++ b/ares/fc/cpu/cpu.cpp @@ -34,6 +34,7 @@ auto CPU::main() -> void { auto CPU::step(u32 clocks) -> void { assert(clocks == rate()); + io.oddCycle ^= 1; Thread::step(clocks); Thread::synchronize(); } diff --git a/ares/fc/cpu/cpu.hpp b/ares/fc/cpu/cpu.hpp index 76ad9435c..49bde108f 100644 --- a/ares/fc/cpu/cpu.hpp +++ b/ares/fc/cpu/cpu.hpp @@ -49,15 +49,13 @@ struct CPU : MOS6502, Thread { auto irqPending() -> bool override; auto nmi(n16& vector) -> void override; - auto oamDMA() -> void; + auto dmcDMAPending() -> void; + auto dma(n16 address) -> void; auto nmiLine(bool) -> void; auto irqLine(bool) -> void; auto apuLine(bool) -> void; - auto rdyLine(bool) -> void; - auto rdyAddress(bool valid, n16 value = 0) -> void; - //protected: struct IO { n1 interruptPending; @@ -65,9 +63,8 @@ struct CPU : MOS6502, Thread { n1 nmiLine; n1 irqLine; n1 apuLine; - n1 rdyLine = 1; - n1 rdyAddressValid; - n16 rdyAddressValue; + n1 oddCycle; + n1 dmcDMAPending; n1 oamDMAPending; n8 oamDMAPage; n8 openBus; diff --git a/ares/fc/cpu/serialization.cpp b/ares/fc/cpu/serialization.cpp index 3e99ee7e7..915844074 100644 --- a/ares/fc/cpu/serialization.cpp +++ b/ares/fc/cpu/serialization.cpp @@ -7,9 +7,7 @@ auto CPU::serialize(serializer& s) -> void { s(io.nmiLine); s(io.irqLine); s(io.apuLine); - s(io.rdyLine); - s(io.rdyAddressValid); - s(io.rdyAddressValue); + s(io.dmcDMAPending); s(io.oamDMAPending); s(io.oamDMAPage); s(io.openBus); diff --git a/ares/fc/cpu/timing.cpp b/ares/fc/cpu/timing.cpp index f27e336c2..9f9e6d97f 100644 --- a/ares/fc/cpu/timing.cpp +++ b/ares/fc/cpu/timing.cpp @@ -1,13 +1,6 @@ auto CPU::read(n16 address) -> n8 { - if(io.oamDMAPending) { - io.oamDMAPending = 0; - read(address); - oamDMA(); - } - - while(io.rdyLine == 0) { - io.openBus = readBus(io.rdyAddressValid ? io.rdyAddressValue : address); - step(rate()); + if(io.oamDMAPending || io.dmcDMAPending) { + dma(address); } io.openBus = readBus(address); @@ -43,10 +36,63 @@ auto CPU::nmi(n16& vector) -> void { } } -auto CPU::oamDMA() -> void { - for(u32 n : range(256)) { - n8 data = read(io.oamDMAPage << 8 | n); - write(0x2004, data); +auto CPU::dmcDMAPending() -> void { + io.dmcDMAPending = 1; +} + +auto CPU::dma(n16 address) -> void { + bool dmcReady = false; + bool oamWriteReady = false; + n8 oamCounter = 0; + + // halt read + io.openBus = readBus(address); + step(rate()); + + while (io.dmcDMAPending || io.oamDMAPending) { + if (io.oddCycle) { + // put_cycle + if (io.oamDMAPending && oamWriteReady) { + if (io.dmcDMAPending) + dmcReady = true; + + // oam write + writeBus(0x2004, io.openBus); + step(rate()); + + oamWriteReady = false; + if (++oamCounter == 0) + io.oamDMAPending = 0; + } else { + // oam (re)alignment cycle + // dmc alignment cycle + if (io.dmcDMAPending) + dmcReady = true; + + io.openBus = readBus(address); + step(rate()); + } + } else { + // get_cycle + if (io.dmcDMAPending && dmcReady) { + // dmc read + io.openBus = readBus(apu.dmc.dmaAddress()); + step(rate()); + + apu.dmc.setDMABuffer(io.openBus); + io.dmcDMAPending = 0; + dmcReady = false; + } else if (io.oamDMAPending) { + // oam read + io.openBus = readBus(io.oamDMAPage << 8 | oamCounter); + step(rate()); + oamWriteReady = true; + } else { + // dmc dummy read cycle + io.openBus = readBus(address); + step(rate()); + } + } } } @@ -65,12 +111,3 @@ auto CPU::apuLine(bool line) -> void { //level-sensitive io.apuLine = line; } - -auto CPU::rdyLine(bool line) -> void { - io.rdyLine = line; -} - -auto CPU::rdyAddress(bool valid, n16 value) -> void { - io.rdyAddressValid = valid; - io.rdyAddressValue = value; -} diff --git a/ares/fc/system/serialization.cpp b/ares/fc/system/serialization.cpp index ee8dca407..661799ba4 100644 --- a/ares/fc/system/serialization.cpp +++ b/ares/fc/system/serialization.cpp @@ -1,4 +1,4 @@ -static const string SerializerVersion = "v140"; +static const string SerializerVersion = "v141"; auto System::serialize(bool synchronize) -> serializer { if(synchronize) scheduler.enter(Scheduler::Mode::Synchronize);