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