diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj
index 523868320b..367d8a6cc0 100644
--- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj
+++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj
@@ -237,6 +237,7 @@
+
diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs
index 099e39fdac..53d5050542 100644
--- a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs
+++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs
@@ -106,6 +106,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
TimeCallback = new LibGambatte.RTCCallback(GetCurrentTime);
LibGambatte.gambatte_setrtccallback(GambatteState, TimeCallback);
+
+ NewSaveCoreSetBuff();
}
catch
{
@@ -253,6 +255,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
ProcessSoundEnd();
FrameAdvancePost();
+
+ //DebugStates(); // for maximum fun only
}
static string MapperName(byte[] romdata)
@@ -414,6 +418,26 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
#region savestates
+ byte[] newsavebuff;
+
+ void NewSaveCoreSetBuff()
+ {
+ newsavebuff = new byte[LibGambatte.gambatte_newstatelen(GambatteState)];
+ }
+
+ byte[] NewSaveCoreBinary()
+ {
+ if (!LibGambatte.gambatte_newstatesave(GambatteState, newsavebuff, newsavebuff.Length))
+ throw new Exception("gambatte_newstatesave() returned false");
+ return newsavebuff;
+ }
+
+ void NewLoadCoreBinary(byte[] data)
+ {
+ if (!LibGambatte.gambatte_newstateload(GambatteState, data, data.Length))
+ throw new Exception("gambatte_newstateload() returned false");
+ }
+
///
/// handles the core-portion of savestating
///
@@ -464,7 +488,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
public void SaveStateBinary(System.IO.BinaryWriter writer)
{
- byte[] data = SaveCoreBinary();
+ //byte[] data = SaveCoreBinary();
+ byte[] data = NewSaveCoreBinary();
writer.Write(data.Length);
writer.Write(data);
@@ -482,7 +507,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
int length = reader.ReadInt32();
byte[] data = reader.ReadBytes(length);
- LoadCoreBinary(data);
+ //LoadCoreBinary(data);
+ NewLoadCoreBinary(data);
// other variables
IsLagFrame = reader.ReadBoolean();
@@ -503,6 +529,18 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
public bool BinarySaveStatesPreferred { get { return true; } }
+ void DebugStates()
+ {
+ var sd = new StateDebug();
+ var Save = new LibGambatte.DataFunction(sd.Save);
+ var Load = new LibGambatte.DataFunction(sd.Load);
+ var EnterSection = new LibGambatte.SectionFunction(sd.EnterSection);
+ var ExitSection = new LibGambatte.SectionFunction(sd.ExitSection);
+
+ LibGambatte.gambatte_newstatesave_ex(GambatteState, Save, EnterSection, ExitSection);
+ LibGambatte.gambatte_newstateload_ex(GambatteState, Load, EnterSection, ExitSection);
+ }
+
#endregion
#region memorycallback
diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibGambatte.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibGambatte.cs
index bbe95e20cf..0412969116 100644
--- a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibGambatte.cs
+++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibGambatte.cs
@@ -298,6 +298,46 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
[DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool gambatte_loadstate(IntPtr core, byte[] data, uint len);
+ ///
+ /// new savestate method
+ ///
+ ///
+ ///
+ [DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int gambatte_newstatelen(IntPtr core);
+
+ ///
+ /// new savestate method
+ ///
+ ///
+ ///
+ ///
+ ///
+ [DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern bool gambatte_newstatesave(IntPtr core, byte[] data, int len);
+
+ ///
+ /// new savestate method
+ ///
+ ///
+ ///
+ ///
+ ///
+ [DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern bool gambatte_newstateload(IntPtr core, byte[] data, int len);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ public delegate void DataFunction(IntPtr data, int length, string name);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ public delegate void SectionFunction(string name);
+
+ [DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void gambatte_newstatesave_ex(IntPtr core, DataFunction Save, SectionFunction EnterSection, SectionFunction ExitSection);
+
+ [DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void gambatte_newstateload_ex(IntPtr core, DataFunction Load, SectionFunction EnterSection, SectionFunction ExitSection);
+
///
/// ROM header title of currently loaded ROM image.
///
diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/StateDebug.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/StateDebug.cs
new file mode 100644
index 0000000000..74b538e1b6
--- /dev/null
+++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/StateDebug.cs
@@ -0,0 +1,61 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
+{
+ /*
+ * the new gambatte savestater includes functionality that
+ * could be used to make structured text mode savestates. this module just does a bit of
+ * sanity checking using that capability
+ */
+
+ public class StateDebug
+ {
+ Dictionary Data = new Dictionary();
+
+ string Path = "/";
+
+ List SaveHistory = new List();
+ int LoadHistoryPos = 0;
+
+ public void Save(IntPtr data, int length, string name)
+ {
+ byte[] d = new byte[length];
+ Marshal.Copy(data, d, 0, length);
+ string s = Path + name;
+ SaveHistory.Add(s);
+ if (Data.ContainsKey(s))
+ throw new Exception("Already stored");
+ Data[s] = d;
+ }
+
+ public void Load(IntPtr data, int length, string name)
+ {
+ string s = Path + name;
+ byte[] d = Data[s];
+ if (SaveHistory[LoadHistoryPos++] != s)
+ throw new Exception("Loading out of order!");
+ Marshal.Copy(d, 0, data, length);
+ }
+
+ public void EnterSection(string name)
+ {
+ Path = Path + name + '/';
+ }
+
+ public void ExitSection(string name)
+ {
+ int i = Path.Substring(0, Path.Length - 1).LastIndexOf('/');
+ if (i < 0)
+ throw new Exception("Couldn't unwind stack!");
+ string newPath = Path.Substring(0, i + 1);
+ string unwind = Path.Substring(0, Path.Length - 1).Substring(i + 1);
+ if (unwind != name)
+ throw new Exception("Left wrong section!");
+ Path = newPath;
+ }
+ }
+}
diff --git a/libgambatte/include/gambatte.h b/libgambatte/include/gambatte.h
index 4af0e6eb43..043c38a8ab 100644
--- a/libgambatte/include/gambatte.h
+++ b/libgambatte/include/gambatte.h
@@ -24,6 +24,7 @@
#include
#include
#include
+#include "newstate.h"
namespace gambatte {
enum { BG_PALETTE = 0, SP1_PALETTE = 1, SP2_PALETTE = 2 };
@@ -129,6 +130,9 @@ public:
void GetRegs(int *dest);
+ void SaveS(NewState *ns);
+ void LoadS(NewState *ns);
+
private:
struct Priv;
Priv *const p_;
diff --git a/libgambatte/libgambatte.vcxproj b/libgambatte/libgambatte.vcxproj
index 9ec4288c72..f87ce0bcba 100644
--- a/libgambatte/libgambatte.vcxproj
+++ b/libgambatte/libgambatte.vcxproj
@@ -102,6 +102,7 @@
+
@@ -134,6 +135,7 @@
+
diff --git a/libgambatte/libgambatte.vcxproj.filters b/libgambatte/libgambatte.vcxproj.filters
index 426b906169..52ac8d5a4d 100644
--- a/libgambatte/libgambatte.vcxproj.filters
+++ b/libgambatte/libgambatte.vcxproj.filters
@@ -120,15 +120,18 @@
Header Files
-
- Header Files
-
Header Files
Header Files
+
+ Header Files
+
+
+ Header Files
+
@@ -209,5 +212,8 @@
Source Files
+
+ Source Files
+
\ No newline at end of file
diff --git a/libgambatte/src/cinterface.cpp b/libgambatte/src/cinterface.cpp
index deb0f2b528..56ddb5afab 100644
--- a/libgambatte/src/cinterface.cpp
+++ b/libgambatte/src/cinterface.cpp
@@ -1,6 +1,7 @@
#include "cinterface.h"
#include "gambatte.h"
#include
+#include "newstate.h"
using namespace gambatte;
@@ -168,7 +169,53 @@ GBEXPORT void gambatte_savestate_destroy(char *data)
GBEXPORT int gambatte_loadstate(void *core, const char *data, unsigned len)
{
GB *g = (GB *) core;
- return g->loadState(std::istringstream(std::string(data, len), std::ios_base::binary | std::ios_base::in));
+ // msvc allows using an Rvalue directly where an Lvalue is need, which is kind of cool and sort of makes sense, but no
+ std::istringstream iss(std::string(data, len), std::ios_base::binary | std::ios_base::in);
+ return g->loadState(iss);
+}
+
+GBEXPORT long gambatte_newstatelen(void *core)
+{
+ GB *g = (GB *) core;
+ NewStateDummy dummy;
+ g->SaveS(&dummy);
+ return dummy.GetLength();
+}
+
+GBEXPORT int gambatte_newstatesave(void *core, char *data, long len)
+{
+ GB *g = (GB *) core;
+ NewStateExternalBuffer saver(data, len);
+ g->SaveS(&saver);
+ return !saver.Overflow() && saver.GetLength() == len;
+}
+
+GBEXPORT int gambatte_newstateload(void *core, const char *data, long len)
+{
+ GB *g = (GB *) core;
+ NewStateExternalBuffer loader((char *)data, len);
+ g->LoadS(&loader);
+ return !loader.Overflow() && loader.GetLength() == len;
+}
+
+GBEXPORT void gambatte_newstatesave_ex(void *core,
+ void (*Save_)(const void *ptr, size_t size, const char *name),
+ void (*EnterSection_)(const char *name),
+ void (*ExitSection_)(const char *name))
+{
+ GB *g = (GB *) core;
+ NewStateExternalFunctions saver(Save_, NULL, EnterSection_, ExitSection_);
+ g->SaveS(&saver);
+}
+
+GBEXPORT void gambatte_newstateload_ex(void *core,
+ void (*Load_)(void *ptr, size_t size, const char *name),
+ void (*EnterSection_)(const char *name),
+ void (*ExitSection_)(const char *name))
+{
+ GB *g = (GB *) core;
+ NewStateExternalFunctions loader(NULL, Load_, EnterSection_, ExitSection_);
+ g->LoadS(&loader);
}
static char horriblebuff[64];
diff --git a/libgambatte/src/cpu.cpp b/libgambatte/src/cpu.cpp
index c83f434f8b..30e5190228 100644
--- a/libgambatte/src/cpu.cpp
+++ b/libgambatte/src/cpu.cpp
@@ -1,2875 +1,2916 @@
-/***************************************************************************
- * 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::saveState(SaveState &state) {
- cycleCounter_ = memory.saveState(state, cycleCounter_);
-
- calcHF(HF1, HF2);
-
- state.cpu.cycleCounter = cycleCounter_;
- state.cpu.PC = PC_;
- state.cpu.SP = SP;
- state.cpu.A = A_;
- state.cpu.B = B;
- state.cpu.C = C;
- state.cpu.D = D;
- state.cpu.E = E;
- state.cpu.F = F();
- state.cpu.H = H;
- state.cpu.L = L;
- state.cpu.skip = skip;
-}
-
-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); 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):
-//Push address of next instruction onto stack and then jump to address stored in next two bytes in memory:
-#define call_nn() do { \
- PUSH(((PC + 2) >> 8) & 0xFF, (PC + 2) & 0xFF); \
- jp_nn(); \
-} 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);
-
- 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[13];
- 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(opcode);
- result[12] = opcode;
- tracecallback((void *)result);
- }
- else {
- PC_READ(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;
- }
- 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;
-}
-
-}
+/***************************************************************************
+ * 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::saveState(SaveState &state) {
+ cycleCounter_ = memory.saveState(state, cycleCounter_);
+
+ calcHF(HF1, HF2);
+
+ state.cpu.cycleCounter = cycleCounter_;
+ state.cpu.PC = PC_;
+ state.cpu.SP = SP;
+ state.cpu.A = A_;
+ state.cpu.B = B;
+ state.cpu.C = C;
+ state.cpu.D = D;
+ state.cpu.E = E;
+ state.cpu.F = F();
+ state.cpu.H = H;
+ state.cpu.L = L;
+ state.cpu.skip = skip;
+}
+
+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); 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):
+//Push address of next instruction onto stack and then jump to address stored in next two bytes in memory:
+#define call_nn() do { \
+ PUSH(((PC + 2) >> 8) & 0xFF, (PC + 2) & 0xFF); \
+ jp_nn(); \
+} 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);
+
+ 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[13];
+ 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(opcode);
+ result[12] = opcode;
+ tracecallback((void *)result);
+ }
+ else {
+ PC_READ(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;
+ }
+ 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::SaveS(NewState *ns)
+{
+ 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);
+}
+
+void CPU::LoadS(NewState *ns)
+{
+ SSL(memory);
+ NSL(cycleCounter_);
+ NSL(PC_);
+ NSL(SP);
+ NSL(HF1);
+ NSL(HF2);
+ NSL(ZF);
+ NSL(CF);
+ NSL(A_);
+ NSL(B);
+ NSL(C);
+ NSL(D);
+ NSL(E);
+ NSL(H);
+ NSL(L);
+ NSL(skip);
+}
+
+
+}
diff --git a/libgambatte/src/cpu.h b/libgambatte/src/cpu.h
index 04bb74d12b..537b15a341 100644
--- a/libgambatte/src/cpu.h
+++ b/libgambatte/src/cpu.h
@@ -20,6 +20,7 @@
#define CPU_H
#include "memory.h"
+#include "newstate.h"
namespace gambatte {
@@ -118,6 +119,9 @@ public:
int LinkStatus(int which) { return memory.LinkStatus(which); }
void GetRegs(int *dest);
+
+ void SaveS(NewState *ns);
+ void LoadS(NewState *ns);
};
}
diff --git a/libgambatte/src/gambatte.cpp b/libgambatte/src/gambatte.cpp
index dcdf63b91d..88d91a30e1 100644
--- a/libgambatte/src/gambatte.cpp
+++ b/libgambatte/src/gambatte.cpp
@@ -245,4 +245,18 @@ void GB::GetRegs(int *dest) {
p_->cpu.GetRegs(dest);
}
+void GB::SaveS(NewState *ns)
+{
+ SSS(p_->cpu);
+ NSS(p_->gbaCgbMode);
+ NSS(p_->vbuff);
+}
+
+void GB::LoadS(NewState *ns)
+{
+ SSL(p_->cpu);
+ NSL(p_->gbaCgbMode);
+ NSL(p_->vbuff);
+}
+
}
diff --git a/libgambatte/src/interruptrequester.cpp b/libgambatte/src/interruptrequester.cpp
index 914f61781b..eb6d7d0db9 100644
--- a/libgambatte/src/interruptrequester.cpp
+++ b/libgambatte/src/interruptrequester.cpp
@@ -100,4 +100,22 @@ void InterruptRequester::setIfreg(const unsigned ifreg) {
eventTimes.setValue(pendingIrqs() ? minIntTime : static_cast(DISABLED_TIME));
}
+void InterruptRequester::SaveS(NewState *ns)
+{
+ SSS(eventTimes);
+ NSS(minIntTime);
+ NSS(ifreg_);
+ NSS(iereg_);
+ NSS(intFlags.flags_);
+}
+
+void InterruptRequester::LoadS(NewState *ns)
+{
+ SSL(eventTimes);
+ NSL(minIntTime);
+ NSL(ifreg_);
+ NSL(iereg_);
+ NSL(intFlags.flags_);
+}
+
}
diff --git a/libgambatte/src/interruptrequester.h b/libgambatte/src/interruptrequester.h
index 78c9d3fffa..b8d0b2968c 100644
--- a/libgambatte/src/interruptrequester.h
+++ b/libgambatte/src/interruptrequester.h
@@ -21,6 +21,7 @@
#include "counterdef.h"
#include "minkeeper.h"
+#include "newstate.h"
namespace gambatte {
struct SaveState;
@@ -33,6 +34,7 @@ class InterruptRequester {
unsigned iereg_;
class IntFlags {
+ friend class InterruptRequester;
unsigned char flags_;
enum { IME_MASK = 1, HALTED_MASK = 2 };
@@ -79,6 +81,9 @@ public:
template void setEventTime(unsigned long value) { eventTimes.setValue(value); }
void setEventTime(const MemEventId id, unsigned long value) { eventTimes.setValue(id, value); }
unsigned long eventTime(MemEventId id) const { return eventTimes.value(id); }
+
+ void SaveS(NewState *ns);
+ void LoadS(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 fb9d476c22..d9c9635ee3 100644
--- a/libgambatte/src/mem/cartridge.cpp
+++ b/libgambatte/src/mem/cartridge.cpp
@@ -35,6 +35,13 @@ public:
virtual bool isAddressWithinAreaRombankCanBeMappedTo(unsigned addr, unsigned bank) const {
return (addr< 0x4000) == (bank == 0);
}
+
+ virtual void SaveS(NewState *ns)
+ {
+ }
+ virtual void LoadS(NewState *ns)
+ {
+ }
};
class Mbc0 : public DefaultMbc {
@@ -63,6 +70,15 @@ public:
enableRam = ss.enableRam;
memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, 0);
}
+
+ virtual void SaveS(NewState *ns)
+ {
+ NSS(enableRam);
+ }
+ virtual void LoadS(NewState *ns)
+ {
+ NSL(enableRam);
+ }
};
static inline unsigned rambanks(const MemPtrs &memptrs) {
@@ -137,6 +153,21 @@ public:
setRambank();
setRombank();
}
+
+ virtual void SaveS(NewState *ns)
+ {
+ NSS(rombank);
+ NSS(rambank);
+ NSS(enableRam);
+ NSS(rambankMode);
+ }
+ virtual void LoadS(NewState *ns)
+ {
+ NSL(rombank);
+ NSL(rambank);
+ NSL(enableRam);
+ NSL(rambankMode);
+ }
};
class Mbc1Multi64 : public Mbc {
@@ -205,6 +236,19 @@ public:
virtual bool isAddressWithinAreaRombankCanBeMappedTo(unsigned addr, unsigned bank) const {
return (addr < 0x4000) == ((bank & 0xF) == 0);
}
+
+ virtual void SaveS(NewState *ns)
+ {
+ NSS(rombank);
+ NSS(enableRam);
+ NSS(rombank0Mode);
+ }
+ virtual void LoadS(NewState *ns)
+ {
+ NSL(rombank);
+ NSL(enableRam);
+ NSL(rombank0Mode);
+ }
};
class Mbc2 : public DefaultMbc {
@@ -244,6 +288,17 @@ public:
memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, 0);
memptrs.setRombank(rombank & (rombanks(memptrs) - 1));
}
+
+ virtual void SaveS(NewState *ns)
+ {
+ NSS(rombank);
+ NSS(enableRam);
+ }
+ virtual void LoadS(NewState *ns)
+ {
+ NSL(rombank);
+ NSL(enableRam);
+ }
};
class Mbc3 : public DefaultMbc {
@@ -315,6 +370,19 @@ public:
setRambank();
setRombank();
}
+
+ virtual void SaveS(NewState *ns)
+ {
+ NSS(rombank);
+ NSS(rambank);
+ NSS(enableRam);
+ }
+ virtual void LoadS(NewState *ns)
+ {
+ NSL(rombank);
+ NSL(rambank);
+ NSL(enableRam);
+ }
};
class HuC1 : public DefaultMbc {
@@ -378,6 +446,20 @@ public:
setRambank();
setRombank();
}
+ virtual void SaveS(NewState *ns)
+ {
+ NSS(rombank);
+ NSS(rambank);
+ NSS(enableRam);
+ NSS(rambankMode);
+ }
+ virtual void LoadS(NewState *ns)
+ {
+ NSL(rombank);
+ NSL(rambank);
+ NSL(enableRam);
+ NSL(rambankMode);
+ }
};
class Mbc5 : public DefaultMbc {
@@ -432,6 +514,19 @@ public:
setRambank();
setRombank();
}
+
+ virtual void SaveS(NewState *ns)
+ {
+ NSS(rombank);
+ NSS(rambank);
+ NSS(enableRam);
+ }
+ virtual void LoadS(NewState *ns)
+ {
+ NSL(rombank);
+ NSL(rambank);
+ NSL(enableRam);
+ }
};
static bool hasRtc(const unsigned headerByte0x147) {
@@ -692,4 +787,18 @@ bool Cartridge::getMemoryArea(int which, unsigned char **data, int *length) {
return false;
}
+void Cartridge::SaveS(NewState *ns)
+{
+ SSS(memptrs);
+ SSS(rtc);
+ TSS(mbc);
+}
+
+void Cartridge::LoadS(NewState *ns)
+{
+ SSL(memptrs);
+ SSL(rtc);
+ TSL(mbc);
+}
+
}
diff --git a/libgambatte/src/mem/cartridge.h b/libgambatte/src/mem/cartridge.h
index 0de7e01d45..2be9646e45 100644
--- a/libgambatte/src/mem/cartridge.h
+++ b/libgambatte/src/mem/cartridge.h
@@ -25,6 +25,7 @@
#include
#include
#include
+#include "newstate.h"
namespace gambatte {
@@ -35,6 +36,9 @@ public:
virtual void saveState(SaveState::Mem &ss) const = 0;
virtual void loadState(const SaveState::Mem &ss) = 0;
virtual bool isAddressWithinAreaRombankCanBeMappedTo(unsigned address, unsigned rombank) const = 0;
+
+ virtual void SaveS(NewState *ns) = 0;
+ virtual void LoadS(NewState *ns) = 0;
};
class Cartridge {
@@ -83,6 +87,9 @@ public:
void setRTCCallback(std::uint32_t (*callback)()) {
rtc.setRTCCallback(callback);
}
+
+ void SaveS(NewState *ns);
+ void LoadS(NewState *ns);
};
}
diff --git a/libgambatte/src/mem/memptrs.cpp b/libgambatte/src/mem/memptrs.cpp
index c43229c271..b757456541 100644
--- a/libgambatte/src/mem/memptrs.cpp
+++ b/libgambatte/src/mem/memptrs.cpp
@@ -24,7 +24,8 @@ 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)
+ wsrambankptr_(0), memchunk_(0), rambankdata_(0), wramdataend_(0), oamDmaSrc_(OAM_DMA_SRC_OFF),
+ memchunk_len(0)
{
}
@@ -34,7 +35,8 @@ MemPtrs::~MemPtrs() {
void MemPtrs::reset(const unsigned rombanks, const unsigned rambanks, const unsigned wrambanks) {
delete []memchunk_;
- memchunk_ = new unsigned char[0x4000 + rombanks * 0x4000ul + 0x4000 + rambanks * 0x2000ul + wrambanks * 0x1000ul + 0x4000];
+ 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;
@@ -136,4 +138,117 @@ void MemPtrs::disconnectOamDmaAreas() {
}
}
+// all pointers here are relative to memchunk_
+#define MSS(a) RSS(a,memchunk_)
+#define MSL(a) RSL(a,memchunk_)
+
+void MemPtrs::SaveS(NewState *ns)
+{
+ 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_);
+ NSS(memchunk_len);
+ PSS(memchunk_, memchunk_len);
+ MSS(rambankdata_);
+ MSS(wramdataend_);
+ NSS(oamDmaSrc_);
+}
+
+void MemPtrs::LoadS(NewState *ns)
+{
+ MSL(rmem_[0x0]);
+ MSL(wmem_[0x0]);
+ MSL(rmem_[0x1]);
+ MSL(wmem_[0x1]);
+ MSL(rmem_[0x2]);
+ MSL(wmem_[0x2]);
+ MSL(rmem_[0x3]);
+ MSL(wmem_[0x3]);
+ MSL(rmem_[0x4]);
+ MSL(wmem_[0x4]);
+ MSL(rmem_[0x5]);
+ MSL(wmem_[0x5]);
+ MSL(rmem_[0x6]);
+ MSL(wmem_[0x6]);
+ MSL(rmem_[0x7]);
+ MSL(wmem_[0x7]);
+ MSL(rmem_[0x8]);
+ MSL(wmem_[0x8]);
+ MSL(rmem_[0x9]);
+ MSL(wmem_[0x9]);
+ MSL(rmem_[0xa]);
+ MSL(wmem_[0xa]);
+ MSL(rmem_[0xb]);
+ MSL(wmem_[0xb]);
+ MSL(rmem_[0xc]);
+ MSL(wmem_[0xc]);
+ MSL(rmem_[0xd]);
+ MSL(wmem_[0xd]);
+ MSL(rmem_[0xe]);
+ MSL(wmem_[0xe]);
+ MSL(rmem_[0xf]);
+ MSL(wmem_[0xf]);
+ //for (int i = 0; i < 0x10; i++)
+ //{
+ // MSL(rmem_[i]);
+ // MSL(wmem_[i]);
+ //}
+ MSL(romdata_[0]);
+ MSL(romdata_[1]);
+ MSL(wramdata_[0]);
+ MSL(wramdata_[1]);
+ MSL(vrambankptr_);
+ MSL(rsrambankptr_);
+ MSL(wsrambankptr_);
+ unsigned tmp = memchunk_len;
+ NSL(memchunk_len);
+ if (tmp != memchunk_len) // how graceful could we be here? not sure
+ __debugbreak();
+ PSL(memchunk_, memchunk_len);
+ MSL(rambankdata_);
+ MSL(wramdataend_);
+ NSL(oamDmaSrc_);
+}
+
}
diff --git a/libgambatte/src/mem/memptrs.h b/libgambatte/src/mem/memptrs.h
index e7c7d10118..edf579959d 100644
--- a/libgambatte/src/mem/memptrs.h
+++ b/libgambatte/src/mem/memptrs.h
@@ -19,6 +19,8 @@
#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,
@@ -38,7 +40,9 @@ class MemPtrs {
unsigned char *wramdataend_;
OamDmaSrc oamDmaSrc_;
-
+
+ unsigned memchunk_len;
+
MemPtrs(const MemPtrs &);
MemPtrs & operator=(const MemPtrs &);
void disconnectOamDmaAreas();
@@ -74,6 +78,9 @@ public:
void setVrambank(unsigned bank) { vrambankptr_ = vramdata() + bank * 0x2000ul - 0x8000; }
void setWrambank(unsigned bank);
void setOamDmaSrc(OamDmaSrc oamDmaSrc);
+
+ void SaveS(NewState *ns);
+ void LoadS(NewState *ns);
};
inline bool isCgb(const MemPtrs &memptrs) {
diff --git a/libgambatte/src/mem/rtc.cpp b/libgambatte/src/mem/rtc.cpp
index 3b33812645..df42338ddb 100644
--- a/libgambatte/src/mem/rtc.cpp
+++ b/libgambatte/src/mem/rtc.cpp
@@ -1,158 +1,218 @@
-/***************************************************************************
- * 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::saveState(SaveState &state) const {
- state.rtc.baseTime = baseTime;
- state.rtc.haltTime = haltTime;
- state.rtc.dataDh = dataDh;
- state.rtc.dataDl = dataDl;
- state.rtc.dataH = dataH;
- state.rtc.dataM = dataM;
- state.rtc.dataS = dataS;
- state.rtc.lastLatchData = lastLatchData;
-}
-
-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;
-}
-
-}
+/***************************************************************************
+ * 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::saveState(SaveState &state) const {
+ state.rtc.baseTime = baseTime;
+ state.rtc.haltTime = haltTime;
+ state.rtc.dataDh = dataDh;
+ state.rtc.dataDl = dataDl;
+ state.rtc.dataH = dataH;
+ state.rtc.dataM = dataM;
+ state.rtc.dataS = dataS;
+ state.rtc.lastLatchData = lastLatchData;
+}
+
+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;
+}
+
+void Rtc::SaveS(NewState *ns)
+{
+ 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);
+}
+
+void Rtc::LoadS(NewState *ns)
+{
+ EBL(activeData, 0);
+ EVL(activeData, &dataS, 1);
+ EVL(activeData, &dataM, 2);
+ EVL(activeData, &dataH, 3);
+ EVL(activeData, &dataDl, 4);
+ EVL(activeData, &dataDh, 5);
+ EEL(activeData, NULL);
+
+ EBL(activeSet, 0);
+ EVL(activeSet, &Rtc::setS, 1);
+ EVL(activeSet, &Rtc::setM, 2);
+ EVL(activeSet, &Rtc::setH, 3);
+ EVL(activeSet, &Rtc::setDl, 4);
+ EVL(activeSet, &Rtc::setDh, 5);
+ EEL(activeSet, NULL);
+
+ NSL(baseTime);
+ NSL(haltTime);
+ NSL(index);
+ NSL(dataDh);
+ NSL(dataDl);
+ NSL(dataH);
+ NSL(dataM);
+ NSL(dataS);
+ NSL(enabled);
+ NSL(lastLatchData);
+}
+
+}
diff --git a/libgambatte/src/mem/rtc.h b/libgambatte/src/mem/rtc.h
index 1dfab845fd..4eb93a6251 100644
--- a/libgambatte/src/mem/rtc.h
+++ b/libgambatte/src/mem/rtc.h
@@ -1,96 +1,100 @@
-/***************************************************************************
- * 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
-
-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 saveState(SaveState &state) const;
- 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;
- }
-};
-
-}
-
-#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 saveState(SaveState &state) const;
+ 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;
+ }
+
+ void SaveS(NewState *ns);
+ void LoadS(NewState *ns);
+};
+
+}
+
+#endif
diff --git a/libgambatte/src/memory.cpp b/libgambatte/src/memory.cpp
index f7680c7106..4aefc7c4d2 100644
--- a/libgambatte/src/memory.cpp
+++ b/libgambatte/src/memory.cpp
@@ -1104,4 +1104,53 @@ int Memory::LinkStatus(int which)
return -1;
}
+void Memory::SaveS(NewState *ns)
+{
+ 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);
+}
+
+void Memory::LoadS(NewState *ns)
+{
+ SSL(cart);
+ NSL(ioamhram);
+ NSL(divLastUpdate);
+ NSL(lastOamDmaUpdate);
+
+ SSL(intreq);
+ SSL(tima);
+ SSL(display);
+ SSL(sound);
+ //SSL(interrupter); // no state
+
+ NSL(dmaSource);
+ NSL(dmaDestination);
+ NSL(oamDmaPos);
+ NSL(serialCnt);
+ NSL(blanklcd);
+
+ NSL(LINKCABLE);
+ NSL(linkClockTrigger);
+}
+
+
+
+
}
diff --git a/libgambatte/src/memory.h b/libgambatte/src/memory.h
index 48f0389133..f76f1b16b2 100644
--- a/libgambatte/src/memory.h
+++ b/libgambatte/src/memory.h
@@ -24,6 +24,7 @@
#include "sound.h"
#include "interrupter.h"
#include "tima.h"
+#include "newstate.h"
namespace gambatte {
class InputGetter;
@@ -194,6 +195,9 @@ public:
void setCgbPalette(unsigned *lut);
int LinkStatus(int which);
+
+ void SaveS(NewState *ns);
+ void LoadS(NewState *ns);
};
}
diff --git a/libgambatte/src/minkeeper.h b/libgambatte/src/minkeeper.h
index e05572e57e..54617d5d1c 100644
--- a/libgambatte/src/minkeeper.h
+++ b/libgambatte/src/minkeeper.h
@@ -20,6 +20,7 @@
#define MINKEEPER_H
#include
+#include "newstate.h"
namespace MinKeeperUtil {
template struct CeiledLog2 { enum { R = 1 + CeiledLog2<(n + 1) / 2>::R }; };
@@ -105,6 +106,20 @@ 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?
+ void SaveS(gambatte::NewState *ns)
+ {
+ NSS(values);
+ NSS(minValue_);
+ NSS(a);
+ }
+ void LoadS(gambatte::NewState *ns)
+ {
+ NSL(values);
+ NSL(minValue_);
+ NSL(a);
+ }
};
template typename MinKeeper::UpdateValueLut MinKeeper::updateValueLut;
diff --git a/libgambatte/src/newstate.cpp b/libgambatte/src/newstate.cpp
new file mode 100644
index 0000000000..dcfb5af1ff
--- /dev/null
+++ b/libgambatte/src/newstate.cpp
@@ -0,0 +1,74 @@
+#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), maxlength(maxlength), length(0)
+{
+}
+
+void NewStateExternalBuffer::Save(const void *ptr, size_t size, const char *name)
+{
+ const char *src = static_cast(ptr);
+ if (maxlength - length >= 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 >= size)
+ {
+ std::memcpy(dst, buffer + length, size);
+ }
+ length += size;
+}
+
+NewStateExternalFunctions::NewStateExternalFunctions(
+ 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))
+ :Save_(Save_),
+ Load_(Load_),
+ EnterSection_(EnterSection_),
+ ExitSection_(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
new file mode 100644
index 0000000000..0a65a1a1f2
--- /dev/null
+++ b/libgambatte/src/newstate.h
@@ -0,0 +1,100 @@
+#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);
+};
+
+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(
+ 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));
+ 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);
+};
+
+
+// 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 (0)
+#define EVS(x,v,n) else if ((x) == (v)) _ttmp = (n)
+#define EES(x,d) ns->Save(&_ttmp, sizeof(_ttmp), #x); } while (0)
+
+#define EBL(x,d) do { int _ttmp = (d); ns->Load(&_ttmp, sizeof(_ttmp), #x); if (0)
+#define EVL(x,v,n) else if (_ttmp == (n)) (x) = (v)
+#define EEL(x,d) else (x) = (d); } while (0)
+
+
+#define RSS(x,b) do { ptrdiff_t _ttmp = (x) == 0 ? 0xdeadbeef : (x) - (b); ns->Save(&_ttmp, sizeof(_ttmp), #x); } while (0)
+#define RSL(x,b) do { ptrdiff_t _ttmp; ns->Load(&_ttmp, sizeof(_ttmp), #x); (x) = (_ttmp == 0xdeadbeef ? 0 : (b) + _ttmp); } while (0)
+
+#define PSS(x,s) ns->Save((x), (s), #x)
+#define PSL(x,s) ns->Load((x), (s), #x)
+
+#define NSS(x) ns->Save(&(x), sizeof(x), #x)
+#define NSL(x) ns->Load(&(x), sizeof(x), #x)
+
+#define SSS(x) do { ns->EnterSection(#x); (x).SaveS(ns); ns->ExitSection(#x); } while (0)
+#define SSL(x) do { ns->EnterSection(#x); (x).LoadS(ns); ns->ExitSection(#x); } while (0)
+
+#define TSS(x) do { ns->EnterSection(#x); (x)->SaveS(ns); ns->ExitSection(#x); } while (0)
+#define TSL(x) do { ns->EnterSection(#x); (x)->LoadS(ns); ns->ExitSection(#x); } while (0)
+
+}
+
+#endif
diff --git a/libgambatte/src/sound.cpp b/libgambatte/src/sound.cpp
index 14d994e354..b0e307afd4 100644
--- a/libgambatte/src/sound.cpp
+++ b/libgambatte/src/sound.cpp
@@ -179,4 +179,29 @@ unsigned PSG::getStatus() const {
return ch1.isActive() | ch2.isActive() << 1 | ch3.isActive() << 2 | ch4.isActive() << 3;
}
+// the buffer and position are not saved, as they're set and flushed on each runfor() call
+void PSG::SaveS(NewState *ns)
+{
+ SSS(ch1);
+ SSS(ch2);
+ SSS(ch3);
+ SSS(ch4);
+ NSS(lastUpdate);
+ NSS(soVol);
+ NSS(rsum);
+ NSS(enabled);
+}
+
+void PSG::LoadS(NewState *ns)
+{
+ SSL(ch1);
+ SSL(ch2);
+ SSL(ch3);
+ SSL(ch4);
+ NSL(lastUpdate);
+ NSL(soVol);
+ NSL(rsum);
+ NSL(enabled);
+}
+
}
diff --git a/libgambatte/src/sound.h b/libgambatte/src/sound.h
index 71d5cdc2e3..b2b1f81cca 100644
--- a/libgambatte/src/sound.h
+++ b/libgambatte/src/sound.h
@@ -23,6 +23,7 @@
#include "sound/channel2.h"
#include "sound/channel3.h"
#include "sound/channel4.h"
+#include "newstate.h"
namespace gambatte {
@@ -88,6 +89,9 @@ public:
void set_so_volume(unsigned nr50);
void map_so(unsigned nr51);
unsigned getStatus() const;
+
+ void SaveS(NewState *ns);
+ void LoadS(NewState *ns);
};
}
diff --git a/libgambatte/src/sound/channel1.cpp b/libgambatte/src/sound/channel1.cpp
index 82e623a697..84aa93c445 100644
--- a/libgambatte/src/sound/channel1.cpp
+++ b/libgambatte/src/sound/channel1.cpp
@@ -104,6 +104,22 @@ void Channel1::SweepUnit::loadState(const SaveState &state) {
negging = state.spu.ch1.sweep.negging;
}
+void Channel1::SweepUnit::SaveS(NewState *ns)
+{
+ NSS(counter);
+ NSS(shadow);
+ NSS(nr0);
+ NSS(negging);
+}
+
+void Channel1::SweepUnit::LoadS(NewState *ns)
+{
+ NSL(counter);
+ NSL(shadow);
+ NSL(nr0);
+ NSL(negging);
+}
+
Channel1::Channel1() :
staticOutputTest(*this, dutyUnit),
disableMaster(master, dutyUnit),
@@ -259,4 +275,48 @@ void Channel1::update(uint_least32_t *buf, const unsigned long soBaseVol, unsign
}
}
+void Channel1::SaveS(NewState *ns)
+{
+ 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);
+}
+
+void Channel1::LoadS(NewState *ns)
+{
+ SSL(lengthCounter);
+ SSL(dutyUnit);
+ SSL(envelopeUnit);
+ SSL(sweepUnit);
+
+ EBL(nextEventUnit, 0);
+ EVL(nextEventUnit, &dutyUnit, 1);
+ EVL(nextEventUnit, &sweepUnit, 2);
+ EVL(nextEventUnit, &envelopeUnit, 3);
+ EVL(nextEventUnit, &lengthCounter, 4);
+ EEL(nextEventUnit, NULL);
+
+ NSL(cycleCounter);
+ NSL(soMask);
+ NSL(prevOut);
+
+ NSL(nr4);
+ NSL(master);
+}
+
}
diff --git a/libgambatte/src/sound/channel1.h b/libgambatte/src/sound/channel1.h
index 9c7727791d..086d36f124 100644
--- a/libgambatte/src/sound/channel1.h
+++ b/libgambatte/src/sound/channel1.h
@@ -25,6 +25,7 @@
#include "duty_unit.h"
#include "envelope_unit.h"
#include "static_output_tester.h"
+#include "newstate.h"
namespace gambatte {
@@ -48,6 +49,9 @@ class Channel1 {
void reset();
void saveState(SaveState &state) const;
void loadState(const SaveState &state);
+
+ void SaveS(NewState *ns);
+ void LoadS(NewState *ns);
};
friend class StaticOutputTester;
@@ -87,6 +91,9 @@ public:
void init(bool cgb);
void saveState(SaveState &state);
void loadState(const SaveState &state);
+
+ void SaveS(NewState *ns);
+ void LoadS(NewState *ns);
};
}
diff --git a/libgambatte/src/sound/channel2.cpp b/libgambatte/src/sound/channel2.cpp
index 91e15eeca6..34e074a4b0 100644
--- a/libgambatte/src/sound/channel2.cpp
+++ b/libgambatte/src/sound/channel2.cpp
@@ -162,4 +162,45 @@ void Channel2::update(uint_least32_t *buf, const unsigned long soBaseVol, unsign
}
}
+void Channel2::SaveS(NewState *ns)
+{
+ 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);
+}
+
+void Channel2::LoadS(NewState *ns)
+{
+ SSL(lengthCounter);
+ SSL(dutyUnit);
+ SSL(envelopeUnit);
+
+ EBL(nextEventUnit, 0);
+ EVL(nextEventUnit, &dutyUnit, 1);
+ EVL(nextEventUnit, &envelopeUnit, 2);
+ EVL(nextEventUnit, &lengthCounter, 3);
+ EEL(nextEventUnit, NULL);
+
+ NSL(cycleCounter);
+ NSL(soMask);
+ NSL(prevOut);
+
+ NSL(nr4);
+ NSL(master);
+}
+
+
}
diff --git a/libgambatte/src/sound/channel2.h b/libgambatte/src/sound/channel2.h
index d6ba821ed9..5e8bed630b 100644
--- a/libgambatte/src/sound/channel2.h
+++ b/libgambatte/src/sound/channel2.h
@@ -24,6 +24,7 @@
#include "duty_unit.h"
#include "envelope_unit.h"
#include "static_output_tester.h"
+#include "newstate.h"
namespace gambatte {
@@ -66,6 +67,9 @@ public:
void init(bool cgb);
void saveState(SaveState &state);
void loadState(const SaveState &state);
+
+ void SaveS(NewState *ns);
+ void LoadS(NewState *ns);
};
}
diff --git a/libgambatte/src/sound/channel3.cpp b/libgambatte/src/sound/channel3.cpp
index a1bde940da..d7fb3f760d 100644
--- a/libgambatte/src/sound/channel3.cpp
+++ b/libgambatte/src/sound/channel3.cpp
@@ -208,4 +208,50 @@ void Channel3::update(uint_least32_t *buf, const unsigned long soBaseVol, unsign
}
}
+void Channel3::SaveS(NewState *ns)
+{
+ 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);
+}
+
+void Channel3::LoadS(NewState *ns)
+{
+ NSL(waveRam);
+
+ SSL(lengthCounter);
+
+ NSL(cycleCounter);
+ NSL(soMask);
+ NSL(prevOut);
+ NSL(waveCounter);
+ NSL(lastReadTime);
+
+ NSL(nr0);
+ NSL(nr3);
+ NSL(nr4);
+ NSL(wavePos);
+ NSL(rShift);
+ NSL(sampleBuf);
+
+ NSL(master);
+ NSL(cgb);
+}
+
}
diff --git a/libgambatte/src/sound/channel3.h b/libgambatte/src/sound/channel3.h
index e80ec68549..1d109a3ab8 100644
--- a/libgambatte/src/sound/channel3.h
+++ b/libgambatte/src/sound/channel3.h
@@ -22,6 +22,7 @@
#include "gbint.h"
#include "master_disabler.h"
#include "length_counter.h"
+#include "newstate.h"
namespace gambatte {
@@ -96,6 +97,9 @@ public:
waveRam[index] = data;
}
+
+ void SaveS(NewState *ns);
+ void LoadS(NewState *ns);
};
}
diff --git a/libgambatte/src/sound/channel4.cpp b/libgambatte/src/sound/channel4.cpp
index 898ee737f6..c924ea9595 100644
--- a/libgambatte/src/sound/channel4.cpp
+++ b/libgambatte/src/sound/channel4.cpp
@@ -163,6 +163,24 @@ void Channel4::Lfsr::loadState(const SaveState &state) {
nr3 = state.mem.ioamhram.get()[0x122];
}
+void Channel4::Lfsr::SaveS(NewState *ns)
+{
+ NSS(counter);
+ NSS(backupCounter);
+ NSS(reg);
+ NSS(nr3);
+ NSS(master);
+}
+
+void Channel4::Lfsr::LoadS(NewState *ns)
+{
+ NSL(counter);
+ NSL(backupCounter);
+ NSL(reg);
+ NSL(nr3);
+ NSL(master);
+}
+
Channel4::Channel4() :
staticOutputTest(*this, lfsr),
disableMaster(master, lfsr),
@@ -300,5 +318,45 @@ void Channel4::update(uint_least32_t *buf, const unsigned long soBaseVol, unsign
cycleCounter -= SoundUnit::COUNTER_MAX;
}
}
+
+void Channel4::SaveS(NewState *ns)
+{
+ 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);
+}
+
+void Channel4::LoadS(NewState *ns)
+{
+ SSL(lengthCounter);
+ SSL(envelopeUnit);
+ SSL(lfsr);
+
+ EBL(nextEventUnit, 0);
+ EVL(nextEventUnit, &lfsr, 1);
+ EVL(nextEventUnit, &envelopeUnit, 2);
+ EVL(nextEventUnit, &lengthCounter, 3);
+ EEL(nextEventUnit, NULL);
+
+ NSL(cycleCounter);
+ NSL(soMask);
+ NSL(prevOut);
+
+ NSL(nr4);
+ NSL(master);
+}
}
diff --git a/libgambatte/src/sound/channel4.h b/libgambatte/src/sound/channel4.h
index b4f2a4d088..85c3d05f22 100644
--- a/libgambatte/src/sound/channel4.h
+++ b/libgambatte/src/sound/channel4.h
@@ -24,6 +24,7 @@
#include "length_counter.h"
#include "envelope_unit.h"
#include "static_output_tester.h"
+#include "newstate.h"
namespace gambatte {
@@ -51,6 +52,9 @@ class Channel4 {
void disableMaster() { killCounter(); master = false; reg = 0x7FFF; }
void killCounter() { counter = COUNTER_DISABLED; }
void reviveCounter(unsigned long cc);
+
+ void SaveS(NewState *ns);
+ void LoadS(NewState *ns);
};
class Ch4MasterDisabler : public MasterDisabler {
@@ -95,6 +99,9 @@ public:
void init(bool cgb);
void saveState(SaveState &state);
void loadState(const SaveState &state);
+
+ void SaveS(NewState *ns);
+ void LoadS(NewState *ns);
};
}
diff --git a/libgambatte/src/sound/duty_unit.cpp b/libgambatte/src/sound/duty_unit.cpp
index 23b8e528f9..d737f3bff0 100644
--- a/libgambatte/src/sound/duty_unit.cpp
+++ b/libgambatte/src/sound/duty_unit.cpp
@@ -149,4 +149,27 @@ void DutyUnit::reviveCounter(const unsigned long cc) {
setCounter();
}
+void DutyUnit::SaveS(NewState *ns)
+{
+ NSS(counter);
+ NSS(nextPosUpdate);
+ NSS(period);
+ NSS(pos);
+ NSS(duty);
+ NSS(high);
+ NSS(enableEvents);
+}
+
+void DutyUnit::LoadS(NewState *ns)
+{
+ NSL(counter);
+ NSL(nextPosUpdate);
+ NSL(period);
+ NSL(pos);
+ NSL(duty);
+ NSL(high);
+ NSL(enableEvents);
+}
+
+
}
diff --git a/libgambatte/src/sound/duty_unit.h b/libgambatte/src/sound/duty_unit.h
index f0fc49cd11..5006ccdbde 100644
--- a/libgambatte/src/sound/duty_unit.h
+++ b/libgambatte/src/sound/duty_unit.h
@@ -22,6 +22,7 @@
#include "sound_unit.h"
#include "master_disabler.h"
#include "../savestate.h"
+#include "newstate.h"
namespace gambatte {
@@ -54,6 +55,9 @@ public:
//intended for use by SweepUnit only.
unsigned getFreq() const { return 2048 - (period >> 1); }
void setFreq(unsigned newFreq, unsigned long cc);
+
+ void SaveS(NewState *ns);
+ void LoadS(NewState *ns);
};
class DutyMasterDisabler : public MasterDisabler {
diff --git a/libgambatte/src/sound/envelope_unit.cpp b/libgambatte/src/sound/envelope_unit.cpp
index 7bcb783475..9812b2cf76 100644
--- a/libgambatte/src/sound/envelope_unit.cpp
+++ b/libgambatte/src/sound/envelope_unit.cpp
@@ -103,4 +103,18 @@ void EnvelopeUnit::loadState(const SaveState::SPU::Env &estate, const unsigned n
this->nr2 = nr2;
}
+void EnvelopeUnit::SaveS(NewState *ns)
+{
+ NSS(counter);
+ NSS(nr2);
+ NSS(volume);
+}
+
+void EnvelopeUnit::LoadS(NewState *ns)
+{
+ NSL(counter);
+ NSL(nr2);
+ NSL(volume);
+}
+
}
diff --git a/libgambatte/src/sound/envelope_unit.h b/libgambatte/src/sound/envelope_unit.h
index fc2551c4c5..46c360f7a5 100644
--- a/libgambatte/src/sound/envelope_unit.h
+++ b/libgambatte/src/sound/envelope_unit.h
@@ -21,6 +21,7 @@
#include "sound_unit.h"
#include "../savestate.h"
+#include "newstate.h"
namespace gambatte {
@@ -47,6 +48,9 @@ public:
void reset();
void saveState(SaveState::SPU::Env &estate) const;
void loadState(const SaveState::SPU::Env &estate, unsigned nr2, unsigned long cc);
+
+ void SaveS(NewState *ns);
+ void LoadS(NewState *ns);
};
}
diff --git a/libgambatte/src/sound/length_counter.cpp b/libgambatte/src/sound/length_counter.cpp
index d295dc0d3e..c9cc87c508 100644
--- a/libgambatte/src/sound/length_counter.cpp
+++ b/libgambatte/src/sound/length_counter.cpp
@@ -88,4 +88,18 @@ void LengthCounter::loadState(const SaveState::SPU::LCounter &lstate, const unsi
lengthCounter = lstate.lengthCounter;
}
+void LengthCounter::SaveS(NewState *ns)
+{
+ NSS(counter);
+ NSS(lengthCounter);
+ NSS(cgb);
+}
+
+void LengthCounter::LoadS(NewState *ns)
+{
+ NSL(counter);
+ NSL(lengthCounter);
+ NSL(cgb);
+}
+
}
diff --git a/libgambatte/src/sound/length_counter.h b/libgambatte/src/sound/length_counter.h
index 3360ab17ff..ebfd9f47bb 100644
--- a/libgambatte/src/sound/length_counter.h
+++ b/libgambatte/src/sound/length_counter.h
@@ -21,6 +21,7 @@
#include "sound_unit.h"
#include "../savestate.h"
+#include "newstate.h"
namespace gambatte {
@@ -41,6 +42,9 @@ public:
void init(bool cgb);
void saveState(SaveState::SPU::LCounter &lstate) const;
void loadState(const SaveState::SPU::LCounter &lstate, unsigned long cc);
+
+ void SaveS(NewState *ns);
+ void LoadS(NewState *ns);
};
}
diff --git a/libgambatte/src/tima.cpp b/libgambatte/src/tima.cpp
index 44ce31cf30..1a98764e43 100644
--- a/libgambatte/src/tima.cpp
+++ b/libgambatte/src/tima.cpp
@@ -168,4 +168,22 @@ void Tima::doIrqEvent(const TimaInterruptRequester timaIrq) {
timaIrq.setNextIrqEventTime(timaIrq.nextIrqEventTime() + ((256u - tma_) << timaClock[tac_ & 3]));
}
+void Tima::SaveS(NewState *ns)
+{
+ NSS(lastUpdate_);
+ NSS(tmatime_);
+ NSS(tima_);
+ NSS(tma_);
+ NSS(tac_);
+}
+
+void Tima::LoadS(NewState *ns)
+{
+ NSL(lastUpdate_);
+ NSL(tmatime_);
+ NSL(tima_);
+ NSL(tma_);
+ NSL(tac_);
+}
+
}
diff --git a/libgambatte/src/tima.h b/libgambatte/src/tima.h
index 48e9ccd4cb..e617eb95ae 100644
--- a/libgambatte/src/tima.h
+++ b/libgambatte/src/tima.h
@@ -60,6 +60,9 @@ public:
unsigned tima(unsigned long cc);
void doIrqEvent(TimaInterruptRequester timaIrq);
+
+ void SaveS(NewState *ns);
+ void LoadS(NewState *ns);
};
}
diff --git a/libgambatte/src/video.cpp b/libgambatte/src/video.cpp
index b01ab8263c..912727c985 100644
--- a/libgambatte/src/video.cpp
+++ b/libgambatte/src/video.cpp
@@ -37,43 +37,9 @@ void LCD::setCgbPalette(unsigned *lut) {
}
unsigned long LCD::gbcToRgb32(const unsigned bgr15) {
- /*
- const unsigned long r = bgr15 & 0x1F;
- const unsigned long g = bgr15 >> 5 & 0x1F;
- const unsigned long b = bgr15 >> 10 & 0x1F;
-
- return ((r * 13 + g * 2 + b) >> 1) << 16 | (g * 3 + b) << 9 | (r * 3 + g * 2 + b * 11) >> 1;
- */
return cgbColorsRgb32[bgr15 & 0x7FFF];
}
-/*static unsigned long gbcToRgb16(const unsigned bgr15) {
- const unsigned r = bgr15 & 0x1F;
- const unsigned g = bgr15 >> 5 & 0x1F;
- const unsigned b = bgr15 >> 10 & 0x1F;
-
- return (((r * 13 + g * 2 + b + 8) << 7) & 0xF800) | ((g * 3 + b + 1) >> 1) << 5 | ((r * 3 + g * 2 + b * 11 + 8) >> 4);
-}
-
-static unsigned long gbcToUyvy(const unsigned bgr15) {
- const unsigned r5 = bgr15 & 0x1F;
- const unsigned g5 = bgr15 >> 5 & 0x1F;
- const unsigned b5 = bgr15 >> 10 & 0x1F;
-
- // y = (r5 * 926151 + g5 * 1723530 + b5 * 854319) / 510000 + 16;
- // u = (b5 * 397544 - r5 * 68824 - g5 * 328720) / 225930 + 128;
- // v = (r5 * 491176 - g5 * 328720 - b5 * 162456) / 178755 + 128;
-
- const unsigned long y = (r5 * 116 + g5 * 216 + b5 * 107 + 16 * 64 + 32) >> 6;
- const unsigned long u = (b5 * 225 - r5 * 39 - g5 * 186 + 128 * 128 + 64) >> 7;
- const unsigned long v = (r5 * 176 - g5 * 118 - b5 * 58 + 128 * 64 + 32) >> 6;
-
-#ifdef WORDS_BIGENDIAN
- return u << 24 | y << 16 | v << 8 | y;
-#else
- return y << 24 | v << 16 | y << 8 | u;
-#endif
-}*/
LCD::LCD(const unsigned char *const oamram, const unsigned char *const vram, const VideoInterruptRequester memEventRequester) :
ppu(nextM0Time_, oamram, vram),
@@ -196,32 +162,6 @@ void LCD::refreshPalettes() {
namespace {
-template
-static void blitOsdElement(uint_least32_t *d,
- const uint_least32_t *s, const unsigned width, unsigned h, const int dpitch, Blend blend)
-{
- while (h--) {
- for (unsigned w = width; w--;) {
- if (*s != 0xFFFFFFFF)
- *d = blend(*s, *d);
-
- ++d;
- ++s;
- }
-
- d += dpitch - static_cast(width);
- }
-}
-
-template
-struct Blend {
- enum { SW = weight - 1 };
- enum { LOWMASK = SW * 0x010101ul };
- uint_least32_t operator()(const uint_least32_t s, const uint_least32_t d) const {
- return (s * SW + d - (((s & LOWMASK) * SW + (d & LOWMASK)) & LOWMASK)) / weight;
- }
-};
-
template
static void clear(T *buf, const unsigned long color, const int dpitch) {
unsigned lines = 144;
@@ -802,4 +742,36 @@ void LCD::setDmgPaletteColor(const unsigned palNum, const unsigned colorNum, con
refreshPalettes();
}
+// don't need to save or load rgb32 color data
+
+void LCD::SaveS(NewState *ns)
+{
+ SSS(ppu);
+ NSS(bgpData);
+ NSS(objpData);
+ SSS(eventTimes_);
+ SSS(m0Irq_);
+ SSS(lycIrq);
+ SSS(nextM0Time_);
+
+ NSS(statReg);
+ NSS(m2IrqStatReg_);
+ NSS(m1IrqStatReg_);
+}
+
+void LCD::LoadS(NewState *ns)
+{
+ SSL(ppu);
+ NSL(bgpData);
+ NSL(objpData);
+ SSL(eventTimes_);
+ SSL(m0Irq_);
+ SSL(lycIrq);
+ SSL(nextM0Time_);
+
+ NSL(statReg);
+ NSL(m2IrqStatReg_);
+ NSL(m1IrqStatReg_);
+}
+
}
diff --git a/libgambatte/src/video.h b/libgambatte/src/video.h
index 526812b99e..9f6ea0c7c1 100644
--- a/libgambatte/src/video.h
+++ b/libgambatte/src/video.h
@@ -25,6 +25,7 @@
#include "interruptrequester.h"
#include "minkeeper.h"
#include
+#include "newstate.h"
namespace gambatte {
@@ -80,6 +81,17 @@ public:
}
unsigned statReg() const { return statReg_; }
+
+ void SaveS(NewState *ns)
+ {
+ NSS(statReg_);
+ NSS(lycReg_);
+ }
+ void LoadS(NewState *ns)
+ {
+ NSL(statReg_);
+ NSL(lycReg_);
+ }
};
class LCD {
@@ -115,6 +127,19 @@ class LCD {
void flagIrq(const unsigned bit) { memEventRequester_.flagIrq(bit); }
void flagHdmaReq() { memEventRequester_.flagHdmaReq(); }
+
+ void SaveS(NewState *ns)
+ {
+ SSS(eventMin_);
+ SSS(memEventMin_);
+ //SSS(memEventRequester_); // not needed
+ }
+ void LoadS(NewState *ns)
+ {
+ SSL(eventMin_);
+ SSL(memEventMin_);
+ //SSL(memEventRequester_); // not needed
+ }
};
PPU ppu;
@@ -258,6 +283,9 @@ public:
unsigned long *spPalette() { return ppu.spPalette(); }
void setScanlineCallback(void (*callback)(), int sl) { scanlinecallback = callback; scanlinecallbacksl = sl; }
+
+ void SaveS(NewState *ns);
+ void LoadS(NewState *ns);
};
}
diff --git a/libgambatte/src/video/ly_counter.cpp b/libgambatte/src/video/ly_counter.cpp
index 5c8828de50..c6cf7ac118 100644
--- a/libgambatte/src/video/ly_counter.cpp
+++ b/libgambatte/src/video/ly_counter.cpp
@@ -64,5 +64,21 @@ void LyCounter::setDoubleSpeed(const bool ds_in) {
ds = ds_in;
lineTime_ = 456U << ds_in;
}
+
+void LyCounter::SaveS(NewState *ns)
+{
+ NSS(time_);
+ NSS(lineTime_);
+ NSS(ly_);
+ NSS(ds);
+}
+
+void LyCounter::LoadS(NewState *ns)
+{
+ NSL(time_);
+ NSL(lineTime_);
+ NSL(ly_);
+ NSL(ds);
+}
}
diff --git a/libgambatte/src/video/ly_counter.h b/libgambatte/src/video/ly_counter.h
index 49ff015ac3..ff60d710f9 100644
--- a/libgambatte/src/video/ly_counter.h
+++ b/libgambatte/src/video/ly_counter.h
@@ -19,6 +19,8 @@
#ifndef LY_COUNTER_H
#define LY_COUNTER_H
+#include "newstate.h"
+
namespace gambatte {
struct SaveState;
@@ -49,6 +51,9 @@ public:
void reset(unsigned long videoCycles, unsigned long lastUpdate);
void setDoubleSpeed(bool ds_in);
unsigned long time() const { return time_; }
+
+ void SaveS(NewState *ns);
+ void LoadS(NewState *ns);
};
}
diff --git a/libgambatte/src/video/lyc_irq.cpp b/libgambatte/src/video/lyc_irq.cpp
index 2b1b3809f3..4e8097a6ca 100644
--- a/libgambatte/src/video/lyc_irq.cpp
+++ b/libgambatte/src/video/lyc_irq.cpp
@@ -99,4 +99,24 @@ void LycIrq::lcdReset() {
lycReg_ = lycRegSrc_;
}
+void LycIrq::SaveS(NewState *ns)
+{
+ NSS(time_);
+ NSS(lycRegSrc_);
+ NSS(statRegSrc_);
+ NSS(lycReg_);
+ NSS(statReg_);
+ NSS(cgb_);
+}
+
+void LycIrq::LoadS(NewState *ns)
+{
+ NSL(time_);
+ NSL(lycRegSrc_);
+ NSL(statRegSrc_);
+ NSL(lycReg_);
+ NSL(statReg_);
+ NSL(cgb_);
+}
+
}
diff --git a/libgambatte/src/video/lyc_irq.h b/libgambatte/src/video/lyc_irq.h
index 019914db6c..874a581d5a 100644
--- a/libgambatte/src/video/lyc_irq.h
+++ b/libgambatte/src/video/lyc_irq.h
@@ -19,6 +19,8 @@
#ifndef VIDEO_LYC_IRQ_H
#define VIDEO_LYC_IRQ_H
+#include "newstate.h"
+
namespace gambatte {
struct SaveState;
@@ -52,6 +54,9 @@ public:
void lycRegChange(unsigned lycReg, const LyCounter &lyCounter, unsigned long cc) {
regChange(statRegSrc_, lycReg, lyCounter, cc);
}
+
+ void SaveS(NewState *ns);
+ void LoadS(NewState *ns);
};
}
diff --git a/libgambatte/src/video/next_m0_time.cpp b/libgambatte/src/video/next_m0_time.cpp
index 869eb45396..3aae7bf0bd 100644
--- a/libgambatte/src/video/next_m0_time.cpp
+++ b/libgambatte/src/video/next_m0_time.cpp
@@ -1,6 +1,21 @@
#include "next_m0_time.h"
#include "ppu.h"
-void gambatte::NextM0Time::predictNextM0Time(const PPU &ppu) {
+namespace gambatte {
+
+void NextM0Time::predictNextM0Time(const PPU &ppu) {
predictedNextM0Time_ = ppu.predictedNextXposTime(167);
}
+
+
+void NextM0Time::SaveS(NewState *ns)
+{
+ NSS(predictedNextM0Time_);
+}
+
+void NextM0Time::LoadS(NewState *ns)
+{
+ NSL(predictedNextM0Time_);
+}
+
+}
diff --git a/libgambatte/src/video/next_m0_time.h b/libgambatte/src/video/next_m0_time.h
index 148f471804..c74f33194f 100644
--- a/libgambatte/src/video/next_m0_time.h
+++ b/libgambatte/src/video/next_m0_time.h
@@ -1,6 +1,8 @@
#ifndef NEXT_M0_TIME_H_
#define NEXT_M0_TIME_H_
+#include "newstate.h"
+
namespace gambatte {
class NextM0Time {
@@ -11,6 +13,9 @@ public:
void predictNextM0Time(const class PPU &v);
void invalidatePredictedNextM0Time() { predictedNextM0Time_ = 0; }
unsigned predictedNextM0Time() const { return predictedNextM0Time_; }
+
+ void SaveS(NewState *ns);
+ void LoadS(NewState *ns);
};
}
diff --git a/libgambatte/src/video/ppu.cpp b/libgambatte/src/video/ppu.cpp
index 276e012daa..5dc678db44 100644
--- a/libgambatte/src/video/ppu.cpp
+++ b/libgambatte/src/video/ppu.cpp
@@ -1745,4 +1745,136 @@ void PPU::update(const unsigned long cc) {
}
}
+void PPU::SaveS(NewState *ns)
+{
+ 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);
+}
+
+void PPU::LoadS(NewState *ns)
+{
+ NSL(p_.bgPalette);
+ NSL(p_.spPalette);
+ NSL(p_.spriteList);
+ NSL(p_.spwordList);
+ NSL(p_.nextSprite);
+ NSL(p_.currentSprite);
+
+ EBL(p_.nextCallPtr, 0);
+ EVL(p_.nextCallPtr, &M2::Ly0::f0_, 1);
+ EVL(p_.nextCallPtr, &M2::LyNon0::f0_, 2);
+ EVL(p_.nextCallPtr, &M2::LyNon0::f1_, 3);
+ EVL(p_.nextCallPtr, &M3Start::f0_, 4);
+ EVL(p_.nextCallPtr, &M3Start::f1_, 5);
+ EVL(p_.nextCallPtr, &M3Loop::Tile::f0_, 6);
+ EVL(p_.nextCallPtr, &M3Loop::Tile::f1_, 7);
+ EVL(p_.nextCallPtr, &M3Loop::Tile::f2_, 8);
+ EVL(p_.nextCallPtr, &M3Loop::Tile::f3_, 9);
+ EVL(p_.nextCallPtr, &M3Loop::Tile::f4_, 10);
+ EVL(p_.nextCallPtr, &M3Loop::Tile::f5_, 11);
+ EVL(p_.nextCallPtr, &M3Loop::LoadSprites::f0_, 12);
+ EVL(p_.nextCallPtr, &M3Loop::LoadSprites::f1_, 13);
+ EVL(p_.nextCallPtr, &M3Loop::LoadSprites::f2_, 14);
+ EVL(p_.nextCallPtr, &M3Loop::LoadSprites::f3_, 15);
+ EVL(p_.nextCallPtr, &M3Loop::LoadSprites::f4_, 16);
+ EVL(p_.nextCallPtr, &M3Loop::LoadSprites::f5_, 17);
+ EVL(p_.nextCallPtr, &M3Loop::StartWindowDraw::f0_, 18);
+ EVL(p_.nextCallPtr, &M3Loop::StartWindowDraw::f1_, 19);
+ EVL(p_.nextCallPtr, &M3Loop::StartWindowDraw::f2_, 20);
+ EVL(p_.nextCallPtr, &M3Loop::StartWindowDraw::f3_, 21);
+ EVL(p_.nextCallPtr, &M3Loop::StartWindowDraw::f4_, 22);
+ EVL(p_.nextCallPtr, &M3Loop::StartWindowDraw::f5_, 23);
+ EEL(p_.nextCallPtr, NULL);
+
+ NSL(p_.now);
+ NSL(p_.lastM0Time);
+ NSL(p_.cycles);
+
+ NSL(p_.tileword);
+ NSL(p_.ntileword);
+
+ SSL(p_.spriteMapper);
+ SSL(p_.lyCounter);
+ //SSL(p_.framebuf); // no state
+
+ NSL(p_.lcdc);
+ NSL(p_.scy);
+ NSL(p_.scx);
+ NSL(p_.wy);
+ NSL(p_.wy2);
+ NSL(p_.wx);
+ NSL(p_.winDrawState);
+ NSL(p_.wscx);
+ NSL(p_.winYPos);
+ NSL(p_.reg0);
+ NSL(p_.reg1);
+ NSL(p_.attrib);
+ NSL(p_.nattrib);
+ NSL(p_.xpos);
+ NSL(p_.endx);
+
+ NSL(p_.cgb);
+ NSL(p_.weMaster);
+}
+
}
diff --git a/libgambatte/src/video/ppu.h b/libgambatte/src/video/ppu.h
index 3e5e853c90..fda7ee0bbd 100644
--- a/libgambatte/src/video/ppu.h
+++ b/libgambatte/src/video/ppu.h
@@ -23,6 +23,8 @@
#include "video/sprite_mapper.h"
#include "gbint.h"
+#include "newstate.h"
+
namespace gambatte {
class PPUFrameBuf {
@@ -128,6 +130,9 @@ public:
void speedChange(unsigned long cycleCounter);
unsigned long * spPalette() { return p_.spPalette; }
void update(unsigned long cc);
+
+ void SaveS(NewState *ns);
+ void LoadS(NewState *ns);
};
}
diff --git a/libgambatte/src/video/sprite_mapper.cpp b/libgambatte/src/video/sprite_mapper.cpp
index 3bd7fe9cf6..7858cb6acc 100644
--- a/libgambatte/src/video/sprite_mapper.cpp
+++ b/libgambatte/src/video/sprite_mapper.cpp
@@ -117,6 +117,28 @@ void SpriteMapper::OamReader::loadState(const SaveState &ss, const unsigned char
change(lu);
}
+void SpriteMapper::OamReader::SaveS(NewState *ns)
+{
+ NSS(buf);
+ NSS(szbuf);
+
+ NSS(lu);
+ NSS(lastChange);
+ NSS(largeSpritesSrc);
+ NSS(cgb_);
+}
+
+void SpriteMapper::OamReader::LoadS(NewState *ns)
+{
+ NSL(buf);
+ NSL(szbuf);
+
+ NSL(lu);
+ NSL(lastChange);
+ NSL(largeSpritesSrc);
+ NSL(cgb_);
+}
+
void SpriteMapper::OamReader::enableDisplay(const unsigned long cc) {
std::memset(buf, 0x00, sizeof(buf));
std::fill(szbuf, szbuf + 40, false);
@@ -179,4 +201,22 @@ unsigned long SpriteMapper::doEvent(const unsigned long time) {
return oamReader.changed() ? time + oamReader.lyCounter.lineTime() : static_cast(DISABLED_TIME);
}
+void SpriteMapper::SaveS(NewState *ns)
+{
+ NSS(spritemap);
+ NSS(num);
+
+ SSS(nextM0Time_);
+ SSS(oamReader);
+}
+
+void SpriteMapper::LoadS(NewState *ns)
+{
+ NSL(spritemap);
+ NSL(num);
+
+ SSL(nextM0Time_);
+ SSL(oamReader);
+}
+
}
diff --git a/libgambatte/src/video/sprite_mapper.h b/libgambatte/src/video/sprite_mapper.h
index b103b25b18..f02447e7fb 100644
--- a/libgambatte/src/video/sprite_mapper.h
+++ b/libgambatte/src/video/sprite_mapper.h
@@ -21,6 +21,7 @@
#include "ly_counter.h"
#include "../savestate.h"
+#include "newstate.h"
namespace gambatte {
class NextM0Time;
@@ -57,6 +58,9 @@ class SpriteMapper {
void saveState(SaveState &state) const { state.ppu.enableDisplayM0Time = lu; }
void loadState(const SaveState &ss, const unsigned char *oamram);
bool inactivePeriodAfterDisplayEnable(const unsigned long cc) const { return cc < lu; }
+
+ void SaveS(NewState *ns);
+ void LoadS(NewState *ns);
};
enum { NEED_SORTING_MASK = 0x80 };
@@ -122,6 +126,9 @@ public:
void saveState(SaveState &state) const { oamReader.saveState(state); }
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); }
+
+ void SaveS(NewState *ns);
+ void LoadS(NewState *ns);
};
}
diff --git a/output/dll/libgambatte.dll b/output/dll/libgambatte.dll
index e3a57dc706..0af7301d60 100644
Binary files a/output/dll/libgambatte.dll and b/output/dll/libgambatte.dll differ