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