diff --git a/libgambatte/src/cinterface.cpp b/libgambatte/src/cinterface.cpp index c224fdf725..6549307c85 100644 --- a/libgambatte/src/cinterface.cpp +++ b/libgambatte/src/cinterface.cpp @@ -29,9 +29,9 @@ GBEXPORT void gambatte_destroy(GB *g) delete g; } -GBEXPORT int gambatte_load(GB *g, const char *romfiledata, unsigned romfilelength, long long now, unsigned flags) +GBEXPORT int gambatte_load(GB *g, const char *romfiledata, unsigned romfilelength, const char *biosfiledata, unsigned biosfilelength, long long now, unsigned flags) { - int ret = g->load(romfiledata, romfilelength, now, flags); + int ret = g->load(romfiledata, romfilelength, biosfiledata, biosfilelength, now, flags); return ret; } diff --git a/libgambatte/src/cpu.h b/libgambatte/src/cpu.h index 02fabe979e..1310257e86 100644 --- a/libgambatte/src/cpu.h +++ b/libgambatte/src/cpu.h @@ -1,132 +1,136 @@ -/*************************************************************************** - * 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. * - ***************************************************************************/ -#ifndef CPU_H -#define CPU_H - -#include "memory.h" +/*************************************************************************** + * 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. * + ***************************************************************************/ +#ifndef CPU_H +#define CPU_H + +#include "memory.h" #include "newstate.h" - -namespace gambatte { - -class CPU { - Memory memory; - - unsigned long cycleCounter_; - - unsigned short PC; - unsigned short SP; - - unsigned HF1, HF2, ZF, CF; - - unsigned char A, B, C, D, E, /*F,*/ H, L; - - bool skip; - - void process(unsigned long cycles); - - void (*tracecallback)(void *); - -public: - - CPU(); -// void halt(); - -// unsigned interrupt(unsigned address, unsigned cycleCounter); - - long runFor(unsigned long cycles); - void setStatePtrs(SaveState &state); - void loadState(const SaveState &state); - void setLayers(unsigned mask) { memory.setLayers(mask); } - - void loadSavedata(const char *data) { memory.loadSavedata(data); } - int saveSavedataLength() {return memory.saveSavedataLength(); } - void saveSavedata(char *dest) { memory.saveSavedata(dest); } - - bool getMemoryArea(int which, unsigned char **data, int *length) { return memory.getMemoryArea(which, data, length); } - - void setVideoBuffer(uint_least32_t *const videoBuf, const int pitch) { - memory.setVideoBuffer(videoBuf, pitch); - } - - void setInputGetter(unsigned (*getInput)()) { - memory.setInputGetter(getInput); - } - - void setReadCallback(void (*callback)(unsigned)) { - memory.setReadCallback(callback); - } - - void setWriteCallback(void (*callback)(unsigned)) { - memory.setWriteCallback(callback); - } - - void setExecCallback(void (*callback)(unsigned)) { - memory.setExecCallback(callback); - } - - void setCDCallback(CDCallback cdc) { - memory.setCDCallback(cdc); - } - - void setTraceCallback(void (*callback)(void *)) { - tracecallback = callback; - } - - void setScanlineCallback(void (*callback)(), int sl) { - memory.setScanlineCallback(callback, sl); - } - - void setRTCCallback(std::uint32_t (*callback)()) { - memory.setRTCCallback(callback); - } - - int load(const char *romfiledata, unsigned romfilelength, bool forceDmg, bool multicartCompat) { - return memory.loadROM(romfiledata, romfilelength, forceDmg, multicartCompat); - } - - bool loaded() const { return memory.loaded(); } - const char * romTitle() const { return memory.romTitle(); } - - void setSoundBuffer(uint_least32_t *const buf) { memory.setSoundBuffer(buf); } - unsigned fillSoundBuffer() { return memory.fillSoundBuffer(cycleCounter_); } - - bool isCgb() const { return memory.isCgb(); } - - void setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned rgb32) { - memory.setDmgPaletteColor(palNum, colorNum, rgb32); - } - - void setCgbPalette(unsigned *lut) { - memory.setCgbPalette(lut); - } - - //unsigned char ExternalRead(unsigned short addr) { return memory.read(addr, cycleCounter_); } - unsigned char ExternalRead(unsigned short addr) { return memory.peek(addr); } - void ExternalWrite(unsigned short addr, unsigned char val) { memory.write_nocb(addr, val, cycleCounter_); } - - int LinkStatus(int which) { return memory.LinkStatus(which); } - - void GetRegs(int *dest); - + +namespace gambatte { + +class CPU { + Memory memory; + + unsigned long cycleCounter_; + + unsigned short PC; + unsigned short SP; + + unsigned HF1, HF2, ZF, CF; + + unsigned char A, B, C, D, E, /*F,*/ H, L; + + bool skip; + + void process(unsigned long cycles); + + void (*tracecallback)(void *); + +public: + + CPU(); +// void halt(); + +// unsigned interrupt(unsigned address, unsigned cycleCounter); + + long runFor(unsigned long cycles); + void setStatePtrs(SaveState &state); + void loadState(const SaveState &state); + void setLayers(unsigned mask) { memory.setLayers(mask); } + + void loadSavedata(const char *data) { memory.loadSavedata(data); } + int saveSavedataLength() {return memory.saveSavedataLength(); } + void saveSavedata(char *dest) { memory.saveSavedata(dest); } + + bool getMemoryArea(int which, unsigned char **data, int *length) { return memory.getMemoryArea(which, data, length); } + + void setVideoBuffer(uint_least32_t *const videoBuf, const int pitch) { + memory.setVideoBuffer(videoBuf, pitch); + } + + void setInputGetter(unsigned (*getInput)()) { + memory.setInputGetter(getInput); + } + + void setReadCallback(void (*callback)(unsigned)) { + memory.setReadCallback(callback); + } + + void setWriteCallback(void (*callback)(unsigned)) { + memory.setWriteCallback(callback); + } + + void setExecCallback(void (*callback)(unsigned)) { + memory.setExecCallback(callback); + } + + void setCDCallback(CDCallback cdc) { + memory.setCDCallback(cdc); + } + + void setTraceCallback(void (*callback)(void *)) { + tracecallback = callback; + } + + void setScanlineCallback(void (*callback)(), int sl) { + memory.setScanlineCallback(callback, sl); + } + + void setRTCCallback(std::uint32_t (*callback)()) { + memory.setRTCCallback(callback); + } + + void reset_bios(int setting) { + memory.bios_reset(setting); + } + + int load(const char *romfiledata, unsigned romfilelength, const char *biosfiledata, unsigned biosfilelength, bool forceDmg, bool multicartCompat) { + return memory.loadROM(romfiledata, romfilelength, biosfiledata, biosfilelength, forceDmg, multicartCompat); + } + + bool loaded() const { return memory.loaded(); } + const char * romTitle() const { return memory.romTitle(); } + + void setSoundBuffer(uint_least32_t *const buf) { memory.setSoundBuffer(buf); } + unsigned fillSoundBuffer() { return memory.fillSoundBuffer(cycleCounter_); } + + bool isCgb() const { return memory.isCgb(); } + + void setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned rgb32) { + memory.setDmgPaletteColor(palNum, colorNum, rgb32); + } + + void setCgbPalette(unsigned *lut) { + memory.setCgbPalette(lut); + } + + //unsigned char ExternalRead(unsigned short addr) { return memory.read(addr, cycleCounter_); } + unsigned char ExternalRead(unsigned short addr) { return memory.peek(addr); } + void ExternalWrite(unsigned short addr, unsigned char val) { memory.write_nocb(addr, val, cycleCounter_); } + + int LinkStatus(int which) { return memory.LinkStatus(which); } + + void GetRegs(int *dest); + template<bool isReader>void SyncState(NewState *ns); -}; - -} - -#endif +}; + +} + +#endif diff --git a/libgambatte/src/gambatte.cpp b/libgambatte/src/gambatte.cpp index d56632d8f5..9a4fa62bf2 100644 --- a/libgambatte/src/gambatte.cpp +++ b/libgambatte/src/gambatte.cpp @@ -94,7 +94,15 @@ void GB::reset(const std::uint32_t now) { SaveState state; p_->cpu.setStatePtrs(state); - setInitState(state, p_->cpu.isCgb(), p_->gbaCgbMode, now); + if (use_bios) + { + p_->cpu.reset_bios(0); + } + else + { + + } + setInitState(state, p_->cpu.isCgb(), p_->gbaCgbMode, now, use_bios); p_->cpu.loadState(state); if (length > 0) { @@ -136,16 +144,17 @@ void GB::setRTCCallback(std::uint32_t (*callback)()) { p_->cpu.setRTCCallback(callback); } -int GB::load(const char *romfiledata, unsigned romfilelength, const std::uint32_t now, const unsigned flags) { +int GB::load(const char *romfiledata, unsigned romfilelength, const char *biosfiledata, unsigned biosfilelength, const std::uint32_t now, const unsigned flags) { //if (p_->cpu.loaded()) // p_->cpu.saveSavedata(); - const int failed = p_->cpu.load(romfiledata, romfilelength, flags & FORCE_DMG, flags & MULTICART_COMPAT); + const int failed = p_->cpu.load(romfiledata, romfilelength, biosfiledata, biosfilelength, flags & FORCE_DMG, flags & MULTICART_COMPAT); + use_bios = biosfilelength > 0 ? true : false; if (!failed) { SaveState state; p_->cpu.setStatePtrs(state); - setInitState(state, p_->cpu.isCgb(), p_->gbaCgbMode = flags & GBA_CGB, now); + setInitState(state, p_->cpu.isCgb(), p_->gbaCgbMode = flags & GBA_CGB, now, use_bios); p_->cpu.loadState(state); //p_->cpu.loadSavedata(); } diff --git a/libgambatte/src/initstate.cpp b/libgambatte/src/initstate.cpp index 6430061789..e2e1892a76 100644 --- a/libgambatte/src/initstate.cpp +++ b/libgambatte/src/initstate.cpp @@ -1146,7 +1146,7 @@ static void setInitialDmgIoamhram(unsigned char *const ioamhram) { } // anon namespace -void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbMode, const std::uint32_t now) { +void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbMode, const std::uint32_t now, bool boot_bios) { static const unsigned char cgbObjpDump[0x40] = { 0x00, 0x00, 0xF2, 0xAB, 0x61, 0xC2, 0xD9, 0xBA, @@ -1166,22 +1166,47 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM 0x83, 0x40, 0x0B, 0x77 }; - state.cpu.cycleCounter = cgb ? 0x102A0 : 0x102A0 + 0x8D2C; - state.cpu.PC = 0x100; - state.cpu.SP = 0xFFFE; - state.cpu.A = cgb * 0x10 | 0x01; - state.cpu.B = cgb & gbaCgbMode; - state.cpu.C = 0x13; - state.cpu.D = 0x00; - state.cpu.E = 0xD8; - state.cpu.F = 0xB0; - state.cpu.H = 0x01; - state.cpu.L = 0x4D; - state.cpu.skip = false; + if (boot_bios) + { + state.cpu.PC = 0x00; + state.cpu.SP = 0xFFFF; + state.cpu.A = 0; + state.cpu.B = 0; + state.cpu.C = 0x0; + state.cpu.D = 0x0; + state.cpu.E = 0x0; + state.cpu.F = 0x0; + state.cpu.H = 0x0; + state.cpu.L = 0x0; + state.cpu.skip = false; + state.cpu.cycleCounter = 0; + state.mem.ioamhram.ptr[0x140] = 0x00; + state.mem.ioamhram.ptr[0x104] = 0x00; + + } + else + { + state.cpu.PC = 0x100; + state.cpu.SP = 0xFFFE; + state.cpu.A = cgb * 0x10 | 0x01; + state.cpu.B = cgb & gbaCgbMode; + state.cpu.C = 0x13; + state.cpu.D = 0x00; + state.cpu.E = 0xD8; + state.cpu.F = 0xB0; + state.cpu.H = 0x01; + state.cpu.L = 0x4D; + state.cpu.skip = false; + setInitialVram(state.mem.vram.ptr, cgb); + state.cpu.cycleCounter = cgb ? 0x102A0 : 0x102A0 + 0x8D2C; + state.mem.ioamhram.ptr[0x140] = 0x91; + state.mem.ioamhram.ptr[0x104] = 0x1C; + + } std::memset(state.mem.sram.ptr, 0xFF, state.mem.sram.getSz()); - setInitialVram(state.mem.vram.ptr, cgb); + if (cgb) { setInitialCgbWram(state.mem.wram.ptr); @@ -1191,8 +1216,6 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM setInitialDmgIoamhram(state.mem.ioamhram.ptr); } - state.mem.ioamhram.ptr[0x104] = 0x1C; - state.mem.ioamhram.ptr[0x140] = 0x91; state.mem.ioamhram.ptr[0x144] = 0x00; state.mem.divLastUpdate = 0; @@ -1258,7 +1281,6 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM state.ppu.oldWy = state.mem.ioamhram.get()[0x14A]; state.ppu.pendingLcdstatIrq = false; - state.spu.cycleCounter = 0x1000 | (state.cpu.cycleCounter >> 1 & 0xFFF); // spu.cycleCounter >> 12 & 7 represents the frame sequencer position. state.spu.ch1.sweep.counter = SoundUnit::COUNTER_DISABLED; diff --git a/libgambatte/src/initstate.h b/libgambatte/src/initstate.h index 8d8ed5aaf6..ec56fae045 100644 --- a/libgambatte/src/initstate.h +++ b/libgambatte/src/initstate.h @@ -22,7 +22,7 @@ #include <cstdint> namespace gambatte { -void setInitState(struct SaveState &state, bool cgb, bool gbaCgbMode, std::uint32_t now); +void setInitState(struct SaveState &state, bool cgb, bool gbaCgbMode, std::uint32_t now, bool boot_bios); } #endif diff --git a/libgambatte/src/memory.cpp b/libgambatte/src/memory.cpp index 4bae856679..79d9820e16 100644 --- a/libgambatte/src/memory.cpp +++ b/libgambatte/src/memory.cpp @@ -1,361 +1,361 @@ -/*************************************************************************** - * 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. * - ***************************************************************************/ -#include "memory.h" -#include "video.h" -#include "sound.h" -#include "savestate.h" -#include <cstring> - -namespace gambatte { - -Memory::Memory(const Interrupter &interrupter_in) -: readCallback(0), - writeCallback(0), - execCallback(0), - cdCallback(0), - getInput(0), - divLastUpdate(0), - lastOamDmaUpdate(DISABLED_TIME), - display(ioamhram, 0, VideoInterruptRequester(&intreq)), - interrupter(interrupter_in), - dmaSource(0), - dmaDestination(0), - oamDmaPos(0xFE), - serialCnt(0), - blanklcd(false), - LINKCABLE(false), - linkClockTrigger(false) -{ - intreq.setEventTime<BLIT>(144*456ul); - intreq.setEventTime<END>(0); -} - -void Memory::setStatePtrs(SaveState &state) { - state.mem.ioamhram.set(ioamhram, sizeof ioamhram); - - cart.setStatePtrs(state); - display.setStatePtrs(state); - sound.setStatePtrs(state); -} - - -static inline int serialCntFrom(const unsigned long cyclesUntilDone, const bool cgbFast) { - return cgbFast ? (cyclesUntilDone + 0xF) >> 4 : (cyclesUntilDone + 0x1FF) >> 9; -} - -void Memory::loadState(const SaveState &state) { - sound.loadState(state); - display.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<SERIAL>(state.mem.nextSerialtime > state.cpu.cycleCounter ? state.mem.nextSerialtime : state.cpu.cycleCounter); - intreq.setEventTime<UNHALT>(state.mem.unhaltTime); - lastOamDmaUpdate = state.mem.lastOamDmaUpdate; - dmaSource = state.mem.dmaSource; - dmaDestination = state.mem.dmaDestination; - oamDmaPos = state.mem.oamDmaPos; - serialCnt = intreq.eventTime(SERIAL) != DISABLED_TIME - ? serialCntFrom(intreq.eventTime(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); - - if (lastOamDmaUpdate != DISABLED_TIME) { - oamDmaInitSetup(); - - const unsigned oamEventPos = oamDmaPos < 0xA0 ? 0xA0 : 0x100; - - intreq.setEventTime<OAM>(lastOamDmaUpdate + (oamEventPos - oamDmaPos) * 4); - } - - intreq.setEventTime<BLIT>((ioamhram[0x140] & 0x80) ? display.nextMode1IrqTime() : state.cpu.cycleCounter); - blanklcd = false; - - if (!isCgb()) - std::memset(cart.vramdata() + 0x2000, 0, 0x2000); -} - -void Memory::setEndtime(const unsigned long cycleCounter, const unsigned long inc) { - if (intreq.eventTime(BLIT) <= cycleCounter) - intreq.setEventTime<BLIT>(intreq.eventTime(BLIT) + (70224 << isDoubleSpeed())); - - intreq.setEventTime<END>(cycleCounter + (inc << isDoubleSpeed())); -} - -void Memory::updateSerial(const unsigned long cc) { - if (!LINKCABLE) { - if (intreq.eventTime(SERIAL) != DISABLED_TIME) { - if (intreq.eventTime(SERIAL) <= cc) { - ioamhram[0x101] = (((ioamhram[0x101] + 1) << serialCnt) - 1) & 0xFF; - ioamhram[0x102] &= 0x7F; - intreq.setEventTime<SERIAL>(DISABLED_TIME); - intreq.flagIrq(8); - } else { - const int targetCnt = serialCntFrom(intreq.eventTime(SERIAL) - cc, ioamhram[0x102] & isCgb() * 2); - ioamhram[0x101] = (((ioamhram[0x101] + 1) << (serialCnt - targetCnt)) - 1) & 0xFF; - serialCnt = targetCnt; - } - } - } - else { - if (intreq.eventTime(SERIAL) != DISABLED_TIME) { - if (intreq.eventTime(SERIAL) <= cc) { - linkClockTrigger = true; - intreq.setEventTime<SERIAL>(DISABLED_TIME); - } - } - } -} - -void Memory::updateTimaIrq(const unsigned long cc) { - while (intreq.eventTime(TIMA) <= cc) - tima.doIrqEvent(TimaInterruptRequester(intreq)); -} - -void Memory::updateIrqs(const unsigned long cc) { - updateSerial(cc); - updateTimaIrq(cc); - display.update(cc); -} - -unsigned long Memory::event(unsigned long cycleCounter) { - if (lastOamDmaUpdate != DISABLED_TIME) - updateOamDma(cycleCounter); - - switch (intreq.minEventId()) { - case UNHALT: - intreq.unhalt(); - intreq.setEventTime<UNHALT>(DISABLED_TIME); - break; - case END: - intreq.setEventTime<END>(DISABLED_TIME - 1); - - while (cycleCounter >= intreq.minEventTime() && intreq.eventTime(END) != DISABLED_TIME) - cycleCounter = event(cycleCounter); - - intreq.setEventTime<END>(DISABLED_TIME); - - break; - case BLIT: - { - const bool lcden = ioamhram[0x140] >> 7 & 1; - unsigned long blitTime = intreq.eventTime(BLIT); - - if (lcden | blanklcd) { - display.updateScreen(blanklcd, cycleCounter); - intreq.setEventTime<BLIT>(DISABLED_TIME); - intreq.setEventTime<END>(DISABLED_TIME); - - while (cycleCounter >= intreq.minEventTime()) - cycleCounter = event(cycleCounter); - } else - blitTime += 70224 << isDoubleSpeed(); - - blanklcd = lcden ^ 1; - intreq.setEventTime<BLIT>(blitTime); - } - break; - case SERIAL: - updateSerial(cycleCounter); - break; - case OAM: - intreq.setEventTime<OAM>(lastOamDmaUpdate == DISABLED_TIME ? - static_cast<unsigned long>(DISABLED_TIME) : intreq.eventTime(OAM) + 0xA0 * 4); - break; - case DMA: - { - const bool doubleSpeed = isDoubleSpeed(); - unsigned dmaSrc = dmaSource; - unsigned dmaDest = dmaDestination; - unsigned dmaLength = ((ioamhram[0x155] & 0x7F) + 0x1) * 0x10; - unsigned length = hdmaReqFlagged(intreq) ? 0x10 : dmaLength; - - ackDmaReq(&intreq); - - if ((static_cast<unsigned long>(dmaDest) + length) & 0x10000) { - length = 0x10000 - dmaDest; - ioamhram[0x155] |= 0x80; - } - - dmaLength -= length; - - if (!(ioamhram[0x140] & 0x80)) - dmaLength = 0; - - { - 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); - - cycleCounter += 2 << doubleSpeed; - - if (cycleCounter - 3 > lOamDmaUpdate) { - oamDmaPos = (oamDmaPos + 1) & 0xFF; - lOamDmaUpdate += 4; - - if (oamDmaPos < 0xA0) { - if (oamDmaPos == 0) - startOamDma(lOamDmaUpdate - 1); - - ioamhram[src & 0xFF] = data; - } else if (oamDmaPos == 0xA0) { - endOamDma(lOamDmaUpdate - 1); - lOamDmaUpdate = DISABLED_TIME; - } - } - - nontrivial_write(0x8000 | (dmaDest++ & 0x1FFF), data, cycleCounter); - } - - lastOamDmaUpdate = lOamDmaUpdate; - } - - cycleCounter += 4; - - 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); - - display.disableHdma(cycleCounter); - } - } - - break; - case TIMA: - tima.doIrqEvent(TimaInterruptRequester(intreq)); - break; - case VIDEO: - display.update(cycleCounter); - break; - case INTERRUPTS: - if (halted()) { - if (isCgb()) - cycleCounter += 4; - - intreq.unhalt(); - intreq.setEventTime<UNHALT>(DISABLED_TIME); - } - - if (ime()) { - unsigned address; - const unsigned pendingIrqs = intreq.pendingIrqs(); - const unsigned n = pendingIrqs & -pendingIrqs; - - if (n < 8) { - static const unsigned char lut[] = { 0x40, 0x48, 0x48, 0x50 }; - address = lut[n-1]; - } else - address = 0x50 + n; - - intreq.ackIrq(n); - cycleCounter = interrupter.interrupt(address, cycleCounter, *this); - } - - break; - } - - return cycleCounter; -} - -unsigned long Memory::stop(unsigned long cycleCounter) { - cycleCounter += 4 << isDoubleSpeed(); - - if (ioamhram[0x14D] & isCgb()) { - sound.generate_samples(cycleCounter, isDoubleSpeed()); - - display.speedChange(cycleCounter); - ioamhram[0x14D] ^= 0x81; - - intreq.setEventTime<BLIT>((ioamhram[0x140] & 0x80) ? display.nextMode1IrqTime() : cycleCounter + (70224 << isDoubleSpeed())); - - if (intreq.eventTime(END) > cycleCounter) { - intreq.setEventTime<END>(cycleCounter + (isDoubleSpeed() ? - (intreq.eventTime(END) - cycleCounter) << 1 : (intreq.eventTime(END) - cycleCounter) >> 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.setEventTime<UNHALT>(cycleCounter + 0x20000 + isDoubleSpeed() * 8); - } - - intreq.halt(); - - return cycleCounter; -} - -static void decCycles(unsigned long &counter, const unsigned long dec) { - if (counter != DISABLED_TIME) - counter -= dec; -} - -void Memory::decEventCycles(const MemEventId eventId, const 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); - - updateIrqs(cycleCounter); - - const unsigned long oldCC = cycleCounter; - - { - const unsigned long divinc = (cycleCounter - 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); - decEventCycles(SERIAL, dec); - decEventCycles(OAM, dec); - decEventCycles(BLIT, dec); - decEventCycles(END, dec); - decEventCycles(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; -} - -void Memory::updateInput() { +/*************************************************************************** + * 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. * + ***************************************************************************/ +#include "memory.h" +#include "video.h" +#include "sound.h" +#include "savestate.h" +#include <cstring> + +namespace gambatte { + +Memory::Memory(const Interrupter &interrupter_in) +: readCallback(0), + writeCallback(0), + execCallback(0), + cdCallback(0), + getInput(0), + divLastUpdate(0), + lastOamDmaUpdate(DISABLED_TIME), + display(ioamhram, 0, VideoInterruptRequester(&intreq)), + interrupter(interrupter_in), + dmaSource(0), + dmaDestination(0), + oamDmaPos(0xFE), + serialCnt(0), + blanklcd(false), + LINKCABLE(false), + linkClockTrigger(false) +{ + intreq.setEventTime<BLIT>(144*456ul); + intreq.setEventTime<END>(0); +} + +void Memory::setStatePtrs(SaveState &state) { + state.mem.ioamhram.set(ioamhram, sizeof ioamhram); + + cart.setStatePtrs(state); + display.setStatePtrs(state); + sound.setStatePtrs(state); +} + + +static inline int serialCntFrom(const unsigned long cyclesUntilDone, const bool cgbFast) { + return cgbFast ? (cyclesUntilDone + 0xF) >> 4 : (cyclesUntilDone + 0x1FF) >> 9; +} + +void Memory::loadState(const SaveState &state) { + sound.loadState(state); + display.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<SERIAL>(state.mem.nextSerialtime > state.cpu.cycleCounter ? state.mem.nextSerialtime : state.cpu.cycleCounter); + intreq.setEventTime<UNHALT>(state.mem.unhaltTime); + lastOamDmaUpdate = state.mem.lastOamDmaUpdate; + dmaSource = state.mem.dmaSource; + dmaDestination = state.mem.dmaDestination; + oamDmaPos = state.mem.oamDmaPos; + serialCnt = intreq.eventTime(SERIAL) != DISABLED_TIME + ? serialCntFrom(intreq.eventTime(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); + + if (lastOamDmaUpdate != DISABLED_TIME) { + oamDmaInitSetup(); + + const unsigned oamEventPos = oamDmaPos < 0xA0 ? 0xA0 : 0x100; + + intreq.setEventTime<OAM>(lastOamDmaUpdate + (oamEventPos - oamDmaPos) * 4); + } + + intreq.setEventTime<BLIT>((ioamhram[0x140] & 0x80) ? display.nextMode1IrqTime() : state.cpu.cycleCounter); + blanklcd = false; + + if (!isCgb()) + std::memset(cart.vramdata() + 0x2000, 0, 0x2000); +} + +void Memory::setEndtime(const unsigned long cycleCounter, const unsigned long inc) { + if (intreq.eventTime(BLIT) <= cycleCounter) + intreq.setEventTime<BLIT>(intreq.eventTime(BLIT) + (70224 << isDoubleSpeed())); + + intreq.setEventTime<END>(cycleCounter + (inc << isDoubleSpeed())); +} + +void Memory::updateSerial(const unsigned long cc) { + if (!LINKCABLE) { + if (intreq.eventTime(SERIAL) != DISABLED_TIME) { + if (intreq.eventTime(SERIAL) <= cc) { + ioamhram[0x101] = (((ioamhram[0x101] + 1) << serialCnt) - 1) & 0xFF; + ioamhram[0x102] &= 0x7F; + intreq.setEventTime<SERIAL>(DISABLED_TIME); + intreq.flagIrq(8); + } else { + const int targetCnt = serialCntFrom(intreq.eventTime(SERIAL) - cc, ioamhram[0x102] & isCgb() * 2); + ioamhram[0x101] = (((ioamhram[0x101] + 1) << (serialCnt - targetCnt)) - 1) & 0xFF; + serialCnt = targetCnt; + } + } + } + else { + if (intreq.eventTime(SERIAL) != DISABLED_TIME) { + if (intreq.eventTime(SERIAL) <= cc) { + linkClockTrigger = true; + intreq.setEventTime<SERIAL>(DISABLED_TIME); + } + } + } +} + +void Memory::updateTimaIrq(const unsigned long cc) { + while (intreq.eventTime(TIMA) <= cc) + tima.doIrqEvent(TimaInterruptRequester(intreq)); +} + +void Memory::updateIrqs(const unsigned long cc) { + updateSerial(cc); + updateTimaIrq(cc); + display.update(cc); +} + +unsigned long Memory::event(unsigned long cycleCounter) { + if (lastOamDmaUpdate != DISABLED_TIME) + updateOamDma(cycleCounter); + + switch (intreq.minEventId()) { + case UNHALT: + intreq.unhalt(); + intreq.setEventTime<UNHALT>(DISABLED_TIME); + break; + case END: + intreq.setEventTime<END>(DISABLED_TIME - 1); + + while (cycleCounter >= intreq.minEventTime() && intreq.eventTime(END) != DISABLED_TIME) + cycleCounter = event(cycleCounter); + + intreq.setEventTime<END>(DISABLED_TIME); + + break; + case BLIT: + { + const bool lcden = ioamhram[0x140] >> 7 & 1; + unsigned long blitTime = intreq.eventTime(BLIT); + + if (lcden | blanklcd) { + display.updateScreen(blanklcd, cycleCounter); + intreq.setEventTime<BLIT>(DISABLED_TIME); + intreq.setEventTime<END>(DISABLED_TIME); + + while (cycleCounter >= intreq.minEventTime()) + cycleCounter = event(cycleCounter); + } else + blitTime += 70224 << isDoubleSpeed(); + + blanklcd = lcden ^ 1; + intreq.setEventTime<BLIT>(blitTime); + } + break; + case SERIAL: + updateSerial(cycleCounter); + break; + case OAM: + intreq.setEventTime<OAM>(lastOamDmaUpdate == DISABLED_TIME ? + static_cast<unsigned long>(DISABLED_TIME) : intreq.eventTime(OAM) + 0xA0 * 4); + break; + case DMA: + { + const bool doubleSpeed = isDoubleSpeed(); + unsigned dmaSrc = dmaSource; + unsigned dmaDest = dmaDestination; + unsigned dmaLength = ((ioamhram[0x155] & 0x7F) + 0x1) * 0x10; + unsigned length = hdmaReqFlagged(intreq) ? 0x10 : dmaLength; + + ackDmaReq(&intreq); + + if ((static_cast<unsigned long>(dmaDest) + length) & 0x10000) { + length = 0x10000 - dmaDest; + ioamhram[0x155] |= 0x80; + } + + dmaLength -= length; + + if (!(ioamhram[0x140] & 0x80)) + dmaLength = 0; + + { + 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); + + cycleCounter += 2 << doubleSpeed; + + if (cycleCounter - 3 > lOamDmaUpdate) { + oamDmaPos = (oamDmaPos + 1) & 0xFF; + lOamDmaUpdate += 4; + + if (oamDmaPos < 0xA0) { + if (oamDmaPos == 0) + startOamDma(lOamDmaUpdate - 1); + + ioamhram[src & 0xFF] = data; + } else if (oamDmaPos == 0xA0) { + endOamDma(lOamDmaUpdate - 1); + lOamDmaUpdate = DISABLED_TIME; + } + } + + nontrivial_write(0x8000 | (dmaDest++ & 0x1FFF), data, cycleCounter); + } + + lastOamDmaUpdate = lOamDmaUpdate; + } + + cycleCounter += 4; + + 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); + + display.disableHdma(cycleCounter); + } + } + + break; + case TIMA: + tima.doIrqEvent(TimaInterruptRequester(intreq)); + break; + case VIDEO: + display.update(cycleCounter); + break; + case INTERRUPTS: + if (halted()) { + if (isCgb()) + cycleCounter += 4; + + intreq.unhalt(); + intreq.setEventTime<UNHALT>(DISABLED_TIME); + } + + if (ime()) { + unsigned address; + const unsigned pendingIrqs = intreq.pendingIrqs(); + const unsigned n = pendingIrqs & -pendingIrqs; + + if (n < 8) { + static const unsigned char lut[] = { 0x40, 0x48, 0x48, 0x50 }; + address = lut[n-1]; + } else + address = 0x50 + n; + + intreq.ackIrq(n); + cycleCounter = interrupter.interrupt(address, cycleCounter, *this); + } + + break; + } + + return cycleCounter; +} + +unsigned long Memory::stop(unsigned long cycleCounter) { + cycleCounter += 4 << isDoubleSpeed(); + + if (ioamhram[0x14D] & isCgb()) { + sound.generate_samples(cycleCounter, isDoubleSpeed()); + + display.speedChange(cycleCounter); + ioamhram[0x14D] ^= 0x81; + + intreq.setEventTime<BLIT>((ioamhram[0x140] & 0x80) ? display.nextMode1IrqTime() : cycleCounter + (70224 << isDoubleSpeed())); + + if (intreq.eventTime(END) > cycleCounter) { + intreq.setEventTime<END>(cycleCounter + (isDoubleSpeed() ? + (intreq.eventTime(END) - cycleCounter) << 1 : (intreq.eventTime(END) - cycleCounter) >> 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.setEventTime<UNHALT>(cycleCounter + 0x20000 + isDoubleSpeed() * 8); + } + + intreq.halt(); + + return cycleCounter; +} + +static void decCycles(unsigned long &counter, const unsigned long dec) { + if (counter != DISABLED_TIME) + counter -= dec; +} + +void Memory::decEventCycles(const MemEventId eventId, const 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); + + updateIrqs(cycleCounter); + + const unsigned long oldCC = cycleCounter; + + { + const unsigned long divinc = (cycleCounter - 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); + decEventCycles(SERIAL, dec); + decEventCycles(OAM, dec); + decEventCycles(BLIT, dec); + decEventCycles(END, dec); + decEventCycles(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; +} + +void Memory::updateInput() { unsigned state = 0xF; if ((ioamhram[0x100] & 0x30) != 0x30 && getInput) { @@ -371,720 +371,725 @@ void Memory::updateInput() { if (state != 0xF && (ioamhram[0x100] & 0xF) == 0xF) intreq.flagIrq(0x10); - ioamhram[0x100] = (ioamhram[0x100] & -0x10u) | state; -} - -void Memory::updateOamDma(const unsigned long cycleCounter) { - const unsigned char *const oamDmaSrc = oamDmaSrcPtr(); - unsigned cycles = (cycleCounter - lastOamDmaUpdate) >> 2; - - while (cycles--) { - oamDmaPos = (oamDmaPos + 1) & 0xFF; - lastOamDmaUpdate += 4; - - 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; - 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); - } else - cart.setOamDmaSrc(OAM_DMA_SRC_INVALID); -} - -static const unsigned char * 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); - case OAM_DMA_SRC_INVALID: - case OAM_DMA_SRC_OFF: break; - } - - return ioamhram[0x146] == 0xFF && !isCgb() ? oamDmaSrcZero() : cart.rdisabledRam(); -} - -void Memory::startOamDma(const unsigned long cycleCounter) { - display.oamChange(cart.rdisabledRam(), cycleCounter); -} - -void Memory::endOamDma(const unsigned long cycleCounter) { - oamDmaPos = 0xFE; - cart.setOamDmaSrc(OAM_DMA_SRC_OFF); - display.oamChange(ioamhram, cycleCounter); -} - -unsigned Memory::nontrivial_ff_read(const unsigned P, const unsigned long cycleCounter) { - if (lastOamDmaUpdate != DISABLED_TIME) - updateOamDma(cycleCounter); - - switch (P & 0x7F) { - case 0x00: - updateInput(); - break; - case 0x01: - case 0x02: - updateSerial(cycleCounter); - break; - case 0x04: - { - const unsigned long divcycles = (cycleCounter - divLastUpdate) >> 8; - ioamhram[0x104] = (ioamhram[0x104] + divcycles) & 0xFF; - divLastUpdate += divcycles << 8; - } - - break; - case 0x05: - ioamhram[0x105] = tima.tima(cycleCounter); - break; - case 0x0F: - updateIrqs(cycleCounter); - ioamhram[0x10F] = intreq.ifreg(); - break; - case 0x26: - if (ioamhram[0x126] & 0x80) { - sound.generate_samples(cycleCounter, isDoubleSpeed()); - ioamhram[0x126] = 0xF0 | sound.getStatus(); - } else - ioamhram[0x126] = 0x70; - - break; - case 0x30: - case 0x31: - case 0x32: - case 0x33: - case 0x34: - case 0x35: - case 0x36: - case 0x37: - case 0x38: - case 0x39: - case 0x3A: - case 0x3B: - case 0x3C: - case 0x3D: - case 0x3E: - case 0x3F: - sound.generate_samples(cycleCounter, isDoubleSpeed()); - return sound.waveRamRead(P & 0xF); - case 0x41: - return ioamhram[0x141] | display.getStat(ioamhram[0x145], cycleCounter); - case 0x44: - return display.getLyReg(cycleCounter/*+4*/); - case 0x69: - return display.cgbBgColorRead(ioamhram[0x168] & 0x3F, cycleCounter); - case 0x6B: - return display.cgbSpColorRead(ioamhram[0x16A] & 0x3F, cycleCounter); - default: break; - } - - return ioamhram[P - 0xFE00]; -} - -static bool isInOamDmaConflictArea(const OamDmaSrc oamDmaSrc, const unsigned addr, const bool cgb) { - struct Area { unsigned short areaUpper, exceptAreaLower, exceptAreaWidth, pad; }; - - static const Area cgbAreas[] = { - { 0xC000, 0x8000, 0x2000, 0 }, - { 0xC000, 0x8000, 0x2000, 0 }, - { 0xA000, 0x0000, 0x8000, 0 }, - { 0xFE00, 0x0000, 0xC000, 0 }, - { 0xC000, 0x8000, 0x2000, 0 }, - { 0x0000, 0x0000, 0x0000, 0 } - }; - - static const Area dmgAreas[] = { - { 0xFE00, 0x8000, 0x2000, 0 }, - { 0xFE00, 0x8000, 0x2000, 0 }, - { 0xA000, 0x0000, 0x8000, 0 }, - { 0xFE00, 0x8000, 0x2000, 0 }, - { 0xFE00, 0x8000, 0x2000, 0 }, - { 0x0000, 0x0000, 0x0000, 0 } - }; - - const Area *const a = cgb ? cgbAreas : dmgAreas; - - return addr < a[oamDmaSrc].areaUpper && addr - 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); - - 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 < 0xA000) { - if (!display.vramAccessible(cycleCounter)) - return 0xFF; - - return cart.vrambankptr()[P]; - } - - if (cart.rsrambankptr()) - return cart.rsrambankptr()[P]; - - return cart.rtcRead(); - } - - if (P < 0xFE00) - return cart.wramdata(P >> 12 & 1)[P & 0xFFF]; - - if (P >= 0xFF00) - return nontrivial_ff_read(P, cycleCounter); - - if (!display.oamReadable(cycleCounter) || oamDmaPos < 0xA0) - return 0xFF; - } - - return ioamhram[P - 0xFE00]; -} - -unsigned Memory::nontrivial_peek(const unsigned P) { - if (P < 0xC000) { - if (P < 0x8000) - return cart.romdata(P >> 14)[P]; - - if (P < 0xA000) { - return cart.vrambankptr()[P]; - } - - if (cart.rsrambankptr()) - return cart.rsrambankptr()[P]; - - 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]; -} - -unsigned Memory::nontrivial_ff_peek(const unsigned P) { - // some regs may be somewhat wrong with this - return ioamhram[P - 0xFE00]; -} - -void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned long cycleCounter) { - if (lastOamDmaUpdate != DISABLED_TIME) - updateOamDma(cycleCounter); - - switch (P & 0xFF) { + ioamhram[0x100] = (ioamhram[0x100] & -0x10u) | state; +} + +void Memory::updateOamDma(const unsigned long cycleCounter) { + const unsigned char *const oamDmaSrc = oamDmaSrcPtr(); + unsigned cycles = (cycleCounter - lastOamDmaUpdate) >> 2; + + while (cycles--) { + oamDmaPos = (oamDmaPos + 1) & 0xFF; + lastOamDmaUpdate += 4; + + 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; + 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); + } else + cart.setOamDmaSrc(OAM_DMA_SRC_INVALID); +} + +static const unsigned char * 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); + case OAM_DMA_SRC_INVALID: + case OAM_DMA_SRC_OFF: break; + } + + return ioamhram[0x146] == 0xFF && !isCgb() ? oamDmaSrcZero() : cart.rdisabledRam(); +} + +void Memory::startOamDma(const unsigned long cycleCounter) { + display.oamChange(cart.rdisabledRam(), cycleCounter); +} + +void Memory::endOamDma(const unsigned long cycleCounter) { + oamDmaPos = 0xFE; + cart.setOamDmaSrc(OAM_DMA_SRC_OFF); + display.oamChange(ioamhram, cycleCounter); +} + +unsigned Memory::nontrivial_ff_read(const unsigned P, const unsigned long cycleCounter) { + if (lastOamDmaUpdate != DISABLED_TIME) + updateOamDma(cycleCounter); + + switch (P & 0x7F) { + case 0x00: + updateInput(); + break; + case 0x01: + case 0x02: + updateSerial(cycleCounter); + break; + case 0x04: + { + const unsigned long divcycles = (cycleCounter - divLastUpdate) >> 8; + ioamhram[0x104] = (ioamhram[0x104] + divcycles) & 0xFF; + divLastUpdate += divcycles << 8; + } + + break; + case 0x05: + ioamhram[0x105] = tima.tima(cycleCounter); + break; + case 0x0F: + updateIrqs(cycleCounter); + ioamhram[0x10F] = intreq.ifreg(); + break; + case 0x26: + if (ioamhram[0x126] & 0x80) { + sound.generate_samples(cycleCounter, isDoubleSpeed()); + ioamhram[0x126] = 0xF0 | sound.getStatus(); + } else + ioamhram[0x126] = 0x70; + + break; + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3A: + case 0x3B: + case 0x3C: + case 0x3D: + case 0x3E: + case 0x3F: + sound.generate_samples(cycleCounter, isDoubleSpeed()); + return sound.waveRamRead(P & 0xF); + case 0x41: + return ioamhram[0x141] | display.getStat(ioamhram[0x145], cycleCounter); + case 0x44: + return display.getLyReg(cycleCounter/*+4*/); + case 0x69: + return display.cgbBgColorRead(ioamhram[0x168] & 0x3F, cycleCounter); + case 0x6B: + return display.cgbSpColorRead(ioamhram[0x16A] & 0x3F, cycleCounter); + default: break; + } + + return ioamhram[P - 0xFE00]; +} + +static bool isInOamDmaConflictArea(const OamDmaSrc oamDmaSrc, const unsigned addr, const bool cgb) { + struct Area { unsigned short areaUpper, exceptAreaLower, exceptAreaWidth, pad; }; + + static const Area cgbAreas[] = { + { 0xC000, 0x8000, 0x2000, 0 }, + { 0xC000, 0x8000, 0x2000, 0 }, + { 0xA000, 0x0000, 0x8000, 0 }, + { 0xFE00, 0x0000, 0xC000, 0 }, + { 0xC000, 0x8000, 0x2000, 0 }, + { 0x0000, 0x0000, 0x0000, 0 } + }; + + static const Area dmgAreas[] = { + { 0xFE00, 0x8000, 0x2000, 0 }, + { 0xFE00, 0x8000, 0x2000, 0 }, + { 0xA000, 0x0000, 0x8000, 0 }, + { 0xFE00, 0x8000, 0x2000, 0 }, + { 0xFE00, 0x8000, 0x2000, 0 }, + { 0x0000, 0x0000, 0x0000, 0 } + }; + + const Area *const a = cgb ? cgbAreas : dmgAreas; + + return addr < a[oamDmaSrc].areaUpper && addr - 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); + + 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 < 0xA000) { + if (!display.vramAccessible(cycleCounter)) + return 0xFF; + + return cart.vrambankptr()[P]; + } + + if (cart.rsrambankptr()) + return cart.rsrambankptr()[P]; + + return cart.rtcRead(); + } + + if (P < 0xFE00) + return cart.wramdata(P >> 12 & 1)[P & 0xFFF]; + + if (P >= 0xFF00) + return nontrivial_ff_read(P, cycleCounter); + + if (!display.oamReadable(cycleCounter) || oamDmaPos < 0xA0) + return 0xFF; + } + + return ioamhram[P - 0xFE00]; +} + +unsigned Memory::nontrivial_peek(const unsigned P) { + if (P < 0xC000) { + if (P < 0x8000) + return cart.romdata(P >> 14)[P]; + + if (P < 0xA000) { + return cart.vrambankptr()[P]; + } + + if (cart.rsrambankptr()) + return cart.rsrambankptr()[P]; + + 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]; +} + +unsigned Memory::nontrivial_ff_peek(const unsigned P) { + // some regs may be somewhat wrong with this + return ioamhram[P - 0xFE00]; +} + +void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned long cycleCounter) { + if (lastOamDmaUpdate != DISABLED_TIME) + updateOamDma(cycleCounter); + + switch (P & 0xFF) { case 0x00: if ((data ^ ioamhram[0x100]) & 0x30) { ioamhram[0x100] = (ioamhram[0x100] & ~0x30u) | (data & 0x30); updateInput(); } - return; - case 0x01: - updateSerial(cycleCounter); - break; - case 0x02: - updateSerial(cycleCounter); - - serialCnt = 8; - intreq.setEventTime<SERIAL>((data & 0x81) == 0x81 - ? (data & isCgb() * 2 ? (cycleCounter & ~0x7ul) + 0x10 * 8 : (cycleCounter & ~0xFFul) + 0x200 * 8) - : static_cast<unsigned long>(DISABLED_TIME)); - - data |= 0x7E - isCgb() * 2; - break; - case 0x04: - ioamhram[0x104] = 0; - divLastUpdate = cycleCounter; - return; - case 0x05: - tima.setTima(data, cycleCounter, TimaInterruptRequester(intreq)); - break; - case 0x06: - tima.setTma(data, cycleCounter, TimaInterruptRequester(intreq)); - break; - case 0x07: - data |= 0xF8; - tima.setTac(data, cycleCounter, TimaInterruptRequester(intreq)); - break; - case 0x0F: - updateIrqs(cycleCounter); - intreq.setIfreg(0xE0 | data); - return; - case 0x10: - if (!sound.isEnabled()) return; - sound.generate_samples(cycleCounter, isDoubleSpeed()); - sound.set_nr10(data); - data |= 0x80; - break; - case 0x11: - if (!sound.isEnabled()) { - if (isCgb()) - return; - - data &= 0x3F; - } - - sound.generate_samples(cycleCounter, isDoubleSpeed()); - sound.set_nr11(data); - data |= 0x3F; - break; - case 0x12: - if (!sound.isEnabled()) return; - sound.generate_samples(cycleCounter, isDoubleSpeed()); - sound.set_nr12(data); - break; - case 0x13: - if (!sound.isEnabled()) return; - sound.generate_samples(cycleCounter, isDoubleSpeed()); - sound.set_nr13(data); - return; - case 0x14: - if (!sound.isEnabled()) return; - sound.generate_samples(cycleCounter, isDoubleSpeed()); - sound.set_nr14(data); - data |= 0xBF; - break; - case 0x16: - if (!sound.isEnabled()) { - if (isCgb()) - return; - - data &= 0x3F; - } - - sound.generate_samples(cycleCounter, isDoubleSpeed()); - sound.set_nr21(data); - data |= 0x3F; - break; - case 0x17: - if (!sound.isEnabled()) return; - sound.generate_samples(cycleCounter, isDoubleSpeed()); - sound.set_nr22(data); - break; - case 0x18: - if (!sound.isEnabled()) return; - sound.generate_samples(cycleCounter, isDoubleSpeed()); - sound.set_nr23(data); - return; - case 0x19: - if (!sound.isEnabled()) return; - sound.generate_samples(cycleCounter, isDoubleSpeed()); - sound.set_nr24(data); - data |= 0xBF; - break; - case 0x1A: - if (!sound.isEnabled()) return; - sound.generate_samples(cycleCounter, isDoubleSpeed()); - sound.set_nr30(data); - data |= 0x7F; - break; - case 0x1B: - if (!sound.isEnabled() && isCgb()) return; - sound.generate_samples(cycleCounter, isDoubleSpeed()); - sound.set_nr31(data); - return; - case 0x1C: - if (!sound.isEnabled()) return; - sound.generate_samples(cycleCounter, isDoubleSpeed()); - sound.set_nr32(data); - data |= 0x9F; - break; - case 0x1D: - if (!sound.isEnabled()) return; - sound.generate_samples(cycleCounter, isDoubleSpeed()); - sound.set_nr33(data); - return; - case 0x1E: - if (!sound.isEnabled()) return; - sound.generate_samples(cycleCounter, isDoubleSpeed()); - sound.set_nr34(data); - data |= 0xBF; - break; - case 0x20: - if (!sound.isEnabled() && isCgb()) return; - sound.generate_samples(cycleCounter, isDoubleSpeed()); - sound.set_nr41(data); - return; - case 0x21: - if (!sound.isEnabled()) return; - sound.generate_samples(cycleCounter, isDoubleSpeed()); - sound.set_nr42(data); - break; - case 0x22: - if (!sound.isEnabled()) return; - sound.generate_samples(cycleCounter, isDoubleSpeed()); - sound.set_nr43(data); - break; - case 0x23: - if (!sound.isEnabled()) return; - sound.generate_samples(cycleCounter, isDoubleSpeed()); - sound.set_nr44(data); - data |= 0xBF; - break; - case 0x24: - if (!sound.isEnabled()) return; - sound.generate_samples(cycleCounter, isDoubleSpeed()); - sound.set_so_volume(data); - break; - case 0x25: - if (!sound.isEnabled()) return; - sound.generate_samples(cycleCounter, isDoubleSpeed()); - sound.map_so(data); - break; - case 0x26: - if ((ioamhram[0x126] ^ data) & 0x80) { - sound.generate_samples(cycleCounter, isDoubleSpeed()); - - if (!(data & 0x80)) { - for (unsigned i = 0xFF10; i < 0xFF26; ++i) - ff_write(i, 0, cycleCounter); - - sound.setEnabled(false); - } else { - sound.reset(); - sound.setEnabled(true); - } - } - - data = (data & 0x80) | (ioamhram[0x126] & 0x7F); - break; - case 0x30: - case 0x31: - case 0x32: - case 0x33: - case 0x34: - case 0x35: - case 0x36: - case 0x37: - case 0x38: - case 0x39: - case 0x3A: - case 0x3B: - case 0x3C: - case 0x3D: - case 0x3E: - case 0x3F: - sound.generate_samples(cycleCounter, isDoubleSpeed()); - sound.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(); - - display.lcdcChange(data, cycleCounter); - ioamhram[0x144] = 0; - ioamhram[0x141] &= 0xF8; - - if (data & 0x80) { - intreq.setEventTime<BLIT>(display.nextMode1IrqTime() + (blanklcd ? 0 : 70224 << isDoubleSpeed())); - } else { - ioamhram[0x141] |= lyc; - intreq.setEventTime<BLIT>(cycleCounter + (456 * 4 << isDoubleSpeed())); - - if (hdmaEnabled) - flagHdmaReq(&intreq); - } - } else - display.lcdcChange(data, cycleCounter); - - ioamhram[0x140] = data; - } - - return; - case 0x41: - display.lcdstatChange(data, cycleCounter); - data = (ioamhram[0x141] & 0x87) | (data & 0x78); - break; - case 0x42: - display.scyChange(data, cycleCounter); - break; - case 0x43: - display.scxChange(data, cycleCounter); - break; - case 0x45: - display.lycRegChange(data, cycleCounter); - break; - case 0x46: - if (lastOamDmaUpdate != DISABLED_TIME) - endOamDma(cycleCounter); - - lastOamDmaUpdate = cycleCounter; - intreq.setEventTime<OAM>(cycleCounter + 8); - ioamhram[0x146] = data; - oamDmaInitSetup(); - return; - case 0x47: - if (!isCgb()) - display.dmgBgPaletteChange(data, cycleCounter); - - break; - case 0x48: - if (!isCgb()) - display.dmgSpPalette1Change(data, cycleCounter); - - break; - case 0x49: - if (!isCgb()) - display.dmgSpPalette2Change(data, cycleCounter); - - break; - case 0x4A: - display.wyChange(data, cycleCounter); - break; - case 0x4B: - display.wxChange(data, cycleCounter); - break; - - case 0x4D: + return; + case 0x01: + updateSerial(cycleCounter); + break; + case 0x02: + updateSerial(cycleCounter); + + serialCnt = 8; + intreq.setEventTime<SERIAL>((data & 0x81) == 0x81 + ? (data & isCgb() * 2 ? (cycleCounter & ~0x7ul) + 0x10 * 8 : (cycleCounter & ~0xFFul) + 0x200 * 8) + : static_cast<unsigned long>(DISABLED_TIME)); + + data |= 0x7E - isCgb() * 2; + break; + case 0x04: + ioamhram[0x104] = 0; + divLastUpdate = cycleCounter; + return; + case 0x05: + tima.setTima(data, cycleCounter, TimaInterruptRequester(intreq)); + break; + case 0x06: + tima.setTma(data, cycleCounter, TimaInterruptRequester(intreq)); + break; + case 0x07: + data |= 0xF8; + tima.setTac(data, cycleCounter, TimaInterruptRequester(intreq)); + break; + case 0x0F: + updateIrqs(cycleCounter); + intreq.setIfreg(0xE0 | data); + return; + case 0x10: + if (!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr10(data); + data |= 0x80; + break; + case 0x11: + if (!sound.isEnabled()) { + if (isCgb()) + return; + + data &= 0x3F; + } + + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr11(data); + data |= 0x3F; + break; + case 0x12: + if (!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr12(data); + break; + case 0x13: + if (!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr13(data); + return; + case 0x14: + if (!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr14(data); + data |= 0xBF; + break; + case 0x16: + if (!sound.isEnabled()) { + if (isCgb()) + return; + + data &= 0x3F; + } + + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr21(data); + data |= 0x3F; + break; + case 0x17: + if (!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr22(data); + break; + case 0x18: + if (!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr23(data); + return; + case 0x19: + if (!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr24(data); + data |= 0xBF; + break; + case 0x1A: + if (!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr30(data); + data |= 0x7F; + break; + case 0x1B: + if (!sound.isEnabled() && isCgb()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr31(data); + return; + case 0x1C: + if (!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr32(data); + data |= 0x9F; + break; + case 0x1D: + if (!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr33(data); + return; + case 0x1E: + if (!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr34(data); + data |= 0xBF; + break; + case 0x20: + if (!sound.isEnabled() && isCgb()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr41(data); + return; + case 0x21: + if (!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr42(data); + break; + case 0x22: + if (!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr43(data); + break; + case 0x23: + if (!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_nr44(data); + data |= 0xBF; + break; + case 0x24: + if (!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.set_so_volume(data); + break; + case 0x25: + if (!sound.isEnabled()) return; + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.map_so(data); + break; + case 0x26: + if ((ioamhram[0x126] ^ data) & 0x80) { + sound.generate_samples(cycleCounter, isDoubleSpeed()); + + if (!(data & 0x80)) { + for (unsigned i = 0xFF10; i < 0xFF26; ++i) + ff_write(i, 0, cycleCounter); + + sound.setEnabled(false); + } else { + sound.reset(); + sound.setEnabled(true); + } + } + + data = (data & 0x80) | (ioamhram[0x126] & 0x7F); + break; + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3A: + case 0x3B: + case 0x3C: + case 0x3D: + case 0x3E: + case 0x3F: + sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.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(); + + display.lcdcChange(data, cycleCounter); + ioamhram[0x144] = 0; + ioamhram[0x141] &= 0xF8; + + if (data & 0x80) { + intreq.setEventTime<BLIT>(display.nextMode1IrqTime() + (blanklcd ? 0 : 70224 << isDoubleSpeed())); + } else { + ioamhram[0x141] |= lyc; + intreq.setEventTime<BLIT>(cycleCounter + (456 * 4 << isDoubleSpeed())); + + if (hdmaEnabled) + flagHdmaReq(&intreq); + } + } else + display.lcdcChange(data, cycleCounter); + + ioamhram[0x140] = data; + } + + return; + case 0x41: + display.lcdstatChange(data, cycleCounter); + data = (ioamhram[0x141] & 0x87) | (data & 0x78); + break; + case 0x42: + display.scyChange(data, cycleCounter); + break; + case 0x43: + display.scxChange(data, cycleCounter); + break; + case 0x45: + display.lycRegChange(data, cycleCounter); + break; + case 0x46: + if (lastOamDmaUpdate != DISABLED_TIME) + endOamDma(cycleCounter); + + lastOamDmaUpdate = cycleCounter; + intreq.setEventTime<OAM>(cycleCounter + 8); + ioamhram[0x146] = data; + oamDmaInitSetup(); + return; + case 0x47: + if (!isCgb()) + display.dmgBgPaletteChange(data, cycleCounter); + + break; + case 0x48: + if (!isCgb()) + display.dmgSpPalette1Change(data, cycleCounter); + + break; + case 0x49: + if (!isCgb()) + display.dmgSpPalette2Change(data, cycleCounter); + + break; + case 0x4A: + display.wyChange(data, cycleCounter); + break; + case 0x4B: + display.wxChange(data, cycleCounter); + break; + + case 0x4D: if (isCgb()) - ioamhram[0x14D] = (ioamhram[0x14D] & ~1u) | (data & 1); return; - case 0x4F: - if (isCgb()) { - cart.setVrambank(data & 1); - ioamhram[0x14F] = 0xFE | data; - } - - return; - case 0x51: - dmaSource = data << 8 | (dmaSource & 0xFF); - return; - case 0x52: - dmaSource = (dmaSource & 0xFF00) | (data & 0xF0); - return; - case 0x53: - dmaDestination = data << 8 | (dmaDestination & 0xFF); - return; - case 0x54: - dmaDestination = (dmaDestination & 0xFF00) | (data & 0xF0); - return; - case 0x55: - if (isCgb()) { - ioamhram[0x155] = data & 0x7F; - - if (display.hdmaIsEnabled()) { - if (!(data & 0x80)) { - ioamhram[0x155] |= 0x80; - display.disableHdma(cycleCounter); - } - } else { - if (data & 0x80) { - if (ioamhram[0x140] & 0x80) { - display.enableHdma(cycleCounter); - } else - flagHdmaReq(&intreq); - } else - flagGdmaReq(&intreq); - } - } - - return; - case 0x56: - if (isCgb()) - ioamhram[0x156] = data | 0x3E; - - return; - case 0x68: - if (isCgb()) - 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); - } - - return; - case 0x6A: - if (isCgb()) - 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); - } - - return; - case 0x6C: - if (isCgb()) - ioamhram[0x16C] = data | 0xFE; - - return; - case 0x70: - if (isCgb()) { - cart.setWrambank((data & 0x07) ? (data & 0x07) : 1); - ioamhram[0x170] = data | 0xF8; - } - - return; - case 0x72: - case 0x73: - case 0x74: - if (isCgb()) - break; - - return; - case 0x75: - if (isCgb()) - ioamhram[0x175] = data | 0x8F; - - return; - case 0xFF: - intreq.setIereg(data); - break; - default: - return; - } - - ioamhram[P - 0xFE00] = data; -} - -void Memory::nontrivial_write(const unsigned P, const unsigned data, const unsigned long cycleCounter) { - if (lastOamDmaUpdate != DISABLED_TIME) { - updateOamDma(cycleCounter); - - 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; - } - } else if (P < 0xC000) { - if (cart.wsrambankptr()) - cart.wsrambankptr()[P] = data; - else - cart.rtcWrite(data); - } else - cart.wramdata(P >> 12 & 1)[P & 0xFFF] = data; - } else if (P - 0xFF80u >= 0x7Fu) { - if (P < 0xFF00) { - if (display.oamWritable(cycleCounter) && oamDmaPos >= 0xA0 && (P < 0xFEA0 || isCgb())) { - display.oamChange(cycleCounter); - ioamhram[P - 0xFE00] = data; - } - } else - nontrivial_ff_write(P, data, cycleCounter); - } else - ioamhram[P - 0xFE00] = data; -} - -int Memory::loadROM(const char *romfiledata, unsigned romfilelength, const bool forceDmg, const bool multicartCompat) { - if (const int fail = cart.loadROM(romfiledata, romfilelength, forceDmg, multicartCompat)) - return fail; - - sound.init(cart.isCgb()); - display.reset(ioamhram, cart.vramdata(), cart.isCgb()); - - return 0; -} - -unsigned Memory::fillSoundBuffer(const unsigned long cycleCounter) { - sound.generate_samples(cycleCounter, isDoubleSpeed()); - return sound.fillBuffer(); -} - -void Memory::setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned long rgb32) { - display.setDmgPaletteColor(palNum, colorNum, rgb32); -} - -void Memory::setCgbPalette(unsigned *lut) { - display.setCgbPalette(lut); -} - -bool Memory::getMemoryArea(int which, unsigned char **data, int *length) { - if (!data || !length) - return false; - - switch (which) - { - case 4: // oam - *data = &ioamhram[0]; - *length = 160; - return true; - case 5: // hram - *data = &ioamhram[384]; - *length = 128; - return true; - case 6: // bgpal - *data = (unsigned char *)display.bgPalette(); - *length = 32; - return true; - case 7: // sppal - *data = (unsigned char *)display.spPalette(); - *length = 32; - return true; - default: // pass to cartridge - return cart.getMemoryArea(which, data, length); - } -} - -int Memory::LinkStatus(int which) -{ - switch (which) - { - case 256: // ClockSignaled - return linkClockTrigger; - case 257: // AckClockSignal - linkClockTrigger = false; - return 0; - case 258: // GetOut - return ioamhram[0x101] & 0xff; - case 259: // connect link cable - LINKCABLE = true; - return 0; - default: // ShiftIn - if (ioamhram[0x102] & 0x80) // was enabled - { - ioamhram[0x101] = which; - ioamhram[0x102] &= 0x7F; - intreq.flagIrq(8); - } - return 0; - } - - return -1; -} - + ioamhram[0x14D] = (ioamhram[0x14D] & ~1u) | (data & 1); return; + case 0x4F: + if (isCgb()) { + cart.setVrambank(data & 1); + ioamhram[0x14F] = 0xFE | data; + } + + return; + case 0x50: + // this is the register that turns off the bootrom + // it can only ever be written to once (with 1) once boot rom finishes + cart.bios_remap(data); + return; + case 0x51: + dmaSource = data << 8 | (dmaSource & 0xFF); + return; + case 0x52: + dmaSource = (dmaSource & 0xFF00) | (data & 0xF0); + return; + case 0x53: + dmaDestination = data << 8 | (dmaDestination & 0xFF); + return; + case 0x54: + dmaDestination = (dmaDestination & 0xFF00) | (data & 0xF0); + return; + case 0x55: + if (isCgb()) { + ioamhram[0x155] = data & 0x7F; + + if (display.hdmaIsEnabled()) { + if (!(data & 0x80)) { + ioamhram[0x155] |= 0x80; + display.disableHdma(cycleCounter); + } + } else { + if (data & 0x80) { + if (ioamhram[0x140] & 0x80) { + display.enableHdma(cycleCounter); + } else + flagHdmaReq(&intreq); + } else + flagGdmaReq(&intreq); + } + } + + return; + case 0x56: + if (isCgb()) + ioamhram[0x156] = data | 0x3E; + + return; + case 0x68: + if (isCgb()) + 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); + } + + return; + case 0x6A: + if (isCgb()) + 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); + } + + return; + case 0x6C: + if (isCgb()) + ioamhram[0x16C] = data | 0xFE; + + return; + case 0x70: + if (isCgb()) { + cart.setWrambank((data & 0x07) ? (data & 0x07) : 1); + ioamhram[0x170] = data | 0xF8; + } + + return; + case 0x72: + case 0x73: + case 0x74: + if (isCgb()) + break; + + return; + case 0x75: + if (isCgb()) + ioamhram[0x175] = data | 0x8F; + + return; + case 0xFF: + intreq.setIereg(data); + break; + default: + return; + } + + ioamhram[P - 0xFE00] = data; +} + +void Memory::nontrivial_write(const unsigned P, const unsigned data, const unsigned long cycleCounter) { + if (lastOamDmaUpdate != DISABLED_TIME) { + updateOamDma(cycleCounter); + + 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; + } + } else if (P < 0xC000) { + if (cart.wsrambankptr()) + cart.wsrambankptr()[P] = data; + else + cart.rtcWrite(data); + } else + cart.wramdata(P >> 12 & 1)[P & 0xFFF] = data; + } else if (P - 0xFF80u >= 0x7Fu) { + if (P < 0xFF00) { + if (display.oamWritable(cycleCounter) && oamDmaPos >= 0xA0 && (P < 0xFEA0 || isCgb())) { + display.oamChange(cycleCounter); + ioamhram[P - 0xFE00] = data; + } + } else + nontrivial_ff_write(P, data, cycleCounter); + } else + ioamhram[P - 0xFE00] = data; +} + +int Memory::loadROM(const char *romfiledata, unsigned romfilelength, const char *biosfiledata, unsigned biosfilelength, const bool forceDmg, const bool multicartCompat) { + if (const int fail = cart.loadROM(romfiledata, romfilelength, biosfiledata, biosfilelength, forceDmg, multicartCompat)) + return fail; + + sound.init(cart.isCgb()); + display.reset(ioamhram, cart.vramdata(), cart.isCgb()); + + return 0; +} + +unsigned Memory::fillSoundBuffer(const unsigned long cycleCounter) { + sound.generate_samples(cycleCounter, isDoubleSpeed()); + return sound.fillBuffer(); +} + +void Memory::setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned long rgb32) { + display.setDmgPaletteColor(palNum, colorNum, rgb32); +} + +void Memory::setCgbPalette(unsigned *lut) { + display.setCgbPalette(lut); +} + +bool Memory::getMemoryArea(int which, unsigned char **data, int *length) { + if (!data || !length) + return false; + + switch (which) + { + case 4: // oam + *data = &ioamhram[0]; + *length = 160; + return true; + case 5: // hram + *data = &ioamhram[384]; + *length = 128; + return true; + case 6: // bgpal + *data = (unsigned char *)display.bgPalette(); + *length = 32; + return true; + case 7: // sppal + *data = (unsigned char *)display.spPalette(); + *length = 32; + return true; + default: // pass to cartridge + return cart.getMemoryArea(which, data, length); + } +} + +int Memory::LinkStatus(int which) +{ + switch (which) + { + case 256: // ClockSignaled + return linkClockTrigger; + case 257: // AckClockSignal + linkClockTrigger = false; + return 0; + case 258: // GetOut + return ioamhram[0x101] & 0xff; + case 259: // connect link cable + LINKCABLE = true; + return 0; + default: // ShiftIn + if (ioamhram[0x102] & 0x80) // was enabled + { + ioamhram[0x101] = which; + ioamhram[0x102] &= 0x7F; + intreq.flagIrq(8); + } + return 0; + } + + return -1; +} + SYNCFUNC(Memory) { SSS(cart); @@ -1107,5 +1112,5 @@ SYNCFUNC(Memory) NSS(LINKCABLE); NSS(linkClockTrigger); } - -} + +} diff --git a/libgambatte/src/memory.h b/libgambatte/src/memory.h index bbf9e89c6d..78a89a0ee4 100644 --- a/libgambatte/src/memory.h +++ b/libgambatte/src/memory.h @@ -1,293 +1,297 @@ -/*************************************************************************** - * 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. * - ***************************************************************************/ -#ifndef MEMORY_H -#define MEMORY_H - -#include "mem/cartridge.h" -#include "video.h" -#include "sound.h" -#include "interrupter.h" -#include "tima.h" +/*************************************************************************** + * 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. * + ***************************************************************************/ +#ifndef MEMORY_H +#define MEMORY_H + +#include "mem/cartridge.h" +#include "video.h" +#include "sound.h" +#include "interrupter.h" +#include "tima.h" #include "newstate.h" #include "gambatte.h" - -namespace gambatte { -class InputGetter; -class FilterInfo; - -class Memory { - Cartridge cart; - unsigned char ioamhram[0x200]; - - void (*readCallback)(unsigned); - void (*writeCallback)(unsigned); - void (*execCallback)(unsigned); - CDCallback cdCallback; - - unsigned (*getInput)(); - unsigned long divLastUpdate; - unsigned long lastOamDmaUpdate; - - InterruptRequester intreq; - Tima tima; - LCD display; - PSG sound; - Interrupter interrupter; - - unsigned short dmaSource; - unsigned short dmaDestination; - unsigned char oamDmaPos; - unsigned char serialCnt; - bool blanklcd; - - bool LINKCABLE; - bool linkClockTrigger; - - void decEventCycles(MemEventId 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(const Interrupter &interrupter); - - bool loaded() const { return cart.loaded(); } - const char * romTitle() const { return cart.romTitle(); } - - int debugGetLY() const { return display.debugGetLY(); } - - void setStatePtrs(SaveState &state); - void loadState(const SaveState &state/*, unsigned long oldCc*/); - void loadSavedata(const char *data) { cart.loadSavedata(data); } - int saveSavedataLength() {return cart.saveSavedataLength(); } - void saveSavedata(char *dest) { cart.saveSavedata(dest); } - void updateInput(); - - 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(); } - - void setLayers(unsigned mask) { display.setLayers(mask); } - - bool isActive() const { return intreq.eventTime(END) != DISABLED_TIME; } - - long cyclesSinceBlit(const unsigned long cc) const { - return cc < intreq.eventTime(BLIT) ? -1 : static_cast<long>((cc - intreq.eventTime(BLIT)) >> isDoubleSpeed()); - } - - void halt() { intreq.halt(); } - void ei(unsigned long cycleCounter) { if (!ime()) { intreq.ei(cycleCounter); } } - - void di() { intreq.di(); } - - unsigned ff_read(const unsigned P, const unsigned long cycleCounter) { - return P < 0xFF80 ? nontrivial_ff_read(P, cycleCounter) : ioamhram[P - 0xFE00]; - } - - struct CDMapResult - { - eCDLog_AddrType type; - unsigned addr; - }; - - CDMapResult CDMap(const unsigned P) const - { - if(P<0x4000) - { - CDMapResult ret = { eCDLog_AddrType_ROM, P }; - return ret; - } - else if(P<0x8000) - { - 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) - { - 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); - if(has && length!=0) - { - CDMapResult ret = { eCDLog_AddrType_CartRAM, addr }; - return ret; - } - } - } - else if(P<0xE000) - { - 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 - { - ////this is just for debugging, really, it's pretty useless - //CDMapResult ret = { eCDLog_AddrType_HRAM, (P-0xFF80) }; - //return ret; - } - - CDMapResult ret = { eCDLog_AddrType_None }; - return ret; - } - - - unsigned read(const unsigned P, const unsigned long cycleCounter) { - if (readCallback) - readCallback(P); - 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); - } - - unsigned read_excb(const unsigned P, const unsigned long cycleCounter, bool first) { - if (execCallback) - execCallback(P); - 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) { - 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); - if(cdCallback) - { - 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 - 0xFF80u < 0x7Fu) { - ioamhram[P - 0xFE00] = data; - } else - nontrivial_ff_write(P, data, cycleCounter); - if(cdCallback) - { - CDMapResult map = CDMap(P); - if(map.type != eCDLog_AddrType_None) - cdCallback(map.addr,map.type,eCDLog_Flags_Data); - } - } - - unsigned long event(unsigned long cycleCounter); - unsigned long resetCounters(unsigned long cycleCounter); - - int loadROM(const char *romfiledata, unsigned romfilelength, bool forceDmg, bool multicartCompat); - - void setInputGetter(unsigned (*getInput)()) { - this->getInput = getInput; - } - - void setReadCallback(void (*callback)(unsigned)) { - this->readCallback = callback; - } - void setWriteCallback(void (*callback)(unsigned)) { - this->writeCallback = callback; - } - void setExecCallback(void (*callback)(unsigned)) { - this->execCallback = callback; - } - void setCDCallback(CDCallback cdc) { - this->cdCallback = cdc; - } - - void setScanlineCallback(void (*callback)(), int sl) { - display.setScanlineCallback(callback, sl); - } - - void setRTCCallback(std::uint32_t (*callback)()) { - cart.setRTCCallback(callback); - } - - void setEndtime(unsigned long cc, unsigned long inc); - - void setSoundBuffer(uint_least32_t *const buf) { sound.setBuffer(buf); } - unsigned fillSoundBuffer(unsigned long cc); - - void setVideoBuffer(uint_least32_t *const videoBuf, const int pitch) { - display.setVideoBuffer(videoBuf, pitch); - } - - void setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned long rgb32); - void setCgbPalette(unsigned *lut); - - int LinkStatus(int which); - + +namespace gambatte { +class InputGetter; +class FilterInfo; + +class Memory { + Cartridge cart; + unsigned char ioamhram[0x200]; + + void (*readCallback)(unsigned); + void (*writeCallback)(unsigned); + void (*execCallback)(unsigned); + CDCallback cdCallback; + + unsigned (*getInput)(); + unsigned long divLastUpdate; + unsigned long lastOamDmaUpdate; + + InterruptRequester intreq; + Tima tima; + LCD display; + PSG sound; + Interrupter interrupter; + + unsigned short dmaSource; + unsigned short dmaDestination; + unsigned char oamDmaPos; + unsigned char serialCnt; + bool blanklcd; + + bool LINKCABLE; + bool linkClockTrigger; + + void decEventCycles(MemEventId 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(const Interrupter &interrupter); + + bool loaded() const { return cart.loaded(); } + const char * romTitle() const { return cart.romTitle(); } + + void bios_reset(int setting) { + nontrivial_ff_write(0x50, setting, 0); + } + + int debugGetLY() const { return display.debugGetLY(); } + + void setStatePtrs(SaveState &state); + void loadState(const SaveState &state/*, unsigned long oldCc*/); + void loadSavedata(const char *data) { cart.loadSavedata(data); } + int saveSavedataLength() {return cart.saveSavedataLength(); } + void saveSavedata(char *dest) { cart.saveSavedata(dest); } + void updateInput(); + + 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(); } + + void setLayers(unsigned mask) { display.setLayers(mask); } + + bool isActive() const { return intreq.eventTime(END) != DISABLED_TIME; } + + long cyclesSinceBlit(const unsigned long cc) const { + return cc < intreq.eventTime(BLIT) ? -1 : static_cast<long>((cc - intreq.eventTime(BLIT)) >> isDoubleSpeed()); + } + + void halt() { intreq.halt(); } + void ei(unsigned long cycleCounter) { if (!ime()) { intreq.ei(cycleCounter); } } + + void di() { intreq.di(); } + + unsigned ff_read(const unsigned P, const unsigned long cycleCounter) { + return P < 0xFF80 ? nontrivial_ff_read(P, cycleCounter) : ioamhram[P - 0xFE00]; + } + + struct CDMapResult + { + eCDLog_AddrType type; + unsigned addr; + }; + + CDMapResult CDMap(const unsigned P) const + { + if(P<0x4000) + { + CDMapResult ret = { eCDLog_AddrType_ROM, P }; + return ret; + } + else if(P<0x8000) + { + 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) + { + 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); + if(has && length!=0) + { + CDMapResult ret = { eCDLog_AddrType_CartRAM, addr }; + return ret; + } + } + } + else if(P<0xE000) + { + 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 + { + ////this is just for debugging, really, it's pretty useless + //CDMapResult ret = { eCDLog_AddrType_HRAM, (P-0xFF80) }; + //return ret; + } + + CDMapResult ret = { eCDLog_AddrType_None }; + return ret; + } + + + unsigned read(const unsigned P, const unsigned long cycleCounter) { + if (readCallback) + readCallback(P); + 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); + } + + unsigned read_excb(const unsigned P, const unsigned long cycleCounter, bool first) { + if (execCallback) + execCallback(P); + 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) { + 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); + if(cdCallback) + { + 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 - 0xFF80u < 0x7Fu) { + ioamhram[P - 0xFE00] = data; + } else + nontrivial_ff_write(P, data, cycleCounter); + if(cdCallback) + { + CDMapResult map = CDMap(P); + if(map.type != eCDLog_AddrType_None) + cdCallback(map.addr,map.type,eCDLog_Flags_Data); + } + } + + unsigned long event(unsigned long cycleCounter); + unsigned long resetCounters(unsigned long cycleCounter); + + int loadROM(const char *romfiledata, unsigned romfilelength, const char *biosfiledata, unsigned biosfilelength, bool forceDmg, bool multicartCompat); + + void setInputGetter(unsigned (*getInput)()) { + this->getInput = getInput; + } + + void setReadCallback(void (*callback)(unsigned)) { + this->readCallback = callback; + } + void setWriteCallback(void (*callback)(unsigned)) { + this->writeCallback = callback; + } + void setExecCallback(void (*callback)(unsigned)) { + this->execCallback = callback; + } + void setCDCallback(CDCallback cdc) { + this->cdCallback = cdc; + } + + void setScanlineCallback(void (*callback)(), int sl) { + display.setScanlineCallback(callback, sl); + } + + void setRTCCallback(std::uint32_t (*callback)()) { + cart.setRTCCallback(callback); + } + + void setEndtime(unsigned long cc, unsigned long inc); + + void setSoundBuffer(uint_least32_t *const buf) { sound.setBuffer(buf); } + unsigned fillSoundBuffer(unsigned long cc); + + void setVideoBuffer(uint_least32_t *const videoBuf, const int pitch) { + display.setVideoBuffer(videoBuf, pitch); + } + + void setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned long rgb32); + void setCgbPalette(unsigned *lut); + + int LinkStatus(int which); + template<bool isReader>void SyncState(NewState *ns); -}; - -} - -#endif +}; + +} + +#endif diff --git a/libgambatte/src/savestate.h b/libgambatte/src/savestate.h index 4110161ca0..e0726764aa 100644 --- a/libgambatte/src/savestate.h +++ b/libgambatte/src/savestate.h @@ -38,7 +38,7 @@ struct SaveState { void set(T *ptr, const unsigned long sz) { this->ptr = ptr; this->sz = sz; } friend class SaverList; - friend void setInitState(SaveState &, bool, bool, std::uint32_t); + friend void setInitState(SaveState &, bool, bool, std::uint32_t, bool); }; struct CPU {