diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index 7bbdf444..689b2d6a 100644 --- a/higan/emulator/emulator.hpp +++ b/higan/emulator/emulator.hpp @@ -13,7 +13,7 @@ using namespace nall; namespace Emulator { static const string Name = "higan"; - static const string Version = "106.48"; + static const string Version = "106.49"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "https://byuu.org/"; diff --git a/higan/sfc/cpu/cpu.cpp b/higan/sfc/cpu/cpu.cpp index 2f3ab10b..34c5a057 100644 --- a/higan/sfc/cpu/cpu.cpp +++ b/higan/sfc/cpu/cpu.cpp @@ -10,15 +10,6 @@ CPU cpu; #include "irq.cpp" #include "serialization.cpp" -auto CPU::interruptPending() const -> bool { return status.interruptPending; } -auto CPU::pio() const -> uint8 { return io.pio; } -auto CPU::joylatch() const -> bool { return io.joypadStrobeLatch; } -auto CPU::synchronizing() const -> bool { return scheduler.synchronizing(); } - -CPU::CPU() { - PPUcounter::scanline = {&CPU::scanline, this}; -} - auto CPU::Enter() -> void { while(true) scheduler.synchronize(), cpu.main(); } @@ -65,10 +56,16 @@ auto CPU::power(bool reset) -> void { create(Enter, system.cpuFrequency()); coprocessors.reset(); PPUcounter::reset(); + PPUcounter::scanline = {&CPU::scanline, this}; function uint8> reader; function void> writer; + reader = {&CPU::readRAM, this}; + writer = {&CPU::writeRAM, this}; + bus.map(reader, writer, "00-3f,80-bf:0000-1fff", 0x2000); + bus.map(reader, writer, "7e-7f:0000-ffff", 0x20000); + reader = {&CPU::readAPU, this}; writer = {&CPU::writeAPU, this}; bus.map(reader, writer, "00-3f,80-bf:2140-217f"); @@ -81,132 +78,27 @@ auto CPU::power(bool reset) -> void { writer = {&CPU::writeDMA, this}; bus.map(reader, writer, "00-3f,80-bf:4300-437f"); - reader = [](uint24 addr, uint8) -> uint8 { return cpu.wram[addr]; }; - writer = [](uint24 addr, uint8 data) -> void { cpu.wram[addr] = data; }; - bus.map(reader, writer, "00-3f,80-bf:0000-1fff", 0x2000); - bus.map(reader, writer, "7e-7f:0000-ffff", 0x20000); - if(!reset) random.array(wram, sizeof(wram)); - //DMA - for(auto& channel : this->channel) { - channel.dmaEnabled = false; - channel.hdmaEnabled = false; - - channel.direction = 1; - channel.indirect = true; - channel.unused = true; - channel.reverseTransfer = true; - channel.fixedTransfer = true; - channel.transferMode = 7; - - channel.targetAddress = 0xff; - - channel.sourceAddress = 0xffff; - channel.sourceBank = 0xff; - - channel.transferSize = 0xffff; - channel.indirectBank = 0xff; - - channel.hdmaAddress = 0xffff; - channel.lineCounter = 0xff; - channel.unknown = 0xff; - - channel.hdmaCompleted = false; - channel.hdmaDoTransfer = false; + for(uint n : range(8)) { + channels[n] = {}; + if(n != 7) channels[n].next = channels[n + 1]; } - //$2181-$2183 - io.wramAddress = 0x000000; + io = {}; + alu = {}; + pipe = {}; - //$4016-$4017 - io.joypadStrobeLatch = 0; - - //$4200 - io.nmiEnabled = false; - io.hirqEnabled = false; - io.virqEnabled = false; - io.autoJoypadPoll = false; - - //$4201 - io.pio = 0xff; - - //$4202-$4203 - io.wrmpya = 0xff; - io.wrmpyb = 0xff; - - //$4204-$4206 - io.wrdiva = 0xffff; - io.wrdivb = 0xff; - - //$4207-$420a - io.hirqPos = 0x01ff; - io.virqPos = 0x01ff; - - //$420d - io.romSpeed = 8; - - //$4214-$4217 - io.rddiv = 0x0000; - io.rdmpy = 0x0000; - - //$4218-$421f - io.joy1 = 0x0000; - io.joy2 = 0x0000; - io.joy3 = 0x0000; - io.joy4 = 0x0000; - - //ALU - alu.mpyctr = 0; - alu.divctr = 0; - alu.shift = 0; - - //Pipe - pipe.valid = false; - pipe.addr = 0; - pipe.data = 0; - - //Timing - clockCounter = 0; - - status.clockCount = 0; + status = {}; status.lineClocks = lineclocks(); - - status.irqLock = false; status.dramRefreshPosition = (version == 1 ? 530 : 538); - status.dramRefreshed = false; - - status.hdmaInitPosition = (version == 1 ? 12 + 8 - dmaCounter() : 12 + dmaCounter()); - status.hdmaInitTriggered = false; - + status.hdmaSetupPosition = (version == 1 ? 12 + 8 - dmaCounter() : 12 + dmaCounter()); status.hdmaPosition = 1104; - status.hdmaTriggered = false; - - status.nmiValid = false; - status.nmiLine = false; - status.nmiTransition = false; - status.nmiPending = false; - status.nmiHold = false; - - status.irqValid = false; - status.irqLine = false; - status.irqTransition = false; - status.irqPending = false; - status.irqHold = false; - - status.powerPending = true; - status.resetPending = false; + status.powerPending = reset == 0; + status.resetPending = reset == 1; status.interruptPending = true; - status.dmaActive = false; - status.dmaClocks = 0; - status.dmaPending = false; - status.hdmaPending = false; - status.hdmaMode = 0; - - status.autoJoypadActive = false; - status.autoJoypadLatch = false; - status.autoJoypadCounter = 0; + clockCounter = 0; } } diff --git a/higan/sfc/cpu/cpu.hpp b/higan/sfc/cpu/cpu.hpp index 277d32a4..19a67cf8 100644 --- a/higan/sfc/cpu/cpu.hpp +++ b/higan/sfc/cpu/cpu.hpp @@ -1,41 +1,26 @@ struct CPU : Processor::WDC65816, Thread, PPUcounter { - auto interruptPending() const -> bool override; - auto pio() const -> uint8; - auto joylatch() const -> bool; - auto synchronizing() const -> bool override; + inline auto interruptPending() const -> bool override { return status.interruptPending; } + inline auto pio() const -> uint8 { return io.pio; } + inline auto synchronizing() const -> bool override { return scheduler.synchronizing(); } //cpu.cpp - CPU(); - static auto Enter() -> void; auto main() -> void; auto load(Markup::Node) -> bool; auto power(bool reset) -> void; //dma.cpp - auto dmaStep(uint clocks) -> void; - auto dmaTransferValid(uint8 bbus, uint24 abus) -> bool; - auto dmaAddressValid(uint24 abus) -> bool; - auto dmaRead(uint24 abus) -> uint8; - auto dmaWrite(bool valid, uint addr = 0, uint8 data = 0) -> void; - auto dmaTransfer(bool direction, uint8 bbus, uint24 abus) -> void; + inline auto dmaEnable() -> bool; + inline auto hdmaEnable() -> bool; + inline auto hdmaActive() -> bool; - inline auto dmaAddressB(uint n, uint channel) -> uint8; - inline auto dmaAddress(uint n) -> uint24; - inline auto hdmaAddress(uint n) -> uint24; - inline auto hdmaIndirectAddress(uint n) -> uint24; - - inline auto dmaEnabledChannels() -> uint; - inline auto hdmaActive(uint n) -> bool; - inline auto hdmaActiveAfter(uint s) -> bool; - inline auto hdmaEnabledChannels() -> uint; - inline auto hdmaActiveChannels() -> uint; + inline auto dmaStep(uint clocks) -> void; + inline auto dmaFlush() -> void; auto dmaRun() -> void; - auto hdmaUpdate(uint n) -> void; + auto hdmaReset() -> void; + auto hdmaSetup() -> void; auto hdmaRun() -> void; - auto hdmaInitReset() -> void; - auto hdmaInit() -> void; //memory.cpp auto idle() -> void override; @@ -45,12 +30,14 @@ struct CPU : Processor::WDC65816, Thread, PPUcounter { auto readDisassembler(uint24 addr) -> uint8 override; //io.cpp - auto readAPU(uint24 addr, uint8 data) -> uint8; - auto readCPU(uint24 addr, uint8 data) -> uint8; - auto readDMA(uint24 addr, uint8 data) -> uint8; - auto writeAPU(uint24 addr, uint8 data) -> void; - auto writeCPU(uint24 addr, uint8 data) -> void; - auto writeDMA(uint24 addr, uint8 data) -> void; + auto readRAM(uint24 address, uint8 data) -> uint8; + auto readAPU(uint24 address, uint8 data) -> uint8; + auto readCPU(uint24 address, uint8 data) -> uint8; + auto readDMA(uint24 address, uint8 data) -> uint8; + auto writeRAM(uint24 address, uint8 data) -> void; + auto writeAPU(uint24 address, uint8 data) -> void; + auto writeCPU(uint24 address, uint8 data) -> void; + auto writeDMA(uint24 address, uint8 data) -> void; //timing.cpp inline auto dmaCounter() const -> uint; @@ -87,81 +74,76 @@ private: uint clockCounter; struct Status { - bool interruptPending; + uint clockCount = 0; + uint lineClocks = 0; - uint clockCount; - uint lineClocks; + bool irqLock = false; - //timing - bool irqLock; + uint dramRefreshPosition = 0; + bool dramRefreshed = false; - uint dramRefreshPosition; - bool dramRefreshed; + uint hdmaSetupPosition = 0; + bool hdmaSetupTriggered = false; - uint hdmaInitPosition; - bool hdmaInitTriggered; + uint hdmaPosition = 0; + bool hdmaTriggered = false; - uint hdmaPosition; - bool hdmaTriggered; + boolean nmiValid; + boolean nmiLine; + boolean nmiTransition; + boolean nmiPending; + boolean nmiHold; - bool nmiValid; - bool nmiLine; - bool nmiTransition; - bool nmiPending; - bool nmiHold; + boolean irqValid; + boolean irqLine; + boolean irqTransition; + boolean irqPending; + boolean irqHold; - bool irqValid; - bool irqLine; - bool irqTransition; - bool irqPending; - bool irqHold; + bool powerPending = false; + bool resetPending = false; - bool powerPending; - bool resetPending; + bool interruptPending = false; - //DMA - bool dmaActive; - uint dmaClocks; - bool dmaPending; - bool hdmaPending; - bool hdmaMode; //0 = init, 1 = run + bool dmaActive = false; + uint dmaClocks = 0; + bool dmaPending = false; + bool hdmaPending = false; + bool hdmaMode = 0; //0 = init, 1 = run - //auto joypad polling - bool autoJoypadActive; - bool autoJoypadLatch; - uint autoJoypadCounter; + bool autoJoypadActive = false; + bool autoJoypadLatch = false; + uint autoJoypadCounter = 0; } status; struct IO { //$2181-$2183 uint17 wramAddress; - //$4016-$4017 - bool joypadStrobeLatch; - //$4200 - bool nmiEnabled; - bool hirqEnabled; - bool virqEnabled; - bool autoJoypadPoll; + boolean hirqEnable; + boolean virqEnable; + boolean irqEnable; + boolean nmiEnable; + boolean autoJoypadPoll; //$4201 - uint8 pio; + uint8 pio = 0xff; //$4202-$4203 - uint8 wrmpya; - uint8 wrmpyb; + uint8 wrmpya = 0xff; + uint8 wrmpyb = 0xff; //$4204-$4206 - uint16 wrdiva; - uint8 wrdivb; + uint16 wrdiva = 0xffff; + uint8 wrdivb = 0xff; //$4207-$420a - uint9 hirqPos; - uint9 virqPos; + uint9 hirqPos = 0x1ff; + uint9 virqPos = 0x1ff; //$420d - uint romSpeed; + uint romSpeed = 8; //$4214-$4217 uint16 rddiv; @@ -175,34 +157,54 @@ private: } io; struct ALU { - uint mpyctr; - uint divctr; - uint shift; + uint mpyctr = 0; + uint divctr = 0; + uint shift = 0; } alu; struct Channel { + //dma.cpp + inline auto step(uint clocks) -> void; + inline auto edge() -> void; + inline auto valid(uint24 address) -> bool; + inline auto read(uint24 address, bool valid) -> uint8; + inline auto read(uint24 address) -> uint8; + inline auto flush() -> void; + inline auto write(uint24 address, uint8 data, bool valid) -> void; + inline auto write(uint24 address, uint8 data) -> void; + inline auto transfer(uint24 address, uint2 index) -> void; + + inline auto dmaRun() -> void; + inline auto hdmaActive() -> bool; + inline auto hdmaFinished() -> bool; + inline auto hdmaReset() -> void; + inline auto hdmaSetup() -> void; + inline auto hdmaReload() -> void; + inline auto hdmaTransfer() -> void; + inline auto hdmaAdvance() -> void; + //$420b - bool dmaEnabled; + uint1 dmaEnable; //$420c - bool hdmaEnabled; + uint1 hdmaEnable; //$43x0 - bool direction; - bool indirect; - bool unused; - bool reverseTransfer; - bool fixedTransfer; - uint3 transferMode; + uint3 transferMode = 7; + uint1 fixedTransfer = 1; + uint1 reverseTransfer = 1; + uint1 unused = 1; + uint1 indirect = 1; + uint1 direction = 1; //$43x1 - uint8 targetAddress; + uint8 targetAddress = 0xff; //$43x2-$43x3 - uint16 sourceAddress; + uint16 sourceAddress = 0xffff; //$43x4 - uint8 sourceBank; + uint8 sourceBank = 0xff; //$43x5-$43x6 union { @@ -211,28 +213,30 @@ private: }; //$43x7 - uint8 indirectBank; + uint8 indirectBank = 0xff; //$43x8-$43x9 - uint16 hdmaAddress; + uint16 hdmaAddress = 0xffff; //$43xa - uint8 lineCounter; + uint8 lineCounter = 0xff; //$43xb/$43xf - uint8 unknown; + uint8 unknown = 0xff; //internal state - bool hdmaCompleted; - bool hdmaDoTransfer; + uint1 hdmaCompleted; + uint1 hdmaDoTransfer; - Channel() : transferSize(0) {} - } channel[8]; + maybe next; + + Channel() : transferSize(0xffff) {} + } channels[8]; struct Pipe { - bool valid; - uint addr; - uint8 data; + uint1 valid; + uint24 address; + uint8 data; } pipe; }; diff --git a/higan/sfc/cpu/dma.cpp b/higan/sfc/cpu/dma.cpp index 4102c080..e23a4829 100644 --- a/higan/sfc/cpu/dma.cpp +++ b/higan/sfc/cpu/dma.cpp @@ -1,240 +1,204 @@ +auto CPU::dmaEnable() -> bool { + for(auto& channel : channels) if(channel.dmaEnable) return true; + return false; +} + +auto CPU::hdmaEnable() -> bool { + for(auto& channel : channels) if(channel.hdmaEnable) return true; + return false; +} + +auto CPU::hdmaActive() -> bool { + for(auto& channel : channels) if(channel.hdmaActive()) return true; + return false; +} + auto CPU::dmaStep(uint clocks) -> void { status.dmaClocks += clocks; step(clocks); } -//============= -//memory access -//============= - -auto CPU::dmaTransferValid(uint8 bbus, uint24 abus) -> bool { - //transfers from WRAM to WRAM are invalid; chip only has one address bus - if(bbus == 0x80 && ((abus & 0xfe0000) == 0x7e0000 || (abus & 0x40e000) == 0x0000)) return false; - return true; +auto CPU::dmaFlush() -> void { + if(!pipe.valid) return; + pipe.valid = false; + bus.write(pipe.address, pipe.data); } -auto CPU::dmaAddressValid(uint24 abus) -> bool { - //A-bus access to B-bus or S-CPU registers are invalid - if((abus & 0x40ff00) == 0x2100) return false; //$00-3f,80-bf:2100-21ff - if((abus & 0x40fe00) == 0x4000) return false; //$00-3f,80-bf:4000-41ff - if((abus & 0x40ffe0) == 0x4200) return false; //$00-3f,80-bf:4200-421f - if((abus & 0x40ff80) == 0x4300) return false; //$00-3f,80-bf:4300-437f - return true; -} - -auto CPU::dmaRead(uint24 abus) -> uint8 { - if(!dmaAddressValid(abus)) return 0x00; - return bus.read(abus, r.mdr); -} - -//simulate two-stage pipeline for DMA transfers; example: -//cycle 0: read N+0 -//cycle 1: write N+0 & read N+1 (parallel; one on A-bus, one on B-bus) -//cycle 2: write N+1 & read N+2 (parallel) -//cycle 3: write N+2 -auto CPU::dmaWrite(bool valid, uint addr, uint8 data) -> void { - if(pipe.valid) bus.write(pipe.addr, pipe.data); - pipe.valid = valid; - pipe.addr = addr; - pipe.data = data; -} - -auto CPU::dmaTransfer(bool direction, uint8 bbus, uint24 abus) -> void { - if(direction == 0) { - dmaStep(4); - r.mdr = dmaRead(abus); - dmaStep(4); - dmaWrite(dmaTransferValid(bbus, abus), 0x2100 | bbus, r.mdr); - } else { - dmaStep(4); - r.mdr = dmaTransferValid(bbus, abus) ? bus.read(0x2100 | bbus, r.mdr) : (uint8)0x00; - dmaStep(4); - dmaWrite(dmaAddressValid(abus), abus, r.mdr); - } -} - -//=================== -//address calculation -//=================== - -auto CPU::dmaAddressB(uint n, uint index) -> uint8 { - switch(channel[n].transferMode) { - case 0: return (channel[n].targetAddress); //0 - case 1: return (channel[n].targetAddress + (index & 1)); //0,1 - case 2: return (channel[n].targetAddress); //0,0 - case 3: return (channel[n].targetAddress + ((index >> 1) & 1)); //0,0,1,1 - case 4: return (channel[n].targetAddress + (index & 3)); //0,1,2,3 - case 5: return (channel[n].targetAddress + (index & 1)); //0,1,0,1 - case 6: return (channel[n].targetAddress); //0,0 [2] - case 7: return (channel[n].targetAddress + ((index >> 1) & 1)); //0,0,1,1 [3] - } - unreachable; -} - -auto CPU::dmaAddress(uint n) -> uint24 { - uint24 addr = channel[n].sourceBank << 16 | channel[n].sourceAddress; - - if(!channel[n].fixedTransfer) { - if(!channel[n].reverseTransfer) { - channel[n].sourceAddress++; - } else { - channel[n].sourceAddress--; - } - } - - return addr; -} - -auto CPU::hdmaAddress(uint n) -> uint24 { - return channel[n].sourceBank << 16 | channel[n].hdmaAddress++; -} - -auto CPU::hdmaIndirectAddress(uint n) -> uint24 { - return channel[n].indirectBank << 16 | channel[n].indirectAddress++; -} - -//============== -//channel status -//============== - -auto CPU::dmaEnabledChannels() -> uint { - uint count = 0; - for(auto n : range(8)) count += channel[n].dmaEnabled; - return count; -} - -auto CPU::hdmaActive(uint n) -> bool { - return channel[n].hdmaEnabled && !channel[n].hdmaCompleted; -} - -auto CPU::hdmaActiveAfter(uint s) -> bool { - for(uint n = s + 1; n < 8; n++) { - if(hdmaActive(n)) return true; - } - return false; -} - -auto CPU::hdmaEnabledChannels() -> uint { - uint count = 0; - for(auto n : range(8)) count += channel[n].hdmaEnabled; - return count; -} - -auto CPU::hdmaActiveChannels() -> uint { - uint count = 0; - for(auto n : range(8)) count += hdmaActive(n); - return count; -} - -//============== -//core functions -//============== - auto CPU::dmaRun() -> void { dmaStep(8); - dmaWrite(false); + dmaFlush(); dmaEdge(); - - for(auto n : range(8)) { - if(!channel[n].dmaEnabled) continue; - - uint index = 0; - do { - dmaTransfer(channel[n].direction, dmaAddressB(n, index++), dmaAddress(n)); - dmaEdge(); - } while(channel[n].dmaEnabled && --channel[n].transferSize); - - dmaStep(8); - dmaWrite(false); - dmaEdge(); - - channel[n].dmaEnabled = false; - } - + for(auto& channel : channels) channel.dmaRun(); + dmaFlush(); status.irqLock = true; } -auto CPU::hdmaUpdate(uint n) -> void { - dmaStep(4); - r.mdr = dmaRead(channel[n].sourceBank << 16 | channel[n].hdmaAddress); - dmaStep(4); - dmaWrite(false); +auto CPU::hdmaReset() -> void { + for(auto& channel : channels) channel.hdmaReset(); +} - if((channel[n].lineCounter & 0x7f) == 0) { - channel[n].lineCounter = r.mdr; - channel[n].hdmaAddress++; - - channel[n].hdmaCompleted = channel[n].lineCounter == 0; - channel[n].hdmaDoTransfer = !channel[n].hdmaCompleted; - - if(channel[n].indirect) { - dmaStep(4); - r.mdr = dmaRead(hdmaAddress(n)); - channel[n].indirectAddress = r.mdr << 8; - dmaStep(4); - dmaWrite(false); - - if(!channel[n].hdmaCompleted || hdmaActiveAfter(n)) { - dmaStep(4); - r.mdr = dmaRead(hdmaAddress(n)); - channel[n].indirectAddress >>= 8; - channel[n].indirectAddress |= r.mdr << 8; - dmaStep(4); - dmaWrite(false); - } - } - } +auto CPU::hdmaSetup() -> void { + dmaStep(8); + dmaFlush(); + for(auto& channel : channels) channel.hdmaSetup(); + dmaFlush(); + status.irqLock = true; } auto CPU::hdmaRun() -> void { dmaStep(8); - dmaWrite(false); + dmaFlush(); + for(auto& channel : channels) channel.hdmaTransfer(); + for(auto& channel : channels) channel.hdmaAdvance(); + dmaFlush(); + status.irqLock = true; +} - for(auto n : range(8)) { - if(!hdmaActive(n)) continue; - channel[n].dmaEnabled = false; //HDMA run during DMA will stop DMA mid-transfer +// - if(channel[n].hdmaDoTransfer) { - static const uint transferLength[8] = {1, 2, 2, 4, 4, 4, 2, 4}; - uint length = transferLength[channel[n].transferMode]; - for(auto index : range(length)) { - uint addr = !channel[n].indirect ? hdmaAddress(n) : hdmaIndirectAddress(n); - dmaTransfer(channel[n].direction, dmaAddressB(n, index), addr); - } +auto CPU::Channel::step(uint clocks) -> void { + return cpu.dmaStep(clocks); +} + +auto CPU::Channel::edge() -> void { + return cpu.dmaEdge(); +} + +auto CPU::Channel::valid(uint24 address) -> bool { + //A-bus cannot access the B-bus or CPU I/O registers + if((address & 0x40ff00) == 0x2100) return false; //00-3f,80-bf:2100-21ff + if((address & 0x40fe00) == 0x4000) return false; //00-3f,80-bf:4000-41ff + if((address & 0x40ffe0) == 0x4200) return false; //00-3f,80-bf:4200-421f + if((address & 0x40ff80) == 0x4300) return false; //00-3f,80-bf:4300-437f + return true; +} + +auto CPU::Channel::read(uint24 address, bool valid) -> uint8 { + step(4); + cpu.r.mdr = valid ? bus.read(address, cpu.r.mdr) : (uint8)0x00; + step(4); + flush(); + return cpu.r.mdr; +} + +auto CPU::Channel::read(uint24 address) -> uint8 { + return read(address, valid(address)); +} + +auto CPU::Channel::flush() -> void { + return cpu.dmaFlush(); +} + +auto CPU::Channel::write(uint24 address, uint8 data, bool valid) -> void { + cpu.pipe.valid = valid; + cpu.pipe.address = address; + cpu.pipe.data = data; +} + +auto CPU::Channel::write(uint24 address, uint8 data) -> void { + return write(address, data, valid(address)); +} + +auto CPU::Channel::transfer(uint24 aAddress, uint2 index) -> void { + uint24 bAddress = 0x2100 | targetAddress; + switch(transferMode) { + case 1: case 5: bAddress += index.bit(0); break; + case 3: case 7: bAddress += index.bit(1); break; + case 4: bAddress += index; break; + } + + //transfers from WRAM to WRAM are invalid + bool valid = bAddress != 0x2180 || ((aAddress & 0xfe0000) != 0x7e0000 && (aAddress & 0x40e000) != 0x0000); + + if(direction == 0) { + auto data = read(aAddress); + write(bAddress, data, valid); + } else { + auto data = read(bAddress, valid); + write(aAddress, data); + } +} + +auto CPU::Channel::dmaRun() -> void { + if(!dmaEnable) return; + + uint2 index = 0; + do { + transfer(sourceBank << 16 | sourceAddress, index++); + if(!fixedTransfer) !reverseTransfer ? sourceAddress++ : sourceAddress--; + edge(); + } while(dmaEnable && --transferSize); + + step(8); + flush(); + edge(); + dmaEnable = false; +} + +auto CPU::Channel::hdmaActive() -> bool { + return hdmaEnable && !hdmaCompleted; +} + +auto CPU::Channel::hdmaFinished() -> bool { + auto channel = next; + while(channel) { + if(channel->hdmaActive()) return false; + channel = channel->next; + } + return true; +} + +auto CPU::Channel::hdmaReset() -> void { + hdmaCompleted = false; + hdmaDoTransfer = false; +} + +auto CPU::Channel::hdmaSetup() -> void { + hdmaDoTransfer = true; //note: needs hardware verification + if(!hdmaEnable) return; + + dmaEnable = false; //HDMA will stop active DMA mid-transfer + hdmaAddress = sourceAddress; + lineCounter = 0; + hdmaReload(); +} + +auto CPU::Channel::hdmaReload() -> void { + auto data = read(sourceBank << 16 | hdmaAddress); + + if((uint7)lineCounter == 0) { + lineCounter = data; + hdmaAddress++; + + hdmaCompleted = lineCounter == 0; + hdmaDoTransfer = !hdmaCompleted; + + if(indirect) { + data = read(sourceBank << 16 | hdmaAddress++); + indirectAddress = data << 8 | 0x00; //todo: should 0x00 be indirectAddress >> 8 ? + if(hdmaCompleted && hdmaFinished()) return; + + data = read(sourceBank << 16 | hdmaAddress++); + indirectAddress = data << 8 | indirectAddress >> 8; } } - - for(auto n : range(8)) { - if(!hdmaActive(n)) continue; - - channel[n].lineCounter--; - channel[n].hdmaDoTransfer = channel[n].lineCounter & 0x80; - hdmaUpdate(n); - } - - status.irqLock = true; } -auto CPU::hdmaInitReset() -> void { - for(auto n : range(8)) { - channel[n].hdmaCompleted = false; - channel[n].hdmaDoTransfer = false; +auto CPU::Channel::hdmaTransfer() -> void { + if(!hdmaActive()) return; + dmaEnable = false; //HDMA will stop active DMA mid-transfer + if(!hdmaDoTransfer) return; + + static const uint lengths[8] = {1, 2, 2, 4, 4, 4, 2, 4}; + for(uint2 index : range(lengths[transferMode])) { + uint24 address = !indirect ? sourceBank << 16 | hdmaAddress++ : indirectBank << 16 | indirectAddress++; + transfer(address, index); } } -auto CPU::hdmaInit() -> void { - dmaStep(8); - dmaWrite(false); - - for(auto n : range(8)) { - channel[n].hdmaDoTransfer = true; //note: needs hardware verification (2017-08-09) - if(!channel[n].hdmaEnabled) continue; - channel[n].dmaEnabled = false; //HDMA init during DMA will stop DMA mid-transfer - - channel[n].hdmaAddress = channel[n].sourceAddress; - channel[n].lineCounter = 0; - hdmaUpdate(n); - } - - status.irqLock = true; +auto CPU::Channel::hdmaAdvance() -> void { + if(!hdmaActive()) return; + lineCounter--; + hdmaDoTransfer = lineCounter.bit(7); + hdmaReload(); } diff --git a/higan/sfc/cpu/io.cpp b/higan/sfc/cpu/io.cpp index d84ee4f3..84771498 100644 --- a/higan/sfc/cpu/io.cpp +++ b/higan/sfc/cpu/io.cpp @@ -1,3 +1,7 @@ +auto CPU::readRAM(uint24 addr, uint8 data) -> uint8 { + return wram[addr]; +} + auto CPU::readAPU(uint24 addr, uint8 data) -> uint8 { synchronize(smp); return smp.portRead(addr.bits(0,1)); @@ -5,97 +9,53 @@ auto CPU::readAPU(uint24 addr, uint8 data) -> uint8 { auto CPU::readCPU(uint24 addr, uint8 data) -> uint8 { switch((uint16)addr) { + case 0x2180: //WMDATA + return bus.read(0x7e0000 | io.wramAddress++, data); - //WMDATA - case 0x2180: { - return bus.read(0x7e0000 | io.wramAddress++, r.mdr); - } + case 0x4016: //JOYSER0 + data &= 0xfc; + data |= controllerPort1.device->data(); + return data; - //JOYSER0 - //7-2 = MDR - //1-0 = Joypad serial data - case 0x4016: { - uint8 v = r.mdr & 0xfc; - v |= controllerPort1.device->data(); - return v; - } + case 0x4017: //JOYSER1 + data &= 0xe0; + data |= 0x1c; //pins are connected to GND + data |= controllerPort2.device->data(); + return data; - //JOYSER1 - case 0x4017: { - //7-5 = MDR - //4-2 = Always 1 (pins are connected to GND) - //1-0 = Joypad serial data - uint8 v = (r.mdr & 0xe0) | 0x1c; - v |= controllerPort2.device->data(); - return v; - } + case 0x4210: //RDNMI + data &= 0x70; + data |= rdnmi() << 7; + data |= (uint4)version; + return data; - //RDNMI - case 0x4210: { - //7 = NMI acknowledge - //6-4 = MDR - //3-0 = CPU (5a22) version - uint8 v = (r.mdr & 0x70); - v |= (uint8)(rdnmi()) << 7; - v |= (version & 0x0f); - return v; - } + case 0x4211: //TIMEUP + data &= 0x7f; + data |= timeup() << 7; + return data; - //TIMEUP - case 0x4211: { - //7 = IRQ acknowledge - //6-0 = MDR - uint8 v = (r.mdr & 0x7f); - v |= (uint8)(timeup()) << 7; - return v; - } + case 0x4212: //HVBJOY + data &= 0x3e; + data |= (status.autoJoypadActive) << 0; + data |= (hcounter() <= 2 || hcounter() >= 1096) << 6; //hblank + data |= (vcounter() >= ppu.vdisp()) << 7; //vblank + return data; - //HVBJOY - case 0x4212: { - //7 = VBLANK acknowledge - //6 = HBLANK acknowledge - //5-1 = MDR - //0 = JOYPAD acknowledge - uint8 v = (r.mdr & 0x3e); - if(status.autoJoypadActive) v |= 0x01; - if(hcounter() <= 2 || hcounter() >= 1096) v |= 0x40; //hblank - if(vcounter() >= ppu.vdisp()) v |= 0x80; //vblank - return v; - } + case 0x4213: return io.pio; //RDIO - //RDIO - case 0x4213: { - return io.pio; - } + case 0x4214: return io.rddiv.byte(0); //RDDIVL + case 0x4215: return io.rddiv.byte(1); //RDDIVH + case 0x4216: return io.rdmpy.byte(0); //RDMPYL + case 0x4217: return io.rdmpy.byte(1); //RDMPYH - //RDDIVL - case 0x4214: { - return io.rddiv.byte(0); - } - - //RDDIVH - case 0x4215: { - return io.rddiv.byte(1); - } - - //RDMPYL - case 0x4216: { - return io.rdmpy.byte(0); - } - - //RDMPYH - case 0x4217: { - return io.rdmpy.byte(1); - } - - case 0x4218: return io.joy1.byte(0); //JOY1L - case 0x4219: return io.joy1.byte(1); //JOY1H - case 0x421a: return io.joy2.byte(0); //JOY2L - case 0x421b: return io.joy2.byte(1); //JOY2H - case 0x421c: return io.joy3.byte(0); //JOY3L - case 0x421d: return io.joy3.byte(1); //JOY3H - case 0x421e: return io.joy4.byte(0); //JOY4L - case 0x421f: return io.joy4.byte(1); //JOY4H + case 0x4218: return io.joy1.byte(0); //JOY1L + case 0x4219: return io.joy1.byte(1); //JOY1H + case 0x421a: return io.joy2.byte(0); //JOY2L + case 0x421b: return io.joy2.byte(1); //JOY2H + case 0x421c: return io.joy3.byte(0); //JOY3L + case 0x421d: return io.joy3.byte(1); //JOY3H + case 0x421e: return io.joy4.byte(0); //JOY4L + case 0x421f: return io.joy4.byte(1); //JOY4H } @@ -103,59 +63,42 @@ auto CPU::readCPU(uint24 addr, uint8 data) -> uint8 { } auto CPU::readDMA(uint24 addr, uint8 data) -> uint8 { - auto& channel = this->channel[addr.bits(4,6)]; + auto& channel = this->channels[addr.bits(4,6)]; - switch(addr & 0xff0f) { + switch(addr & 0xff8f) { - //DMAPx - case 0x4300: return ( - channel.transferMode << 0 - | channel.fixedTransfer << 3 - | channel.reverseTransfer << 4 - | channel.unused << 5 - | channel.indirect << 6 - | channel.direction << 7 - ); + case 0x4300: //DMAPx + return ( + channel.transferMode << 0 + | channel.fixedTransfer << 3 + | channel.reverseTransfer << 4 + | channel.unused << 5 + | channel.indirect << 6 + | channel.direction << 7 + ); - //BBADx - case 0x4301: return channel.targetAddress; - - //A1TxL - case 0x4302: return channel.sourceAddress >> 0; - - //A1TxH - case 0x4303: return channel.sourceAddress >> 8; - - //A1Bx - case 0x4304: return channel.sourceBank; - - //DASxL -- union { uint16 transferSize; uint16 indirectAddress; }; - case 0x4305: return channel.transferSize.byte(0); - - //DASxH -- union { uint16 transferSize; uint16 indirectAddress; }; - case 0x4306: return channel.transferSize.byte(1); - - //DASBx - case 0x4307: return channel.indirectBank; - - //A2AxL - case 0x4308: return channel.hdmaAddress.byte(0); - - //A2AxH - case 0x4309: return channel.hdmaAddress.byte(1); - - //NTRLx - case 0x430a: return channel.lineCounter; - - //??? - case 0x430b: - case 0x430f: return channel.unknown; + case 0x4301: return channel.targetAddress; //BBADx + case 0x4302: return channel.sourceAddress.byte(0); //A1TxL + case 0x4303: return channel.sourceAddress.byte(1); //A1TxH + case 0x4304: return channel.sourceBank; //A1Bx + case 0x4305: return channel.transferSize.byte(0); //DASxL + case 0x4306: return channel.transferSize.byte(1); //DASxH + case 0x4307: return channel.indirectBank; //DASBx + case 0x4308: return channel.hdmaAddress.byte(0); //A2AxL + case 0x4309: return channel.hdmaAddress.byte(1); //A2AxH + case 0x430a: return channel.lineCounter; //NTRLx + case 0x430b: return channel.unknown; //???x + case 0x430f: return channel.unknown; //???x ($43xb mirror) } return data; } +auto CPU::writeRAM(uint24 addr, uint8 data) -> void { + wram[addr] = data; +} + auto CPU::writeAPU(uint24 addr, uint8 data) -> void { synchronize(smp); return smp.portWrite(addr.bits(0,1), data); @@ -164,44 +107,44 @@ auto CPU::writeAPU(uint24 addr, uint8 data) -> void { auto CPU::writeCPU(uint24 addr, uint8 data) -> void { switch((uint16)addr) { - //WMDATA - case 0x2180: { + case 0x2180: //WMDATA return bus.write(0x7e0000 | io.wramAddress++, data); - } - case 0x2181: io.wramAddress.bits( 0, 7) = data; return; //WMADDL - case 0x2182: io.wramAddress.bits( 8,15) = data; return; //WMADDM - case 0x2183: io.wramAddress.bit (16 ) = data.bit(0); return; //WMADDH + case 0x2181: //WMADDL + io.wramAddress.bits(0,7) = data; + return; - //JOYSER0 - case 0x4016: { - //bit 0 is shared between JOYSER0 and JOYSER1, therefore + case 0x2182: //WMADDM + io.wramAddress.bits(8,15) = data; + return; + + case 0x2183: //WMADDH + io.wramAddress.bit(16) = data.bit(0); + return; + + case 0x4016: //JOYSER0 + //bit 0 is shared between JOYSER0 and JOYSER1: //strobing $4016.d0 affects both controller port latches. //$4017 bit 0 writes are ignored. controllerPort1.device->latch(data.bit(0)); controllerPort2.device->latch(data.bit(0)); return; - } - //NMITIMEN - case 0x4200: { + case 0x4200: //NMITIMEN io.autoJoypadPoll = data.bit(0); nmitimenUpdate(data); return; - } - //WRIO - case 0x4201: { + case 0x4201: //WRIO if(io.pio.bit(7) && !data.bit(7)) ppu.latchCounters(); io.pio = data; return; - } - //WRMPYA - case 0x4202: io.wrmpya = data; return; + case 0x4202: //WRMPYA + io.wrmpya = data; + return; - //WRMPYB - case 0x4203: { + case 0x4203: //WRMPYB io.rdmpy = 0; if(alu.mpyctr || alu.divctr) return; @@ -211,13 +154,16 @@ auto CPU::writeCPU(uint24 addr, uint8 data) -> void { alu.mpyctr = 8; //perform multiplication over the next eight cycles alu.shift = io.wrmpyb; return; - } - case 0x4204: { io.wrdiva.byte(0) = data; return; } //WRDIVL - case 0x4205: { io.wrdiva.byte(1) = data; return; } //WRDIVH + case 0x4204: //WRDIVL + io.wrdiva.byte(0) = data; + return; - //WRDIVB - case 0x4206: { + case 0x4205: //WRDIVH + io.wrdiva.byte(1) = data; + return; + + case 0x4206: //WRDIVB io.rdmpy = io.wrdiva; if(alu.mpyctr || alu.divctr) return; @@ -226,43 +172,45 @@ auto CPU::writeCPU(uint24 addr, uint8 data) -> void { alu.divctr = 16; //perform division over the next sixteen cycles alu.shift = io.wrdivb << 16; return; - } - case 0x4207: io.hirqPos.bits(0,7) = data; return; //HTIMEL - case 0x4208: io.hirqPos.bit (8 ) = data.bit(0); return; //HTIMEH + case 0x4207: //HTIMEL + io.hirqPos.bits(0,7) = data; + return; - case 0x4209: io.virqPos.bits(0,7) = data; return; //VTIMEL - case 0x420a: io.virqPos.bit (8 ) = data.bit(0); return; //VTIMEH + case 0x4208: //HTIMEH + io.hirqPos.bit(8) = data.bit(0); + return; - //DMAEN - case 0x420b: { - for(auto n : range(8)) channel[n].dmaEnabled = data.bit(n); + case 0x4209: //VTIMEL + io.virqPos.bits(0,7) = data; + return; + + case 0x420a: //VTIMEH + io.virqPos.bit(8) = data.bit(0); + return; + + case 0x420b: //DMAEN + for(auto n : range(8)) channels[n].dmaEnable = data.bit(n); if(data) status.dmaPending = true; return; - } - //HDMAEN - case 0x420c: { - for(auto n : range(8)) channel[n].hdmaEnabled = data.bit(n); + case 0x420c: //HDMAEN + for(auto n : range(8)) channels[n].hdmaEnable = data.bit(n); return; - } - //MEMSEL - case 0x420d: { + case 0x420d: //MEMSEL io.romSpeed = data.bit(0) ? 6 : 8; return; - } } } auto CPU::writeDMA(uint24 addr, uint8 data) -> void { - auto& channel = this->channel[addr.bits(4,6)]; + auto& channel = this->channels[addr.bits(4,6)]; - switch(addr & 0xff0f) { + switch(addr & 0xff8f) { - //DMAPx - case 0x4300: { + case 0x4300: //DMAPx channel.transferMode = data.bits(0,2); channel.fixedTransfer = data.bit (3); channel.reverseTransfer = data.bit (4); @@ -270,41 +218,54 @@ auto CPU::writeDMA(uint24 addr, uint8 data) -> void { channel.indirect = data.bit (6); channel.direction = data.bit (7); return; - } - //DDBADx - case 0x4301: channel.targetAddress = data; return; + case 0x4301: //BBADx + channel.targetAddress = data; + return; - //A1TxL - case 0x4302: channel.sourceAddress.byte(0) = data; return; + case 0x4302: //A1TxL + channel.sourceAddress.byte(0) = data; + return; - //A1TxH - case 0x4303: channel.sourceAddress.byte(1) = data; return; + case 0x4303: //A1TxH + channel.sourceAddress.byte(1) = data; + return; - //A1Bx - case 0x4304: channel.sourceBank = data; return; + case 0x4304: //A1Bx + channel.sourceBank = data; + return; - //DASxL -- union { uint16 transferSize; uint16 indirectAddress; }; - case 0x4305: channel.transferSize.byte(0) = data; return; + case 0x4305: //DASxL + channel.transferSize.byte(0) = data; + return; - //DASxH -- union { uint16 transferSize; uint16 indirectAddress; }; - case 0x4306: channel.transferSize.byte(1) = data; return; + case 0x4306: //DASxH + channel.transferSize.byte(1) = data; + return; - //DASBx - case 0x4307: channel.indirectBank = data; return; + case 0x4307: //DASBx + channel.indirectBank = data; + return; - //A2AxL - case 0x4308: channel.hdmaAddress.byte(0) = data; return; + case 0x4308: //A2AxL + channel.hdmaAddress.byte(0) = data; + return; - //A2AxH - case 0x4309: channel.hdmaAddress.byte(1) = data; return; + case 0x4309: //A2AxH + channel.hdmaAddress.byte(1) = data; + return; - //NTRLx - case 0x430a: channel.lineCounter = data; return; + case 0x430a: //NTRLx + channel.lineCounter = data; + return; - //??? - case 0x430b: - case 0x430f: channel.unknown = data; return; + case 0x430b: //???x + channel.unknown = data; + return; + + case 0x430f: //???x ($43xb mirror) + channel.unknown = data; + return; } } diff --git a/higan/sfc/cpu/irq.cpp b/higan/sfc/cpu/irq.cpp index 5d268e0c..6153b389 100644 --- a/higan/sfc/cpu/irq.cpp +++ b/higan/sfc/cpu/irq.cpp @@ -5,68 +5,40 @@ //it is used to emulate hardware communication delay between opcode and interrupt units. auto CPU::pollInterrupts() -> void { //NMI hold - if(status.nmiHold) { - status.nmiHold = false; - if(io.nmiEnabled) status.nmiTransition = true; - } + if(status.nmiHold.lower() && io.nmiEnable) status.nmiTransition = true; //NMI test - bool nmiValid = vcounter(2) >= ppu.vdisp(); - if(!status.nmiValid && nmiValid) { - //0->1 edge sensitive transition - status.nmiLine = true; - status.nmiHold = true; //hold /NMI for four cycles - } else if(status.nmiValid && !nmiValid) { - //1->0 edge sensitive transition - status.nmiLine = false; + if(status.nmiValid.flip(vcounter(2) >= ppu.vdisp())) { + if(status.nmiLine = status.nmiValid) status.nmiHold = true; //hold /NMI for four cycles } - status.nmiValid = nmiValid; //IRQ hold status.irqHold = false; - if(status.irqLine) { - if(io.virqEnabled || io.hirqEnabled) status.irqTransition = true; - } + if(status.irqLine && io.irqEnable) status.irqTransition = true; //IRQ test - bool irqValid = io.virqEnabled || io.hirqEnabled; - if(irqValid) { - if((io.virqEnabled && vcounter(10) != (io.virqPos)) - || (io.hirqEnabled && hcounter(10) != (io.hirqPos + 1) * 4) - || (io.virqPos && vcounter(6) == 0) //IRQs cannot trigger on last dot of field - ) irqValid = false; - } - if(!status.irqValid && irqValid) { - //0->1 edge sensitive transition - status.irqLine = true; - status.irqHold = true; //hold /IRQ for four cycles - } - status.irqValid = irqValid; + if(status.irqValid.raise(io.irqEnable + && (!io.virqEnable || vcounter(10) == io.virqPos) + && (!io.hirqEnable || hcounter(10) == io.hirqPos + 1 << 2) + )) status.irqLine = status.irqHold = true; //hold /IRQ for four cycles } auto CPU::nmitimenUpdate(uint8 data) -> void { - bool nmiEnabled = io.nmiEnabled; - bool virqEnabled = io.virqEnabled; - bool hirqEnabled = io.hirqEnabled; - io.nmiEnabled = data & 0x80; - io.virqEnabled = data & 0x20; - io.hirqEnabled = data & 0x10; + io.hirqEnable = data.bit(4); + io.virqEnable = data.bit(5); + io.irqEnable = io.hirqEnable || io.virqEnable; - //0->1 edge sensitive transition - if(!nmiEnabled && io.nmiEnabled && status.nmiLine) { - status.nmiTransition = true; - } - - //?->1 level sensitive transition - if(io.virqEnabled && !io.hirqEnabled && status.irqLine) { + if(io.virqEnable && !io.hirqEnable && status.irqLine) { status.irqTransition = true; - } - - if(!io.virqEnabled && !io.hirqEnabled) { + } 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; } diff --git a/higan/sfc/cpu/serialization.cpp b/higan/sfc/cpu/serialization.cpp index deff2249..0050baa0 100644 --- a/higan/sfc/cpu/serialization.cpp +++ b/higan/sfc/cpu/serialization.cpp @@ -8,8 +8,6 @@ auto CPU::serialize(serializer& s) -> void { s.integer(version); s.integer(clockCounter); - s.integer(status.interruptPending); - s.integer(status.clockCount); s.integer(status.lineClocks); @@ -18,27 +16,29 @@ auto CPU::serialize(serializer& s) -> void { s.integer(status.dramRefreshPosition); s.integer(status.dramRefreshed); - s.integer(status.hdmaInitPosition); - s.integer(status.hdmaInitTriggered); + s.integer(status.hdmaSetupPosition); + s.integer(status.hdmaSetupTriggered); s.integer(status.hdmaPosition); s.integer(status.hdmaTriggered); - s.integer(status.nmiValid); - s.integer(status.nmiLine); - s.integer(status.nmiTransition); - s.integer(status.nmiPending); - s.integer(status.nmiHold); + s.boolean(status.nmiValid); + s.boolean(status.nmiLine); + s.boolean(status.nmiTransition); + s.boolean(status.nmiPending); + s.boolean(status.nmiHold); - s.integer(status.irqValid); - s.integer(status.irqLine); - s.integer(status.irqTransition); - s.integer(status.irqPending); - s.integer(status.irqHold); + s.boolean(status.irqValid); + s.boolean(status.irqLine); + s.boolean(status.irqTransition); + s.boolean(status.irqPending); + s.boolean(status.irqHold); s.integer(status.powerPending); s.integer(status.resetPending); + s.integer(status.interruptPending); + s.integer(status.dmaActive); s.integer(status.dmaClocks); s.integer(status.dmaPending); @@ -51,12 +51,11 @@ auto CPU::serialize(serializer& s) -> void { s.integer(io.wramAddress); - s.integer(io.joypadStrobeLatch); - - s.integer(io.nmiEnabled); - s.integer(io.hirqEnabled); - s.integer(io.virqEnabled); - s.integer(io.autoJoypadPoll); + s.boolean(io.hirqEnable); + s.boolean(io.virqEnable); + s.boolean(io.irqEnable); + s.boolean(io.nmiEnable); + s.boolean(io.autoJoypadPoll); s.integer(io.pio); @@ -83,9 +82,9 @@ auto CPU::serialize(serializer& s) -> void { s.integer(alu.divctr); s.integer(alu.shift); - for(auto& channel : this->channel) { - s.integer(channel.dmaEnabled); - s.integer(channel.hdmaEnabled); + for(auto& channel : channels) { + s.integer(channel.dmaEnable); + s.integer(channel.hdmaEnable); s.integer(channel.direction); s.integer(channel.indirect); s.integer(channel.unused); @@ -105,6 +104,6 @@ auto CPU::serialize(serializer& s) -> void { } s.integer(pipe.valid); - s.integer(pipe.addr); + s.integer(pipe.address); s.integer(pipe.data); } diff --git a/higan/sfc/cpu/timing.cpp b/higan/sfc/cpu/timing.cpp index 41d2400c..e7e2d259 100644 --- a/higan/sfc/cpu/timing.cpp +++ b/higan/sfc/cpu/timing.cpp @@ -39,9 +39,9 @@ auto CPU::scanline() -> void { for(auto coprocessor : coprocessors) synchronize(*coprocessor); if(vcounter() == 0) { - //HDMA init triggers once every frame - status.hdmaInitPosition = (version == 1 ? 12 + 8 - dmaCounter() : 12 + dmaCounter()); - status.hdmaInitTriggered = false; + //HDMA setup triggers once every frame + status.hdmaSetupPosition = (version == 1 ? 12 + 8 - dmaCounter() : 12 + dmaCounter()); + status.hdmaSetupTriggered = false; status.autoJoypadCounter = 0; } @@ -88,12 +88,12 @@ auto CPU::dmaEdge() -> void { if(status.dmaActive) { if(status.hdmaPending) { status.hdmaPending = false; - if(hdmaEnabledChannels()) { - if(!dmaEnabledChannels()) { + if(hdmaEnable()) { + if(!dmaEnable()) { dmaStep(8 - dmaCounter()); } - status.hdmaMode == 0 ? hdmaInit() : hdmaRun(); - if(!dmaEnabledChannels()) { + status.hdmaMode == 0 ? hdmaSetup() : hdmaRun(); + if(!dmaEnable()) { step(status.clockCount - (status.dmaClocks % status.clockCount)); status.dmaActive = false; } @@ -102,7 +102,7 @@ auto CPU::dmaEdge() -> void { if(status.dmaPending) { status.dmaPending = false; - if(dmaEnabledChannels()) { + if(dmaEnable()) { dmaStep(8 - dmaCounter()); dmaRun(); step(status.clockCount - (status.dmaClocks % status.clockCount)); @@ -111,10 +111,10 @@ auto CPU::dmaEdge() -> void { } } - if(!status.hdmaInitTriggered && hcounter() >= status.hdmaInitPosition) { - status.hdmaInitTriggered = true; - hdmaInitReset(); - if(hdmaEnabledChannels()) { + if(!status.hdmaSetupTriggered && hcounter() >= status.hdmaSetupPosition) { + status.hdmaSetupTriggered = true; + hdmaReset(); + if(hdmaEnable()) { status.hdmaPending = true; status.hdmaMode = 0; } @@ -122,7 +122,7 @@ auto CPU::dmaEdge() -> void { if(!status.hdmaTriggered && hcounter() >= status.hdmaPosition) { status.hdmaTriggered = true; - if(hdmaActiveChannels()) { + if(hdmaActive()) { status.hdmaPending = true; status.hdmaMode = 1; } @@ -177,8 +177,8 @@ auto CPU::joypadEdge() -> void { //trigger during certain events (immediately after DMA, writes to $4200, etc) auto CPU::lastCycle() -> void { if(!status.irqLock) { - status.nmiPending |= nmiTest(); - status.irqPending |= irqTest(); + if(nmiTest()) status.nmiPending = true; + if(irqTest()) status.irqPending = true; status.interruptPending = (status.nmiPending || status.irqPending); } } diff --git a/higan/sfc/ppu/counter/counter-inline.hpp b/higan/sfc/ppu/counter/counter-inline.hpp index c128543b..7c3e7ec1 100644 --- a/higan/sfc/ppu/counter/counter-inline.hpp +++ b/higan/sfc/ppu/counter/counter-inline.hpp @@ -26,27 +26,27 @@ auto PPUcounter::tick(uint clocks) -> void { //internal auto PPUcounter::vcounterTick() -> void { if(++status.vcounter == 128) status.interlace = ppu.interlace(); - - if((Region::NTSC() && status.interlace == 0 && status.vcounter == 262) - || (Region::NTSC() && status.interlace == 1 && status.vcounter == 263) - || (Region::NTSC() && status.interlace == 1 && status.vcounter == 262 && status.field == 1) - || (Region::PAL() && status.interlace == 0 && status.vcounter == 312) - || (Region::PAL() && status.interlace == 1 && status.vcounter == 313) - || (Region::PAL() && status.interlace == 1 && status.vcounter == 312 && status.field == 1) - ) { + if(vcounter() == (Region::NTSC() ? 262 : 312) + (interlace() && !field())) { status.vcounter = 0; - status.field = !status.field; + status.field ^= 1; } + status.lineclocks = 1364; + //NTSC and PAL scanlines rates would not match up with color clocks if every scanline were 1364 clocks + //to offset for this error, NTSC has one short scanline, and PAL has one long scanline + if(Region::NTSC() && interlace() == 0 && field() == 1 && vcounter() == 240) status.lineclocks -= 4; + if(Region::PAL() && interlace() == 1 && field() == 1 && vcounter() == 311) status.lineclocks += 4; if(scanline) scanline(); } +auto PPUcounter::interlace() const -> bool { return status.interlace; } auto PPUcounter::field() const -> bool { return status.field; } -auto PPUcounter::vcounter() const -> uint16 { return status.vcounter; } -auto PPUcounter::hcounter() const -> uint16 { return status.hcounter; } +auto PPUcounter::vcounter() const -> uint { return status.vcounter; } +auto PPUcounter::hcounter() const -> uint { return status.hcounter; } +auto PPUcounter::lineclocks() const -> uint { return status.lineclocks; } auto PPUcounter::field(uint offset) const -> bool { return history.field[(history.index - (offset >> 1)) & 2047]; } -auto PPUcounter::vcounter(uint offset) const -> uint16 { return history.vcounter[(history.index - (offset >> 1)) & 2047]; } -auto PPUcounter::hcounter(uint offset) const -> uint16 { return history.hcounter[(history.index - (offset >> 1)) & 2047]; } +auto PPUcounter::vcounter(uint offset) const -> uint { return history.vcounter[(history.index - (offset >> 1)) & 2047]; } +auto PPUcounter::hcounter(uint offset) const -> uint { return history.hcounter[(history.index - (offset >> 1)) & 2047]; } //one PPU dot = 4 CPU clocks // @@ -57,29 +57,15 @@ auto PPUcounter::hcounter(uint offset) const -> uint16 { return history.hcounter //dot 323 range = {1292, 1294, 1296} //dot 327 range = {1310, 1312, 1314} -auto PPUcounter::hdot() const -> uint16 { - if(Region::NTSC() && status.interlace == 0 && vcounter() == 240 && field() == 1) { +auto PPUcounter::hdot() const -> uint { + if(lineclocks() == 1360) { return (hcounter() >> 2); } else { return (hcounter() - ((hcounter() > 1292) << 1) - ((hcounter() > 1310) << 1)) >> 2; } } -auto PPUcounter::lineclocks() const -> uint16 { - if(Region::NTSC() && status.interlace == 0 && vcounter() == 240 && field() == 1) return 1360; - return 1364; -} - auto PPUcounter::reset() -> void { - status.interlace = 0; - status.field = 0; - status.vcounter = 0; - status.hcounter = 0; - history.index = 0; - - for(auto n : range(2048)) { - history.field [n] = 0; - history.vcounter[n] = 0; - history.hcounter[n] = 0; - } + status = {}; + history = {}; } diff --git a/higan/sfc/ppu/counter/counter.hpp b/higan/sfc/ppu/counter/counter.hpp index 9937bdbb..3073996c 100644 --- a/higan/sfc/ppu/counter/counter.hpp +++ b/higan/sfc/ppu/counter/counter.hpp @@ -14,36 +14,37 @@ struct PPUcounter { alwaysinline auto tick() -> void; alwaysinline auto tick(uint clocks) -> void; + alwaysinline auto interlace() const -> bool; alwaysinline auto field() const -> bool; - alwaysinline auto vcounter() const -> uint16; - alwaysinline auto hcounter() const -> uint16; - inline auto hdot() const -> uint16; - inline auto lineclocks() const -> uint16; + alwaysinline auto vcounter() const -> uint; + alwaysinline auto hcounter() const -> uint; + alwaysinline auto hdot() const -> uint; + alwaysinline auto lineclocks() const -> uint; alwaysinline auto field(uint offset) const -> bool; - alwaysinline auto vcounter(uint offset) const -> uint16; - alwaysinline auto hcounter(uint offset) const -> uint16; + alwaysinline auto vcounter(uint offset) const -> uint; + alwaysinline auto hcounter(uint offset) const -> uint; inline auto reset() -> void; auto serialize(serializer&) -> void; - function void> scanline; + function scanline; private: - inline auto vcounterTick() -> void; + alwaysinline auto vcounterTick() -> void; struct { - bool interlace; - bool field; - uint16 vcounter; - uint16 hcounter; + bool interlace = 0; + bool field = 0; + uint vcounter = 0; + uint hcounter = 0; + uint lineclocks = 1364; } status; struct { - bool field[2048]; - uint16 vcounter[2048]; - uint16 hcounter[2048]; - - int32 index; + uint index = 0; + bool field[2048] = {}; + uint vcounter[2048] = {}; + uint hcounter[2048] = {}; } history; }; diff --git a/higan/sfc/smp/io.cpp b/higan/sfc/smp/io.cpp new file mode 100644 index 00000000..72ffd444 --- /dev/null +++ b/higan/sfc/smp/io.cpp @@ -0,0 +1,182 @@ +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; + } +} diff --git a/higan/sfc/smp/memory.cpp b/higan/sfc/smp/memory.cpp index a29eccca..b85a1f6e 100644 --- a/higan/sfc/smp/memory.cpp +++ b/higan/sfc/smp/memory.cpp @@ -1,219 +1,32 @@ -alwaysinline auto SMP::ramRead(uint16 addr) -> uint8 { - if(addr >= 0xffc0 && io.iplromEnable) return iplrom[addr & 0x3f]; +auto SMP::readRAM(uint16 address) -> uint8 { + if(address >= 0xffc0 && io.iplromEnable) return iplrom[address & 0x3f]; if(io.ramDisable) return 0x5a; //0xff on mini-SNES - return dsp.apuram[addr]; + return dsp.apuram[address]; } -alwaysinline auto SMP::ramWrite(uint16 addr, uint8 data) -> void { +auto SMP::writeRAM(uint16 address, 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 + if(io.ramWritable && !io.ramDisable) dsp.apuram[address] = data; } auto SMP::idle() -> void { wait(); } -auto SMP::read(uint16 addr) -> uint8 { - wait(addr); - uint8 data = busRead(addr); +auto SMP::read(uint16 address) -> uint8 { + wait(address); + uint8 data = readRAM(address); + if((address & 0xfff0) == 0x00f0) data = readIO(address); return data; } -auto SMP::write(uint16 addr, uint8 data) -> void { - wait(addr); - busWrite(addr, data); +auto SMP::write(uint16 address, uint8 data) -> void { + wait(address); + writeRAM(address, data); //even IO writes affect underlying RAM + if((address & 0xfff0) == 0x00f0) writeIO(address, 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]; +auto SMP::readDisassembler(uint16 address) -> uint8 { + if((address & 0xfff0) == 0x00f0) return 0x00; + return readRAM(address); } diff --git a/higan/sfc/smp/serialization.cpp b/higan/sfc/smp/serialization.cpp index 0a1c82b4..caaec3ab 100644 --- a/higan/sfc/smp/serialization.cpp +++ b/higan/sfc/smp/serialization.cpp @@ -33,23 +33,23 @@ auto SMP::serialize(serializer& s) -> void { s.integer(timer0.stage1); s.integer(timer0.stage2); s.integer(timer0.stage3); - s.integer(timer0.line); - s.integer(timer0.enable); + s.boolean(timer0.line); + s.boolean(timer0.enable); s.integer(timer0.target); s.integer(timer1.stage0); s.integer(timer1.stage1); s.integer(timer1.stage2); s.integer(timer1.stage3); - s.integer(timer1.line); - s.integer(timer1.enable); + s.boolean(timer1.line); + s.boolean(timer1.enable); s.integer(timer1.target); s.integer(timer2.stage0); s.integer(timer2.stage1); s.integer(timer2.stage2); s.integer(timer2.stage3); - s.integer(timer2.line); - s.integer(timer2.enable); + s.boolean(timer2.line); + s.boolean(timer2.enable); s.integer(timer2.target); } diff --git a/higan/sfc/smp/smp.cpp b/higan/sfc/smp/smp.cpp index 51cd864c..3facdf76 100644 --- a/higan/sfc/smp/smp.cpp +++ b/higan/sfc/smp/smp.cpp @@ -3,15 +3,11 @@ namespace SuperFamicom { SMP smp; - #include "memory.cpp" +#include "io.cpp" #include "timing.cpp" #include "serialization.cpp" -auto SMP::synchronizing() const -> bool { - return scheduler.synchronizing(); -} - auto SMP::Enter() -> void { while(true) scheduler.synchronize(), smp.main(); } @@ -39,67 +35,10 @@ auto SMP::power(bool reset) -> void { r.pc.byte.l = iplrom[62]; r.pc.byte.h = iplrom[63]; - //timing - io.clockCounter = 0; - io.dspCounter = 0; - - //external - io.apu0 = 0x00; - io.apu1 = 0x00; - io.apu2 = 0x00; - io.apu3 = 0x00; - - //$00f0 - io.timersDisable = false; - io.ramWritable = true; - io.ramDisable = false; - io.timersEnable = true; - io.externalWaitStates = 0; - io.internalWaitStates = 0; - - //$00f1 - io.iplromEnable = true; - - //$00f2 - io.dspAddr = 0x00; - - //$00f4-00f7 - io.cpu0 = 0x00; - io.cpu1 = 0x00; - io.cpu2 = 0x00; - io.cpu3 = 0x00; - - //$00f8-$00f9 - io.aux4 = 0x00; - io.aux5 = 0x00; - - timer0.stage0 = 0; - timer1.stage0 = 0; - timer2.stage0 = 0; - - timer0.stage1 = 0; - timer1.stage1 = 0; - timer2.stage1 = 0; - - timer0.stage2 = 0; - timer1.stage2 = 0; - timer2.stage2 = 0; - - timer0.stage3 = 0; - timer1.stage3 = 0; - timer2.stage3 = 0; - - timer0.line = 0; - timer1.line = 0; - timer2.line = 0; - - timer0.enable = false; - timer1.enable = false; - timer2.enable = false; - - timer0.target = 0; - timer1.target = 0; - timer2.target = 0; + io = {}; + timer0 = {}; + timer1 = {}; + timer2 = {}; } } diff --git a/higan/sfc/smp/smp.hpp b/higan/sfc/smp/smp.hpp index 1987b756..a5846fb3 100644 --- a/higan/sfc/smp/smp.hpp +++ b/higan/sfc/smp/smp.hpp @@ -1,14 +1,14 @@ //Sony CXP1100Q-1 struct SMP : Processor::SPC700, Thread { - uint8 iplrom[64]; - - //smp.cpp - auto synchronizing() const -> bool override; + inline auto synchronizing() const -> bool override { return scheduler.synchronizing(); } + //io.cpp auto portRead(uint2 port) const -> uint8; auto portWrite(uint2 port, uint8 data) -> void; + //smp.cpp + static auto Enter() -> void; auto main() -> void; auto load(Markup::Node) -> bool; auto power(bool reset) -> void; @@ -16,11 +16,13 @@ struct SMP : Processor::SPC700, Thread { //serialization.cpp auto serialize(serializer&) -> void; + uint8 iplrom[64]; + private: struct IO { //timing - uint clockCounter; - uint dspCounter; + uint clockCounter = 0; + uint dspCounter = 0; //external uint8 apu0; @@ -30,14 +32,14 @@ private: //$00f0 uint1 timersDisable; - uint1 ramWritable; + uint1 ramWritable = true; uint1 ramDisable; - uint1 timersEnable; + uint1 timersEnable = true; uint2 externalWaitStates; uint2 internalWaitStates; //$00f1 - bool iplromEnable; + uint1 iplromEnable = true; //$00f2 uint8 dspAddr; @@ -53,30 +55,30 @@ private: uint8 aux5; } io; - static auto Enter() -> void; - //memory.cpp - auto ramRead(uint16 addr) -> uint8; - auto ramWrite(uint16 addr, uint8 data) -> void; - - auto busRead(uint16 addr) -> uint8; - auto busWrite(uint16 addr, uint8 data) -> void; + inline auto readRAM(uint16 address) -> uint8; + inline auto writeRAM(uint16 address, uint8 data) -> void; auto idle() -> void override; - auto read(uint16 addr) -> uint8 override; - auto write(uint16 addr, uint8 data) -> void override; + auto read(uint16 address) -> uint8 override; + auto write(uint16 address, uint8 data) -> void override; - auto readDisassembler(uint16 addr) -> uint8 override; + auto readDisassembler(uint16 address) -> uint8 override; + + //io.cpp + inline auto readIO(uint16 address) -> uint8; + inline auto writeIO(uint16 address, uint8 data) -> void; //timing.cpp - template struct Timer { - uint8 stage0; - uint8 stage1; - uint8 stage2; - uint4 stage3; - bool line; - bool enable; - uint8 target; + template + struct Timer { + uint8 stage0; + uint8 stage1; + uint8 stage2; + uint4 stage3; + boolean line; + boolean enable; + uint8 target; auto step(uint clocks) -> void; auto synchronizeStage1() -> void; diff --git a/nall/primitives.hpp b/nall/primitives.hpp index 697ea9e0..b31ca689 100644 --- a/nall/primitives.hpp +++ b/nall/primitives.hpp @@ -16,6 +16,10 @@ struct Boolean { inline auto raise() { return data == 0 ? data = 1, true : false; } inline auto lower() { return data == 1 ? data = 0, true : false; } + inline auto flip(bool value) { return data != value ? (data = value, true) : false; } + inline auto raise(bool value) { return !data && value ? (data = value, true) : (data = value, false); } + inline auto lower(bool value) { return data && !value ? (data = value, true) : (data = value, false); } + inline auto serialize(serializer& s) { s(data); } private: