diff --git a/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs b/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs index fc48c80b9d..af3d781483 100644 --- a/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs +++ b/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs @@ -205,6 +205,9 @@ namespace BizHawk.Emulation.Common FirmwareAndOption("b2e1955d957a475de2411770452eff4ea19f4cee", 1024, "O2", "BIOS", "Odyssey2.bin", "Odyssey 2 Bios"); FirmwareAndOption("a6120aed50831c9c0d95dbdf707820f601d9452e", 1024, "O2", "BIOS-C52", "PhillipsC52.bin", "Phillips C52 Bios"); + + FirmwareAndOption("4ED31EC6B0B175BB109C0EB5FD3D193DA823339F", 256, "GB", "World", "GB_boot_ROM.gb", "Game Boy BIOS"); + FirmwareAndOption("1293D68BF9643BC4F36954C1E80E38F39864528D", 2304, "GBC", "World", "GBC_boot_ROM.gb", "Game Boy Color BIOS"); } // adds a defined firmware ID to the database @@ -336,4 +339,4 @@ namespace BizHawk.Emulation.Common return found.FirstOrDefault(); } } // static class FirmwareDatabase -} \ No newline at end of file +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IEmulator.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IEmulator.cs index b13d7e4dc8..38c11b0234 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IEmulator.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IEmulator.cs @@ -95,8 +95,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy { if (GambatteState != IntPtr.Zero) { + Console.WriteLine("disposing"); LibGambatte.gambatte_destroy(GambatteState); + Console.WriteLine("step2"); GambatteState = IntPtr.Zero; + Console.WriteLine("disposed"); } DisposeSound(); diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISettable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISettable.cs index f6cf22c4f1..f213b6cdb1 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISettable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISettable.cs @@ -80,6 +80,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy public class GambatteSyncSettings { + [DisplayName("Enable BIOS: WARNING: File must exist!")] + [Description("Boots game using system BIOS. Should be used for TASing")] + [DefaultValue(false)] + public bool EnableBIOS { get; set; } + [DisplayName("Force DMG Mode")] [Description("Force the game to run on DMG hardware, even if it's detected as a CGB game. Relevant for games that are \"CGB Enhanced\" but do not require CGB.")] [DefaultValue(false)] diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs index e2e3f6b4ea..d7e3812de6 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs @@ -52,6 +52,23 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy throw new InvalidOperationException("gambatte_create() returned null???"); } + Console.WriteLine(game.System); + + byte[] BiosRom; + + if (game.System == "GB") + { + BiosRom = new byte[256]; + BiosRom = comm.CoreFileProvider.GetFirmware("GB", "World", false); + } + else + { + BiosRom = new byte[2304]; + BiosRom = comm.CoreFileProvider.GetFirmware("GBC", "World", false); + } + + int bios_length = BiosRom == null ? 0 : BiosRom.Length; + try { _syncSettings = (GambatteSyncSettings)syncSettings ?? new GambatteSyncSettings(); @@ -66,6 +83,25 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy if (_syncSettings.ForceDMG) { flags |= LibGambatte.LoadFlags.FORCE_DMG; + + // we need to change the BIOS to GB bios + if (game.System == "GBC") + { + BiosRom = null; + BiosRom = new byte[256]; + BiosRom = comm.CoreFileProvider.GetFirmware("GB", "World", false); + } + } + + if (_syncSettings.EnableBIOS && BiosRom == null) + { + throw new MissingFirmwareException("Boot Rom not found"); + } + + // to disable BIOS loading into gambatte, just set bios_length to 0 + if (!_syncSettings.EnableBIOS) + { + bios_length = 0; } if (_syncSettings.GBACGB) @@ -78,7 +114,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy flags |= LibGambatte.LoadFlags.MULTICART_COMPAT; } - if (LibGambatte.gambatte_load(GambatteState, file, (uint)file.Length, GetCurrentTime(), flags) != 0) + if (LibGambatte.gambatte_load(GambatteState, file, (uint)file.Length, BiosRom, (uint)bios_length, GetCurrentTime(), flags) != 0) { throw new InvalidOperationException("gambatte_load() returned non-zero (is this not a gb or gbc rom?)"); } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibGambatte.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibGambatte.cs index 86b7e4fffd..06aaa58fa6 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibGambatte.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibGambatte.cs @@ -57,7 +57,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy /// ORed combination of LoadFlags. /// 0 on success, negative value on failure. [DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)] - public static extern int gambatte_load(IntPtr core, byte[] romdata, uint length, long now, LoadFlags flags); + public static extern int gambatte_load(IntPtr core, byte[] romdata, uint length, byte[] biosdata, uint bioslength, long now, LoadFlags flags); /// /// Emulates until at least 'samples' stereo sound samples are produced in the supplied buffer, diff --git a/libgambatte/include/gambatte.h b/libgambatte/include/gambatte.h index 43e56d5fb9..2265ffa6d9 100644 --- a/libgambatte/include/gambatte.h +++ b/libgambatte/include/gambatte.h @@ -60,7 +60,8 @@ public: * @param flags ORed combination of LoadFlags. * @return 0 on success, negative value on failure. */ - int load(const char *romfiledata, unsigned romfilelength, std::uint32_t now, unsigned flags = 0); + bool use_bios; + int load(const char *romfiledata, unsigned romfilelength, const char *biosfiledata, unsigned biosfilelength, std::uint32_t now, unsigned flags = 0); /** Emulates until at least 'samples' stereo sound samples are produced in the supplied buffer, * or until a video frame has been drawn. diff --git a/libgambatte/libgambatte.vcxproj b/libgambatte/libgambatte.vcxproj index 2f3aaa552c..4d34da2828 100644 --- a/libgambatte/libgambatte.vcxproj +++ b/libgambatte/libgambatte.vcxproj @@ -1,5 +1,5 @@  - + Debug @@ -28,23 +28,27 @@ DynamicLibrary true Unicode + v140 DynamicLibrary true Unicode + v140 DynamicLibrary false true Unicode + v140 DynamicLibrary false true Unicode + v140 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); + templatevoid SyncState(NewState *ns); -}; - -} - -#endif +}; + +} + +#endif diff --git a/libgambatte/src/gambatte.cpp b/libgambatte/src/gambatte.cpp index d56632d8f5..c3843a862c 100644 --- a/libgambatte/src/gambatte.cpp +++ b/libgambatte/src/gambatte.cpp @@ -94,7 +94,12 @@ 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); + } + + setInitState(state, p_->cpu.isCgb(), p_->gbaCgbMode, now, use_bios); p_->cpu.loadState(state); if (length > 0) { @@ -136,16 +141,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(); } @@ -228,6 +234,7 @@ SYNCFUNC(GB) SSS(p_->cpu); NSS(p_->gbaCgbMode); NSS(p_->vbuff); + NSS(use_bios); } } diff --git a/libgambatte/src/initstate.cpp b/libgambatte/src/initstate.cpp index 6430061789..307a7f460c 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; + state.mem.using_bios = true; + } + 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 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/mem/cartridge.cpp b/libgambatte/src/mem/cartridge.cpp index b8b642f4c4..4117c86f54 100644 --- a/libgambatte/src/mem/cartridge.cpp +++ b/libgambatte/src/mem/cartridge.cpp @@ -1,152 +1,153 @@ -/*************************************************************************** - * Copyright (C) 2007-2010 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 "cartridge.h" -#include "../savestate.h" -#include -#include -#include - -namespace gambatte { - -namespace { - -static unsigned toMulti64Rombank(const unsigned rombank) { - return (rombank >> 1 & 0x30) | (rombank & 0xF); -} - -class DefaultMbc : public Mbc { -public: - virtual bool isAddressWithinAreaRombankCanBeMappedTo(unsigned addr, unsigned bank) const { - return (addr< 0x4000) == (bank == 0); - } +/*************************************************************************** + * Copyright (C) 2007-2010 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 "cartridge.h" +#include "../savestate.h" +#include +#include +#include +#include + +namespace gambatte { + +namespace { + +static unsigned toMulti64Rombank(const unsigned rombank) { + return (rombank >> 1 & 0x30) | (rombank & 0xF); +} + +class DefaultMbc : public Mbc { +public: + virtual bool isAddressWithinAreaRombankCanBeMappedTo(unsigned addr, unsigned bank) const { + return (addr< 0x4000) == (bank == 0); + } virtual void SyncState(NewState *ns, bool isReader) { - } -}; - -class Mbc0 : public DefaultMbc { - MemPtrs &memptrs; - bool enableRam; - -public: - explicit Mbc0(MemPtrs &memptrs) - : memptrs(memptrs), - enableRam(false) - { - } - - virtual void romWrite(const unsigned P, const unsigned data) { - if (P < 0x2000) { - enableRam = (data & 0xF) == 0xA; - memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, 0); - } - } - - virtual void saveState(SaveState::Mem &ss) const { - ss.enableRam = enableRam; - } - - virtual void loadState(const SaveState::Mem &ss) { - enableRam = ss.enableRam; - memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, 0); - } - + } +}; + +class Mbc0 : public DefaultMbc { + MemPtrs &memptrs; + bool enableRam; + +public: + explicit Mbc0(MemPtrs &memptrs) + : memptrs(memptrs), + enableRam(false) + { + } + + virtual void romWrite(const unsigned P, const unsigned data) { + if (P < 0x2000) { + enableRam = (data & 0xF) == 0xA; + memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, 0); + } + } + + virtual void saveState(SaveState::Mem &ss) const { + ss.enableRam = enableRam; + } + + virtual void loadState(const SaveState::Mem &ss) { + enableRam = ss.enableRam; + memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, 0); + } + virtual void SyncState(NewState *ns, bool isReader) { NSS(enableRam); } -}; - -static inline unsigned rambanks(const MemPtrs &memptrs) { - return static_cast(memptrs.rambankdataend() - memptrs.rambankdata()) / 0x2000; -} - -static inline unsigned rombanks(const MemPtrs &memptrs) { - return static_cast(memptrs.romdataend() - memptrs.romdata() ) / 0x4000; -} - -class Mbc1 : public DefaultMbc { - MemPtrs &memptrs; - unsigned char rombank; - unsigned char rambank; - bool enableRam; - bool rambankMode; - - static unsigned adjustedRombank(unsigned bank) { return bank & 0x1F ? bank : bank | 1; } - void setRambank() const { memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, rambank & (rambanks(memptrs) - 1)); } - void setRombank() const { memptrs.setRombank(adjustedRombank(rombank & (rombanks(memptrs) - 1))); } - -public: - explicit Mbc1(MemPtrs &memptrs) - : memptrs(memptrs), - rombank(1), - rambank(0), - enableRam(false), - rambankMode(false) - { - } - - virtual void romWrite(const unsigned P, const unsigned data) { - switch (P >> 13 & 3) { - case 0: - enableRam = (data & 0xF) == 0xA; - setRambank(); - break; - case 1: - rombank = rambankMode ? data & 0x1F : (rombank & 0x60) | (data & 0x1F); - setRombank(); - break; - case 2: - if (rambankMode) { - rambank = data & 3; - setRambank(); - } else { - rombank = (data << 5 & 0x60) | (rombank & 0x1F); - setRombank(); - } - - break; - case 3: - // Pretty sure this should take effect immediately, but I have a policy not to change old behavior - // unless I have something (eg. a verified test or a game) that justifies it. - rambankMode = data & 1; - break; - } - } - - virtual void saveState(SaveState::Mem &ss) const { - ss.rombank = rombank; - ss.rambank = rambank; - ss.enableRam = enableRam; - ss.rambankMode = rambankMode; - } - - virtual void loadState(const SaveState::Mem &ss) { - rombank = ss.rombank; - rambank = ss.rambank; - enableRam = ss.enableRam; - rambankMode = ss.rambankMode; - setRambank(); - setRombank(); - } - +}; + +static inline unsigned rambanks(const MemPtrs &memptrs) { + return static_cast(memptrs.rambankdataend() - memptrs.rambankdata()) / 0x2000; +} + +static inline unsigned rombanks(const MemPtrs &memptrs) { + return static_cast(memptrs.romdataend() - memptrs.romdata() ) / 0x4000; +} + +class Mbc1 : public DefaultMbc { + MemPtrs &memptrs; + unsigned char rombank; + unsigned char rambank; + bool enableRam; + bool rambankMode; + + static unsigned adjustedRombank(unsigned bank) { return bank & 0x1F ? bank : bank | 1; } + void setRambank() const { memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, rambank & (rambanks(memptrs) - 1)); } + void setRombank() const { memptrs.setRombank(adjustedRombank(rombank & (rombanks(memptrs) - 1))); } + +public: + explicit Mbc1(MemPtrs &memptrs) + : memptrs(memptrs), + rombank(1), + rambank(0), + enableRam(false), + rambankMode(false) + { + } + + virtual void romWrite(const unsigned P, const unsigned data) { + switch (P >> 13 & 3) { + case 0: + enableRam = (data & 0xF) == 0xA; + setRambank(); + break; + case 1: + rombank = rambankMode ? data & 0x1F : (rombank & 0x60) | (data & 0x1F); + setRombank(); + break; + case 2: + if (rambankMode) { + rambank = data & 3; + setRambank(); + } else { + rombank = (data << 5 & 0x60) | (rombank & 0x1F); + setRombank(); + } + + break; + case 3: + // Pretty sure this should take effect immediately, but I have a policy not to change old behavior + // unless I have something (eg. a verified test or a game) that justifies it. + rambankMode = data & 1; + break; + } + } + + virtual void saveState(SaveState::Mem &ss) const { + ss.rombank = rombank; + ss.rambank = rambank; + ss.enableRam = enableRam; + ss.rambankMode = rambankMode; + } + + virtual void loadState(const SaveState::Mem &ss) { + rombank = ss.rombank; + rambank = ss.rambank; + enableRam = ss.enableRam; + rambankMode = ss.rambankMode; + setRambank(); + setRombank(); + } + virtual void SyncState(NewState *ns, bool isReader) { NSS(rombank); @@ -154,267 +155,267 @@ public: NSS(enableRam); NSS(rambankMode); } -}; - -class Mbc1Multi64 : public Mbc { - MemPtrs &memptrs; - unsigned char rombank; - bool enableRam; - bool rombank0Mode; - - static unsigned adjustedRombank(unsigned bank) { return bank & 0x1F ? bank : bank | 1; } - - void setRombank() const { - if (rombank0Mode) { - const unsigned rb = toMulti64Rombank(rombank); - memptrs.setRombank0(rb & 0x30); - memptrs.setRombank(adjustedRombank(rb)); - } else { - memptrs.setRombank0(0); - memptrs.setRombank(adjustedRombank(rombank & (rombanks(memptrs) - 1))); - } - } - -public: - explicit Mbc1Multi64(MemPtrs &memptrs) - : memptrs(memptrs), - rombank(1), - enableRam(false), - rombank0Mode(false) - { - } - - virtual void romWrite(const unsigned P, const unsigned data) { - switch (P >> 13 & 3) { - case 0: - enableRam = (data & 0xF) == 0xA; - memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, 0); - break; - case 1: - rombank = (rombank & 0x60) | (data & 0x1F); - memptrs.setRombank(adjustedRombank(rombank0Mode ? toMulti64Rombank(rombank) : rombank & (rombanks(memptrs) - 1))); - break; - case 2: - rombank = (data << 5 & 0x60) | (rombank & 0x1F); - setRombank(); - break; - case 3: - rombank0Mode = data & 1; - setRombank(); - break; - } - } - - virtual void saveState(SaveState::Mem &ss) const { - ss.rombank = rombank; - ss.enableRam = enableRam; - ss.rambankMode = rombank0Mode; - } - - virtual void loadState(const SaveState::Mem &ss) { - rombank = ss.rombank; - enableRam = ss.enableRam; - rombank0Mode = ss.rambankMode; - memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, 0); - setRombank(); - } - - virtual bool isAddressWithinAreaRombankCanBeMappedTo(unsigned addr, unsigned bank) const { - return (addr < 0x4000) == ((bank & 0xF) == 0); - } - +}; + +class Mbc1Multi64 : public Mbc { + MemPtrs &memptrs; + unsigned char rombank; + bool enableRam; + bool rombank0Mode; + + static unsigned adjustedRombank(unsigned bank) { return bank & 0x1F ? bank : bank | 1; } + + void setRombank() const { + if (rombank0Mode) { + const unsigned rb = toMulti64Rombank(rombank); + memptrs.setRombank0(rb & 0x30); + memptrs.setRombank(adjustedRombank(rb)); + } else { + memptrs.setRombank0(0); + memptrs.setRombank(adjustedRombank(rombank & (rombanks(memptrs) - 1))); + } + } + +public: + explicit Mbc1Multi64(MemPtrs &memptrs) + : memptrs(memptrs), + rombank(1), + enableRam(false), + rombank0Mode(false) + { + } + + virtual void romWrite(const unsigned P, const unsigned data) { + switch (P >> 13 & 3) { + case 0: + enableRam = (data & 0xF) == 0xA; + memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, 0); + break; + case 1: + rombank = (rombank & 0x60) | (data & 0x1F); + memptrs.setRombank(adjustedRombank(rombank0Mode ? toMulti64Rombank(rombank) : rombank & (rombanks(memptrs) - 1))); + break; + case 2: + rombank = (data << 5 & 0x60) | (rombank & 0x1F); + setRombank(); + break; + case 3: + rombank0Mode = data & 1; + setRombank(); + break; + } + } + + virtual void saveState(SaveState::Mem &ss) const { + ss.rombank = rombank; + ss.enableRam = enableRam; + ss.rambankMode = rombank0Mode; + } + + virtual void loadState(const SaveState::Mem &ss) { + rombank = ss.rombank; + enableRam = ss.enableRam; + rombank0Mode = ss.rambankMode; + memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, 0); + setRombank(); + } + + virtual bool isAddressWithinAreaRombankCanBeMappedTo(unsigned addr, unsigned bank) const { + return (addr < 0x4000) == ((bank & 0xF) == 0); + } + virtual void SyncState(NewState *ns, bool isReader) { NSS(rombank); NSS(enableRam); NSS(rombank0Mode); } -}; - -class Mbc2 : public DefaultMbc { - MemPtrs &memptrs; - unsigned char rombank; - bool enableRam; - -public: - explicit Mbc2(MemPtrs &memptrs) - : memptrs(memptrs), - rombank(1), - enableRam(false) - { - } - - virtual void romWrite(const unsigned P, const unsigned data) { - switch (P & 0x6100) { - case 0x0000: - enableRam = (data & 0xF) == 0xA; - memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, 0); - break; - case 0x2100: - rombank = data & 0xF; - memptrs.setRombank(rombank & (rombanks(memptrs) - 1)); - break; - } - } - - virtual void saveState(SaveState::Mem &ss) const { - ss.rombank = rombank; - ss.enableRam = enableRam; - } - - virtual void loadState(const SaveState::Mem &ss) { - rombank = ss.rombank; - enableRam = ss.enableRam; - memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, 0); - memptrs.setRombank(rombank & (rombanks(memptrs) - 1)); - } - +}; + +class Mbc2 : public DefaultMbc { + MemPtrs &memptrs; + unsigned char rombank; + bool enableRam; + +public: + explicit Mbc2(MemPtrs &memptrs) + : memptrs(memptrs), + rombank(1), + enableRam(false) + { + } + + virtual void romWrite(const unsigned P, const unsigned data) { + switch (P & 0x6100) { + case 0x0000: + enableRam = (data & 0xF) == 0xA; + memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, 0); + break; + case 0x2100: + rombank = data & 0xF; + memptrs.setRombank(rombank & (rombanks(memptrs) - 1)); + break; + } + } + + virtual void saveState(SaveState::Mem &ss) const { + ss.rombank = rombank; + ss.enableRam = enableRam; + } + + virtual void loadState(const SaveState::Mem &ss) { + rombank = ss.rombank; + enableRam = ss.enableRam; + memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, 0); + memptrs.setRombank(rombank & (rombanks(memptrs) - 1)); + } + virtual void SyncState(NewState *ns, bool isReader) { NSS(rombank); NSS(enableRam); } -}; - -class Mbc3 : public DefaultMbc { - MemPtrs &memptrs; - Rtc *const rtc; - unsigned char rombank; - unsigned char rambank; - bool enableRam; - - static unsigned adjustedRombank(unsigned bank) { return bank & 0x7F ? bank : bank | 1; } - void setRambank() const { - unsigned flags = enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0; - - if (rtc) { - rtc->set(enableRam, rambank); - - if (rtc->getActive()) - flags |= MemPtrs::RTC_EN; - } - - memptrs.setRambank(flags, rambank & (rambanks(memptrs) - 1)); - } - // we adjust the rombank before masking with size? this seems correct, as how would the mbc - // know that high rom address outputs were not connected - void setRombank() const { memptrs.setRombank(adjustedRombank(rombank) & (rombanks(memptrs) - 1)); } - -public: - Mbc3(MemPtrs &memptrs, Rtc *const rtc) - : memptrs(memptrs), - rtc(rtc), - rombank(1), - rambank(0), - enableRam(false) - { - } - - virtual void romWrite(const unsigned P, const unsigned data) { - switch (P >> 13 & 3) { - case 0: - enableRam = (data & 0xF) == 0xA; - setRambank(); - break; - case 1: - rombank = data & 0x7F; - setRombank(); - break; - case 2: - rambank = data; - setRambank(); - break; - case 3: - if (rtc) - rtc->latch(data); - - break; - } - } - - virtual void saveState(SaveState::Mem &ss) const { - ss.rombank = rombank; - ss.rambank = rambank; - ss.enableRam = enableRam; - } - - virtual void loadState(const SaveState::Mem &ss) { - rombank = ss.rombank; - rambank = ss.rambank; - enableRam = ss.enableRam; - setRambank(); - setRombank(); - } - +}; + +class Mbc3 : public DefaultMbc { + MemPtrs &memptrs; + Rtc *const rtc; + unsigned char rombank; + unsigned char rambank; + bool enableRam; + + static unsigned adjustedRombank(unsigned bank) { return bank & 0x7F ? bank : bank | 1; } + void setRambank() const { + unsigned flags = enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0; + + if (rtc) { + rtc->set(enableRam, rambank); + + if (rtc->getActive()) + flags |= MemPtrs::RTC_EN; + } + + memptrs.setRambank(flags, rambank & (rambanks(memptrs) - 1)); + } + // we adjust the rombank before masking with size? this seems correct, as how would the mbc + // know that high rom address outputs were not connected + void setRombank() const { memptrs.setRombank(adjustedRombank(rombank) & (rombanks(memptrs) - 1)); } + +public: + Mbc3(MemPtrs &memptrs, Rtc *const rtc) + : memptrs(memptrs), + rtc(rtc), + rombank(1), + rambank(0), + enableRam(false) + { + } + + virtual void romWrite(const unsigned P, const unsigned data) { + switch (P >> 13 & 3) { + case 0: + enableRam = (data & 0xF) == 0xA; + setRambank(); + break; + case 1: + rombank = data & 0x7F; + setRombank(); + break; + case 2: + rambank = data; + setRambank(); + break; + case 3: + if (rtc) + rtc->latch(data); + + break; + } + } + + virtual void saveState(SaveState::Mem &ss) const { + ss.rombank = rombank; + ss.rambank = rambank; + ss.enableRam = enableRam; + } + + virtual void loadState(const SaveState::Mem &ss) { + rombank = ss.rombank; + rambank = ss.rambank; + enableRam = ss.enableRam; + setRambank(); + setRombank(); + } + virtual void SyncState(NewState *ns, bool isReader) { NSS(rombank); NSS(rambank); NSS(enableRam); } -}; - -class HuC1 : public DefaultMbc { - MemPtrs &memptrs; - unsigned char rombank; - unsigned char rambank; - bool enableRam; - bool rambankMode; - - void setRambank() const { - memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : MemPtrs::READ_EN, - rambankMode ? rambank & (rambanks(memptrs) - 1) : 0); - } - - void setRombank() const { memptrs.setRombank((rambankMode ? rombank : rambank << 6 | rombank) & (rombanks(memptrs) - 1)); } - -public: - explicit HuC1(MemPtrs &memptrs) - : memptrs(memptrs), - rombank(1), - rambank(0), - enableRam(false), - rambankMode(false) - { - } - - virtual void romWrite(const unsigned P, const unsigned data) { - switch (P >> 13 & 3) { - case 0: - enableRam = (data & 0xF) == 0xA; - setRambank(); - break; - case 1: - rombank = data & 0x3F; - setRombank(); - break; - case 2: - rambank = data & 3; - rambankMode ? setRambank() : setRombank(); - break; - case 3: - rambankMode = data & 1; - setRambank(); - setRombank(); - break; - } - } - - virtual void saveState(SaveState::Mem &ss) const { - ss.rombank = rombank; - ss.rambank = rambank; - ss.enableRam = enableRam; - ss.rambankMode = rambankMode; - } - - virtual void loadState(const SaveState::Mem &ss) { - rombank = ss.rombank; - rambank = ss.rambank; - enableRam = ss.enableRam; - rambankMode = ss.rambankMode; - setRambank(); - setRombank(); - } +}; + +class HuC1 : public DefaultMbc { + MemPtrs &memptrs; + unsigned char rombank; + unsigned char rambank; + bool enableRam; + bool rambankMode; + + void setRambank() const { + memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : MemPtrs::READ_EN, + rambankMode ? rambank & (rambanks(memptrs) - 1) : 0); + } + + void setRombank() const { memptrs.setRombank((rambankMode ? rombank : rambank << 6 | rombank) & (rombanks(memptrs) - 1)); } + +public: + explicit HuC1(MemPtrs &memptrs) + : memptrs(memptrs), + rombank(1), + rambank(0), + enableRam(false), + rambankMode(false) + { + } + + virtual void romWrite(const unsigned P, const unsigned data) { + switch (P >> 13 & 3) { + case 0: + enableRam = (data & 0xF) == 0xA; + setRambank(); + break; + case 1: + rombank = data & 0x3F; + setRombank(); + break; + case 2: + rambank = data & 3; + rambankMode ? setRambank() : setRombank(); + break; + case 3: + rambankMode = data & 1; + setRambank(); + setRombank(); + break; + } + } + + virtual void saveState(SaveState::Mem &ss) const { + ss.rombank = rombank; + ss.rambank = rambank; + ss.enableRam = enableRam; + ss.rambankMode = rambankMode; + } + + virtual void loadState(const SaveState::Mem &ss) { + rombank = ss.rombank; + rambank = ss.rambank; + enableRam = ss.enableRam; + rambankMode = ss.rambankMode; + setRambank(); + setRombank(); + } virtual void SyncState(NewState *ns, bool isReader) { @@ -423,327 +424,375 @@ public: NSS(enableRam); NSS(rambankMode); } -}; - -class Mbc5 : public DefaultMbc { - MemPtrs &memptrs; - unsigned short rombank; - unsigned char rambank; - bool enableRam; - - static unsigned adjustedRombank(const unsigned bank) { return bank; } - void setRambank() const { memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, rambank & (rambanks(memptrs) - 1)); } - void setRombank() const { memptrs.setRombank(adjustedRombank(rombank & (rombanks(memptrs) - 1))); } - -public: - explicit Mbc5(MemPtrs &memptrs) - : memptrs(memptrs), - rombank(1), - rambank(0), - enableRam(false) - { - } - - virtual void romWrite(const unsigned P, const unsigned data) { - switch (P >> 13 & 3) { - case 0: - enableRam = (data & 0xF) == 0xA; - setRambank(); - break; - case 1: - rombank = P < 0x3000 ? (rombank & 0x100) | data - : (data << 8 & 0x100) | (rombank & 0xFF); - setRombank(); - break; - case 2: - rambank = data & 0xF; - setRambank(); - break; - case 3: - break; - } - } - - virtual void saveState(SaveState::Mem &ss) const { - ss.rombank = rombank; - ss.rambank = rambank; - ss.enableRam = enableRam; - } - - virtual void loadState(const SaveState::Mem &ss) { - rombank = ss.rombank; - rambank = ss.rambank; - enableRam = ss.enableRam; - setRambank(); - setRombank(); - } - +}; + +class Mbc5 : public DefaultMbc { + MemPtrs &memptrs; + unsigned short rombank; + unsigned char rambank; + bool enableRam; + + static unsigned adjustedRombank(const unsigned bank) { return bank; } + void setRambank() const { memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, rambank & (rambanks(memptrs) - 1)); } + void setRombank() const { memptrs.setRombank(adjustedRombank(rombank & (rombanks(memptrs) - 1))); } + +public: + explicit Mbc5(MemPtrs &memptrs) + : memptrs(memptrs), + rombank(1), + rambank(0), + enableRam(false) + { + } + + virtual void romWrite(const unsigned P, const unsigned data) { + switch (P >> 13 & 3) { + case 0: + enableRam = (data & 0xF) == 0xA; + setRambank(); + break; + case 1: + rombank = P < 0x3000 ? (rombank & 0x100) | data + : (data << 8 & 0x100) | (rombank & 0xFF); + setRombank(); + break; + case 2: + rambank = data & 0xF; + setRambank(); + break; + case 3: + break; + } + } + + virtual void saveState(SaveState::Mem &ss) const { + ss.rombank = rombank; + ss.rambank = rambank; + ss.enableRam = enableRam; + } + + virtual void loadState(const SaveState::Mem &ss) { + rombank = ss.rombank; + rambank = ss.rambank; + enableRam = ss.enableRam; + setRambank(); + setRombank(); + } + virtual void SyncState(NewState *ns, bool isReader) { NSS(rombank); NSS(rambank); NSS(enableRam); } -}; - -static bool hasRtc(const unsigned headerByte0x147) { - switch (headerByte0x147) { - case 0x0F: - case 0x10: return true; - default: return false; - } -} - -} - -void Cartridge::setStatePtrs(SaveState &state) { - state.mem.vram.set(memptrs.vramdata(), memptrs.vramdataend() - memptrs.vramdata()); - state.mem.sram.set(memptrs.rambankdata(), memptrs.rambankdataend() - memptrs.rambankdata()); - state.mem.wram.set(memptrs.wramdata(0), memptrs.wramdataend() - memptrs.wramdata(0)); -} - -void Cartridge::loadState(const SaveState &state) { - rtc.loadState(state); - mbc->loadState(state.mem); -} - -static void enforce8bit(unsigned char *data, unsigned long sz) { - if (static_cast(0x100)) - while (sz--) - *data++ &= 0xFF; -} - -static unsigned pow2ceil(unsigned n) { - --n; - n |= n >> 1; - n |= n >> 2; - n |= n >> 4; - n |= n >> 8; - ++n; - - return n; -} - -int Cartridge::loadROM(const char *romfiledata, unsigned romfilelength, const bool forceDmg, const bool multicartCompat) { - //const std::auto_ptr rom(newFileInstance(romfile)); - - //if (rom->fail()) - // return -1; - - unsigned rambanks = 1; - unsigned rombanks = 2; - bool cgb = false; - enum Cartridgetype { PLAIN, MBC1, MBC2, MBC3, MBC5, HUC1 } type = PLAIN; - - { - unsigned char header[0x150]; - //rom->read(reinterpret_cast(header), sizeof header); - if (romfilelength >= sizeof header) - std::memcpy(header, romfiledata, sizeof header); - else - return -1; - - switch (header[0x0147]) { - case 0x00: std::puts("Plain ROM loaded."); type = PLAIN; break; - case 0x01: std::puts("MBC1 ROM loaded."); type = MBC1; break; - case 0x02: std::puts("MBC1 ROM+RAM loaded."); type = MBC1; break; - case 0x03: std::puts("MBC1 ROM+RAM+BATTERY loaded."); type = MBC1; break; - case 0x05: std::puts("MBC2 ROM loaded."); type = MBC2; break; - case 0x06: std::puts("MBC2 ROM+BATTERY loaded."); type = MBC2; break; - case 0x08: std::puts("Plain ROM with additional RAM loaded."); type = PLAIN; break; - case 0x09: std::puts("Plain ROM with additional RAM and Battery loaded."); type = PLAIN; break; - case 0x0B: std::puts("MM01 ROM not supported."); return -1; - case 0x0C: std::puts("MM01 ROM not supported."); return -1; - case 0x0D: std::puts("MM01 ROM not supported."); return -1; - case 0x0F: std::puts("MBC3 ROM+TIMER+BATTERY loaded."); type = MBC3; break; - case 0x10: std::puts("MBC3 ROM+TIMER+RAM+BATTERY loaded."); type = MBC3; break; - case 0x11: std::puts("MBC3 ROM loaded."); type = MBC3; break; - case 0x12: std::puts("MBC3 ROM+RAM loaded."); type = MBC3; break; - case 0x13: std::puts("MBC3 ROM+RAM+BATTERY loaded."); type = MBC3; break; - case 0x15: std::puts("MBC4 ROM not supported."); return -1; - case 0x16: std::puts("MBC4 ROM not supported."); return -1; - case 0x17: std::puts("MBC4 ROM not supported."); return -1; - case 0x19: std::puts("MBC5 ROM loaded."); type = MBC5; break; - case 0x1A: std::puts("MBC5 ROM+RAM loaded."); type = MBC5; break; - case 0x1B: std::puts("MBC5 ROM+RAM+BATTERY loaded."); type = MBC5; break; - case 0x1C: std::puts("MBC5+RUMBLE ROM not supported."); type = MBC5; break; - case 0x1D: std::puts("MBC5+RUMBLE+RAM ROM not suported."); type = MBC5; break; - case 0x1E: std::puts("MBC5+RUMBLE+RAM+BATTERY ROM not supported."); type = MBC5; break; - case 0xFC: std::puts("Pocket Camera ROM not supported."); return -1; - case 0xFD: std::puts("Bandai TAMA5 ROM not supported."); return -1; - case 0xFE: std::puts("HuC3 ROM not supported."); return -1; - case 0xFF: std::puts("HuC1 ROM+RAM+BATTERY loaded."); type = HUC1; break; - default: std::puts("Wrong data-format, corrupt or unsupported ROM."); return -1; - } - - /*switch (header[0x0148]) { - case 0x00: rombanks = 2; break; - case 0x01: rombanks = 4; break; - case 0x02: rombanks = 8; break; - case 0x03: rombanks = 16; break; - case 0x04: rombanks = 32; break; - case 0x05: rombanks = 64; break; - case 0x06: rombanks = 128; break; - case 0x07: rombanks = 256; break; - case 0x08: rombanks = 512; break; - case 0x52: rombanks = 72; break; - case 0x53: rombanks = 80; break; - case 0x54: rombanks = 96; break; - default: return -1; - } - - std::printf("rombanks: %u\n", rombanks);*/ - - switch (header[0x0149]) { - case 0x00: /*std::puts("No RAM");*/ rambanks = type == MBC2; break; - case 0x01: /*std::puts("2kB RAM");*/ /*rambankrom=1; break;*/ - case 0x02: /*std::puts("8kB RAM");*/ - rambanks = 1; - break; - case 0x03: /*std::puts("32kB RAM");*/ - rambanks = 4; - break; - case 0x04: /*std::puts("128kB RAM");*/ - rambanks = 16; - break; - case 0x05: /*std::puts("undocumented kB RAM");*/ - rambanks = 16; - break; - default: /*std::puts("Wrong data-format, corrupt or unsupported ROM loaded.");*/ - rambanks = 16; - break; - } - - cgb = header[0x0143] >> 7 & (1 ^ forceDmg); - std::printf("cgb: %d\n", cgb); - } - - std::printf("rambanks: %u\n", rambanks); - - const std::size_t filesize = romfilelength; //rom->size(); - rombanks = std::max(pow2ceil(filesize / 0x4000), 2u); - std::printf("rombanks: %u\n", static_cast(filesize / 0x4000)); - - mbc.reset(); - memptrs.reset(rombanks, rambanks, cgb ? 8 : 2); - rtc.set(false, 0); - - //rom->rewind(); - //rom->read(reinterpret_cast(memptrs.romdata()), (filesize / 0x4000) * 0x4000ul); - std::memcpy(memptrs.romdata(), romfiledata, (filesize / 0x4000) * 0x4000ul); - std::memset(memptrs.romdata() + (filesize / 0x4000) * 0x4000ul, 0xFF, (rombanks - filesize / 0x4000) * 0x4000ul); - enforce8bit(memptrs.romdata(), rombanks * 0x4000ul); - - //if (rom->fail()) - // return -1; - - switch (type) { - case PLAIN: mbc.reset(new Mbc0(memptrs)); break; - case MBC1: - if (!rambanks && rombanks == 64 && multicartCompat) { - std::puts("Multi-ROM \"MBC1\" presumed"); - mbc.reset(new Mbc1Multi64(memptrs)); - } else - mbc.reset(new Mbc1(memptrs)); - - break; - case MBC2: mbc.reset(new Mbc2(memptrs)); break; - case MBC3: mbc.reset(new Mbc3(memptrs, hasRtc(memptrs.romdata()[0x147]) ? &rtc : 0)); break; - case MBC5: mbc.reset(new Mbc5(memptrs)); break; - case HUC1: mbc.reset(new HuC1(memptrs)); break; - } - - return 0; -} - -static bool hasBattery(const unsigned char headerByte0x147) { - switch (headerByte0x147) { - case 0x03: - case 0x06: - case 0x09: - case 0x0F: - case 0x10: - case 0x13: - case 0x1B: - case 0x1E: - case 0xFF: return true; - default: return false; - } -} - -void Cartridge::loadSavedata(const char *data) { - if (hasBattery(memptrs.romdata()[0x147])) { - int length = memptrs.rambankdataend() - memptrs.rambankdata(); - std::memcpy(memptrs.rambankdata(), data, length); - data += length; - enforce8bit(memptrs.rambankdata(), length); - } - - if (hasRtc(memptrs.romdata()[0x147])) { - unsigned long basetime; - std::memcpy(&basetime, data, 4); - rtc.setBaseTime(basetime); - } -} - -int Cartridge::saveSavedataLength() { - int ret = 0; - if (hasBattery(memptrs.romdata()[0x147])) { - ret = memptrs.rambankdataend() - memptrs.rambankdata(); - } - if (hasRtc(memptrs.romdata()[0x147])) { - ret += 4; - } - return ret; -} - -void Cartridge::saveSavedata(char *dest) { - if (hasBattery(memptrs.romdata()[0x147])) { - int length = memptrs.rambankdataend() - memptrs.rambankdata(); - std::memcpy(dest, memptrs.rambankdata(), length); - dest += length; - } - - if (hasRtc(memptrs.romdata()[0x147])) { - const unsigned long basetime = rtc.getBaseTime(); - std::memcpy(dest, &basetime, 4); - } -} - -bool Cartridge::getMemoryArea(int which, unsigned char **data, int *length) const { - if (!data || !length) - return false; - - switch (which) - { - case 0: - *data = memptrs.vramdata(); - *length = memptrs.vramdataend() - memptrs.vramdata(); - return true; - case 1: - *data = memptrs.romdata(); - *length = memptrs.romdataend() - memptrs.romdata(); - return true; - case 2: - *data = memptrs.wramdata(0); - *length = memptrs.wramdataend() - memptrs.wramdata(0); - return true; - case 3: - *data = memptrs.rambankdata(); - *length = memptrs.rambankdataend() - memptrs.rambankdata(); - return true; - - default: - return false; - } - return false; -} - +}; + +static bool hasRtc(const unsigned headerByte0x147) { + switch (headerByte0x147) { + case 0x0F: + case 0x10: return true; + default: return false; + } +} + +} + +void Cartridge::setStatePtrs(SaveState &state) { + state.mem.vram.set(memptrs.vramdata(), memptrs.vramdataend() - memptrs.vramdata()); + state.mem.sram.set(memptrs.rambankdata(), memptrs.rambankdataend() - memptrs.rambankdata()); + state.mem.wram.set(memptrs.wramdata(0), memptrs.wramdataend() - memptrs.wramdata(0)); + +} + +void Cartridge::loadState(const SaveState &state) { + rtc.loadState(state); + mbc->loadState(state.mem); + //if (state.mem.using_bios) { + // bios_remap(0); + //} +} + +static void enforce8bit(unsigned char *data, unsigned long sz) { + if (static_cast(0x100)) + while (sz--) + *data++ &= 0xFF; +} + +static unsigned pow2ceil(unsigned n) { + --n; + n |= n >> 1; + n |= n >> 2; + n |= n >> 4; + n |= n >> 8; + ++n; + + return n; +} + +void Cartridge::bios_remap(int setting) { + // disable the BIOS if writing 1 or 0x22 (GBC) + if (setting == 1 || setting == 0x11) { + std::memcpy(memptrs.romdata(), memptrs.notbiosdata_, loc_bios_length); + using_bios = false; + } + + // we'll also use it to reset to BIOS on reset + if (setting == 0) { + std::memcpy(memptrs.romdata(), memptrs.biosdata_, loc_bios_length); + using_bios = true; + } +} + +int Cartridge::loadROM(const char *romfiledata, unsigned romfilelength, const char *biosfiledata, unsigned biosfilelength, const bool forceDmg, const bool multicartCompat) { + //const std::auto_ptr rom(newFileInstance(romfile)); + + //if (rom->fail()) + // return -1; + + unsigned rambanks = 1; + unsigned rombanks = 2; + bool cgb = false; + enum Cartridgetype { PLAIN, MBC1, MBC2, MBC3, MBC5, HUC1 } type = PLAIN; + + { + unsigned char header[0x150]; + //rom->read(reinterpret_cast(header), sizeof header); + if (romfilelength >= sizeof header) + std::memcpy(header, romfiledata, sizeof header); + else + return -1; + + switch (header[0x0147]) { + case 0x00: std::puts("Plain ROM loaded."); type = PLAIN; break; + case 0x01: std::puts("MBC1 ROM loaded."); type = MBC1; break; + case 0x02: std::puts("MBC1 ROM+RAM loaded."); type = MBC1; break; + case 0x03: std::puts("MBC1 ROM+RAM+BATTERY loaded."); type = MBC1; break; + case 0x05: std::puts("MBC2 ROM loaded."); type = MBC2; break; + case 0x06: std::puts("MBC2 ROM+BATTERY loaded."); type = MBC2; break; + case 0x08: std::puts("Plain ROM with additional RAM loaded."); type = PLAIN; break; + case 0x09: std::puts("Plain ROM with additional RAM and Battery loaded."); type = PLAIN; break; + case 0x0B: std::puts("MM01 ROM not supported."); return -1; + case 0x0C: std::puts("MM01 ROM not supported."); return -1; + case 0x0D: std::puts("MM01 ROM not supported."); return -1; + case 0x0F: std::puts("MBC3 ROM+TIMER+BATTERY loaded."); type = MBC3; break; + case 0x10: std::puts("MBC3 ROM+TIMER+RAM+BATTERY loaded."); type = MBC3; break; + case 0x11: std::puts("MBC3 ROM loaded."); type = MBC3; break; + case 0x12: std::puts("MBC3 ROM+RAM loaded."); type = MBC3; break; + case 0x13: std::puts("MBC3 ROM+RAM+BATTERY loaded."); type = MBC3; break; + case 0x15: std::puts("MBC4 ROM not supported."); return -1; + case 0x16: std::puts("MBC4 ROM not supported."); return -1; + case 0x17: std::puts("MBC4 ROM not supported."); return -1; + case 0x19: std::puts("MBC5 ROM loaded."); type = MBC5; break; + case 0x1A: std::puts("MBC5 ROM+RAM loaded."); type = MBC5; break; + case 0x1B: std::puts("MBC5 ROM+RAM+BATTERY loaded."); type = MBC5; break; + case 0x1C: std::puts("MBC5+RUMBLE ROM not supported."); type = MBC5; break; + case 0x1D: std::puts("MBC5+RUMBLE+RAM ROM not suported."); type = MBC5; break; + case 0x1E: std::puts("MBC5+RUMBLE+RAM+BATTERY ROM not supported."); type = MBC5; break; + case 0xFC: std::puts("Pocket Camera ROM not supported."); return -1; + case 0xFD: std::puts("Bandai TAMA5 ROM not supported."); return -1; + case 0xFE: std::puts("HuC3 ROM not supported."); return -1; + case 0xFF: std::puts("HuC1 ROM+RAM+BATTERY loaded."); type = HUC1; break; + default: std::puts("Wrong data-format, corrupt or unsupported ROM."); return -1; + } + + /*switch (header[0x0148]) { + case 0x00: rombanks = 2; break; + case 0x01: rombanks = 4; break; + case 0x02: rombanks = 8; break; + case 0x03: rombanks = 16; break; + case 0x04: rombanks = 32; break; + case 0x05: rombanks = 64; break; + case 0x06: rombanks = 128; break; + case 0x07: rombanks = 256; break; + case 0x08: rombanks = 512; break; + case 0x52: rombanks = 72; break; + case 0x53: rombanks = 80; break; + case 0x54: rombanks = 96; break; + default: return -1; + } + + std::printf("rombanks: %u\n", rombanks);*/ + + switch (header[0x0149]) { + case 0x00: /*std::puts("No RAM");*/ rambanks = type == MBC2; break; + case 0x01: /*std::puts("2kB RAM");*/ /*rambankrom=1; break;*/ + case 0x02: /*std::puts("8kB RAM");*/ + rambanks = 1; + break; + case 0x03: /*std::puts("32kB RAM");*/ + rambanks = 4; + break; + case 0x04: /*std::puts("128kB RAM");*/ + rambanks = 16; + break; + case 0x05: /*std::puts("undocumented kB RAM");*/ + rambanks = 16; + break; + default: /*std::puts("Wrong data-format, corrupt or unsupported ROM loaded.");*/ + rambanks = 16; + break; + } + + cgb = header[0x0143] >> 7 & (1 ^ forceDmg); + std::printf("cgb: %d\n", cgb); + } + + std::printf("rambanks: %u\n", rambanks); + + const std::size_t filesize = romfilelength; //rom->size(); + rombanks = std::max(pow2ceil(filesize / 0x4000), 2u); + std::printf("rombanks: %u\n", static_cast(filesize / 0x4000)); + + mbc.reset(); + + use_bios = biosfilelength > 0 ? true : false; + loc_bios_length = biosfilelength; + + memptrs.reset(rombanks, rambanks, cgb ? 8 : 2); + rtc.set(false, 0); + + //rom->rewind(); + //rom->read(reinterpret_cast(memptrs.romdata()), (filesize / 0x4000) * 0x4000ul); + + std::memcpy(memptrs.romdata(), romfiledata, (filesize / 0x4000) * 0x4000ul); + std::memset(memptrs.romdata() + (filesize / 0x4000) * 0x4000ul, 0xFF, (rombanks - filesize / 0x4000) * 0x4000ul); + enforce8bit(memptrs.romdata(), rombanks * 0x4000ul); + + //we want to copy in the bios data only if it exists + if (use_bios) { + using_bios = true; + memptrs.use_bios = true; + + memptrs.biosdata_ = new unsigned char[biosfilelength]; + memptrs.notbiosdata_ = new unsigned char[biosfilelength]; + + std::memcpy(memptrs.biosdata_, biosfiledata, biosfilelength); + std::memcpy(memptrs.notbiosdata_, romfiledata, biosfilelength); + + //if using GBC, the header is not overwritten by the BIOS + if (biosfilelength > 256) { + std::memcpy(memptrs.biosdata_ + 256, memptrs.notbiosdata_ + 256, 256); + } + + + std::memcpy(memptrs.romdata(), memptrs.biosdata_, biosfilelength); + } + + //if (rom->fail()) + // return -1; + + switch (type) { + case PLAIN: mbc.reset(new Mbc0(memptrs)); break; + case MBC1: + if (!rambanks && rombanks == 64 && multicartCompat) { + std::puts("Multi-ROM \"MBC1\" presumed"); + mbc.reset(new Mbc1Multi64(memptrs)); + } else + mbc.reset(new Mbc1(memptrs)); + + break; + case MBC2: mbc.reset(new Mbc2(memptrs)); break; + case MBC3: mbc.reset(new Mbc3(memptrs, hasRtc(memptrs.romdata()[0x147]) ? &rtc : 0)); break; + case MBC5: mbc.reset(new Mbc5(memptrs)); break; + case HUC1: mbc.reset(new HuC1(memptrs)); break; + } + + return 0; +} + +static bool hasBattery(const unsigned char headerByte0x147) { + switch (headerByte0x147) { + case 0x03: + case 0x06: + case 0x09: + case 0x0F: + case 0x10: + case 0x13: + case 0x1B: + case 0x1E: + case 0xFF: return true; + default: return false; + } +} + +void Cartridge::loadSavedata(const char *data) { + if (hasBattery(memptrs.romdata()[0x147])) { + int length = memptrs.rambankdataend() - memptrs.rambankdata(); + std::memcpy(memptrs.rambankdata(), data, length); + data += length; + enforce8bit(memptrs.rambankdata(), length); + } + + if (hasRtc(memptrs.romdata()[0x147])) { + unsigned long basetime; + std::memcpy(&basetime, data, 4); + rtc.setBaseTime(basetime); + } +} + +int Cartridge::saveSavedataLength() { + int ret = 0; + if (hasBattery(memptrs.romdata()[0x147])) { + ret = memptrs.rambankdataend() - memptrs.rambankdata(); + } + if (hasRtc(memptrs.romdata()[0x147])) { + ret += 4; + } + return ret; +} + +void Cartridge::saveSavedata(char *dest) { + if (hasBattery(memptrs.romdata()[0x147])) { + int length = memptrs.rambankdataend() - memptrs.rambankdata(); + std::memcpy(dest, memptrs.rambankdata(), length); + dest += length; + } + + if (hasRtc(memptrs.romdata()[0x147])) { + const unsigned long basetime = rtc.getBaseTime(); + std::memcpy(dest, &basetime, 4); + } +} + +bool Cartridge::getMemoryArea(int which, unsigned char **data, int *length) const { + if (!data || !length) + return false; + + switch (which) + { + case 0: + *data = memptrs.vramdata(); + *length = memptrs.vramdataend() - memptrs.vramdata(); + return true; + case 1: + *data = memptrs.romdata(); + *length = memptrs.romdataend() - memptrs.romdata(); + return true; + case 2: + *data = memptrs.wramdata(0); + *length = memptrs.wramdataend() - memptrs.wramdata(0); + return true; + case 3: + *data = memptrs.rambankdata(); + *length = memptrs.rambankdataend() - memptrs.rambankdata(); + return true; + + default: + return false; + } + return false; +} + SYNCFUNC(Cartridge) { SSS(memptrs); SSS(rtc); TSS(mbc); + NSS(using_bios); + + if (using_bios) { + bios_remap(0); + } +} + } - -} diff --git a/libgambatte/src/mem/cartridge.h b/libgambatte/src/mem/cartridge.h index 54f694df31..c3a3d7ce27 100644 --- a/libgambatte/src/mem/cartridge.h +++ b/libgambatte/src/mem/cartridge.h @@ -66,6 +66,10 @@ class Cartridge { public: void setStatePtrs(SaveState &); void loadState(const SaveState &); + + bool use_bios; + bool using_bios; + unsigned loc_bios_length; bool loaded() const { return mbc.get(); } @@ -97,13 +101,15 @@ public: bool getMemoryArea(int which, unsigned char **data, int *length) const; - int loadROM(const char *romfiledata, unsigned romfilelength, bool forceDmg, bool multicartCompat); + int loadROM(const char *romfiledata, unsigned romfilelength, const char *biosfiledata, unsigned biosfilelength, bool forceDmg, bool multicartCompat); const char * romTitle() const { return reinterpret_cast(memptrs.romdata() + 0x134); } void setRTCCallback(std::uint32_t (*callback)()) { rtc.setRTCCallback(callback); } + void bios_remap(int setting); + templatevoid SyncState(NewState *ns); }; diff --git a/libgambatte/src/mem/memptrs.cpp b/libgambatte/src/mem/memptrs.cpp index 6e8b2fe644..4ddf5e0149 100644 --- a/libgambatte/src/mem/memptrs.cpp +++ b/libgambatte/src/mem/memptrs.cpp @@ -1,151 +1,162 @@ -/*************************************************************************** - * Copyright (C) 2007-2010 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 "memptrs.h" -#include -#include - -namespace gambatte { - -MemPtrs::MemPtrs() -: rmem_(), wmem_(), romdata_(), wramdata_(), vrambankptr_(0), rsrambankptr_(0), - wsrambankptr_(0), memchunk_(0), rambankdata_(0), wramdataend_(0), oamDmaSrc_(OAM_DMA_SRC_OFF), - memchunk_len(0) -{ -} - -MemPtrs::~MemPtrs() { - delete []memchunk_; -} - -void MemPtrs::reset(const unsigned rombanks, const unsigned rambanks, const unsigned wrambanks) { - delete []memchunk_; - memchunk_len = 0x4000 + rombanks * 0x4000ul + 0x4000 + rambanks * 0x2000ul + wrambanks * 0x1000ul + 0x4000; - memchunk_ = new unsigned char[memchunk_len]; - - romdata_[0] = romdata(); - rambankdata_ = romdata_[0] + rombanks * 0x4000ul + 0x4000; - wramdata_[0] = rambankdata_ + rambanks * 0x2000ul; - wramdataend_ = wramdata_[0] + wrambanks * 0x1000ul; - - std::memset(rdisabledRamw(), 0xFF, 0x2000); - - oamDmaSrc_ = OAM_DMA_SRC_OFF; - rmem_[0x3] = rmem_[0x2] = rmem_[0x1] = rmem_[0x0] = romdata_[0]; - rmem_[0xC] = wmem_[0xC] = wramdata_[0] - 0xC000; - rmem_[0xE] = wmem_[0xE] = wramdata_[0] - 0xE000; - setRombank(1); - setRambank(0, 0); - setVrambank(0); - setWrambank(1); - - // we save only the ram areas - memchunk_saveoffs = vramdata() - memchunk_; - memchunk_savelen = wramdataend() - memchunk_ - memchunk_saveoffs; -} - -void MemPtrs::setRombank0(const unsigned bank) { - romdata_[0] = romdata() + bank * 0x4000ul; - rmem_[0x3] = rmem_[0x2] = rmem_[0x1] = rmem_[0x0] = romdata_[0]; - disconnectOamDmaAreas(); -} - -void MemPtrs::setRombank(const unsigned bank) { - romdata_[1] = romdata() + bank * 0x4000ul - 0x4000; - rmem_[0x7] = rmem_[0x6] = rmem_[0x5] = rmem_[0x4] = romdata_[1]; - disconnectOamDmaAreas(); -} - -void MemPtrs::setRambank(const unsigned flags, const unsigned rambank) { - unsigned char *const srambankptr = flags & RTC_EN - ? 0 - : (rambankdata() != rambankdataend() - ? rambankdata_ + rambank * 0x2000ul - 0xA000 : wdisabledRam() - 0xA000); - - rsrambankptr_ = (flags & READ_EN) && srambankptr != wdisabledRam() - 0xA000 ? srambankptr : rdisabledRamw() - 0xA000; - wsrambankptr_ = flags & WRITE_EN ? srambankptr : wdisabledRam() - 0xA000; - rmem_[0xB] = rmem_[0xA] = rsrambankptr_; - wmem_[0xB] = wmem_[0xA] = wsrambankptr_; - disconnectOamDmaAreas(); -} - -void MemPtrs::setWrambank(const unsigned bank) { - wramdata_[1] = wramdata_[0] + ((bank & 0x07) ? (bank & 0x07) : 1) * 0x1000; - rmem_[0xD] = wmem_[0xD] = wramdata_[1] - 0xD000; - disconnectOamDmaAreas(); -} - -void MemPtrs::setOamDmaSrc(const OamDmaSrc oamDmaSrc) { - rmem_[0x3] = rmem_[0x2] = rmem_[0x1] = rmem_[0x0] = romdata_[0]; - rmem_[0x7] = rmem_[0x6] = rmem_[0x5] = rmem_[0x4] = romdata_[1]; - rmem_[0xB] = rmem_[0xA] = rsrambankptr_; - wmem_[0xB] = wmem_[0xA] = wsrambankptr_; - rmem_[0xC] = wmem_[0xC] = wramdata_[0] - 0xC000; - rmem_[0xD] = wmem_[0xD] = wramdata_[1] - 0xD000; - rmem_[0xE] = wmem_[0xE] = wramdata_[0] - 0xE000; - - oamDmaSrc_ = oamDmaSrc; - disconnectOamDmaAreas(); -} - -void MemPtrs::disconnectOamDmaAreas() { - if (isCgb(*this)) { - switch (oamDmaSrc_) { - case OAM_DMA_SRC_ROM: // fall through - case OAM_DMA_SRC_SRAM: - case OAM_DMA_SRC_INVALID: - std::fill(rmem_, rmem_ + 8, static_cast(0)); - rmem_[0xB] = rmem_[0xA] = 0; - wmem_[0xB] = wmem_[0xA] = 0; - break; - case OAM_DMA_SRC_VRAM: - break; - case OAM_DMA_SRC_WRAM: - rmem_[0xE] = rmem_[0xD] = rmem_[0xC] = 0; - wmem_[0xE] = wmem_[0xD] = wmem_[0xC] = 0; - break; - case OAM_DMA_SRC_OFF: - break; - } - } else { - switch (oamDmaSrc_) { - case OAM_DMA_SRC_ROM: // fall through - case OAM_DMA_SRC_SRAM: - case OAM_DMA_SRC_WRAM: - case OAM_DMA_SRC_INVALID: - std::fill(rmem_, rmem_ + 8, static_cast(0)); - rmem_[0xB] = rmem_[0xA] = 0; - wmem_[0xB] = wmem_[0xA] = 0; - rmem_[0xE] = rmem_[0xD] = rmem_[0xC] = 0; - wmem_[0xE] = wmem_[0xD] = wmem_[0xC] = 0; - break; - case OAM_DMA_SRC_VRAM: - break; - case OAM_DMA_SRC_OFF: - break; - } - } -} - -// all pointers here are relative to memchunk_ -#define MSS(a) RSS(a,memchunk_) -#define MSL(a) RSL(a,memchunk_) - +/*************************************************************************** + * Copyright (C) 2007-2010 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 "memptrs.h" +#include +#include + +namespace gambatte { + +MemPtrs::MemPtrs() +: rmem_(), wmem_(), romdata_(), wramdata_(), vrambankptr_(0), rsrambankptr_(0), + wsrambankptr_(0), memchunk_(0), rambankdata_(0), wramdataend_(0), oamDmaSrc_(OAM_DMA_SRC_OFF), + memchunk_len(0) +{ +} + +MemPtrs::~MemPtrs() { + delete []memchunk_; + if (use_bios) + { + delete[]biosdata_; + delete[]notbiosdata_; + } +} + +void MemPtrs::reset(const unsigned rombanks, const unsigned rambanks, const unsigned wrambanks) { + delete []memchunk_; + + memchunk_len = 0x4000 + rombanks * 0x4000ul + 0x4000 + rambanks * 0x2000ul + wrambanks * 0x1000ul + 0x4000; + memchunk_ = new unsigned char[memchunk_len]; + + romdata_[0] = romdata(); + + rambankdata_ = romdata_[0] + rombanks * 0x4000ul + 0x4000; + wramdata_[0] = rambankdata_ + rambanks * 0x2000ul; + wramdataend_ = wramdata_[0] + wrambanks * 0x1000ul; + + std::memset(rdisabledRamw(), 0xFF, 0x2000); + + oamDmaSrc_ = OAM_DMA_SRC_OFF; + rmem_[0x3] = rmem_[0x2] = rmem_[0x1] = rmem_[0x0] = romdata_[0]; + rmem_[0xC] = wmem_[0xC] = wramdata_[0] - 0xC000; + rmem_[0xE] = wmem_[0xE] = wramdata_[0] - 0xE000; + setRombank(1); + setRambank(0, 0); + setVrambank(0); + setWrambank(1); + + // we save only the ram areas + memchunk_saveoffs = vramdata() - memchunk_; + memchunk_savelen = wramdataend() - memchunk_ - memchunk_saveoffs; +} + +void MemPtrs::setRombank0(const unsigned bank) { + + romdata_[0] = romdata() + bank * 0x4000ul; + + rmem_[0x3] = rmem_[0x2] = rmem_[0x1] = rmem_[0x0] = romdata_[0]; + disconnectOamDmaAreas(); +} + +void MemPtrs::setRombank(const unsigned bank) { + + romdata_[1] = romdata() + bank * 0x4000ul - 0x4000; + + rmem_[0x7] = rmem_[0x6] = rmem_[0x5] = rmem_[0x4] = romdata_[1]; + disconnectOamDmaAreas(); +} + +void MemPtrs::setRambank(const unsigned flags, const unsigned rambank) { + unsigned char *const srambankptr = flags & RTC_EN + ? 0 + : (rambankdata() != rambankdataend() + ? rambankdata_ + rambank * 0x2000ul - 0xA000 : wdisabledRam() - 0xA000); + + rsrambankptr_ = (flags & READ_EN) && srambankptr != wdisabledRam() - 0xA000 ? srambankptr : rdisabledRamw() - 0xA000; + wsrambankptr_ = flags & WRITE_EN ? srambankptr : wdisabledRam() - 0xA000; + rmem_[0xB] = rmem_[0xA] = rsrambankptr_; + wmem_[0xB] = wmem_[0xA] = wsrambankptr_; + disconnectOamDmaAreas(); +} + +void MemPtrs::setWrambank(const unsigned bank) { + wramdata_[1] = wramdata_[0] + ((bank & 0x07) ? (bank & 0x07) : 1) * 0x1000; + rmem_[0xD] = wmem_[0xD] = wramdata_[1] - 0xD000; + disconnectOamDmaAreas(); +} + +void MemPtrs::setOamDmaSrc(const OamDmaSrc oamDmaSrc) { + rmem_[0x3] = rmem_[0x2] = rmem_[0x1] = rmem_[0x0] = romdata_[0]; + rmem_[0x7] = rmem_[0x6] = rmem_[0x5] = rmem_[0x4] = romdata_[1]; + rmem_[0xB] = rmem_[0xA] = rsrambankptr_; + wmem_[0xB] = wmem_[0xA] = wsrambankptr_; + rmem_[0xC] = wmem_[0xC] = wramdata_[0] - 0xC000; + rmem_[0xD] = wmem_[0xD] = wramdata_[1] - 0xD000; + rmem_[0xE] = wmem_[0xE] = wramdata_[0] - 0xE000; + + oamDmaSrc_ = oamDmaSrc; + disconnectOamDmaAreas(); +} + +void MemPtrs::disconnectOamDmaAreas() { + if (isCgb(*this)) { + switch (oamDmaSrc_) { + case OAM_DMA_SRC_ROM: // fall through + case OAM_DMA_SRC_SRAM: + case OAM_DMA_SRC_INVALID: + std::fill(rmem_, rmem_ + 8, static_cast(0)); + rmem_[0xB] = rmem_[0xA] = 0; + wmem_[0xB] = wmem_[0xA] = 0; + break; + case OAM_DMA_SRC_VRAM: + break; + case OAM_DMA_SRC_WRAM: + rmem_[0xE] = rmem_[0xD] = rmem_[0xC] = 0; + wmem_[0xE] = wmem_[0xD] = wmem_[0xC] = 0; + break; + case OAM_DMA_SRC_OFF: + break; + } + } else { + switch (oamDmaSrc_) { + case OAM_DMA_SRC_ROM: // fall through + case OAM_DMA_SRC_SRAM: + case OAM_DMA_SRC_WRAM: + case OAM_DMA_SRC_INVALID: + std::fill(rmem_, rmem_ + 8, static_cast(0)); + rmem_[0xB] = rmem_[0xA] = 0; + wmem_[0xB] = wmem_[0xA] = 0; + rmem_[0xE] = rmem_[0xD] = rmem_[0xC] = 0; + wmem_[0xE] = wmem_[0xD] = wmem_[0xC] = 0; + break; + case OAM_DMA_SRC_VRAM: + break; + case OAM_DMA_SRC_OFF: + break; + } + } +} + +// all pointers here are relative to memchunk_ +#define MSS(a) RSS(a,memchunk_) +#define MSL(a) RSL(a,memchunk_) + SYNCFUNC(MemPtrs) { /* @@ -215,6 +226,10 @@ SYNCFUNC(MemPtrs) MSS(rambankdata_); MSS(wramdataend_); NSS(oamDmaSrc_); + + NSS(biosdata_); + NSS(notbiosdata_); + NSS(use_bios); +} + } - -} diff --git a/libgambatte/src/mem/memptrs.h b/libgambatte/src/mem/memptrs.h index af21c02a15..3c63670907 100644 --- a/libgambatte/src/mem/memptrs.h +++ b/libgambatte/src/mem/memptrs.h @@ -1,93 +1,97 @@ -/*************************************************************************** - * Copyright (C) 2007-2010 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 MEMPTRS_H -#define MEMPTRS_H - +/*************************************************************************** + * Copyright (C) 2007-2010 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 MEMPTRS_H +#define MEMPTRS_H + #include "newstate.h" - -namespace gambatte { - -enum OamDmaSrc { OAM_DMA_SRC_ROM, OAM_DMA_SRC_SRAM, OAM_DMA_SRC_VRAM, - OAM_DMA_SRC_WRAM, OAM_DMA_SRC_INVALID, OAM_DMA_SRC_OFF }; - -class MemPtrs { - const unsigned char *rmem_[0x10]; - unsigned char *wmem_[0x10]; - - unsigned char *romdata_[2]; - unsigned char *wramdata_[2]; - unsigned char *vrambankptr_; - unsigned char *rsrambankptr_; - unsigned char *wsrambankptr_; - unsigned char *memchunk_; - unsigned char *rambankdata_; - unsigned char *wramdataend_; - - OamDmaSrc oamDmaSrc_; - - int memchunk_len; - int memchunk_saveoffs; - int memchunk_savelen; - - MemPtrs(const MemPtrs &); - MemPtrs & operator=(const MemPtrs &); - void disconnectOamDmaAreas(); - unsigned char * rdisabledRamw() const { return wramdataend_ ; } - unsigned char * wdisabledRam() const { return wramdataend_ + 0x2000; } -public: - enum RamFlag { READ_EN = 1, WRITE_EN = 2, RTC_EN = 4 }; - - MemPtrs(); - ~MemPtrs(); - void reset(unsigned rombanks, unsigned rambanks, unsigned wrambanks); - - const unsigned char * rmem(unsigned area) const { return rmem_[area]; } - unsigned char * wmem(unsigned area) const { return wmem_[area]; } - unsigned char * vramdata() const { return rambankdata_ - 0x4000; } - unsigned char * vramdataend() const { return rambankdata_; } - unsigned char * romdata() const { return memchunk_ + 0x4000; } - unsigned char * romdata(unsigned area) const { return romdata_[area]; } - unsigned char * romdataend() const { return rambankdata_ - 0x4000; } - unsigned char * wramdata(unsigned area) const { return wramdata_[area]; } - unsigned char * wramdataend() const { return wramdataend_; } - unsigned char * rambankdata() const { return rambankdata_; } - unsigned char * rambankdataend() const { return wramdata_[0]; } - const unsigned char * rdisabledRam() const { return rdisabledRamw(); } - const unsigned char * rsrambankptr() const { return rsrambankptr_; } - unsigned char * wsrambankptr() const { return wsrambankptr_; } - unsigned char * vrambankptr() const { return vrambankptr_; } - OamDmaSrc oamDmaSrc() const { return oamDmaSrc_; } - - void setRombank0(unsigned bank); - void setRombank(unsigned bank); - void setRambank(unsigned ramFlags, unsigned rambank); - void setVrambank(unsigned bank) { vrambankptr_ = vramdata() + bank * 0x2000ul - 0x8000; } - void setWrambank(unsigned bank); - void setOamDmaSrc(OamDmaSrc oamDmaSrc); - + +namespace gambatte { + +enum OamDmaSrc { OAM_DMA_SRC_ROM, OAM_DMA_SRC_SRAM, OAM_DMA_SRC_VRAM, + OAM_DMA_SRC_WRAM, OAM_DMA_SRC_INVALID, OAM_DMA_SRC_OFF }; + +class MemPtrs { + const unsigned char *rmem_[0x10]; + unsigned char *wmem_[0x10]; + + unsigned char *romdata_[2]; + unsigned char *wramdata_[2]; + unsigned char *vrambankptr_; + unsigned char *rsrambankptr_; + unsigned char *wsrambankptr_; + unsigned char *memchunk_; + unsigned char *rambankdata_; + unsigned char *wramdataend_; + + OamDmaSrc oamDmaSrc_; + + int memchunk_len; + int memchunk_saveoffs; + int memchunk_savelen; + + MemPtrs(const MemPtrs &); + MemPtrs & operator=(const MemPtrs &); + void disconnectOamDmaAreas(); + unsigned char * rdisabledRamw() const { return wramdataend_ ; } + unsigned char * wdisabledRam() const { return wramdataend_ + 0x2000; } +public: + unsigned char *biosdata_; + unsigned char *notbiosdata_; + bool use_bios; + + enum RamFlag { READ_EN = 1, WRITE_EN = 2, RTC_EN = 4 }; + + MemPtrs(); + ~MemPtrs(); + void reset(unsigned rombanks, unsigned rambanks, unsigned wrambanks); + + const unsigned char * rmem(unsigned area) const { return rmem_[area]; } + unsigned char * wmem(unsigned area) const { return wmem_[area]; } + unsigned char * vramdata() const { return rambankdata_ - 0x4000; } + unsigned char * vramdataend() const { return rambankdata_; } + unsigned char * romdata() const { return memchunk_ + 0x4000;} + unsigned char * romdata(unsigned area) const { return romdata_[area]; } + unsigned char * romdataend() const { return rambankdata_ - 0x4000; } + unsigned char * wramdata(unsigned area) const { return wramdata_[area]; } + unsigned char * wramdataend() const { return wramdataend_; } + unsigned char * rambankdata() const { return rambankdata_; } + unsigned char * rambankdataend() const { return wramdata_[0]; } + const unsigned char * rdisabledRam() const { return rdisabledRamw(); } + const unsigned char * rsrambankptr() const { return rsrambankptr_; } + unsigned char * wsrambankptr() const { return wsrambankptr_; } + unsigned char * vrambankptr() const { return vrambankptr_; } + OamDmaSrc oamDmaSrc() const { return oamDmaSrc_; } + + void setRombank0(unsigned bank); + void setRombank(unsigned bank); + void setRambank(unsigned ramFlags, unsigned rambank); + void setVrambank(unsigned bank) { vrambankptr_ = vramdata() + bank * 0x2000ul - 0x8000; } + void setWrambank(unsigned bank); + void setOamDmaSrc(OamDmaSrc oamDmaSrc); + templatevoid SyncState(NewState *ns); -}; - -inline bool isCgb(const MemPtrs &memptrs) { - return memptrs.wramdataend() - memptrs.wramdata(0) == 0x8000; -} - -} - -#endif +}; + +inline bool isCgb(const MemPtrs &memptrs) { + return memptrs.wramdataend() - memptrs.wramdata(0) == 0x8000; +} + +} + +#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 - -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(144*456ul); - intreq.setEventTime(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(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(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(lastOamDmaUpdate + (oamEventPos - oamDmaPos) * 4); - } - - intreq.setEventTime((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(intreq.eventTime(BLIT) + (70224 << isDoubleSpeed())); - - intreq.setEventTime(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(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(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(DISABLED_TIME); - break; - case END: - intreq.setEventTime(DISABLED_TIME - 1); - - while (cycleCounter >= intreq.minEventTime() && intreq.eventTime(END) != DISABLED_TIME) - cycleCounter = event(cycleCounter); - - intreq.setEventTime(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(DISABLED_TIME); - intreq.setEventTime(DISABLED_TIME); - - while (cycleCounter >= intreq.minEventTime()) - cycleCounter = event(cycleCounter); - } else - blitTime += 70224 << isDoubleSpeed(); - - blanklcd = lcden ^ 1; - intreq.setEventTime(blitTime); - } - break; - case SERIAL: - updateSerial(cycleCounter); - break; - case OAM: - intreq.setEventTime(lastOamDmaUpdate == DISABLED_TIME ? - static_cast(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(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(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((ioamhram[0x140] & 0x80) ? display.nextMode1IrqTime() : cycleCounter + (70224 << isDoubleSpeed())); - - if (intreq.eventTime(END) > cycleCounter) { - intreq.setEventTime(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(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 + +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(144*456ul); + intreq.setEventTime(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(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(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(lastOamDmaUpdate + (oamEventPos - oamDmaPos) * 4); + } + + intreq.setEventTime((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(intreq.eventTime(BLIT) + (70224 << isDoubleSpeed())); + + intreq.setEventTime(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(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(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(DISABLED_TIME); + break; + case END: + intreq.setEventTime(DISABLED_TIME - 1); + + while (cycleCounter >= intreq.minEventTime() && intreq.eventTime(END) != DISABLED_TIME) + cycleCounter = event(cycleCounter); + + intreq.setEventTime(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(DISABLED_TIME); + intreq.setEventTime(DISABLED_TIME); + + while (cycleCounter >= intreq.minEventTime()) + cycleCounter = event(cycleCounter); + } else + blitTime += 70224 << isDoubleSpeed(); + + blanklcd = lcden ^ 1; + intreq.setEventTime(blitTime); + } + break; + case SERIAL: + updateSerial(cycleCounter); + break; + case OAM: + intreq.setEventTime(lastOamDmaUpdate == DISABLED_TIME ? + static_cast(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(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(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((ioamhram[0x140] & 0x80) ? display.nextMode1IrqTime() : cycleCounter + (70224 << isDoubleSpeed())); + + if (intreq.eventTime(END) > cycleCounter) { + intreq.setEventTime(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(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((data & 0x81) == 0x81 - ? (data & isCgb() * 2 ? (cycleCounter & ~0x7ul) + 0x10 * 8 : (cycleCounter & ~0xFFul) + 0x200 * 8) - : static_cast(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(display.nextMode1IrqTime() + (blanklcd ? 0 : 70224 << isDoubleSpeed())); - } else { - ioamhram[0x141] |= lyc; - intreq.setEventTime(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(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((data & 0x81) == 0x81 + ? (data & isCgb() * 2 ? (cycleCounter & ~0x7ul) + 0x10 * 8 : (cycleCounter & ~0xFFul) + 0x200 * 8) + : static_cast(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(display.nextMode1IrqTime() + (blanklcd ? 0 : 70224 << isDoubleSpeed())); + } else { + ioamhram[0x141] |= lyc; + intreq.setEventTime(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(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((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((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); + templatevoid SyncState(NewState *ns); -}; - -} - -#endif +}; + +} + +#endif diff --git a/libgambatte/src/savestate.h b/libgambatte/src/savestate.h index 4110161ca0..be3f21322a 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 { @@ -78,6 +78,7 @@ struct SaveState { bool enableRam; bool rambankMode; bool hdmaTransfer; + bool using_bios; } mem; struct PPU { diff --git a/output/dll/libgambatte.dll b/output/dll/libgambatte.dll index 87284591fd..ecd8fa7bbb 100644 Binary files a/output/dll/libgambatte.dll and b/output/dll/libgambatte.dll differ