From ae986987581c880b3c11715e8efe4370101d36d1 Mon Sep 17 00:00:00 2001 From: MrWint Date: Sun, 26 May 2019 14:17:22 +0200 Subject: [PATCH] libgambatte: Refactor Memory --- libgambatte/src/initstate.cpp | 2 + libgambatte/src/memory.cpp | 1174 +++++++++++++++++---------------- libgambatte/src/memory.h | 411 ++++++------ libgambatte/src/savestate.h | 2 + output/dll/libgambatte.dll | Bin 168960 -> 168448 bytes 5 files changed, 816 insertions(+), 773 deletions(-) diff --git a/libgambatte/src/initstate.cpp b/libgambatte/src/initstate.cpp index 1263191c3b..ce0b7b0905 100644 --- a/libgambatte/src/initstate.cpp +++ b/libgambatte/src/initstate.cpp @@ -1206,6 +1206,7 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM state.mem.lastOamDmaUpdate = disabled_time; state.mem.unhaltTime = disabled_time; state.mem.minIntTime = 0; + state.mem.halttime = 0; state.mem.rombank = 1; state.mem.dmaSource = 0; state.mem.dmaDestination = 0; @@ -1217,6 +1218,7 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM state.mem.rambankMode = false; state.mem.hdmaTransfer = false; state.mem.gbIsCgb = cgb; + state.mem.stopped = false; for (int i = 0x00; i < 0x40; i += 0x02) { diff --git a/libgambatte/src/memory.cpp b/libgambatte/src/memory.cpp index fc18337001..54eea33844 100644 --- a/libgambatte/src/memory.cpp +++ b/libgambatte/src/memory.cpp @@ -1,300 +1,315 @@ -/*************************************************************************** - * Copyright (C) 2007 by Sindre Aamås * - * aamas@stud.ntnu.no * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License version 2 as * - * published by the Free Software Foundation. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License version 2 for more details. * - * * - * You should have received a copy of the GNU General Public License * - * version 2 along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ +// +// Copyright (C) 2007 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + #include "memory.h" -#include "video.h" -#include "sound.h" #include "savestate.h" +#include "sound.h" +#include "video.h" #include namespace gambatte { Memory::Memory(unsigned short &sp, unsigned short &pc) -: readCallback(0), - writeCallback(0), - execCallback(0), - cdCallback(0), - linkCallback(0), - getInput(0), - divLastUpdate(0), - lastOamDmaUpdate(disabled_time), - display(ioamhram, 0, VideoInterruptRequester(intreq)), - dmaSource(0), - dmaDestination(0), - oamDmaPos(0xFE), - serialCnt(0), - blanklcd(false), - LINKCABLE(false), - linkClockTrigger(false), - SP(sp), - PC(pc) +: readCallback_(0) +, writeCallback_(0) +, execCallback_(0) +, cdCallback_(0) +, linkCallback_(0) +, getInput_(0) +, divLastUpdate_(0) +, lastOamDmaUpdate_(disabled_time) +, lcd_(ioamhram_, 0, VideoInterruptRequester(intreq_)) +, dmaSource_(0) +, dmaDestination_(0) +, oamDmaPos_(0xFE) +, serialCnt_(0) +, blanklcd_(false) +, LINKCABLE_(false) +, linkClockTrigger_(false) +, sp_(sp) +, pc_(pc) { - intreq.setEventTime(144*456ul); - intreq.setEventTime(0); + intreq_.setEventTime(144 * 456ul); + intreq_.setEventTime(0); } void Memory::setStatePtrs(SaveState &state) { - state.mem.ioamhram.set(ioamhram, sizeof ioamhram); + state.mem.ioamhram.set(ioamhram_, sizeof ioamhram_); - cart.setStatePtrs(state); - display.setStatePtrs(state); - sound.setStatePtrs(state); + cart_.setStatePtrs(state); + lcd_.setStatePtrs(state); + psg_.setStatePtrs(state); } -static inline int serialCntFrom(const unsigned long cyclesUntilDone, const bool cgbFast) { +static int serialCntFrom(unsigned long cyclesUntilDone, bool cgbFast) { return cgbFast ? (cyclesUntilDone + 0xF) >> 4 : (cyclesUntilDone + 0x1FF) >> 9; } -void Memory::loadState(const SaveState &state) { - biosMode = state.mem.biosMode; - cgbSwitching = state.mem.cgbSwitching; - agbMode = state.mem.agbMode; +void Memory::loadState(SaveState const &state) { + biosMode_ = state.mem.biosMode; + cgbSwitching_ = state.mem.cgbSwitching; + agbMode_ = state.mem.agbMode; gbIsCgb_ = state.mem.gbIsCgb; - sound.loadState(state); - display.loadState(state, state.mem.oamDmaPos < 0xA0 ? cart.rdisabledRam() : ioamhram); - tima.loadState(state, TimaInterruptRequester(intreq)); - cart.loadState(state); - intreq.loadState(state); + stopped_ = state.mem.stopped; + psg_.loadState(state); + lcd_.loadState(state, state.mem.oamDmaPos < 0xA0 ? cart_.rdisabledRam() : ioamhram_); + tima_.loadState(state, TimaInterruptRequester(intreq_)); + cart_.loadState(state); + intreq_.loadState(state); - divLastUpdate = state.mem.divLastUpdate; - intreq.setEventTime(state.mem.nextSerialtime > state.cpu.cycleCounter ? state.mem.nextSerialtime : state.cpu.cycleCounter); - intreq.setEventTime(state.mem.unhaltTime); - lastOamDmaUpdate = state.mem.lastOamDmaUpdate; - dmaSource = state.mem.dmaSource; - dmaDestination = state.mem.dmaDestination; - oamDmaPos = state.mem.oamDmaPos; - serialCnt = intreq.eventTime(intevent_serial) != disabled_time - ? serialCntFrom(intreq.eventTime(intevent_serial) - state.cpu.cycleCounter, ioamhram[0x102] & isCgb() * 2) - : 8; + divLastUpdate_ = state.mem.divLastUpdate; + intreq_.setEventTime(state.mem.nextSerialtime > state.cpu.cycleCounter + ? state.mem.nextSerialtime + : state.cpu.cycleCounter); + intreq_.setEventTime(state.mem.unhaltTime); + halttime_ = state.mem.halttime; + lastOamDmaUpdate_ = state.mem.lastOamDmaUpdate; + dmaSource_ = state.mem.dmaSource; + dmaDestination_ = state.mem.dmaDestination; + oamDmaPos_ = state.mem.oamDmaPos; + serialCnt_ = intreq_.eventTime(intevent_serial) != disabled_time + ? serialCntFrom(intreq_.eventTime(intevent_serial) - state.cpu.cycleCounter, + ioamhram_[0x102] & isCgb() * 2) + : 8; - cart.setVrambank(ioamhram[0x14F] & isCgb()); - cart.setOamDmaSrc(oam_dma_src_off); - cart.setWrambank(isCgb() && (ioamhram[0x170] & 0x07) ? ioamhram[0x170] & 0x07 : 1); + cart_.setVrambank(ioamhram_[0x14F] & isCgb()); + cart_.setOamDmaSrc(oam_dma_src_off); + cart_.setWrambank(isCgb() && (ioamhram_[0x170] & 0x07) ? ioamhram_[0x170] & 0x07 : 1); - if (lastOamDmaUpdate != disabled_time) { + if (lastOamDmaUpdate_ != disabled_time) { oamDmaInitSetup(); - const unsigned oamEventPos = oamDmaPos < 0xA0 ? 0xA0 : 0x100; - - intreq.setEventTime(lastOamDmaUpdate + (oamEventPos - oamDmaPos) * 4); + unsigned oamEventPos = oamDmaPos_ < 0xA0 ? 0xA0 : 0x100; + intreq_.setEventTime( + lastOamDmaUpdate_ + (oamEventPos - oamDmaPos_) * 4); } - intreq.setEventTime((ioamhram[0x140] & 0x80) ? display.nextMode1IrqTime() : state.cpu.cycleCounter); - blanklcd = false; + intreq_.setEventTime(ioamhram_[0x140] & lcdc_en + ? lcd_.nextMode1IrqTime() + : state.cpu.cycleCounter); + blanklcd_ = false; if (!isCgb()) - std::memset(cart.vramdata() + 0x2000, 0, 0x2000); + std::memset(cart_.vramdata() + 0x2000, 0, 0x2000); } -void Memory::setEndtime(const unsigned long cycleCounter, const unsigned long inc) { - if (intreq.eventTime(intevent_blit) <= cycleCounter) - intreq.setEventTime(intreq.eventTime(intevent_blit) + (70224 << isDoubleSpeed())); +void Memory::setEndtime(unsigned long cc, unsigned long inc) { + if (intreq_.eventTime(intevent_blit) <= cc) { + intreq_.setEventTime(intreq_.eventTime(intevent_blit) + + (70224 << isDoubleSpeed())); + } - intreq.setEventTime(cycleCounter + (inc << isDoubleSpeed())); + intreq_.setEventTime(cc + (inc << isDoubleSpeed())); } -void Memory::updateSerial(const unsigned long cc) { - if (!LINKCABLE) { - if (intreq.eventTime(intevent_serial) != disabled_time) { - if (intreq.eventTime(intevent_serial) <= cc) { - ioamhram[0x101] = (((ioamhram[0x101] + 1) << serialCnt) - 1) & 0xFF; - ioamhram[0x102] &= 0x7F; - intreq.setEventTime(disabled_time); - intreq.flagIrq(8); +void Memory::updateSerial(unsigned long const cc) { + if (!LINKCABLE_) { + if (intreq_.eventTime(intevent_serial) != disabled_time) { + if (intreq_.eventTime(intevent_serial) <= cc) { + ioamhram_[0x101] = (((ioamhram_[0x101] + 1) << serialCnt_) - 1) & 0xFF; + ioamhram_[0x102] &= 0x7F; + intreq_.setEventTime(disabled_time); + intreq_.flagIrq(8); } else { - const int targetCnt = serialCntFrom(intreq.eventTime(intevent_serial) - cc, ioamhram[0x102] & isCgb() * 2); - ioamhram[0x101] = (((ioamhram[0x101] + 1) << (serialCnt - targetCnt)) - 1) & 0xFF; - serialCnt = targetCnt; + int const targetCnt = serialCntFrom(intreq_.eventTime(intevent_serial) - cc, + ioamhram_[0x102] & isCgb() * 2); + ioamhram_[0x101] = (((ioamhram_[0x101] + 1) << (serialCnt_ - targetCnt)) - 1) & 0xFF; + serialCnt_ = targetCnt; } } - } - else { - if (intreq.eventTime(intevent_serial) != disabled_time) { - if (intreq.eventTime(intevent_serial) <= cc) { - linkClockTrigger = true; - intreq.setEventTime(disabled_time); - if (linkCallback) - linkCallback(); + } else { + if (intreq_.eventTime(intevent_serial) != disabled_time) { + if (intreq_.eventTime(intevent_serial) <= cc) { + linkClockTrigger_ = true; + intreq_.setEventTime(disabled_time); + if (linkCallback_) + linkCallback_(); } } } } -void Memory::updateTimaIrq(const unsigned long cc) { - while (intreq.eventTime(intevent_tima) <= cc) - tima.doIrqEvent(TimaInterruptRequester(intreq)); +void Memory::updateTimaIrq(unsigned long cc) { + while (intreq_.eventTime(intevent_tima) <= cc) + tima_.doIrqEvent(TimaInterruptRequester(intreq_)); } -void Memory::updateIrqs(const unsigned long cc) { +void Memory::updateIrqs(unsigned long cc) { updateSerial(cc); updateTimaIrq(cc); - display.update(cc); + lcd_.update(cc); } -unsigned long Memory::event(unsigned long cycleCounter) { - if (lastOamDmaUpdate != disabled_time) - updateOamDma(cycleCounter); +unsigned long Memory::event(unsigned long cc) { + if (lastOamDmaUpdate_ != disabled_time) + updateOamDma(cc); - switch (intreq.minEventId()) { + switch (intreq_.minEventId()) { case intevent_unhalt: - nontrivial_ff_write(0xFF04, 0, cycleCounter); - PC = (PC + 1) & 0xFFFF; - cycleCounter += 4; - intreq.unhalt(); - intreq.setEventTime(disabled_time); + nontrivial_ff_write(0xFF04, 0, cc); + pc_ = (pc_ + 1) & 0xFFFF; + cc += 4; + intreq_.unhalt(); + intreq_.setEventTime(disabled_time); break; case intevent_end: - intreq.setEventTime(disabled_time - 1); + intreq_.setEventTime(disabled_time - 1); - while (cycleCounter >= intreq.minEventTime() && intreq.eventTime(intevent_end) != disabled_time) - cycleCounter = event(cycleCounter); + while (cc >= intreq_.minEventTime() + && intreq_.eventTime(intevent_end) != disabled_time) { + cc = event(cc); + } - intreq.setEventTime(disabled_time); + intreq_.setEventTime(disabled_time); break; case intevent_blit: { - const bool lcden = ioamhram[0x140] >> 7 & 1; - unsigned long blitTime = intreq.eventTime(intevent_blit); + bool const lcden = ioamhram_[0x140] & lcdc_en; + unsigned long blitTime = intreq_.eventTime(intevent_blit); - if (lcden | blanklcd) { - display.updateScreen(blanklcd, cycleCounter); - intreq.setEventTime(disabled_time); - intreq.setEventTime(disabled_time); + if (lcden | blanklcd_) { + lcd_.updateScreen(blanklcd_, cc); + intreq_.setEventTime(disabled_time); + intreq_.setEventTime(disabled_time); - while (cycleCounter >= intreq.minEventTime()) - cycleCounter = event(cycleCounter); + while (cc >= intreq_.minEventTime()) + cc = event(cc); } else blitTime += 70224 << isDoubleSpeed(); - blanklcd = lcden ^ 1; - intreq.setEventTime(blitTime); + blanklcd_ = lcden ^ 1; + intreq_.setEventTime(blitTime); } break; case intevent_serial: - updateSerial(cycleCounter); + updateSerial(cc); break; case intevent_oam: - intreq.setEventTime(lastOamDmaUpdate == disabled_time ? - static_cast(disabled_time) : intreq.eventTime(intevent_oam) + 0xA0 * 4); + intreq_.setEventTime(lastOamDmaUpdate_ == disabled_time + ? static_cast(disabled_time) + : intreq_.eventTime(intevent_oam) + 0xA0 * 4); break; case intevent_dma: { - const bool doubleSpeed = isDoubleSpeed(); - unsigned dmaSrc = dmaSource; - unsigned dmaDest = dmaDestination; - unsigned dmaLength = ((ioamhram[0x155] & 0x7F) + 0x1) * 0x10; - unsigned length = hdmaReqFlagged(intreq) ? 0x10 : dmaLength; + bool const doubleSpeed = isDoubleSpeed(); + unsigned dmaSrc = dmaSource_; + unsigned dmaDest = dmaDestination_; + unsigned dmaLength = ((ioamhram_[0x155] & 0x7F) + 0x1) * 0x10; + unsigned length = hdmaReqFlagged(intreq_) ? 0x10 : dmaLength; - ackDmaReq(intreq); + ackDmaReq(intreq_); if ((static_cast(dmaDest) + length) & 0x10000) { length = 0x10000 - dmaDest; - ioamhram[0x155] |= 0x80; + ioamhram_[0x155] |= 0x80; } dmaLength -= length; - if (!(ioamhram[0x140] & 0x80)) + if (!(ioamhram_[0x140] & lcdc_en)) dmaLength = 0; { - unsigned long lOamDmaUpdate = lastOamDmaUpdate; - lastOamDmaUpdate = disabled_time; + unsigned long lOamDmaUpdate = lastOamDmaUpdate_; + lastOamDmaUpdate_ = disabled_time; while (length--) { - const unsigned src = dmaSrc++ & 0xFFFF; - const unsigned data = ((src & 0xE000) == 0x8000 || src > 0xFDFF) ? 0xFF : read(src, cycleCounter); + unsigned const src = dmaSrc++ & 0xFFFF; + unsigned const data = (src & 0xE000) == 0x8000 || src > 0xFDFF + ? 0xFF + : read(src, cc); - cycleCounter += 2 << doubleSpeed; + cc += 2 << doubleSpeed; - if (cycleCounter - 3 > lOamDmaUpdate) { - oamDmaPos = (oamDmaPos + 1) & 0xFF; + if (cc - 3 > lOamDmaUpdate) { + oamDmaPos_ = (oamDmaPos_ + 1) & 0xFF; lOamDmaUpdate += 4; - if (oamDmaPos < 0xA0) { - if (oamDmaPos == 0) + if (oamDmaPos_ < 0xA0) { + if (oamDmaPos_ == 0) startOamDma(lOamDmaUpdate - 1); - ioamhram[src & 0xFF] = data; - } else if (oamDmaPos == 0xA0) { + ioamhram_[src & 0xFF] = data; + } else if (oamDmaPos_ == 0xA0) { endOamDma(lOamDmaUpdate - 1); lOamDmaUpdate = disabled_time; } } - nontrivial_write(0x8000 | (dmaDest++ & 0x1FFF), data, cycleCounter); + nontrivial_write(0x8000 | (dmaDest++ & 0x1FFF), data, cc); } - lastOamDmaUpdate = lOamDmaUpdate; + lastOamDmaUpdate_ = lOamDmaUpdate; } - cycleCounter += 4; + cc += 4; - dmaSource = dmaSrc; - dmaDestination = dmaDest; - ioamhram[0x155] = ((dmaLength / 0x10 - 0x1) & 0xFF) | (ioamhram[0x155] & 0x80); + dmaSource_ = dmaSrc; + dmaDestination_ = dmaDest; + ioamhram_[0x155] = ((dmaLength / 0x10 - 0x1) & 0xFF) | (ioamhram_[0x155] & 0x80); - if ((ioamhram[0x155] & 0x80) && display.hdmaIsEnabled()) { - if (lastOamDmaUpdate != disabled_time) - updateOamDma(cycleCounter); + if ((ioamhram_[0x155] & 0x80) && lcd_.hdmaIsEnabled()) { + if (lastOamDmaUpdate_ != disabled_time) + updateOamDma(cc); - display.disableHdma(cycleCounter); + lcd_.disableHdma(cc); } } break; case intevent_tima: - tima.doIrqEvent(TimaInterruptRequester(intreq)); + tima_.doIrqEvent(TimaInterruptRequester(intreq_)); break; case intevent_video: - display.update(cycleCounter); + lcd_.update(cc); break; case intevent_interrupts: - if (stopped) { - intreq.setEventTime(disabled_time); + if (stopped_) { + intreq_.setEventTime(disabled_time); break; } - if (halted()) { - if (gbIsCgb_ || (!gbIsCgb_ && cycleCounter <= halttime + 4)) - cycleCounter += 4; - intreq.unhalt(); - intreq.setEventTime(disabled_time); + if (halted()) { + if (gbIsCgb_ || (!gbIsCgb_ && cc <= halttime_ + 4)) + cc += 4; + + intreq_.unhalt(); + intreq_.setEventTime(disabled_time); } if (ime()) { unsigned address; - cycleCounter += 12; - display.update(cycleCounter); - SP = (SP - 2) & 0xFFFF; - write(SP + 1, PC >> 8, cycleCounter); - unsigned ie = intreq.iereg(); + cc += 12; + lcd_.update(cc); + sp_ = (sp_ - 2) & 0xFFFF; + write(sp_ + 1, pc_ >> 8, cc); + unsigned ie = intreq_.iereg(); - cycleCounter += 4; - display.update(cycleCounter); - write(SP, PC & 0xFF, cycleCounter); - const unsigned pendingIrqs = ie & intreq.ifreg(); + cc += 4; + lcd_.update(cc); + write(sp_, pc_ & 0xFF, cc); + const unsigned pendingIrqs = ie & intreq_.ifreg(); - cycleCounter += 4; - display.update(cycleCounter); + cc += 4; + lcd_.update(cc); const unsigned n = pendingIrqs & -pendingIrqs; if (n == 0) { @@ -306,199 +321,201 @@ unsigned long Memory::event(unsigned long cycleCounter) { } else address = 0x50 + n; - intreq.ackIrq(n); - PC = address; + intreq_.ackIrq(n); + pc_ = address; } break; } - return cycleCounter; + return cc; } -unsigned long Memory::stop(unsigned long cycleCounter) { - cycleCounter += 4; +unsigned long Memory::stop(unsigned long cc) { + cc += 4; - if (ioamhram[0x14D] & isCgb()) { - sound.generateSamples(cycleCounter, isDoubleSpeed()); + if (ioamhram_[0x14D] & isCgb()) { + psg_.generateSamples(cc, isDoubleSpeed()); + lcd_.speedChange(cc); + ioamhram_[0x14D] ^= 0x81; + intreq_.setEventTime(ioamhram_[0x140] & lcdc_en + ? lcd_.nextMode1IrqTime() + : cc + (70224 << isDoubleSpeed())); - display.speedChange(cycleCounter); - ioamhram[0x14D] ^= 0x81; - - intreq.setEventTime((ioamhram[0x140] & 0x80) ? display.nextMode1IrqTime() : cycleCounter + (70224 << isDoubleSpeed())); - - if (intreq.eventTime(intevent_end) > cycleCounter) { - intreq.setEventTime(cycleCounter + (isDoubleSpeed() ? - (intreq.eventTime(intevent_end) - cycleCounter) << 1 : (intreq.eventTime(intevent_end) - cycleCounter) >> 1)); + if (intreq_.eventTime(intevent_end) > cc) { + intreq_.setEventTime(cc + + ( isDoubleSpeed() + ? (intreq_.eventTime(intevent_end) - cc) << 1 + : (intreq_.eventTime(intevent_end) - cc) >> 1)); } - // when switching speed, it seems that the CPU spontaneously restarts soon? - // otherwise, the cpu should be allowed to stay halted as long as needed - // so only execute this line when switching speed - intreq.halt(); - intreq.setEventTime(cycleCounter + 0x20000); + intreq_.halt(); + intreq_.setEventTime(cc + 0x20000); } else { - stopped = true; - intreq.halt(); + stopped_ = true; + intreq_.halt(); } - return cycleCounter; + return cc; } -static void decCycles(unsigned long &counter, const unsigned long dec) { +static void decCycles(unsigned long &counter, unsigned long dec) { if (counter != disabled_time) counter -= dec; } -void Memory::decEventCycles(const IntEventId eventId, const unsigned long dec) { - if (intreq.eventTime(eventId) != disabled_time) - intreq.setEventTime(eventId, intreq.eventTime(eventId) - dec); +void Memory::decEventCycles(IntEventId eventId, unsigned long dec) { + if (intreq_.eventTime(eventId) != disabled_time) + intreq_.setEventTime(eventId, intreq_.eventTime(eventId) - dec); } -unsigned long Memory::resetCounters(unsigned long cycleCounter) { - if (lastOamDmaUpdate != disabled_time) - updateOamDma(cycleCounter); +unsigned long Memory::resetCounters(unsigned long cc) { + if (lastOamDmaUpdate_ != disabled_time) + updateOamDma(cc); - updateIrqs(cycleCounter); - - const unsigned long oldCC = cycleCounter; + updateIrqs(cc); { - const unsigned long divinc = (cycleCounter - divLastUpdate) >> 8; - ioamhram[0x104] = (ioamhram[0x104] + divinc) & 0xFF; - divLastUpdate += divinc << 8; + unsigned long divinc = (cc - divLastUpdate_) >> 8; + ioamhram_[0x104] = (ioamhram_[0x104] + divinc) & 0xFF; + divLastUpdate_ += divinc << 8; } - const unsigned long dec = cycleCounter < 0x10000 ? 0 : (cycleCounter & ~0x7FFFul) - 0x8000; - - decCycles(divLastUpdate, dec); - decCycles(lastOamDmaUpdate, dec); + unsigned long const dec = cc < 0x10000 + ? 0 + : (cc & ~0x7FFFul) - 0x8000; + decCycles(divLastUpdate_, dec); + decCycles(lastOamDmaUpdate_, dec); decEventCycles(intevent_serial, dec); decEventCycles(intevent_oam, dec); decEventCycles(intevent_blit, dec); decEventCycles(intevent_end, dec); decEventCycles(intevent_unhalt, dec); - cycleCounter -= dec; - - intreq.resetCc(oldCC, cycleCounter); - tima.resetCc(oldCC, cycleCounter, TimaInterruptRequester(intreq)); - display.resetCc(oldCC, cycleCounter); - sound.resetCounter(cycleCounter, oldCC, isDoubleSpeed()); - - return cycleCounter; + unsigned long const oldCC = cc; + cc -= dec; + intreq_.resetCc(oldCC, cc); + tima_.resetCc(oldCC, cc, TimaInterruptRequester(intreq_)); + lcd_.resetCc(oldCC, cc); + psg_.resetCounter(cc, oldCC, isDoubleSpeed()); + return cc; } void Memory::updateInput() { unsigned state = 0xF; - if ((ioamhram[0x100] & 0x30) != 0x30 && getInput) { - unsigned input = (*getInput)(); + if ((ioamhram_[0x100] & 0x30) != 0x30 && getInput_) { + unsigned input = (*getInput_)(); unsigned dpad_state = ~input >> 4; unsigned button_state = ~input; - if (!(ioamhram[0x100] & 0x10)) + if (!(ioamhram_[0x100] & 0x10)) state &= dpad_state; - if (!(ioamhram[0x100] & 0x20)) + if (!(ioamhram_[0x100] & 0x20)) state &= button_state; + + if (state != 0xF && (ioamhram_[0x100] & 0xF) == 0xF) + intreq_.flagIrq(0x10); } - if (state != 0xF && (ioamhram[0x100] & 0xF) == 0xF) - intreq.flagIrq(0x10); - - ioamhram[0x100] = (ioamhram[0x100] & -0x10u) | state; + ioamhram_[0x100] = (ioamhram_[0x100] & -0x10u) | state; } -void Memory::updateOamDma(const unsigned long cycleCounter) { - const unsigned char *const oamDmaSrc = oamDmaSrcPtr(); - unsigned cycles = (cycleCounter - lastOamDmaUpdate) >> 2; +void Memory::updateOamDma(unsigned long const cc) { + unsigned char const *const oamDmaSrc = oamDmaSrcPtr(); + unsigned cycles = (cc - lastOamDmaUpdate_) >> 2; while (cycles--) { - oamDmaPos = (oamDmaPos + 1) & 0xFF; - lastOamDmaUpdate += 4; + oamDmaPos_ = (oamDmaPos_ + 1) & 0xFF; + lastOamDmaUpdate_ += 4; - if (oamDmaPos < 0xA0) { - if (oamDmaPos == 0) - startOamDma(lastOamDmaUpdate - 1); + if (oamDmaPos_ < 0xA0) { + if (oamDmaPos_ == 0) + startOamDma(lastOamDmaUpdate_ - 1); - ioamhram[oamDmaPos] = oamDmaSrc ? oamDmaSrc[oamDmaPos] : cart.rtcRead(); - } else if (oamDmaPos == 0xA0) { - endOamDma(lastOamDmaUpdate - 1); - lastOamDmaUpdate = disabled_time; + ioamhram_[oamDmaPos_] = oamDmaSrc ? oamDmaSrc[oamDmaPos_] : cart_.rtcRead(); + } else if (oamDmaPos_ == 0xA0) { + endOamDma(lastOamDmaUpdate_ - 1); + lastOamDmaUpdate_ = disabled_time; break; } } } void Memory::oamDmaInitSetup() { - if (ioamhram[0x146] < 0xA0) { - cart.setOamDmaSrc(ioamhram[0x146] < 0x80 ? oam_dma_src_rom : oam_dma_src_vram); - } else if (ioamhram[0x146] < 0xFE - isCgb() * 0x1E) { - cart.setOamDmaSrc(ioamhram[0x146] < 0xC0 ? oam_dma_src_sram : oam_dma_src_wram); + if (ioamhram_[0x146] < 0xA0) { + cart_.setOamDmaSrc(ioamhram_[0x146] < 0x80 ? oam_dma_src_rom : oam_dma_src_vram); + } else if (ioamhram_[0x146] < 0xFE - isCgb() * 0x1E) { + cart_.setOamDmaSrc(ioamhram_[0x146] < 0xC0 ? oam_dma_src_sram : oam_dma_src_wram); } else - cart.setOamDmaSrc(oam_dma_src_invalid); + cart_.setOamDmaSrc(oam_dma_src_invalid); } -static const unsigned char * oamDmaSrcZero() { +static unsigned char const * oamDmaSrcZero() { static unsigned char zeroMem[0xA0]; return zeroMem; } -const unsigned char * Memory::oamDmaSrcPtr() const { - switch (cart.oamDmaSrc()) { - case oam_dma_src_rom: return cart.romdata(ioamhram[0x146] >> 6) + (ioamhram[0x146] << 8); - case oam_dma_src_sram: return cart.rsrambankptr() ? cart.rsrambankptr() + (ioamhram[0x146] << 8) : 0; - case oam_dma_src_vram: return cart.vrambankptr() + (ioamhram[0x146] << 8); - case oam_dma_src_wram: return cart.wramdata(ioamhram[0x146] >> 4 & 1) + (ioamhram[0x146] << 8 & 0xFFF); +unsigned char const * Memory::oamDmaSrcPtr() const { + switch (cart_.oamDmaSrc()) { + case oam_dma_src_rom: + return cart_.romdata(ioamhram_[0x146] >> 6) + (ioamhram_[0x146] << 8); + case oam_dma_src_sram: + return cart_.rsrambankptr() ? cart_.rsrambankptr() + (ioamhram_[0x146] << 8) : 0; + case oam_dma_src_vram: + return cart_.vrambankptr() + (ioamhram_[0x146] << 8); + case oam_dma_src_wram: + return cart_.wramdata(ioamhram_[0x146] >> 4 & 1) + (ioamhram_[0x146] << 8 & 0xFFF); case oam_dma_src_invalid: - case oam_dma_src_off: break; + case oam_dma_src_off: + break; } - return ioamhram[0x146] == 0xFF && !isCgb() ? oamDmaSrcZero() : cart.rdisabledRam(); + return ioamhram_[0x146] == 0xFF && !isCgb() ? oamDmaSrcZero() : cart_.rdisabledRam(); } -void Memory::startOamDma(const unsigned long cycleCounter) { - display.oamChange(cart.rdisabledRam(), cycleCounter); +void Memory::startOamDma(unsigned long cc) { + lcd_.oamChange(cart_.rdisabledRam(), cc); } -void Memory::endOamDma(const unsigned long cycleCounter) { - oamDmaPos = 0xFE; - cart.setOamDmaSrc(oam_dma_src_off); - display.oamChange(ioamhram, cycleCounter); +void Memory::endOamDma(unsigned long cc) { + oamDmaPos_ = 0xFE; + cart_.setOamDmaSrc(oam_dma_src_off); + lcd_.oamChange(ioamhram_, cc); } -unsigned Memory::nontrivial_ff_read(const unsigned P, const unsigned long cycleCounter) { - if (lastOamDmaUpdate != disabled_time) - updateOamDma(cycleCounter); +unsigned Memory::nontrivial_ff_read(unsigned const p, unsigned long const cc) { + if (lastOamDmaUpdate_ != disabled_time) + updateOamDma(cc); - switch (P) { + switch (p) { case 0x00: updateInput(); break; case 0x01: case 0x02: - updateSerial(cycleCounter); + updateSerial(cc); break; case 0x04: { - const unsigned long divcycles = (cycleCounter - divLastUpdate) >> 8; - ioamhram[0x104] = (ioamhram[0x104] + divcycles) & 0xFF; - divLastUpdate += divcycles << 8; + unsigned long divcycles = (cc - divLastUpdate_) >> 8; + ioamhram_[0x104] = (ioamhram_[0x104] + divcycles) & 0xFF; + divLastUpdate_ += divcycles << 8; } break; case 0x05: - ioamhram[0x105] = tima.tima(cycleCounter); + ioamhram_[0x105] = tima_.tima(cc); break; case 0x0F: - updateIrqs(cycleCounter); - ioamhram[0x10F] = intreq.ifreg(); + updateIrqs(cc); + ioamhram_[0x10F] = intreq_.ifreg(); break; case 0x26: - if (ioamhram[0x126] & 0x80) { - sound.generateSamples(cycleCounter, isDoubleSpeed()); - ioamhram[0x126] = 0xF0 | sound.getStatus(); + if (ioamhram_[0x126] & 0x80) { + psg_.generateSamples(cc, isDoubleSpeed()); + ioamhram_[0x126] = 0xF0 | psg_.getStatus(); } else - ioamhram[0x126] = 0x70; + ioamhram_[0x126] = 0x70; break; case 0x30: @@ -517,26 +534,27 @@ unsigned Memory::nontrivial_ff_read(const unsigned P, const unsigned long cycleC case 0x3D: case 0x3E: case 0x3F: - sound.generateSamples(cycleCounter, isDoubleSpeed()); - return sound.waveRamRead(P & 0xF); + psg_.generateSamples(cc, isDoubleSpeed()); + return psg_.waveRamRead(p & 0xF); case 0x41: - return ioamhram[0x141] | display.getStat(ioamhram[0x145], cycleCounter); + return ioamhram_[0x141] | lcd_.getStat(ioamhram_[0x145], cc); case 0x44: - return display.getLyReg(cycleCounter/*+4*/); + return lcd_.getLyReg(cc); case 0x69: - return display.cgbBgColorRead(ioamhram[0x168] & 0x3F, cycleCounter); + return lcd_.cgbBgColorRead(ioamhram_[0x168] & 0x3F, cc); case 0x6B: - return display.cgbSpColorRead(ioamhram[0x16A] & 0x3F, cycleCounter); - default: break; + return lcd_.cgbSpColorRead(ioamhram_[0x16A] & 0x3F, cc); + default: + break; } - return ioamhram[P + 0x100]; + return ioamhram_[p + 0x100]; } -static bool isInOamDmaConflictArea(const OamDmaSrc oamDmaSrc, const unsigned addr, const bool cgb) { +static bool isInOamDmaConflictArea(OamDmaSrc const oamDmaSrc, unsigned const p, bool const cgb) { struct Area { unsigned short areaUpper, exceptAreaLower, exceptAreaWidth, pad; }; - static const Area cgbAreas[] = { + static Area const cgbAreas[] = { { 0xC000, 0x8000, 0x2000, 0 }, { 0xC000, 0x8000, 0x2000, 0 }, { 0xA000, 0x0000, 0x8000, 0 }, @@ -545,7 +563,7 @@ static bool isInOamDmaConflictArea(const OamDmaSrc oamDmaSrc, const unsigned add { 0x0000, 0x0000, 0x0000, 0 } }; - static const Area dmgAreas[] = { + static Area const dmgAreas[] = { { 0xFE00, 0x8000, 0x2000, 0 }, { 0xFE00, 0x8000, 0x2000, 0 }, { 0xA000, 0x0000, 0x8000, 0 }, @@ -554,257 +572,297 @@ static bool isInOamDmaConflictArea(const OamDmaSrc oamDmaSrc, const unsigned add { 0x0000, 0x0000, 0x0000, 0 } }; - const Area *const a = cgb ? cgbAreas : dmgAreas; - - return addr < a[oamDmaSrc].areaUpper && addr - a[oamDmaSrc].exceptAreaLower >= a[oamDmaSrc].exceptAreaWidth; + Area const *a = cgb ? cgbAreas : dmgAreas; + return p < a[oamDmaSrc].areaUpper + && p - a[oamDmaSrc].exceptAreaLower >= a[oamDmaSrc].exceptAreaWidth; } -unsigned Memory::nontrivial_read(const unsigned P, const unsigned long cycleCounter) { - if (P < 0xFF80) { - if (lastOamDmaUpdate != disabled_time) { - updateOamDma(cycleCounter); +unsigned Memory::nontrivial_read(unsigned const p, unsigned long const cc) { + if (p < 0xFF80) { + if (lastOamDmaUpdate_ != disabled_time) { + updateOamDma(cc); - if (isInOamDmaConflictArea(cart.oamDmaSrc(), P, isCgb()) && oamDmaPos < 0xA0) - return ioamhram[oamDmaPos]; + if (isInOamDmaConflictArea(cart_.oamDmaSrc(), p, isCgb()) && oamDmaPos_ < 0xA0) + return ioamhram_[oamDmaPos_]; } - if (P < 0xC000) { - if (P < 0x8000) - return cart.romdata(P >> 14)[P]; + if (p < 0xC000) { + if (p < 0x8000) + return cart_.romdata(p >> 14)[p]; - if (P < 0xA000) { - if (!display.vramAccessible(cycleCounter)) + if (p < 0xA000) { + if (!lcd_.vramAccessible(cc)) return 0xFF; - return cart.vrambankptr()[P]; + return cart_.vrambankptr()[p]; } - if (cart.rsrambankptr()) - return cart.rsrambankptr()[P]; + if (cart_.rsrambankptr()) + return cart_.rsrambankptr()[p]; - return cart.rtcRead(); + return cart_.rtcRead(); } - if (P < 0xFE00) - return cart.wramdata(P >> 12 & 1)[P & 0xFFF]; + if (p < 0xFE00) + return cart_.wramdata(p >> 12 & 1)[p & 0xFFF]; - long const ffp = long(P) - 0xFF00; + long const ffp = long(p) - 0xFF00; if (ffp >= 0) - return nontrivial_ff_read(ffp, cycleCounter); + return nontrivial_ff_read(ffp, cc); - if (!display.oamReadable(cycleCounter) || oamDmaPos < 0xA0) + if (!lcd_.oamReadable(cc) || oamDmaPos_ < 0xA0) return 0xFF; } - return ioamhram[P - 0xFE00]; + return ioamhram_[p - 0xFE00]; } -unsigned Memory::nontrivial_peek(const unsigned P) { - if (P < 0xC000) { - if (P < 0x8000) - return cart.romdata(P >> 14)[P]; +unsigned Memory::nontrivial_peek(unsigned const p) { + if (p < 0xC000) { + if (p < 0x8000) + return cart_.romdata(p >> 14)[p]; - if (P < 0xA000) { - return cart.vrambankptr()[P]; + if (p < 0xA000) { + return cart_.vrambankptr()[p]; } - if (cart.rsrambankptr()) - return cart.rsrambankptr()[P]; + if (cart_.rsrambankptr()) + return cart_.rsrambankptr()[p]; - return cart.rtcRead(); // verified side-effect free + return cart_.rtcRead(); // verified side-effect free } - if (P < 0xFE00) - return cart.wramdata(P >> 12 & 1)[P & 0xFFF]; - if (P >= 0xFF00 && P < 0xFF80) - return nontrivial_ff_peek(P); - return ioamhram[P - 0xFE00]; + if (p < 0xFE00) + return cart_.wramdata(p >> 12 & 1)[p & 0xFFF]; + if (p >= 0xFF00 && p < 0xFF80) + return nontrivial_ff_peek(p); + return ioamhram_[p - 0xFE00]; } -unsigned Memory::nontrivial_ff_peek(const unsigned P) { +unsigned Memory::nontrivial_ff_peek(unsigned const p) { // some regs may be somewhat wrong with this - return ioamhram[P - 0xFE00]; + return ioamhram_[p - 0xFE00]; } -void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned long cycleCounter) { - if (lastOamDmaUpdate != disabled_time) - updateOamDma(cycleCounter); +void Memory::nontrivial_ff_write(unsigned const p, unsigned data, unsigned long const cc) { + if (lastOamDmaUpdate_ != disabled_time) + updateOamDma(cc); - switch (P & 0xFF) { + switch (p & 0xFF) { case 0x00: - if ((data ^ ioamhram[0x100]) & 0x30) { - ioamhram[0x100] = (ioamhram[0x100] & ~0x30u) | (data & 0x30); + if ((data ^ ioamhram_[0x100]) & 0x30) { + ioamhram_[0x100] = (ioamhram_[0x100] & ~0x30u) | (data & 0x30); updateInput(); } + return; case 0x01: - updateSerial(cycleCounter); + updateSerial(cc); break; case 0x02: - updateSerial(cycleCounter); + updateSerial(cc); + serialCnt_ = 8; - serialCnt = 8; - intreq.setEventTime((data & 0x81) == 0x81 - ? (data & isCgb() * 2 ? (cycleCounter & ~0x7ul) + 0x10 * 8 : (cycleCounter & ~0xFFul) + 0x200 * 8) - : static_cast(disabled_time)); + if ((data & 0x81) == 0x81) { + intreq_.setEventTime(data & isCgb() * 2 + ? (cc & ~0x07ul) + 0x010 * 8 + : (cc & ~0xFFul) + 0x200 * 8); + } else + intreq_.setEventTime(disabled_time); data |= 0x7E - isCgb() * 2; break; case 0x04: - ioamhram[0x104] = 0; - divLastUpdate = cycleCounter; - tima.resTac(cycleCounter, TimaInterruptRequester(intreq)); + ioamhram_[0x104] = 0; + divLastUpdate_ = cc; + tima_.resTac(cc, TimaInterruptRequester(intreq_)); return; case 0x05: - tima.setTima(data, cycleCounter, TimaInterruptRequester(intreq)); + tima_.setTima(data, cc, TimaInterruptRequester(intreq_)); break; case 0x06: - tima.setTma(data, cycleCounter, TimaInterruptRequester(intreq)); + tima_.setTma(data, cc, TimaInterruptRequester(intreq_)); break; case 0x07: data |= 0xF8; - tima.setTac(data, cycleCounter, TimaInterruptRequester(intreq), gbIsCgb_); + tima_.setTac(data, cc, TimaInterruptRequester(intreq_), gbIsCgb_); break; case 0x0F: - updateIrqs(cycleCounter); - intreq.setIfreg(0xE0 | data); + updateIrqs(cc); + intreq_.setIfreg(0xE0 | data); return; case 0x10: - if (!sound.isEnabled()) return; - sound.generateSamples(cycleCounter, isDoubleSpeed()); - sound.setNr10(data); + if (!psg_.isEnabled()) + return; + + psg_.generateSamples(cc, isDoubleSpeed()); + psg_.setNr10(data); data |= 0x80; break; case 0x11: - if (!sound.isEnabled()) { + if (!psg_.isEnabled()) { if (isCgb()) return; data &= 0x3F; } - sound.generateSamples(cycleCounter, isDoubleSpeed()); - sound.setNr11(data); + psg_.generateSamples(cc, isDoubleSpeed()); + psg_.setNr11(data); data |= 0x3F; break; case 0x12: - if (!sound.isEnabled()) return; - sound.generateSamples(cycleCounter, isDoubleSpeed()); - sound.setNr12(data); + if (!psg_.isEnabled()) + return; + + psg_.generateSamples(cc, isDoubleSpeed()); + psg_.setNr12(data); break; case 0x13: - if (!sound.isEnabled()) return; - sound.generateSamples(cycleCounter, isDoubleSpeed()); - sound.setNr13(data); + if (!psg_.isEnabled()) + return; + + psg_.generateSamples(cc, isDoubleSpeed()); + psg_.setNr13(data); return; case 0x14: - if (!sound.isEnabled()) return; - sound.generateSamples(cycleCounter, isDoubleSpeed()); - sound.setNr14(data); + if (!psg_.isEnabled()) + return; + + psg_.generateSamples(cc, isDoubleSpeed()); + psg_.setNr14(data); data |= 0xBF; break; case 0x16: - if (!sound.isEnabled()) { + if (!psg_.isEnabled()) { if (isCgb()) return; data &= 0x3F; } - sound.generateSamples(cycleCounter, isDoubleSpeed()); - sound.setNr21(data); + psg_.generateSamples(cc, isDoubleSpeed()); + psg_.setNr21(data); data |= 0x3F; break; case 0x17: - if (!sound.isEnabled()) return; - sound.generateSamples(cycleCounter, isDoubleSpeed()); - sound.setNr22(data); + if (!psg_.isEnabled()) + return; + + psg_.generateSamples(cc, isDoubleSpeed()); + psg_.setNr22(data); break; case 0x18: - if (!sound.isEnabled()) return; - sound.generateSamples(cycleCounter, isDoubleSpeed()); - sound.setNr23(data); + if (!psg_.isEnabled()) + return; + + psg_.generateSamples(cc, isDoubleSpeed()); + psg_.setNr23(data); return; case 0x19: - if (!sound.isEnabled()) return; - sound.generateSamples(cycleCounter, isDoubleSpeed()); - sound.setNr24(data); + if (!psg_.isEnabled()) + return; + + psg_.generateSamples(cc, isDoubleSpeed()); + psg_.setNr24(data); data |= 0xBF; break; case 0x1A: - if (!sound.isEnabled()) return; - sound.generateSamples(cycleCounter, isDoubleSpeed()); - sound.setNr30(data); + if (!psg_.isEnabled()) + return; + + psg_.generateSamples(cc, isDoubleSpeed()); + psg_.setNr30(data); data |= 0x7F; break; case 0x1B: - if (!sound.isEnabled() && isCgb()) return; - sound.generateSamples(cycleCounter, isDoubleSpeed()); - sound.setNr31(data); + if (!psg_.isEnabled() && isCgb()) + return; + + psg_.generateSamples(cc, isDoubleSpeed()); + psg_.setNr31(data); return; case 0x1C: - if (!sound.isEnabled()) return; - sound.generateSamples(cycleCounter, isDoubleSpeed()); - sound.setNr32(data); + if (!psg_.isEnabled()) + return; + + psg_.generateSamples(cc, isDoubleSpeed()); + psg_.setNr32(data); data |= 0x9F; break; case 0x1D: - if (!sound.isEnabled()) return; - sound.generateSamples(cycleCounter, isDoubleSpeed()); - sound.setNr33(data); + if (!psg_.isEnabled()) + return; + + psg_.generateSamples(cc, isDoubleSpeed()); + psg_.setNr33(data); return; case 0x1E: - if (!sound.isEnabled()) return; - sound.generateSamples(cycleCounter, isDoubleSpeed()); - sound.setNr34(data); + if (!psg_.isEnabled()) + return; + + psg_.generateSamples(cc, isDoubleSpeed()); + psg_.setNr34(data); data |= 0xBF; break; case 0x20: - if (!sound.isEnabled() && isCgb()) return; - sound.generateSamples(cycleCounter, isDoubleSpeed()); - sound.setNr41(data); + if (!psg_.isEnabled() && isCgb()) + return; + + psg_.generateSamples(cc, isDoubleSpeed()); + psg_.setNr41(data); return; case 0x21: - if (!sound.isEnabled()) return; - sound.generateSamples(cycleCounter, isDoubleSpeed()); - sound.setNr42(data); + if (!psg_.isEnabled()) + return; + + psg_.generateSamples(cc, isDoubleSpeed()); + psg_.setNr42(data); break; case 0x22: - if (!sound.isEnabled()) return; - sound.generateSamples(cycleCounter, isDoubleSpeed()); - sound.setNr43(data); + if (!psg_.isEnabled()) + return; + + psg_.generateSamples(cc, isDoubleSpeed()); + psg_.setNr43(data); break; case 0x23: - if (!sound.isEnabled()) return; - sound.generateSamples(cycleCounter, isDoubleSpeed()); - sound.setNr44(data); + if (!psg_.isEnabled()) + return; + + psg_.generateSamples(cc, isDoubleSpeed()); + psg_.setNr44(data); data |= 0xBF; break; case 0x24: - if (!sound.isEnabled()) return; - sound.generateSamples(cycleCounter, isDoubleSpeed()); - sound.setSoVolume(data); + if (!psg_.isEnabled()) + return; + + psg_.generateSamples(cc, isDoubleSpeed()); + psg_.setSoVolume(data); break; case 0x25: - if (!sound.isEnabled()) return; - sound.generateSamples(cycleCounter, isDoubleSpeed()); - sound.mapSo(data); + if (!psg_.isEnabled()) + return; + + psg_.generateSamples(cc, isDoubleSpeed()); + psg_.mapSo(data); break; case 0x26: - if ((ioamhram[0x126] ^ data) & 0x80) { - sound.generateSamples(cycleCounter, isDoubleSpeed()); + if ((ioamhram_[0x126] ^ data) & 0x80) { + psg_.generateSamples(cc, isDoubleSpeed()); if (!(data & 0x80)) { for (unsigned i = 0x10; i < 0x26; ++i) - ff_write(i, 0, cycleCounter); + ff_write(i, 0, cc); - sound.setEnabled(false); + psg_.setEnabled(false); } else { - sound.reset(); - sound.setEnabled(true); + psg_.reset(); + psg_.setEnabled(true); } } - data = (data & 0x80) | (ioamhram[0x126] & 0x7F); + data = (data & 0x80) | (ioamhram_[0x126] & 0x7F); break; case 0x30: case 0x31: @@ -822,178 +880,178 @@ void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned case 0x3D: case 0x3E: case 0x3F: - sound.generateSamples(cycleCounter, isDoubleSpeed()); - sound.waveRamWrite(P & 0xF, data); + psg_.generateSamples(cc, isDoubleSpeed()); + psg_.waveRamWrite(p & 0xF, data); break; case 0x40: - if (ioamhram[0x140] != data) { - if ((ioamhram[0x140] ^ data) & 0x80) { - const unsigned lyc = display.getStat(ioamhram[0x145], cycleCounter) & 4; - const bool hdmaEnabled = display.hdmaIsEnabled(); + if (ioamhram_[0x140] != data) { + if ((ioamhram_[0x140] ^ data) & lcdc_en) { + unsigned const lyc = lcd_.getStat(ioamhram_[0x145], cc) + & lcdstat_lycflag; + bool const hdmaEnabled = lcd_.hdmaIsEnabled(); - display.lcdcChange(data, cycleCounter); - ioamhram[0x144] = 0; - ioamhram[0x141] &= 0xF8; + lcd_.lcdcChange(data, cc); + ioamhram_[0x144] = 0; + ioamhram_[0x141] &= 0xF8; - if (data & 0x80) { - intreq.setEventTime(display.nextMode1IrqTime() + (blanklcd ? 0 : 70224 << isDoubleSpeed())); + if (data & lcdc_en) { + intreq_.setEventTime(blanklcd_ + ? lcd_.nextMode1IrqTime() + : lcd_.nextMode1IrqTime() + + (70224 << isDoubleSpeed())); } else { - ioamhram[0x141] |= lyc; - intreq.setEventTime(cycleCounter + (456 * 4 << isDoubleSpeed())); + ioamhram_[0x141] |= lyc; + intreq_.setEventTime( + cc + (456 * 4 << isDoubleSpeed())); if (hdmaEnabled) - flagHdmaReq(intreq); + flagHdmaReq(intreq_); } } else - display.lcdcChange(data, cycleCounter); + lcd_.lcdcChange(data, cc); - ioamhram[0x140] = data; + ioamhram_[0x140] = data; } return; case 0x41: - display.lcdstatChange(data, cycleCounter); - data = (ioamhram[0x141] & 0x87) | (data & 0x78); + lcd_.lcdstatChange(data, cc); + data = (ioamhram_[0x141] & 0x87) | (data & 0x78); break; case 0x42: - display.scyChange(data, cycleCounter); + lcd_.scyChange(data, cc); break; case 0x43: - display.scxChange(data, cycleCounter); + lcd_.scxChange(data, cc); break; case 0x45: - display.lycRegChange(data, cycleCounter); + lcd_.lycRegChange(data, cc); break; case 0x46: - if (lastOamDmaUpdate != disabled_time) - endOamDma(cycleCounter); + if (lastOamDmaUpdate_ != disabled_time) + endOamDma(cc); - lastOamDmaUpdate = cycleCounter; - intreq.setEventTime(cycleCounter + 8); - ioamhram[0x146] = data; + lastOamDmaUpdate_ = cc; + intreq_.setEventTime(cc + 8); + ioamhram_[0x146] = data; oamDmaInitSetup(); return; case 0x47: if (!isCgb()) - display.dmgBgPaletteChange(data, cycleCounter); + lcd_.dmgBgPaletteChange(data, cc); break; case 0x48: if (!isCgb()) - display.dmgSpPalette1Change(data, cycleCounter); + lcd_.dmgSpPalette1Change(data, cc); break; case 0x49: if (!isCgb()) - display.dmgSpPalette2Change(data, cycleCounter); + lcd_.dmgSpPalette2Change(data, cc); break; case 0x4A: - display.wyChange(data, cycleCounter); + lcd_.wyChange(data, cc); break; case 0x4B: - display.wxChange(data, cycleCounter); - break; - case 0x4C: - if (biosMode) { - //flagClockReq(&intreq); - } + lcd_.wxChange(data, cc); break; case 0x4D: if (isCgb()) - ioamhram[0x14D] = (ioamhram[0x14D] & ~1u) | (data & 1); return; + ioamhram_[0x14D] = (ioamhram_[0x14D] & ~1u) | (data & 1); + + return; case 0x4F: if (isCgb()) { - cart.setVrambank(data & 1); - ioamhram[0x14F] = 0xFE | data; + cart_.setVrambank(data & 1); + ioamhram_[0x14F] = 0xFE | data; } return; case 0x50: - biosMode = false; - if (cgbSwitching) { - display.copyCgbPalettesToDmg(); - display.setCgb(false); - cgbSwitching = false; + biosMode_ = false; + if(cgbSwitching_) { + lcd_.copyCgbPalettesToDmg(); + lcd_.setCgb(false); + cgbSwitching_ = false; } return; case 0x51: - dmaSource = data << 8 | (dmaSource & 0xFF); + dmaSource_ = data << 8 | (dmaSource_ & 0xFF); return; case 0x52: - dmaSource = (dmaSource & 0xFF00) | (data & 0xF0); + dmaSource_ = (dmaSource_ & 0xFF00) | (data & 0xF0); return; case 0x53: - dmaDestination = data << 8 | (dmaDestination & 0xFF); + dmaDestination_ = data << 8 | (dmaDestination_ & 0xFF); return; case 0x54: - dmaDestination = (dmaDestination & 0xFF00) | (data & 0xF0); + dmaDestination_ = (dmaDestination_ & 0xFF00) | (data & 0xF0); return; case 0x55: if (isCgb()) { - ioamhram[0x155] = data & 0x7F; + ioamhram_[0x155] = data & 0x7F; - if (display.hdmaIsEnabled()) { + if (lcd_.hdmaIsEnabled()) { if (!(data & 0x80)) { - ioamhram[0x155] |= 0x80; - display.disableHdma(cycleCounter); + ioamhram_[0x155] |= 0x80; + lcd_.disableHdma(cc); } } else { if (data & 0x80) { - if (ioamhram[0x140] & 0x80) { - display.enableHdma(cycleCounter); + if (ioamhram_[0x140] & lcdc_en) { + lcd_.enableHdma(cc); } else - flagHdmaReq(intreq); + flagHdmaReq(intreq_); } else - flagGdmaReq(intreq); + flagGdmaReq(intreq_); } } return; case 0x56: if (isCgb()) - ioamhram[0x156] = data | 0x3E; + ioamhram_[0x156] = data | 0x3E; return; case 0x68: if (isCgb()) - ioamhram[0x168] = data | 0x40; + ioamhram_[0x168] = data | 0x40; return; case 0x69: if (isCgb()) { - const unsigned index = ioamhram[0x168] & 0x3F; - - display.cgbBgColorChange(index, data, cycleCounter); - - ioamhram[0x168] = (ioamhram[0x168] & ~0x3F) | ((index + (ioamhram[0x168] >> 7)) & 0x3F); + unsigned index = ioamhram_[0x168] & 0x3F; + lcd_.cgbBgColorChange(index, data, cc); + ioamhram_[0x168] = (ioamhram_[0x168] & ~0x3F) + | ((index + (ioamhram_[0x168] >> 7)) & 0x3F); } return; case 0x6A: if (isCgb()) - ioamhram[0x16A] = data | 0x40; + ioamhram_[0x16A] = data | 0x40; return; case 0x6B: if (isCgb()) { - const unsigned index = ioamhram[0x16A] & 0x3F; - - display.cgbSpColorChange(index, data, cycleCounter); - - ioamhram[0x16A] = (ioamhram[0x16A] & ~0x3F) | ((index + (ioamhram[0x16A] >> 7)) & 0x3F); + unsigned index = ioamhram_[0x16A] & 0x3F; + lcd_.cgbSpColorChange(index, data, cc); + ioamhram_[0x16A] = (ioamhram_[0x16A] & ~0x3F) + | ((index + (ioamhram_[0x16A] >> 7)) & 0x3F); } return; case 0x6C: - ioamhram[0x16C] = data | 0xFE; - cgbSwitching = true; + ioamhram_[0x16C] = data | 0xFE; + cgbSwitching_ = true; return; case 0x70: if (isCgb()) { - cart.setWrambank((data & 0x07) ? (data & 0x07) : 1); - ioamhram[0x170] = data | 0xF8; + cart_.setWrambank(data & 0x07 ? data & 0x07 : 1); + ioamhram_[0x170] = data | 0xF8; } return; @@ -1006,74 +1064,74 @@ void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned return; case 0x75: if (isCgb()) - ioamhram[0x175] = data | 0x8F; + ioamhram_[0x175] = data | 0x8F; return; case 0xFF: - intreq.setIereg(data); + intreq_.setIereg(data); break; default: return; } - ioamhram[P + 0x100] = data; + ioamhram_[p + 0x100] = data; } -void Memory::nontrivial_write(const unsigned P, const unsigned data, const unsigned long cycleCounter) { - if (lastOamDmaUpdate != disabled_time) { - updateOamDma(cycleCounter); +void Memory::nontrivial_write(unsigned const p, unsigned const data, unsigned long const cc) { + if (lastOamDmaUpdate_ != disabled_time) { + updateOamDma(cc); - if (isInOamDmaConflictArea(cart.oamDmaSrc(), P, isCgb()) && oamDmaPos < 0xA0) { - ioamhram[oamDmaPos] = data; + if (isInOamDmaConflictArea(cart_.oamDmaSrc(), p, isCgb()) && oamDmaPos_ < 0xA0) { + ioamhram_[oamDmaPos_] = data; return; } } - if (P < 0xFE00) { - if (P < 0xA000) { - if (P < 0x8000) { - cart.mbcWrite(P, data); - } else if (display.vramAccessible(cycleCounter)) { - display.vramChange(cycleCounter); - cart.vrambankptr()[P] = data; + if (p < 0xFE00) { + if (p < 0xA000) { + if (p < 0x8000) { + cart_.mbcWrite(p, data); + } else if (lcd_.vramAccessible(cc)) { + lcd_.vramChange(cc); + cart_.vrambankptr()[p] = data; } - } else if (P < 0xC000) { - if (cart.wsrambankptr()) - cart.wsrambankptr()[P] = data; + } else if (p < 0xC000) { + if (cart_.wsrambankptr()) + cart_.wsrambankptr()[p] = data; else - cart.rtcWrite(data); + cart_.rtcWrite(data); } else - cart.wramdata(P >> 12 & 1)[P & 0xFFF] = data; - } else if (P - 0xFF80u >= 0x7Fu) { - long const ffp = long(P) - 0xFF00; + cart_.wramdata(p >> 12 & 1)[p & 0xFFF] = data; + } else if (p - 0xFF80u >= 0x7Fu) { + long const ffp = long(p) - 0xFF00; if (ffp < 0) { - if (display.oamWritable(cycleCounter) && oamDmaPos >= 0xA0 && (P < 0xFEA0 || isCgb())) { - display.oamChange(cycleCounter); - ioamhram[P - 0xFE00] = data; + if (lcd_.oamWritable(cc) && oamDmaPos_ >= 0xA0 && (p < 0xFEA0 || isCgb())) { + lcd_.oamChange(cc); + ioamhram_[p - 0xFE00] = data; } } else - nontrivial_ff_write(ffp, data, cycleCounter); + nontrivial_ff_write(ffp, data, cc); } else - ioamhram[P - 0xFE00] = data; + ioamhram_[p - 0xFE00] = data; } LoadRes Memory::loadROM(char const *romfiledata, unsigned romfilelength, const bool forceDmg, const bool multicartCompat) { - if (LoadRes const fail = cart.loadROM(romfiledata, romfilelength, forceDmg, multicartCompat)) + if (LoadRes const fail = cart_.loadROM(romfiledata, romfilelength, forceDmg, multicartCompat)) return fail; - sound.init(cart.isCgb()); - display.reset(ioamhram, cart.vramdata(), cart.isCgb()); + psg_.init(cart_.isCgb()); + lcd_.reset(ioamhram_, cart_.vramdata(), cart_.isCgb()); return LOADRES_OK; } -std::size_t Memory::fillSoundBuffer(const unsigned long cycleCounter) { - sound.generateSamples(cycleCounter, isDoubleSpeed()); - return sound.fillBuffer(); +std::size_t Memory::fillSoundBuffer(unsigned long cc) { + psg_.generateSamples(cc, isDoubleSpeed()); + return psg_.fillBuffer(); } void Memory::setCgbPalette(unsigned *lut) { - display.setCgbPalette(lut); + lcd_.setCgbPalette(lut); } bool Memory::getMemoryArea(int which, unsigned char **data, int *length) { @@ -1083,23 +1141,23 @@ bool Memory::getMemoryArea(int which, unsigned char **data, int *length) { switch (which) { case 4: // oam - *data = &ioamhram[0]; + *data = &ioamhram_[0]; *length = 160; return true; case 5: // hram - *data = &ioamhram[384]; + *data = &ioamhram_[384]; *length = 128; return true; case 6: // bgpal - *data = (unsigned char *)display.bgPalette(); + *data = (unsigned char *)lcd_.bgPalette(); *length = 32; return true; case 7: // sppal - *data = (unsigned char *)display.spPalette(); + *data = (unsigned char *)lcd_.spPalette(); *length = 32; return true; default: // pass to cartridge - return cart.getMemoryArea(which, data, length); + return cart_.getMemoryArea(which, data, length); } } @@ -1108,21 +1166,21 @@ int Memory::linkStatus(int which) switch (which) { case 256: // ClockSignaled - return linkClockTrigger; + return linkClockTrigger_; case 257: // AckClockSignal - linkClockTrigger = false; + linkClockTrigger_ = false; return 0; case 258: // GetOut - return ioamhram[0x101] & 0xff; + return ioamhram_[0x101] & 0xff; case 259: // connect link cable - LINKCABLE = true; + LINKCABLE_ = true; return 0; default: // ShiftIn - if (ioamhram[0x102] & 0x80) // was enabled + if (ioamhram_[0x102] & 0x80) // was enabled { - ioamhram[0x101] = which; - ioamhram[0x102] &= 0x7F; - intreq.flagIrq(8); + ioamhram_[0x101] = which; + ioamhram_[0x102] &= 0x7F; + intreq_.flagIrq(8); } return 0; } @@ -1132,30 +1190,30 @@ int Memory::linkStatus(int which) SYNCFUNC(Memory) { - SSS(cart); - NSS(ioamhram); - NSS(divLastUpdate); - NSS(lastOamDmaUpdate); - NSS(biosMode); - NSS(cgbSwitching); - NSS(agbMode); + SSS(cart_); + NSS(ioamhram_); + NSS(divLastUpdate_); + NSS(lastOamDmaUpdate_); + NSS(biosMode_); + NSS(cgbSwitching_); + NSS(agbMode_); NSS(gbIsCgb_); - NSS(stopped); - NSS(halttime); + NSS(stopped_); + NSS(halttime_); - SSS(intreq); - SSS(tima); - SSS(display); - SSS(sound); + SSS(intreq_); + SSS(tima_); + SSS(lcd_); + SSS(psg_); - NSS(dmaSource); - NSS(dmaDestination); - NSS(oamDmaPos); - NSS(serialCnt); - NSS(blanklcd); + NSS(dmaSource_); + NSS(dmaDestination_); + NSS(oamDmaPos_); + NSS(serialCnt_); + NSS(blanklcd_); - NSS(LINKCABLE); - NSS(linkClockTrigger); + NSS(LINKCABLE_); + NSS(linkClockTrigger_); } } diff --git a/libgambatte/src/memory.h b/libgambatte/src/memory.h index 7d4a250dc8..4999e6d34c 100644 --- a/libgambatte/src/memory.h +++ b/libgambatte/src/memory.h @@ -1,30 +1,30 @@ -/*************************************************************************** - * Copyright (C) 2007 by Sindre Aamås * - * aamas@stud.ntnu.no * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License version 2 as * - * published by the Free Software Foundation. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License version 2 for more details. * - * * - * You should have received a copy of the GNU General Public License * - * version 2 along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ +// +// Copyright (C) 2007 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + #ifndef MEMORY_H #define MEMORY_H static unsigned char const agbOverride[0xD] = { 0xFF, 0x00, 0xCD, 0x03, 0x35, 0xAA, 0x31, 0x90, 0x94, 0x00, 0x00, 0x00, 0x00 }; #include "mem/cartridge.h" -#include "video.h" #include "sound.h" #include "tima.h" +#include "video.h" #include "newstate.h" #include "gambatte.h" @@ -32,111 +32,59 @@ namespace gambatte { class FilterInfo; class Memory { - Cartridge cart; - unsigned char ioamhram[0x200]; - unsigned char cgbBios[0x900]; - unsigned char dmgBios[0x100]; - bool biosMode; - bool cgbSwitching; - bool agbMode; - bool gbIsCgb_; - bool stopped; - unsigned short &SP; - unsigned short &PC; - unsigned long basetime; - unsigned long halttime; - - MemoryCallback readCallback; - MemoryCallback writeCallback; - MemoryCallback execCallback; - CDCallback cdCallback; - void(*linkCallback)(); - - unsigned (*getInput)(); - unsigned long divLastUpdate; - unsigned long lastOamDmaUpdate; - - InterruptRequester intreq; - Tima tima; - LCD display; - PSG sound; - - unsigned short dmaSource; - unsigned short dmaDestination; - unsigned char oamDmaPos; - unsigned char serialCnt; - bool blanklcd; - - bool LINKCABLE; - bool linkClockTrigger; - - void decEventCycles(IntEventId eventId, unsigned long dec); - - void oamDmaInitSetup(); - void updateOamDma(unsigned long cycleCounter); - void startOamDma(unsigned long cycleCounter); - void endOamDma(unsigned long cycleCounter); - const unsigned char * oamDmaSrcPtr() const; - - unsigned nontrivial_ff_read(unsigned P, unsigned long cycleCounter); - unsigned nontrivial_read(unsigned P, unsigned long cycleCounter); - void nontrivial_ff_write(unsigned P, unsigned data, unsigned long cycleCounter); - void nontrivial_write(unsigned P, unsigned data, unsigned long cycleCounter); - - unsigned nontrivial_peek(unsigned P); - unsigned nontrivial_ff_peek(unsigned P); - - void updateSerial(unsigned long cc); - void updateTimaIrq(unsigned long cc); - void updateIrqs(unsigned long cc); - - bool isDoubleSpeed() const { return display.isDoubleSpeed(); } - public: explicit Memory(unsigned short &sp, unsigned short &pc); - bool loaded() const { return cart.loaded(); } - unsigned curRomBank() const { return cart.curRomBank(); } - char const * romTitle() const { return cart.romTitle(); } - - int debugGetLY() const { return display.debugGetLY(); } - + bool loaded() const { return cart_.loaded(); } + unsigned char curRomBank() const { return cart_.curRomBank(); } + char const * romTitle() const { return cart_.romTitle(); } + int debugGetLY() const { return lcd_.debugGetLY(); } void setStatePtrs(SaveState &state); - void loadState(const SaveState &state/*, unsigned long oldCc*/); - void loadSavedata(char const *data) { cart.loadSavedata(data); } - int saveSavedataLength() {return cart.saveSavedataLength(); } - void saveSavedata(char *dest) { cart.saveSavedata(dest); } + void loadState(SaveState const &state); + void loadSavedata(char const *data) { cart_.loadSavedata(data); } + int saveSavedataLength() {return cart_.saveSavedataLength(); } + void saveSavedata(char *dest) { cart_.saveSavedata(dest); } void updateInput(); - unsigned char* cgbBiosBuffer() { return (unsigned char*)cgbBios; } - unsigned char* dmgBiosBuffer() { return (unsigned char*)dmgBios; } + unsigned char* cgbBiosBuffer() { return (unsigned char*)cgbBios_; } + unsigned char* dmgBiosBuffer() { return (unsigned char*)dmgBios_; } bool gbIsCgb() { return gbIsCgb_; } bool getMemoryArea(int which, unsigned char **data, int *length); // { return cart.getMemoryArea(which, data, length); } unsigned long stop(unsigned long cycleCounter); - bool isCgb() const { return display.isCgb(); } - bool ime() const { return intreq.ime(); } - bool halted() const { return intreq.halted(); } - unsigned long nextEventTime() const { return intreq.minEventTime(); } + bool isCgb() const { return lcd_.isCgb(); } + bool ime() const { return intreq_.ime(); } + bool halted() const { return intreq_.halted(); } + unsigned long nextEventTime() const { return intreq_.minEventTime(); } + void setLayers(unsigned mask) { lcd_.setLayers(mask); } + bool isActive() const { return intreq_.eventTime(intevent_end) != disabled_time; } - void setLayers(unsigned mask) { display.setLayers(mask); } + long cyclesSinceBlit(unsigned long cc) const { + if (cc < intreq_.eventTime(intevent_blit)) + return -1; - bool isActive() const { return intreq.eventTime(intevent_end) != disabled_time; } - - long cyclesSinceBlit(const unsigned long cc) const { - return cc < intreq.eventTime(intevent_blit) ? -1 : static_cast((cc - intreq.eventTime(intevent_blit)) >> isDoubleSpeed()); + return (cc - intreq_.eventTime(intevent_blit)) >> isDoubleSpeed(); } - void halt(unsigned long cycleCounter) { halttime = cycleCounter; intreq.halt(); } - void ei(unsigned long cycleCounter) { if (!ime()) { intreq.ei(cycleCounter); } } + void halt(unsigned long cycleCounter) { halttime_ = cycleCounter; intreq_.halt(); } + void ei(unsigned long cycleCounter) { if (!ime()) { intreq_.ei(cycleCounter); } } + void di() { intreq_.di(); } - void di() { intreq.di(); } + unsigned readBios(unsigned p) { + if (gbIsCgb_) { + if (agbMode_ && p >= 0xF3 && p < 0x100) { + return (agbOverride[p - 0xF3] + cgbBios_[p]) & 0xFF; + } + return cgbBios_[p]; + } + return dmgBios_[p]; + } - unsigned ff_read(const unsigned P, const unsigned long cycleCounter) { - if (readCallback) - readCallback(P, (cycleCounter - basetime) >> 1); - return P < 0x80 ? nontrivial_ff_read(P, cycleCounter) : ioamhram[P + 0x100]; + unsigned ff_read(unsigned p, unsigned long cc) { + if (readCallback_) + readCallback_(p, (cc - basetime_) >> 1); + return p < 0x80 ? nontrivial_ff_read(p, cc) : ioamhram_[p + 0x100]; } struct CDMapResult @@ -145,30 +93,30 @@ public: unsigned addr; }; - CDMapResult CDMap(const unsigned P) const + CDMapResult CDMap(const unsigned p) const { - if(P<0x4000) + if(p < 0x4000) { - CDMapResult ret = { eCDLog_AddrType_ROM, P }; + CDMapResult ret = { eCDLog_AddrType_ROM, p }; return ret; } - else if(P<0x8000) + else if(p < 0x8000) { - unsigned bank = cart.rmem(P>>12) - cart.rmem(0); - unsigned addr = P+bank; + unsigned bank = cart_.rmem(p >> 12) - cart_.rmem(0); + unsigned addr = p + bank; CDMapResult ret = { eCDLog_AddrType_ROM, addr }; return ret; } - else if(P<0xA000) {} - else if(P<0xC000) + else if(p < 0xA000) {} + else if(p < 0xC000) { - if(cart.wsrambankptr()) + if(cart_.wsrambankptr()) { //not bankable. but. we're not sure how much might be here unsigned char *data; int length; - bool has = cart.getMemoryArea(3,&data,&length); - unsigned addr = P&(length-1); + bool has = cart_.getMemoryArea(3,&data,&length); + unsigned addr = p & (length-1); if(has && length!=0) { CDMapResult ret = { eCDLog_AddrType_CartRAM, addr }; @@ -176,14 +124,14 @@ public: } } } - else if(P<0xE000) + else if(p < 0xE000) { - unsigned bank = cart.wramdata(P >> 12 & 1) - cart.wramdata(0); - unsigned addr = (P&0xFFF)+bank; + unsigned bank = cart_.wramdata(p >> 12 & 1) - cart_.wramdata(0); + unsigned addr = (p & 0xFFF) + bank; CDMapResult ret = { eCDLog_AddrType_WRAM, addr }; return ret; } - else if(P<0xFF80) {} + else if(p < 0xFF80) {} else { ////this is just for debugging, really, it's pretty useless @@ -195,157 +143,190 @@ public: return ret; } - - unsigned readBios(const unsigned P) { - if (gbIsCgb_) { - if (agbMode && P >= 0xF3 && P < 0x100) { - return (agbOverride[P - 0xF3] + cgbBios[P]) & 0xFF; - } - return cgbBios[P]; - } - return dmgBios[P]; - } - - unsigned read(const unsigned P, const unsigned long cycleCounter) { - if (readCallback) - readCallback(P, (cycleCounter - basetime) >> 1); - bool biosRange = ((!gbIsCgb_ && P < 0x100) || (gbIsCgb_ && P < 0x900 && (P < 0x100 || P >= 0x200))); - if(biosMode) { + unsigned read(unsigned p, unsigned long cc) { + if (readCallback_) + readCallback_(p, (cc - basetime_) >> 1); + bool biosRange = ((!gbIsCgb_ && p < 0x100) || (gbIsCgb_ && p < 0x900 && (p < 0x100 || p >= 0x200))); + if(biosMode_) { if (biosRange) - return readBios(P); + return readBios(p); } - else - { - if(cdCallback) - { - CDMapResult map = CDMap(P); - if(map.type != eCDLog_AddrType_None) - cdCallback(map.addr,map.type,eCDLog_Flags_Data); - } + else if(cdCallback_) { + CDMapResult map = CDMap(p); + if(map.type != eCDLog_AddrType_None) + cdCallback_(map.addr, map.type, eCDLog_Flags_Data); } - return cart.rmem(P >> 12) ? cart.rmem(P >> 12)[P] : nontrivial_read(P, cycleCounter); + return cart_.rmem(p >> 12) ? cart_.rmem(p >> 12)[p] : nontrivial_read(p, cc); } - unsigned read_excb(const unsigned P, const unsigned long cycleCounter, bool first) { - if (execCallback) - execCallback(P, (cycleCounter - basetime) >> 1); - bool biosRange = ((!gbIsCgb_ && P < 0x100) || (gbIsCgb_ && P < 0x900 && (P < 0x100 || P >= 0x200))); - if (biosMode) { + unsigned read_excb(unsigned p, unsigned long cc, bool first) { + if (execCallback_) + execCallback_(p, (cc - basetime_) >> 1); + bool biosRange = ((!gbIsCgb_ && p < 0x100) || (gbIsCgb_ && p < 0x900 && (p < 0x100 || p >= 0x200))); + if (biosMode_) { if(biosRange) - return readBios(P); + return readBios(p); } - else - { - if(cdCallback) - { - CDMapResult map = CDMap(P); - if(map.type != eCDLog_AddrType_None) - cdCallback(map.addr,map.type,first?eCDLog_Flags_ExecFirst : eCDLog_Flags_ExecOperand); - } - } - return cart.rmem(P >> 12) ? cart.rmem(P >> 12)[P] : nontrivial_read(P, cycleCounter); - } - - unsigned peek(const unsigned P) { - if (biosMode && ((!gbIsCgb_ && P < 0x100) || (gbIsCgb_ && P < 0x900 && (P < 0x100 || P >= 0x200)))) { - return readBios(P); - } - return cart.rmem(P >> 12) ? cart.rmem(P >> 12)[P] : nontrivial_peek(P); - } - - void write_nocb(const unsigned P, const unsigned data, const unsigned long cycleCounter) { - if (cart.wmem(P >> 12)) { - cart.wmem(P >> 12)[P] = data; - } else - nontrivial_write(P, data, cycleCounter); - } - - void write(const unsigned P, const unsigned data, const unsigned long cycleCounter) { - if (cart.wmem(P >> 12)) { - cart.wmem(P >> 12)[P] = data; - } else - nontrivial_write(P, data, cycleCounter); - if (writeCallback) - writeCallback(P, (cycleCounter - basetime) >> 1); - if(cdCallback && !biosMode) - { - CDMapResult map = CDMap(P); + else if(cdCallback_) { + CDMapResult map = CDMap(p); if(map.type != eCDLog_AddrType_None) - cdCallback(map.addr,map.type,eCDLog_Flags_Data); + cdCallback_(map.addr, map.type, first ? eCDLog_Flags_ExecFirst : eCDLog_Flags_ExecOperand); + } + return cart_.rmem(p >> 12) ? cart_.rmem(p >> 12)[p] : nontrivial_read(p, cc); + } + + unsigned peek(unsigned p) { + bool biosRange = ((!gbIsCgb_ && p < 0x100) || (gbIsCgb_ && p < 0x900 && (p < 0x100 || p >= 0x200))); + if (biosMode_ && biosRange) { + return readBios(p); + } + return cart_.rmem(p >> 12) ? cart_.rmem(p >> 12)[p] : nontrivial_peek(p); + } + + void write_nocb(unsigned p, unsigned data, unsigned long cc) { + if (cart_.wmem(p >> 12)) { + cart_.wmem(p >> 12)[p] = data; + } else + nontrivial_write(p, data, cc); + } + + void write(unsigned p, unsigned data, unsigned long cc) { + if (cart_.wmem(p >> 12)) { + cart_.wmem(p >> 12)[p] = data; + } else + nontrivial_write(p, data, cc); + if (writeCallback_) + writeCallback_(p, (cc - basetime_) >> 1); + if(cdCallback_ && !biosMode_) { + CDMapResult map = CDMap(p); + if(map.type != eCDLog_AddrType_None) + cdCallback_(map.addr, map.type, eCDLog_Flags_Data); } } - void ff_write(const unsigned P, const unsigned data, const unsigned long cycleCounter) { - if (P - 0x80u < 0x7Fu) { - ioamhram[P + 0x100] = data; + void ff_write(unsigned p, unsigned data, unsigned long cc) { + if (p - 0x80u < 0x7Fu) { + ioamhram_[p + 0x100] = data; } else - nontrivial_ff_write(P, data, cycleCounter); - if (writeCallback) - writeCallback(P, (cycleCounter - basetime) >> 1); - if(cdCallback && !biosMode) + nontrivial_ff_write(p, data, cc); + if (writeCallback_) + writeCallback_(0xff00 + p, (cc - basetime_) >> 1); + if(cdCallback_ && !biosMode_) { - CDMapResult map = CDMap(P); + CDMapResult map = CDMap(0xff00 + p); if(map.type != eCDLog_AddrType_None) - cdCallback(map.addr,map.type,eCDLog_Flags_Data); + cdCallback_(map.addr, map.type, eCDLog_Flags_Data); } } unsigned long event(unsigned long cycleCounter); unsigned long resetCounters(unsigned long cycleCounter); - LoadRes loadROM(char const *romfiledata, unsigned romfilelength, bool forceDmg, bool multicartCompat); void setInputGetter(unsigned (*getInput)()) { - this->getInput = getInput; + getInput_ = getInput; } void setReadCallback(MemoryCallback callback) { - this->readCallback = callback; + this->readCallback_ = callback; } void setWriteCallback(MemoryCallback callback) { - this->writeCallback = callback; + this->writeCallback_ = callback; } void setExecCallback(MemoryCallback callback) { - this->execCallback = callback; + this->execCallback_ = callback; } void setCDCallback(CDCallback cdc) { - this->cdCallback = cdc; + this->cdCallback_ = cdc; } void setScanlineCallback(void (*callback)(), int sl) { - display.setScanlineCallback(callback, sl); + lcd_.setScanlineCallback(callback, sl); } void setRTCCallback(std::uint32_t (*callback)()) { - cart.setRTCCallback(callback); + cart_.setRTCCallback(callback); } void setLinkCallback(void(*callback)()) { - this->linkCallback = callback; + this->linkCallback_ = callback; } - void setBasetime(unsigned long cc) { basetime = cc; } void setEndtime(unsigned long cc, unsigned long inc); + void setBasetime(unsigned long cc) { basetime_ = cc; } - void setSoundBuffer(uint_least32_t *const buf) { sound.setBuffer(buf); } + void setSoundBuffer(uint_least32_t *buf) { psg_.setBuffer(buf); } std::size_t fillSoundBuffer(unsigned long cc); - void setVideoBuffer(uint_least32_t *const videoBuf, const std::ptrdiff_t pitch) { - display.setVideoBuffer(videoBuf, pitch); + void setVideoBuffer(uint_least32_t *videoBuf, std::ptrdiff_t pitch) { + lcd_.setVideoBuffer(videoBuf, pitch); } void setDmgPaletteColor(int palNum, int colorNum, unsigned long rgb32) { - display.setDmgPaletteColor(palNum, colorNum, rgb32); + lcd_.setDmgPaletteColor(palNum, colorNum, rgb32); } void setCgbPalette(unsigned *lut); void blackScreen() { - display.blackScreen(); + lcd_.blackScreen(); } int linkStatus(int which); +private: + Cartridge cart_; + unsigned char ioamhram_[0x200]; + unsigned char cgbBios_[0x900]; + unsigned char dmgBios_[0x100]; + unsigned (*getInput_)(); + unsigned long divLastUpdate_; + unsigned long lastOamDmaUpdate_; + InterruptRequester intreq_; + Tima tima_; + LCD lcd_; + PSG psg_; + unsigned short dmaSource_; + unsigned short dmaDestination_; + unsigned char oamDmaPos_; + unsigned char serialCnt_; + bool blanklcd_; + bool biosMode_; + bool cgbSwitching_; + bool agbMode_; + bool gbIsCgb_; + unsigned short &sp_; + unsigned short &pc_; + unsigned long basetime_; + unsigned long halttime_; + bool stopped_; + + MemoryCallback readCallback_; + MemoryCallback writeCallback_; + MemoryCallback execCallback_; + CDCallback cdCallback_; + void(*linkCallback_)(); + bool LINKCABLE_; + bool linkClockTrigger_; + + void decEventCycles(IntEventId eventId, unsigned long dec); + void oamDmaInitSetup(); + void updateOamDma(unsigned long cycleCounter); + void startOamDma(unsigned long cycleCounter); + void endOamDma(unsigned long cycleCounter); + unsigned char const * oamDmaSrcPtr() const; + unsigned nontrivial_ff_read(unsigned p, unsigned long cycleCounter); + unsigned nontrivial_read(unsigned p, unsigned long cycleCounter); + void nontrivial_ff_write(unsigned p, unsigned data, unsigned long cycleCounter); + void nontrivial_write(unsigned p, unsigned data, unsigned long cycleCounter); + unsigned nontrivial_peek(unsigned p); + unsigned nontrivial_ff_peek(unsigned p); + void updateSerial(unsigned long cc); + void updateTimaIrq(unsigned long cc); + void updateIrqs(unsigned long cc); + bool isDoubleSpeed() const { return lcd_.isDoubleSpeed(); } + +public: templatevoid SyncState(NewState *ns); }; diff --git a/libgambatte/src/savestate.h b/libgambatte/src/savestate.h index b4489d12af..e56290685d 100644 --- a/libgambatte/src/savestate.h +++ b/libgambatte/src/savestate.h @@ -70,6 +70,7 @@ struct SaveState { unsigned long lastOamDmaUpdate; unsigned long minIntTime; unsigned long unhaltTime; + unsigned long halttime; unsigned short rombank; unsigned short dmaSource; unsigned short dmaDestination; @@ -84,6 +85,7 @@ struct SaveState { unsigned char /*bool*/ cgbSwitching; unsigned char /*bool*/ agbMode; unsigned char /*bool*/ gbIsCgb; + unsigned char /*bool*/ stopped; } mem; struct PPU { diff --git a/output/dll/libgambatte.dll b/output/dll/libgambatte.dll index e9b8c6dbbc6f4ba0eb3dc1880951964f17aa6df6..18ca122371aa4d7b04eb68c038c45c11b69fbab5 100644 GIT binary patch delta 61850 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 delta 61844 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