diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs index 74412c7ff4..2e04142fdf 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs @@ -93,6 +93,25 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy throw new InvalidOperationException("gambatte_load() returned non-zero (is this not a gb or gbc rom?)"); } + if ((flags & LibGambatte.LoadFlags.FORCE_DMG) == LibGambatte.LoadFlags.FORCE_DMG) + { + byte[] Bios = comm.CoreFileProvider.GetFirmware("GB", "World", false, "BIOS Not Found, Cannot Load"); + + if (LibGambatte.gambatte_loaddmgbios(GambatteState, Bios) != 0) + { + throw new InvalidOperationException("gambatte_loaddmgbios() returned non-zero (bios error)"); + } + } + else + { + byte[] Bios = comm.CoreFileProvider.GetFirmware("GBC", "World", false, "BIOS Not Found, Cannot Load"); + + if (LibGambatte.gambatte_loadgbcbios(GambatteState, Bios) != 0) + { + throw new InvalidOperationException("gambatte_loadgbcbios() returned non-zero (bios error)"); + } + } + // set real default colors (before anyone mucks with them at all) PutSettings((GambatteSettings)settings ?? new GambatteSettings()); diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibGambatte.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibGambatte.cs index 3624bc9512..621f8c8998 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibGambatte.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibGambatte.cs @@ -59,6 +59,24 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy [DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)] public static extern int gambatte_load(IntPtr core, byte[] romdata, uint length, long now, LoadFlags flags); + /// + /// Load GB BIOS image. + /// + /// opaque state pointer + /// the bios data, can be disposed of once this function returns + /// 0 on success, negative value on failure. + [DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)] + public static extern int gambatte_loaddmgbios(IntPtr core, byte[] biosdata); + + /// + /// Load GBC BIOS image. + /// + /// opaque state pointer + /// the bios data, can be disposed of once this function returns + /// 0 on success, negative value on failure. + [DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)] + public static extern int gambatte_loadgbcbios(IntPtr core, byte[] biosdata); + /// /// 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/include/gambatte.h b/libgambatte/include/gambatte.h index 5f04191fe6..872bf73263 100644 --- a/libgambatte/include/gambatte.h +++ b/libgambatte/include/gambatte.h @@ -51,7 +51,8 @@ public: enum LoadFlag { FORCE_DMG = 1, /**< Treat the ROM as not having CGB support regardless of what its header advertises. */ GBA_CGB = 2, /**< Use GBA intial CPU register values when in CGB mode. */ - MULTICART_COMPAT = 4 /**< Use heuristics to detect and support some multicart MBCs disguised as MBC1. */ + MULTICART_COMPAT = 4, /**< Use heuristics to detect and support some multicart MBCs disguised as MBC1. */ + TRUE_COLOR = 8 /**< Use GBP color conversion instead of GBC-screen approximation */ }; /** Load ROM image. @@ -62,6 +63,9 @@ public: */ int load(const char *romfiledata, unsigned romfilelength, std::uint32_t now, unsigned flags = 0); + int loadGBCBios(const char* biosfiledata); + int loadDMGBios(const char* biosfiledata); + /** Emulates until at least 'samples' stereo sound samples are produced in the supplied buffer, * or until a video frame has been drawn. * @@ -109,7 +113,7 @@ public: void setTraceCallback(void (*callback)(void *)); void setScanlineCallback(void (*callback)(), int sl); void setRTCCallback(std::uint32_t (*callback)()); - void setLinkCallback(void (*callback)()); + void setLinkCallback(void(*callback)()); /** Returns true if the currently loaded ROM image is treated as having CGB support. */ bool isCgb() const; @@ -135,6 +139,9 @@ public: void GetRegs(int *dest); + void SetInterruptAddresses(int *addrs, int numAddrs); + int GetHitInterruptAddress(); + templatevoid SyncState(NewState *ns); private: diff --git a/libgambatte/libgambatte.vcxproj b/libgambatte/libgambatte.vcxproj index 4d34da2828..0af47e31bb 100644 --- a/libgambatte/libgambatte.vcxproj +++ b/libgambatte/libgambatte.vcxproj @@ -1,231 +1,222 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {5D630682-7BDA-474D-B387-0EB420DDC199} - Win32Proj - libgambatte - - - - DynamicLibrary - true - Unicode - v140 - - - DynamicLibrary - true - Unicode - v140 - - - DynamicLibrary - false - true - Unicode - v140 - - - DynamicLibrary - false - true - Unicode - v140 - - - - - - - - - - - - - - - - - - - true - - - true - - - false - - - false - - - - - - Level3 - Disabled - WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBGAMBATTE_EXPORTS;%(PreprocessorDefinitions) - include;src;src\common - 4244;4373;4800;4804 - - - Windows - true - - - copy /y $(TargetDir)$(TargetFileName) $(ProjectDir)..\output\dll\$(TargetFileName) - - - - - - - Level3 - Disabled - WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBGAMBATTE_EXPORTS;%(PreprocessorDefinitions) - include;src;src\common - 4244;4373;4800;4804 - - - Windows - true - - - copy /y $(TargetDir)$(TargetFileName) $(ProjectDir)..\output\dll\$(TargetFileName) - - - - - Level3 - - - MaxSpeed - true - true - WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBGAMBATTE_EXPORTS;%(PreprocessorDefinitions) - include;src;src\common - 4244;4373;4800;4804 - - - Windows - true - true - true - - - copy /y $(TargetDir)$(TargetFileName) $(ProjectDir)..\output\dll\$(TargetFileName) - - - - - Level3 - - - MaxSpeed - true - true - WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBGAMBATTE_EXPORTS;%(PreprocessorDefinitions) - include;src;src\common - 4244;4373;4800;4804 - - - Windows - true - true - true - - - copy /y $(TargetDir)$(TargetFileName) $(ProjectDir)..\output\dll\$(TargetFileName) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {5D630682-7BDA-474D-B387-0EB420DDC199} + Win32Proj + libgambatte + 8.1 + + + + DynamicLibrary + true + v140 + Unicode + + + DynamicLibrary + false + v140 + true + Unicode + + + DynamicLibrary + true + v140 + Unicode + + + DynamicLibrary + false + v140 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBGAMBATTE_EXPORTS;%(PreprocessorDefinitions) + include;src;src\common + 4244;4373;4800;4804 + + + Windows + true + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBGAMBATTE_EXPORTS;%(PreprocessorDefinitions) + include;src;src\common + 4244;4373;4800;4804 + + + Windows + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBGAMBATTE_EXPORTS;%(PreprocessorDefinitions) + include;src;src\common + 4244;4373;4800;4804 + + + Windows + true + true + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBGAMBATTE_EXPORTS;%(PreprocessorDefinitions) + include;src;src\common + 4244;4373;4800;4804 + + + Windows + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/libgambatte/src/cinterface.cpp b/libgambatte/src/cinterface.cpp index abb750b12b..f50658c23a 100644 --- a/libgambatte/src/cinterface.cpp +++ b/libgambatte/src/cinterface.cpp @@ -1,203 +1,225 @@ -#include "cinterface.h" -#include "gambatte.h" -#include -#include -#include "newstate.h" - -using namespace gambatte; - -// new is actually called in a few different places, so replace all of them for determinism guarantees -void *operator new(std::size_t n) -{ - void *p = std::malloc(n); - std::memset(p, 0, n); - return p; -} - -void operator delete(void *p) -{ - std::free(p); -} - -GBEXPORT GB *gambatte_create() -{ - return new GB(); -} - -GBEXPORT void gambatte_destroy(GB *g) -{ - delete g; -} - -GBEXPORT int gambatte_load(GB *g, const char *romfiledata, unsigned romfilelength, long long now, unsigned flags) -{ - int ret = g->load(romfiledata, romfilelength, now, flags); - return ret; -} - -GBEXPORT int gambatte_runfor(GB *g, short *soundbuf, unsigned *samples) -{ - unsigned sampv = *samples; - int ret = g->runFor((unsigned int *) soundbuf, sampv); - *samples = sampv; - return ret; -} - -GBEXPORT void gambatte_blitto(GB *g, unsigned int *videobuf, int pitch) -{ - g->blitTo((unsigned int *)videobuf, pitch); -} - -GBEXPORT void gambatte_setlayers(GB *g, unsigned mask) -{ - g->setLayers(mask); -} - -GBEXPORT void gambatte_reset(GB *g, long long now) -{ - g->reset(now); -} - -GBEXPORT void gambatte_setdmgpalettecolor(GB *g, unsigned palnum, unsigned colornum, unsigned rgb32) -{ - g->setDmgPaletteColor(palnum, colornum, rgb32); -} - -GBEXPORT void gambatte_setcgbpalette(GB *g, unsigned *lut) -{ - g->setCgbPalette(lut); -} - -GBEXPORT void gambatte_setinputgetter(GB *g, unsigned (*getinput)(void)) -{ - g->setInputGetter(getinput); -} - -GBEXPORT void gambatte_setreadcallback(GB *g, void (*callback)(unsigned)) -{ - g->setReadCallback(callback); -} - -GBEXPORT void gambatte_setwritecallback(GB *g, void (*callback)(unsigned)) -{ - g->setWriteCallback(callback); -} - -GBEXPORT void gambatte_setexeccallback(GB *g, void (*callback)(unsigned)) -{ - g->setExecCallback(callback); -} - -GBEXPORT void gambatte_setcdcallback(GB *g, CDCallback cdc) -{ - g->setCDCallback(cdc); -} - - -GBEXPORT void gambatte_settracecallback(GB *g, void (*callback)(void *)) -{ - g->setTraceCallback(callback); -} - -GBEXPORT void gambatte_setscanlinecallback(GB *g, void (*callback)(), int sl) -{ - g->setScanlineCallback(callback, sl); -} - -GBEXPORT void gambatte_setrtccallback(GB *g, unsigned int (*callback)()) -{ - g->setRTCCallback(callback); -} - -GBEXPORT void gambatte_setlinkcallback(GB *g, void (*callback)()) -{ - g->setLinkCallback(callback); -} - -GBEXPORT int gambatte_iscgb(GB *g) -{ - return g->isCgb(); -} - -GBEXPORT int gambatte_isloaded(GB *g) -{ - return g->isLoaded(); -} - -GBEXPORT void gambatte_savesavedata(GB *g, char *dest) -{ - g->saveSavedata(dest); -} - -GBEXPORT void gambatte_loadsavedata(GB *g, const char *data) -{ - g->loadSavedata(data); -} - -GBEXPORT int gambatte_savesavedatalength(GB *g) -{ - return g->saveSavedataLength(); -} - -GBEXPORT int gambatte_newstatelen(GB *g) -{ - NewStateDummy dummy; - g->SyncState(&dummy); - return dummy.GetLength(); -} - -GBEXPORT int gambatte_newstatesave(GB *g, char *data, int len) -{ - NewStateExternalBuffer saver(data, len); - g->SyncState(&saver); - return !saver.Overflow() && saver.GetLength() == len; -} - -GBEXPORT int gambatte_newstateload(GB *g, const char *data, int len) -{ - NewStateExternalBuffer loader((char *)data, len); - g->SyncState(&loader); - return !loader.Overflow() && loader.GetLength() == len; -} - -GBEXPORT void gambatte_newstatesave_ex(GB *g, FPtrs *ff) -{ - NewStateExternalFunctions saver(ff); - g->SyncState(&saver); -} - -GBEXPORT void gambatte_newstateload_ex(GB *g, FPtrs *ff) -{ - NewStateExternalFunctions loader(ff); - g->SyncState(&loader); -} - -GBEXPORT void gambatte_romtitle(GB *g, char *dest) -{ - std::strcpy(dest, g->romTitle().c_str()); -} - -GBEXPORT int gambatte_getmemoryarea(GB *g, int which, unsigned char **data, int *length) -{ - return g->getMemoryArea(which, data, length); -} - -GBEXPORT unsigned char gambatte_cpuread(GB *g, unsigned short addr) -{ - return g->ExternalRead(addr); -} - -GBEXPORT void gambatte_cpuwrite(GB *g, unsigned short addr, unsigned char val) -{ - g->ExternalWrite(addr, val); -} - -GBEXPORT int gambatte_linkstatus(GB *g, int which) -{ - return g->LinkStatus(which); -} - -GBEXPORT void gambatte_getregs(GB *g, int *dest) -{ - g->GetRegs(dest); -} +#include "cinterface.h" +#include "gambatte.h" +#include +#include +#include "newstate.h" + +using namespace gambatte; + +// new is actually called in a few different places, so replace all of them for determinism guarantees +void *operator new(std::size_t n) +{ + void *p = std::malloc(n); + std::memset(p, 0, n); + return p; +} + +void operator delete(void *p) +{ + std::free(p); +} + +GBEXPORT GB *gambatte_create() +{ + return new GB(); +} + +GBEXPORT void gambatte_destroy(GB *g) +{ + delete g; +} + +GBEXPORT int gambatte_load(GB *g, const char *romfiledata, unsigned romfilelength, long long now, unsigned flags) +{ + int ret = g->load(romfiledata, romfilelength, now, flags); + return ret; +} + +GBEXPORT int gambatte_loadgbcbios(GB* g, const char* biosfiledata) +{ + int ret = g->loadGBCBios(biosfiledata); + return ret; +} + +GBEXPORT int gambatte_loaddmgbios(GB* g, const char* biosfiledata) +{ + int ret = g->loadDMGBios(biosfiledata); + return ret; +} + +GBEXPORT int gambatte_runfor(GB *g, short *soundbuf, unsigned *samples) +{ + unsigned sampv = *samples; + int ret = g->runFor((unsigned int *) soundbuf, sampv); + *samples = sampv; + return ret; +} + +GBEXPORT void gambatte_blitto(GB *g, unsigned int *videobuf, int pitch) +{ + g->blitTo((unsigned int *)videobuf, pitch); +} + +GBEXPORT void gambatte_setlayers(GB *g, unsigned mask) +{ + g->setLayers(mask); +} + +GBEXPORT void gambatte_reset(GB *g, long long now) +{ + g->reset(now); +} + +GBEXPORT void gambatte_setdmgpalettecolor(GB *g, unsigned palnum, unsigned colornum, unsigned rgb32) +{ + //g->setDmgPaletteColor(palnum, colornum, rgb32); +} + +GBEXPORT void gambatte_setcgbpalette(GB *g, unsigned *lut) +{ + g->setCgbPalette(lut); +} + +GBEXPORT void gambatte_setinputgetter(GB *g, unsigned (*getinput)(void)) +{ + g->setInputGetter(getinput); +} + +GBEXPORT void gambatte_setreadcallback(GB *g, void (*callback)(unsigned)) +{ + g->setReadCallback(callback); +} + +GBEXPORT void gambatte_setwritecallback(GB *g, void (*callback)(unsigned)) +{ + g->setWriteCallback(callback); +} + +GBEXPORT void gambatte_setexeccallback(GB *g, void (*callback)(unsigned)) +{ + g->setExecCallback(callback); +} + +GBEXPORT void gambatte_setcdcallback(GB *g, CDCallback cdc) +{ + g->setCDCallback(cdc); +} + + +GBEXPORT void gambatte_settracecallback(GB *g, void (*callback)(void *)) +{ + g->setTraceCallback(callback); +} + +GBEXPORT void gambatte_setscanlinecallback(GB *g, void (*callback)(), int sl) +{ + g->setScanlineCallback(callback, sl); +} + +GBEXPORT void gambatte_setrtccallback(GB *g, unsigned int (*callback)()) +{ + g->setRTCCallback(callback); +} + +GBEXPORT void gambatte_setlinkcallback(GB *g, void(*callback)()) +{ + g->setLinkCallback(callback); +} + +GBEXPORT int gambatte_iscgb(GB *g) +{ + return g->isCgb(); +} + +GBEXPORT int gambatte_isloaded(GB *g) +{ + return g->isLoaded(); +} + +GBEXPORT void gambatte_savesavedata(GB *g, char *dest) +{ + g->saveSavedata(dest); +} + +GBEXPORT void gambatte_loadsavedata(GB *g, const char *data) +{ + g->loadSavedata(data); +} + +GBEXPORT int gambatte_savesavedatalength(GB *g) +{ + return g->saveSavedataLength(); +} + +GBEXPORT int gambatte_newstatelen(GB *g) +{ + NewStateDummy dummy; + g->SyncState(&dummy); + return dummy.GetLength(); +} + +GBEXPORT int gambatte_newstatesave(GB *g, char *data, int len) +{ + NewStateExternalBuffer saver(data, len); + g->SyncState(&saver); + return !saver.Overflow() && saver.GetLength() == len; +} + +GBEXPORT int gambatte_newstateload(GB *g, const char *data, int len) +{ + NewStateExternalBuffer loader((char *)data, len); + g->SyncState(&loader); + return !loader.Overflow() && loader.GetLength() == len; +} + +GBEXPORT void gambatte_newstatesave_ex(GB *g, FPtrs *ff) +{ + NewStateExternalFunctions saver(ff); + g->SyncState(&saver); +} + +GBEXPORT void gambatte_newstateload_ex(GB *g, FPtrs *ff) +{ + NewStateExternalFunctions loader(ff); + g->SyncState(&loader); +} + +GBEXPORT void gambatte_romtitle(GB *g, char *dest) +{ + std::strcpy(dest, g->romTitle().c_str()); +} + +GBEXPORT int gambatte_getmemoryarea(GB *g, int which, unsigned char **data, int *length) +{ + return g->getMemoryArea(which, data, length); +} + +GBEXPORT unsigned char gambatte_cpuread(GB *g, unsigned short addr) +{ + return g->ExternalRead(addr); +} + +GBEXPORT void gambatte_cpuwrite(GB *g, unsigned short addr, unsigned char val) +{ + g->ExternalWrite(addr, val); +} + +GBEXPORT int gambatte_linkstatus(GB *g, int which) +{ + return g->LinkStatus(which); +} + +GBEXPORT void gambatte_getregs(GB *g, int *dest) +{ + g->GetRegs(dest); +} + +GBEXPORT void gambatte_setinterruptaddresses(GB *g, int *addrs, int numAddrs) +{ + g->SetInterruptAddresses(addrs, numAddrs); +} + +GBEXPORT int gambatte_gethitinterruptaddress(GB *g) +{ + return g->GetHitInterruptAddress(); +} diff --git a/libgambatte/src/cinterface.h b/libgambatte/src/cinterface.h index 353991ab12..5e8a0087ec 100644 --- a/libgambatte/src/cinterface.h +++ b/libgambatte/src/cinterface.h @@ -1,8 +1,8 @@ -#ifndef CINTERFACE_H -#define CINTERFACE_H - -// these are all documented on the C# side - -#define GBEXPORT extern "C" __declspec(dllexport) - -#endif +#ifndef CINTERFACE_H +#define CINTERFACE_H + +// these are all documented on the C# side + +#define GBEXPORT extern "C" __declspec(dllexport) + +#endif diff --git a/libgambatte/src/cpu.cpp b/libgambatte/src/cpu.cpp index 88665f1989..0fcebe0ca6 100644 --- a/libgambatte/src/cpu.cpp +++ b/libgambatte/src/cpu.cpp @@ -1,2893 +1,2918 @@ -/*************************************************************************** - * 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 "cpu.h" -#include "memory.h" -#include "savestate.h" - -namespace gambatte { - -CPU::CPU() -: memory(Interrupter(SP, PC)), - cycleCounter_(0), - PC(0x100), - SP(0xFFFE), - HF1(0xF), - HF2(0xF), - ZF(0), - CF(0x100), - A(0x01), - B(0x00), - C(0x13), - D(0x00), - E(0xD8), - H(0x01), - L(0x4D), - skip(false), - tracecallback(0) -{ -} - -long CPU::runFor(const unsigned long cycles) { - process(cycles/* << memory.isDoubleSpeed()*/); - - const long csb = memory.cyclesSinceBlit(cycleCounter_); - - if (cycleCounter_ & 0x80000000) - cycleCounter_ = memory.resetCounters(cycleCounter_); - - return csb; -} - -// (HF2 & 0x200) == true means HF is set. -// (HF2 & 0x400) marks the subtract flag. -// (HF2 & 0x800) is set for inc/dec. -// (HF2 & 0x100) is set if there's a carry to add. -static void calcHF(const unsigned HF1, unsigned& HF2) { - unsigned arg1 = HF1 & 0xF; - unsigned arg2 = (HF2 & 0xF) + (HF2 >> 8 & 1); - - if (HF2 & 0x800) { - arg1 = arg2; - arg2 = 1; - } - - if (HF2 & 0x400) - arg1 -= arg2; - else - arg1 = (arg1 + arg2) << 5; - - HF2 |= arg1 & 0x200; -} - -#define F() (((HF2 & 0x600) | (CF & 0x100)) >> 4 | ((ZF & 0xFF) ? 0 : 0x80)) - -#define FROM_F(f_in) do { \ - unsigned from_f_var = f_in; \ -\ - ZF = ~from_f_var & 0x80; \ - HF2 = from_f_var << 4 & 0x600; \ - CF = from_f_var << 4 & 0x100; \ -} while (0) - -void CPU::setStatePtrs(SaveState &state) { - memory.setStatePtrs(state); -} - -void CPU::loadState(const SaveState &state) { - memory.loadState(state/*, cycleCounter_*/); - - cycleCounter_ = state.cpu.cycleCounter; - PC = state.cpu.PC & 0xFFFF; - SP = state.cpu.SP & 0xFFFF; - A = state.cpu.A & 0xFF; - B = state.cpu.B & 0xFF; - C = state.cpu.C & 0xFF; - D = state.cpu.D & 0xFF; - E = state.cpu.E & 0xFF; - FROM_F(state.cpu.F); - H = state.cpu.H & 0xFF; - L = state.cpu.L & 0xFF; - skip = state.cpu.skip; -} - -#define BC() ( B << 8 | C ) -#define DE() ( D << 8 | E ) -#define HL() ( H << 8 | L ) - -#define READ(dest, addr) do { (dest) = memory.read(addr, cycleCounter); cycleCounter += 4; } while (0) -// #define PC_READ(dest, addr) do { (dest) = memory.pc_read(addr, cycleCounter); cycleCounter += 4; } while (0) -#define PC_READ(dest) do { (dest) = memory.read_excb(PC, cycleCounter, false); PC = (PC + 1) & 0xFFFF; cycleCounter += 4; } while (0) -#define PC_READ_FIRST(dest) do { (dest) = memory.read_excb(PC, cycleCounter, true); PC = (PC + 1) & 0xFFFF; cycleCounter += 4; } while (0) -#define FF_READ(dest, addr) do { (dest) = memory.ff_read(addr, cycleCounter); cycleCounter += 4; } while (0) - -#define WRITE(addr, data) do { memory.write(addr, data, cycleCounter); cycleCounter += 4; } while (0) -#define FF_WRITE(addr, data) do { memory.ff_write(addr, data, cycleCounter); cycleCounter += 4; } while (0) - -#define PC_MOD(data) do { PC = data; cycleCounter += 4; } while (0) - -#define PUSH(r1, r2) do { \ - SP = (SP - 1) & 0xFFFF; \ - WRITE(SP, (r1)); \ - SP = (SP - 1) & 0xFFFF; \ - WRITE(SP, (r2)); \ -} while (0) - -//CB OPCODES (Shifts, rotates and bits): -//swap r (8 cycles): -//Swap upper and lower nibbles of 8-bit register, reset flags, check zero flag: -#define swap_r(r) do { \ - CF = HF2 = 0; \ - ZF = (r); \ - (r) = (ZF << 4 | ZF >> 4) & 0xFF; \ -} while (0) - -//rlc r (8 cycles): -//Rotate 8-bit register left, store old bit7 in CF. Reset SF and HCF, Check ZF: -#define rlc_r(r) do { \ - CF = (r) << 1; \ - ZF = CF | CF >> 8; \ - (r) = ZF & 0xFF; \ - HF2 = 0; \ -} while (0) - -//rl r (8 cycles): -//Rotate 8-bit register left through CF, store old bit7 in CF, old CF value becomes bit0. Reset SF and HCF, Check ZF: -#define rl_r(r) do { \ - const unsigned rl_r_var_oldcf = CF >> 8 & 1; \ - CF = (r) << 1; \ - ZF = CF | rl_r_var_oldcf; \ - (r) = ZF & 0xFF; \ - HF2 = 0; \ -} while (0) - -//rrc r (8 cycles): -//Rotate 8-bit register right, store old bit0 in CF. Reset SF and HCF, Check ZF: -#define rrc_r(r) do { \ - ZF = (r); \ - CF = ZF << 8; \ - (r) = (ZF | CF) >> 1 & 0xFF; \ - HF2 = 0; \ -} while (0) - -//rr r (8 cycles): -//Rotate 8-bit register right through CF, store old bit0 in CF, old CF value becomes bit7. Reset SF and HCF, Check ZF: -#define rr_r(r) do { \ - const unsigned rr_r_var_oldcf = CF & 0x100; \ - CF = (r) << 8; \ - (r) = ZF = ((r) | rr_r_var_oldcf) >> 1; \ - HF2 = 0; \ -} while (0) - -//sla r (8 cycles): -//Shift 8-bit register left, store old bit7 in CF. Reset SF and HCF, Check ZF: -#define sla_r(r) do { \ - ZF = CF = (r) << 1; \ - (r) = ZF & 0xFF; \ - HF2 = 0; \ -} while (0) - -//sra r (8 cycles): -//Shift 8-bit register right, store old bit0 in CF. bit7=old bit7. Reset SF and HCF, Check ZF: -#define sra_r(r) do { \ - CF = (r) << 8; \ - ZF = (r) >> 1; \ - (r) = ZF | ((r) & 0x80); \ - HF2 = 0; \ -} while (0) - -//srl r (8 cycles): -//Shift 8-bit register right, store old bit0 in CF. Reset SF and HCF, Check ZF: -#define srl_r(r) do { \ - ZF = (r); \ - CF = (r) << 8; \ - ZF >>= 1; \ - (r) = ZF; \ - HF2 = 0; \ -} while (0) - -//bit n,r (8 cycles): -//bit n,(hl) (12 cycles): -//Test bitn in 8-bit value, check ZF, unset SF, set HCF: -#define bitn_u8(bitmask, u8) do { \ - ZF = (u8) & (bitmask); \ - HF2 = 0x200; \ -} while (0) - -#define bit0_u8(u8) bitn_u8(1, (u8)) -#define bit1_u8(u8) bitn_u8(2, (u8)) -#define bit2_u8(u8) bitn_u8(4, (u8)) -#define bit3_u8(u8) bitn_u8(8, (u8)) -#define bit4_u8(u8) bitn_u8(0x10, (u8)) -#define bit5_u8(u8) bitn_u8(0x20, (u8)) -#define bit6_u8(u8) bitn_u8(0x40, (u8)) -#define bit7_u8(u8) bitn_u8(0x80, (u8)) - -//set n,r (8 cycles): -//Set bitn of 8-bit register: -#define set0_r(r) ( (r) |= 0x1 ) -#define set1_r(r) ( (r) |= 0x2 ) -#define set2_r(r) ( (r) |= 0x4 ) -#define set3_r(r) ( (r) |= 0x8 ) -#define set4_r(r) ( (r) |= 0x10 ) -#define set5_r(r) ( (r) |= 0x20 ) -#define set6_r(r) ( (r) |= 0x40 ) -#define set7_r(r) ( (r) |= 0x80 ) - -//set n,(hl) (16 cycles): -//Set bitn of value at address stored in HL: -#define setn_mem_hl(n) do { \ - const unsigned setn_mem_hl_var_addr = HL(); \ - unsigned setn_mem_hl_var_tmp; \ -\ - READ(setn_mem_hl_var_tmp, setn_mem_hl_var_addr); \ - setn_mem_hl_var_tmp |= 1 << (n); \ -\ - WRITE(setn_mem_hl_var_addr, setn_mem_hl_var_tmp); \ -} while (0) - -//res n,r (8 cycles): -//Unset bitn of 8-bit register: -#define res0_r(r) ( (r) &= 0xFE ) -#define res1_r(r) ( (r) &= 0xFD ) -#define res2_r(r) ( (r) &= 0xFB ) -#define res3_r(r) ( (r) &= 0xF7 ) -#define res4_r(r) ( (r) &= 0xEF ) -#define res5_r(r) ( (r) &= 0xDF ) -#define res6_r(r) ( (r) &= 0xBF ) -#define res7_r(r) ( (r) &= 0x7F ) - -//res n,(hl) (16 cycles): -//Unset bitn of value at address stored in HL: -#define resn_mem_hl(n) do { \ - const unsigned resn_mem_hl_var_addr = HL(); \ - unsigned resn_mem_hl_var_tmp; \ -\ - READ(resn_mem_hl_var_tmp, resn_mem_hl_var_addr); \ - resn_mem_hl_var_tmp &= ~(1 << (n)); \ -\ - WRITE(resn_mem_hl_var_addr, resn_mem_hl_var_tmp); \ -} while (0) - - -//16-BIT LOADS: -//ld rr,nn (12 cycles) -//set rr to 16-bit value of next 2 bytes in memory -#define ld_rr_nn(r1, r2) do { \ - PC_READ(r2); \ - PC_READ(r1); \ -} while (0) - -//push rr (16 cycles): -//Push value of register pair onto stack: -#define push_rr(r1, r2) do { \ - PUSH(r1, r2); \ - cycleCounter += 4; \ -} while (0) - -//pop rr (12 cycles): -//Pop two bytes off stack into register pair: -#define pop_rr(r1, r2) do { \ - READ(r2, SP); \ - SP = (SP + 1) & 0xFFFF; \ - READ(r1, SP); \ - SP = (SP + 1) & 0xFFFF; \ -} while (0) - -//8-BIT ALU: -//add a,r (4 cycles): -//add a,(addr) (8 cycles): -//Add 8-bit value to A, check flags: -#define add_a_u8(u8) do { \ - HF1 = A; \ - HF2 = u8; \ - ZF = CF = A + HF2; \ - A = ZF & 0xFF; \ - calcHF(HF1, HF2); \ -} while (0) - -//adc a,r (4 cycles): -//adc a,(addr) (8 cycles): -//Add 8-bit value+CF to A, check flags: -#define adc_a_u8(u8) do { \ - HF1 = A; \ - HF2 = (CF & 0x100) | (u8); \ - ZF = CF = (CF >> 8 & 1) + (u8) + A; \ - A = ZF & 0xFF; \ - calcHF(HF1, HF2); \ -} while (0) - -//sub a,r (4 cycles): -//sub a,(addr) (8 cycles): -//Subtract 8-bit value from A, check flags: -#define sub_a_u8(u8) do { \ - HF1 = A; \ - HF2 = u8; \ - ZF = CF = A - HF2; \ - A = ZF & 0xFF; \ - HF2 |= 0x400; \ - calcHF(HF1, HF2); \ -} while (0) - -//sbc a,r (4 cycles): -//sbc a,(addr) (8 cycles): -//Subtract CF and 8-bit value from A, check flags: -#define sbc_a_u8(u8) do { \ - HF1 = A; \ - HF2 = 0x400 | (CF & 0x100) | (u8); \ - ZF = CF = A - ((CF >> 8) & 1) - (u8); \ - A = ZF & 0xFF; \ - calcHF(HF1, HF2); \ -} while (0) - -//and a,r (4 cycles): -//and a,(addr) (8 cycles): -//bitwise and 8-bit value into A, check flags: -#define and_a_u8(u8) do { \ - HF2 = 0x200; \ - CF = 0; \ - A &= (u8); \ - ZF = A; \ -} while (0) - -//or a,r (4 cycles): -//or a,(hl) (8 cycles): -//bitwise or 8-bit value into A, check flags: -#define or_a_u8(u8) do { \ - CF = HF2 = 0; \ - A |= (u8); \ - ZF = A; \ -} while (0) - -//xor a,r (4 cycles): -//xor a,(hl) (8 cycles): -//bitwise xor 8-bit value into A, check flags: -#define xor_a_u8(u8) do { \ - CF = HF2 = 0; \ - A ^= (u8); \ - ZF = A; \ -} while (0) - -//cp a,r (4 cycles): -//cp a,(addr) (8 cycles): -//Compare (subtract without storing result) 8-bit value to A, check flags: -#define cp_a_u8(u8) do { \ - HF1 = A; \ - HF2 = u8; \ - ZF = CF = A - HF2; \ - HF2 |= 0x400; \ - calcHF(HF1, HF2); \ -} while (0) - -//inc r (4 cycles): -//Increment value of 8-bit register, check flags except CF: -#define inc_r(r) do { \ - HF2 = (r) | 0x800; \ - ZF = (r) + 1; \ - (r) = ZF & 0xFF; \ - calcHF(HF1, HF2); \ -} while (0) - -//dec r (4 cycles): -//Decrement value of 8-bit register, check flags except CF: -#define dec_r(r) do { \ - HF2 = (r) | 0xC00; \ - ZF = (r) - 1; \ - (r) = ZF & 0xFF; \ - calcHF(HF1, HF2); \ -} while (0) - -//16-BIT ARITHMETIC -//add hl,rr (8 cycles): -//add 16-bit register to HL, check flags except ZF: -/*#define add_hl_rr(rh, rl) do { \ - L = HF1 = L + (rl); \ - HF1 >>= 8; \ - HF1 += H; \ - HF2 = (rh); \ - H = CF = HF1 + (rh); \ - cycleCounter += 4; \ -} while (0)*/ - -#define add_hl_rr(rh, rl) do { \ - CF = L + (rl); \ - L = CF & 0xFF; \ - HF1 = H; \ - HF2 = (CF & 0x100) | (rh); \ - CF = H + (CF >> 8) + (rh); \ - H = CF & 0xFF; \ - cycleCounter += 4; \ - calcHF(HF1, HF2); \ -} while (0) - -//inc rr (8 cycles): -//Increment 16-bit register: -#define inc_rr(rh, rl) do { \ - const unsigned inc_rr_var_tmp = (rl) + 1; \ - (rl) = inc_rr_var_tmp & 0xFF; \ - (rh) = ((rh) + (inc_rr_var_tmp >> 8)) & 0xFF; \ - cycleCounter += 4; \ -} while (0) - -//dec rr (8 cycles): -//Decrement 16-bit register: -#define dec_rr(rh, rl) do { \ - const unsigned dec_rr_var_tmp = (rl) - 1; \ - (rl) = dec_rr_var_tmp & 0xFF; \ - (rh) = ((rh) - (dec_rr_var_tmp >> 8 & 1)) & 0xFF; \ - cycleCounter += 4; \ -} while (0) - -#define sp_plus_n(sumout) do { \ - unsigned sp_plus_n_var_n; \ - PC_READ(sp_plus_n_var_n); \ - sp_plus_n_var_n = (sp_plus_n_var_n ^ 0x80) - 0x80; \ - \ - const unsigned sp_plus_n_var_sum = SP + sp_plus_n_var_n; \ - CF = SP ^ sp_plus_n_var_n ^ sp_plus_n_var_sum; \ - HF2 = CF << 5 & 0x200; \ - ZF = 1; \ - cycleCounter += 4; \ - (sumout) = sp_plus_n_var_sum & 0xFFFF; \ - calcHF(HF1, HF2); \ -} while (0) - -//JUMPS: -//jp nn (16 cycles): -//Jump to address stored in the next two bytes in memory: -#define jp_nn() do { \ - unsigned jp_nn_var_l, jp_nn_var_h; \ -\ - PC_READ(jp_nn_var_l); \ - PC_READ(jp_nn_var_h); \ -\ - PC_MOD(jp_nn_var_h << 8 | jp_nn_var_l); \ -} while (0) - -//jr disp (12 cycles): -//Jump to value of next (signed) byte in memory+current address: -#define jr_disp() do { \ - unsigned jr_disp_var_tmp; \ -\ - PC_READ(jr_disp_var_tmp); \ - jr_disp_var_tmp = (jr_disp_var_tmp ^ 0x80) - 0x80; \ -\ - PC_MOD((PC + jr_disp_var_tmp) & 0xFFFF); \ -} while (0) - -// CALLS, RESTARTS AND RETURNS: -// call nn (24 cycles): -// Jump to 16-bit immediate operand and push return address onto stack: -#define call_nn() do { \ - unsigned const npc = (PC + 2) & 0xFFFF; \ - jp_nn(); \ - PUSH(npc >> 8, npc & 0xFF); \ -} while (0) - -//rst n (16 Cycles): -//Push present address onto stack, jump to address n (one of 00h,08h,10h,18h,20h,28h,30h,38h): -#define rst_n(n) do { \ - PUSH(PC >> 8, PC & 0xFF); \ - PC_MOD(n); \ -} while (0) - -//ret (16 cycles): -//Pop two bytes from the stack and jump to that address: -#define ret() do { \ - unsigned ret_var_l, ret_var_h; \ -\ - pop_rr(ret_var_h, ret_var_l); \ -\ - PC_MOD(ret_var_h << 8 | ret_var_l); \ -} while (0) - -void CPU::process(const unsigned long cycles) { - memory.setEndtime(cycleCounter_, cycles); - memory.updateInput(); - - //unsigned char A = A_; - unsigned long cycleCounter = cycleCounter_; - - while (memory.isActive()) { - //unsigned short PC = PC_; - - if (memory.halted()) { - if (cycleCounter < memory.nextEventTime()) { - const unsigned long cycles = memory.nextEventTime() - cycleCounter; - cycleCounter += cycles + (-cycles & 3); - } - } else while (cycleCounter < memory.nextEventTime()) { - unsigned char opcode; - - if (tracecallback) { - int result[14]; - result[0] = cycleCounter; - result[1] = PC; - result[2] = SP; - result[3] = A; - result[4] = B; - result[5] = C; - result[6] = D; - result[7] = E; - result[8] = F(); - result[9] = H; - result[10] = L; - result[11] = skip; - PC_READ_FIRST(opcode); - result[12] = opcode; - result[13] = memory.debugGetLY(); - tracecallback((void *)result); - } - else { - PC_READ_FIRST(opcode); - } - - if (skip) { - PC = (PC - 1) & 0xFFFF; - skip = false; - } - - switch (opcode) { - //nop (4 cycles): - //Do nothing for 4 cycles: - case 0x00: - break; - case 0x01: - ld_rr_nn(B, C); - break; - case 0x02: - WRITE(BC(), A); - break; - case 0x03: - inc_rr(B, C); - break; - case 0x04: - inc_r(B); - break; - case 0x05: - dec_r(B); - break; - case 0x06: - PC_READ(B); - break; - - //rlca (4 cycles): - //Rotate 8-bit register A left, store old bit7 in CF. Reset SF, HCF, ZF: - case 0x07: - CF = A << 1; - A = (CF | CF >> 8) & 0xFF; - HF2 = 0; - ZF = 1; - break; - - //ld (nn),SP (20 cycles): - //Put value of SP into address given by next 2 bytes in memory: - case 0x08: - { - unsigned l, h; - - PC_READ(l); - PC_READ(h); - - const unsigned addr = h << 8 | l; - - WRITE(addr, SP & 0xFF); - WRITE((addr + 1) & 0xFFFF, SP >> 8); - } - break; - - case 0x09: - add_hl_rr(B, C); - break; - case 0x0A: - READ(A, BC()); - break; - case 0x0B: - dec_rr(B, C); - break; - case 0x0C: - inc_r(C); - break; - case 0x0D: - dec_r(C); - break; - case 0x0E: - PC_READ(C); - break; - - //rrca (4 cycles): - //Rotate 8-bit register A right, store old bit0 in CF. Reset SF, HCF, ZF: - case 0x0F: - CF = A << 8 | A; - A = CF >> 1 & 0xFF; - HF2 = 0; - ZF = 1; - break; - - //stop (4 cycles): - //Halt CPU and LCD display until button pressed: - case 0x10: - PC = (PC + 1) & 0xFFFF; - - cycleCounter = memory.stop(cycleCounter); - - if (cycleCounter < memory.nextEventTime()) { - const unsigned long cycles = memory.nextEventTime() - cycleCounter; - cycleCounter += cycles + (-cycles & 3); - } - - break; - case 0x11: - ld_rr_nn(D, E); - break; - case 0x12: - WRITE(DE(), A); - break; - case 0x13: - inc_rr(D, E); - break; - case 0x14: - inc_r(D); - break; - case 0x15: - dec_r(D); - break; - case 0x16: - PC_READ(D); - break; - - //rla (4 cycles): - //Rotate 8-bit register A left through CF, store old bit7 in CF, old CF value becomes bit0. Reset SF, HCF, ZF: - case 0x17: - { - const unsigned oldcf = CF >> 8 & 1; - CF = A << 1; - A = (CF | oldcf) & 0xFF; - } - - HF2 = 0; - ZF = 1; - break; - - case 0x18: - jr_disp(); - break; - case 0x19: - add_hl_rr(D, E); - break; - case 0x1A: - READ(A, DE()); - break; - case 0x1B: - dec_rr(D, E); - break; - case 0x1C: - inc_r(E); - break; - case 0x1D: - dec_r(E); - break; - case 0x1E: - PC_READ(E); - break; - - //rra (4 cycles): - //Rotate 8-bit register A right through CF, store old bit0 in CF, old CF value becomes bit7. Reset SF, HCF, ZF: - case 0x1F: - { - const unsigned oldcf = CF & 0x100; - CF = A << 8; - A = (A | oldcf) >> 1; - } - - HF2 = 0; - ZF = 1; - break; - - //jr nz,disp (12;8 cycles): - //Jump to value of next (signed) byte in memory+current address if ZF is unset: - case 0x20: - if (ZF & 0xFF) { - jr_disp(); - } else { - PC_MOD((PC + 1) & 0xFFFF); - } - break; - - case 0x21: - ld_rr_nn(H, L); - break; - - //ldi (hl),a (8 cycles): - //Put A into memory address in hl. Increment HL: - case 0x22: - { - unsigned addr = HL(); - - WRITE(addr, A); - - addr = (addr + 1) & 0xFFFF; - L = addr; - H = addr >> 8; - } - break; - - case 0x23: - inc_rr(H, L); - break; - case 0x24: - inc_r(H); - break; - case 0x25: - dec_r(H); - break; - case 0x26: - PC_READ(H); - break; - - - //daa (4 cycles): - //Adjust register A to correctly represent a BCD. Check ZF, HF and CF: - case 0x27: - /*{ - unsigned correction = ((A > 0x99) || (CF & 0x100)) ? 0x60 : 0x00; - - calcHF(HF1, HF2); - - if ((A & 0x0F) > 0x09 || (HF2 & 0x200)) - correction |= 0x06; - - HF1 = A; - HF2 = (HF2 & 0x400) | correction; - CF = (correction & 0x40) << 2; - A = (HF2 & 0x400) ? A - correction : (A + correction); - ZF = A; - }*/ - - calcHF(HF1, HF2); - - { - unsigned correction = (CF & 0x100) ? 0x60 : 0x00; - - if (HF2 & 0x200) - correction |= 0x06; - - if (!(HF2 &= 0x400)) { - if ((A & 0x0F) > 0x09) - correction |= 0x06; - - if (A > 0x99) - correction |= 0x60; - - A += correction; - } else - A -= correction; - - CF = correction << 2 & 0x100; - ZF = A; - A &= 0xFF; - } - break; - - //jr z,disp (12;8 cycles): - //Jump to value of next (signed) byte in memory+current address if ZF is set: - case 0x28: - if (ZF & 0xFF) { - PC_MOD((PC + 1) & 0xFFFF); - } else { - jr_disp(); - } - break; - - //add hl,hl (8 cycles): - //add 16-bit register HL to HL, check flags except ZF: - case 0x29: - add_hl_rr(H, L); - break; - - //ldi a,(hl) (8 cycles): - //Put value at address in hl into A. Increment HL: - case 0x2A: - { - unsigned addr = HL(); - - READ(A, addr); - - addr = (addr + 1) & 0xFFFF; - L = addr; - H = addr >> 8; - } - break; - - case 0x2B: - dec_rr(H, L); - break; - case 0x2C: - inc_r(L); - break; - case 0x2D: - dec_r(L); - break; - case 0x2E: - PC_READ(L); - break; - - //cpl (4 cycles): - //Complement register A. (Flip all bits), set SF and HCF: - case 0x2F: /*setSubtractFlag(); setHalfCarryFlag();*/ - HF2 = 0x600; - A ^= 0xFF; - break; - - //jr nc,disp (12;8 cycles): - //Jump to value of next (signed) byte in memory+current address if CF is unset: - case 0x30: - if (CF & 0x100) { - PC_MOD((PC + 1) & 0xFFFF); - } else { - jr_disp(); - } - break; - - //ld sp,nn (12 cycles) - //set sp to 16-bit value of next 2 bytes in memory - case 0x31: - { - unsigned l, h; - - PC_READ(l); - PC_READ(h); - - SP = h << 8 | l; - } - break; - - //ldd (hl),a (8 cycles): - //Put A into memory address in hl. Decrement HL: - case 0x32: - { - unsigned addr = HL(); - - WRITE(addr, A); - - addr = (addr - 1) & 0xFFFF; - L = addr; - H = addr >> 8; - } - break; - - case 0x33: - SP = (SP + 1) & 0xFFFF; - cycleCounter += 4; - break; - - //inc (hl) (12 cycles): - //Increment value at address in hl, check flags except CF: - case 0x34: - { - const unsigned addr = HL(); - - READ(HF2, addr); - ZF = HF2 + 1; - WRITE(addr, ZF & 0xFF); - HF2 |= 0x800; - calcHF(HF1, HF2); - } - break; - - //dec (hl) (12 cycles): - //Decrement value at address in hl, check flags except CF: - case 0x35: - { - const unsigned addr = HL(); - - READ(HF2, addr); - ZF = HF2 - 1; - WRITE(addr, ZF & 0xFF); - HF2 |= 0xC00; - calcHF(HF1, HF2); - } - break; - - //ld (hl),n (12 cycles): - //set memory at address in hl to value of next byte in memory: - case 0x36: - { - unsigned tmp; - - PC_READ(tmp); - WRITE(HL(), tmp); - } - break; - - //scf (4 cycles): - //Set CF. Unset SF and HCF: - case 0x37: /*setCarryFlag(); unsetSubtractFlag(); unsetHalfCarryFlag();*/ - CF = 0x100; - HF2 = 0; - break; - - //jr c,disp (12;8 cycles): - //Jump to value of next (signed) byte in memory+current address if CF is set: - case 0x38: //PC+=(((int8_t)memory.read(PC++))*CarryFlag()); Cycles(8); break; - if (CF & 0x100) { - jr_disp(); - } else { - PC_MOD((PC + 1) & 0xFFFF); - } - break; - - //add hl,sp (8 cycles): - //add SP to HL, check flags except ZF: - case 0x39: /*add_hl_rr(SP>>8, SP); break;*/ - CF = L + SP; - L = CF & 0xFF; - HF1 = H; - HF2 = ((CF ^ SP) & 0x100) | SP >> 8; - CF >>= 8; - CF += H; - H = CF & 0xFF; - cycleCounter += 4; - calcHF(HF1, HF2); - break; - - //ldd a,(hl) (8 cycles): - //Put value at address in hl into A. Decrement HL: - case 0x3A: - { - unsigned addr = HL(); - - A = memory.read(addr, cycleCounter); - cycleCounter += 4; - - addr = (addr - 1) & 0xFFFF; - L = addr; - H = addr >> 8; - } - break; - - case 0x3B: - SP = (SP - 1) & 0xFFFF; - cycleCounter += 4; - break; - case 0x3C: - inc_r(A); - break; - case 0x3D: - dec_r(A); - break; - case 0x3E: - PC_READ(A); - break; - - //ccf (4 cycles): - //Complement CF (unset if set vv.) Unset SF and HCF. - case 0x3F: /*complementCarryFlag(); unsetSubtractFlag(); unsetHalfCarryFlag();*/ - CF ^= 0x100; - HF2 = 0; - break; - - //ld r,r (4 cycles):next_irqEventTime - //ld r,(r) (8 cycles): - case 0x40: - B = B; - break; - case 0x41: - B = C; - break; - case 0x42: - B = D; - break; - case 0x43: - B = E; - break; - case 0x44: - B = H; - break; - case 0x45: - B = L; - break; - case 0x46: - READ(B, HL()); - break; - case 0x47: - B = A; - break; - case 0x48: - C = B; - break; - case 0x49: - C = C; - break; - case 0x4A: - C = D; - break; - case 0x4B: - C = E; - break; - case 0x4C: - C = H; - break; - case 0x4D: - C = L; - break; - case 0x4E: - READ(C, HL()); - break; - case 0x4F: - C = A; - break; - case 0x50: - D = B; - break; - case 0x51: - D = C; - break; - case 0x52: - D = D; - break; - case 0x53: - D = E; - break; - case 0x54: - D = H; - break; - case 0x55: - D = L; - break; - case 0x56: - READ(D, HL()); - break; - case 0x57: - D = A; - break; - case 0x58: - E = B; - break; - case 0x59: - E = C; - break; - case 0x5A: - E = D; - break; - case 0x5B: - E = E; - break; - case 0x5C: - E = H; - break; - case 0x5D: - E = L; - break; - case 0x5E: - READ(E, HL()); - break; - case 0x5F: - E = A; - break; - case 0x60: - H = B; - break; - case 0x61: - H = C; - break; - case 0x62: - H = D; - break; - case 0x63: - H = E; - break; - case 0x64: - H = H; - break; - case 0x65: - H = L; - break; - case 0x66: - READ(H, HL()); - break; - case 0x67: - H = A; - break; - case 0x68: - L = B; - break; - case 0x69: - L = C; - break; - case 0x6A: - L = D; - break; - case 0x6B: - L = E; - break; - case 0x6C: - L = H; - break; - case 0x6D: - L = L; - break; - case 0x6E: - READ(L, HL()); - break; - case 0x6F: - L = A; - break; - case 0x70: - WRITE(HL(), B); - break; - case 0x71: - WRITE(HL(), C); - break; - case 0x72: - WRITE(HL(), D); - break; - case 0x73: - WRITE(HL(), E); - break; - case 0x74: - WRITE(HL(), H); - break; - case 0x75: - WRITE(HL(), L); - break; - - //halt (4 cycles): - case 0x76: - if (!memory.ime() && (memory.ff_read(0xFF0F, cycleCounter) & memory.ff_read(0xFFFF, cycleCounter) & 0x1F)) { - if (memory.isCgb()) - cycleCounter += 4; - else - skip = true; - } else { - memory.halt(); - - if (cycleCounter < memory.nextEventTime()) { - const unsigned long cycles = memory.nextEventTime() - cycleCounter; - cycleCounter += cycles + (-cycles & 3); - } - } - - break; - case 0x77: - WRITE(HL(), A); - break; - case 0x78: - A = B; - break; - case 0x79: - A = C; - break; - case 0x7A: - A = D; - break; - case 0x7B: - A = E; - break; - case 0x7C: - A = H; - break; - case 0x7D: - A = L; - break; - case 0x7E: - READ(A, HL()); - break; - case 0x7F: - // A = A; - break; - case 0x80: - add_a_u8(B); - break; - case 0x81: - add_a_u8(C); - break; - case 0x82: - add_a_u8(D); - break; - case 0x83: - add_a_u8(E); - break; - case 0x84: - add_a_u8(H); - break; - case 0x85: - add_a_u8(L); - break; - case 0x86: - { - unsigned data; - - READ(data, HL()); - - add_a_u8(data); - } - break; - case 0x87: - add_a_u8(A); - break; - case 0x88: - adc_a_u8(B); - break; - case 0x89: - adc_a_u8(C); - break; - case 0x8A: - adc_a_u8(D); - break; - case 0x8B: - adc_a_u8(E); - break; - case 0x8C: - adc_a_u8(H); - break; - case 0x8D: - adc_a_u8(L); - break; - case 0x8E: - { - unsigned data; - - READ(data, HL()); - - adc_a_u8(data); - } - break; - case 0x8F: - adc_a_u8(A); - break; - case 0x90: - sub_a_u8(B); - break; - case 0x91: - sub_a_u8(C); - break; - case 0x92: - sub_a_u8(D); - break; - case 0x93: - sub_a_u8(E); - break; - case 0x94: - sub_a_u8(H); - break; - case 0x95: - sub_a_u8(L); - break; - case 0x96: - { - unsigned data; - - READ(data, HL()); - - sub_a_u8(data); - } - break; - //A-A is always 0: - case 0x97: - HF2 = 0x400; - CF = ZF = A = 0; - break; - case 0x98: - sbc_a_u8(B); - break; - case 0x99: - sbc_a_u8(C); - break; - case 0x9A: - sbc_a_u8(D); - break; - case 0x9B: - sbc_a_u8(E); - break; - case 0x9C: - sbc_a_u8(H); - break; - case 0x9D: - sbc_a_u8(L); - break; - case 0x9E: - { - unsigned data; - - READ(data, HL()); - - sbc_a_u8(data); - } - break; - case 0x9F: - sbc_a_u8(A); - break; - case 0xA0: - and_a_u8(B); - break; - case 0xA1: - and_a_u8(C); - break; - case 0xA2: - and_a_u8(D); - break; - case 0xA3: - and_a_u8(E); - break; - case 0xA4: - and_a_u8(H); - break; - case 0xA5: - and_a_u8(L); - break; - case 0xA6: - { - unsigned data; - - READ(data, HL()); - - and_a_u8(data); - } - break; - //A&A will always be A: - case 0xA7: - ZF = A; - CF = 0; - HF2 = 0x200; - break; - case 0xA8: - xor_a_u8(B); - break; - case 0xA9: - xor_a_u8(C); - break; - case 0xAA: - xor_a_u8(D); - break; - case 0xAB: - xor_a_u8(E); - break; - case 0xAC: - xor_a_u8(H); - break; - case 0xAD: - xor_a_u8(L); - break; - case 0xAE: - { - unsigned data; - - READ(data, HL()); - - xor_a_u8(data); - } - break; - //A^A will always be 0: - case 0xAF: - CF = HF2 = ZF = A = 0; - break; - case 0xB0: - or_a_u8(B); - break; - case 0xB1: - or_a_u8(C); - break; - case 0xB2: - or_a_u8(D); - break; - case 0xB3: - or_a_u8(E); - break; - case 0xB4: - or_a_u8(H); - break; - case 0xB5: - or_a_u8(L); - break; - case 0xB6: - { - unsigned data; - - READ(data, HL()); - - or_a_u8(data); - } - break; - //A|A will always be A: - case 0xB7: - ZF = A; - HF2 = CF = 0; - break; - case 0xB8: - cp_a_u8(B); - break; - case 0xB9: - cp_a_u8(C); - break; - case 0xBA: - cp_a_u8(D); - break; - case 0xBB: - cp_a_u8(E); - break; - case 0xBC: - cp_a_u8(H); - break; - case 0xBD: - cp_a_u8(L); - break; - case 0xBE: - { - unsigned data; - - READ(data, HL()); - - cp_a_u8(data); - } - break; - //A always equals A: - case 0xBF: - CF = ZF = 0; - HF2 = 0x400; - calcHF(HF1, HF2); \ - break; - - //ret nz (20;8 cycles): - //Pop two bytes from the stack and jump to that address, if ZF is unset: - case 0xC0: - cycleCounter += 4; - - if (ZF & 0xFF) { - ret(); - } - break; - - case 0xC1: - pop_rr(B, C); - break; - - //jp nz,nn (16;12 cycles): - //Jump to address stored in next two bytes in memory if ZF is unset: - case 0xC2: - if (ZF & 0xFF) { - jp_nn(); - } else { - PC_MOD((PC + 2) & 0xFFFF); - cycleCounter += 4; - } - break; - - case 0xC3: - jp_nn(); - break; - - //call nz,nn (24;12 cycles): - //Push address of next instruction onto stack and then jump to address stored in next two bytes in memory, if ZF is unset: - case 0xC4: - if (ZF & 0xFF) { - call_nn(); - } else { - PC_MOD((PC + 2) & 0xFFFF); - cycleCounter += 4; - } - break; - - case 0xC5: - push_rr(B, C); - break; - case 0xC6: - { - unsigned data; - - PC_READ(data); - - add_a_u8(data); - } - break; - case 0xC7: - rst_n(0x00); - break; - - //ret z (20;8 cycles): - //Pop two bytes from the stack and jump to that address, if ZF is set: - case 0xC8: - cycleCounter += 4; - - if (!(ZF & 0xFF)) { - ret(); - } - - break; - - //ret (16 cycles): - //Pop two bytes from the stack and jump to that address: - case 0xC9: - ret(); - break; - - //jp z,nn (16;12 cycles): - //Jump to address stored in next two bytes in memory if ZF is set: - case 0xCA: - if (ZF & 0xFF) { - PC_MOD((PC + 2) & 0xFFFF); - cycleCounter += 4; - } else { - jp_nn(); - } - break; - - - //CB OPCODES (Shifts, rotates and bits): - case 0xCB: - PC_READ(opcode); - - switch (opcode) { - case 0x00: - rlc_r(B); - break; - case 0x01: - rlc_r(C); - break; - case 0x02: - rlc_r(D); - break; - case 0x03: - rlc_r(E); - break; - case 0x04: - rlc_r(H); - break; - case 0x05: - rlc_r(L); - break; - //rlc (hl) (16 cycles): - //Rotate 8-bit value stored at address in HL left, store old bit7 in CF. Reset SF and HCF. Check ZF: - case 0x06: - { - const unsigned addr = HL(); - - READ(CF, addr); - CF <<= 1; - - ZF = CF | (CF >> 8); - - WRITE(addr, ZF & 0xFF); - - HF2 = 0; - } - break; - case 0x07: - rlc_r(A); - break; - case 0x08: - rrc_r(B); - break; - case 0x09: - rrc_r(C); - break; - case 0x0A: - rrc_r(D); - break; - case 0x0B: - rrc_r(E); - break; - case 0x0C: - rrc_r(H); - break; - case 0x0D: - rrc_r(L); - break; - //rrc (hl) (16 cycles): - //Rotate 8-bit value stored at address in HL right, store old bit0 in CF. Reset SF and HCF. Check ZF: - case 0x0E: - { - const unsigned addr = HL(); - - READ(ZF, addr); - - CF = ZF << 8; - - WRITE(addr, (ZF | CF) >> 1 & 0xFF); - - HF2 = 0; - } - break; - case 0x0F: - rrc_r(A); - break; - case 0x10: - rl_r(B); - break; - case 0x11: - rl_r(C); - break; - case 0x12: - rl_r(D); - break; - case 0x13: - rl_r(E); - break; - case 0x14: - rl_r(H); - break; - case 0x15: - rl_r(L); - break; - //rl (hl) (16 cycles): - //Rotate 8-bit value stored at address in HL left thorugh CF, store old bit7 in CF, old CF value becoms bit0. Reset SF and HCF. Check ZF: - case 0x16: - { - const unsigned addr = HL(); - const unsigned oldcf = CF >> 8 & 1; - - READ(CF, addr); - CF <<= 1; - - ZF = CF | oldcf; - - WRITE(addr, ZF & 0xFF); - - HF2 = 0; - } - break; - case 0x17: - rl_r(A); - break; - case 0x18: - rr_r(B); - break; - case 0x19: - rr_r(C); - break; - case 0x1A: - rr_r(D); - break; - case 0x1B: - rr_r(E); - break; - case 0x1C: - rr_r(H); - break; - case 0x1D: - rr_r(L); - break; - //rr (hl) (16 cycles): - //Rotate 8-bit value stored at address in HL right thorugh CF, store old bit0 in CF, old CF value becoms bit7. Reset SF and HCF. Check ZF: - case 0x1E: - { - const unsigned addr = HL(); - - READ(ZF, addr); - - const unsigned oldcf = CF & 0x100; - CF = ZF << 8; - ZF = (ZF | oldcf) >> 1; - - WRITE(addr, ZF); - - HF2 = 0; - } - break; - case 0x1F: - rr_r(A); - break; - case 0x20: - sla_r(B); - break; - case 0x21: - sla_r(C); - break; - case 0x22: - sla_r(D); - break; - case 0x23: - sla_r(E); - break; - case 0x24: - sla_r(H); - break; - case 0x25: - sla_r(L); - break; - //sla (hl) (16 cycles): - //Shift 8-bit value stored at address in HL left, store old bit7 in CF. Reset SF and HCF. Check ZF: - case 0x26: - { - const unsigned addr = HL(); - - READ(CF, addr); - CF <<= 1; - - ZF = CF; - - WRITE(addr, ZF & 0xFF); - - HF2 = 0; - } - break; - case 0x27: - sla_r(A); - break; - case 0x28: - sra_r(B); - break; - case 0x29: - sra_r(C); - break; - case 0x2A: - sra_r(D); - break; - case 0x2B: - sra_r(E); - break; - case 0x2C: - sra_r(H); - break; - case 0x2D: - sra_r(L); - break; - //sra (hl) (16 cycles): - //Shift 8-bit value stored at address in HL right, store old bit0 in CF, bit7=old bit7. Reset SF and HCF. Check ZF: - case 0x2E: - { - const unsigned addr = HL(); - - READ(CF, addr); - - ZF = CF >> 1; - - WRITE(addr, ZF | (CF & 0x80)); - - CF <<= 8; - HF2 = 0; - } - break; - case 0x2F: - sra_r(A); - break; - case 0x30: - swap_r(B); - break; - case 0x31: - swap_r(C); - break; - case 0x32: - swap_r(D); - break; - case 0x33: - swap_r(E); - break; - case 0x34: - swap_r(H); - break; - case 0x35: - swap_r(L); - break; - //swap (hl) (16 cycles): - //Swap upper and lower nibbles of 8-bit value stored at address in HL, reset flags, check zero flag: - case 0x36: - { - const unsigned addr = HL(); - - READ(ZF, addr); - - WRITE(addr, (ZF << 4 | ZF >> 4) & 0xFF); - - CF = HF2 = 0; - } - break; - case 0x37: - swap_r(A); - break; - case 0x38: - srl_r(B); - break; - case 0x39: - srl_r(C); - break; - case 0x3A: - srl_r(D); - break; - case 0x3B: - srl_r(E); - break; - case 0x3C: - srl_r(H); - break; - case 0x3D: - srl_r(L); - break; - //srl (hl) (16 cycles): - //Shift 8-bit value stored at address in HL right, store old bit0 in CF. Reset SF and HCF. Check ZF: - case 0x3E: - { - const unsigned addr = HL(); - - READ(CF, addr); - - ZF = CF >> 1; - - WRITE(addr, ZF); - - CF <<= 8; - HF2 = 0; - } - break; - case 0x3F: - srl_r(A); - break; - case 0x40: - bit0_u8(B); - break; - case 0x41: - bit0_u8(C); - break; - case 0x42: - bit0_u8(D); - break; - case 0x43: - bit0_u8(E); - break; - case 0x44: - bit0_u8(H); - break; - case 0x45: - bit0_u8(L); - break; - case 0x46: - { - unsigned data; - - READ(data, HL()); - - bit0_u8(data); - } - break; - case 0x47: - bit0_u8(A); - break; - case 0x48: - bit1_u8(B); - break; - case 0x49: - bit1_u8(C); - break; - case 0x4A: - bit1_u8(D); - break; - case 0x4B: - bit1_u8(E); - break; - case 0x4C: - bit1_u8(H); - break; - case 0x4D: - bit1_u8(L); - break; - case 0x4E: - { - unsigned data; - - READ(data, HL()); - - bit1_u8(data); - } - break; - case 0x4F: - bit1_u8(A); - break; - case 0x50: - bit2_u8(B); - break; - case 0x51: - bit2_u8(C); - break; - case 0x52: - bit2_u8(D); - break; - case 0x53: - bit2_u8(E); - break; - case 0x54: - bit2_u8(H); - break; - case 0x55: - bit2_u8(L); - break; - case 0x56: - { - unsigned data; - - READ(data, HL()); - - bit2_u8(data); - } - break; - case 0x57: - bit2_u8(A); - break; - case 0x58: - bit3_u8(B); - break; - case 0x59: - bit3_u8(C); - break; - case 0x5A: - bit3_u8(D); - break; - case 0x5B: - bit3_u8(E); - break; - case 0x5C: - bit3_u8(H); - break; - case 0x5D: - bit3_u8(L); - break; - case 0x5E: - { - unsigned data; - - READ(data, HL()); - - bit3_u8(data); - } - break; - case 0x5F: - bit3_u8(A); - break; - case 0x60: - bit4_u8(B); - break; - case 0x61: - bit4_u8(C); - break; - case 0x62: - bit4_u8(D); - break; - case 0x63: - bit4_u8(E); - break; - case 0x64: - bit4_u8(H); - break; - case 0x65: - bit4_u8(L); - break; - case 0x66: - { - unsigned data; - - READ(data, HL()); - - bit4_u8(data); - } - break; - case 0x67: - bit4_u8(A); - break; - case 0x68: - bit5_u8(B); - break; - case 0x69: - bit5_u8(C); - break; - case 0x6A: - bit5_u8(D); - break; - case 0x6B: - bit5_u8(E); - break; - case 0x6C: - bit5_u8(H); - break; - case 0x6D: - bit5_u8(L); - break; - case 0x6E: - { - unsigned data; - - READ(data, HL()); - - bit5_u8(data); - } - break; - case 0x6F: - bit5_u8(A); - break; - case 0x70: - bit6_u8(B); - break; - case 0x71: - bit6_u8(C); - break; - case 0x72: - bit6_u8(D); - break; - case 0x73: - bit6_u8(E); - break; - case 0x74: - bit6_u8(H); - break; - case 0x75: - bit6_u8(L); - break; - case 0x76: - { - unsigned data; - - READ(data, HL()); - - bit6_u8(data); - } - break; - case 0x77: - bit6_u8(A); - break; - case 0x78: - bit7_u8(B); - break; - case 0x79: - bit7_u8(C); - break; - case 0x7A: - bit7_u8(D); - break; - case 0x7B: - bit7_u8(E); - break; - case 0x7C: - bit7_u8(H); - break; - case 0x7D: - bit7_u8(L); - break; - case 0x7E: - { - unsigned data; - - READ(data, HL()); - - bit7_u8(data); - } - break; - case 0x7F: - bit7_u8(A); - break; - case 0x80: - res0_r(B); - break; - case 0x81: - res0_r(C); - break; - case 0x82: - res0_r(D); - break; - case 0x83: - res0_r(E); - break; - case 0x84: - res0_r(H); - break; - case 0x85: - res0_r(L); - break; - case 0x86: - resn_mem_hl(0); - break; - case 0x87: - res0_r(A); - break; - case 0x88: - res1_r(B); - break; - case 0x89: - res1_r(C); - break; - case 0x8A: - res1_r(D); - break; - case 0x8B: - res1_r(E); - break; - case 0x8C: - res1_r(H); - break; - case 0x8D: - res1_r(L); - break; - case 0x8E: - resn_mem_hl(1); - break; - case 0x8F: - res1_r(A); - break; - case 0x90: - res2_r(B); - break; - case 0x91: - res2_r(C); - break; - case 0x92: - res2_r(D); - break; - case 0x93: - res2_r(E); - break; - case 0x94: - res2_r(H); - break; - case 0x95: - res2_r(L); - break; - case 0x96: - resn_mem_hl(2); - break; - case 0x97: - res2_r(A); - break; - case 0x98: - res3_r(B); - break; - case 0x99: - res3_r(C); - break; - case 0x9A: - res3_r(D); - break; - case 0x9B: - res3_r(E); - break; - case 0x9C: - res3_r(H); - break; - case 0x9D: - res3_r(L); - break; - case 0x9E: - resn_mem_hl(3); - break; - case 0x9F: - res3_r(A); - break; - case 0xA0: - res4_r(B); - break; - case 0xA1: - res4_r(C); - break; - case 0xA2: - res4_r(D); - break; - case 0xA3: - res4_r(E); - break; - case 0xA4: - res4_r(H); - break; - case 0xA5: - res4_r(L); - break; - case 0xA6: - resn_mem_hl(4); - break; - case 0xA7: - res4_r(A); - break; - case 0xA8: - res5_r(B); - break; - case 0xA9: - res5_r(C); - break; - case 0xAA: - res5_r(D); - break; - case 0xAB: - res5_r(E); - break; - case 0xAC: - res5_r(H); - break; - case 0xAD: - res5_r(L); - break; - case 0xAE: - resn_mem_hl(5); - break; - case 0xAF: - res5_r(A); - break; - case 0xB0: - res6_r(B); - break; - case 0xB1: - res6_r(C); - break; - case 0xB2: - res6_r(D); - break; - case 0xB3: - res6_r(E); - break; - case 0xB4: - res6_r(H); - break; - case 0xB5: - res6_r(L); - break; - case 0xB6: - resn_mem_hl(6); - break; - case 0xB7: - res6_r(A); - break; - case 0xB8: - res7_r(B); - break; - case 0xB9: - res7_r(C); - break; - case 0xBA: - res7_r(D); - break; - case 0xBB: - res7_r(E); - break; - case 0xBC: - res7_r(H); - break; - case 0xBD: - res7_r(L); - break; - case 0xBE: - resn_mem_hl(7); - break; - case 0xBF: - res7_r(A); - break; - case 0xC0: - set0_r(B); - break; - case 0xC1: - set0_r(C); - break; - case 0xC2: - set0_r(D); - break; - case 0xC3: - set0_r(E); - break; - case 0xC4: - set0_r(H); - break; - case 0xC5: - set0_r(L); - break; - case 0xC6: - setn_mem_hl(0); - break; - case 0xC7: - set0_r(A); - break; - case 0xC8: - set1_r(B); - break; - case 0xC9: - set1_r(C); - break; - case 0xCA: - set1_r(D); - break; - case 0xCB: - set1_r(E); - break; - case 0xCC: - set1_r(H); - break; - case 0xCD: - set1_r(L); - break; - case 0xCE: - setn_mem_hl(1); - break; - case 0xCF: - set1_r(A); - break; - case 0xD0: - set2_r(B); - break; - case 0xD1: - set2_r(C); - break; - case 0xD2: - set2_r(D); - break; - case 0xD3: - set2_r(E); - break; - case 0xD4: - set2_r(H); - break; - case 0xD5: - set2_r(L); - break; - case 0xD6: - setn_mem_hl(2); - break; - case 0xD7: - set2_r(A); - break; - case 0xD8: - set3_r(B); - break; - case 0xD9: - set3_r(C); - break; - case 0xDA: - set3_r(D); - break; - case 0xDB: - set3_r(E); - break; - case 0xDC: - set3_r(H); - break; - case 0xDD: - set3_r(L); - break; - case 0xDE: - setn_mem_hl(3); - break; - case 0xDF: - set3_r(A); - break; - case 0xE0: - set4_r(B); - break; - case 0xE1: - set4_r(C); - break; - case 0xE2: - set4_r(D); - break; - case 0xE3: - set4_r(E); - break; - case 0xE4: - set4_r(H); - break; - case 0xE5: - set4_r(L); - break; - case 0xE6: - setn_mem_hl(4); - break; - case 0xE7: - set4_r(A); - break; - case 0xE8: - set5_r(B); - break; - case 0xE9: - set5_r(C); - break; - case 0xEA: - set5_r(D); - break; - case 0xEB: - set5_r(E); - break; - case 0xEC: - set5_r(H); - break; - case 0xED: - set5_r(L); - break; - case 0xEE: - setn_mem_hl(5); - break; - case 0xEF: - set5_r(A); - break; - case 0xF0: - set6_r(B); - break; - case 0xF1: - set6_r(C); - break; - case 0xF2: - set6_r(D); - break; - case 0xF3: - set6_r(E); - break; - case 0xF4: - set6_r(H); - break; - case 0xF5: - set6_r(L); - break; - case 0xF6: - setn_mem_hl(6); - break; - case 0xF7: - set6_r(A); - break; - case 0xF8: - set7_r(B); - break; - case 0xF9: - set7_r(C); - break; - case 0xFA: - set7_r(D); - break; - case 0xFB: - set7_r(E); - break; - case 0xFC: - set7_r(H); - break; - case 0xFD: - set7_r(L); - break; - case 0xFE: - setn_mem_hl(7); - break; - case 0xFF: - set7_r(A); - break; -// default: break; - } - break; - - - //call z,nn (24;12 cycles): - //Push address of next instruction onto stack and then jump to address stored in next two bytes in memory, if ZF is set: - case 0xCC: - if (ZF & 0xFF) { - PC_MOD((PC + 2) & 0xFFFF); - cycleCounter += 4; - } else { - call_nn(); - } - break; - - case 0xCD: - call_nn(); - break; - case 0xCE: - { - unsigned data; - - PC_READ(data); - - adc_a_u8(data); - } - break; - case 0xCF: - rst_n(0x08); - break; - - //ret nc (20;8 cycles): - //Pop two bytes from the stack and jump to that address, if CF is unset: - case 0xD0: - cycleCounter += 4; - - if (!(CF & 0x100)) { - ret(); - } - - break; - - case 0xD1: - pop_rr(D, E); - break; - - //jp nc,nn (16;12 cycles): - //Jump to address stored in next two bytes in memory if CF is unset: - case 0xD2: - if (CF & 0x100) { - PC_MOD((PC + 2) & 0xFFFF); - cycleCounter += 4; - } else { - jp_nn(); - } - break; - - case 0xD3: /*doesn't exist*/ - skip = true; - memory.di(); - break; - - //call nc,nn (24;12 cycles): - //Push address of next instruction onto stack and then jump to address stored in next two bytes in memory, if CF is unset: - case 0xD4: - if (CF & 0x100) { - PC_MOD((PC + 2) & 0xFFFF); - cycleCounter += 4; - } else { - call_nn(); - } - break; - - case 0xD5: - push_rr(D, E); - break; - case 0xD6: - { - unsigned data; - - PC_READ(data); - - sub_a_u8(data); - } - break; - case 0xD7: - rst_n(0x10); - break; - - //ret c (20;8 cycles): - //Pop two bytes from the stack and jump to that address, if CF is set: - case 0xD8: - cycleCounter += 4; - - if (CF & 0x100) { - ret(); - } - - break; - - //reti (16 cycles): - //Pop two bytes from the stack and jump to that address, then enable interrupts: - case 0xD9: - { - unsigned l, h; - - pop_rr(h, l); - - memory.ei(cycleCounter); - - PC_MOD(h << 8 | l); - } - break; - - //jp c,nn (16;12 cycles): - //Jump to address stored in next two bytes in memory if CF is set: - case 0xDA: //PC=( ((PC+2)*(1-CarryFlag())) + (((memory.read(PC+1)<<8)+memory.read(PC))*CarryFlag()) ); Cycles(12); break; - if (CF & 0x100) { - jp_nn(); - } else { - PC_MOD((PC + 2) & 0xFFFF); - cycleCounter += 4; - } - break; - - case 0xDB: /*doesn't exist*/ - skip = true; - memory.di(); - break; - - //call z,nn (24;12 cycles): - //Push address of next instruction onto stack and then jump to address stored in next two bytes in memory, if CF is set: - case 0xDC: - if (CF & 0x100) { - call_nn(); - } else { - PC_MOD((PC + 2) & 0xFFFF); - cycleCounter += 4; - } - break; - - case 0xDD: /*doesn't exist*/ - skip = true; - memory.di(); - break; - - case 0xDE: - { - unsigned data; - - PC_READ(data); - - sbc_a_u8(data); - } - break; - case 0xDF: - rst_n(0x18); - break; - - //ld ($FF00+n),a (12 cycles): - //Put value in A into address (0xFF00 + next byte in memory): - case 0xE0: - { - unsigned tmp; - - PC_READ(tmp); - - FF_WRITE(0xFF00 | tmp, A); - } - break; - - case 0xE1: - pop_rr(H, L); - break; - - //ld ($FF00+C),a (8 ycles): - //Put A into address (0xFF00 + register C): - case 0xE2: - FF_WRITE(0xFF00 | C, A); - break; - case 0xE3: /*doesn't exist*/ - skip = true; - memory.di(); - break; - case 0xE4: /*doesn't exist*/ - skip = true; - memory.di(); - break; - case 0xE5: - push_rr(H, L); - break; - case 0xE6: - { - unsigned data; - - PC_READ(data); - - and_a_u8(data); - } - break; - case 0xE7: - rst_n(0x20); - break; - - //add sp,n (16 cycles): - //Add next (signed) byte in memory to SP, reset ZF and SF, check HCF and CF: - case 0xE8: - /*{ - int8_t tmp = int8_t(memory.pc_read(PC++, cycleCounter)); - HF2 = (((SP & 0xFFF) + tmp) >> 3) & 0x200; - CF = SP + tmp; - SP = CF; - CF >>= 8; - ZF = 1; - cycleCounter += 12; - }*/ - sp_plus_n(SP); - cycleCounter += 4; - break; - - //jp hl (4 cycles): - //Jump to address in hl: - case 0xE9: - PC = HL(); - break; - - //ld (nn),a (16 cycles): - //set memory at address given by the next 2 bytes to value in A: - //Incrementing PC before call, because of possible interrupt. - case 0xEA: - { - unsigned l, h; - - PC_READ(l); - PC_READ(h); - - WRITE(h << 8 | l, A); - } - break; - - case 0xEB: /*doesn't exist*/ - skip = true; - memory.di(); - break; - case 0xEC: /*doesn't exist*/ - skip = true; - memory.di(); - break; - case 0xED: /*doesn't exist*/ - skip = true; - memory.di(); - break; - case 0xEE: - { - unsigned data; - - PC_READ(data); - - xor_a_u8(data); - } - break; - case 0xEF: - rst_n(0x28); - break; - - //ld a,($FF00+n) (12 cycles): - //Put value at address (0xFF00 + next byte in memory) into A: - case 0xF0: - { - unsigned tmp; - - PC_READ(tmp); - - FF_READ(A, 0xFF00 | tmp); - } - break; - - case 0xF1: /*pop_rr(A, F); Cycles(12); break;*/ - { - unsigned F; - - pop_rr(A, F); - - FROM_F(F); - } - break; - - //ld a,($FF00+C) (8 cycles): - //Put value at address (0xFF00 + register C) into A: - case 0xF2: - FF_READ(A, 0xFF00 | C); - break; - - //di (4 cycles): - case 0xF3: - memory.di(); - break; - - case 0xF4: /*doesn't exist*/ - skip = true; - memory.di(); - break; - case 0xF5: /*push_rr(A, F); Cycles(16); break;*/ - calcHF(HF1, HF2); - - { - unsigned F = F(); - - push_rr(A, F); - } - break; - - case 0xF6: - { - unsigned data; - - PC_READ(data); - - or_a_u8(data); - } - break; - case 0xF7: - rst_n(0x30); - break; - - //ldhl sp,n (12 cycles): - //Put (sp+next (signed) byte in memory) into hl (unsets ZF and SF, may enable HF and CF): - case 0xF8: - /*{ - int8_t tmp = int8_t(memory.pc_read(PC++, cycleCounter)); - HF2 = (((SP & 0xFFF) + tmp) >> 3) & 0x200; - CF = SP + tmp; - L = CF; - CF >>= 8; - H = CF; - ZF = 1; - cycleCounter += 8; - }*/ - { - unsigned sum; - sp_plus_n(sum); - L = sum & 0xFF; - H = sum >> 8; - } - break; - - //ld sp,hl (8 cycles): - //Put value in HL into SP - case 0xF9: - SP = HL(); - cycleCounter += 4; - break; - - //ld a,(nn) (16 cycles): - //set A to value in memory at address given by the 2 next bytes. - case 0xFA: - { - unsigned l, h; - - PC_READ(l); - PC_READ(h); - - READ(A, h << 8 | l); - } - break; - - //ei (4 cycles): - //Enable Interrupts after next instruction: - case 0xFB: - memory.ei(cycleCounter); - break; - - case 0xFC: /*doesn't exist*/ - skip = true; - memory.di(); - break; - case 0xFD: /*doesn't exist*/ - skip = true; - memory.di(); - break; - case 0xFE: - { - unsigned data; - - PC_READ(data); - - cp_a_u8(data); - } - break; - case 0xFF: - rst_n(0x38); - break; -// default: break; - } - } - - //PC_ = PC; - cycleCounter = memory.event(cycleCounter); - } - - //A_ = A; - cycleCounter_ = cycleCounter; -} - -void CPU::GetRegs(int *dest) -{ - dest[0] = PC; - dest[1] = SP; - dest[2] = A; - dest[3] = B; - dest[4] = C; - dest[5] = D; - dest[6] = E; - dest[7] = F(); - dest[8] = H; - dest[9] = L; -} - -SYNCFUNC(CPU) -{ - SSS(memory); - NSS(cycleCounter_); - NSS(PC); - NSS(SP); - NSS(HF1); - NSS(HF2); - NSS(ZF); - NSS(CF); - NSS(A); - NSS(B); - NSS(C); - NSS(D); - NSS(E); - NSS(H); - NSS(L); - NSS(skip); -} - -} +/*************************************************************************** + * 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 "cpu.h" +#include "memory.h" +#include "savestate.h" + +namespace gambatte { + +CPU::CPU() +: memory(Interrupter(SP, PC), SP, PC), + cycleCounter_(0), + PC(0x100), + SP(0xFFFE), + HF1(0xF), + HF2(0xF), + ZF(0), + CF(0x100), + A(0x01), + B(0x00), + C(0x13), + D(0x00), + E(0xD8), + H(0x01), + L(0x4D), + skip(false), + numInterruptAddresses(), + tracecallback(0) +{ +} + +long CPU::runFor(const unsigned long cycles) { + process(cycles/* << memory.isDoubleSpeed()*/); + + const long csb = memory.cyclesSinceBlit(cycleCounter_); + + if (cycleCounter_ & 0x80000000) + cycleCounter_ = memory.resetCounters(cycleCounter_); + + return csb; +} + +// (HF2 & 0x200) == true means HF is set. +// (HF2 & 0x400) marks the subtract flag. +// (HF2 & 0x800) is set for inc/dec. +// (HF2 & 0x100) is set if there's a carry to add. +static void calcHF(const unsigned HF1, unsigned& HF2) { + unsigned arg1 = HF1 & 0xF; + unsigned arg2 = (HF2 & 0xF) + (HF2 >> 8 & 1); + + if (HF2 & 0x800) { + arg1 = arg2; + arg2 = 1; + } + + if (HF2 & 0x400) + arg1 -= arg2; + else + arg1 = (arg1 + arg2) << 5; + + HF2 |= arg1 & 0x200; +} + +#define F() (((HF2 & 0x600) | (CF & 0x100)) >> 4 | ((ZF & 0xFF) ? 0 : 0x80)) + +#define FROM_F(f_in) do { \ + unsigned from_f_var = f_in; \ +\ + ZF = ~from_f_var & 0x80; \ + HF2 = from_f_var << 4 & 0x600; \ + CF = from_f_var << 4 & 0x100; \ +} while (0) + +void CPU::setStatePtrs(SaveState &state) { + memory.setStatePtrs(state); +} + +void CPU::loadState(const SaveState &state) { + memory.loadState(state/*, cycleCounter_*/); + + cycleCounter_ = state.cpu.cycleCounter; + PC = state.cpu.PC & 0xFFFF; + SP = state.cpu.SP & 0xFFFF; + A = state.cpu.A & 0xFF; + B = state.cpu.B & 0xFF; + C = state.cpu.C & 0xFF; + D = state.cpu.D & 0xFF; + E = state.cpu.E & 0xFF; + FROM_F(state.cpu.F); + H = state.cpu.H & 0xFF; + L = state.cpu.L & 0xFF; + skip = state.cpu.skip; +} + +#define BC() ( B << 8 | C ) +#define DE() ( D << 8 | E ) +#define HL() ( H << 8 | L ) + +#define READ(dest, addr) do { (dest) = memory.read(addr, cycleCounter); cycleCounter += 4; } while (0) +#define PEEK(dest, addr) do { (dest) = memory.read(addr, cycleCounter); } while(0) +// #define PC_READ(dest, addr) do { (dest) = memory.pc_read(addr, cycleCounter); cycleCounter += 4; } while (0) +#define PC_READ(dest) do { (dest) = memory.read_excb(PC, cycleCounter, false); PC = (PC + 1) & 0xFFFF; cycleCounter += 4; } while (0) +#define PC_READ_FIRST(dest) do { (dest) = memory.read_excb(PC, cycleCounter, true); PC = (PC + 1) & 0xFFFF; cycleCounter += 4; } while (0) +#define FF_READ(dest, addr) do { (dest) = memory.ff_read(addr, cycleCounter); cycleCounter += 4; } while (0) + +#define WRITE(addr, data) do { memory.write(addr, data, cycleCounter); cycleCounter += 4; } while (0) +#define FF_WRITE(addr, data) do { memory.ff_write(addr, data, cycleCounter); cycleCounter += 4; } while (0) + +#define PC_MOD(data) do { PC = data; cycleCounter += 4; } while (0) + +#define PUSH(r1, r2) do { \ + SP = (SP - 1) & 0xFFFF; \ + WRITE(SP, (r1)); \ + SP = (SP - 1) & 0xFFFF; \ + WRITE(SP, (r2)); \ +} while (0) + +//CB OPCODES (Shifts, rotates and bits): +//swap r (8 cycles): +//Swap upper and lower nibbles of 8-bit register, reset flags, check zero flag: +#define swap_r(r) do { \ + CF = HF2 = 0; \ + ZF = (r); \ + (r) = (ZF << 4 | ZF >> 4) & 0xFF; \ +} while (0) + +//rlc r (8 cycles): +//Rotate 8-bit register left, store old bit7 in CF. Reset SF and HCF, Check ZF: +#define rlc_r(r) do { \ + CF = (r) << 1; \ + ZF = CF | CF >> 8; \ + (r) = ZF & 0xFF; \ + HF2 = 0; \ +} while (0) + +//rl r (8 cycles): +//Rotate 8-bit register left through CF, store old bit7 in CF, old CF value becomes bit0. Reset SF and HCF, Check ZF: +#define rl_r(r) do { \ + const unsigned rl_r_var_oldcf = CF >> 8 & 1; \ + CF = (r) << 1; \ + ZF = CF | rl_r_var_oldcf; \ + (r) = ZF & 0xFF; \ + HF2 = 0; \ +} while (0) + +//rrc r (8 cycles): +//Rotate 8-bit register right, store old bit0 in CF. Reset SF and HCF, Check ZF: +#define rrc_r(r) do { \ + ZF = (r); \ + CF = ZF << 8; \ + (r) = (ZF | CF) >> 1 & 0xFF; \ + HF2 = 0; \ +} while (0) + +//rr r (8 cycles): +//Rotate 8-bit register right through CF, store old bit0 in CF, old CF value becomes bit7. Reset SF and HCF, Check ZF: +#define rr_r(r) do { \ + const unsigned rr_r_var_oldcf = CF & 0x100; \ + CF = (r) << 8; \ + (r) = ZF = ((r) | rr_r_var_oldcf) >> 1; \ + HF2 = 0; \ +} while (0) + +//sla r (8 cycles): +//Shift 8-bit register left, store old bit7 in CF. Reset SF and HCF, Check ZF: +#define sla_r(r) do { \ + ZF = CF = (r) << 1; \ + (r) = ZF & 0xFF; \ + HF2 = 0; \ +} while (0) + +//sra r (8 cycles): +//Shift 8-bit register right, store old bit0 in CF. bit7=old bit7. Reset SF and HCF, Check ZF: +#define sra_r(r) do { \ + CF = (r) << 8; \ + ZF = (r) >> 1; \ + (r) = ZF | ((r) & 0x80); \ + HF2 = 0; \ +} while (0) + +//srl r (8 cycles): +//Shift 8-bit register right, store old bit0 in CF. Reset SF and HCF, Check ZF: +#define srl_r(r) do { \ + ZF = (r); \ + CF = (r) << 8; \ + ZF >>= 1; \ + (r) = ZF; \ + HF2 = 0; \ +} while (0) + +//bit n,r (8 cycles): +//bit n,(hl) (12 cycles): +//Test bitn in 8-bit value, check ZF, unset SF, set HCF: +#define bitn_u8(bitmask, u8) do { \ + ZF = (u8) & (bitmask); \ + HF2 = 0x200; \ +} while (0) + +#define bit0_u8(u8) bitn_u8(1, (u8)) +#define bit1_u8(u8) bitn_u8(2, (u8)) +#define bit2_u8(u8) bitn_u8(4, (u8)) +#define bit3_u8(u8) bitn_u8(8, (u8)) +#define bit4_u8(u8) bitn_u8(0x10, (u8)) +#define bit5_u8(u8) bitn_u8(0x20, (u8)) +#define bit6_u8(u8) bitn_u8(0x40, (u8)) +#define bit7_u8(u8) bitn_u8(0x80, (u8)) + +//set n,r (8 cycles): +//Set bitn of 8-bit register: +#define set0_r(r) ( (r) |= 0x1 ) +#define set1_r(r) ( (r) |= 0x2 ) +#define set2_r(r) ( (r) |= 0x4 ) +#define set3_r(r) ( (r) |= 0x8 ) +#define set4_r(r) ( (r) |= 0x10 ) +#define set5_r(r) ( (r) |= 0x20 ) +#define set6_r(r) ( (r) |= 0x40 ) +#define set7_r(r) ( (r) |= 0x80 ) + +//set n,(hl) (16 cycles): +//Set bitn of value at address stored in HL: +#define setn_mem_hl(n) do { \ + const unsigned setn_mem_hl_var_addr = HL(); \ + unsigned setn_mem_hl_var_tmp; \ +\ + READ(setn_mem_hl_var_tmp, setn_mem_hl_var_addr); \ + setn_mem_hl_var_tmp |= 1 << (n); \ +\ + WRITE(setn_mem_hl_var_addr, setn_mem_hl_var_tmp); \ +} while (0) + +//res n,r (8 cycles): +//Unset bitn of 8-bit register: +#define res0_r(r) ( (r) &= 0xFE ) +#define res1_r(r) ( (r) &= 0xFD ) +#define res2_r(r) ( (r) &= 0xFB ) +#define res3_r(r) ( (r) &= 0xF7 ) +#define res4_r(r) ( (r) &= 0xEF ) +#define res5_r(r) ( (r) &= 0xDF ) +#define res6_r(r) ( (r) &= 0xBF ) +#define res7_r(r) ( (r) &= 0x7F ) + +//res n,(hl) (16 cycles): +//Unset bitn of value at address stored in HL: +#define resn_mem_hl(n) do { \ + const unsigned resn_mem_hl_var_addr = HL(); \ + unsigned resn_mem_hl_var_tmp; \ +\ + READ(resn_mem_hl_var_tmp, resn_mem_hl_var_addr); \ + resn_mem_hl_var_tmp &= ~(1 << (n)); \ +\ + WRITE(resn_mem_hl_var_addr, resn_mem_hl_var_tmp); \ +} while (0) + + +//16-BIT LOADS: +//ld rr,nn (12 cycles) +//set rr to 16-bit value of next 2 bytes in memory +#define ld_rr_nn(r1, r2) do { \ + PC_READ(r2); \ + PC_READ(r1); \ +} while (0) + +//push rr (16 cycles): +//Push value of register pair onto stack: +#define push_rr(r1, r2) do { \ + PUSH(r1, r2); \ + cycleCounter += 4; \ +} while (0) + +//pop rr (12 cycles): +//Pop two bytes off stack into register pair: +#define pop_rr(r1, r2) do { \ + READ(r2, SP); \ + SP = (SP + 1) & 0xFFFF; \ + READ(r1, SP); \ + SP = (SP + 1) & 0xFFFF; \ +} while (0) + +//8-BIT ALU: +//add a,r (4 cycles): +//add a,(addr) (8 cycles): +//Add 8-bit value to A, check flags: +#define add_a_u8(u8) do { \ + HF1 = A; \ + HF2 = u8; \ + ZF = CF = A + HF2; \ + A = ZF & 0xFF; \ +} while (0) + +//adc a,r (4 cycles): +//adc a,(addr) (8 cycles): +//Add 8-bit value+CF to A, check flags: +#define adc_a_u8(u8) do { \ + HF1 = A; \ + HF2 = (CF & 0x100) | (u8); \ + ZF = CF = (CF >> 8 & 1) + (u8) + A; \ + A = ZF & 0xFF; \ +} while (0) + +//sub a,r (4 cycles): +//sub a,(addr) (8 cycles): +//Subtract 8-bit value from A, check flags: +#define sub_a_u8(u8) do { \ + HF1 = A; \ + HF2 = u8; \ + ZF = CF = A - HF2; \ + A = ZF & 0xFF; \ + HF2 |= 0x400; \ +} while (0) + +//sbc a,r (4 cycles): +//sbc a,(addr) (8 cycles): +//Subtract CF and 8-bit value from A, check flags: +#define sbc_a_u8(u8) do { \ + HF1 = A; \ + HF2 = 0x400 | (CF & 0x100) | (u8); \ + ZF = CF = A - ((CF >> 8) & 1) - (u8); \ + A = ZF & 0xFF; \ +} while (0) + +//and a,r (4 cycles): +//and a,(addr) (8 cycles): +//bitwise and 8-bit value into A, check flags: +#define and_a_u8(u8) do { \ + HF2 = 0x200; \ + CF = 0; \ + A &= (u8); \ + ZF = A; \ +} while (0) + +//or a,r (4 cycles): +//or a,(hl) (8 cycles): +//bitwise or 8-bit value into A, check flags: +#define or_a_u8(u8) do { \ + CF = HF2 = 0; \ + A |= (u8); \ + ZF = A; \ +} while (0) + +//xor a,r (4 cycles): +//xor a,(hl) (8 cycles): +//bitwise xor 8-bit value into A, check flags: +#define xor_a_u8(u8) do { \ + CF = HF2 = 0; \ + A ^= (u8); \ + ZF = A; \ +} while (0) + +//cp a,r (4 cycles): +//cp a,(addr) (8 cycles): +//Compare (subtract without storing result) 8-bit value to A, check flags: +#define cp_a_u8(u8) do { \ + HF1 = A; \ + HF2 = u8; \ + ZF = CF = A - HF2; \ + HF2 |= 0x400; \ +} while (0) + +//inc r (4 cycles): +//Increment value of 8-bit register, check flags except CF: +#define inc_r(r) do { \ + HF2 = (r) | 0x800; \ + ZF = (r) + 1; \ + (r) = ZF & 0xFF; \ +} while (0) + +//dec r (4 cycles): +//Decrement value of 8-bit register, check flags except CF: +#define dec_r(r) do { \ + HF2 = (r) | 0xC00; \ + ZF = (r) - 1; \ + (r) = ZF & 0xFF; \ +} while (0) + +//16-BIT ARITHMETIC +//add hl,rr (8 cycles): +//add 16-bit register to HL, check flags except ZF: +/*#define add_hl_rr(rh, rl) do { \ + L = HF1 = L + (rl); \ + HF1 >>= 8; \ + HF1 += H; \ + HF2 = (rh); \ + H = CF = HF1 + (rh); \ + cycleCounter += 4; \ +} while (0)*/ + +#define add_hl_rr(rh, rl) do { \ + CF = L + (rl); \ + L = CF & 0xFF; \ + HF1 = H; \ + HF2 = (CF & 0x100) | (rh); \ + CF = H + (CF >> 8) + (rh); \ + H = CF & 0xFF; \ + cycleCounter += 4; \ +} while (0) + +//inc rr (8 cycles): +//Increment 16-bit register: +#define inc_rr(rh, rl) do { \ + const unsigned inc_rr_var_tmp = (rl) + 1; \ + (rl) = inc_rr_var_tmp & 0xFF; \ + (rh) = ((rh) + (inc_rr_var_tmp >> 8)) & 0xFF; \ + cycleCounter += 4; \ +} while (0) + +//dec rr (8 cycles): +//Decrement 16-bit register: +#define dec_rr(rh, rl) do { \ + const unsigned dec_rr_var_tmp = (rl) - 1; \ + (rl) = dec_rr_var_tmp & 0xFF; \ + (rh) = ((rh) - (dec_rr_var_tmp >> 8 & 1)) & 0xFF; \ + cycleCounter += 4; \ +} while (0) + +#define sp_plus_n(sumout) do { \ + unsigned sp_plus_n_var_n; \ + PC_READ(sp_plus_n_var_n); \ + sp_plus_n_var_n = (sp_plus_n_var_n ^ 0x80) - 0x80; \ + \ + const unsigned sp_plus_n_var_sum = SP + sp_plus_n_var_n; \ + CF = SP ^ sp_plus_n_var_n ^ sp_plus_n_var_sum; \ + HF2 = CF << 5 & 0x200; \ + ZF = 1; \ + cycleCounter += 4; \ + (sumout) = sp_plus_n_var_sum & 0xFFFF; \ +} while (0) + +//JUMPS: +//jp nn (16 cycles): +//Jump to address stored in the next two bytes in memory: +#define jp_nn() do { \ + unsigned jp_nn_var_l, jp_nn_var_h; \ +\ + PC_READ(jp_nn_var_l); \ + PC_READ(jp_nn_var_h); \ +\ + PC_MOD(jp_nn_var_h << 8 | jp_nn_var_l); \ +} while (0) + +//jr disp (12 cycles): +//Jump to value of next (signed) byte in memory+current address: +#define jr_disp() do { \ + unsigned jr_disp_var_tmp; \ +\ + PC_READ(jr_disp_var_tmp); \ + jr_disp_var_tmp = (jr_disp_var_tmp ^ 0x80) - 0x80; \ +\ + PC_MOD((PC + jr_disp_var_tmp) & 0xFFFF); \ +} while (0) + +// CALLS, RESTARTS AND RETURNS: +// call nn (24 cycles): +// Jump to 16-bit immediate operand and push return address onto stack: +#define call_nn() do { \ + unsigned const npc = (PC + 2) & 0xFFFF; \ + jp_nn(); \ + PUSH(npc >> 8, npc & 0xFF); \ +} while (0) + +//rst n (16 Cycles): +//Push present address onto stack, jump to address n (one of 00h,08h,10h,18h,20h,28h,30h,38h): +#define rst_n(n) do { \ + PUSH(PC >> 8, PC & 0xFF); \ + PC_MOD(n); \ +} while (0) + +//ret (16 cycles): +//Pop two bytes from the stack and jump to that address: +#define ret() do { \ + unsigned ret_var_l, ret_var_h; \ +\ + pop_rr(ret_var_h, ret_var_l); \ +\ + PC_MOD(ret_var_h << 8 | ret_var_l); \ +} while (0) + +void CPU::process(const unsigned long cycles) { + memory.setEndtime(cycleCounter_, cycles); + hitInterruptAddress = 0; + memory.updateInput(); + + //unsigned char A = A_; + unsigned long cycleCounter = cycleCounter_; + + while (memory.isActive()) { + //unsigned short PC = PC_; + + if (memory.halted()) { + if (cycleCounter < memory.nextEventTime()) { + const unsigned long cycles = memory.nextEventTime() - cycleCounter; + cycleCounter += cycles + (-cycles & 3); + } + } else while (cycleCounter < memory.nextEventTime()) { + unsigned char opcode = 0x00; + + int FullPC = PC; + + if (PC >= 0x4000 && PC <= 0x7FFF) + FullPC |= memory.curRomBank() << 16; + + for (int i = 0; i < numInterruptAddresses; i++) { + if (FullPC == interruptAddresses[i]) { + hitInterruptAddress = interruptAddresses[i]; + memory.setEndtime(cycleCounter, 0); + break; + } + } + + if (!hitInterruptAddress) + { + if (tracecallback) { + int result[14]; + result[0] = cycleCounter; + result[1] = PC; + result[2] = SP; + result[3] = A; + result[4] = B; + result[5] = C; + result[6] = D; + result[7] = E; + result[8] = F(); + result[9] = H; + result[10] = L; + result[11] = skip; + PC_READ_FIRST(opcode); + result[12] = opcode; + result[13] = memory.debugGetLY(); + tracecallback((void *)result); + } + else { + PC_READ_FIRST(opcode); + } + + if (skip) { + PC = (PC - 1) & 0xFFFF; + skip = false; + } + } + + switch (opcode) { + //nop (4 cycles): + //Do nothing for 4 cycles: + case 0x00: + break; + case 0x01: + ld_rr_nn(B, C); + break; + case 0x02: + WRITE(BC(), A); + break; + case 0x03: + inc_rr(B, C); + break; + case 0x04: + inc_r(B); + break; + case 0x05: + dec_r(B); + break; + case 0x06: + PC_READ(B); + break; + + //rlca (4 cycles): + //Rotate 8-bit register A left, store old bit7 in CF. Reset SF, HCF, ZF: + case 0x07: + CF = A << 1; + A = (CF | CF >> 8) & 0xFF; + HF2 = 0; + ZF = 1; + break; + + //ld (nn),SP (20 cycles): + //Put value of SP into address given by next 2 bytes in memory: + case 0x08: + { + unsigned l, h; + + PC_READ(l); + PC_READ(h); + + const unsigned addr = h << 8 | l; + + WRITE(addr, SP & 0xFF); + WRITE((addr + 1) & 0xFFFF, SP >> 8); + } + break; + + case 0x09: + add_hl_rr(B, C); + break; + case 0x0A: + READ(A, BC()); + break; + case 0x0B: + dec_rr(B, C); + break; + case 0x0C: + inc_r(C); + break; + case 0x0D: + dec_r(C); + break; + case 0x0E: + PC_READ(C); + break; + + //rrca (4 cycles): + //Rotate 8-bit register A right, store old bit0 in CF. Reset SF, HCF, ZF: + case 0x0F: + CF = A << 8 | A; + A = CF >> 1 & 0xFF; + HF2 = 0; + ZF = 1; + break; + + //stop (4 cycles): + //Halt CPU and LCD display until button pressed: + case 0x10: + { + unsigned char followingByte; + PEEK(followingByte, PC); + PC = (PC + 1) & 0xFFFF; + + //if (followingByte != 0x00) { + //memory.di(); + //memory.blackScreen(); + //} + + cycleCounter = memory.stop(cycleCounter); + + if (cycleCounter < memory.nextEventTime()) { + const unsigned long cycles = memory.nextEventTime() - cycleCounter; + cycleCounter += cycles + (-cycles & 3); + } + } + break; + case 0x11: + ld_rr_nn(D, E); + break; + case 0x12: + WRITE(DE(), A); + break; + case 0x13: + inc_rr(D, E); + break; + case 0x14: + inc_r(D); + break; + case 0x15: + dec_r(D); + break; + case 0x16: + PC_READ(D); + break; + + //rla (4 cycles): + //Rotate 8-bit register A left through CF, store old bit7 in CF, old CF value becomes bit0. Reset SF, HCF, ZF: + case 0x17: + { + const unsigned oldcf = CF >> 8 & 1; + CF = A << 1; + A = (CF | oldcf) & 0xFF; + } + + HF2 = 0; + ZF = 1; + break; + + case 0x18: + jr_disp(); + break; + case 0x19: + add_hl_rr(D, E); + break; + case 0x1A: + READ(A, DE()); + break; + case 0x1B: + dec_rr(D, E); + break; + case 0x1C: + inc_r(E); + break; + case 0x1D: + dec_r(E); + break; + case 0x1E: + PC_READ(E); + break; + + //rra (4 cycles): + //Rotate 8-bit register A right through CF, store old bit0 in CF, old CF value becomes bit7. Reset SF, HCF, ZF: + case 0x1F: + { + const unsigned oldcf = CF & 0x100; + CF = A << 8; + A = (A | oldcf) >> 1; + } + + HF2 = 0; + ZF = 1; + break; + + //jr nz,disp (12;8 cycles): + //Jump to value of next (signed) byte in memory+current address if ZF is unset: + case 0x20: + if (ZF & 0xFF) { + jr_disp(); + } else { + PC_MOD((PC + 1) & 0xFFFF); + } + break; + + case 0x21: + ld_rr_nn(H, L); + break; + + //ldi (hl),a (8 cycles): + //Put A into memory address in hl. Increment HL: + case 0x22: + { + unsigned addr = HL(); + + WRITE(addr, A); + + addr = (addr + 1) & 0xFFFF; + L = addr; + H = addr >> 8; + } + break; + + case 0x23: + inc_rr(H, L); + break; + case 0x24: + inc_r(H); + break; + case 0x25: + dec_r(H); + break; + case 0x26: + PC_READ(H); + break; + + + //daa (4 cycles): + //Adjust register A to correctly represent a BCD. Check ZF, HF and CF: + case 0x27: + /*{ + unsigned correction = ((A > 0x99) || (CF & 0x100)) ? 0x60 : 0x00; + + calcHF(HF1, HF2); + + if ((A & 0x0F) > 0x09 || (HF2 & 0x200)) + correction |= 0x06; + + HF1 = A; + HF2 = (HF2 & 0x400) | correction; + CF = (correction & 0x40) << 2; + A = (HF2 & 0x400) ? A - correction : (A + correction); + ZF = A; + }*/ + + calcHF(HF1, HF2); + + { + unsigned correction = (CF & 0x100) ? 0x60 : 0x00; + + if (HF2 & 0x200) + correction |= 0x06; + + if (!(HF2 &= 0x400)) { + if ((A & 0x0F) > 0x09) + correction |= 0x06; + + if (A > 0x99) + correction |= 0x60; + + A += correction; + } else + A -= correction; + + CF = correction << 2 & 0x100; + ZF = A; + A &= 0xFF; + } + break; + + //jr z,disp (12;8 cycles): + //Jump to value of next (signed) byte in memory+current address if ZF is set: + case 0x28: + if (ZF & 0xFF) { + PC_MOD((PC + 1) & 0xFFFF); + } else { + jr_disp(); + } + break; + + //add hl,hl (8 cycles): + //add 16-bit register HL to HL, check flags except ZF: + case 0x29: + add_hl_rr(H, L); + break; + + //ldi a,(hl) (8 cycles): + //Put value at address in hl into A. Increment HL: + case 0x2A: + { + unsigned addr = HL(); + + READ(A, addr); + + addr = (addr + 1) & 0xFFFF; + L = addr; + H = addr >> 8; + } + break; + + case 0x2B: + dec_rr(H, L); + break; + case 0x2C: + inc_r(L); + break; + case 0x2D: + dec_r(L); + break; + case 0x2E: + PC_READ(L); + break; + + //cpl (4 cycles): + //Complement register A. (Flip all bits), set SF and HCF: + case 0x2F: /*setSubtractFlag(); setHalfCarryFlag();*/ + HF2 = 0x600; + A ^= 0xFF; + break; + + //jr nc,disp (12;8 cycles): + //Jump to value of next (signed) byte in memory+current address if CF is unset: + case 0x30: + if (CF & 0x100) { + PC_MOD((PC + 1) & 0xFFFF); + } else { + jr_disp(); + } + break; + + //ld sp,nn (12 cycles) + //set sp to 16-bit value of next 2 bytes in memory + case 0x31: + { + unsigned l, h; + + PC_READ(l); + PC_READ(h); + + SP = h << 8 | l; + } + break; + + //ldd (hl),a (8 cycles): + //Put A into memory address in hl. Decrement HL: + case 0x32: + { + unsigned addr = HL(); + + WRITE(addr, A); + + addr = (addr - 1) & 0xFFFF; + L = addr; + H = addr >> 8; + } + break; + + case 0x33: + SP = (SP + 1) & 0xFFFF; + cycleCounter += 4; + break; + + //inc (hl) (12 cycles): + //Increment value at address in hl, check flags except CF: + case 0x34: + { + const unsigned addr = HL(); + + READ(HF2, addr); + ZF = HF2 + 1; + WRITE(addr, ZF & 0xFF); + HF2 |= 0x800; + } + break; + + //dec (hl) (12 cycles): + //Decrement value at address in hl, check flags except CF: + case 0x35: + { + const unsigned addr = HL(); + + READ(HF2, addr); + ZF = HF2 - 1; + WRITE(addr, ZF & 0xFF); + HF2 |= 0xC00; + } + break; + + //ld (hl),n (12 cycles): + //set memory at address in hl to value of next byte in memory: + case 0x36: + { + unsigned tmp; + + PC_READ(tmp); + WRITE(HL(), tmp); + } + break; + + //scf (4 cycles): + //Set CF. Unset SF and HCF: + case 0x37: /*setCarryFlag(); unsetSubtractFlag(); unsetHalfCarryFlag();*/ + CF = 0x100; + HF2 = 0; + break; + + //jr c,disp (12;8 cycles): + //Jump to value of next (signed) byte in memory+current address if CF is set: + case 0x38: //PC+=(((int8_t)memory.read(PC++))*CarryFlag()); Cycles(8); break; + if (CF & 0x100) { + jr_disp(); + } else { + PC_MOD((PC + 1) & 0xFFFF); + } + break; + + //add hl,sp (8 cycles): + //add SP to HL, check flags except ZF: + case 0x39: /*add_hl_rr(SP>>8, SP); break;*/ + CF = L + SP; + L = CF & 0xFF; + HF1 = H; + HF2 = ((CF ^ SP) & 0x100) | SP >> 8; + CF >>= 8; + CF += H; + H = CF & 0xFF; + cycleCounter += 4; + break; + + //ldd a,(hl) (8 cycles): + //Put value at address in hl into A. Decrement HL: + case 0x3A: + { + unsigned addr = HL(); + + A = memory.read(addr, cycleCounter); + cycleCounter += 4; + + addr = (addr - 1) & 0xFFFF; + L = addr; + H = addr >> 8; + } + break; + + case 0x3B: + SP = (SP - 1) & 0xFFFF; + cycleCounter += 4; + break; + case 0x3C: + inc_r(A); + break; + case 0x3D: + dec_r(A); + break; + case 0x3E: + PC_READ(A); + break; + + //ccf (4 cycles): + //Complement CF (unset if set vv.) Unset SF and HCF. + case 0x3F: /*complementCarryFlag(); unsetSubtractFlag(); unsetHalfCarryFlag();*/ + CF ^= 0x100; + HF2 = 0; + break; + + //ld r,r (4 cycles):next_irqEventTime + //ld r,(r) (8 cycles): + case 0x40: + B = B; + break; + case 0x41: + B = C; + break; + case 0x42: + B = D; + break; + case 0x43: + B = E; + break; + case 0x44: + B = H; + break; + case 0x45: + B = L; + break; + case 0x46: + READ(B, HL()); + break; + case 0x47: + B = A; + break; + case 0x48: + C = B; + break; + case 0x49: + C = C; + break; + case 0x4A: + C = D; + break; + case 0x4B: + C = E; + break; + case 0x4C: + C = H; + break; + case 0x4D: + C = L; + break; + case 0x4E: + READ(C, HL()); + break; + case 0x4F: + C = A; + break; + case 0x50: + D = B; + break; + case 0x51: + D = C; + break; + case 0x52: + D = D; + break; + case 0x53: + D = E; + break; + case 0x54: + D = H; + break; + case 0x55: + D = L; + break; + case 0x56: + READ(D, HL()); + break; + case 0x57: + D = A; + break; + case 0x58: + E = B; + break; + case 0x59: + E = C; + break; + case 0x5A: + E = D; + break; + case 0x5B: + E = E; + break; + case 0x5C: + E = H; + break; + case 0x5D: + E = L; + break; + case 0x5E: + READ(E, HL()); + break; + case 0x5F: + E = A; + break; + case 0x60: + H = B; + break; + case 0x61: + H = C; + break; + case 0x62: + H = D; + break; + case 0x63: + H = E; + break; + case 0x64: + H = H; + break; + case 0x65: + H = L; + break; + case 0x66: + READ(H, HL()); + break; + case 0x67: + H = A; + break; + case 0x68: + L = B; + break; + case 0x69: + L = C; + break; + case 0x6A: + L = D; + break; + case 0x6B: + L = E; + break; + case 0x6C: + L = H; + break; + case 0x6D: + L = L; + break; + case 0x6E: + READ(L, HL()); + break; + case 0x6F: + L = A; + break; + case 0x70: + WRITE(HL(), B); + break; + case 0x71: + WRITE(HL(), C); + break; + case 0x72: + WRITE(HL(), D); + break; + case 0x73: + WRITE(HL(), E); + break; + case 0x74: + WRITE(HL(), H); + break; + case 0x75: + WRITE(HL(), L); + break; + + //halt (4 cycles): + case 0x76: + if (!memory.ime() && (memory.ff_read(0xFF0F, cycleCounter) & memory.ff_read(0xFFFF, cycleCounter) & 0x1F)) { + if (memory.isCgb()) + cycleCounter += 4; + else + skip = true; + } else { + memory.halt(); + + if (cycleCounter < memory.nextEventTime()) { + const unsigned long cycles = memory.nextEventTime() - cycleCounter; + cycleCounter += cycles + (-cycles & 3); + } + } + + break; + case 0x77: + WRITE(HL(), A); + break; + case 0x78: + A = B; + break; + case 0x79: + A = C; + break; + case 0x7A: + A = D; + break; + case 0x7B: + A = E; + break; + case 0x7C: + A = H; + break; + case 0x7D: + A = L; + break; + case 0x7E: + READ(A, HL()); + break; + case 0x7F: + // A = A; + break; + case 0x80: + add_a_u8(B); + break; + case 0x81: + add_a_u8(C); + break; + case 0x82: + add_a_u8(D); + break; + case 0x83: + add_a_u8(E); + break; + case 0x84: + add_a_u8(H); + break; + case 0x85: + add_a_u8(L); + break; + case 0x86: + { + unsigned data; + + READ(data, HL()); + + add_a_u8(data); + } + break; + case 0x87: + add_a_u8(A); + break; + case 0x88: + adc_a_u8(B); + break; + case 0x89: + adc_a_u8(C); + break; + case 0x8A: + adc_a_u8(D); + break; + case 0x8B: + adc_a_u8(E); + break; + case 0x8C: + adc_a_u8(H); + break; + case 0x8D: + adc_a_u8(L); + break; + case 0x8E: + { + unsigned data; + + READ(data, HL()); + + adc_a_u8(data); + } + break; + case 0x8F: + adc_a_u8(A); + break; + case 0x90: + sub_a_u8(B); + break; + case 0x91: + sub_a_u8(C); + break; + case 0x92: + sub_a_u8(D); + break; + case 0x93: + sub_a_u8(E); + break; + case 0x94: + sub_a_u8(H); + break; + case 0x95: + sub_a_u8(L); + break; + case 0x96: + { + unsigned data; + + READ(data, HL()); + + sub_a_u8(data); + } + break; + //A-A is always 0: + case 0x97: + HF2 = 0x400; + CF = ZF = A = 0; + break; + case 0x98: + sbc_a_u8(B); + break; + case 0x99: + sbc_a_u8(C); + break; + case 0x9A: + sbc_a_u8(D); + break; + case 0x9B: + sbc_a_u8(E); + break; + case 0x9C: + sbc_a_u8(H); + break; + case 0x9D: + sbc_a_u8(L); + break; + case 0x9E: + { + unsigned data; + + READ(data, HL()); + + sbc_a_u8(data); + } + break; + case 0x9F: + sbc_a_u8(A); + break; + case 0xA0: + and_a_u8(B); + break; + case 0xA1: + and_a_u8(C); + break; + case 0xA2: + and_a_u8(D); + break; + case 0xA3: + and_a_u8(E); + break; + case 0xA4: + and_a_u8(H); + break; + case 0xA5: + and_a_u8(L); + break; + case 0xA6: + { + unsigned data; + + READ(data, HL()); + + and_a_u8(data); + } + break; + //A&A will always be A: + case 0xA7: + ZF = A; + CF = 0; + HF2 = 0x200; + break; + case 0xA8: + xor_a_u8(B); + break; + case 0xA9: + xor_a_u8(C); + break; + case 0xAA: + xor_a_u8(D); + break; + case 0xAB: + xor_a_u8(E); + break; + case 0xAC: + xor_a_u8(H); + break; + case 0xAD: + xor_a_u8(L); + break; + case 0xAE: + { + unsigned data; + + READ(data, HL()); + + xor_a_u8(data); + } + break; + //A^A will always be 0: + case 0xAF: + CF = HF2 = ZF = A = 0; + break; + case 0xB0: + or_a_u8(B); + break; + case 0xB1: + or_a_u8(C); + break; + case 0xB2: + or_a_u8(D); + break; + case 0xB3: + or_a_u8(E); + break; + case 0xB4: + or_a_u8(H); + break; + case 0xB5: + or_a_u8(L); + break; + case 0xB6: + { + unsigned data; + + READ(data, HL()); + + or_a_u8(data); + } + break; + //A|A will always be A: + case 0xB7: + ZF = A; + HF2 = CF = 0; + break; + case 0xB8: + cp_a_u8(B); + break; + case 0xB9: + cp_a_u8(C); + break; + case 0xBA: + cp_a_u8(D); + break; + case 0xBB: + cp_a_u8(E); + break; + case 0xBC: + cp_a_u8(H); + break; + case 0xBD: + cp_a_u8(L); + break; + case 0xBE: + { + unsigned data; + + READ(data, HL()); + + cp_a_u8(data); + } + break; + //A always equals A: + case 0xBF: + CF = ZF = 0; + HF2 = 0x400; + break; + + //ret nz (20;8 cycles): + //Pop two bytes from the stack and jump to that address, if ZF is unset: + case 0xC0: + cycleCounter += 4; + + if (ZF & 0xFF) { + ret(); + } + break; + + case 0xC1: + pop_rr(B, C); + break; + + //jp nz,nn (16;12 cycles): + //Jump to address stored in next two bytes in memory if ZF is unset: + case 0xC2: + if (ZF & 0xFF) { + jp_nn(); + } else { + PC_MOD((PC + 2) & 0xFFFF); + cycleCounter += 4; + } + break; + + case 0xC3: + jp_nn(); + break; + + //call nz,nn (24;12 cycles): + //Push address of next instruction onto stack and then jump to address stored in next two bytes in memory, if ZF is unset: + case 0xC4: + if (ZF & 0xFF) { + call_nn(); + } else { + PC_MOD((PC + 2) & 0xFFFF); + cycleCounter += 4; + } + break; + + case 0xC5: + push_rr(B, C); + break; + case 0xC6: + { + unsigned data; + + PC_READ(data); + + add_a_u8(data); + } + break; + case 0xC7: + rst_n(0x00); + break; + + //ret z (20;8 cycles): + //Pop two bytes from the stack and jump to that address, if ZF is set: + case 0xC8: + cycleCounter += 4; + + if (!(ZF & 0xFF)) { + ret(); + } + + break; + + //ret (16 cycles): + //Pop two bytes from the stack and jump to that address: + case 0xC9: + ret(); + break; + + //jp z,nn (16;12 cycles): + //Jump to address stored in next two bytes in memory if ZF is set: + case 0xCA: + if (ZF & 0xFF) { + PC_MOD((PC + 2) & 0xFFFF); + cycleCounter += 4; + } else { + jp_nn(); + } + break; + + + //CB OPCODES (Shifts, rotates and bits): + case 0xCB: + PC_READ(opcode); + + switch (opcode) { + case 0x00: + rlc_r(B); + break; + case 0x01: + rlc_r(C); + break; + case 0x02: + rlc_r(D); + break; + case 0x03: + rlc_r(E); + break; + case 0x04: + rlc_r(H); + break; + case 0x05: + rlc_r(L); + break; + //rlc (hl) (16 cycles): + //Rotate 8-bit value stored at address in HL left, store old bit7 in CF. Reset SF and HCF. Check ZF: + case 0x06: + { + const unsigned addr = HL(); + + READ(CF, addr); + CF <<= 1; + + ZF = CF | (CF >> 8); + + WRITE(addr, ZF & 0xFF); + + HF2 = 0; + } + break; + case 0x07: + rlc_r(A); + break; + case 0x08: + rrc_r(B); + break; + case 0x09: + rrc_r(C); + break; + case 0x0A: + rrc_r(D); + break; + case 0x0B: + rrc_r(E); + break; + case 0x0C: + rrc_r(H); + break; + case 0x0D: + rrc_r(L); + break; + //rrc (hl) (16 cycles): + //Rotate 8-bit value stored at address in HL right, store old bit0 in CF. Reset SF and HCF. Check ZF: + case 0x0E: + { + const unsigned addr = HL(); + + READ(ZF, addr); + + CF = ZF << 8; + + WRITE(addr, (ZF | CF) >> 1 & 0xFF); + + HF2 = 0; + } + break; + case 0x0F: + rrc_r(A); + break; + case 0x10: + rl_r(B); + break; + case 0x11: + rl_r(C); + break; + case 0x12: + rl_r(D); + break; + case 0x13: + rl_r(E); + break; + case 0x14: + rl_r(H); + break; + case 0x15: + rl_r(L); + break; + //rl (hl) (16 cycles): + //Rotate 8-bit value stored at address in HL left thorugh CF, store old bit7 in CF, old CF value becoms bit0. Reset SF and HCF. Check ZF: + case 0x16: + { + const unsigned addr = HL(); + const unsigned oldcf = CF >> 8 & 1; + + READ(CF, addr); + CF <<= 1; + + ZF = CF | oldcf; + + WRITE(addr, ZF & 0xFF); + + HF2 = 0; + } + break; + case 0x17: + rl_r(A); + break; + case 0x18: + rr_r(B); + break; + case 0x19: + rr_r(C); + break; + case 0x1A: + rr_r(D); + break; + case 0x1B: + rr_r(E); + break; + case 0x1C: + rr_r(H); + break; + case 0x1D: + rr_r(L); + break; + //rr (hl) (16 cycles): + //Rotate 8-bit value stored at address in HL right thorugh CF, store old bit0 in CF, old CF value becoms bit7. Reset SF and HCF. Check ZF: + case 0x1E: + { + const unsigned addr = HL(); + + READ(ZF, addr); + + const unsigned oldcf = CF & 0x100; + CF = ZF << 8; + ZF = (ZF | oldcf) >> 1; + + WRITE(addr, ZF); + + HF2 = 0; + } + break; + case 0x1F: + rr_r(A); + break; + case 0x20: + sla_r(B); + break; + case 0x21: + sla_r(C); + break; + case 0x22: + sla_r(D); + break; + case 0x23: + sla_r(E); + break; + case 0x24: + sla_r(H); + break; + case 0x25: + sla_r(L); + break; + //sla (hl) (16 cycles): + //Shift 8-bit value stored at address in HL left, store old bit7 in CF. Reset SF and HCF. Check ZF: + case 0x26: + { + const unsigned addr = HL(); + + READ(CF, addr); + CF <<= 1; + + ZF = CF; + + WRITE(addr, ZF & 0xFF); + + HF2 = 0; + } + break; + case 0x27: + sla_r(A); + break; + case 0x28: + sra_r(B); + break; + case 0x29: + sra_r(C); + break; + case 0x2A: + sra_r(D); + break; + case 0x2B: + sra_r(E); + break; + case 0x2C: + sra_r(H); + break; + case 0x2D: + sra_r(L); + break; + //sra (hl) (16 cycles): + //Shift 8-bit value stored at address in HL right, store old bit0 in CF, bit7=old bit7. Reset SF and HCF. Check ZF: + case 0x2E: + { + const unsigned addr = HL(); + + READ(CF, addr); + + ZF = CF >> 1; + + WRITE(addr, ZF | (CF & 0x80)); + + CF <<= 8; + HF2 = 0; + } + break; + case 0x2F: + sra_r(A); + break; + case 0x30: + swap_r(B); + break; + case 0x31: + swap_r(C); + break; + case 0x32: + swap_r(D); + break; + case 0x33: + swap_r(E); + break; + case 0x34: + swap_r(H); + break; + case 0x35: + swap_r(L); + break; + //swap (hl) (16 cycles): + //Swap upper and lower nibbles of 8-bit value stored at address in HL, reset flags, check zero flag: + case 0x36: + { + const unsigned addr = HL(); + + READ(ZF, addr); + + WRITE(addr, (ZF << 4 | ZF >> 4) & 0xFF); + + CF = HF2 = 0; + } + break; + case 0x37: + swap_r(A); + break; + case 0x38: + srl_r(B); + break; + case 0x39: + srl_r(C); + break; + case 0x3A: + srl_r(D); + break; + case 0x3B: + srl_r(E); + break; + case 0x3C: + srl_r(H); + break; + case 0x3D: + srl_r(L); + break; + //srl (hl) (16 cycles): + //Shift 8-bit value stored at address in HL right, store old bit0 in CF. Reset SF and HCF. Check ZF: + case 0x3E: + { + const unsigned addr = HL(); + + READ(CF, addr); + + ZF = CF >> 1; + + WRITE(addr, ZF); + + CF <<= 8; + HF2 = 0; + } + break; + case 0x3F: + srl_r(A); + break; + case 0x40: + bit0_u8(B); + break; + case 0x41: + bit0_u8(C); + break; + case 0x42: + bit0_u8(D); + break; + case 0x43: + bit0_u8(E); + break; + case 0x44: + bit0_u8(H); + break; + case 0x45: + bit0_u8(L); + break; + case 0x46: + { + unsigned data; + + READ(data, HL()); + + bit0_u8(data); + } + break; + case 0x47: + bit0_u8(A); + break; + case 0x48: + bit1_u8(B); + break; + case 0x49: + bit1_u8(C); + break; + case 0x4A: + bit1_u8(D); + break; + case 0x4B: + bit1_u8(E); + break; + case 0x4C: + bit1_u8(H); + break; + case 0x4D: + bit1_u8(L); + break; + case 0x4E: + { + unsigned data; + + READ(data, HL()); + + bit1_u8(data); + } + break; + case 0x4F: + bit1_u8(A); + break; + case 0x50: + bit2_u8(B); + break; + case 0x51: + bit2_u8(C); + break; + case 0x52: + bit2_u8(D); + break; + case 0x53: + bit2_u8(E); + break; + case 0x54: + bit2_u8(H); + break; + case 0x55: + bit2_u8(L); + break; + case 0x56: + { + unsigned data; + + READ(data, HL()); + + bit2_u8(data); + } + break; + case 0x57: + bit2_u8(A); + break; + case 0x58: + bit3_u8(B); + break; + case 0x59: + bit3_u8(C); + break; + case 0x5A: + bit3_u8(D); + break; + case 0x5B: + bit3_u8(E); + break; + case 0x5C: + bit3_u8(H); + break; + case 0x5D: + bit3_u8(L); + break; + case 0x5E: + { + unsigned data; + + READ(data, HL()); + + bit3_u8(data); + } + break; + case 0x5F: + bit3_u8(A); + break; + case 0x60: + bit4_u8(B); + break; + case 0x61: + bit4_u8(C); + break; + case 0x62: + bit4_u8(D); + break; + case 0x63: + bit4_u8(E); + break; + case 0x64: + bit4_u8(H); + break; + case 0x65: + bit4_u8(L); + break; + case 0x66: + { + unsigned data; + + READ(data, HL()); + + bit4_u8(data); + } + break; + case 0x67: + bit4_u8(A); + break; + case 0x68: + bit5_u8(B); + break; + case 0x69: + bit5_u8(C); + break; + case 0x6A: + bit5_u8(D); + break; + case 0x6B: + bit5_u8(E); + break; + case 0x6C: + bit5_u8(H); + break; + case 0x6D: + bit5_u8(L); + break; + case 0x6E: + { + unsigned data; + + READ(data, HL()); + + bit5_u8(data); + } + break; + case 0x6F: + bit5_u8(A); + break; + case 0x70: + bit6_u8(B); + break; + case 0x71: + bit6_u8(C); + break; + case 0x72: + bit6_u8(D); + break; + case 0x73: + bit6_u8(E); + break; + case 0x74: + bit6_u8(H); + break; + case 0x75: + bit6_u8(L); + break; + case 0x76: + { + unsigned data; + + READ(data, HL()); + + bit6_u8(data); + } + break; + case 0x77: + bit6_u8(A); + break; + case 0x78: + bit7_u8(B); + break; + case 0x79: + bit7_u8(C); + break; + case 0x7A: + bit7_u8(D); + break; + case 0x7B: + bit7_u8(E); + break; + case 0x7C: + bit7_u8(H); + break; + case 0x7D: + bit7_u8(L); + break; + case 0x7E: + { + unsigned data; + + READ(data, HL()); + + bit7_u8(data); + } + break; + case 0x7F: + bit7_u8(A); + break; + case 0x80: + res0_r(B); + break; + case 0x81: + res0_r(C); + break; + case 0x82: + res0_r(D); + break; + case 0x83: + res0_r(E); + break; + case 0x84: + res0_r(H); + break; + case 0x85: + res0_r(L); + break; + case 0x86: + resn_mem_hl(0); + break; + case 0x87: + res0_r(A); + break; + case 0x88: + res1_r(B); + break; + case 0x89: + res1_r(C); + break; + case 0x8A: + res1_r(D); + break; + case 0x8B: + res1_r(E); + break; + case 0x8C: + res1_r(H); + break; + case 0x8D: + res1_r(L); + break; + case 0x8E: + resn_mem_hl(1); + break; + case 0x8F: + res1_r(A); + break; + case 0x90: + res2_r(B); + break; + case 0x91: + res2_r(C); + break; + case 0x92: + res2_r(D); + break; + case 0x93: + res2_r(E); + break; + case 0x94: + res2_r(H); + break; + case 0x95: + res2_r(L); + break; + case 0x96: + resn_mem_hl(2); + break; + case 0x97: + res2_r(A); + break; + case 0x98: + res3_r(B); + break; + case 0x99: + res3_r(C); + break; + case 0x9A: + res3_r(D); + break; + case 0x9B: + res3_r(E); + break; + case 0x9C: + res3_r(H); + break; + case 0x9D: + res3_r(L); + break; + case 0x9E: + resn_mem_hl(3); + break; + case 0x9F: + res3_r(A); + break; + case 0xA0: + res4_r(B); + break; + case 0xA1: + res4_r(C); + break; + case 0xA2: + res4_r(D); + break; + case 0xA3: + res4_r(E); + break; + case 0xA4: + res4_r(H); + break; + case 0xA5: + res4_r(L); + break; + case 0xA6: + resn_mem_hl(4); + break; + case 0xA7: + res4_r(A); + break; + case 0xA8: + res5_r(B); + break; + case 0xA9: + res5_r(C); + break; + case 0xAA: + res5_r(D); + break; + case 0xAB: + res5_r(E); + break; + case 0xAC: + res5_r(H); + break; + case 0xAD: + res5_r(L); + break; + case 0xAE: + resn_mem_hl(5); + break; + case 0xAF: + res5_r(A); + break; + case 0xB0: + res6_r(B); + break; + case 0xB1: + res6_r(C); + break; + case 0xB2: + res6_r(D); + break; + case 0xB3: + res6_r(E); + break; + case 0xB4: + res6_r(H); + break; + case 0xB5: + res6_r(L); + break; + case 0xB6: + resn_mem_hl(6); + break; + case 0xB7: + res6_r(A); + break; + case 0xB8: + res7_r(B); + break; + case 0xB9: + res7_r(C); + break; + case 0xBA: + res7_r(D); + break; + case 0xBB: + res7_r(E); + break; + case 0xBC: + res7_r(H); + break; + case 0xBD: + res7_r(L); + break; + case 0xBE: + resn_mem_hl(7); + break; + case 0xBF: + res7_r(A); + break; + case 0xC0: + set0_r(B); + break; + case 0xC1: + set0_r(C); + break; + case 0xC2: + set0_r(D); + break; + case 0xC3: + set0_r(E); + break; + case 0xC4: + set0_r(H); + break; + case 0xC5: + set0_r(L); + break; + case 0xC6: + setn_mem_hl(0); + break; + case 0xC7: + set0_r(A); + break; + case 0xC8: + set1_r(B); + break; + case 0xC9: + set1_r(C); + break; + case 0xCA: + set1_r(D); + break; + case 0xCB: + set1_r(E); + break; + case 0xCC: + set1_r(H); + break; + case 0xCD: + set1_r(L); + break; + case 0xCE: + setn_mem_hl(1); + break; + case 0xCF: + set1_r(A); + break; + case 0xD0: + set2_r(B); + break; + case 0xD1: + set2_r(C); + break; + case 0xD2: + set2_r(D); + break; + case 0xD3: + set2_r(E); + break; + case 0xD4: + set2_r(H); + break; + case 0xD5: + set2_r(L); + break; + case 0xD6: + setn_mem_hl(2); + break; + case 0xD7: + set2_r(A); + break; + case 0xD8: + set3_r(B); + break; + case 0xD9: + set3_r(C); + break; + case 0xDA: + set3_r(D); + break; + case 0xDB: + set3_r(E); + break; + case 0xDC: + set3_r(H); + break; + case 0xDD: + set3_r(L); + break; + case 0xDE: + setn_mem_hl(3); + break; + case 0xDF: + set3_r(A); + break; + case 0xE0: + set4_r(B); + break; + case 0xE1: + set4_r(C); + break; + case 0xE2: + set4_r(D); + break; + case 0xE3: + set4_r(E); + break; + case 0xE4: + set4_r(H); + break; + case 0xE5: + set4_r(L); + break; + case 0xE6: + setn_mem_hl(4); + break; + case 0xE7: + set4_r(A); + break; + case 0xE8: + set5_r(B); + break; + case 0xE9: + set5_r(C); + break; + case 0xEA: + set5_r(D); + break; + case 0xEB: + set5_r(E); + break; + case 0xEC: + set5_r(H); + break; + case 0xED: + set5_r(L); + break; + case 0xEE: + setn_mem_hl(5); + break; + case 0xEF: + set5_r(A); + break; + case 0xF0: + set6_r(B); + break; + case 0xF1: + set6_r(C); + break; + case 0xF2: + set6_r(D); + break; + case 0xF3: + set6_r(E); + break; + case 0xF4: + set6_r(H); + break; + case 0xF5: + set6_r(L); + break; + case 0xF6: + setn_mem_hl(6); + break; + case 0xF7: + set6_r(A); + break; + case 0xF8: + set7_r(B); + break; + case 0xF9: + set7_r(C); + break; + case 0xFA: + set7_r(D); + break; + case 0xFB: + set7_r(E); + break; + case 0xFC: + set7_r(H); + break; + case 0xFD: + set7_r(L); + break; + case 0xFE: + setn_mem_hl(7); + break; + case 0xFF: + set7_r(A); + break; +// default: break; + } + break; + + + //call z,nn (24;12 cycles): + //Push address of next instruction onto stack and then jump to address stored in next two bytes in memory, if ZF is set: + case 0xCC: + if (ZF & 0xFF) { + PC_MOD((PC + 2) & 0xFFFF); + cycleCounter += 4; + } else { + call_nn(); + } + break; + + case 0xCD: + call_nn(); + break; + case 0xCE: + { + unsigned data; + + PC_READ(data); + + adc_a_u8(data); + } + break; + case 0xCF: + rst_n(0x08); + break; + + //ret nc (20;8 cycles): + //Pop two bytes from the stack and jump to that address, if CF is unset: + case 0xD0: + cycleCounter += 4; + + if (!(CF & 0x100)) { + ret(); + } + + break; + + case 0xD1: + pop_rr(D, E); + break; + + //jp nc,nn (16;12 cycles): + //Jump to address stored in next two bytes in memory if CF is unset: + case 0xD2: + if (CF & 0x100) { + PC_MOD((PC + 2) & 0xFFFF); + cycleCounter += 4; + } else { + jp_nn(); + } + break; + + case 0xD3: /*doesn't exist*/ + skip = true; + memory.di(); + break; + + //call nc,nn (24;12 cycles): + //Push address of next instruction onto stack and then jump to address stored in next two bytes in memory, if CF is unset: + case 0xD4: + if (CF & 0x100) { + PC_MOD((PC + 2) & 0xFFFF); + cycleCounter += 4; + } else { + call_nn(); + } + break; + + case 0xD5: + push_rr(D, E); + break; + case 0xD6: + { + unsigned data; + + PC_READ(data); + + sub_a_u8(data); + } + break; + case 0xD7: + rst_n(0x10); + break; + + //ret c (20;8 cycles): + //Pop two bytes from the stack and jump to that address, if CF is set: + case 0xD8: + cycleCounter += 4; + + if (CF & 0x100) { + ret(); + } + + break; + + //reti (16 cycles): + //Pop two bytes from the stack and jump to that address, then enable interrupts: + case 0xD9: + { + unsigned l, h; + + pop_rr(h, l); + + memory.ei(cycleCounter); + + PC_MOD(h << 8 | l); + } + break; + + //jp c,nn (16;12 cycles): + //Jump to address stored in next two bytes in memory if CF is set: + case 0xDA: //PC=( ((PC+2)*(1-CarryFlag())) + (((memory.read(PC+1)<<8)+memory.read(PC))*CarryFlag()) ); Cycles(12); break; + if (CF & 0x100) { + jp_nn(); + } else { + PC_MOD((PC + 2) & 0xFFFF); + cycleCounter += 4; + } + break; + + case 0xDB: /*doesn't exist*/ + skip = true; + memory.di(); + break; + + //call z,nn (24;12 cycles): + //Push address of next instruction onto stack and then jump to address stored in next two bytes in memory, if CF is set: + case 0xDC: + if (CF & 0x100) { + call_nn(); + } else { + PC_MOD((PC + 2) & 0xFFFF); + cycleCounter += 4; + } + break; + + case 0xDD: /*doesn't exist*/ + skip = true; + memory.di(); + break; + + case 0xDE: + { + unsigned data; + + PC_READ(data); + + sbc_a_u8(data); + } + break; + case 0xDF: + rst_n(0x18); + break; + + //ld ($FF00+n),a (12 cycles): + //Put value in A into address (0xFF00 + next byte in memory): + case 0xE0: + { + unsigned tmp; + + PC_READ(tmp); + + FF_WRITE(0xFF00 | tmp, A); + } + break; + + case 0xE1: + pop_rr(H, L); + break; + + //ld ($FF00+C),a (8 ycles): + //Put A into address (0xFF00 + register C): + case 0xE2: + FF_WRITE(0xFF00 | C, A); + break; + case 0xE3: /*doesn't exist*/ + skip = true; + memory.di(); + break; + case 0xE4: /*doesn't exist*/ + skip = true; + memory.di(); + break; + case 0xE5: + push_rr(H, L); + break; + case 0xE6: + { + unsigned data; + + PC_READ(data); + + and_a_u8(data); + } + break; + case 0xE7: + rst_n(0x20); + break; + + //add sp,n (16 cycles): + //Add next (signed) byte in memory to SP, reset ZF and SF, check HCF and CF: + case 0xE8: + /*{ + int8_t tmp = int8_t(memory.pc_read(PC++, cycleCounter)); + HF2 = (((SP & 0xFFF) + tmp) >> 3) & 0x200; + CF = SP + tmp; + SP = CF; + CF >>= 8; + ZF = 1; + cycleCounter += 12; + }*/ + sp_plus_n(SP); + cycleCounter += 4; + break; + + //jp hl (4 cycles): + //Jump to address in hl: + case 0xE9: + PC = HL(); + break; + + //ld (nn),a (16 cycles): + //set memory at address given by the next 2 bytes to value in A: + //Incrementing PC before call, because of possible interrupt. + case 0xEA: + { + unsigned l, h; + + PC_READ(l); + PC_READ(h); + + WRITE(h << 8 | l, A); + } + break; + + case 0xEB: /*doesn't exist*/ + skip = true; + memory.di(); + break; + case 0xEC: /*doesn't exist*/ + skip = true; + memory.di(); + break; + case 0xED: /*doesn't exist*/ + skip = true; + memory.di(); + break; + case 0xEE: + { + unsigned data; + + PC_READ(data); + + xor_a_u8(data); + } + break; + case 0xEF: + rst_n(0x28); + break; + + //ld a,($FF00+n) (12 cycles): + //Put value at address (0xFF00 + next byte in memory) into A: + case 0xF0: + { + unsigned tmp; + + PC_READ(tmp); + + FF_READ(A, 0xFF00 | tmp); + } + break; + + case 0xF1: /*pop_rr(A, F); Cycles(12); break;*/ + { + unsigned F; + + pop_rr(A, F); + + FROM_F(F); + } + break; + + //ld a,($FF00+C) (8 cycles): + //Put value at address (0xFF00 + register C) into A: + case 0xF2: + FF_READ(A, 0xFF00 | C); + break; + + //di (4 cycles): + case 0xF3: + memory.di(); + break; + + case 0xF4: /*doesn't exist*/ + skip = true; + memory.di(); + break; + case 0xF5: /*push_rr(A, F); Cycles(16); break;*/ + calcHF(HF1, HF2); + + { + unsigned F = F(); + + push_rr(A, F); + } + break; + + case 0xF6: + { + unsigned data; + + PC_READ(data); + + or_a_u8(data); + } + break; + case 0xF7: + rst_n(0x30); + break; + + //ldhl sp,n (12 cycles): + //Put (sp+next (signed) byte in memory) into hl (unsets ZF and SF, may enable HF and CF): + case 0xF8: + /*{ + int8_t tmp = int8_t(memory.pc_read(PC++, cycleCounter)); + HF2 = (((SP & 0xFFF) + tmp) >> 3) & 0x200; + CF = SP + tmp; + L = CF; + CF >>= 8; + H = CF; + ZF = 1; + cycleCounter += 8; + }*/ + { + unsigned sum; + sp_plus_n(sum); + L = sum & 0xFF; + H = sum >> 8; + } + break; + + //ld sp,hl (8 cycles): + //Put value in HL into SP + case 0xF9: + SP = HL(); + cycleCounter += 4; + break; + + //ld a,(nn) (16 cycles): + //set A to value in memory at address given by the 2 next bytes. + case 0xFA: + { + unsigned l, h; + + PC_READ(l); + PC_READ(h); + + READ(A, h << 8 | l); + } + break; + + //ei (4 cycles): + //Enable Interrupts after next instruction: + case 0xFB: + memory.ei(cycleCounter); + break; + + case 0xFC: /*doesn't exist*/ + skip = true; + memory.di(); + break; + case 0xFD: /*doesn't exist*/ + skip = true; + memory.di(); + break; + case 0xFE: + { + unsigned data; + + PC_READ(data); + + cp_a_u8(data); + } + break; + case 0xFF: + rst_n(0x38); + break; +// default: break; + } + } + + //PC_ = PC; + cycleCounter = memory.event(cycleCounter); + } + + //A_ = A; + cycleCounter_ = cycleCounter; +} + +void CPU::GetRegs(int *dest) +{ + dest[0] = PC; + dest[1] = SP; + dest[2] = A; + dest[3] = B; + dest[4] = C; + dest[5] = D; + dest[6] = E; + dest[7] = F(); + dest[8] = H; + dest[9] = L; +} + +void CPU::SetInterruptAddresses(int *addrs, int numAddrs) +{ + interruptAddresses = addrs; + numInterruptAddresses = numAddrs; +} + +int CPU::GetHitInterruptAddress() +{ + return hitInterruptAddress; +} + +SYNCFUNC(CPU) +{ + SSS(memory); + NSS(cycleCounter_); + NSS(PC); + NSS(SP); + NSS(HF1); + NSS(HF2); + NSS(ZF); + NSS(CF); + NSS(A); + NSS(B); + NSS(C); + NSS(D); + NSS(E); + NSS(H); + NSS(L); + NSS(skip); +} + +} diff --git a/libgambatte/src/cpu.h b/libgambatte/src/cpu.h index b36112f7ea..46dc592015 100644 --- a/libgambatte/src/cpu.h +++ b/libgambatte/src/cpu.h @@ -1,136 +1,147 @@ -/*************************************************************************** - * 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); - } - - void setLinkCallback(void (*callback)()) { - memory.setLinkCallback(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); - - templatevoid SyncState(NewState *ns); -}; - -} - -#endif +/*************************************************************************** + * 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; + + int *interruptAddresses; + int numInterruptAddresses; + int hitInterruptAddress; + + 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 setLinkCallback(void(*callback)()) { + memory.setLinkCallback(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* cgbBiosBuffer() { return memory.cgbBiosBuffer(); } + unsigned char* dmgBiosBuffer() { return memory.dmgBiosBuffer(); } + bool gbIsCgb() { return memory.gbIsCgb(); } + + //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); + + void SetInterruptAddresses(int *addrs, int numAddrs); + int GetHitInterruptAddress(); + + templatevoid SyncState(NewState *ns); +}; + +} + +#endif diff --git a/libgambatte/src/gambatte.cpp b/libgambatte/src/gambatte.cpp index 5e9c59e63f..511024947a 100644 --- a/libgambatte/src/gambatte.cpp +++ b/libgambatte/src/gambatte.cpp @@ -26,12 +26,12 @@ namespace gambatte { struct GB::Priv { CPU cpu; - bool gbaCgbMode; + unsigned loadflags; unsigned layersMask; uint_least32_t vbuff[160*144]; - Priv() : gbaCgbMode(false), layersMask(LAYER_MASK_BG | LAYER_MASK_OBJ) + Priv() : loadflags(0), layersMask(LAYER_MASK_BG | LAYER_MASK_OBJ) { } @@ -94,8 +94,7 @@ void GB::reset(const std::uint32_t now) { SaveState state; p_->cpu.setStatePtrs(state); - - setInitState(state, p_->cpu.isCgb(), p_->gbaCgbMode, now); + setInitState(state, !(p_->loadflags & FORCE_DMG), p_->loadflags & GBA_CGB, p_->loadflags & TRUE_COLOR, now); p_->cpu.loadState(state); if (length > 0) { @@ -146,11 +145,12 @@ int GB::load(const char *romfiledata, unsigned romfilelength, const std::uint32_ // p_->cpu.saveSavedata(); const int failed = p_->cpu.load(romfiledata, romfilelength, flags & FORCE_DMG, flags & MULTICART_COMPAT); - + if (!failed) { SaveState state; p_->cpu.setStatePtrs(state); - setInitState(state, p_->cpu.isCgb(), p_->gbaCgbMode = flags & GBA_CGB, now); + p_->loadflags = flags; + setInitState(state, !(flags & FORCE_DMG), flags & GBA_CGB, flags & TRUE_COLOR, now); p_->cpu.loadState(state); //p_->cpu.loadSavedata(); } @@ -158,6 +158,16 @@ int GB::load(const char *romfiledata, unsigned romfilelength, const std::uint32_ return failed; } +int GB::loadGBCBios(const char* biosfiledata) { + memcpy(p_->cpu.cgbBiosBuffer(), biosfiledata, 0x900); + return 0; +} + +int GB::loadDMGBios(const char* biosfiledata) { + memcpy(p_->cpu.dmgBiosBuffer(), biosfiledata, 0x100); + return 0; +} + bool GB::isCgb() const { return p_->cpu.isCgb(); } @@ -228,10 +238,20 @@ void GB::GetRegs(int *dest) { p_->cpu.GetRegs(dest); } +void GB::SetInterruptAddresses(int *addrs, int numAddrs) +{ + p_->cpu.SetInterruptAddresses(addrs, numAddrs); +} + +int GB::GetHitInterruptAddress() +{ + return p_->cpu.GetHitInterruptAddress(); +} + SYNCFUNC(GB) { SSS(p_->cpu); - NSS(p_->gbaCgbMode); + NSS(p_->loadflags); NSS(p_->vbuff); } diff --git a/libgambatte/src/initstate.cpp b/libgambatte/src/initstate.cpp index 5f11104d08..c791a140de 100644 --- a/libgambatte/src/initstate.cpp +++ b/libgambatte/src/initstate.cpp @@ -1041,11 +1041,11 @@ static void setInitialCgbIoamhram(unsigned char *const ioamhram) { }; static const unsigned char ffxxDump[0x100] = { - 0xCF, 0x00, 0x7C, 0xFF, 0x44, 0x00, 0x00, 0xF8, + 0xCF, 0x00, 0x7C, 0xFF, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, - 0x80, 0xBF, 0xF3, 0xFF, 0xBF, 0xFF, 0x3F, 0x00, + 0x80, 0x3F, 0x00, 0xFF, 0xBF, 0xFF, 0x3F, 0x00, 0xFF, 0xBF, 0x7F, 0xFF, 0x9F, 0xFF, 0xBF, 0xFF, - 0xFF, 0x00, 0x00, 0xBF, 0x77, 0xF3, 0xF1, 0xFF, + 0xFF, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x70, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, @@ -1146,61 +1146,58 @@ 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 bool trueColors, const std::uint32_t now) { static const unsigned char cgbObjpDump[0x40] = { - 0x00, 0x00, 0xF2, 0xAB, - 0x61, 0xC2, 0xD9, 0xBA, - 0x88, 0x6E, 0xDD, 0x63, - 0x28, 0x27, 0xFB, 0x9F, - 0x35, 0x42, 0xD6, 0xD4, - 0x50, 0x48, 0x57, 0x5E, - 0x23, 0x3E, 0x3D, 0xCA, - 0x71, 0x21, 0x37, 0xC0, - 0xC6, 0xB3, 0xFB, 0xF9, - 0x08, 0x00, 0x8D, 0x29, - 0xA3, 0x20, 0xDB, 0x87, - 0x62, 0x05, 0x5D, 0xD4, - 0x0E, 0x08, 0xFE, 0xAF, - 0x20, 0x02, 0xD7, 0xFF, - 0x07, 0x6A, 0x55, 0xEC, + 0x00, 0x00, 0xF2, 0xAB, + 0x61, 0xC2, 0xD9, 0xBA, + 0x88, 0x6E, 0xDD, 0x63, + 0x28, 0x27, 0xFB, 0x9F, + 0x35, 0x42, 0xD6, 0xD4, + 0x50, 0x48, 0x57, 0x5E, + 0x23, 0x3E, 0x3D, 0xCA, + 0x71, 0x21, 0x37, 0xC0, + 0xC6, 0xB3, 0xFB, 0xF9, + 0x08, 0x00, 0x8D, 0x29, + 0xA3, 0x20, 0xDB, 0x87, + 0x62, 0x05, 0x5D, 0xD4, + 0x0E, 0x08, 0xFE, 0xAF, + 0x20, 0x02, 0xD7, 0xFF, + 0x07, 0x6A, 0x55, 0xEC, 0x83, 0x40, 0x0B, 0x77 }; - - 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.cpu.cycleCounter = 8; + state.cpu.PC = 0; + state.cpu.SP = 0; + state.cpu.A = 0; + state.cpu.B = 0; + state.cpu.C = 0; + state.cpu.D = 0; + state.cpu.E = 0; + state.cpu.F = 0; + state.cpu.H = 0; + state.cpu.L = 0; + state.cpu.skip = false; + state.mem.biosMode = true; + state.mem.cgbSwitching = false; + state.mem.agbMode = gbaCgbMode; std::memset(state.mem.sram.ptr, 0xFF, state.mem.sram.getSz()); + + setInitialVram(state.mem.vram.ptr, cgb); if (cgb) { setInitialCgbWram(state.mem.wram.ptr); - } - else { - setInitialDmgWram(state.mem.wram.ptr); - } - - if (cgb) { setInitialCgbIoamhram(state.mem.ioamhram.ptr); - } - else { + } else { + setInitialDmgWram(state.mem.wram.ptr); setInitialDmgIoamhram(state.mem.ioamhram.ptr); } - - state.mem.ioamhram.ptr[0x140] = 0x91; - state.mem.ioamhram.ptr[0x104] = 0x1C; + + state.mem.ioamhram.ptr[0x104] = 0; + state.mem.ioamhram.ptr[0x140] = 0; state.mem.ioamhram.ptr[0x144] = 0x00; - + state.mem.divLastUpdate = 0; state.mem.timaLastUpdate = 0; state.mem.tmatime = DISABLED_TIME; @@ -1218,29 +1215,30 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM state.mem.enableRam = false; state.mem.rambankMode = false; state.mem.hdmaTransfer = false; + state.mem.gbIsCgb = cgb; - + for (unsigned i = 0x00; i < 0x40; i += 0x02) { - state.ppu.bgpData.ptr[i] = 0xFF; + state.ppu.bgpData.ptr[i ] = 0xFF; state.ppu.bgpData.ptr[i + 1] = 0x7F; } - + std::memcpy(state.ppu.objpData.ptr, cgbObjpDump, sizeof(cgbObjpDump)); - + if (!cgb) { state.ppu.bgpData.ptr[0] = state.mem.ioamhram.get()[0x147]; state.ppu.objpData.ptr[0] = state.mem.ioamhram.get()[0x148]; state.ppu.objpData.ptr[1] = state.mem.ioamhram.get()[0x149]; } - + for (unsigned pos = 0; pos < 80; ++pos) state.ppu.oamReaderBuf.ptr[pos] = state.mem.ioamhram.ptr[(pos * 2 & ~3) | (pos & 1)]; - + std::fill_n(state.ppu.oamReaderSzbuf.ptr, 40, false); std::memset(state.ppu.spAttribList, 0, sizeof(state.ppu.spAttribList)); std::memset(state.ppu.spByte0List, 0, sizeof(state.ppu.spByte0List)); std::memset(state.ppu.spByte1List, 0, sizeof(state.ppu.spByte1List)); - state.ppu.videoCycles = cgb ? 144 * 456ul + 164 : 153 * 456ul + 396; + state.ppu.videoCycles = 0; state.ppu.enableDisplayM0Time = state.cpu.cycleCounter; state.ppu.winYPos = 0xFF; state.ppu.xpos = 0; @@ -1254,7 +1252,7 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM state.ppu.state = 0; state.ppu.nextSprite = 0; state.ppu.currentSprite = 0; - state.ppu.lyc = state.mem.ioamhram.get()[0x145]; + state.ppu.lyc = state.mem.ioamhram.get()[0x145]; state.ppu.m0lyc = state.mem.ioamhram.get()[0x145]; state.ppu.weMaster = false; state.ppu.winDrawState = 0; @@ -1263,36 +1261,39 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM state.ppu.nextM0Irq = 0; state.ppu.oldWy = state.mem.ioamhram.get()[0x14A]; state.ppu.pendingLcdstatIrq = false; + state.ppu.isCgb = cgb; + state.ppu.trueColors = !trueColors; - state.spu.cycleCounter = 0x1000 | (state.cpu.cycleCounter >> 1 & 0xFFF); // spu.cycleCounter >> 12 & 7 represents the frame sequencer position. - + + state.spu.cycleCounter = 0; // spu.cycleCounter >> 12 & 7 represents the frame sequencer position. + state.spu.ch1.sweep.counter = SoundUnit::COUNTER_DISABLED; state.spu.ch1.sweep.shadow = 0; state.spu.ch1.sweep.nr0 = 0; state.spu.ch1.sweep.negging = false; - state.spu.ch1.duty.nextPosUpdate = (state.spu.cycleCounter & ~1) + 2048 * 2; + state.spu.ch1.duty.nextPosUpdate = (state.spu.cycleCounter & ~1ul) + 37 * 2; state.spu.ch1.duty.nr3 = 0; state.spu.ch1.duty.pos = 0; state.spu.ch1.env.counter = SoundUnit::COUNTER_DISABLED; state.spu.ch1.env.volume = 0; state.spu.ch1.lcounter.counter = SoundUnit::COUNTER_DISABLED; - state.spu.ch1.lcounter.lengthCounter = 0x40; + state.spu.ch1.lcounter.lengthCounter = 0; state.spu.ch1.nr4 = 0; state.spu.ch1.master = true; - - state.spu.ch2.duty.nextPosUpdate = (state.spu.cycleCounter & ~1) + 2048 * 2; + + state.spu.ch2.duty.nextPosUpdate = SoundUnit::COUNTER_DISABLED; state.spu.ch2.duty.nr3 = 0; state.spu.ch2.duty.pos = 0; - state.spu.ch2.env.counter = state.spu.cycleCounter - ((state.spu.cycleCounter - 0x1000) & 0x7FFF) + 8ul * 0x8000; + state.spu.ch2.env.counter = SoundUnit::COUNTER_DISABLED; state.spu.ch2.env.volume = 0; state.spu.ch2.lcounter.counter = SoundUnit::COUNTER_DISABLED; - state.spu.ch2.lcounter.lengthCounter = 0x40; + state.spu.ch2.lcounter.lengthCounter = 0; state.spu.ch2.nr4 = 0; state.spu.ch2.master = false; - + for (unsigned i = 0; i < 0x10; ++i) state.spu.ch3.waveRam.ptr[i] = state.mem.ioamhram.get()[0x130 + i]; - + state.spu.ch3.lcounter.counter = SoundUnit::COUNTER_DISABLED; state.spu.ch3.lcounter.lengthCounter = 0x100; state.spu.ch3.waveCounter = SoundUnit::COUNTER_DISABLED; @@ -1302,16 +1303,16 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM state.spu.ch3.wavePos = 0; state.spu.ch3.sampleBuf = 0; state.spu.ch3.master = false; - + state.spu.ch4.lfsr.counter = state.spu.cycleCounter + 4; state.spu.ch4.lfsr.reg = 0xFF; - state.spu.ch4.env.counter = state.spu.cycleCounter - ((state.spu.cycleCounter - 0x1000) & 0x7FFF) + 8ul * 0x8000; + state.spu.ch4.env.counter = SoundUnit::COUNTER_DISABLED; state.spu.ch4.env.volume = 0; state.spu.ch4.lcounter.counter = SoundUnit::COUNTER_DISABLED; - state.spu.ch4.lcounter.lengthCounter = 0x40; + state.spu.ch4.lcounter.lengthCounter = 0; state.spu.ch4.nr4 = 0; state.spu.ch4.master = false; - + state.rtc.baseTime = now; state.rtc.haltTime = state.rtc.baseTime; state.rtc.dataDh = 0; diff --git a/libgambatte/src/initstate.h b/libgambatte/src/initstate.h index 8d8ed5aaf6..36587e8512 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, bool trueColors, std::uint32_t now); } #endif diff --git a/libgambatte/src/insertion_sort.h b/libgambatte/src/insertion_sort.h index 939ba07442..f6a3c991a4 100644 --- a/libgambatte/src/insertion_sort.h +++ b/libgambatte/src/insertion_sort.h @@ -1,51 +1,51 @@ -/*************************************************************************** - * 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 INSERTION_SORT_H -#define INSERTION_SORT_H - -#include - -template -void insertionSort(T *const start, T *const end, Less less) { - if (start >= end) - return; - - T *a = start; - - while (++a < end) { - const T e = *a; - - T *b = a; - - while (b != start && less(e, *(b - 1))) { - *b = *(b - 1); - b = b - 1; - } - - *b = e; - } -} - -template -inline void insertionSort(T *const start, T *const end) { - insertionSort(start, end, std::less()); -} - -#endif /*INSERTION_SORT_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 INSERTION_SORT_H +#define INSERTION_SORT_H + +#include + +template +void insertionSort(T *const start, T *const end, Less less) { + if (start >= end) + return; + + T *a = start; + + while (++a < end) { + const T e = *a; + + T *b = a; + + while (b != start && less(e, *(b - 1))) { + *b = *(b - 1); + b = b - 1; + } + + *b = e; + } +} + +template +inline void insertionSort(T *const start, T *const end) { + insertionSort(start, end, std::less()); +} + +#endif /*INSERTION_SORT_H*/ diff --git a/libgambatte/src/interruptrequester.cpp b/libgambatte/src/interruptrequester.cpp index 5ed7869c39..c656ad2368 100644 --- a/libgambatte/src/interruptrequester.cpp +++ b/libgambatte/src/interruptrequester.cpp @@ -94,13 +94,13 @@ void InterruptRequester::setIfreg(const unsigned ifreg) { eventTimes.setValue(pendingIrqs() ? minIntTime : static_cast(DISABLED_TIME)); } -SYNCFUNC(InterruptRequester) -{ - SSS(eventTimes); - NSS(minIntTime); - NSS(ifreg_); - NSS(iereg_); - NSS(intFlags.flags_); -} +SYNCFUNC(InterruptRequester) +{ + SSS(eventTimes); + NSS(minIntTime); + NSS(ifreg_); + NSS(iereg_); + NSS(intFlags.flags_); +} } diff --git a/libgambatte/src/interruptrequester.h b/libgambatte/src/interruptrequester.h index c99efc73f6..8d22e35afb 100644 --- a/libgambatte/src/interruptrequester.h +++ b/libgambatte/src/interruptrequester.h @@ -21,7 +21,7 @@ #include "counterdef.h" #include "minkeeper.h" -#include "newstate.h" +#include "newstate.h" namespace gambatte { struct SaveState; @@ -62,6 +62,7 @@ public: void resetCc(unsigned long oldCc, unsigned long newCc); unsigned ifreg() const { return ifreg_; } + unsigned iereg() const { return iereg_; } unsigned pendingIrqs() const { return ifreg_ & iereg_; } bool ime() const { return intFlags.ime(); } bool halted() const { return intFlags.halted(); } @@ -81,7 +82,7 @@ public: void setEventTime(const MemEventId id, unsigned long value) { eventTimes.setValue(id, value); } unsigned long eventTime(MemEventId id) const { return eventTimes.value(id); } - templatevoid SyncState(NewState *ns); + templatevoid SyncState(NewState *ns); }; inline void flagHdmaReq(InterruptRequester *const intreq) { intreq->setEventTime(0); } diff --git a/libgambatte/src/mem/cartridge.cpp b/libgambatte/src/mem/cartridge.cpp index 548508ba3d..e062fb0c96 100644 --- a/libgambatte/src/mem/cartridge.cpp +++ b/libgambatte/src/mem/cartridge.cpp @@ -1,753 +1,750 @@ -/*************************************************************************** - * 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); - } - - 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(); - } - - virtual void SyncState(NewState *ns, bool isReader) - { - NSS(rombank); - NSS(rambank); - 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); - } - - 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)); - } - - 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(); - } - - 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(); - } - - virtual void SyncState(NewState *ns, bool isReader) - { - NSS(rombank); - NSS(rambank); - 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(); - } - - 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); - //cgb = forceDmg ? false : true; - 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; -} - -SYNCFUNC(Cartridge) -{ - SSS(memptrs); - SSS(rtc); - TSS(mbc); -} - -} +/*************************************************************************** + * 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); + } + + 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(); + } + + virtual void SyncState(NewState *ns, bool isReader) + { + NSS(rombank); + NSS(rambank); + 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); + } + + 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)); + } + + 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(); + } + + 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(); + } + + virtual void SyncState(NewState *ns, bool isReader) + { + NSS(rombank); + NSS(rambank); + 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(); + } + + 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 = !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; +} + +SYNCFUNC(Cartridge) +{ + SSS(memptrs); + SSS(rtc); + TSS(mbc); +} + +} diff --git a/libgambatte/src/mem/cartridge.h b/libgambatte/src/mem/cartridge.h index 0015924966..511c4438ae 100644 --- a/libgambatte/src/mem/cartridge.h +++ b/libgambatte/src/mem/cartridge.h @@ -1,112 +1,113 @@ -/*************************************************************************** - * 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 CARTRIDGE_H -#define CARTRIDGE_H - -#include "memptrs.h" -#include "rtc.h" -#include "savestate.h" -#include -#include -#include -#include "newstate.h" - -namespace gambatte { - - //DOOM -//enum eAddressMappingType -//{ -// eAddressMappingType_ROM, -// eAddressMappingType_RAM -//}; -// -//struct AddressMapping -//{ -// int32_t address; -// eAddressMappingType type; -//}; - -class Mbc { -public: - virtual ~Mbc() {} - virtual void romWrite(unsigned P, unsigned data) = 0; - virtual void loadState(const SaveState::Mem &ss) = 0; - virtual bool isAddressWithinAreaRombankCanBeMappedTo(unsigned address, unsigned rombank) const = 0; - //virtual void mapAddress(AddressMapping* mapping, unsigned address) const = 0; //DOOM - - templatevoid SyncState(NewState *ns) - { - // can't have virtual templates, so.. - SyncState(ns, isReader); - } - virtual void SyncState(NewState *ns, bool isReader) = 0; -}; - -class Cartridge { - MemPtrs memptrs; - Rtc rtc; - std::auto_ptr mbc; - -public: - void setStatePtrs(SaveState &); - void loadState(const SaveState &); - - bool loaded() const { return mbc.get(); } - - const unsigned char * rmem(unsigned area) const { return memptrs.rmem(area); } - unsigned char * wmem(unsigned area) const { return memptrs.wmem(area); } - unsigned char * vramdata() const { return memptrs.vramdata(); } - unsigned char * romdata(unsigned area) const { return memptrs.romdata(area); } - unsigned char * wramdata(unsigned area) const { return memptrs.wramdata(area); } - const unsigned char * rdisabledRam() const { return memptrs.rdisabledRam(); } - const unsigned char * rsrambankptr() const { return memptrs.rsrambankptr(); } - unsigned char * wsrambankptr() const { return memptrs.wsrambankptr(); } - unsigned char * vrambankptr() const { return memptrs.vrambankptr(); } - OamDmaSrc oamDmaSrc() const { return memptrs.oamDmaSrc(); } - - void setVrambank(unsigned bank) { memptrs.setVrambank(bank); } - void setWrambank(unsigned bank) { memptrs.setWrambank(bank); } - void setOamDmaSrc(OamDmaSrc oamDmaSrc) { memptrs.setOamDmaSrc(oamDmaSrc); } - - void mbcWrite(unsigned addr, unsigned data) { mbc->romWrite(addr, data); } - - bool isCgb() const { return gambatte::isCgb(memptrs); } - - void rtcWrite(unsigned data) { rtc.write(data); } - unsigned char rtcRead() const { return *rtc.getActive(); } - - void loadSavedata(const char *data); - int saveSavedataLength(); - void saveSavedata(char *dest); - - bool getMemoryArea(int which, unsigned char **data, int *length) const; - - int loadROM(const char *romfiledata, unsigned romfilelength, bool forceDmg, bool multicartCompat); - const char * romTitle() const { return reinterpret_cast(memptrs.romdata() + 0x134); } - - void setRTCCallback(std::uint32_t (*callback)()) { - rtc.setRTCCallback(callback); - } - - templatevoid SyncState(NewState *ns); -}; - -} - -#endif +/*************************************************************************** + * 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 CARTRIDGE_H +#define CARTRIDGE_H + +#include "memptrs.h" +#include "rtc.h" +#include "savestate.h" +#include +#include +#include +#include "newstate.h" + +namespace gambatte { + + //DOOM +//enum eAddressMappingType +//{ +// eAddressMappingType_ROM, +// eAddressMappingType_RAM +//}; +// +//struct AddressMapping +//{ +// int32_t address; +// eAddressMappingType type; +//}; + +class Mbc { +public: + virtual ~Mbc() {} + virtual void romWrite(unsigned P, unsigned data) = 0; + virtual void loadState(const SaveState::Mem &ss) = 0; + virtual bool isAddressWithinAreaRombankCanBeMappedTo(unsigned address, unsigned rombank) const = 0; + //virtual void mapAddress(AddressMapping* mapping, unsigned address) const = 0; //DOOM + + templatevoid SyncState(NewState *ns) + { + // can't have virtual templates, so.. + SyncState(ns, isReader); + } + virtual void SyncState(NewState *ns, bool isReader) = 0; +}; + +class Cartridge { + MemPtrs memptrs; + Rtc rtc; + std::auto_ptr mbc; + +public: + void setStatePtrs(SaveState &); + void loadState(const SaveState &); + + bool loaded() const { return mbc.get(); } + + const unsigned char * rmem(unsigned area) const { return memptrs.rmem(area); } + unsigned char * wmem(unsigned area) const { return memptrs.wmem(area); } + unsigned char * vramdata() const { return memptrs.vramdata(); } + unsigned char * romdata(unsigned area) const { return memptrs.romdata(area); } + unsigned char * wramdata(unsigned area) const { return memptrs.wramdata(area); } + const unsigned char * rdisabledRam() const { return memptrs.rdisabledRam(); } + const unsigned char * rsrambankptr() const { return memptrs.rsrambankptr(); } + unsigned char * wsrambankptr() const { return memptrs.wsrambankptr(); } + unsigned char * vrambankptr() const { return memptrs.vrambankptr(); } + OamDmaSrc oamDmaSrc() const { return memptrs.oamDmaSrc(); } + unsigned curRomBank() const { return memptrs.curRomBank(); } + + void setVrambank(unsigned bank) { memptrs.setVrambank(bank); } + void setWrambank(unsigned bank) { memptrs.setWrambank(bank); } + void setOamDmaSrc(OamDmaSrc oamDmaSrc) { memptrs.setOamDmaSrc(oamDmaSrc); } + + void mbcWrite(unsigned addr, unsigned data) { mbc->romWrite(addr, data); } + + bool isCgb() const { return gambatte::isCgb(memptrs); } + + void rtcWrite(unsigned data) { rtc.write(data); } + unsigned char rtcRead() const { return *rtc.getActive(); } + + void loadSavedata(const char *data); + int saveSavedataLength(); + void saveSavedata(char *dest); + + bool getMemoryArea(int which, unsigned char **data, int *length) const; + + int loadROM(const char *romfiledata, unsigned romfilelength, bool forceDmg, bool multicartCompat); + const char * romTitle() const { return reinterpret_cast(memptrs.romdata() + 0x134); } + + void setRTCCallback(std::uint32_t (*callback)()) { + rtc.setRTCCallback(callback); + } + + templatevoid SyncState(NewState *ns); +}; + +} + +#endif diff --git a/libgambatte/src/mem/memptrs.cpp b/libgambatte/src/mem/memptrs.cpp index 1b0399770a..bd166e86cf 100644 --- a/libgambatte/src/mem/memptrs.cpp +++ b/libgambatte/src/mem/memptrs.cpp @@ -1,226 +1,223 @@ -/*************************************************************************** - * 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_) - -SYNCFUNC(MemPtrs) -{ - /* - int memchunk_len_old = memchunk_len; - int memchunk_saveoffs_old = memchunk_saveoffs; - int memchunk_savelen_old = memchunk_savelen; - */ - - NSS(memchunk_len); - NSS(memchunk_saveoffs); - NSS(memchunk_savelen); - - /* - if (isReader) - { - if (memchunk_len != memchunk_len_old || memchunk_saveoffs != memchunk_saveoffs_old || memchunk_savelen != memchunk_savelen_old) - __debugbreak(); - } - */ - - PSS(memchunk_ + memchunk_saveoffs, memchunk_savelen); - - MSS(rmem_[0x0]); - MSS(wmem_[0x0]); - MSS(rmem_[0x1]); - MSS(wmem_[0x1]); - MSS(rmem_[0x2]); - MSS(wmem_[0x2]); - MSS(rmem_[0x3]); - MSS(wmem_[0x3]); - MSS(rmem_[0x4]); - MSS(wmem_[0x4]); - MSS(rmem_[0x5]); - MSS(wmem_[0x5]); - MSS(rmem_[0x6]); - MSS(wmem_[0x6]); - MSS(rmem_[0x7]); - MSS(wmem_[0x7]); - MSS(rmem_[0x8]); - MSS(wmem_[0x8]); - MSS(rmem_[0x9]); - MSS(wmem_[0x9]); - MSS(rmem_[0xa]); - MSS(wmem_[0xa]); - MSS(rmem_[0xb]); - MSS(wmem_[0xb]); - MSS(rmem_[0xc]); - MSS(wmem_[0xc]); - MSS(rmem_[0xd]); - MSS(wmem_[0xd]); - MSS(rmem_[0xe]); - MSS(wmem_[0xe]); - MSS(rmem_[0xf]); - MSS(wmem_[0xf]); - //for (int i = 0; i < 0x10; i++) - //{ - // MSS(rmem_[i]); - // MSS(wmem_[i]); - //} - MSS(romdata_[0]); - MSS(romdata_[1]); - MSS(wramdata_[0]); - MSS(wramdata_[1]); - MSS(vrambankptr_); - MSS(rsrambankptr_); - MSS(wsrambankptr_); - MSS(rambankdata_); - MSS(wramdataend_); - NSS(oamDmaSrc_); -} - -} +/*************************************************************************** + * 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), + curRomBank_(1), + 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) { + curRomBank_ = 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) +{ + /* + int memchunk_len_old = memchunk_len; + int memchunk_saveoffs_old = memchunk_saveoffs; + int memchunk_savelen_old = memchunk_savelen; + */ + + NSS(memchunk_len); + NSS(memchunk_saveoffs); + NSS(memchunk_savelen); + + /* + if (isReader) + { + if (memchunk_len != memchunk_len_old || memchunk_saveoffs != memchunk_saveoffs_old || memchunk_savelen != memchunk_savelen_old) + __debugbreak(); + } + */ + + PSS(memchunk_ + memchunk_saveoffs, memchunk_savelen); + + MSS(rmem_[0x0]); + MSS(wmem_[0x0]); + MSS(rmem_[0x1]); + MSS(wmem_[0x1]); + MSS(rmem_[0x2]); + MSS(wmem_[0x2]); + MSS(rmem_[0x3]); + MSS(wmem_[0x3]); + MSS(rmem_[0x4]); + MSS(wmem_[0x4]); + MSS(rmem_[0x5]); + MSS(wmem_[0x5]); + MSS(rmem_[0x6]); + MSS(wmem_[0x6]); + MSS(rmem_[0x7]); + MSS(wmem_[0x7]); + MSS(rmem_[0x8]); + MSS(wmem_[0x8]); + MSS(rmem_[0x9]); + MSS(wmem_[0x9]); + MSS(rmem_[0xa]); + MSS(wmem_[0xa]); + MSS(rmem_[0xb]); + MSS(wmem_[0xb]); + MSS(rmem_[0xc]); + MSS(wmem_[0xc]); + MSS(rmem_[0xd]); + MSS(wmem_[0xd]); + MSS(rmem_[0xe]); + MSS(wmem_[0xe]); + MSS(rmem_[0xf]); + MSS(wmem_[0xf]); + //for (int i = 0; i < 0x10; i++) + //{ + // MSS(rmem_[i]); + // MSS(wmem_[i]); + //} + MSS(romdata_[0]); + MSS(romdata_[1]); + MSS(wramdata_[0]); + MSS(wramdata_[1]); + MSS(vrambankptr_); + MSS(rsrambankptr_); + MSS(wsrambankptr_); + MSS(rambankdata_); + MSS(wramdataend_); + NSS(oamDmaSrc_); + NSS(curRomBank_); +} + +} diff --git a/libgambatte/src/mem/memptrs.h b/libgambatte/src/mem/memptrs.h index 5e4f772a26..68fd2b0006 100644 --- a/libgambatte/src/mem/memptrs.h +++ b/libgambatte/src/mem/memptrs.h @@ -1,93 +1,96 @@ -/*************************************************************************** - * 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); - - templatevoid SyncState(NewState *ns); -}; - -inline bool isCgb(const MemPtrs &memptrs) { - return memptrs.wramdataend() - memptrs.wramdata(0) == 0x8000; -} - -} - -#endif +/*************************************************************************** + * 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_; + + unsigned curRomBank_; + + 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_; } + unsigned curRomBank() const { return curRomBank_; } + + 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 diff --git a/libgambatte/src/mem/rtc.cpp b/libgambatte/src/mem/rtc.cpp index 3fc1ed1a2e..ae97d0876c 100644 --- a/libgambatte/src/mem/rtc.cpp +++ b/libgambatte/src/mem/rtc.cpp @@ -1,177 +1,177 @@ -/*************************************************************************** - * 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 "rtc.h" -#include "../savestate.h" -#include - -namespace gambatte { - -Rtc::Rtc() -: activeData(NULL), - activeSet(NULL), - baseTime(0), - haltTime(0), - index(5), - dataDh(0), - dataDl(0), - dataH(0), - dataM(0), - dataS(0), - enabled(false), - lastLatchData(false), - timeCB(0) -{ -} - -void Rtc::doLatch() { - std::uint32_t tmp = ((dataDh & 0x40) ? haltTime : timeCB()) - baseTime; - - while (tmp > 0x1FF * 86400) { - baseTime += 0x1FF * 86400; - tmp -= 0x1FF * 86400; - dataDh |= 0x80; - } - - dataDl = (tmp / 86400) & 0xFF; - dataDh &= 0xFE; - dataDh |= ((tmp / 86400) & 0x100) >> 8; - tmp %= 86400; - - dataH = tmp / 3600; - tmp %= 3600; - - dataM = tmp / 60; - tmp %= 60; - - dataS = tmp; -} - -void Rtc::doSwapActive() { - if (!enabled || index > 4) { - activeData = NULL; - activeSet = NULL; - } else switch (index) { - case 0x00: - activeData = &dataS; - activeSet = &Rtc::setS; - break; - case 0x01: - activeData = &dataM; - activeSet = &Rtc::setM; - break; - case 0x02: - activeData = &dataH; - activeSet = &Rtc::setH; - break; - case 0x03: - activeData = &dataDl; - activeSet = &Rtc::setDl; - break; - case 0x04: - activeData = &dataDh; - activeSet = &Rtc::setDh; - break; - } -} - -void Rtc::loadState(const SaveState &state) { - baseTime = state.rtc.baseTime; - haltTime = state.rtc.haltTime; - dataDh = state.rtc.dataDh; - dataDl = state.rtc.dataDl; - dataH = state.rtc.dataH; - dataM = state.rtc.dataM; - dataS = state.rtc.dataS; - lastLatchData = state.rtc.lastLatchData; - - doSwapActive(); -} - -void Rtc::setDh(const unsigned new_dh) { - const std::uint32_t unixtime = (dataDh & 0x40) ? haltTime : timeCB(); - const std::uint32_t old_highdays = ((unixtime - baseTime) / 86400) & 0x100; - baseTime += old_highdays * 86400; - baseTime -= ((new_dh & 0x1) << 8) * 86400; - - if ((dataDh ^ new_dh) & 0x40) { - if (new_dh & 0x40) - haltTime = timeCB(); - else - baseTime += timeCB() - haltTime; - } -} - -void Rtc::setDl(const unsigned new_lowdays) { - const std::uint32_t unixtime = (dataDh & 0x40) ? haltTime : timeCB(); - const std::uint32_t old_lowdays = ((unixtime - baseTime) / 86400) & 0xFF; - baseTime += old_lowdays * 86400; - baseTime -= new_lowdays * 86400; -} - -void Rtc::setH(const unsigned new_hours) { - const std::uint32_t unixtime = (dataDh & 0x40) ? haltTime : timeCB(); - const std::uint32_t old_hours = ((unixtime - baseTime) / 3600) % 24; - baseTime += old_hours * 3600; - baseTime -= new_hours * 3600; -} - -void Rtc::setM(const unsigned new_minutes) { - const std::uint32_t unixtime = (dataDh & 0x40) ? haltTime : timeCB(); - const std::uint32_t old_minutes = ((unixtime - baseTime) / 60) % 60; - baseTime += old_minutes * 60; - baseTime -= new_minutes * 60; -} - -void Rtc::setS(const unsigned new_seconds) { - const std::uint32_t unixtime = (dataDh & 0x40) ? haltTime : timeCB(); - baseTime += (unixtime - baseTime) % 60; - baseTime -= new_seconds; -} - -SYNCFUNC(Rtc) -{ - EBS(activeData, 0); - EVS(activeData, &dataS, 1); - EVS(activeData, &dataM, 2); - EVS(activeData, &dataH, 3); - EVS(activeData, &dataDl, 4); - EVS(activeData, &dataDh, 5); - EES(activeData, NULL); - - EBS(activeSet, 0); - EVS(activeSet, &Rtc::setS, 1); - EVS(activeSet, &Rtc::setM, 2); - EVS(activeSet, &Rtc::setH, 3); - EVS(activeSet, &Rtc::setDl, 4); - EVS(activeSet, &Rtc::setDh, 5); - EES(activeSet, NULL); - - NSS(baseTime); - NSS(haltTime); - NSS(index); - NSS(dataDh); - NSS(dataDl); - NSS(dataH); - NSS(dataM); - NSS(dataS); - NSS(enabled); - NSS(lastLatchData); -} - -} +/*************************************************************************** + * 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 "rtc.h" +#include "../savestate.h" +#include + +namespace gambatte { + +Rtc::Rtc() +: activeData(NULL), + activeSet(NULL), + baseTime(0), + haltTime(0), + index(5), + dataDh(0), + dataDl(0), + dataH(0), + dataM(0), + dataS(0), + enabled(false), + lastLatchData(false), + timeCB(0) +{ +} + +void Rtc::doLatch() { + std::uint32_t tmp = ((dataDh & 0x40) ? haltTime : timeCB()) - baseTime; + + while (tmp > 0x1FF * 86400) { + baseTime += 0x1FF * 86400; + tmp -= 0x1FF * 86400; + dataDh |= 0x80; + } + + dataDl = (tmp / 86400) & 0xFF; + dataDh &= 0xFE; + dataDh |= ((tmp / 86400) & 0x100) >> 8; + tmp %= 86400; + + dataH = tmp / 3600; + tmp %= 3600; + + dataM = tmp / 60; + tmp %= 60; + + dataS = tmp; +} + +void Rtc::doSwapActive() { + if (!enabled || index > 4) { + activeData = NULL; + activeSet = NULL; + } else switch (index) { + case 0x00: + activeData = &dataS; + activeSet = &Rtc::setS; + break; + case 0x01: + activeData = &dataM; + activeSet = &Rtc::setM; + break; + case 0x02: + activeData = &dataH; + activeSet = &Rtc::setH; + break; + case 0x03: + activeData = &dataDl; + activeSet = &Rtc::setDl; + break; + case 0x04: + activeData = &dataDh; + activeSet = &Rtc::setDh; + break; + } +} + +void Rtc::loadState(const SaveState &state) { + baseTime = state.rtc.baseTime; + haltTime = state.rtc.haltTime; + dataDh = state.rtc.dataDh; + dataDl = state.rtc.dataDl; + dataH = state.rtc.dataH; + dataM = state.rtc.dataM; + dataS = state.rtc.dataS; + lastLatchData = state.rtc.lastLatchData; + + doSwapActive(); +} + +void Rtc::setDh(const unsigned new_dh) { + const std::uint32_t unixtime = (dataDh & 0x40) ? haltTime : timeCB(); + const std::uint32_t old_highdays = ((unixtime - baseTime) / 86400) & 0x100; + baseTime += old_highdays * 86400; + baseTime -= ((new_dh & 0x1) << 8) * 86400; + + if ((dataDh ^ new_dh) & 0x40) { + if (new_dh & 0x40) + haltTime = timeCB(); + else + baseTime += timeCB() - haltTime; + } +} + +void Rtc::setDl(const unsigned new_lowdays) { + const std::uint32_t unixtime = (dataDh & 0x40) ? haltTime : timeCB(); + const std::uint32_t old_lowdays = ((unixtime - baseTime) / 86400) & 0xFF; + baseTime += old_lowdays * 86400; + baseTime -= new_lowdays * 86400; +} + +void Rtc::setH(const unsigned new_hours) { + const std::uint32_t unixtime = (dataDh & 0x40) ? haltTime : timeCB(); + const std::uint32_t old_hours = ((unixtime - baseTime) / 3600) % 24; + baseTime += old_hours * 3600; + baseTime -= new_hours * 3600; +} + +void Rtc::setM(const unsigned new_minutes) { + const std::uint32_t unixtime = (dataDh & 0x40) ? haltTime : timeCB(); + const std::uint32_t old_minutes = ((unixtime - baseTime) / 60) % 60; + baseTime += old_minutes * 60; + baseTime -= new_minutes * 60; +} + +void Rtc::setS(const unsigned new_seconds) { + const std::uint32_t unixtime = (dataDh & 0x40) ? haltTime : timeCB(); + baseTime += (unixtime - baseTime) % 60; + baseTime -= new_seconds; +} + +SYNCFUNC(Rtc) +{ + EBS(activeData, 0); + EVS(activeData, &dataS, 1); + EVS(activeData, &dataM, 2); + EVS(activeData, &dataH, 3); + EVS(activeData, &dataDl, 4); + EVS(activeData, &dataDh, 5); + EES(activeData, NULL); + + EBS(activeSet, 0); + EVS(activeSet, &Rtc::setS, 1); + EVS(activeSet, &Rtc::setM, 2); + EVS(activeSet, &Rtc::setH, 3); + EVS(activeSet, &Rtc::setDl, 4); + EVS(activeSet, &Rtc::setDh, 5); + EES(activeSet, NULL); + + NSS(baseTime); + NSS(haltTime); + NSS(index); + NSS(dataDh); + NSS(dataDl); + NSS(dataH); + NSS(dataM); + NSS(dataS); + NSS(enabled); + NSS(lastLatchData); +} + +} diff --git a/libgambatte/src/mem/rtc.h b/libgambatte/src/mem/rtc.h index 00652a2181..cdbd7e66d0 100644 --- a/libgambatte/src/mem/rtc.h +++ b/libgambatte/src/mem/rtc.h @@ -1,98 +1,98 @@ -/*************************************************************************** - * 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 RTC_H -#define RTC_H - -#include -#include "newstate.h" - -namespace gambatte { - -struct SaveState; - -class Rtc { -private: - unsigned char *activeData; - void (Rtc::*activeSet)(unsigned); - std::uint32_t baseTime; - std::uint32_t haltTime; - unsigned char index; - unsigned char dataDh; - unsigned char dataDl; - unsigned char dataH; - unsigned char dataM; - unsigned char dataS; - bool enabled; - bool lastLatchData; - std::uint32_t (*timeCB)(); - - void doLatch(); - void doSwapActive(); - void setDh(unsigned new_dh); - void setDl(unsigned new_lowdays); - void setH(unsigned new_hours); - void setM(unsigned new_minutes); - void setS(unsigned new_seconds); - -public: - Rtc(); - - const unsigned char* getActive() const { return activeData; } - std::uint32_t getBaseTime() const { return baseTime; } - - void setBaseTime(const std::uint32_t baseTime) { - this->baseTime = baseTime; -// doLatch(); - } - - void latch(const unsigned data) { - if (!lastLatchData && data == 1) - doLatch(); - - lastLatchData = data; - } - - void loadState(const SaveState &state); - - void set(const bool enabled, unsigned bank) { - bank &= 0xF; - bank -= 8; - - this->enabled = enabled; - this->index = bank; - - doSwapActive(); - } - - void write(const unsigned data) { -// if (activeSet) - (this->*activeSet)(data); - *activeData = data; - } - - void setRTCCallback(std::uint32_t (*callback)()) { - timeCB = callback; - } - - templatevoid SyncState(NewState *ns); -}; - -} - -#endif +/*************************************************************************** + * 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 RTC_H +#define RTC_H + +#include +#include "newstate.h" + +namespace gambatte { + +struct SaveState; + +class Rtc { +private: + unsigned char *activeData; + void (Rtc::*activeSet)(unsigned); + std::uint32_t baseTime; + std::uint32_t haltTime; + unsigned char index; + unsigned char dataDh; + unsigned char dataDl; + unsigned char dataH; + unsigned char dataM; + unsigned char dataS; + bool enabled; + bool lastLatchData; + std::uint32_t (*timeCB)(); + + void doLatch(); + void doSwapActive(); + void setDh(unsigned new_dh); + void setDl(unsigned new_lowdays); + void setH(unsigned new_hours); + void setM(unsigned new_minutes); + void setS(unsigned new_seconds); + +public: + Rtc(); + + const unsigned char* getActive() const { return activeData; } + std::uint32_t getBaseTime() const { return baseTime; } + + void setBaseTime(const std::uint32_t baseTime) { + this->baseTime = baseTime; +// doLatch(); + } + + void latch(const unsigned data) { + if (!lastLatchData && data == 1) + doLatch(); + + lastLatchData = data; + } + + void loadState(const SaveState &state); + + void set(const bool enabled, unsigned bank) { + bank &= 0xF; + bank -= 8; + + this->enabled = enabled; + this->index = bank; + + doSwapActive(); + } + + void write(const unsigned data) { +// if (activeSet) + (this->*activeSet)(data); + *activeData = data; + } + + void setRTCCallback(std::uint32_t (*callback)()) { + timeCB = callback; + } + + templatevoid SyncState(NewState *ns); +}; + +} + +#endif diff --git a/libgambatte/src/memory.cpp b/libgambatte/src/memory.cpp index 0a52eb6686..a5fcc148ef 100644 --- a/libgambatte/src/memory.cpp +++ b/libgambatte/src/memory.cpp @@ -1,1118 +1,1161 @@ -/*************************************************************************** - * 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), - linkCallback(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); - if (linkCallback) - linkCallback(); - } - } - } -} - -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) { - unsigned input = (*getInput)(); - unsigned dpad_state = ~input >> 4; - unsigned button_state = ~input; - if (!(ioamhram[0x100] & 0x10)) - state &= dpad_state; - if (!(ioamhram[0x100] & 0x20)) - state &= button_state; - } - - 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) { - 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: - if (isCgb()) - 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 - 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; -} - -SYNCFUNC(Memory) -{ - SSS(cart); - NSS(ioamhram); - NSS(divLastUpdate); - NSS(lastOamDmaUpdate); - - SSS(intreq); - SSS(tima); - SSS(display); - SSS(sound); - //SSS(interrupter); // no state - - NSS(dmaSource); - NSS(dmaDestination); - NSS(oamDmaPos); - NSS(serialCnt); - NSS(blanklcd); - - NSS(LINKCABLE); - NSS(linkClockTrigger); -} - -} +/*************************************************************************** + * 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, unsigned short &sp, unsigned short &pc) +: readCallback(0), + writeCallback(0), + execCallback(0), + cdCallback(0), + linkCallback(0), + getInput(0), + divLastUpdate(0), + lastOamDmaUpdate(DISABLED_TIME), + display(ioamhram, 0, VideoInterruptRequester(&intreq)), + interrupter(interrupter_in), + dmaSource(0), + dmaDestination(0), + oamDmaPos(0xFE), + serialCnt(0), + blanklcd(false), + LINKCABLE(false), + linkClockTrigger(false), + SP(sp), + PC(pc) +{ + intreq.setEventTime(144*456ul); + intreq.setEventTime(0); +} + +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) { + biosMode = state.mem.biosMode; + cgbSwitching = state.mem.cgbSwitching; + agbMode = state.mem.agbMode; + gbIsCgb_ = state.mem.gbIsCgb; + sound.loadState(state); + display.loadState(state, state.mem.oamDmaPos < 0xA0 ? cart.rdisabledRam() : ioamhram); + tima.loadState(state, TimaInterruptRequester(intreq)); + cart.loadState(state); + intreq.loadState(state); + + 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); + if (linkCallback) + linkCallback(); + } + } + } +} + +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 (gbIsCgb_) + cycleCounter += 4; + + intreq.unhalt(); + intreq.setEventTime(DISABLED_TIME); + } + + if (ime()) { + unsigned address; + + cycleCounter += 12; + display.update(cycleCounter); + SP = (SP - 2) & 0xFFFF; + write(SP + 1, PC >> 8, cycleCounter); + unsigned ie = intreq.iereg(); + + cycleCounter += 4; + display.update(cycleCounter); + write(SP, PC & 0xFF, cycleCounter); + const unsigned pendingIrqs = ie & intreq.ifreg(); + + cycleCounter += 4; + display.update(cycleCounter); + const unsigned n = pendingIrqs & -pendingIrqs; + + if (n == 0) { + address = 0; + } + else if (n < 8) { + static const unsigned char lut[] = { 0x40, 0x48, 0x48, 0x50 }; + address = lut[n-1]; + } else + address = 0x50 + n; + + intreq.ackIrq(n); + PC = address; + } + + 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.halt(); + intreq.setEventTime(cycleCounter + 0x20000 + isDoubleSpeed() * 8); + } + else { + if ((ioamhram[0x100] & 0x30) == 0x30) { + di(); + intreq.halt(); + } + else { + intreq.halt(); + intreq.setEventTime(cycleCounter + 0x20000 + isDoubleSpeed() * 8); + } + } + + 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) { + unsigned input = (*getInput)(); + unsigned dpad_state = ~input >> 4; + unsigned button_state = ~input; + if (!(ioamhram[0x100] & 0x10)) + state &= dpad_state; + if (!(ioamhram[0x100] & 0x20)) + state &= button_state; + } + + 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) { + 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 0x4C: + if (biosMode) { + //flagClockReq(&intreq); + } + 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 0x50: + biosMode = false; + if (cgbSwitching) { + display.copyCgbPalettesToDmg(); + display.setCgb(false); + cgbSwitching = false; + } + 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: + ioamhram[0x16C] = data | 0xFE; + cgbSwitching = true; + + 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; +} + +SYNCFUNC(Memory) +{ + SSS(cart); + NSS(ioamhram); + NSS(divLastUpdate); + NSS(lastOamDmaUpdate); + NSS(biosMode); + NSS(cgbSwitching); + NSS(agbMode); + NSS(gbIsCgb_); + + SSS(intreq); + SSS(tima); + SSS(display); + SSS(sound); + //SSS(interrupter); // no state + + NSS(dmaSource); + NSS(dmaDestination); + NSS(oamDmaPos); + NSS(serialCnt); + NSS(blanklcd); + + NSS(LINKCABLE); + NSS(linkClockTrigger); +} + +} diff --git a/libgambatte/src/memory.h b/libgambatte/src/memory.h index 9ea65bf4a1..95a2e18f16 100644 --- a/libgambatte/src/memory.h +++ b/libgambatte/src/memory.h @@ -1,298 +1,336 @@ -/*************************************************************************** - * 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; - void (*linkCallback)(); - - 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 setLinkCallback(void (*callback)()) { - this->linkCallback = 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 +/*************************************************************************** + * 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 + +static unsigned char const agbOverride[0xD] = { 0xFF, 0x00, 0xCD, 0x03, 0x35, 0xAA, 0x31, 0x90, 0x94, 0x00, 0x00, 0x00, 0x00 }; + +#include "mem/cartridge.h" +#include "video.h" +#include "sound.h" +#include "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]; + unsigned char cgbBios[0x900]; + unsigned char dmgBios[0x100]; + bool biosMode; + bool cgbSwitching; + bool agbMode; + bool gbIsCgb_; + unsigned short &SP; + unsigned short &PC; + + void (*readCallback)(unsigned); + void (*writeCallback)(unsigned); + void (*execCallback)(unsigned); + CDCallback cdCallback; + void(*linkCallback)(); + + 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, unsigned short &sp, unsigned short &pc); + + bool loaded() const { return cart.loaded(); } + unsigned curRomBank() const { return cart.curRomBank(); } + 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(); + + unsigned char* cgbBiosBuffer() { return (unsigned char*)cgbBios; } + unsigned char* dmgBiosBuffer() { return (unsigned char*)dmgBios; } + bool gbIsCgb() { return gbIsCgb_; } + + bool getMemoryArea(int which, unsigned char **data, int *length); // { return cart.getMemoryArea(which, data, length); } + + unsigned long stop(unsigned long cycleCounter); + bool isCgb() const { return display.isCgb(); } + bool ime() const { return intreq.ime(); } + bool halted() const { return intreq.halted(); } + unsigned long nextEventTime() const { return intreq.minEventTime(); } + + 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 readBios(const unsigned P) { + if (gbIsCgb_) { + if (agbMode && P >= 0xF3 && P < 0x100) { + return (agbOverride[P - 0xF3] + cgbBios[P]) & 0xFF; + } + return cgbBios[P]; + } + return dmgBios[P]; + } + + unsigned read(const unsigned P, const unsigned long cycleCounter) { + if (readCallback) + readCallback(P); + if(cdCallback) + { + CDMapResult map = CDMap(P); + if(map.type != eCDLog_AddrType_None) + cdCallback(map.addr,map.type,eCDLog_Flags_Data); + } + if (biosMode && ((!gbIsCgb_ && P < 0x100) || (gbIsCgb_ && P < 0x900 && (P < 0x100 || P >= 0x200)))) { + return readBios(P); + } + return cart.rmem(P >> 12) ? cart.rmem(P >> 12)[P] : nontrivial_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); + } + if (biosMode && ((!gbIsCgb_ && P < 0x100) || (gbIsCgb_ && P < 0x900 && (P < 0x100 || P >= 0x200)))) { + return readBios(P); + } + return cart.rmem(P >> 12) ? cart.rmem(P >> 12)[P] : nontrivial_read(P, cycleCounter); + } + + unsigned peek(const unsigned P) { + if (biosMode && ((!gbIsCgb_ && P < 0x100) || (gbIsCgb_ && P < 0x900 && (P < 0x100 || P >= 0x200)))) { + return readBios(P); + } + return cart.rmem(P >> 12) ? cart.rmem(P >> 12)[P] : nontrivial_peek(P); + } + + void write_nocb(const unsigned P, const unsigned data, const unsigned long cycleCounter) { + if (cart.wmem(P >> 12)) { + cart.wmem(P >> 12)[P] = data; + } else + nontrivial_write(P, data, cycleCounter); + } + + void write(const unsigned P, const unsigned data, const unsigned long cycleCounter) { + if (cart.wmem(P >> 12)) { + cart.wmem(P >> 12)[P] = data; + } else + nontrivial_write(P, data, cycleCounter); + if (writeCallback) + writeCallback(P); + 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 setLinkCallback(void(*callback)()) { + this->linkCallback = 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); + + void blackScreen() { + display.blackScreen(); + } + + int LinkStatus(int which); + + templatevoid SyncState(NewState *ns); +}; + +} + +#endif diff --git a/libgambatte/src/minkeeper.h b/libgambatte/src/minkeeper.h index 0031adf098..ad2d6f79d2 100644 --- a/libgambatte/src/minkeeper.h +++ b/libgambatte/src/minkeeper.h @@ -20,7 +20,7 @@ #define MINKEEPER_H #include -#include "newstate.h" +#include "newstate.h" namespace MinKeeperUtil { template struct CeiledLog2 { enum { R = 1 + CeiledLog2<(n + 1) / 2>::R }; }; @@ -108,12 +108,12 @@ public: unsigned long value(const int id) const { return values[id]; } // not sure if i understood everything in minkeeper correctly, so something might be missing here? - template - void SyncState(gambatte::NewState *ns) - { - NSS(values); - NSS(minValue_); - NSS(a); + template + void SyncState(gambatte::NewState *ns) + { + NSS(values); + NSS(minValue_); + NSS(a); } }; diff --git a/libgambatte/src/newstate.cpp b/libgambatte/src/newstate.cpp index fa01dbf6ef..9d4214674a 100644 --- a/libgambatte/src/newstate.cpp +++ b/libgambatte/src/newstate.cpp @@ -1,69 +1,69 @@ -#include "newstate.h" -#include -#include - -namespace gambatte { - -NewStateDummy::NewStateDummy() - :length(0) -{ -} -void NewStateDummy::Save(const void *ptr, size_t size, const char *name) -{ - length += size; -} -void NewStateDummy::Load(void *ptr, size_t size, const char *name) -{ -} - -NewStateExternalBuffer::NewStateExternalBuffer(char *buffer, long maxlength) - :buffer(buffer), length(0), maxlength(maxlength) -{ -} - -void NewStateExternalBuffer::Save(const void *ptr, size_t size, const char *name) -{ - if (maxlength - length >= (long)size) - { - std::memcpy(buffer + length, ptr, size); - } - length += size; -} - -void NewStateExternalBuffer::Load(void *ptr, size_t size, const char *name) -{ - char *dst = static_cast(ptr); - if (maxlength - length >= (long)size) - { - std::memcpy(dst, buffer + length, size); - } - length += size; -} - -NewStateExternalFunctions::NewStateExternalFunctions(const FPtrs *ff) - :Save_(ff->Save_), - Load_(ff->Load_), - EnterSection_(ff->EnterSection_), - ExitSection_(ff->ExitSection_) -{ -} - -void NewStateExternalFunctions::Save(const void *ptr, size_t size, const char *name) -{ - Save_(ptr, size, name); -} -void NewStateExternalFunctions::Load(void *ptr, size_t size, const char *name) -{ - Load_(ptr, size, name); -} -void NewStateExternalFunctions::EnterSection(const char *name) -{ - EnterSection_(name); -} -void NewStateExternalFunctions::ExitSection(const char *name) -{ - ExitSection_(name); -} - - -} +#include "newstate.h" +#include +#include + +namespace gambatte { + +NewStateDummy::NewStateDummy() + :length(0) +{ +} +void NewStateDummy::Save(const void *ptr, size_t size, const char *name) +{ + length += size; +} +void NewStateDummy::Load(void *ptr, size_t size, const char *name) +{ +} + +NewStateExternalBuffer::NewStateExternalBuffer(char *buffer, long maxlength) + :buffer(buffer), length(0), maxlength(maxlength) +{ +} + +void NewStateExternalBuffer::Save(const void *ptr, size_t size, const char *name) +{ + if (maxlength - length >= (long)size) + { + std::memcpy(buffer + length, ptr, size); + } + length += size; +} + +void NewStateExternalBuffer::Load(void *ptr, size_t size, const char *name) +{ + char *dst = static_cast(ptr); + if (maxlength - length >= (long)size) + { + std::memcpy(dst, buffer + length, size); + } + length += size; +} + +NewStateExternalFunctions::NewStateExternalFunctions(const FPtrs *ff) + :Save_(ff->Save_), + Load_(ff->Load_), + EnterSection_(ff->EnterSection_), + ExitSection_(ff->ExitSection_) +{ +} + +void NewStateExternalFunctions::Save(const void *ptr, size_t size, const char *name) +{ + Save_(ptr, size, name); +} +void NewStateExternalFunctions::Load(void *ptr, size_t size, const char *name) +{ + Load_(ptr, size, name); +} +void NewStateExternalFunctions::EnterSection(const char *name) +{ + EnterSection_(name); +} +void NewStateExternalFunctions::ExitSection(const char *name) +{ + ExitSection_(name); +} + + +} diff --git a/libgambatte/src/newstate.h b/libgambatte/src/newstate.h index 37e2a7038f..2457a83669 100644 --- a/libgambatte/src/newstate.h +++ b/libgambatte/src/newstate.h @@ -1,102 +1,102 @@ -#ifndef NEWSTATE_H -#define NEWSTATE_H - -#include -#include - -namespace gambatte { - -class NewState -{ -public: - virtual void Save(const void *ptr, size_t size, const char *name) = 0; - virtual void Load(void *ptr, size_t size, const char *name) = 0; - virtual void EnterSection(const char *name) { } - virtual void ExitSection(const char *name) { } -}; - -class NewStateDummy : public NewState -{ -private: - long length; -public: - NewStateDummy(); - long GetLength() { return length; } - void Rewind() { length = 0; } - virtual void Save(const void *ptr, size_t size, const char *name); - virtual void Load(void *ptr, size_t size, const char *name); -}; - -class NewStateExternalBuffer : public NewState -{ -private: - char *const buffer; - long length; - const long maxlength; -public: - NewStateExternalBuffer(char *buffer, long maxlength); - long GetLength() { return length; } - void Rewind() { length = 0; } - bool Overflow() { return length > maxlength; } - virtual void Save(const void *ptr, size_t size, const char *name); - virtual void Load(void *ptr, size_t size, const char *name); -}; - -struct FPtrs -{ - void (*Save_)(const void *ptr, size_t size, const char *name); - void (*Load_)(void *ptr, size_t size, const char *name); - void (*EnterSection_)(const char *name); - void (*ExitSection_)(const char *name); -}; - -class NewStateExternalFunctions : public NewState -{ -private: - void (*Save_)(const void *ptr, size_t size, const char *name); - void (*Load_)(void *ptr, size_t size, const char *name); - void (*EnterSection_)(const char *name); - void (*ExitSection_)(const char *name); -public: - NewStateExternalFunctions(const FPtrs *ff); - virtual void Save(const void *ptr, size_t size, const char *name); - virtual void Load(void *ptr, size_t size, const char *name); - virtual void EnterSection(const char *name); - virtual void ExitSection(const char *name); -}; - -// defines and explicitly instantiates -#define SYNCFUNC(x)\ - template void x::SyncState(NewState *ns);\ - template void x::SyncState(NewState *ns);\ - templatevoid x::SyncState(NewState *ns) - -// N = normal variable -// P = pointer to fixed size data -// S = "sub object" -// T = "ptr to sub object" -// R = pointer, store its offset from some other pointer -// E = general purpose cased value "enum" - - -// first line is default value in converted enum; last line is default value in argument x -#define EBS(x,d) do { int _ttmp = (d); if (isReader) ns->Load(&_ttmp, sizeof(_ttmp), #x); if (0) -#define EVS(x,v,n) else if (!isReader && (x) == (v)) _ttmp = (n); else if (isReader && _ttmp == (n)) (x) = (v) -#define EES(x,d) else if (isReader) (x) = (d); if (!isReader) ns->Save(&_ttmp, sizeof(_ttmp), #x); } while (0) - -#define RSS(x,b) do { if (isReader)\ -{ ptrdiff_t _ttmp; ns->Load(&_ttmp, sizeof(_ttmp), #x); (x) = (_ttmp == (ptrdiff_t)0xdeadbeef ? 0 : (b) + _ttmp); }\ - else\ -{ ptrdiff_t _ttmp = (x) == 0 ? 0xdeadbeef : (x) - (b); ns->Save(&_ttmp, sizeof(_ttmp), #x); } } while (0) - -#define PSS(x,s) do { if (isReader) ns->Load((x), (s), #x); else ns->Save((x), (s), #x); } while (0) - -#define NSS(x) do { if (isReader) ns->Load(&(x), sizeof(x), #x); else ns->Save(&(x), sizeof(x), #x); } while (0) - -#define SSS(x) do { ns->EnterSection(#x); (x).SyncState(ns); ns->ExitSection(#x); } while (0) - -#define TSS(x) do { ns->EnterSection(#x); (x)->SyncState(ns); ns->ExitSection(#x); } while (0) - -} - -#endif +#ifndef NEWSTATE_H +#define NEWSTATE_H + +#include +#include + +namespace gambatte { + +class NewState +{ +public: + virtual void Save(const void *ptr, size_t size, const char *name) = 0; + virtual void Load(void *ptr, size_t size, const char *name) = 0; + virtual void EnterSection(const char *name) { } + virtual void ExitSection(const char *name) { } +}; + +class NewStateDummy : public NewState +{ +private: + long length; +public: + NewStateDummy(); + long GetLength() { return length; } + void Rewind() { length = 0; } + virtual void Save(const void *ptr, size_t size, const char *name); + virtual void Load(void *ptr, size_t size, const char *name); +}; + +class NewStateExternalBuffer : public NewState +{ +private: + char *const buffer; + long length; + const long maxlength; +public: + NewStateExternalBuffer(char *buffer, long maxlength); + long GetLength() { return length; } + void Rewind() { length = 0; } + bool Overflow() { return length > maxlength; } + virtual void Save(const void *ptr, size_t size, const char *name); + virtual void Load(void *ptr, size_t size, const char *name); +}; + +struct FPtrs +{ + void (*Save_)(const void *ptr, size_t size, const char *name); + void (*Load_)(void *ptr, size_t size, const char *name); + void (*EnterSection_)(const char *name); + void (*ExitSection_)(const char *name); +}; + +class NewStateExternalFunctions : public NewState +{ +private: + void (*Save_)(const void *ptr, size_t size, const char *name); + void (*Load_)(void *ptr, size_t size, const char *name); + void (*EnterSection_)(const char *name); + void (*ExitSection_)(const char *name); +public: + NewStateExternalFunctions(const FPtrs *ff); + virtual void Save(const void *ptr, size_t size, const char *name); + virtual void Load(void *ptr, size_t size, const char *name); + virtual void EnterSection(const char *name); + virtual void ExitSection(const char *name); +}; + +// defines and explicitly instantiates +#define SYNCFUNC(x)\ + template void x::SyncState(NewState *ns);\ + template void x::SyncState(NewState *ns);\ + templatevoid x::SyncState(NewState *ns) + +// N = normal variable +// P = pointer to fixed size data +// S = "sub object" +// T = "ptr to sub object" +// R = pointer, store its offset from some other pointer +// E = general purpose cased value "enum" + + +// first line is default value in converted enum; last line is default value in argument x +#define EBS(x,d) do { int _ttmp = (d); if (isReader) ns->Load(&_ttmp, sizeof(_ttmp), #x); if (0) +#define EVS(x,v,n) else if (!isReader && (x) == (v)) _ttmp = (n); else if (isReader && _ttmp == (n)) (x) = (v) +#define EES(x,d) else if (isReader) (x) = (d); if (!isReader) ns->Save(&_ttmp, sizeof(_ttmp), #x); } while (0) + +#define RSS(x,b) do { if (isReader)\ +{ ptrdiff_t _ttmp; ns->Load(&_ttmp, sizeof(_ttmp), #x); (x) = (_ttmp == (ptrdiff_t)0xdeadbeef ? 0 : (b) + _ttmp); }\ + else\ +{ ptrdiff_t _ttmp = (x) == 0 ? 0xdeadbeef : (x) - (b); ns->Save(&_ttmp, sizeof(_ttmp), #x); } } while (0) + +#define PSS(x,s) do { if (isReader) ns->Load((x), (s), #x); else ns->Save((x), (s), #x); } while (0) + +#define NSS(x) do { if (isReader) ns->Load(&(x), sizeof(x), #x); else ns->Save(&(x), sizeof(x), #x); } while (0) + +#define SSS(x) do { ns->EnterSection(#x); (x).SyncState(ns); ns->ExitSection(#x); } while (0) + +#define TSS(x) do { ns->EnterSection(#x); (x)->SyncState(ns); ns->ExitSection(#x); } while (0) + +} + +#endif diff --git a/libgambatte/src/savestate.h b/libgambatte/src/savestate.h index 4110161ca0..7693299b5c 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, bool, std::uint32_t); }; struct CPU { @@ -78,6 +78,10 @@ struct SaveState { bool enableRam; bool rambankMode; bool hdmaTransfer; + bool biosMode; + bool cgbSwitching; + bool agbMode; + bool gbIsCgb; } mem; struct PPU { @@ -113,6 +117,8 @@ struct SaveState { unsigned char wscx; bool weMaster; bool pendingLcdstatIrq; + bool isCgb; + bool trueColors; } ppu; struct SPU { diff --git a/libgambatte/src/sound.cpp b/libgambatte/src/sound.cpp index be28da8e1d..01685afc74 100644 --- a/libgambatte/src/sound.cpp +++ b/libgambatte/src/sound.cpp @@ -173,16 +173,16 @@ unsigned PSG::getStatus() const { } // the buffer and position are not saved, as they're set and flushed on each runfor() call -SYNCFUNC(PSG) -{ - SSS(ch1); - SSS(ch2); - SSS(ch3); - SSS(ch4); - NSS(lastUpdate); - NSS(soVol); - NSS(rsum); - NSS(enabled); -} +SYNCFUNC(PSG) +{ + SSS(ch1); + SSS(ch2); + SSS(ch3); + SSS(ch4); + NSS(lastUpdate); + NSS(soVol); + NSS(rsum); + NSS(enabled); +} } diff --git a/libgambatte/src/sound.h b/libgambatte/src/sound.h index 0d39db788e..7d430d613d 100644 --- a/libgambatte/src/sound.h +++ b/libgambatte/src/sound.h @@ -23,7 +23,7 @@ #include "sound/channel2.h" #include "sound/channel3.h" #include "sound/channel4.h" -#include "newstate.h" +#include "newstate.h" namespace gambatte { @@ -89,7 +89,7 @@ public: void map_so(unsigned nr51); unsigned getStatus() const; - templatevoid SyncState(NewState *ns); + templatevoid SyncState(NewState *ns); }; } diff --git a/libgambatte/src/sound/channel1.cpp b/libgambatte/src/sound/channel1.cpp index b99902f28e..eae98c966c 100644 --- a/libgambatte/src/sound/channel1.cpp +++ b/libgambatte/src/sound/channel1.cpp @@ -97,14 +97,14 @@ void Channel1::SweepUnit::loadState(const SaveState &state) { negging = state.spu.ch1.sweep.negging; } -template -void Channel1::SweepUnit::SyncState(NewState *ns) -{ - NSS(counter); - NSS(shadow); - NSS(nr0); - NSS(negging); -} +template +void Channel1::SweepUnit::SyncState(NewState *ns) +{ + NSS(counter); + NSS(shadow); + NSS(nr0); + NSS(negging); +} Channel1::Channel1() : staticOutputTest(*this, dutyUnit), @@ -250,26 +250,26 @@ void Channel1::update(uint_least32_t *buf, const unsigned long soBaseVol, unsign } } -SYNCFUNC(Channel1) -{ - SSS(lengthCounter); - SSS(dutyUnit); - SSS(envelopeUnit); - SSS(sweepUnit); - - EBS(nextEventUnit, 0); - EVS(nextEventUnit, &dutyUnit, 1); - EVS(nextEventUnit, &sweepUnit, 2); - EVS(nextEventUnit, &envelopeUnit, 3); - EVS(nextEventUnit, &lengthCounter, 4); - EES(nextEventUnit, NULL); - - NSS(cycleCounter); - NSS(soMask); - NSS(prevOut); - - NSS(nr4); - NSS(master); -} +SYNCFUNC(Channel1) +{ + SSS(lengthCounter); + SSS(dutyUnit); + SSS(envelopeUnit); + SSS(sweepUnit); + + EBS(nextEventUnit, 0); + EVS(nextEventUnit, &dutyUnit, 1); + EVS(nextEventUnit, &sweepUnit, 2); + EVS(nextEventUnit, &envelopeUnit, 3); + EVS(nextEventUnit, &lengthCounter, 4); + EES(nextEventUnit, NULL); + + NSS(cycleCounter); + NSS(soMask); + NSS(prevOut); + + NSS(nr4); + NSS(master); +} } diff --git a/libgambatte/src/sound/channel1.h b/libgambatte/src/sound/channel1.h index 3467547a58..5cd7c23ed2 100644 --- a/libgambatte/src/sound/channel1.h +++ b/libgambatte/src/sound/channel1.h @@ -1,97 +1,97 @@ -/*************************************************************************** - * 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 SOUND_CHANNEL1_H -#define SOUND_CHANNEL1_H - -#include "gbint.h" -#include "master_disabler.h" -#include "length_counter.h" -#include "duty_unit.h" -#include "envelope_unit.h" -#include "static_output_tester.h" -#include "newstate.h" - -namespace gambatte { - -struct SaveState; - -class Channel1 { - class SweepUnit : public SoundUnit { - MasterDisabler &disableMaster; - DutyUnit &dutyUnit; - unsigned short shadow; - unsigned char nr0; - bool negging; - - unsigned calcFreq(); - - public: - SweepUnit(MasterDisabler &disabler, DutyUnit &dutyUnit); - void event(); - void nr0Change(unsigned newNr0); - void nr4Init(unsigned long cycleCounter); - void reset(); - void loadState(const SaveState &state); - - templatevoid SyncState(NewState *ns); - }; - - friend class StaticOutputTester; - - StaticOutputTester staticOutputTest; - DutyMasterDisabler disableMaster; - LengthCounter lengthCounter; - DutyUnit dutyUnit; - EnvelopeUnit envelopeUnit; - SweepUnit sweepUnit; - - SoundUnit *nextEventUnit; - - unsigned long cycleCounter; - unsigned long soMask; - unsigned long prevOut; - - unsigned char nr4; - bool master; - - void setEvent(); - -public: - Channel1(); - void setNr0(unsigned data); - void setNr1(unsigned data); - void setNr2(unsigned data); - void setNr3(unsigned data); - void setNr4(unsigned data); - - void setSo(unsigned long soMask); - bool isActive() const { return master; } - - void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles); - - void reset(); - void init(bool cgb); - void loadState(const SaveState &state); - - templatevoid SyncState(NewState *ns); -}; - -} - -#endif +/*************************************************************************** + * 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 SOUND_CHANNEL1_H +#define SOUND_CHANNEL1_H + +#include "gbint.h" +#include "master_disabler.h" +#include "length_counter.h" +#include "duty_unit.h" +#include "envelope_unit.h" +#include "static_output_tester.h" +#include "newstate.h" + +namespace gambatte { + +struct SaveState; + +class Channel1 { + class SweepUnit : public SoundUnit { + MasterDisabler &disableMaster; + DutyUnit &dutyUnit; + unsigned short shadow; + unsigned char nr0; + bool negging; + + unsigned calcFreq(); + + public: + SweepUnit(MasterDisabler &disabler, DutyUnit &dutyUnit); + void event(); + void nr0Change(unsigned newNr0); + void nr4Init(unsigned long cycleCounter); + void reset(); + void loadState(const SaveState &state); + + templatevoid SyncState(NewState *ns); + }; + + friend class StaticOutputTester; + + StaticOutputTester staticOutputTest; + DutyMasterDisabler disableMaster; + LengthCounter lengthCounter; + DutyUnit dutyUnit; + EnvelopeUnit envelopeUnit; + SweepUnit sweepUnit; + + SoundUnit *nextEventUnit; + + unsigned long cycleCounter; + unsigned long soMask; + unsigned long prevOut; + + unsigned char nr4; + bool master; + + void setEvent(); + +public: + Channel1(); + void setNr0(unsigned data); + void setNr1(unsigned data); + void setNr2(unsigned data); + void setNr3(unsigned data); + void setNr4(unsigned data); + + void setSo(unsigned long soMask); + bool isActive() const { return master; } + + void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles); + + void reset(); + void init(bool cgb); + void loadState(const SaveState &state); + + templatevoid SyncState(NewState *ns); +}; + +} + +#endif diff --git a/libgambatte/src/sound/channel2.cpp b/libgambatte/src/sound/channel2.cpp index 78cb44fb07..b3135e2299 100644 --- a/libgambatte/src/sound/channel2.cpp +++ b/libgambatte/src/sound/channel2.cpp @@ -153,24 +153,24 @@ void Channel2::update(uint_least32_t *buf, const unsigned long soBaseVol, unsign } } -SYNCFUNC(Channel2) -{ - SSS(lengthCounter); - SSS(dutyUnit); - SSS(envelopeUnit); - - EBS(nextEventUnit, 0); - EVS(nextEventUnit, &dutyUnit, 1); - EVS(nextEventUnit, &envelopeUnit, 2); - EVS(nextEventUnit, &lengthCounter, 3); - EES(nextEventUnit, NULL); - - NSS(cycleCounter); - NSS(soMask); - NSS(prevOut); - - NSS(nr4); - NSS(master); -} +SYNCFUNC(Channel2) +{ + SSS(lengthCounter); + SSS(dutyUnit); + SSS(envelopeUnit); + + EBS(nextEventUnit, 0); + EVS(nextEventUnit, &dutyUnit, 1); + EVS(nextEventUnit, &envelopeUnit, 2); + EVS(nextEventUnit, &lengthCounter, 3); + EES(nextEventUnit, NULL); + + NSS(cycleCounter); + NSS(soMask); + NSS(prevOut); + + NSS(nr4); + NSS(master); +} } diff --git a/libgambatte/src/sound/channel2.h b/libgambatte/src/sound/channel2.h index 2f98309e6a..9cc2ef28b6 100644 --- a/libgambatte/src/sound/channel2.h +++ b/libgambatte/src/sound/channel2.h @@ -1,75 +1,75 @@ -/*************************************************************************** - * 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 SOUND_CHANNEL2_H -#define SOUND_CHANNEL2_H - -#include "gbint.h" -#include "length_counter.h" -#include "duty_unit.h" -#include "envelope_unit.h" -#include "static_output_tester.h" -#include "newstate.h" - -namespace gambatte { - -struct SaveState; - -class Channel2 { - friend class StaticOutputTester; - - StaticOutputTester staticOutputTest; - DutyMasterDisabler disableMaster; - LengthCounter lengthCounter; - DutyUnit dutyUnit; - EnvelopeUnit envelopeUnit; - - SoundUnit *nextEventUnit; - - unsigned long cycleCounter; - unsigned long soMask; - unsigned long prevOut; - - unsigned char nr4; - bool master; - - void setEvent(); - -public: - Channel2(); - void setNr1(unsigned data); - void setNr2(unsigned data); - void setNr3(unsigned data); - void setNr4(unsigned data); - - void setSo(unsigned long soMask); - // void deactivate() { disableMaster(); setEvent(); } - bool isActive() const { return master; } - - void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles); - - void reset(); - void init(bool cgb); - void loadState(const SaveState &state); - - templatevoid SyncState(NewState *ns); -}; - -} - -#endif +/*************************************************************************** + * 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 SOUND_CHANNEL2_H +#define SOUND_CHANNEL2_H + +#include "gbint.h" +#include "length_counter.h" +#include "duty_unit.h" +#include "envelope_unit.h" +#include "static_output_tester.h" +#include "newstate.h" + +namespace gambatte { + +struct SaveState; + +class Channel2 { + friend class StaticOutputTester; + + StaticOutputTester staticOutputTest; + DutyMasterDisabler disableMaster; + LengthCounter lengthCounter; + DutyUnit dutyUnit; + EnvelopeUnit envelopeUnit; + + SoundUnit *nextEventUnit; + + unsigned long cycleCounter; + unsigned long soMask; + unsigned long prevOut; + + unsigned char nr4; + bool master; + + void setEvent(); + +public: + Channel2(); + void setNr1(unsigned data); + void setNr2(unsigned data); + void setNr3(unsigned data); + void setNr4(unsigned data); + + void setSo(unsigned long soMask); + // void deactivate() { disableMaster(); setEvent(); } + bool isActive() const { return master; } + + void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles); + + void reset(); + void init(bool cgb); + void loadState(const SaveState &state); + + templatevoid SyncState(NewState *ns); +}; + +} + +#endif diff --git a/libgambatte/src/sound/channel3.cpp b/libgambatte/src/sound/channel3.cpp index d4cf78414c..0d0d069f9f 100644 --- a/libgambatte/src/sound/channel3.cpp +++ b/libgambatte/src/sound/channel3.cpp @@ -196,27 +196,27 @@ void Channel3::update(uint_least32_t *buf, const unsigned long soBaseVol, unsign } } -SYNCFUNC(Channel3) -{ - NSS(waveRam); - - SSS(lengthCounter); - - NSS(cycleCounter); - NSS(soMask); - NSS(prevOut); - NSS(waveCounter); - NSS(lastReadTime); - - NSS(nr0); - NSS(nr3); - NSS(nr4); - NSS(wavePos); - NSS(rShift); - NSS(sampleBuf); - - NSS(master); - NSS(cgb); -} +SYNCFUNC(Channel3) +{ + NSS(waveRam); + + SSS(lengthCounter); + + NSS(cycleCounter); + NSS(soMask); + NSS(prevOut); + NSS(waveCounter); + NSS(lastReadTime); + + NSS(nr0); + NSS(nr3); + NSS(nr4); + NSS(wavePos); + NSS(rShift); + NSS(sampleBuf); + + NSS(master); + NSS(cgb); +} } diff --git a/libgambatte/src/sound/channel3.h b/libgambatte/src/sound/channel3.h index ae0ba2f64a..fe81789899 100644 --- a/libgambatte/src/sound/channel3.h +++ b/libgambatte/src/sound/channel3.h @@ -1,105 +1,105 @@ -/*************************************************************************** - * 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 SOUND_CHANNEL3_H -#define SOUND_CHANNEL3_H - -#include "gbint.h" -#include "master_disabler.h" -#include "length_counter.h" -#include "newstate.h" - -namespace gambatte { - -struct SaveState; - -class Channel3 { - class Ch3MasterDisabler : public MasterDisabler { - unsigned long &waveCounter; - - public: - Ch3MasterDisabler(bool &m, unsigned long &wC) : MasterDisabler(m), waveCounter(wC) {} - void operator()() { MasterDisabler::operator()(); waveCounter = SoundUnit::COUNTER_DISABLED; } - }; - - unsigned char waveRam[0x10]; - - Ch3MasterDisabler disableMaster; - LengthCounter lengthCounter; - - unsigned long cycleCounter; - unsigned long soMask; - unsigned long prevOut; - unsigned long waveCounter; - unsigned long lastReadTime; - - unsigned char nr0; - unsigned char nr3; - unsigned char nr4; - unsigned char wavePos; - unsigned char rShift; - unsigned char sampleBuf; - - bool master; - bool cgb; - - void updateWaveCounter(unsigned long cc); - -public: - Channel3(); - bool isActive() const { return master; } - void reset(); - void init(bool cgb); - void setStatePtrs(SaveState &state); - void loadState(const SaveState &state); - void setNr0(unsigned data); - void setNr1(unsigned data) { lengthCounter.nr1Change(data, nr4, cycleCounter); } - void setNr2(unsigned data); - void setNr3(unsigned data) { nr3 = data; } - void setNr4(unsigned data); - void setSo(unsigned long soMask); - void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles); - - unsigned waveRamRead(unsigned index) const { - if (master) { - if (!cgb && cycleCounter != lastReadTime) - return 0xFF; - - index = wavePos >> 1; - } - - return waveRam[index]; - } - - void waveRamWrite(unsigned index, unsigned data) { - if (master) { - if (!cgb && cycleCounter != lastReadTime) - return; - - index = wavePos >> 1; - } - - waveRam[index] = data; - } - - templatevoid SyncState(NewState *ns); -}; - -} - -#endif +/*************************************************************************** + * 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 SOUND_CHANNEL3_H +#define SOUND_CHANNEL3_H + +#include "gbint.h" +#include "master_disabler.h" +#include "length_counter.h" +#include "newstate.h" + +namespace gambatte { + +struct SaveState; + +class Channel3 { + class Ch3MasterDisabler : public MasterDisabler { + unsigned long &waveCounter; + + public: + Ch3MasterDisabler(bool &m, unsigned long &wC) : MasterDisabler(m), waveCounter(wC) {} + void operator()() { MasterDisabler::operator()(); waveCounter = SoundUnit::COUNTER_DISABLED; } + }; + + unsigned char waveRam[0x10]; + + Ch3MasterDisabler disableMaster; + LengthCounter lengthCounter; + + unsigned long cycleCounter; + unsigned long soMask; + unsigned long prevOut; + unsigned long waveCounter; + unsigned long lastReadTime; + + unsigned char nr0; + unsigned char nr3; + unsigned char nr4; + unsigned char wavePos; + unsigned char rShift; + unsigned char sampleBuf; + + bool master; + bool cgb; + + void updateWaveCounter(unsigned long cc); + +public: + Channel3(); + bool isActive() const { return master; } + void reset(); + void init(bool cgb); + void setStatePtrs(SaveState &state); + void loadState(const SaveState &state); + void setNr0(unsigned data); + void setNr1(unsigned data) { lengthCounter.nr1Change(data, nr4, cycleCounter); } + void setNr2(unsigned data); + void setNr3(unsigned data) { nr3 = data; } + void setNr4(unsigned data); + void setSo(unsigned long soMask); + void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles); + + unsigned waveRamRead(unsigned index) const { + if (master) { + if (!cgb && cycleCounter != lastReadTime) + return 0xFF; + + index = wavePos >> 1; + } + + return waveRam[index]; + } + + void waveRamWrite(unsigned index, unsigned data) { + if (master) { + if (!cgb && cycleCounter != lastReadTime) + return; + + index = wavePos >> 1; + } + + waveRam[index] = data; + } + + templatevoid SyncState(NewState *ns); +}; + +} + +#endif diff --git a/libgambatte/src/sound/channel4.cpp b/libgambatte/src/sound/channel4.cpp index f1b355adc7..31c0590646 100644 --- a/libgambatte/src/sound/channel4.cpp +++ b/libgambatte/src/sound/channel4.cpp @@ -157,15 +157,15 @@ void Channel4::Lfsr::loadState(const SaveState &state) { nr3 = state.mem.ioamhram.get()[0x122]; } -template -void Channel4::Lfsr::SyncState(NewState *ns) -{ - NSS(counter); - NSS(backupCounter); - NSS(reg); - NSS(nr3); - NSS(master); -} +template +void Channel4::Lfsr::SyncState(NewState *ns) +{ + NSS(counter); + NSS(backupCounter); + NSS(reg); + NSS(nr3); + NSS(master); +} Channel4::Channel4() : staticOutputTest(*this, lfsr), @@ -295,25 +295,25 @@ void Channel4::update(uint_least32_t *buf, const unsigned long soBaseVol, unsign cycleCounter -= SoundUnit::COUNTER_MAX; } } - -SYNCFUNC(Channel4) -{ - SSS(lengthCounter); - SSS(envelopeUnit); - SSS(lfsr); - - EBS(nextEventUnit, 0); - EVS(nextEventUnit, &lfsr, 1); - EVS(nextEventUnit, &envelopeUnit, 2); - EVS(nextEventUnit, &lengthCounter, 3); - EES(nextEventUnit, NULL); - - NSS(cycleCounter); - NSS(soMask); - NSS(prevOut); - - NSS(nr4); - NSS(master); -} + +SYNCFUNC(Channel4) +{ + SSS(lengthCounter); + SSS(envelopeUnit); + SSS(lfsr); + + EBS(nextEventUnit, 0); + EVS(nextEventUnit, &lfsr, 1); + EVS(nextEventUnit, &envelopeUnit, 2); + EVS(nextEventUnit, &lengthCounter, 3); + EES(nextEventUnit, NULL); + + NSS(cycleCounter); + NSS(soMask); + NSS(prevOut); + + NSS(nr4); + NSS(master); +} } diff --git a/libgambatte/src/sound/channel4.h b/libgambatte/src/sound/channel4.h index 6365ad682f..785c98f2f6 100644 --- a/libgambatte/src/sound/channel4.h +++ b/libgambatte/src/sound/channel4.h @@ -24,7 +24,7 @@ #include "length_counter.h" #include "envelope_unit.h" #include "static_output_tester.h" -#include "newstate.h" +#include "newstate.h" namespace gambatte { @@ -52,7 +52,7 @@ class Channel4 { void killCounter() { counter = COUNTER_DISABLED; } void reviveCounter(unsigned long cc); - templatevoid SyncState(NewState *ns); + templatevoid SyncState(NewState *ns); }; class Ch4MasterDisabler : public MasterDisabler { @@ -97,7 +97,7 @@ public: void init(bool cgb); void loadState(const SaveState &state); - templatevoid SyncState(NewState *ns); + templatevoid SyncState(NewState *ns); }; } diff --git a/libgambatte/src/sound/duty_unit.cpp b/libgambatte/src/sound/duty_unit.cpp index 2059239a75..02ba2a8a04 100644 --- a/libgambatte/src/sound/duty_unit.cpp +++ b/libgambatte/src/sound/duty_unit.cpp @@ -142,15 +142,15 @@ void DutyUnit::reviveCounter(const unsigned long cc) { setCounter(); } -SYNCFUNC(DutyUnit) -{ - NSS(counter); - NSS(nextPosUpdate); - NSS(period); - NSS(pos); - NSS(duty); - NSS(high); - NSS(enableEvents); -} +SYNCFUNC(DutyUnit) +{ + NSS(counter); + NSS(nextPosUpdate); + NSS(period); + NSS(pos); + NSS(duty); + NSS(high); + NSS(enableEvents); +} } diff --git a/libgambatte/src/sound/duty_unit.h b/libgambatte/src/sound/duty_unit.h index cdf4a64f32..0a5f3e9412 100644 --- a/libgambatte/src/sound/duty_unit.h +++ b/libgambatte/src/sound/duty_unit.h @@ -22,7 +22,7 @@ #include "sound_unit.h" #include "master_disabler.h" #include "../savestate.h" -#include "newstate.h" +#include "newstate.h" namespace gambatte { @@ -55,7 +55,7 @@ public: unsigned getFreq() const { return 2048 - (period >> 1); } void setFreq(unsigned newFreq, unsigned long cc); - templatevoid SyncState(NewState *ns); + templatevoid SyncState(NewState *ns); }; class DutyMasterDisabler : public MasterDisabler { diff --git a/libgambatte/src/sound/envelope_unit.cpp b/libgambatte/src/sound/envelope_unit.cpp index f482bbf7a3..7c81607fa0 100644 --- a/libgambatte/src/sound/envelope_unit.cpp +++ b/libgambatte/src/sound/envelope_unit.cpp @@ -98,11 +98,11 @@ void EnvelopeUnit::loadState(const SaveState::SPU::Env &estate, const unsigned n this->nr2 = nr2; } -SYNCFUNC(EnvelopeUnit) -{ - NSS(counter); - NSS(nr2); - NSS(volume); -} +SYNCFUNC(EnvelopeUnit) +{ + NSS(counter); + NSS(nr2); + NSS(volume); +} } diff --git a/libgambatte/src/sound/envelope_unit.h b/libgambatte/src/sound/envelope_unit.h index 68d6bdc54c..4a0587d141 100644 --- a/libgambatte/src/sound/envelope_unit.h +++ b/libgambatte/src/sound/envelope_unit.h @@ -21,7 +21,7 @@ #include "sound_unit.h" #include "../savestate.h" -#include "newstate.h" +#include "newstate.h" namespace gambatte { @@ -48,7 +48,7 @@ public: void reset(); void loadState(const SaveState::SPU::Env &estate, unsigned nr2, unsigned long cc); - templatevoid SyncState(NewState *ns); + templatevoid SyncState(NewState *ns); }; } diff --git a/libgambatte/src/sound/length_counter.cpp b/libgambatte/src/sound/length_counter.cpp index cb3616a4ff..e1e9f00862 100644 --- a/libgambatte/src/sound/length_counter.cpp +++ b/libgambatte/src/sound/length_counter.cpp @@ -83,11 +83,11 @@ void LengthCounter::loadState(const SaveState::SPU::LCounter &lstate, const unsi lengthCounter = lstate.lengthCounter; } -SYNCFUNC(LengthCounter) -{ - NSS(counter); - NSS(lengthCounter); - NSS(cgb); -} +SYNCFUNC(LengthCounter) +{ + NSS(counter); + NSS(lengthCounter); + NSS(cgb); +} } diff --git a/libgambatte/src/sound/length_counter.h b/libgambatte/src/sound/length_counter.h index 533606171a..7250ac0a10 100644 --- a/libgambatte/src/sound/length_counter.h +++ b/libgambatte/src/sound/length_counter.h @@ -21,7 +21,7 @@ #include "sound_unit.h" #include "../savestate.h" -#include "newstate.h" +#include "newstate.h" namespace gambatte { @@ -42,7 +42,7 @@ public: void init(bool cgb); void loadState(const SaveState::SPU::LCounter &lstate, unsigned long cc); - templatevoid SyncState(NewState *ns); + templatevoid SyncState(NewState *ns); }; } diff --git a/libgambatte/src/tima.cpp b/libgambatte/src/tima.cpp index c125bd57fb..5272443ba2 100644 --- a/libgambatte/src/tima.cpp +++ b/libgambatte/src/tima.cpp @@ -163,13 +163,13 @@ void Tima::doIrqEvent(const TimaInterruptRequester timaIrq) { timaIrq.setNextIrqEventTime(timaIrq.nextIrqEventTime() + ((256u - tma_) << timaClock[tac_ & 3])); } -SYNCFUNC(Tima) -{ - NSS(lastUpdate_); - NSS(tmatime_); - NSS(tima_); - NSS(tma_); - NSS(tac_); -} +SYNCFUNC(Tima) +{ + NSS(lastUpdate_); + NSS(tmatime_); + NSS(tima_); + NSS(tma_); + NSS(tac_); +} } diff --git a/libgambatte/src/tima.h b/libgambatte/src/tima.h index 4bb6c31d31..a3c0c995bb 100644 --- a/libgambatte/src/tima.h +++ b/libgambatte/src/tima.h @@ -60,7 +60,7 @@ public: void doIrqEvent(TimaInterruptRequester timaIrq); - templatevoid SyncState(NewState *ns); + templatevoid SyncState(NewState *ns); }; } diff --git a/libgambatte/src/video.cpp b/libgambatte/src/video.cpp index fe17b72310..0437f51283 100644 --- a/libgambatte/src/video.cpp +++ b/libgambatte/src/video.cpp @@ -36,7 +36,14 @@ void LCD::setCgbPalette(unsigned *lut) { refreshPalettes(); } -unsigned long LCD::gbcToRgb32(const unsigned bgr15) { +unsigned long LCD::gbcToRgb32(const unsigned bgr15, bool trueColor) { + unsigned long const r = bgr15 & 0x1F; + unsigned long const g = bgr15 >> 5 & 0x1F; + unsigned long const b = bgr15 >> 10 & 0x1F; + + if (trueColor) + return (r << 19) | (g << 11) | (b << 3); + return cgbColorsRgb32[bgr15 & 0x7FFF]; } @@ -66,6 +73,10 @@ void LCD::reset(const unsigned char *const oamram, const unsigned char *vram, co refreshPalettes(); } +void LCD::setCgb(bool cgb) { + ppu.setCgb(cgb); +} + static unsigned long mode2IrqSchedule(const unsigned statReg, const LyCounter &lyCounter, const unsigned long cycleCounter) { if (!(statReg & 0x20)) return DISABLED_TIME; @@ -140,8 +151,8 @@ void LCD::loadState(const SaveState &state, const unsigned char *const oamram) { void LCD::refreshPalettes() { if (ppu.cgb()) { for (unsigned i = 0; i < 8 * 8; i += 2) { - ppu.bgPalette()[i >> 1] = gbcToRgb32( bgpData[i] | bgpData[i + 1] << 8); - ppu.spPalette()[i >> 1] = gbcToRgb32(objpData[i] | objpData[i + 1] << 8); + ppu.bgPalette()[i >> 1] = gbcToRgb32( bgpData[i] | bgpData[i + 1] << 8, isTrueColors()); + ppu.spPalette()[i >> 1] = gbcToRgb32(objpData[i] | objpData[i + 1] << 8, isTrueColors()); } } else { setDmgPalette(ppu.bgPalette() , dmgColorsRgb32 , bgpData[0]); @@ -150,6 +161,32 @@ void LCD::refreshPalettes() { } } +void LCD::copyCgbPalettesToDmg() { + for (unsigned i = 0; i < 4; i++) { + dmgColorsRgb32[i] = gbcToRgb32(bgpData[i * 2] | bgpData[i * 2 + 1] << 8, isTrueColors()); + } + for (unsigned i = 0; i < 8; i++) { + dmgColorsRgb32[i + 4] = gbcToRgb32(objpData[i * 2] | objpData[i * 2 + 1] << 8, isTrueColors()); + } +} + +void LCD::blackScreen() { + if (ppu.cgb()) { + for (unsigned i = 0; i < 8 * 8; i += 2) { + ppu.bgPalette()[i >> 1] = 0; + ppu.spPalette()[i >> 1] = 0; + } + } + else { + for (unsigned i = 0; i < 4; i++) { + dmgColorsRgb32[i] = 0; + } + for (unsigned i = 0; i < 8; i++) { + dmgColorsRgb32[i + 4] = 0; + } + } +} + namespace { template @@ -168,7 +205,7 @@ void LCD::updateScreen(const bool blanklcd, const unsigned long cycleCounter) { update(cycleCounter); if (blanklcd && ppu.frameBuf().fb()) { - const unsigned long color = ppu.cgb() ? gbcToRgb32(0xFFFF) : dmgColorsRgb32[0]; + const unsigned long color = ppu.cgb() ? gbcToRgb32(0xFFFF, isTrueColors()) : dmgColorsRgb32[0]; clear(ppu.frameBuf().fb(), color, ppu.frameBuf().pitch()); } } @@ -282,23 +319,23 @@ bool LCD::cgbpAccessible(const unsigned long cycleCounter) { } void LCD::doCgbColorChange(unsigned char *const pdata, - unsigned long *const palette, unsigned index, const unsigned data) { + unsigned long *const palette, unsigned index, const unsigned data, bool trueColor) { pdata[index] = data; index >>= 1; - palette[index] = gbcToRgb32(pdata[index << 1] | pdata[(index << 1) + 1] << 8); + palette[index] = gbcToRgb32(pdata[index << 1] | pdata[(index << 1) + 1] << 8, trueColor); } void LCD::doCgbBgColorChange(unsigned index, const unsigned data, const unsigned long cycleCounter) { if (cgbpAccessible(cycleCounter)) { update(cycleCounter); - doCgbColorChange(bgpData, ppu.bgPalette(), index, data); + doCgbColorChange(bgpData, ppu.bgPalette(), index, data, isTrueColors()); } } void LCD::doCgbSpColorChange(unsigned index, const unsigned data, const unsigned long cycleCounter) { if (cgbpAccessible(cycleCounter)) { update(cycleCounter); - doCgbColorChange(objpData, ppu.spPalette(), index, data); + doCgbColorChange(objpData, ppu.spPalette(), index, data, isTrueColors()); } } @@ -734,19 +771,19 @@ void LCD::setDmgPaletteColor(const unsigned palNum, const unsigned colorNum, con // don't need to save or load rgb32 color data -SYNCFUNC(LCD) -{ - SSS(ppu); - NSS(bgpData); - NSS(objpData); - SSS(eventTimes_); - SSS(m0Irq_); - SSS(lycIrq); - SSS(nextM0Time_); - - NSS(statReg); - NSS(m2IrqStatReg_); - NSS(m1IrqStatReg_); -} +SYNCFUNC(LCD) +{ + SSS(ppu); + NSS(bgpData); + NSS(objpData); + SSS(eventTimes_); + SSS(m0Irq_); + SSS(lycIrq); + SSS(nextM0Time_); + + NSS(statReg); + NSS(m2IrqStatReg_); + NSS(m1IrqStatReg_); +} } diff --git a/libgambatte/src/video.h b/libgambatte/src/video.h index cf4cd6e30a..67aacc3a9c 100644 --- a/libgambatte/src/video.h +++ b/libgambatte/src/video.h @@ -25,7 +25,7 @@ #include "interruptrequester.h" #include "minkeeper.h" #include -#include "newstate.h" +#include "newstate.h" namespace gambatte { @@ -79,10 +79,10 @@ public: unsigned statReg() const { return statReg_; } template - void SyncState(NewState *ns) - { - NSS(statReg_); - NSS(lycReg_); + void SyncState(NewState *ns) + { + NSS(statReg_); + NSS(lycReg_); } }; @@ -120,12 +120,12 @@ class LCD { void flagIrq(const unsigned bit) { memEventRequester_.flagIrq(bit); } void flagHdmaReq() { memEventRequester_.flagHdmaReq(); } - template - void SyncState(NewState *ns) - { - SSS(eventMin_); - SSS(memEventMin_); - //SSS(memEventRequester_); // not needed + template + void SyncState(NewState *ns) + { + SSS(eventMin_); + SSS(memEventMin_); + //SSS(memEventRequester_); // not needed } }; @@ -147,8 +147,8 @@ class LCD { static void setDmgPalette(unsigned long *palette, const unsigned long *dmgColors, unsigned data); void setDmgPaletteColor(unsigned index, unsigned long rgb32); - unsigned long gbcToRgb32(const unsigned bgr15); - void doCgbColorChange(unsigned char *const pdata, unsigned long *const palette, unsigned index, const unsigned data); + unsigned long gbcToRgb32(const unsigned bgr15, bool trueColor); + void doCgbColorChange(unsigned char *const pdata, unsigned long *const palette, unsigned index, const unsigned data, bool trueColor); void refreshPalettes(); void setDBuffer(); @@ -175,6 +175,9 @@ public: void setCgbPalette(unsigned *lut); void setVideoBuffer(uint_least32_t *videoBuf, int pitch); void setLayers(unsigned mask) { ppu.setLayers(mask); } + void setCgb(bool cgb); + void copyCgbPalettesToDmg(); + void blackScreen(); int debugGetLY() const { return ppu.lyCounter().ly(); } @@ -267,13 +270,14 @@ public: bool isCgb() const { return ppu.cgb(); } bool isDoubleSpeed() const { return ppu.lyCounter().isDoubleSpeed(); } + bool isTrueColors() const { return ppu.trueColors(); } unsigned long *bgPalette() { return ppu.bgPalette(); } unsigned long *spPalette() { return ppu.spPalette(); } void setScanlineCallback(void (*callback)(), int sl) { scanlinecallback = callback; scanlinecallbacksl = sl; } - templatevoid SyncState(NewState *ns); + templatevoid SyncState(NewState *ns); }; } diff --git a/libgambatte/src/video/ly_counter.cpp b/libgambatte/src/video/ly_counter.cpp index 01ae017132..4b978dfeee 100644 --- a/libgambatte/src/video/ly_counter.cpp +++ b/libgambatte/src/video/ly_counter.cpp @@ -64,13 +64,13 @@ void LyCounter::setDoubleSpeed(const bool ds_in) { ds = ds_in; lineTime_ = 456U << ds_in; } - -SYNCFUNC(LyCounter) -{ - NSS(time_); - NSS(lineTime_); - NSS(ly_); - NSS(ds); -} + +SYNCFUNC(LyCounter) +{ + NSS(time_); + NSS(lineTime_); + NSS(ly_); + NSS(ds); +} } diff --git a/libgambatte/src/video/ly_counter.h b/libgambatte/src/video/ly_counter.h index f65d9ef7dc..9aca9877b1 100644 --- a/libgambatte/src/video/ly_counter.h +++ b/libgambatte/src/video/ly_counter.h @@ -19,7 +19,7 @@ #ifndef LY_COUNTER_H #define LY_COUNTER_H -#include "newstate.h" +#include "newstate.h" namespace gambatte { @@ -52,7 +52,7 @@ public: void setDoubleSpeed(bool ds_in); unsigned long time() const { return time_; } - templatevoid SyncState(NewState *ns); + templatevoid SyncState(NewState *ns); }; } diff --git a/libgambatte/src/video/lyc_irq.cpp b/libgambatte/src/video/lyc_irq.cpp index d9a138a3fd..024ef35233 100644 --- a/libgambatte/src/video/lyc_irq.cpp +++ b/libgambatte/src/video/lyc_irq.cpp @@ -95,14 +95,14 @@ void LycIrq::lcdReset() { lycReg_ = lycRegSrc_; } -SYNCFUNC(LycIrq) -{ - NSS(time_); - NSS(lycRegSrc_); - NSS(statRegSrc_); - NSS(lycReg_); - NSS(statReg_); - NSS(cgb_); -} +SYNCFUNC(LycIrq) +{ + NSS(time_); + NSS(lycRegSrc_); + NSS(statRegSrc_); + NSS(lycReg_); + NSS(statReg_); + NSS(cgb_); +} } diff --git a/libgambatte/src/video/lyc_irq.h b/libgambatte/src/video/lyc_irq.h index 19577a802a..510039d996 100644 --- a/libgambatte/src/video/lyc_irq.h +++ b/libgambatte/src/video/lyc_irq.h @@ -19,7 +19,7 @@ #ifndef VIDEO_LYC_IRQ_H #define VIDEO_LYC_IRQ_H -#include "newstate.h" +#include "newstate.h" namespace gambatte { @@ -54,7 +54,7 @@ public: regChange(statRegSrc_, lycReg, lyCounter, cc); } - templatevoid SyncState(NewState *ns); + templatevoid SyncState(NewState *ns); }; } diff --git a/libgambatte/src/video/next_m0_time.cpp b/libgambatte/src/video/next_m0_time.cpp index 15bf6441b6..79cc59d06c 100644 --- a/libgambatte/src/video/next_m0_time.cpp +++ b/libgambatte/src/video/next_m0_time.cpp @@ -7,9 +7,9 @@ void NextM0Time::predictNextM0Time(const PPU &ppu) { predictedNextM0Time_ = ppu.predictedNextXposTime(167); } -SYNCFUNC(NextM0Time) -{ - NSS(predictedNextM0Time_); -} - -} +SYNCFUNC(NextM0Time) +{ + NSS(predictedNextM0Time_); +} + +} diff --git a/libgambatte/src/video/next_m0_time.h b/libgambatte/src/video/next_m0_time.h index a47f337118..0a91c4d330 100644 --- a/libgambatte/src/video/next_m0_time.h +++ b/libgambatte/src/video/next_m0_time.h @@ -1,7 +1,7 @@ #ifndef NEXT_M0_TIME_H_ #define NEXT_M0_TIME_H_ -#include "newstate.h" +#include "newstate.h" namespace gambatte { @@ -14,7 +14,7 @@ public: void invalidatePredictedNextM0Time() { predictedNextM0Time_ = 0; } unsigned predictedNextM0Time() const { return predictedNextM0Time_; } - templatevoid SyncState(NewState *ns); + templatevoid SyncState(NewState *ns); }; } diff --git a/libgambatte/src/video/ppu.cpp b/libgambatte/src/video/ppu.cpp index ed884f7b97..7ff497e44d 100644 --- a/libgambatte/src/video/ppu.cpp +++ b/libgambatte/src/video/ppu.cpp @@ -1630,6 +1630,8 @@ void PPU::loadState(const SaveState &ss, const unsigned char *const oamram) { p_.weMaster = ss.ppu.weMaster; p_.winDrawState = ss.ppu.winDrawState & (WIN_DRAW_START | WIN_DRAW_STARTED); p_.lastM0Time = p_.now - ss.ppu.lastM0Time; + p_.cgb = ss.ppu.isCgb; + p_.trueColors = ss.ppu.trueColors; loadSpriteList(p_, ss); if (m3loopState && videoCycles < 144 * 456L && p_.xpos < 168 @@ -1739,70 +1741,71 @@ void PPU::update(const unsigned long cc) { } } -SYNCFUNC(PPU) -{ - NSS(p_.bgPalette); - NSS(p_.spPalette); - NSS(p_.spriteList); - NSS(p_.spwordList); - NSS(p_.nextSprite); - NSS(p_.currentSprite); - - EBS(p_.nextCallPtr, 0); - EVS(p_.nextCallPtr, &M2::Ly0::f0_, 1); - EVS(p_.nextCallPtr, &M2::LyNon0::f0_, 2); - EVS(p_.nextCallPtr, &M2::LyNon0::f1_, 3); - EVS(p_.nextCallPtr, &M3Start::f0_, 4); - EVS(p_.nextCallPtr, &M3Start::f1_, 5); - EVS(p_.nextCallPtr, &M3Loop::Tile::f0_, 6); - EVS(p_.nextCallPtr, &M3Loop::Tile::f1_, 7); - EVS(p_.nextCallPtr, &M3Loop::Tile::f2_, 8); - EVS(p_.nextCallPtr, &M3Loop::Tile::f3_, 9); - EVS(p_.nextCallPtr, &M3Loop::Tile::f4_, 10); - EVS(p_.nextCallPtr, &M3Loop::Tile::f5_, 11); - EVS(p_.nextCallPtr, &M3Loop::LoadSprites::f0_, 12); - EVS(p_.nextCallPtr, &M3Loop::LoadSprites::f1_, 13); - EVS(p_.nextCallPtr, &M3Loop::LoadSprites::f2_, 14); - EVS(p_.nextCallPtr, &M3Loop::LoadSprites::f3_, 15); - EVS(p_.nextCallPtr, &M3Loop::LoadSprites::f4_, 16); - EVS(p_.nextCallPtr, &M3Loop::LoadSprites::f5_, 17); - EVS(p_.nextCallPtr, &M3Loop::StartWindowDraw::f0_, 18); - EVS(p_.nextCallPtr, &M3Loop::StartWindowDraw::f1_, 19); - EVS(p_.nextCallPtr, &M3Loop::StartWindowDraw::f2_, 20); - EVS(p_.nextCallPtr, &M3Loop::StartWindowDraw::f3_, 21); - EVS(p_.nextCallPtr, &M3Loop::StartWindowDraw::f4_, 22); - EVS(p_.nextCallPtr, &M3Loop::StartWindowDraw::f5_, 23); - EES(p_.nextCallPtr, NULL); - - NSS(p_.now); - NSS(p_.lastM0Time); - NSS(p_.cycles); - - NSS(p_.tileword); - NSS(p_.ntileword); - - SSS(p_.spriteMapper); - SSS(p_.lyCounter); - //SSS(p_.framebuf); // no state - - NSS(p_.lcdc); - NSS(p_.scy); - NSS(p_.scx); - NSS(p_.wy); - NSS(p_.wy2); - NSS(p_.wx); - NSS(p_.winDrawState); - NSS(p_.wscx); - NSS(p_.winYPos); - NSS(p_.reg0); - NSS(p_.reg1); - NSS(p_.attrib); - NSS(p_.nattrib); - NSS(p_.xpos); - NSS(p_.endx); - - NSS(p_.cgb); - NSS(p_.weMaster); -} +SYNCFUNC(PPU) +{ + NSS(p_.bgPalette); + NSS(p_.spPalette); + NSS(p_.spriteList); + NSS(p_.spwordList); + NSS(p_.nextSprite); + NSS(p_.currentSprite); + + EBS(p_.nextCallPtr, 0); + EVS(p_.nextCallPtr, &M2::Ly0::f0_, 1); + EVS(p_.nextCallPtr, &M2::LyNon0::f0_, 2); + EVS(p_.nextCallPtr, &M2::LyNon0::f1_, 3); + EVS(p_.nextCallPtr, &M3Start::f0_, 4); + EVS(p_.nextCallPtr, &M3Start::f1_, 5); + EVS(p_.nextCallPtr, &M3Loop::Tile::f0_, 6); + EVS(p_.nextCallPtr, &M3Loop::Tile::f1_, 7); + EVS(p_.nextCallPtr, &M3Loop::Tile::f2_, 8); + EVS(p_.nextCallPtr, &M3Loop::Tile::f3_, 9); + EVS(p_.nextCallPtr, &M3Loop::Tile::f4_, 10); + EVS(p_.nextCallPtr, &M3Loop::Tile::f5_, 11); + EVS(p_.nextCallPtr, &M3Loop::LoadSprites::f0_, 12); + EVS(p_.nextCallPtr, &M3Loop::LoadSprites::f1_, 13); + EVS(p_.nextCallPtr, &M3Loop::LoadSprites::f2_, 14); + EVS(p_.nextCallPtr, &M3Loop::LoadSprites::f3_, 15); + EVS(p_.nextCallPtr, &M3Loop::LoadSprites::f4_, 16); + EVS(p_.nextCallPtr, &M3Loop::LoadSprites::f5_, 17); + EVS(p_.nextCallPtr, &M3Loop::StartWindowDraw::f0_, 18); + EVS(p_.nextCallPtr, &M3Loop::StartWindowDraw::f1_, 19); + EVS(p_.nextCallPtr, &M3Loop::StartWindowDraw::f2_, 20); + EVS(p_.nextCallPtr, &M3Loop::StartWindowDraw::f3_, 21); + EVS(p_.nextCallPtr, &M3Loop::StartWindowDraw::f4_, 22); + EVS(p_.nextCallPtr, &M3Loop::StartWindowDraw::f5_, 23); + EES(p_.nextCallPtr, NULL); + + NSS(p_.now); + NSS(p_.lastM0Time); + NSS(p_.cycles); + + NSS(p_.tileword); + NSS(p_.ntileword); + + SSS(p_.spriteMapper); + SSS(p_.lyCounter); + //SSS(p_.framebuf); // no state + + NSS(p_.lcdc); + NSS(p_.scy); + NSS(p_.scx); + NSS(p_.wy); + NSS(p_.wy2); + NSS(p_.wx); + NSS(p_.winDrawState); + NSS(p_.wscx); + NSS(p_.winYPos); + NSS(p_.reg0); + NSS(p_.reg1); + NSS(p_.attrib); + NSS(p_.nattrib); + NSS(p_.xpos); + NSS(p_.endx); + + NSS(p_.cgb); + NSS(p_.trueColors); + NSS(p_.weMaster); +} } diff --git a/libgambatte/src/video/ppu.h b/libgambatte/src/video/ppu.h index 6ca279af9e..12e197408c 100644 --- a/libgambatte/src/video/ppu.h +++ b/libgambatte/src/video/ppu.h @@ -23,7 +23,7 @@ #include "video/sprite_mapper.h" #include "gbint.h" -#include "newstate.h" +#include "newstate.h" namespace gambatte { @@ -92,6 +92,7 @@ struct PPUPriv { unsigned char endx; bool cgb; + bool trueColors; bool weMaster; PPUPriv(NextM0Time &nextM0Time, const unsigned char *oamram, const unsigned char *vram); @@ -107,6 +108,7 @@ public: unsigned long * bgPalette() { return p_.bgPalette; } bool cgb() const { return p_.cgb; } + bool trueColors() const { return p_.trueColors; } void doLyCountEvent() { p_.lyCounter.doEvent(); } unsigned long doSpriteMapEvent(unsigned long time) { return p_.spriteMapper.doEvent(time); } const PPUFrameBuf & frameBuf() const { return p_.framebuf; } @@ -133,8 +135,10 @@ public: unsigned long * spPalette() { return p_.spPalette; } void update(unsigned long cc); void setLayers(unsigned mask) { p_.layersMask = mask; } + void setCgb(bool cgb) { p_.cgb = cgb; } + void setTrueColors(bool trueColors) { p_.trueColors = trueColors; } - templatevoid SyncState(NewState *ns); + templatevoid SyncState(NewState *ns); }; } diff --git a/libgambatte/src/video/sprite_mapper.cpp b/libgambatte/src/video/sprite_mapper.cpp index 80b588c088..cd2e28b1b2 100644 --- a/libgambatte/src/video/sprite_mapper.cpp +++ b/libgambatte/src/video/sprite_mapper.cpp @@ -117,16 +117,16 @@ void SpriteMapper::OamReader::loadState(const SaveState &ss, const unsigned char change(lu); } -SYNCFUNC(SpriteMapper::OamReader) -{ - NSS(buf); - NSS(szbuf); - - NSS(lu); - NSS(lastChange); - NSS(largeSpritesSrc); - NSS(cgb_); -} +SYNCFUNC(SpriteMapper::OamReader) +{ + NSS(buf); + NSS(szbuf); + + NSS(lu); + NSS(lastChange); + NSS(largeSpritesSrc); + NSS(cgb_); +} void SpriteMapper::OamReader::enableDisplay(const unsigned long cc) { std::memset(buf, 0x00, sizeof(buf)); @@ -190,13 +190,13 @@ unsigned long SpriteMapper::doEvent(const unsigned long time) { return oamReader.changed() ? time + oamReader.lyCounter.lineTime() : static_cast(DISABLED_TIME); } -SYNCFUNC(SpriteMapper) -{ - NSS(spritemap); - NSS(num); - - SSS(nextM0Time_); - SSS(oamReader); -} +SYNCFUNC(SpriteMapper) +{ + NSS(spritemap); + NSS(num); + + SSS(nextM0Time_); + SSS(oamReader); +} } diff --git a/libgambatte/src/video/sprite_mapper.h b/libgambatte/src/video/sprite_mapper.h index 09e269d769..b6171f842d 100644 --- a/libgambatte/src/video/sprite_mapper.h +++ b/libgambatte/src/video/sprite_mapper.h @@ -21,7 +21,7 @@ #include "ly_counter.h" #include "../savestate.h" -#include "newstate.h" +#include "newstate.h" namespace gambatte { class NextM0Time; @@ -58,7 +58,7 @@ class SpriteMapper { void loadState(const SaveState &ss, const unsigned char *oamram); bool inactivePeriodAfterDisplayEnable(const unsigned long cc) const { return cc < lu; } - templatevoid SyncState(NewState *ns); + templatevoid SyncState(NewState *ns); }; enum { NEED_SORTING_MASK = 0x80 }; @@ -124,7 +124,7 @@ public: void loadState(const SaveState &state, const unsigned char *const oamram) { oamReader.loadState(state, oamram); mapSprites(); } bool inactivePeriodAfterDisplayEnable(unsigned long cc) const { return oamReader.inactivePeriodAfterDisplayEnable(cc); } - templatevoid SyncState(NewState *ns); + templatevoid SyncState(NewState *ns); }; } diff --git a/output/dll/libgambatte.dll b/output/dll/libgambatte.dll index ce430d492b..252e3af447 100644 Binary files a/output/dll/libgambatte.dll and b/output/dll/libgambatte.dll differ