lsnes/libgambatte-patches/svn364/0001-Changes-to-make-libgambatte-rerecording-friendly.patch
2013-12-05 08:49:54 +02:00

5797 lines
192 KiB
Diff

From f4fcace8e1261f4e0e01c7db174dc148eab1180f Mon Sep 17 00:00:00 2001
From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
Date: Fri, 18 Jan 2013 21:06:30 +0200
Subject: [PATCH 1/3] Changes to make libgambatte rerecording friendly
---
Makefile | 10 +
libgambatte/Makefile | 18 ++
libgambatte/include/gambatte.h | 72 +++++++-
libgambatte/src/bitmap_font.cpp | 10 +-
libgambatte/src/bitmap_font.h | 6 +-
libgambatte/src/cpu.cpp | 45 ++++-
libgambatte/src/cpu.h | 31 +++-
libgambatte/src/file/file.cpp | 34 ++++
libgambatte/src/file/file.h | 10 +
libgambatte/src/gambatte.cpp | 124 +++++++++++--
libgambatte/src/initstate.cpp | 8 +-
libgambatte/src/initstate.h | 8 +-
libgambatte/src/interrupter.cpp | 17 +-
libgambatte/src/interrupter.h | 18 +-
libgambatte/src/interruptrequester.cpp | 23 ++-
libgambatte/src/interruptrequester.h | 27 ++-
libgambatte/src/loadsave.cpp | 266 +++++++++++++++++++++++++++
libgambatte/src/loadsave.h | 160 ++++++++++++++++
libgambatte/src/mem/cartridge.cpp | 160 +++++++++++++---
libgambatte/src/mem/cartridge.h | 37 +++-
libgambatte/src/mem/memptrs.cpp | 32 +++-
libgambatte/src/mem/memptrs.h | 17 +-
libgambatte/src/mem/rtc.cpp | 57 +++++-
libgambatte/src/mem/rtc.h | 11 +-
libgambatte/src/memory.cpp | 108 +++++++----
libgambatte/src/memory.h | 80 +++++---
libgambatte/src/minkeeper.h | 30 ++-
libgambatte/src/savestate.h | 54 +++---
libgambatte/src/sound.cpp | 36 +++-
libgambatte/src/sound.h | 17 +-
libgambatte/src/sound/channel1.cpp | 44 ++++-
libgambatte/src/sound/channel1.h | 25 ++-
libgambatte/src/sound/channel2.cpp | 41 ++++-
libgambatte/src/sound/channel2.h | 17 +-
libgambatte/src/sound/channel3.cpp | 41 ++++-
libgambatte/src/sound/channel3.h | 27 ++-
libgambatte/src/sound/channel4.cpp | 59 ++++--
libgambatte/src/sound/channel4.h | 40 ++--
libgambatte/src/sound/duty_unit.cpp | 34 +++-
libgambatte/src/sound/duty_unit.h | 29 +--
libgambatte/src/sound/envelope_unit.cpp | 19 +-
libgambatte/src/sound/envelope_unit.h | 12 +-
libgambatte/src/sound/length_counter.cpp | 19 +-
libgambatte/src/sound/length_counter.h | 13 +-
libgambatte/src/sound/sound_unit.h | 15 +-
libgambatte/src/sound/static_output_tester.h | 8 +-
libgambatte/src/state_osd_elements.cpp | 8 +-
libgambatte/src/statesaver.cpp | 6 +-
libgambatte/src/statesaver.h | 7 +
libgambatte/src/tima.cpp | 35 ++--
libgambatte/src/tima.h | 28 +--
libgambatte/src/video.cpp | 120 ++++++------
libgambatte/src/video.h | 118 +++++++-----
libgambatte/src/video/ly_counter.cpp | 14 +-
libgambatte/src/video/ly_counter.h | 26 ++-
libgambatte/src/video/lyc_irq.cpp | 14 +-
libgambatte/src/video/lyc_irq.h | 27 ++-
libgambatte/src/video/next_m0_time.h | 10 +
libgambatte/src/video/ppu.cpp | 125 ++++++++++---
libgambatte/src/video/ppu.h | 93 +++++++---
libgambatte/src/video/sprite_mapper.cpp | 17 +-
libgambatte/src/video/sprite_mapper.h | 53 ++++--
62 files changed, 2098 insertions(+), 572 deletions(-)
create mode 100644 Makefile
create mode 100644 libgambatte/Makefile
create mode 100644 libgambatte/src/loadsave.cpp
create mode 100644 libgambatte/src/loadsave.h
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..2714a5b
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,10 @@
+all: libgambatte/__all_files__
+
+libgambatte/__all_files__: forcelook
+ $(MAKE) -C libgambatte
+
+clean: forcelook
+ $(MAKE) -C libgambatte clean
+
+forcelook:
+ @true
diff --git a/libgambatte/Makefile b/libgambatte/Makefile
new file mode 100644
index 0000000..7c3724e
--- /dev/null
+++ b/libgambatte/Makefile
@@ -0,0 +1,18 @@
+all: libgambatte.$(ARCHIVE_SUFFIX)
+
+REALAR=$(CROSS_PREFIX)ar
+
+OBJECTS=$(patsubst %.cpp,%.$(OBJECT_SUFFIX),$(wildcard src/*.cpp src/video/*.cpp src/mem/*.cpp src/sound/*.cpp src/file/file.cpp))
+
+%.o: %.cpp
+ $(gambatte_compiler) $(CFLAGS) -Wno-deprecated-declarations -DHAVE_CSTDINT -I../common -Iinclude -Isrc -c -o $@ $<
+
+libgambatte.$(ARCHIVE_SUFFIX): $(OBJECTS)
+ $(REALAR) crvs $@ $^
+ $(REALRANLIB) $@
+
+clean: forcelook
+ rm -f $(OBJECTS) libgambatte.$(ARCHIVE_SUFFIX)
+
+forcelook:
+ @true
\ No newline at end of file
diff --git a/libgambatte/include/gambatte.h b/libgambatte/include/gambatte.h
index e997b25..11a54a9 100644
--- a/libgambatte/include/gambatte.h
+++ b/libgambatte/include/gambatte.h
@@ -23,6 +23,11 @@
#include "loadres.h"
#include "gbint.h"
#include <string>
+#include <vector>
+
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
namespace gambatte {
enum { BG_PALETTE = 0, SP1_PALETTE = 1, SP2_PALETTE = 2 };
@@ -31,7 +36,7 @@ class GB {
public:
GB();
~GB();
-
+
enum LoadFlag {
FORCE_DMG = 1, /**< Treat the ROM as not having CGB support regardless of what its header advertises. */
GBA_CGB = 2, /**< Use GBA intial CPU register values when in CGB mode. */
@@ -45,7 +50,15 @@ public:
* @return 0 on success, negative value on failure.
*/
LoadRes load(const std::string &romfile, unsigned flags = 0);
-
+ /** Load ROM image.
+ *
+ * @param image Raw ROM image data.
+ * @param isize Size of raw ROM image data.
+ * @param flags ORed combination of LoadFlags.
+ * @return 0 on success, negative value on failure.
+ */
+ LoadRes load(const unsigned char* image, size_t isize, unsigned flags = 0);
+
/** Emulates until at least 'samples' stereo sound samples are produced in the supplied buffer,
* or until a video frame has been drawn.
*
@@ -67,7 +80,7 @@ public:
* @param samples in: number of stereo samples to produce, out: actual number of samples produced
* @return sample number at which the video frame was produced. -1 means no frame was produced.
*/
- long runFor(gambatte::uint_least32_t *videoBuf, int pitch,
+ signed runFor(gambatte::uint_least32_t *videoBuf, int pitch,
gambatte::uint_least32_t *soundBuf, unsigned &samples);
/** Reset to initial state.
@@ -121,7 +134,18 @@ public:
* @return success
*/
bool loadState(const std::string &filepath);
-
+
+ /** Save savestate to given buffer.
+ */
+ void saveState(std::vector<char>& data, const std::vector<char>& cmpdata);
+ /** Save savestate to given buffer.
+ */
+ void saveState(std::vector<char>& data);
+ /** Load savestate from given buffer.
+ */
+ void loadState(const std::vector<char>& data);
+
+
/** Selects which state slot to save state to or load state from.
* There are 10 such slots, numbered from 0 to 9 (periodically extended for all n).
*/
@@ -145,14 +169,52 @@ public:
* @param codes Game Shark codes in format 01HHHHHH;01HHHHHH;... where H is [0-9]|[A-F]
*/
void setGameShark(const std::string &codes);
-
+
+ /** Set RTC base time.
+ */
+ void setRtcBase(time_t time);
+
+ /** Get RTC base time.
+ */
+ time_t getRtcBase();
+
+ /** Get pointer and size to Work RAM.
+ * @return The pointer and size of Work RAM.
+ */
+ std::pair<unsigned char*, size_t> getWorkRam();
+
+ /** Get pointer and size to Save RAM.
+ * @return The pointer and size of Save RAM.
+ */
+ std::pair<unsigned char*, size_t> getSaveRam();
+
+ /** Get pointer and size to I/O RAM.
+ * @return The pointer and size of I/O RAM.
+ */
+ std::pair<unsigned char*, size_t> getIoRam();
+
+ /** Get pointer and size to Video RAM.
+ * @return The pointer and size of Video RAM.
+ */
+ std::pair<unsigned char*, size_t> getVideoRam();
+
+ /** Function to get wall time. */
+ void set_walltime_fn(time_t (*_walltime)());
+
+ /** Get version. */
+ static std::string version();
private:
+ void preload_common();
+ void postload_common(const unsigned flags);
struct Priv;
Priv *const p_;
+ time_t (*walltime)();
GB(const GB &);
GB & operator=(const GB &);
};
}
+#define GAMBATTE_USES_LOADRES
+
#endif
diff --git a/libgambatte/src/bitmap_font.cpp b/libgambatte/src/bitmap_font.cpp
index 7d6f14d..13b591b 100644
--- a/libgambatte/src/bitmap_font.cpp
+++ b/libgambatte/src/bitmap_font.cpp
@@ -68,6 +68,10 @@
gnome dot org.
*/
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
#include "bitmap_font.h"
static const unsigned char n0_bits[] = { 0x68,
@@ -285,10 +289,10 @@ unsigned getWidth(const char *chars) {
namespace {
class Rgb32Fill {
- const unsigned long color;
+ const uint_least32_t color;
public:
- explicit Rgb32Fill(unsigned long color) : color(color) {}
+ explicit Rgb32Fill(uint_least32_t color) : color(color) {}
void operator()(gambatte::uint_least32_t *dest, unsigned /*pitch*/) const {
*dest = color;
@@ -296,7 +300,7 @@ public:
};
}
-void print(gambatte::uint_least32_t *dest, const unsigned pitch, const unsigned long color, const char *chars) {
+void print(gambatte::uint_least32_t *dest, const unsigned pitch, const uint_least32_t color, const char *chars) {
print(dest, pitch, Rgb32Fill(color), chars);
}
diff --git a/libgambatte/src/bitmap_font.h b/libgambatte/src/bitmap_font.h
index eca12aa..26d4fdc 100644
--- a/libgambatte/src/bitmap_font.h
+++ b/libgambatte/src/bitmap_font.h
@@ -19,6 +19,10 @@
#ifndef BITMAP_FONT_H
#define BITMAP_FONT_H
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
#include "gbint.h"
namespace bitmapfont {
@@ -42,7 +46,7 @@ unsigned getWidth(const char *chars);
template<class RandomAccessIterator, class Fill>
void print(RandomAccessIterator dest, unsigned pitch, Fill fill, const char *chars);
-void print(gambatte::uint_least32_t *dest, unsigned pitch, unsigned long color, const char *chars);
+void print(gambatte::uint_least32_t *dest, unsigned pitch, uint_least32_t color, const char *chars);
void utoa(unsigned u, char *a);
// --- INTERFACE END ---
diff --git a/libgambatte/src/cpu.cpp b/libgambatte/src/cpu.cpp
index 554a724..2d7f495 100644
--- a/libgambatte/src/cpu.cpp
+++ b/libgambatte/src/cpu.cpp
@@ -20,10 +20,14 @@
#include "memory.h"
#include "savestate.h"
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
namespace gambatte {
-CPU::CPU()
-: memory(Interrupter(SP, PC_)),
+CPU::CPU(time_t (**_getCurrentTime)())
+: memory(Interrupter(SP, PC_), _getCurrentTime),
cycleCounter_(0),
PC_(0x100),
SP(0xFFFE),
@@ -42,10 +46,10 @@ CPU::CPU()
{
}
-long CPU::runFor(const unsigned long cycles) {
+signed CPU::runFor(const unsigned cycles) {
process(cycles/* << memory.isDoubleSpeed()*/);
- const long csb = memory.cyclesSinceBlit(cycleCounter_);
+ const signed csb = memory.cyclesSinceBlit(cycleCounter_);
if (cycleCounter_ & 0x80000000)
cycleCounter_ = memory.resetCounters(cycleCounter_);
@@ -124,6 +128,27 @@ void CPU::loadState(const SaveState &state) {
skip = state.cpu.skip;
}
+void CPU::loadOrSave(loadsave& state)
+{
+ memory.loadOrSave(state);
+ state(cycleCounter_);
+ state(PC_);
+ state(SP);
+ state(HF1);
+ state(HF2);
+ state(ZF);
+ state(CF);
+ state(A_);
+ state(B);
+ state(C);
+ state(D);
+ state(E);
+ state(H);
+ state(L);
+ state(skip);
+}
+
+
#define BC() ( B << 8 | C )
#define DE() ( D << 8 | E )
#define HL() ( H << 8 | L )
@@ -503,18 +528,18 @@ void CPU::loadState(const SaveState &state) {
PC_MOD(ret_var_h << 8 | ret_var_l); \
} while (0)
-void CPU::process(const unsigned long cycles) {
+void CPU::process(const unsigned cycles) {
memory.setEndtime(cycleCounter_, cycles);
unsigned char A = A_;
- unsigned long cycleCounter = cycleCounter_;
+ unsigned cycleCounter = cycleCounter_;
while (memory.isActive()) {
unsigned short PC = PC_;
-
+
if (memory.halted()) {
if (cycleCounter < memory.nextEventTime()) {
- const unsigned long cycles = memory.nextEventTime() - cycleCounter;
+ const unsigned cycles = memory.nextEventTime() - cycleCounter;
cycleCounter += cycles + (-cycles & 3);
}
} else while (cycleCounter < memory.nextEventTime()) {
@@ -612,7 +637,7 @@ void CPU::process(const unsigned long cycles) {
cycleCounter = memory.stop(cycleCounter);
if (cycleCounter < memory.nextEventTime()) {
- const unsigned long cycles = memory.nextEventTime() - cycleCounter;
+ const unsigned cycles = memory.nextEventTime() - cycleCounter;
cycleCounter += cycles + (-cycles & 3);
}
@@ -1140,7 +1165,7 @@ void CPU::process(const unsigned long cycles) {
memory.halt();
if (cycleCounter < memory.nextEventTime()) {
- const unsigned long cycles = memory.nextEventTime() - cycleCounter;
+ const unsigned cycles = memory.nextEventTime() - cycleCounter;
cycleCounter += cycles + (-cycles & 3);
}
}
diff --git a/libgambatte/src/cpu.h b/libgambatte/src/cpu.h
index e0f7fc7..fd4bd64 100644
--- a/libgambatte/src/cpu.h
+++ b/libgambatte/src/cpu.h
@@ -19,14 +19,19 @@
#ifndef CPU_H
#define CPU_H
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
#include "memory.h"
+#include "loadsave.h"
namespace gambatte {
class CPU {
Memory memory;
- unsigned long cycleCounter_;
+ unsigned cycleCounter_;
unsigned short PC_;
unsigned short SP;
@@ -37,20 +42,22 @@ class CPU {
bool skip;
- void process(unsigned long cycles);
+ void process(unsigned cycles);
public:
- CPU();
+ CPU(time_t (**_getCurrentTime)());
// void halt();
// unsigned interrupt(unsigned address, unsigned cycleCounter);
- long runFor(unsigned long cycles);
+ signed runFor(unsigned cycles);
void setStatePtrs(SaveState &state);
void saveState(SaveState &state);
void loadState(const SaveState &state);
-
+
+ void loadOrSave(loadsave& state);
+
void loadSavedata() { memory.loadSavedata(); }
void saveSavedata() { memory.saveSavedata(); }
@@ -77,7 +84,11 @@ public:
LoadRes load(std::string const &romfile, bool forceDmg, bool multicartCompat) {
return memory.loadROM(romfile, forceDmg, multicartCompat);
}
-
+
+ LoadRes load(const unsigned char* image, size_t isize, bool forceDmg, bool multicartCompat) {
+ return memory.loadROM(image, isize, forceDmg, multicartCompat);
+ }
+
bool loaded() const { return memory.loaded(); }
char const * romTitle() const { return memory.romTitle(); }
PakInfo const pakInfo(bool multicartCompat) const { return memory.pakInfo(multicartCompat); }
@@ -93,6 +104,14 @@ public:
void setGameGenie(const std::string &codes) { memory.setGameGenie(codes); }
void setGameShark(const std::string &codes) { memory.setGameShark(codes); }
+
+ void setRtcBase(time_t time) { memory.setRtcBase(time); }
+ time_t getRtcBase() { return memory.getRtcBase(); }
+ std::pair<unsigned char*, size_t> getWorkRam() { return memory.getWorkRam(); }
+ std::pair<unsigned char*, size_t> getSaveRam() { return memory.getSaveRam(); }
+ std::pair<unsigned char*, size_t> getIoRam() { return memory.getIoRam(); }
+ std::pair<unsigned char*, size_t> getVideoRam() { return memory.getVideoRam(); };
+
};
}
diff --git a/libgambatte/src/file/file.cpp b/libgambatte/src/file/file.cpp
index 2337327..2fe3ad4 100644
--- a/libgambatte/src/file/file.cpp
+++ b/libgambatte/src/file/file.cpp
@@ -20,7 +20,41 @@ Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
***************************************************************************/
#include "stdfile.h"
+#include <cstring>
+
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
std::auto_ptr<gambatte::File> gambatte::newFileInstance(const std::string &filepath) {
return std::auto_ptr<File>(new StdFile(filepath.c_str()));
}
+
+namespace
+{
+ struct MemoryFile : public gambatte::File
+ {
+ MemoryFile(const unsigned char* image, size_t isize) : buf(image), bufsize(isize),
+ ptr(0), xfail(false) {}
+ ~MemoryFile() {}
+ void rewind() { ptr = 0; xfail = false; }
+ std::size_t size() const { return bufsize; }
+ void read(char *buffer, std::size_t amount) {
+ if(amount > bufsize - ptr) {
+ memcpy(buffer, buf, bufsize - ptr);
+ xfail = true;
+ } else
+ memcpy(buffer, buf, amount);
+ }
+ bool fail() const { return xfail; }
+ private:
+ const unsigned char* buf;
+ size_t bufsize;
+ size_t ptr;
+ bool xfail;
+ };
+}
+
+std::auto_ptr<gambatte::File> gambatte::newFileInstance(const unsigned char* image, size_t isize) {
+ return std::auto_ptr<File>(new MemoryFile(image, isize));
+}
diff --git a/libgambatte/src/file/file.h b/libgambatte/src/file/file.h
index 045cd25..392319e 100644
--- a/libgambatte/src/file/file.h
+++ b/libgambatte/src/file/file.h
@@ -22,9 +22,18 @@ Free Software Foundation, Inc.,
#ifndef GAMBATTE_FILE_H
#define GAMBATTE_FILE_H
+
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
#include <memory>
#include <string>
+//
+// Modified 2012-07-10 by H. Ilari Liusvaara
+// - New API methods.
+
namespace gambatte {
class File {
@@ -37,6 +46,7 @@ public:
};
std::auto_ptr<File> newFileInstance(const std::string &filepath);
+std::auto_ptr<File> newFileInstance(const unsigned char* image, size_t isize);
}
diff --git a/libgambatte/src/gambatte.cpp b/libgambatte/src/gambatte.cpp
index 83a68f7..9c4fcdc 100644
--- a/libgambatte/src/gambatte.cpp
+++ b/libgambatte/src/gambatte.cpp
@@ -25,6 +25,10 @@
#include <sstream>
#include <cstring>
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
static const std::string itos(const int i) {
std::stringstream ss;
ss << i;
@@ -35,16 +39,24 @@ static const std::string statePath(const std::string &basePath, const int stateN
return basePath + "_" + itos(stateNo) + ".gqs";
}
+namespace
+{
+ time_t default_walltime()
+ {
+ return time(0);
+ }
+}
+
namespace gambatte {
struct GB::Priv {
CPU cpu;
int stateNo;
unsigned loadflags;
- Priv() : stateNo(1), loadflags(0) {}
+ Priv(time_t (**_getCurrentTime)()) : stateNo(1), loadflags(0), cpu(_getCurrentTime) {}
};
-GB::GB() : p_(new Priv) {}
+GB::GB() : p_(new Priv(&walltime)), walltime(default_walltime) {}
GB::~GB() {
if (p_->cpu.loaded())
@@ -53,7 +65,7 @@ GB::~GB() {
delete p_;
}
-long GB::runFor(gambatte::uint_least32_t *const videoBuf, const int pitch,
+signed GB::runFor(gambatte::uint_least32_t *const videoBuf, const int pitch,
gambatte::uint_least32_t *const soundBuf, unsigned &samples) {
if (!p_->cpu.loaded()) {
samples = 0;
@@ -62,10 +74,10 @@ long GB::runFor(gambatte::uint_least32_t *const videoBuf, const int pitch,
p_->cpu.setVideoBuffer(videoBuf, pitch);
p_->cpu.setSoundBuffer(soundBuf);
- const long cyclesSinceBlit = p_->cpu.runFor(samples * 2);
+ const signed cyclesSinceBlit = p_->cpu.runFor(samples * 2);
samples = p_->cpu.fillSoundBuffer();
- return cyclesSinceBlit < 0 ? cyclesSinceBlit : static_cast<long>(samples) - (cyclesSinceBlit >> 1);
+ return cyclesSinceBlit < 0 ? cyclesSinceBlit : static_cast<signed>(samples) - (cyclesSinceBlit >> 1);
}
void GB::reset() {
@@ -74,7 +86,7 @@ void GB::reset() {
SaveState state;
p_->cpu.setStatePtrs(state);
- setInitState(state, p_->cpu.isCgb(), p_->loadflags & GBA_CGB);
+ setInitState(state, p_->cpu.isCgb(), p_->loadflags & GBA_CGB, walltime());
p_->cpu.loadState(state);
p_->cpu.loadSavedata();
}
@@ -88,27 +100,46 @@ void GB::setSaveDir(const std::string &sdir) {
p_->cpu.setSaveDir(sdir);
}
-LoadRes GB::load(std::string const &romfile, unsigned const flags) {
+void GB::preload_common()
+{
if (p_->cpu.loaded())
p_->cpu.saveSavedata();
+}
+
+void GB::postload_common(const unsigned flags)
+{
+ SaveState state;
+ p_->cpu.setStatePtrs(state);
+ setInitState(state, p_->cpu.isCgb(), flags & GBA_CGB, walltime());
+ p_->cpu.loadState(state);
+ p_->cpu.loadSavedata();
+
+ p_->stateNo = 1;
+ p_->cpu.setOsdElement(std::auto_ptr<OsdElement>());
+}
+
+LoadRes GB::load(std::string const &romfile, unsigned const flags) {
+ preload_common();
LoadRes const loadres = p_->cpu.load(romfile, flags & FORCE_DMG, flags & MULTICART_COMPAT);
- if (loadres == LOADRES_OK) {
- SaveState state;
- p_->cpu.setStatePtrs(state);
- p_->loadflags = flags;
- setInitState(state, p_->cpu.isCgb(), flags & GBA_CGB);
- p_->cpu.loadState(state);
- p_->cpu.loadSavedata();
-
- p_->stateNo = 1;
- p_->cpu.setOsdElement(std::auto_ptr<OsdElement>());
- }
+ if (loadres == LOADRES_OK)
+ postload_common(flags);
return loadres;
}
+LoadRes GB::load(const unsigned char* image, size_t isize, unsigned flags) {
+ preload_common();
+
+ LoadRes const loadres = p_->cpu.load(image, isize, flags & FORCE_DMG, flags & MULTICART_COMPAT);
+
+ if (loadres == LOADRES_OK)
+ postload_common(flags);
+
+ return loadres;
+}
+
bool GB::isCgb() const {
return p_->cpu.isCgb();
}
@@ -171,6 +202,29 @@ bool GB::saveState(const gambatte::uint_least32_t *const videoBuf, const int pit
return false;
}
+void GB::saveState(std::vector<char>& data, const std::vector<char>& cmpdata) {
+ if (p_->cpu.loaded()) {
+ loadsave_save l(cmpdata);
+ p_->cpu.loadOrSave(l);
+ data = l.get();
+ }
+}
+
+void GB::saveState(std::vector<char>& data) {
+ if (p_->cpu.loaded()) {
+ loadsave_save l;
+ p_->cpu.loadOrSave(l);
+ data = l.get();
+ }
+}
+
+void GB::loadState(const std::vector<char>& data) {
+ if (p_->cpu.loaded()) {
+ loadsave_load l(data);
+ p_->cpu.loadOrSave(l);
+ }
+}
+
void GB::selectState(int n) {
n -= (n / 10) * 10;
p_->stateNo = n < 0 ? n + 10 : n;
@@ -202,4 +256,38 @@ void GB::setGameShark(const std::string &codes) {
p_->cpu.setGameShark(codes);
}
+void GB::setRtcBase(time_t time) {
+ p_->cpu.setRtcBase(time);
+}
+
+time_t GB::getRtcBase() {
+ return p_->cpu.getRtcBase();
+}
+
+std::pair<unsigned char*, size_t> GB::getWorkRam() {
+ return p_->cpu.getWorkRam();
+}
+
+std::pair<unsigned char*, size_t> GB::getSaveRam() {
+ return p_->cpu.getSaveRam();
+}
+
+std::pair<unsigned char*, size_t> GB::getIoRam() {
+ return p_->cpu.getIoRam();
+}
+
+std::pair<unsigned char*, size_t> GB::getVideoRam() {
+ return p_->cpu.getVideoRam();
+}
+
+void GB::set_walltime_fn(time_t (*_walltime)())
+{
+ walltime = _walltime;
+}
+
+std::string GB::version()
+{
+ return "SVN364";
+}
+
}
diff --git a/libgambatte/src/initstate.cpp b/libgambatte/src/initstate.cpp
index 2c28a35..2183f6f 100644
--- a/libgambatte/src/initstate.cpp
+++ b/libgambatte/src/initstate.cpp
@@ -24,6 +24,10 @@
#include <cstring>
#include <ctime>
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
namespace {
static void setInitialCgbWram(unsigned char *const wram) {
@@ -1147,7 +1151,7 @@ static void setInitialDmgIoamhram(unsigned char *const ioamhram) {
} // anon namespace
-void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbMode) {
+void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbMode, time_t starttime) {
static const unsigned char cgbObjpDump[0x40] = {
0x00, 0x00, 0xF2, 0xAB,
0x61, 0xC2, 0xD9, 0xBA,
@@ -1308,7 +1312,7 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM
state.spu.ch4.nr4 = 0;
state.spu.ch4.master = false;
- state.rtc.baseTime = std::time(0);
+ state.rtc.baseTime = starttime;
state.rtc.haltTime = state.rtc.baseTime;
state.rtc.dataDh = 0;
state.rtc.dataDl = 0;
diff --git a/libgambatte/src/initstate.h b/libgambatte/src/initstate.h
index a3792b8..54a303e 100644
--- a/libgambatte/src/initstate.h
+++ b/libgambatte/src/initstate.h
@@ -19,8 +19,14 @@
#ifndef INITSTATE_H
#define INITSTATE_H
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
+#include <ctime>
+
namespace gambatte {
-void setInitState(struct SaveState &state, bool cgb, bool gbaCgbMode);
+void setInitState(struct SaveState &state, bool cgb, bool gbaCgbMode, time_t starttime);
}
#endif
diff --git a/libgambatte/src/interrupter.cpp b/libgambatte/src/interrupter.cpp
index 63e13b2..1592443 100644
--- a/libgambatte/src/interrupter.cpp
+++ b/libgambatte/src/interrupter.cpp
@@ -19,6 +19,10 @@
#include "interrupter.h"
#include "memory.h"
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
namespace gambatte {
Interrupter::Interrupter(unsigned short &SP_in, unsigned short &PC_in) :
@@ -26,7 +30,7 @@ Interrupter::Interrupter(unsigned short &SP_in, unsigned short &PC_in) :
PC(PC_in)
{}
-unsigned long Interrupter::interrupt(const unsigned address, unsigned long cycleCounter, Memory &memory) {
+unsigned Interrupter::interrupt(const unsigned address, unsigned cycleCounter, Memory &memory) {
cycleCounter += 8;
SP = (SP - 1) & 0xFFFF;
memory.write(SP, PC >> 8, cycleCounter);
@@ -61,11 +65,20 @@ void Interrupter::setGameShark(const std::string &codes) {
}
}
-void Interrupter::applyVblankCheats(const unsigned long cycleCounter, Memory &memory) {
+void Interrupter::applyVblankCheats(const unsigned cycleCounter, Memory &memory) {
for (std::size_t i = 0, size = gsCodes.size(); i < size; ++i) {
if (gsCodes[i].type == 0x01)
memory.write(gsCodes[i].address, gsCodes[i].value, cycleCounter);
}
}
+void Interrupter::loadOrSave(loadsave& state) {
+ unsigned gssize = gsCodes.size();
+ state(gssize);
+ if(!state.saving())
+ gsCodes.resize(gssize);
+ for(unsigned i = 0; i < gssize; i++)
+ gsCodes[i].loadOrSave(state);
+}
+
}
diff --git a/libgambatte/src/interrupter.h b/libgambatte/src/interrupter.h
index 1ccbd34..323e43f 100644
--- a/libgambatte/src/interrupter.h
+++ b/libgambatte/src/interrupter.h
@@ -19,8 +19,14 @@
#ifndef INTERRUPTER_H
#define INTERRUPTER_H
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
#include <string>
#include <vector>
+#include "loadsave.h"
+
namespace gambatte {
@@ -28,6 +34,12 @@ struct GsCode {
unsigned short address;
unsigned char value;
unsigned char type;
+
+ void loadOrSave(loadsave& state) {
+ state(address);
+ state(value);
+ state(type);
+ }
};
class Interrupter {
@@ -35,11 +47,13 @@ class Interrupter {
unsigned short &PC;
std::vector<GsCode> gsCodes;
- void applyVblankCheats(unsigned long cc, class Memory &mem);
+ void applyVblankCheats(unsigned cc, class Memory &mem);
public:
Interrupter(unsigned short &SP, unsigned short &PC);
- unsigned long interrupt(const unsigned address, unsigned long cycleCounter, class Memory &memory);
+ unsigned interrupt(const unsigned address, unsigned cycleCounter, class Memory &memory);
void setGameShark(const std::string &codes);
+
+ void loadOrSave(loadsave& state);
};
}
diff --git a/libgambatte/src/interruptrequester.cpp b/libgambatte/src/interruptrequester.cpp
index fa9e68f..4f53238 100644
--- a/libgambatte/src/interruptrequester.cpp
+++ b/libgambatte/src/interruptrequester.cpp
@@ -19,6 +19,10 @@
#include "interruptrequester.h"
#include "savestate.h"
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
namespace gambatte {
InterruptRequester::InterruptRequester() : minIntTime(0), ifreg_(0), iereg_(0) {}
@@ -35,17 +39,26 @@ void InterruptRequester::loadState(const SaveState &state) {
iereg_ = state.mem.ioamhram.get()[0x1FF] & 0x1F;
intFlags.set(state.mem.IME, state.mem.halted);
- eventTimes.setValue<INTERRUPTS>(intFlags.imeOrHalted() && pendingIrqs() ? minIntTime : static_cast<unsigned long>(DISABLED_TIME));
+ eventTimes.setValue<INTERRUPTS>(intFlags.imeOrHalted() && pendingIrqs() ? minIntTime : static_cast<unsigned>(DISABLED_TIME));
+}
+
+void InterruptRequester::loadOrSave(loadsave& state)
+{
+ eventTimes.loadOrSave(state);
+ state(minIntTime);
+ state(ifreg_);
+ state(iereg_);
+ intFlags.loadOrSave(state);
}
-void InterruptRequester::resetCc(const unsigned long oldCc, const unsigned long newCc) {
+void InterruptRequester::resetCc(const unsigned oldCc, const unsigned newCc) {
minIntTime = minIntTime < oldCc ? 0 : minIntTime - (oldCc - newCc);
if (eventTimes.value(INTERRUPTS) != DISABLED_TIME)
eventTimes.setValue<INTERRUPTS>(minIntTime);
}
-void InterruptRequester::ei(const unsigned long cc) {
+void InterruptRequester::ei(const unsigned cc) {
intFlags.setIme();
minIntTime = cc + 1;
@@ -90,14 +103,14 @@ void InterruptRequester::setIereg(const unsigned iereg) {
iereg_ = iereg & 0x1F;
if (intFlags.imeOrHalted())
- eventTimes.setValue<INTERRUPTS>(pendingIrqs() ? minIntTime : static_cast<unsigned long>(DISABLED_TIME));
+ eventTimes.setValue<INTERRUPTS>(pendingIrqs() ? minIntTime : static_cast<unsigned>(DISABLED_TIME));
}
void InterruptRequester::setIfreg(const unsigned ifreg) {
ifreg_ = ifreg;
if (intFlags.imeOrHalted())
- eventTimes.setValue<INTERRUPTS>(pendingIrqs() ? minIntTime : static_cast<unsigned long>(DISABLED_TIME));
+ eventTimes.setValue<INTERRUPTS>(pendingIrqs() ? minIntTime : static_cast<unsigned>(DISABLED_TIME));
}
}
diff --git a/libgambatte/src/interruptrequester.h b/libgambatte/src/interruptrequester.h
index 00e1f8a..7ac9388 100644
--- a/libgambatte/src/interruptrequester.h
+++ b/libgambatte/src/interruptrequester.h
@@ -19,8 +19,13 @@
#ifndef INTERRUPT_REQUESTER_H
#define INTERRUPT_REQUESTER_H
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
#include "counterdef.h"
#include "minkeeper.h"
+#include "loadsave.h"
namespace gambatte {
struct SaveState;
@@ -28,7 +33,7 @@ enum MemEventId { UNHALT, END, BLIT, SERIAL, OAM, DMA, TIMA, VIDEO, INTERRUPTS }
class InterruptRequester {
MinKeeper<INTERRUPTS + 1> eventTimes;
- unsigned long minIntTime;
+ unsigned minIntTime;
unsigned ifreg_;
unsigned iereg_;
@@ -38,7 +43,9 @@ class InterruptRequester {
public:
IntFlags() : flags_(0) {}
-
+
+ void loadOrSave(loadsave& state) { state(flags_); }
+
bool ime() const { return flags_ & IME_MASK; }
bool halted() const { return flags_ & HALTED_MASK; }
bool imeOrHalted() const { return flags_; }
@@ -57,15 +64,17 @@ public:
void saveState(SaveState &) const;
void loadState(const SaveState &);
-
- void resetCc(unsigned long oldCc, unsigned long newCc);
+
+ void loadOrSave(loadsave& state);
+
+ void resetCc(unsigned oldCc, unsigned newCc);
unsigned ifreg() const { return ifreg_; }
unsigned pendingIrqs() const { return ifreg_ & iereg_; }
bool ime() const { return intFlags.ime(); }
bool halted() const { return intFlags.halted(); }
- void ei(unsigned long cc);
+ void ei(unsigned cc);
void di();
void halt();
void unhalt();
@@ -75,10 +84,10 @@ public:
void setIfreg(unsigned ifreg);
MemEventId minEventId() const { return static_cast<MemEventId>(eventTimes.min()); }
- unsigned long minEventTime() const { return eventTimes.minValue(); }
- template<MemEventId id> void setEventTime(unsigned long value) { eventTimes.setValue<id>(value); }
- void setEventTime(const MemEventId id, unsigned long value) { eventTimes.setValue(id, value); }
- unsigned long eventTime(MemEventId id) const { return eventTimes.value(id); }
+ unsigned minEventTime() const { return eventTimes.minValue(); }
+ template<MemEventId id> void setEventTime(unsigned value) { eventTimes.setValue<id>(value); }
+ void setEventTime(const MemEventId id, unsigned value) { eventTimes.setValue(id, value); }
+ unsigned eventTime(MemEventId id) const { return eventTimes.value(id); }
};
inline void flagHdmaReq(InterruptRequester *const intreq) { intreq->setEventTime<DMA>(0); }
diff --git a/libgambatte/src/loadsave.cpp b/libgambatte/src/loadsave.cpp
new file mode 100644
index 0000000..37ea71a
--- /dev/null
+++ b/libgambatte/src/loadsave.cpp
@@ -0,0 +1,266 @@
+/***************************************************************************
+ * Copyright (C) 2012 by H. Ilari Liusvaara *
+ * ilari.liusvaara@elisanet.fi *
+ * *
+ * 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 "loadsave.h"
+#include <stdexcept>
+#include <cstring>
+#include <iostream>
+
+namespace gambatte {
+
+loadsave::~loadsave() throw() {}
+
+loadsave_load::loadsave_load(const std::vector<char>& _memory)
+ : memory(_memory)
+{
+ ptr = 0;
+}
+
+template<typename T> void loadsave_load::do_op(T& x)
+{
+ unsigned long long v = 0;
+ if(ptr + sizeof(T) > memory.size())
+ throw std::runtime_error("Loadstate overflow");
+ for(size_t i = 0; i < sizeof(T); i++)
+ v |= ((unsigned long long)(unsigned char)memory[ptr++] << (8 * (sizeof(T) - i - 1)));
+ x = (T)v;
+}
+
+template<typename T> void loadsave_load::do_op(T& x, unsigned char _tag)
+{
+ if(ptr + 1 > memory.size())
+ throw std::runtime_error("Loadstate overflow");
+ unsigned char _rtag = memory[ptr++];
+ if(_rtag != _tag) {
+ std::cerr << "Wrong type tag: expected=" << (int)_tag << ", got=" << (int)_rtag << std::endl;
+ throw std::runtime_error("Loadstate desynced");
+ }
+ do_op(x);
+}
+
+template<typename T> void loadsave_load::do_op(T* x, size_t s, unsigned char _tag)
+{
+ if(ptr + 1 > memory.size())
+ throw std::runtime_error("Loadstate overflow");
+ unsigned char _rtag = memory[ptr++];
+ if(_rtag != _tag) {
+ std::cerr << "Wrong type tag: expected=" << (int)_tag << ", got=" << (int)_rtag << std::endl;
+ throw std::runtime_error("Loadstate desynced");
+ }
+ unsigned size;
+ do_op(size);
+ if(size != s) {
+ std::cerr << "Wrong number of entries: expected=" << s << ", got=" << size << std::endl;
+ throw std::runtime_error("Loadstate desynced");
+ }
+ for(size_t i = 0; i < s; i++)
+ do_op(x[i]);
+}
+
+void loadsave_load::operator()(bool& x)
+{
+ char c;
+ do_op(c, 0);
+ x = (c != 0);
+}
+
+
+loadsave_load::~loadsave_load() throw() {}
+void loadsave_load::operator()(signed char& x) { do_op(x, 1); }
+void loadsave_load::operator()(unsigned char& x) { do_op(x, 2); }
+void loadsave_load::operator()(signed short& x) { do_op(x, 3); }
+void loadsave_load::operator()(unsigned short& x) { do_op(x, 4); }
+void loadsave_load::operator()(signed int& x) { do_op(x, 5); }
+void loadsave_load::operator()(unsigned int& x) { do_op(x, 6); }
+void loadsave_load::operator()(signed long long& x) { do_op(x, 7); }
+void loadsave_load::operator()(unsigned long long& x) { do_op(x, 8); }
+void loadsave_load::operator()(signed char* x, size_t s) { do_op(x, s, 9); }
+void loadsave_load::operator()(unsigned char* x, size_t s) { do_op(x, s, 10); }
+void loadsave_load::operator()(signed short* x, size_t s) { do_op(x, s, 11); }
+void loadsave_load::operator()(unsigned short* x, size_t s) { do_op(x, s, 12); }
+void loadsave_load::operator()(signed int* x, size_t s) { do_op(x, s, 13); }
+void loadsave_load::operator()(unsigned int* x, size_t s) { do_op(x, s, 14); }
+void loadsave_load::operator()(signed long long* x, size_t s) { do_op(x, s, 15); }
+void loadsave_load::operator()(unsigned long long* x, size_t s) { do_op(x, s, 16); }
+
+void loadsave_load::tag(unsigned short _tag)
+{
+ unsigned short _rtag;
+ do_op(_rtag, 18);
+ if(_tag != _rtag) {
+ std::cerr << "Wrong inner tag: expected=" << (int)_tag << ", got=" << (int)_rtag << std::endl;
+ throw std::runtime_error("Loadstate desynced");
+ }
+}
+
+void loadsave_load::operator()(unsigned char*& ptr, unsigned char* abase)
+{
+ char x;
+ do_op(x, 17);
+ if(!x)
+ ptr = NULL;
+ else {
+ unsigned y;
+ do_op(y);
+ ptr = abase + y;
+ }
+}
+
+void loadsave_load::operator()(const unsigned char*& ptr, unsigned char* abase)
+{
+ char x;
+ do_op(x, 19);
+ if(!x)
+ ptr = NULL;
+ else {
+ unsigned y;
+ do_op(y);
+ ptr = abase + y;
+ }
+}
+
+
+bool loadsave_load::saving() { return false; }
+
+#define BLOCKBYTES 65500
+
+void loadsave_save::pushbytes(char* bytes, size_t amount)
+{
+ if(!nextptr || memory[nextptr - 1].second + amount > BLOCKBYTES) {
+ memory.push_back(std::make_pair(new char[BLOCKBYTES], (size_t)0));
+ nextptr++;
+ }
+ if(cmp.size())
+ try { if(cmp.size() < used + amount || memcmp(&cmp[used], bytes, amount)) throw 42; } catch(...) {}
+ memcpy(memory[nextptr - 1].first + memory[nextptr - 1].second, bytes, amount);
+ memory[nextptr - 1].second += amount;
+ used += amount;
+}
+
+template<typename T> void loadsave_save::do_op(T& x)
+{
+ unsigned long long v = x;
+ char buf[sizeof(T)];
+ for(size_t i = 0; i < sizeof(T); i++)
+ buf[i] = v >> (8 * (sizeof(T) - i - 1));
+ pushbytes(buf, sizeof(T));
+}
+
+template<typename T> void loadsave_save::do_op(T& x, unsigned char _tag)
+{
+ pushbytes((char*)&_tag, 1);
+ do_op(x);
+}
+
+template<typename T> void loadsave_save::do_op(T* x, size_t s, unsigned char _tag)
+{
+ pushbytes((char*)&_tag, 1);
+ unsigned size = s;
+ do_op(size);
+ for(size_t i = 0; i < s; i++)
+ do_op(x[i]);
+}
+
+loadsave_save::loadsave_save()
+{
+ used = 0;
+ nextptr = 0;
+}
+
+loadsave_save::loadsave_save(const std::vector<char>& _memory)
+{
+ used = 0;
+ nextptr = 0;
+ cmp = _memory;
+}
+
+loadsave_save::~loadsave_save() throw()
+{
+ for(auto i : memory)
+ delete[] i.first;
+}
+
+void loadsave_save::operator()(bool& x)
+{
+ char y = x ? 1 : 0;
+ char z = 0;
+ pushbytes(&z, 1);
+ pushbytes(&y, 1);
+}
+
+void loadsave_save::operator()(signed char& x) { do_op(x, 1); }
+void loadsave_save::operator()(unsigned char& x) { do_op(x, 2); }
+void loadsave_save::operator()(signed short& x) { do_op(x, 3); }
+void loadsave_save::operator()(unsigned short& x) { do_op(x, 4); }
+void loadsave_save::operator()(signed int& x) { do_op(x, 5); }
+void loadsave_save::operator()(unsigned int& x) { do_op(x, 6); }
+void loadsave_save::operator()(signed long long& x) { do_op(x, 7); }
+void loadsave_save::operator()(unsigned long long& x) { do_op(x, 8); }
+void loadsave_save::operator()(signed char* x, size_t s) { do_op(x, s, 9); }
+void loadsave_save::operator()(unsigned char* x, size_t s) { do_op(x, s, 10); }
+void loadsave_save::operator()(signed short* x, size_t s) { do_op(x, s, 11); }
+void loadsave_save::operator()(unsigned short* x, size_t s) { do_op(x, s, 12); }
+void loadsave_save::operator()(signed int* x, size_t s) { do_op(x, s, 13); }
+void loadsave_save::operator()(unsigned int* x, size_t s) { do_op(x, s, 14); }
+void loadsave_save::operator()(signed long long* x, size_t s) { do_op(x, s, 15); }
+void loadsave_save::operator()(unsigned long long* x, size_t s) { do_op(x, s, 16); }
+bool loadsave_save::saving() { return true; }
+
+void loadsave_save::operator()(unsigned char*& ptr, unsigned char* abase)
+{
+ if(!ptr) {
+ char x = 0;
+ do_op(x, 17);
+ } else {
+ char x = 1;
+ unsigned y = ptr - abase;
+ do_op(x, 17);
+ do_op(y);
+ }
+}
+
+void loadsave_save::operator()(const unsigned char*& ptr, unsigned char* abase)
+{
+ if(!ptr) {
+ char x = 0;
+ do_op(x, 19);
+ } else {
+ char x = 1;
+ unsigned y = ptr - abase;
+ do_op(x, 19);
+ do_op(y);
+ }
+}
+
+void loadsave_save::tag(unsigned short _tag)
+{
+ do_op(_tag, 18);
+}
+
+std::vector<char> loadsave_save::get()
+{
+ std::vector<char> x;
+ x.resize(used);
+ size_t ptr = 0;
+ for(auto i : memory) {
+ memcpy(&x[ptr], i.first, i.second);
+ ptr += i.second;
+ }
+ return x;
+}
+}
diff --git a/libgambatte/src/loadsave.h b/libgambatte/src/loadsave.h
new file mode 100644
index 0000000..10ebf63
--- /dev/null
+++ b/libgambatte/src/loadsave.h
@@ -0,0 +1,160 @@
+#ifndef _loadsave__hpp__included__
+#define _loadsave__hpp__included__
+/***************************************************************************
+ * Copyright (C) 2012 by H. Ilari Liusvaara *
+ * ilari.liusvaara@elisanet.fi *
+ * *
+ * 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 <cstdint>
+#include <vector>
+#include <cstdlib>
+#include <stdexcept>
+
+namespace gambatte {
+ class loadsave
+ {
+ private:
+ unsigned enumVal;
+ bool enumAssigned;
+ public:
+ virtual ~loadsave() throw();
+ virtual void operator()(bool& x) = 0;
+ virtual void operator()(signed char& x) = 0;
+ virtual void operator()(unsigned char& x) = 0;
+ virtual void operator()(signed short& x) = 0;
+ virtual void operator()(unsigned short& x) = 0;
+ virtual void operator()(signed int& x) = 0;
+ virtual void operator()(unsigned int& x) = 0;
+ virtual void operator()(signed long long& x) = 0;
+ virtual void operator()(unsigned long long& x) = 0;
+ virtual void operator()(signed char* x, size_t s) = 0;
+ virtual void operator()(unsigned char* x, size_t s) = 0;
+ virtual void operator()(signed short* x, size_t s) = 0;
+ virtual void operator()(unsigned short* x, size_t s) = 0;
+ virtual void operator()(signed int* x, size_t s) = 0;
+ virtual void operator()(unsigned int* x, size_t s) = 0;
+ virtual void operator()(long long* x, size_t s) = 0;
+ virtual void operator()(unsigned long long* x, size_t s) = 0;
+ virtual void operator()(unsigned char*& ptr, unsigned char* abase) = 0;
+ virtual void operator()(const unsigned char*& ptr, unsigned char* abase) = 0;
+ virtual void tag(unsigned short tag) = 0;
+ void time(time_t& t) {
+ unsigned long long t_ = t;
+ (*this)(t_);
+ t = t_;
+ }
+ void startEnumeration() {
+ enumAssigned = false;
+ enumVal = 0xFFFFFFFFU;
+ if(!saving())
+ (*this)(enumVal);
+ }
+ template<typename T> void enumerate(T& ptr, T candiate, unsigned symbol) {
+ if(saving()) {
+ if(ptr == candiate) {
+ enumVal = symbol;
+ enumAssigned = true;
+ }
+ } else {
+ if(enumVal == symbol) {
+ ptr = candiate;
+ enumAssigned = true;
+ }
+ }
+ }
+ void endEnumeration() {
+ if(saving())
+ (*this)(enumVal);
+ if(!enumAssigned)
+ throw std::runtime_error("Enumeration missing a choice");
+ }
+ virtual bool saving() = 0;
+ };
+
+ class loadsave_load : public loadsave
+ {
+ const std::vector<char>& memory;
+ size_t ptr;
+ template<typename T> inline void do_op(T& x);
+ template<typename T> inline void do_op(T& x, unsigned char _tag);
+ template<typename T> void do_op(T* x, size_t s, unsigned char _tag);
+ public:
+ loadsave_load(const std::vector<char>& _memory);
+ ~loadsave_load() throw();
+ void operator()(bool& x);
+ void operator()(signed char& x);
+ void operator()(unsigned char& x);
+ void operator()(signed short& x);
+ void operator()(unsigned short& x);
+ void operator()(signed int& x);
+ void operator()(unsigned int& x);
+ void operator()(signed long long& x);
+ void operator()(unsigned long long& x);
+ void operator()(signed char* x, size_t s);
+ void operator()(unsigned char* x, size_t s);
+ void operator()(signed short* x, size_t s);
+ void operator()(unsigned short* x, size_t s);
+ void operator()(signed int* x, size_t s);
+ void operator()(unsigned int* x, size_t s);
+ void operator()(signed long long* x, size_t s);
+ void operator()(unsigned long long* x, size_t s);
+ void operator()(unsigned char*& ptr, unsigned char* abase);
+ void operator()(const unsigned char*& ptr, unsigned char* abase);
+ void tag(unsigned short _tag);
+ bool saving();
+ };
+
+ class loadsave_save : public loadsave
+ {
+ std::vector<std::pair<char*, size_t>> memory;
+ size_t nextptr;
+ size_t used;
+ inline void pushbytes(char* bytes, size_t amount);
+ template<typename T> inline void do_op(T& x);
+ template<typename T> inline void do_op(T& x, unsigned char _tag);
+ template<typename T> void do_op(T* x, size_t s, unsigned char _tag);
+ std::vector<char> cmp;
+ public:
+ loadsave_save();
+ loadsave_save(const std::vector<char>& _memory);
+ ~loadsave_save() throw();
+ void operator()(bool& x);
+ void operator()(signed char& x);
+ void operator()(unsigned char& x);
+ void operator()(signed short& x);
+ void operator()(unsigned short& x);
+ void operator()(signed int& x);
+ void operator()(unsigned int& x);
+ void operator()(signed long long& x);
+ void operator()(unsigned long long& x);
+ void operator()(signed char* x, size_t s);
+ void operator()(unsigned char* x, size_t s);
+ void operator()(signed short* x, size_t s);
+ void operator()(unsigned short* x, size_t s);
+ void operator()(signed int* x, size_t s);
+ void operator()(unsigned int* x, size_t s);
+ void operator()(signed long long* x, size_t s);
+ void operator()(unsigned long long* x, size_t s);
+ void operator()(unsigned char*& ptr, unsigned char* abase);
+ void operator()(const unsigned char*& ptr, unsigned char* abase);
+ void tag(unsigned short _tag);
+ bool saving();
+ std::vector<char> get();
+ };
+}
+
+#endif
diff --git a/libgambatte/src/mem/cartridge.cpp b/libgambatte/src/mem/cartridge.cpp
index 6c3749c..b10f080 100644
--- a/libgambatte/src/mem/cartridge.cpp
+++ b/libgambatte/src/mem/cartridge.cpp
@@ -23,6 +23,10 @@
#include <cstring>
#include <fstream>
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
namespace gambatte {
namespace {
@@ -36,6 +40,8 @@ public:
virtual bool isAddressWithinAreaRombankCanBeMappedTo(unsigned addr, unsigned bank) const {
return (addr< 0x4000) == (bank == 0);
}
+ void loadOrSave(loadsave& state) {
+ }
};
class Mbc0 : public DefaultMbc {
@@ -64,6 +70,10 @@ public:
enableRam = ss.enableRam;
memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, 0);
}
+
+ void loadOrSave(loadsave& state) {
+ state(enableRam);
+ }
};
static inline unsigned rambanks(const MemPtrs &memptrs) {
@@ -95,6 +105,13 @@ public:
{
}
+ void loadOrSave(loadsave& state) {
+ state(rombank);
+ state(rambank);
+ state(enableRam);
+ state(rambankMode);
+ }
+
virtual void romWrite(const unsigned P, const unsigned data) {
switch (P >> 13 & 3) {
case 0:
@@ -168,6 +185,12 @@ public:
{
}
+ void loadOrSave(loadsave& state) {
+ state(rombank);
+ state(enableRam);
+ state(rombank0Mode);
+ }
+
virtual void romWrite(const unsigned P, const unsigned data) {
switch (P >> 13 & 3) {
case 0:
@@ -221,6 +244,11 @@ public:
{
}
+ void loadOrSave(loadsave& state) {
+ state(rombank);
+ state(enableRam);
+ }
+
virtual void romWrite(const unsigned P, const unsigned data) {
switch (P & 0x6100) {
case 0x0000:
@@ -277,6 +305,12 @@ public:
{
}
+ void loadOrSave(loadsave& state) {
+ state(rombank);
+ state(rambank);
+ state(enableRam);
+ }
+
virtual void romWrite(const unsigned P, const unsigned data) {
switch (P >> 13 & 3) {
case 0:
@@ -338,6 +372,13 @@ public:
{
}
+ void loadOrSave(loadsave& state) {
+ state(rombank);
+ state(rambank);
+ state(enableRam);
+ state(rambankMode);
+ }
+
virtual void romWrite(const unsigned P, const unsigned data) {
switch (P >> 13 & 3) {
case 0:
@@ -396,6 +437,12 @@ public:
{
}
+ void loadOrSave(loadsave& state) {
+ state(rombank);
+ state(rambank);
+ state(enableRam);
+ }
+
virtual void romWrite(const unsigned P, const unsigned data) {
switch (P >> 13 & 3) {
case 0:
@@ -510,7 +557,15 @@ static bool presumedMulti64Mbc1(unsigned char const header[], unsigned const rom
LoadRes Cartridge::loadROM(std::string const &romfile, bool const forceDmg, bool const multicartCompat) {
const std::auto_ptr<File> rom(newFileInstance(romfile));
+ return loadROM(rom.get(), forceDmg, multicartCompat, romfile);
+}
+
+LoadRes Cartridge::loadROM(const unsigned char* image, size_t isize, const bool forceDmg, const bool multicartCompat) {
+ const std::auto_ptr<File> rom(newFileInstance(image, isize));
+ return loadROM(rom.get(), forceDmg, multicartCompat, "");
+}
+LoadRes Cartridge::loadROM(File* rom, const bool forceDmg, const bool multicartCompat, const std::string& filename) {
if (rom->fail())
return LOADRES_IO_ERROR;
@@ -593,8 +648,15 @@ LoadRes Cartridge::loadROM(std::string const &romfile, bool const forceDmg, bool
if (rom->fail())
return LOADRES_IO_ERROR;
- defaultSaveBasePath = stripExtension(romfile);
-
+ if(filename != "") {
+ defaultSaveBasePath = stripExtension(filename);
+ memoryCartridge = false;
+ } else {
+ defaultSaveBasePath = "";
+ memoryCartridge = true;
+ }
+ clearMemorySavedData();
+
switch (type) {
case PLAIN: mbc.reset(new Mbc0(memptrs)); break;
case MBC1:
@@ -632,45 +694,69 @@ void Cartridge::loadSavedata() {
const std::string &sbp = saveBasePath();
if (hasBattery(memptrs.romdata()[0x147])) {
- std::ifstream file((sbp + ".sav").c_str(), std::ios::binary | std::ios::in);
+ if(memoryCartridge) {
+ if(memoryCartridgeSram.size())
+ memcpy(memptrs.rambankdata(), &memoryCartridgeSram[0], memptrs.rambankdataend() - memptrs.rambankdata());
+ } else {
+ std::ifstream file((sbp + ".sav").c_str(), std::ios::binary | std::ios::in);
- if (file.is_open()) {
- file.read(reinterpret_cast<char*>(memptrs.rambankdata()), memptrs.rambankdataend() - memptrs.rambankdata());
- enforce8bit(memptrs.rambankdata(), memptrs.rambankdataend() - memptrs.rambankdata());
+ if (file.is_open()) {
+ file.read(reinterpret_cast<char*>(memptrs.rambankdata()), memptrs.rambankdataend() - memptrs.rambankdata());
+ enforce8bit(memptrs.rambankdata(), memptrs.rambankdataend() - memptrs.rambankdata());
+ }
}
}
if (hasRtc(memptrs.romdata()[0x147])) {
- std::ifstream file((sbp + ".rtc").c_str(), std::ios::binary | std::ios::in);
+ if(memoryCartridge) {
+ rtc.setBaseTime(memoryCartridgeRtcBase);
+ } else {
+ std::ifstream file((sbp + ".rtc").c_str(), std::ios::binary | std::ios::in);
- if (file.is_open()) {
- unsigned long basetime = file.get() & 0xFF;
+ if (file.is_open()) {
+ unsigned long basetime = file.get() & 0xFF;
- basetime = basetime << 8 | (file.get() & 0xFF);
- basetime = basetime << 8 | (file.get() & 0xFF);
- basetime = basetime << 8 | (file.get() & 0xFF);
+ basetime = basetime << 8 | (file.get() & 0xFF);
+ basetime = basetime << 8 | (file.get() & 0xFF);
+ basetime = basetime << 8 | (file.get() & 0xFF);
- rtc.setBaseTime(basetime);
+ rtc.setBaseTime(basetime);
+ }
}
}
}
+void Cartridge::clearMemorySavedData()
+{
+ memoryCartridgeRtcBase = 0;
+ memoryCartridgeSram.resize(0);
+}
+
void Cartridge::saveSavedata() {
const std::string &sbp = saveBasePath();
if (hasBattery(memptrs.romdata()[0x147])) {
- std::ofstream file((sbp + ".sav").c_str(), std::ios::binary | std::ios::out);
- file.write(reinterpret_cast<const char*>(memptrs.rambankdata()), memptrs.rambankdataend() - memptrs.rambankdata());
+ if(memoryCartridge) {
+ memoryCartridgeSram.resize(memptrs.rambankdataend() - memptrs.rambankdata());
+ memcpy(&memoryCartridgeSram[0], memptrs.rambankdata(), memptrs.rambankdataend() - memptrs.rambankdata());
+ } else {
+ std::ofstream file((sbp + ".sav").c_str(), std::ios::binary | std::ios::out);
+ file.write(reinterpret_cast<const char*>(memptrs.rambankdata()), memptrs.rambankdataend() - memptrs.rambankdata());
+ }
}
if (hasRtc(memptrs.romdata()[0x147])) {
- std::ofstream file((sbp + ".rtc").c_str(), std::ios::binary | std::ios::out);
- const unsigned long basetime = rtc.getBaseTime();
+ if(memoryCartridge) {
+ memoryCartridgeRtcBase = rtc.getBaseTime();
+ } else {
+ std::ofstream file((sbp + ".rtc").c_str(), std::ios::binary | std::ios::out);
+ const unsigned long basetime = rtc.getBaseTime();
- file.put(basetime >> 24 & 0xFF);
- file.put(basetime >> 16 & 0xFF);
- file.put(basetime >> 8 & 0xFF);
- file.put(basetime & 0xFF);
+ file.put(basetime >> 24 & 0xFF);
+ file.put(basetime >> 16 & 0xFF);
+ file.put(basetime >> 8 & 0xFF);
+ file.put(basetime & 0xFF);
+ }
}
}
@@ -727,4 +813,36 @@ PakInfo const Cartridge::pakInfo(bool const multipakCompat) const {
return PakInfo();
}
+std::pair<unsigned char*, size_t> Cartridge::getSaveRam() {
+ size_t sramsize = memptrs.rambankdataend() - memptrs.rambankdata();
+ return std::make_pair(memptrs.rambankdata(), sramsize);
+}
+
+std::pair<unsigned char*, size_t> Cartridge::getVideoRam() {
+ size_t vramsize = memptrs.vramdataend() - memptrs.vramdata();
+ return std::make_pair(memptrs.vramdata(), vramsize);
+}
+
+std::pair<unsigned char*, size_t> Cartridge::getWorkRam() {
+ size_t worksize = memptrs.wramdataend() - memptrs.wramdata(0);
+ return std::make_pair(memptrs.wramdata(0), worksize);
+}
+
+Cartridge::Cartridge(time_t (**_getCurrentTime)())
+ : rtc(_getCurrentTime) {
+ memoryCartridge = true;
+}
+
+void Cartridge::loadOrSave(loadsave& state) {
+ memptrs.loadOrSave(state);
+ rtc.loadOrSave(state);
+ mbc->loadOrSave(state);
+ unsigned ggsize = ggUndoList.size();
+ state(ggsize);
+ if(!state.saving())
+ ggUndoList.resize(ggsize);
+ for(size_t i = 0; i < ggsize; i++)
+ ggUndoList[i].loadOrSave(state);
+}
+
}
diff --git a/libgambatte/src/mem/cartridge.h b/libgambatte/src/mem/cartridge.h
index 9519eea..642cc83 100644
--- a/libgambatte/src/mem/cartridge.h
+++ b/libgambatte/src/mem/cartridge.h
@@ -26,9 +26,16 @@
#include <memory>
#include <string>
#include <vector>
+#include "../loadsave.h"
+
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
namespace gambatte {
+class File;
+
class Mbc {
public:
virtual ~Mbc() {}
@@ -36,13 +43,19 @@ 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 loadOrSave(loadsave& state) = 0;
};
class Cartridge {
struct AddrData {
- unsigned long addr;
+ unsigned addr;
unsigned char data;
- AddrData(unsigned long addr, unsigned data) : addr(addr), data(data) {}
+ AddrData(unsigned addr, unsigned data) : addr(addr), data(data) {}
+ AddrData() {}
+ void loadOrSave(loadsave& state) {
+ state(addr);
+ state(data);
+ }
};
MemPtrs memptrs;
@@ -51,14 +64,22 @@ class Cartridge {
std::string defaultSaveBasePath;
std::string saveDir;
std::vector<AddrData> ggUndoList;
-
+ bool memoryCartridge;
+ time_t memoryCartridgeRtcBase;
+ std::vector<unsigned char> memoryCartridgeSram;
+
void applyGameGenie(const std::string &code);
-
+
+ LoadRes loadROM(File* rom, const bool forceDmg, const bool multicartCompat, const std::string& filename);
+ void clearMemorySavedData();
public:
+ Cartridge(time_t (**_getCurrentTime)());
void setStatePtrs(SaveState &);
void saveState(SaveState &) const;
void loadState(const SaveState &);
+ void loadOrSave(loadsave& state);
+
bool loaded() const { return mbc.get(); }
const unsigned char * rmem(unsigned area) const { return memptrs.rmem(area); }
@@ -88,9 +109,17 @@ public:
const std::string saveBasePath() const;
void setSaveDir(const std::string &dir);
LoadRes loadROM(const std::string &romfile, bool forceDmg, bool multicartCompat);
+ LoadRes loadROM(const unsigned char* image, size_t isize, bool forceDmg, bool multicartCompat);
char const * romTitle() const { return reinterpret_cast<const char *>(memptrs.romdata() + 0x134); }
class PakInfo const pakInfo(bool multicartCompat) const;
void setGameGenie(const std::string &codes);
+
+ void setRtcBase(time_t time) { rtc.setBaseTime(time); }
+ time_t getRtcBase() { return rtc.getBaseTime(); }
+ std::pair<unsigned char*, size_t> getWorkRam();
+ std::pair<unsigned char*, size_t> getSaveRam();
+ std::pair<unsigned char*, size_t> getVideoRam();
+
};
}
diff --git a/libgambatte/src/mem/memptrs.cpp b/libgambatte/src/mem/memptrs.cpp
index fab2e35..d61cefd 100644
--- a/libgambatte/src/mem/memptrs.cpp
+++ b/libgambatte/src/mem/memptrs.cpp
@@ -20,6 +20,10 @@
#include <algorithm>
#include <cstring>
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
namespace gambatte {
MemPtrs::MemPtrs()
@@ -34,7 +38,11 @@ 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_size = 0x4000 + rombanks * 0x4000ul + 0x4000 + rambanks * 0x2000ul + wrambanks * 0x1000ul + 0x4000;
+ memchunk_ = new unsigned char[memchunk_size];
+
+ //FIXME: Make this random.
+ memset(memchunk_, 0, memchunk_size);
romdata_[0] = romdata();
rambankdata_ = romdata_[0] + rombanks * 0x4000ul + 0x4000;
@@ -136,4 +144,26 @@ void MemPtrs::disconnectOamDmaAreas() {
}
}
+void MemPtrs::loadOrSave(loadsave& state)
+{
+ state(memchunk_, 0x4000);
+ state(romdataend(), memchunk_size - (romdataend() - memchunk_));
+ int oamDmaSrc_2 = oamDmaSrc_;
+ state(oamDmaSrc_2);
+ oamDmaSrc_ = (OamDmaSrc)oamDmaSrc_2;
+ //Rmem is constant.
+ for(unsigned i = 0; i < 0x10; i++)
+ state(wmem_[i], memchunk_);
+ for(unsigned i = 0; i < 0x10; i++)
+ state(rmem_[i], memchunk_);
+ state(romdata_[0], memchunk_);
+ state(romdata_[1], memchunk_);
+ state(rambankdata_, memchunk_);
+ state(rsrambankptr_, memchunk_);
+ state(wsrambankptr_, memchunk_);
+ state(wramdataend_, memchunk_);
+ state(vrambankptr_, memchunk_);
+ //memchunk_size is cart constant, not saved.
+}
+
}
diff --git a/libgambatte/src/mem/memptrs.h b/libgambatte/src/mem/memptrs.h
index b4e1abe..5c6406e 100644
--- a/libgambatte/src/mem/memptrs.h
+++ b/libgambatte/src/mem/memptrs.h
@@ -19,6 +19,12 @@
#ifndef MEMPTRS_H
#define MEMPTRS_H
+#include "../loadsave.h"
+
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
namespace gambatte {
enum OamDmaSrc { OAM_DMA_SRC_ROM, OAM_DMA_SRC_SRAM, OAM_DMA_SRC_VRAM,
@@ -28,15 +34,16 @@ class MemPtrs {
const unsigned char *rmem_[0x10];
unsigned char *wmem_[0x10];
+ unsigned char *memchunk_;
unsigned char *romdata_[2];
unsigned char *wramdata_[2];
+ unsigned char *rambankdata_;
+ unsigned char *wramdataend_;
unsigned char *vrambankptr_;
unsigned char *rsrambankptr_;
unsigned char *wsrambankptr_;
- unsigned char *memchunk_;
- unsigned char *rambankdata_;
- unsigned char *wramdataend_;
-
+ unsigned memchunk_size;
+
OamDmaSrc oamDmaSrc_;
MemPtrs(const MemPtrs &);
@@ -74,6 +81,8 @@ public:
void setVrambank(unsigned bank) { vrambankptr_ = vramdata() + bank * 0x2000ul - 0x8000; }
void setWrambank(unsigned bank);
void setOamDmaSrc(OamDmaSrc oamDmaSrc);
+
+ void loadOrSave(loadsave& state);
};
inline bool isCgb(const MemPtrs &memptrs) {
diff --git a/libgambatte/src/mem/rtc.cpp b/libgambatte/src/mem/rtc.cpp
index a834248..59f8fdc 100644
--- a/libgambatte/src/mem/rtc.cpp
+++ b/libgambatte/src/mem/rtc.cpp
@@ -19,9 +19,13 @@
#include "rtc.h"
#include "../savestate.h"
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
namespace gambatte {
-Rtc::Rtc()
+Rtc::Rtc(time_t (**_getCurrentTime)())
: activeData(NULL),
activeSet(NULL),
baseTime(0),
@@ -33,12 +37,13 @@ Rtc::Rtc()
dataM(0),
dataS(0),
enabled(false),
- lastLatchData(false)
+ lastLatchData(false),
+ getCurrentTime(_getCurrentTime)
{
}
void Rtc::doLatch() {
- std::time_t tmp = ((dataDh & 0x40) ? haltTime : std::time(0)) - baseTime;
+ std::time_t tmp = ((dataDh & 0x40) ? haltTime : (*getCurrentTime)()) - baseTime;
while (tmp > 0x1FF * 86400) {
baseTime += 0x1FF * 86400;
@@ -113,44 +118,76 @@ void Rtc::loadState(const SaveState &state) {
}
void Rtc::setDh(const unsigned new_dh) {
- const std::time_t unixtime = (dataDh & 0x40) ? haltTime : std::time(0);
+ const std::time_t unixtime = (dataDh & 0x40) ? haltTime : (*getCurrentTime)();
const std::time_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 = std::time(0);
+ haltTime = (*getCurrentTime)();
else
- baseTime += std::time(0) - haltTime;
+ baseTime += (*getCurrentTime)() - haltTime;
}
}
void Rtc::setDl(const unsigned new_lowdays) {
- const std::time_t unixtime = (dataDh & 0x40) ? haltTime : std::time(0);
+ const std::time_t unixtime = (dataDh & 0x40) ? haltTime : (*getCurrentTime)();
const std::time_t old_lowdays = ((unixtime - baseTime) / 86400) & 0xFF;
baseTime += old_lowdays * 86400;
baseTime -= new_lowdays * 86400;
}
void Rtc::setH(const unsigned new_hours) {
- const std::time_t unixtime = (dataDh & 0x40) ? haltTime : std::time(0);
+ const std::time_t unixtime = (dataDh & 0x40) ? haltTime : (*getCurrentTime)();
const std::time_t old_hours = ((unixtime - baseTime) / 3600) % 24;
baseTime += old_hours * 3600;
baseTime -= new_hours * 3600;
}
void Rtc::setM(const unsigned new_minutes) {
- const std::time_t unixtime = (dataDh & 0x40) ? haltTime : std::time(0);
+ const std::time_t unixtime = (dataDh & 0x40) ? haltTime : (*getCurrentTime)();
const std::time_t old_minutes = ((unixtime - baseTime) / 60) % 60;
baseTime += old_minutes * 60;
baseTime -= new_minutes * 60;
}
void Rtc::setS(const unsigned new_seconds) {
- const std::time_t unixtime = (dataDh & 0x40) ? haltTime : std::time(0);
+ const std::time_t unixtime = (dataDh & 0x40) ? haltTime : (*getCurrentTime)();
baseTime += (unixtime - baseTime) % 60;
baseTime -= new_seconds;
}
+void Rtc::loadOrSave(loadsave& state)
+{
+ state.startEnumeration();
+ state.enumerate<unsigned char*>(activeData, NULL, 0);
+ state.enumerate(activeData, &dataDh, 1);
+ state.enumerate(activeData, &dataDl, 2);
+ state.enumerate(activeData, &dataH, 3);
+ state.enumerate(activeData, &dataM, 4);
+ state.enumerate(activeData, &dataS, 5);
+ state.endEnumeration();
+
+ state.startEnumeration();
+ state.enumerate<void (gambatte::Rtc::*)(unsigned int)>(activeSet, NULL, 0);
+ state.enumerate(activeSet, &Rtc::setDh, 1);
+ state.enumerate(activeSet, &Rtc::setDl, 2);
+ state.enumerate(activeSet, &Rtc::setH, 3);
+ state.enumerate(activeSet, &Rtc::setM, 4);
+ state.enumerate(activeSet, &Rtc::setS, 5);
+ state.endEnumeration();
+
+ state.time(baseTime);
+ state.time(haltTime);
+ state(index);
+ state(dataDh);
+ state(dataDl);
+ state(dataH);
+ state(dataM);
+ state(dataS);
+ state(enabled);
+ state(lastLatchData);
+}
+
}
diff --git a/libgambatte/src/mem/rtc.h b/libgambatte/src/mem/rtc.h
index 022d235..ac967b1 100644
--- a/libgambatte/src/mem/rtc.h
+++ b/libgambatte/src/mem/rtc.h
@@ -20,6 +20,12 @@
#define RTC_H
#include <ctime>
+#include "../loadsave.h"
+
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
namespace gambatte {
@@ -39,6 +45,7 @@ private:
unsigned char dataS;
bool enabled;
bool lastLatchData;
+ time_t (**getCurrentTime)();
void doLatch();
void doSwapActive();
@@ -49,7 +56,7 @@ private:
void setS(unsigned new_seconds);
public:
- Rtc();
+ Rtc(time_t (**_getCurrentTime)());
const unsigned char* getActive() const { return activeData; }
std::time_t getBaseTime() const { return baseTime; }
@@ -84,6 +91,8 @@ public:
(this->*activeSet)(data);
*activeData = data;
}
+
+ void loadOrSave(loadsave& state);
};
}
diff --git a/libgambatte/src/memory.cpp b/libgambatte/src/memory.cpp
index ed3c66b..917d818 100644
--- a/libgambatte/src/memory.cpp
+++ b/libgambatte/src/memory.cpp
@@ -23,9 +23,13 @@
#include "savestate.h"
#include <cstring>
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
namespace gambatte {
-Memory::Memory(const Interrupter &interrupter_in)
+Memory::Memory(const Interrupter &interrupter_in, time_t (**_getCurrentTime)())
: getInput(0),
divLastUpdate(0),
lastOamDmaUpdate(DISABLED_TIME),
@@ -35,7 +39,8 @@ Memory::Memory(const Interrupter &interrupter_in)
dmaDestination(0),
oamDmaPos(0xFE),
serialCnt(0),
- blanklcd(false)
+ blanklcd(false),
+ cart(_getCurrentTime)
{
intreq.setEventTime<BLIT>(144*456ul);
intreq.setEventTime<END>(0);
@@ -49,7 +54,7 @@ void Memory::setStatePtrs(SaveState &state) {
sound.setStatePtrs(state);
}
-unsigned long Memory::saveState(SaveState &state, unsigned long cycleCounter) {
+unsigned Memory::saveState(SaveState &state, unsigned cycleCounter) {
cycleCounter = resetCounters(cycleCounter);
nontrivial_ff_read(0xFF05, cycleCounter);
nontrivial_ff_read(0xFF0F, cycleCounter);
@@ -72,7 +77,7 @@ unsigned long Memory::saveState(SaveState &state, unsigned long cycleCounter) {
return cycleCounter;
}
-static inline int serialCntFrom(const unsigned long cyclesUntilDone, const bool cgbFast) {
+static inline int serialCntFrom(const unsigned cyclesUntilDone, const bool cgbFast) {
return cgbFast ? (cyclesUntilDone + 0xF) >> 4 : (cyclesUntilDone + 0x1FF) >> 9;
}
@@ -113,14 +118,14 @@ void Memory::loadState(const SaveState &state) {
std::memset(cart.vramdata() + 0x2000, 0, 0x2000);
}
-void Memory::setEndtime(const unsigned long cycleCounter, const unsigned long inc) {
+void Memory::setEndtime(const unsigned cycleCounter, const unsigned inc) {
if (intreq.eventTime(BLIT) <= cycleCounter)
intreq.setEventTime<BLIT>(intreq.eventTime(BLIT) + (70224 << isDoubleSpeed()));
intreq.setEventTime<END>(cycleCounter + (inc << isDoubleSpeed()));
}
-void Memory::updateSerial(const unsigned long cc) {
+void Memory::updateSerial(const unsigned cc) {
if (intreq.eventTime(SERIAL) != DISABLED_TIME) {
if (intreq.eventTime(SERIAL) <= cc) {
ioamhram[0x101] = (((ioamhram[0x101] + 1) << serialCnt) - 1) & 0xFF;
@@ -135,18 +140,18 @@ void Memory::updateSerial(const unsigned long cc) {
}
}
-void Memory::updateTimaIrq(const unsigned long cc) {
+void Memory::updateTimaIrq(const unsigned cc) {
while (intreq.eventTime(TIMA) <= cc)
tima.doIrqEvent(TimaInterruptRequester(intreq));
}
-void Memory::updateIrqs(const unsigned long cc) {
+void Memory::updateIrqs(const unsigned cc) {
updateSerial(cc);
updateTimaIrq(cc);
display.update(cc);
}
-unsigned long Memory::event(unsigned long cycleCounter) {
+unsigned Memory::event(unsigned cycleCounter) {
if (lastOamDmaUpdate != DISABLED_TIME)
updateOamDma(cycleCounter);
@@ -167,10 +172,10 @@ unsigned long Memory::event(unsigned long cycleCounter) {
case BLIT:
{
const bool lcden = ioamhram[0x140] >> 7 & 1;
- unsigned long blitTime = intreq.eventTime(BLIT);
+ unsigned blitTime = intreq.eventTime(BLIT);
if (lcden | blanklcd) {
- display.updateScreen(blanklcd, cycleCounter);
+ display.updateScreen(blanklcd, cycleCounter, videoBuf_, pitch_);
intreq.setEventTime<BLIT>(DISABLED_TIME);
intreq.setEventTime<END>(DISABLED_TIME);
@@ -188,7 +193,7 @@ unsigned long Memory::event(unsigned long cycleCounter) {
break;
case OAM:
intreq.setEventTime<OAM>(lastOamDmaUpdate == DISABLED_TIME ?
- static_cast<unsigned long>(DISABLED_TIME) : intreq.eventTime(OAM) + 0xA0 * 4);
+ static_cast<unsigned>(DISABLED_TIME) : intreq.eventTime(OAM) + 0xA0 * 4);
break;
case DMA:
{
@@ -200,7 +205,7 @@ unsigned long Memory::event(unsigned long cycleCounter) {
ackDmaReq(&intreq);
- if ((static_cast<unsigned long>(dmaDest) + length) & 0x10000) {
+ if ((static_cast<unsigned>(dmaDest) + length) & 0x10000) {
length = 0x10000 - dmaDest;
ioamhram[0x155] |= 0x80;
}
@@ -211,7 +216,7 @@ unsigned long Memory::event(unsigned long cycleCounter) {
dmaLength = 0;
{
- unsigned long lOamDmaUpdate = lastOamDmaUpdate;
+ unsigned lOamDmaUpdate = lastOamDmaUpdate;
lastOamDmaUpdate = DISABLED_TIME;
while (length--) {
@@ -292,7 +297,7 @@ unsigned long Memory::event(unsigned long cycleCounter) {
return cycleCounter;
}
-unsigned long Memory::stop(unsigned long cycleCounter) {
+unsigned Memory::stop(unsigned cycleCounter) {
cycleCounter += 4 << isDoubleSpeed();
if (ioamhram[0x14D] & isCgb()) {
@@ -315,31 +320,31 @@ unsigned long Memory::stop(unsigned long cycleCounter) {
return cycleCounter;
}
-static void decCycles(unsigned long &counter, const unsigned long dec) {
+static void decCycles(unsigned &counter, const unsigned dec) {
if (counter != DISABLED_TIME)
counter -= dec;
}
-void Memory::decEventCycles(const MemEventId eventId, const unsigned long dec) {
+void Memory::decEventCycles(const MemEventId eventId, const unsigned dec) {
if (intreq.eventTime(eventId) != DISABLED_TIME)
intreq.setEventTime(eventId, intreq.eventTime(eventId) - dec);
}
-unsigned long Memory::resetCounters(unsigned long cycleCounter) {
+unsigned Memory::resetCounters(unsigned cycleCounter) {
if (lastOamDmaUpdate != DISABLED_TIME)
updateOamDma(cycleCounter);
updateIrqs(cycleCounter);
- const unsigned long oldCC = cycleCounter;
+ const unsigned oldCC = cycleCounter;
{
- const unsigned long divinc = (cycleCounter - divLastUpdate) >> 8;
+ const unsigned divinc = (cycleCounter - divLastUpdate) >> 8;
ioamhram[0x104] = (ioamhram[0x104] + divinc) & 0xFF;
divLastUpdate += divinc << 8;
}
- const unsigned long dec = cycleCounter < 0x10000 ? 0 : (cycleCounter & ~0x7FFFul) - 0x8000;
+ const unsigned dec = cycleCounter < 0x10000 ? 0 : (cycleCounter & ~0x7FFFul) - 0x8000;
decCycles(divLastUpdate, dec);
decCycles(lastOamDmaUpdate, dec);
@@ -378,7 +383,7 @@ void Memory::updateInput() {
ioamhram[0x100] &= button;
}
-void Memory::updateOamDma(const unsigned long cycleCounter) {
+void Memory::updateOamDma(const unsigned cycleCounter) {
const unsigned char *const oamDmaSrc = oamDmaSrcPtr();
unsigned cycles = (cycleCounter - lastOamDmaUpdate) >> 2;
@@ -426,17 +431,17 @@ const unsigned char * Memory::oamDmaSrcPtr() const {
return ioamhram[0x146] == 0xFF && !isCgb() ? oamDmaSrcZero() : cart.rdisabledRam();
}
-void Memory::startOamDma(const unsigned long cycleCounter) {
+void Memory::startOamDma(const unsigned cycleCounter) {
display.oamChange(cart.rdisabledRam(), cycleCounter);
}
-void Memory::endOamDma(const unsigned long cycleCounter) {
+void Memory::endOamDma(const unsigned cycleCounter) {
oamDmaPos = 0xFE;
cart.setOamDmaSrc(OAM_DMA_SRC_OFF);
display.oamChange(ioamhram, cycleCounter);
}
-unsigned Memory::nontrivial_ff_read(const unsigned P, const unsigned long cycleCounter) {
+unsigned Memory::nontrivial_ff_read(const unsigned P, const unsigned cycleCounter) {
if (lastOamDmaUpdate != DISABLED_TIME)
updateOamDma(cycleCounter);
@@ -450,7 +455,7 @@ unsigned Memory::nontrivial_ff_read(const unsigned P, const unsigned long cycleC
break;
case 0x04:
{
- const unsigned long divcycles = (cycleCounter - divLastUpdate) >> 8;
+ const unsigned divcycles = (cycleCounter - divLastUpdate) >> 8;
ioamhram[0x104] = (ioamhram[0x104] + divcycles) & 0xFF;
divLastUpdate += divcycles << 8;
}
@@ -529,7 +534,7 @@ static bool isInOamDmaConflictArea(const OamDmaSrc oamDmaSrc, const unsigned add
return addr < a[oamDmaSrc].areaUpper && addr - a[oamDmaSrc].exceptAreaLower >= a[oamDmaSrc].exceptAreaWidth;
}
-unsigned Memory::nontrivial_read(const unsigned P, const unsigned long cycleCounter) {
+unsigned Memory::nontrivial_read(const unsigned P, const unsigned cycleCounter) {
if (P < 0xFF80) {
if (lastOamDmaUpdate != DISABLED_TIME) {
updateOamDma(cycleCounter);
@@ -568,7 +573,7 @@ unsigned Memory::nontrivial_read(const unsigned P, const unsigned long cycleCoun
return ioamhram[P - 0xFE00];
}
-void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned long cycleCounter) {
+void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned cycleCounter) {
if (lastOamDmaUpdate != DISABLED_TIME)
updateOamDma(cycleCounter);
@@ -585,7 +590,7 @@ void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned
serialCnt = 8;
intreq.setEventTime<SERIAL>((data & 0x81) == 0x81
? (data & isCgb() * 2 ? (cycleCounter & ~0x7ul) + 0x10 * 8 : (cycleCounter & ~0xFFul) + 0x200 * 8)
- : static_cast<unsigned long>(DISABLED_TIME));
+ : static_cast<unsigned>(DISABLED_TIME));
data |= 0x7E - isCgb() * 2;
break;
@@ -946,7 +951,7 @@ void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned
ioamhram[P - 0xFE00] = data;
}
-void Memory::nontrivial_write(const unsigned P, const unsigned data, const unsigned long cycleCounter) {
+void Memory::nontrivial_write(const unsigned P, const unsigned data, const unsigned cycleCounter) {
if (lastOamDmaUpdate != DISABLED_TIME) {
updateOamDma(cycleCounter);
@@ -983,24 +988,57 @@ void Memory::nontrivial_write(const unsigned P, const unsigned data, const unsig
ioamhram[P - 0xFE00] = data;
}
+void Memory::postLoadRom()
+{
+ sound.init(cart.isCgb());
+ display.reset(ioamhram, cart.vramdata(), cart.isCgb());
+ interrupter.setGameShark(std::string());
+}
+
LoadRes Memory::loadROM(std::string const &romfile, bool const forceDmg, bool const multicartCompat) {
if (LoadRes const fail = cart.loadROM(romfile, forceDmg, multicartCompat))
return fail;
- sound.init(cart.isCgb());
- display.reset(ioamhram, cart.vramdata(), cart.isCgb());
- interrupter.setGameShark(std::string());
+ postLoadRom();
return LOADRES_OK;
}
-unsigned Memory::fillSoundBuffer(const unsigned long cycleCounter) {
+LoadRes Memory::loadROM(const unsigned char* image, size_t isize, const bool forceDmg, const bool multicartCompat) {
+ if (LoadRes fail = cart.loadROM(image, isize, forceDmg, multicartCompat))
+ return fail;
+
+ postLoadRom();
+
+ return LOADRES_OK;
+}
+
+unsigned Memory::fillSoundBuffer(const unsigned cycleCounter) {
sound.generate_samples(cycleCounter, isDoubleSpeed());
return sound.fillBuffer();
}
-void Memory::setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned long rgb32) {
+void Memory::setDmgPaletteColor(unsigned palNum, unsigned colorNum, uint_least32_t rgb32) {
display.setDmgPaletteColor(palNum, colorNum, rgb32);
}
+void Memory::loadOrSave(loadsave& state)
+{
+ state(ioamhram, 0x200);
+ //Don't save getInput, it has no state.
+ state(divLastUpdate);
+ state(lastOamDmaUpdate);
+ intreq.loadOrSave(state);
+ cart.loadOrSave(state);
+ tima.loadOrSave(state);
+ display.loadOrSave(state);
+ sound.loadOrSave(state);
+ interrupter.loadOrSave(state);
+ state(dmaSource);
+ state(dmaDestination);
+ state(oamDmaPos);
+ state(serialCnt);
+ state(blanklcd);
+}
+
}
diff --git a/libgambatte/src/memory.h b/libgambatte/src/memory.h
index 02ed524..0278b33 100644
--- a/libgambatte/src/memory.h
+++ b/libgambatte/src/memory.h
@@ -19,6 +19,10 @@
#ifndef MEMORY_H
#define MEMORY_H
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
#include "mem/cartridge.h"
#include "interrupter.h"
#include "pakinfo.h"
@@ -35,8 +39,8 @@ class Memory {
unsigned char ioamhram[0x200];
InputGetter *getInput;
- unsigned long divLastUpdate;
- unsigned long lastOamDmaUpdate;
+ unsigned divLastUpdate;
+ unsigned lastOamDmaUpdate;
InterruptRequester intreq;
Tima tima;
@@ -50,35 +54,41 @@ class Memory {
unsigned char serialCnt;
bool blanklcd;
+ uint_least32_t* videoBuf_;
+ unsigned pitch_;
+
void updateInput();
- void decEventCycles(MemEventId eventId, unsigned long dec);
+ void decEventCycles(MemEventId eventId, unsigned dec);
void oamDmaInitSetup();
- void updateOamDma(unsigned long cycleCounter);
- void startOamDma(unsigned long cycleCounter);
- void endOamDma(unsigned long cycleCounter);
+ void updateOamDma(unsigned cycleCounter);
+ void startOamDma(unsigned cycleCounter);
+ void endOamDma(unsigned cycleCounter);
const unsigned char * oamDmaSrcPtr() const;
- unsigned nontrivial_ff_read(unsigned P, unsigned long cycleCounter);
- unsigned nontrivial_read(unsigned P, unsigned long cycleCounter);
- void nontrivial_ff_write(unsigned P, unsigned data, unsigned long cycleCounter);
- void nontrivial_write(unsigned P, unsigned data, unsigned long cycleCounter);
+ unsigned nontrivial_ff_read(unsigned P, unsigned cycleCounter);
+ unsigned nontrivial_read(unsigned P, unsigned cycleCounter);
+ void nontrivial_ff_write(unsigned P, unsigned data, unsigned cycleCounter);
+ void nontrivial_write(unsigned P, unsigned data, unsigned cycleCounter);
- void updateSerial(unsigned long cc);
- void updateTimaIrq(unsigned long cc);
- void updateIrqs(unsigned long cc);
+ void updateSerial(unsigned cc);
+ void updateTimaIrq(unsigned cc);
+ void updateIrqs(unsigned cc);
bool isDoubleSpeed() const { return display.isDoubleSpeed(); }
+ void postLoadRom();
public:
- explicit Memory(const Interrupter &interrupter);
+ explicit Memory(const Interrupter &interrupter, time_t (**_getCurrentTime)());
bool loaded() const { return cart.loaded(); }
char const * romTitle() const { return cart.romTitle(); }
PakInfo const pakInfo(bool multicartCompat) const { return cart.pakInfo(multicartCompat); }
+ void loadOrSave(loadsave& state);
+
void setStatePtrs(SaveState &state);
- unsigned long saveState(SaveState &state, unsigned long cc);
+ unsigned saveState(SaveState &state, unsigned cc);
void loadState(const SaveState &state/*, unsigned long oldCc*/);
void loadSavedata() { cart.loadSavedata(); }
void saveSavedata() { cart.saveSavedata(); }
@@ -88,67 +98,77 @@ public:
display.setOsdElement(osdElement);
}
- unsigned long stop(unsigned long cycleCounter);
+ unsigned stop(unsigned cycleCounter);
bool isCgb() const { return display.isCgb(); }
bool ime() const { return intreq.ime(); }
bool halted() const { return intreq.halted(); }
- unsigned long nextEventTime() const { return intreq.minEventTime(); }
+ unsigned nextEventTime() const { return intreq.minEventTime(); }
bool isActive() const { return intreq.eventTime(END) != DISABLED_TIME; }
- long cyclesSinceBlit(const unsigned long cc) const {
- return cc < intreq.eventTime(BLIT) ? -1 : static_cast<long>((cc - intreq.eventTime(BLIT)) >> isDoubleSpeed());
+ signed cyclesSinceBlit(const unsigned cc) const {
+ return cc < intreq.eventTime(BLIT) ? -1 : static_cast<signed>((cc - intreq.eventTime(BLIT)) >> isDoubleSpeed());
}
void halt() { intreq.halt(); }
- void ei(unsigned long cycleCounter) { if (!ime()) { intreq.ei(cycleCounter); } }
+ void ei(unsigned cycleCounter) { if (!ime()) { intreq.ei(cycleCounter); } }
void di() { intreq.di(); }
- unsigned ff_read(const unsigned P, const unsigned long cycleCounter) {
+ unsigned ff_read(const unsigned P, const unsigned cycleCounter) {
return P < 0xFF80 ? nontrivial_ff_read(P, cycleCounter) : ioamhram[P - 0xFE00];
}
- unsigned read(const unsigned P, const unsigned long cycleCounter) {
+ unsigned read(const unsigned P, const unsigned cycleCounter) {
return cart.rmem(P >> 12) ? cart.rmem(P >> 12)[P] : nontrivial_read(P, cycleCounter);
}
- void write(const unsigned P, const unsigned data, const unsigned long cycleCounter) {
+ void write(const unsigned P, const unsigned data, const unsigned cycleCounter) {
if (cart.wmem(P >> 12)) {
cart.wmem(P >> 12)[P] = data;
} else
nontrivial_write(P, data, cycleCounter);
}
- void ff_write(const unsigned P, const unsigned data, const unsigned long cycleCounter) {
+ void ff_write(const unsigned P, const unsigned data, const unsigned cycleCounter) {
if (P - 0xFF80u < 0x7Fu) {
ioamhram[P - 0xFE00] = data;
} else
nontrivial_ff_write(P, data, cycleCounter);
}
- unsigned long event(unsigned long cycleCounter);
- unsigned long resetCounters(unsigned long cycleCounter);
+ unsigned event(unsigned cycleCounter);
+ unsigned resetCounters(unsigned cycleCounter);
LoadRes loadROM(const std::string &romfile, bool forceDmg, bool multicartCompat);
+ LoadRes loadROM(const unsigned char* image, size_t isize, bool forceDmg, bool multicartCompat);
void setSaveDir(const std::string &dir) { cart.setSaveDir(dir); }
void setInputGetter(InputGetter *getInput) {
this->getInput = getInput;
}
- void setEndtime(unsigned long cc, unsigned long inc);
+ void setEndtime(unsigned cc, unsigned inc);
void setSoundBuffer(uint_least32_t *const buf) { sound.setBuffer(buf); }
- unsigned fillSoundBuffer(unsigned long cc);
+ unsigned fillSoundBuffer(unsigned cc);
void setVideoBuffer(uint_least32_t *const videoBuf, const int pitch) {
- display.setVideoBuffer(videoBuf, pitch);
+ videoBuf_ = videoBuf;
+ pitch_ = pitch;
}
- void setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned long rgb32);
+ void setDmgPaletteColor(unsigned palNum, unsigned colorNum, uint_least32_t rgb32);
void setGameGenie(const std::string &codes) { cart.setGameGenie(codes); }
void setGameShark(const std::string &codes) { interrupter.setGameShark(codes); }
+
+ void setRtcBase(time_t time) { cart.setRtcBase(time); }
+ time_t getRtcBase() { return cart.getRtcBase(); }
+ std::pair<unsigned char*, size_t> getWorkRam() { return cart.getWorkRam(); }
+ std::pair<unsigned char*, size_t> getSaveRam() { return cart.getSaveRam(); }
+ std::pair<unsigned char*, size_t> getIoRam() { return std::make_pair(ioamhram, sizeof(ioamhram)); }
+ std::pair<unsigned char*, size_t> getVideoRam() { return cart.getVideoRam(); };
+
};
}
diff --git a/libgambatte/src/minkeeper.h b/libgambatte/src/minkeeper.h
index bf2de9c..794f558 100644
--- a/libgambatte/src/minkeeper.h
+++ b/libgambatte/src/minkeeper.h
@@ -19,7 +19,12 @@
#ifndef MINKEEPER_H
#define MINKEEPER_H
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
#include <algorithm>
+#include "loadsave.h"
namespace MinKeeperUtil {
template<int n> struct CeiledLog2 { enum { R = 1 + CeiledLog2<(n + 1) / 2>::R }; };
@@ -81,36 +86,43 @@ class MinKeeper {
};
static UpdateValueLut updateValueLut;
- unsigned long values[ids];
- unsigned long minValue_;
+ unsigned values[ids];
+ unsigned minValue_;
int a[Sum<LEVELS>::R];
template<int id> static void updateValue(MinKeeper<ids> &m);
public:
- explicit MinKeeper(unsigned long initValue = 0xFFFFFFFF);
-
+ explicit MinKeeper(unsigned initValue = 0xFFFFFFFFULL);
+
+ void loadOrSave(gambatte::loadsave& state) {
+ state(values, ids);
+ state(minValue_);
+ //updateValueLut is constant for our purposes.
+ state(a, Sum<LEVELS>::R);
+ }
+
int min() const { return a[0]; }
- unsigned long minValue() const { return minValue_; }
+ unsigned minValue() const { return minValue_; }
template<int id>
- void setValue(const unsigned long cnt) {
+ void setValue(const unsigned cnt) {
values[id] = cnt;
updateValue<id / 2>(*this);
}
- void setValue(const int id, const unsigned long cnt) {
+ void setValue(const int id, const unsigned cnt) {
values[id] = cnt;
updateValueLut.call(id >> 1, *this);
}
- unsigned long value(const int id) const { return values[id]; }
+ unsigned value(const int id) const { return values[id]; }
};
template<int ids> typename MinKeeper<ids>::UpdateValueLut MinKeeper<ids>::updateValueLut;
template<int ids>
-MinKeeper<ids>::MinKeeper(const unsigned long initValue) {
+MinKeeper<ids>::MinKeeper(const unsigned initValue) {
std::fill(values, values + ids, initValue);
for (int i = 0; i < Num<LEVELS-1>::R; ++i) {
diff --git a/libgambatte/src/savestate.h b/libgambatte/src/savestate.h
index 6f91124..9477c75 100644
--- a/libgambatte/src/savestate.h
+++ b/libgambatte/src/savestate.h
@@ -19,6 +19,12 @@
#ifndef SAVESTATE_H
#define SAVESTATE_H
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
+#include <ctime>
+
namespace gambatte {
class SaverList;
@@ -27,20 +33,20 @@ struct SaveState {
template<typename T>
class Ptr {
T *ptr;
- unsigned long sz;
+ unsigned sz;
public:
Ptr() : ptr(0), sz(0) {}
const T* get() const { return ptr; }
- unsigned long getSz() const { return sz; }
- void set(T *ptr, const unsigned long sz) { this->ptr = ptr; this->sz = sz; }
+ unsigned getSz() const { return sz; }
+ void set(T *ptr, const unsigned sz) { this->ptr = ptr; this->sz = sz; }
friend class SaverList;
- friend void setInitState(SaveState &, bool, bool);
+ friend void setInitState(SaveState &, bool, bool, time_t);
};
struct CPU {
- unsigned long cycleCounter;
+ unsigned cycleCounter;
unsigned short PC;
unsigned short SP;
unsigned char A;
@@ -59,13 +65,13 @@ struct SaveState {
Ptr<unsigned char> sram;
Ptr<unsigned char> wram;
Ptr<unsigned char> ioamhram;
- unsigned long divLastUpdate;
- unsigned long timaLastUpdate;
- unsigned long tmatime;
- unsigned long nextSerialtime;
- unsigned long lastOamDmaUpdate;
- unsigned long minIntTime;
- unsigned long unhaltTime;
+ unsigned divLastUpdate;
+ unsigned timaLastUpdate;
+ unsigned tmatime;
+ unsigned nextSerialtime;
+ unsigned lastOamDmaUpdate;
+ unsigned minIntTime;
+ unsigned unhaltTime;
unsigned short rombank;
unsigned short dmaSource;
unsigned short dmaDestination;
@@ -85,8 +91,8 @@ struct SaveState {
Ptr<unsigned char> oamReaderBuf;
Ptr<bool> oamReaderSzbuf;
- unsigned long videoCycles;
- unsigned long enableDisplayM0Time;
+ unsigned videoCycles;
+ unsigned enableDisplayM0Time;
unsigned short lastM0Time;
unsigned short nextM0Irq;
unsigned short tileword;
@@ -115,24 +121,24 @@ struct SaveState {
struct SPU {
struct Duty {
- unsigned long nextPosUpdate;
+ unsigned nextPosUpdate;
unsigned char nr3;
unsigned char pos;
};
struct Env {
- unsigned long counter;
+ unsigned counter;
unsigned char volume;
};
struct LCounter {
- unsigned long counter;
+ unsigned counter;
unsigned short lengthCounter;
};
struct {
struct {
- unsigned long counter;
+ unsigned counter;
unsigned short shadow;
unsigned char nr0;
bool negging;
@@ -155,8 +161,8 @@ struct SaveState {
struct {
Ptr<unsigned char> waveRam;
LCounter lcounter;
- unsigned long waveCounter;
- unsigned long lastReadTime;
+ unsigned waveCounter;
+ unsigned lastReadTime;
unsigned char nr3;
unsigned char nr4;
unsigned char wavePos;
@@ -166,7 +172,7 @@ struct SaveState {
struct {
struct {
- unsigned long counter;
+ unsigned counter;
unsigned short reg;
} lfsr;
Env env;
@@ -175,12 +181,12 @@ struct SaveState {
bool master;
} ch4;
- unsigned long cycleCounter;
+ unsigned cycleCounter;
} spu;
struct RTC {
- unsigned long baseTime;
- unsigned long haltTime;
+ unsigned baseTime;
+ unsigned haltTime;
unsigned char dataDh;
unsigned char dataDl;
unsigned char dataH;
diff --git a/libgambatte/src/sound.cpp b/libgambatte/src/sound.cpp
index 8c1e7ae..366a2c4 100644
--- a/libgambatte/src/sound.cpp
+++ b/libgambatte/src/sound.cpp
@@ -21,6 +21,10 @@
#include <cstring>
#include <algorithm>
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
/*
Frame Sequencer
@@ -89,7 +93,7 @@ void PSG::loadState(const SaveState &state) {
enabled = state.mem.ioamhram.get()[0x126] >> 7 & 1;
}
-void PSG::accumulate_channels(const unsigned long cycles) {
+void PSG::accumulate_channels(const unsigned cycles) {
uint_least32_t *const buf = buffer + bufferPos;
std::memset(buf, 0, cycles * sizeof(uint_least32_t));
@@ -99,17 +103,16 @@ void PSG::accumulate_channels(const unsigned long cycles) {
ch4.update(buf, soVol, cycles);
}
-void PSG::generate_samples(const unsigned long cycleCounter, const unsigned doubleSpeed) {
- const unsigned long cycles = (cycleCounter - lastUpdate) >> (1 + doubleSpeed);
+void PSG::generate_samples(const unsigned cycleCounter, const unsigned doubleSpeed) {
+ const unsigned cycles = (cycleCounter - lastUpdate) >> (1 + doubleSpeed);
lastUpdate += cycles << (1 + doubleSpeed);
-
if (cycles)
accumulate_channels(cycles);
bufferPos += cycles;
}
-void PSG::resetCounter(const unsigned long newCc, const unsigned long oldCc, const unsigned doubleSpeed) {
+void PSG::resetCounter(const unsigned newCc, const unsigned oldCc, const unsigned doubleSpeed) {
generate_samples(oldCc, doubleSpeed);
lastUpdate = newCc - (oldCc - lastUpdate);
}
@@ -155,11 +158,11 @@ unsigned PSG::fillBuffer() {
}
#ifdef WORDS_BIGENDIAN
-static const unsigned long so1Mul = 0x00000001;
-static const unsigned long so2Mul = 0x00010000;
+static const unsigned so1Mul = 0x00000001;
+static const unsigned so2Mul = 0x00010000;
#else
-static const unsigned long so1Mul = 0x00010000;
-static const unsigned long so2Mul = 0x00000001;
+static const unsigned so1Mul = 0x00010000;
+static const unsigned so2Mul = 0x00000001;
#endif
void PSG::set_so_volume(const unsigned nr50) {
@@ -167,7 +170,7 @@ void PSG::set_so_volume(const unsigned nr50) {
}
void PSG::map_so(const unsigned nr51) {
- const unsigned long tmp = nr51 * so1Mul + (nr51 >> 4) * so2Mul;
+ const unsigned tmp = nr51 * so1Mul + (nr51 >> 4) * so2Mul;
ch1.setSo((tmp & 0x00010001) * 0xFFFF);
ch2.setSo((tmp >> 1 & 0x00010001) * 0xFFFF);
@@ -179,4 +182,17 @@ unsigned PSG::getStatus() const {
return ch1.isActive() | ch2.isActive() << 1 | ch3.isActive() << 2 | ch4.isActive() << 3;
}
+void PSG::loadOrSave(loadsave& state)
+{
+ ch1.loadOrSave(state);
+ ch2.loadOrSave(state);
+ ch3.loadOrSave(state);
+ ch4.loadOrSave(state);
+ state(lastUpdate);
+ state(soVol);
+ state(rsum);
+ state(bufferPos);
+ state(enabled);
+}
+
}
diff --git a/libgambatte/src/sound.h b/libgambatte/src/sound.h
index f929cd5..62df442 100644
--- a/libgambatte/src/sound.h
+++ b/libgambatte/src/sound.h
@@ -19,10 +19,15 @@
#ifndef SOUND_H
#define SOUND_H
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
#include "sound/channel1.h"
#include "sound/channel2.h"
#include "sound/channel3.h"
#include "sound/channel4.h"
+#include "loadsave.h"
namespace gambatte {
@@ -34,8 +39,8 @@ class PSG {
uint_least32_t *buffer;
- unsigned long lastUpdate;
- unsigned long soVol;
+ unsigned lastUpdate;
+ unsigned soVol;
uint_least32_t rsum;
@@ -43,7 +48,7 @@ class PSG {
bool enabled;
- void accumulate_channels(unsigned long cycles);
+ void accumulate_channels(unsigned cycles);
public:
PSG();
@@ -53,8 +58,10 @@ public:
void saveState(SaveState &state);
void loadState(const SaveState &state);
- void generate_samples(unsigned long cycleCounter, unsigned doubleSpeed);
- void resetCounter(unsigned long newCc, unsigned long oldCc, unsigned doubleSpeed);
+ void loadOrSave(loadsave& state);
+
+ void generate_samples(unsigned cycleCounter, unsigned doubleSpeed);
+ void resetCounter(unsigned newCc, unsigned oldCc, unsigned doubleSpeed);
unsigned fillBuffer();
void setBuffer(uint_least32_t *const buf) { buffer = buf; bufferPos = 0; }
diff --git a/libgambatte/src/sound/channel1.cpp b/libgambatte/src/sound/channel1.cpp
index eebc4d4..6b9ccf1 100644
--- a/libgambatte/src/sound/channel1.cpp
+++ b/libgambatte/src/sound/channel1.cpp
@@ -20,6 +20,9 @@
#include "../savestate.h"
#include <algorithm>
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
namespace gambatte {
@@ -47,7 +50,7 @@ unsigned Channel1::SweepUnit::calcFreq() {
}
void Channel1::SweepUnit::event() {
- const unsigned long period = nr0 >> 4 & 0x07;
+ const unsigned period = nr0 >> 4 & 0x07;
if (period) {
const unsigned freq = calcFreq();
@@ -70,7 +73,7 @@ void Channel1::SweepUnit::nr0Change(const unsigned newNr0) {
nr0 = newNr0;
}
-void Channel1::SweepUnit::nr4Init(const unsigned long cc) {
+void Channel1::SweepUnit::nr4Init(const unsigned cc) {
negging = false;
shadow = dutyUnit.getFreq();
@@ -172,7 +175,7 @@ void Channel1::setNr4(const unsigned data) {
setEvent();
}
-void Channel1::setSo(const unsigned long soMask) {
+void Channel1::setSo(const unsigned soMask) {
this->soMask = soMask;
staticOutputTest(cycleCounter);
setEvent();
@@ -215,15 +218,15 @@ void Channel1::loadState(const SaveState &state) {
master = state.spu.ch1.master;
}
-void Channel1::update(uint_least32_t *buf, const unsigned long soBaseVol, unsigned long cycles) {
- const unsigned long outBase = envelopeUnit.dacIsOn() ? soBaseVol & soMask : 0;
- const unsigned long outLow = outBase * (0 - 15ul);
- const unsigned long endCycles = cycleCounter + cycles;
+void Channel1::update(uint_least32_t *buf, const unsigned soBaseVol, unsigned cycles) {
+ const unsigned outBase = envelopeUnit.dacIsOn() ? soBaseVol & soMask : 0;
+ const unsigned outLow = outBase * (0 - 15ul);
+ const unsigned endCycles = cycleCounter + cycles;
for (;;) {
- const unsigned long outHigh = master ? outBase * (envelopeUnit.getVolume() * 2 - 15ul) : outLow;
- const unsigned long nextMajorEvent = nextEventUnit->getCounter() < endCycles ? nextEventUnit->getCounter() : endCycles;
- unsigned long out = dutyUnit.isHighState() ? outHigh : outLow;
+ const unsigned outHigh = master ? outBase * (envelopeUnit.getVolume() * 2 - 15ul) : outLow;
+ const unsigned nextMajorEvent = nextEventUnit->getCounter() < endCycles ? nextEventUnit->getCounter() : endCycles;
+ unsigned out = dutyUnit.isHighState() ? outHigh : outLow;
while (dutyUnit.getCounter() <= nextMajorEvent) {
*buf = out - prevOut;
@@ -259,4 +262,25 @@ void Channel1::update(uint_least32_t *buf, const unsigned long soBaseVol, unsign
}
}
+void Channel1::loadOrSave(loadsave& state) {
+ //disableMaster has no state.
+ lengthCounter.loadOrSave(state);
+ dutyUnit.loadOrSave(state);
+ envelopeUnit.loadOrSave(state);
+ sweepUnit.loadOrSave(state);
+
+ state.startEnumeration();
+ state.enumerate<SoundUnit*>(nextEventUnit, NULL, 0);
+ state.enumerate<SoundUnit*>(nextEventUnit, &sweepUnit, 1);
+ state.enumerate<SoundUnit*>(nextEventUnit, &envelopeUnit, 2);
+ state.enumerate<SoundUnit*>(nextEventUnit, &lengthCounter, 3);
+ state.endEnumeration();
+
+ state(cycleCounter);
+ state(soMask);
+ state(prevOut);
+ state(nr4);
+ state(master);
+}
+
}
diff --git a/libgambatte/src/sound/channel1.h b/libgambatte/src/sound/channel1.h
index f61e002..03875f5 100644
--- a/libgambatte/src/sound/channel1.h
+++ b/libgambatte/src/sound/channel1.h
@@ -19,12 +19,17 @@
#ifndef SOUND_CHANNEL1_H
#define SOUND_CHANNEL1_H
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
#include "gbint.h"
#include "master_disabler.h"
#include "length_counter.h"
#include "duty_unit.h"
#include "envelope_unit.h"
#include "static_output_tester.h"
+#include "loadsave.h"
namespace gambatte {
@@ -44,10 +49,16 @@ class Channel1 {
SweepUnit(MasterDisabler &disabler, DutyUnit &dutyUnit);
void event();
void nr0Change(unsigned newNr0);
- void nr4Init(unsigned long cycleCounter);
+ void nr4Init(unsigned cycleCounter);
void reset();
void saveState(SaveState &state) const;
void loadState(const SaveState &state);
+ void loadOrSave(loadsave& state) {
+ loadOrSave2(state);
+ state(shadow);
+ state(nr0);
+ state(negging);
+ }
};
friend class StaticOutputTester<Channel1,DutyUnit>;
@@ -61,9 +72,9 @@ class Channel1 {
SoundUnit *nextEventUnit;
- unsigned long cycleCounter;
- unsigned long soMask;
- unsigned long prevOut;
+ unsigned cycleCounter;
+ unsigned soMask;
+ unsigned prevOut;
unsigned char nr4;
bool master;
@@ -78,15 +89,17 @@ public:
void setNr3(unsigned data);
void setNr4(unsigned data);
- void setSo(unsigned long soMask);
+ void setSo(unsigned soMask);
bool isActive() const { return master; }
- void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles);
+ void update(uint_least32_t *buf, unsigned soBaseVol, unsigned cycles);
void reset();
void init(bool cgb);
void saveState(SaveState &state);
void loadState(const SaveState &state);
+
+ void loadOrSave(loadsave& state);
};
}
diff --git a/libgambatte/src/sound/channel2.cpp b/libgambatte/src/sound/channel2.cpp
index a46a11c..6bd5b04 100644
--- a/libgambatte/src/sound/channel2.cpp
+++ b/libgambatte/src/sound/channel2.cpp
@@ -19,6 +19,10 @@
#include "channel2.h"
#include "../savestate.h"
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
namespace gambatte {
Channel2::Channel2() :
@@ -80,7 +84,7 @@ void Channel2::setNr4(const unsigned data) {
setEvent();
}
-void Channel2::setSo(const unsigned long soMask) {
+void Channel2::setSo(const unsigned soMask) {
this->soMask = soMask;
staticOutputTest(cycleCounter);
setEvent();
@@ -119,16 +123,15 @@ void Channel2::loadState(const SaveState &state) {
master = state.spu.ch2.master;
}
-void Channel2::update(uint_least32_t *buf, const unsigned long soBaseVol, unsigned long cycles) {
- const unsigned long outBase = envelopeUnit.dacIsOn() ? soBaseVol & soMask : 0;
- const unsigned long outLow = outBase * (0 - 15ul);
- const unsigned long endCycles = cycleCounter + cycles;
+void Channel2::update(uint_least32_t *buf, const unsigned soBaseVol, unsigned cycles) {
+ const unsigned outBase = envelopeUnit.dacIsOn() ? soBaseVol & soMask : 0;
+ const unsigned outLow = outBase * (0 - 15ul);
+ const unsigned endCycles = cycleCounter + cycles;
for (;;) {
- const unsigned long outHigh = master ? outBase * (envelopeUnit.getVolume() * 2 - 15ul) : outLow;
- const unsigned long nextMajorEvent = nextEventUnit->getCounter() < endCycles ? nextEventUnit->getCounter() : endCycles;
- unsigned long out = dutyUnit.isHighState() ? outHigh : outLow;
-
+ const unsigned outHigh = master ? outBase * (envelopeUnit.getVolume() * 2 - 15ul) : outLow;
+ const unsigned nextMajorEvent = nextEventUnit->getCounter() < endCycles ? nextEventUnit->getCounter() : endCycles;
+ unsigned out = dutyUnit.isHighState() ? outHigh : outLow;
while (dutyUnit.getCounter() <= nextMajorEvent) {
*buf += out - prevOut;
prevOut = out;
@@ -162,4 +165,24 @@ void Channel2::update(uint_least32_t *buf, const unsigned long soBaseVol, unsign
}
}
+void Channel2::loadOrSave(loadsave& state)
+{
+ //disableMaster has no state.
+ lengthCounter.loadOrSave(state);
+ dutyUnit.loadOrSave(state);
+ envelopeUnit.loadOrSave(state);
+
+ state.startEnumeration();
+ state.enumerate<SoundUnit*>(nextEventUnit, NULL, 0);
+ state.enumerate<SoundUnit*>(nextEventUnit, &lengthCounter, 1);
+ state.enumerate<SoundUnit*>(nextEventUnit, &envelopeUnit, 2);
+ state.endEnumeration();
+
+ state(cycleCounter);
+ state(soMask);
+ state(prevOut);
+ state(nr4);
+ state(master);
+}
+
}
diff --git a/libgambatte/src/sound/channel2.h b/libgambatte/src/sound/channel2.h
index 2be39b7..71148b0 100644
--- a/libgambatte/src/sound/channel2.h
+++ b/libgambatte/src/sound/channel2.h
@@ -24,6 +24,11 @@
#include "duty_unit.h"
#include "envelope_unit.h"
#include "static_output_tester.h"
+#include "loadsave.h"
+
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
namespace gambatte {
@@ -40,9 +45,9 @@ class Channel2 {
SoundUnit *nextEventUnit;
- unsigned long cycleCounter;
- unsigned long soMask;
- unsigned long prevOut;
+ unsigned cycleCounter;
+ unsigned soMask;
+ unsigned prevOut;
unsigned char nr4;
bool master;
@@ -56,16 +61,18 @@ public:
void setNr3(unsigned data);
void setNr4(unsigned data);
- void setSo(unsigned long soMask);
+ void setSo(unsigned soMask);
// void deactivate() { disableMaster(); setEvent(); }
bool isActive() const { return master; }
- void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles);
+ void update(uint_least32_t *buf, unsigned soBaseVol, unsigned cycles);
void reset();
void init(bool cgb);
void saveState(SaveState &state);
void loadState(const SaveState &state);
+
+ void loadOrSave(loadsave& state);
};
}
diff --git a/libgambatte/src/sound/channel3.cpp b/libgambatte/src/sound/channel3.cpp
index 6eb41da..ec2c8ed 100644
--- a/libgambatte/src/sound/channel3.cpp
+++ b/libgambatte/src/sound/channel3.cpp
@@ -21,6 +21,10 @@
#include <cstring>
#include <algorithm>
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
static inline unsigned toPeriod(const unsigned nr3, const unsigned nr4) {
return 0x800 - ((nr4 << 8 & 0x700) | nr3);
}
@@ -80,7 +84,7 @@ void Channel3::setNr4(const unsigned data) {
}
}
-void Channel3::setSo(const unsigned long soMask) {
+void Channel3::setSo(const unsigned soMask) {
this->soMask = soMask;
}
@@ -128,10 +132,10 @@ void Channel3::loadState(const SaveState &state) {
setNr2(state.mem.ioamhram.get()[0x11C]);
}
-void Channel3::updateWaveCounter(const unsigned long cc) {
+void Channel3::updateWaveCounter(const unsigned cc) {
if (cc >= waveCounter) {
const unsigned period = toPeriod(nr3, nr4);
- const unsigned long periods = (cc - waveCounter) / period;
+ const unsigned periods = (cc - waveCounter) / period;
lastReadTime = waveCounter + periods * period;
waveCounter = lastReadTime + period;
@@ -143,15 +147,15 @@ void Channel3::updateWaveCounter(const unsigned long cc) {
}
}
-void Channel3::update(uint_least32_t *buf, const unsigned long soBaseVol, unsigned long cycles) {
- const unsigned long outBase = (nr0/* & 0x80*/) ? soBaseVol & soMask : 0;
+void Channel3::update(uint_least32_t *buf, const unsigned soBaseVol, unsigned cycles) {
+ const unsigned outBase = (nr0/* & 0x80*/) ? soBaseVol & soMask : 0;
if (outBase && rShift != 4) {
- const unsigned long endCycles = cycleCounter + cycles;
+ const unsigned endCycles = cycleCounter + cycles;
for (;;) {
- const unsigned long nextMajorEvent = lengthCounter.getCounter() < endCycles ? lengthCounter.getCounter() : endCycles;
- unsigned long out = outBase * (master ? ((sampleBuf >> (~wavePos << 2 & 4) & 0xF) >> rShift) * 2 - 15ul : 0 - 15ul);
+ const unsigned nextMajorEvent = lengthCounter.getCounter() < endCycles ? lengthCounter.getCounter() : endCycles;
+ unsigned out = outBase * (master ? ((sampleBuf >> (~wavePos << 2 & 4) & 0xF) >> rShift) * 2 - 15ul : 0 - 15ul);
while (waveCounter <= nextMajorEvent) {
*buf += out - prevOut;
@@ -180,7 +184,7 @@ void Channel3::update(uint_least32_t *buf, const unsigned long soBaseVol, unsign
break;
}
} else {
- unsigned long const out = outBase * (0 - 15ul);
+ unsigned const out = outBase * (0 - 15ul);
*buf += out - prevOut;
prevOut = out;
cycleCounter += cycles;
@@ -204,4 +208,23 @@ void Channel3::update(uint_least32_t *buf, const unsigned long soBaseVol, unsign
}
}
+void Channel3::loadOrSave(loadsave& state) {
+ state(waveRam, 0x10);
+ //disableMaster has no saveable state.
+ lengthCounter.loadOrSave(state);
+ state(cycleCounter);
+ state(soMask);
+ state(prevOut);
+ state(waveCounter);
+ state(lastReadTime);
+ state(nr0);
+ state(nr3);
+ state(nr4);
+ state(wavePos);
+ state(rShift);
+ state(sampleBuf);
+ state(master);
+ state(cgb);
+}
+
}
diff --git a/libgambatte/src/sound/channel3.h b/libgambatte/src/sound/channel3.h
index 287161d..9632013 100644
--- a/libgambatte/src/sound/channel3.h
+++ b/libgambatte/src/sound/channel3.h
@@ -19,9 +19,14 @@
#ifndef SOUND_CHANNEL3_H
#define SOUND_CHANNEL3_H
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
#include "gbint.h"
#include "master_disabler.h"
#include "length_counter.h"
+#include "loadsave.h"
namespace gambatte {
@@ -29,10 +34,10 @@ struct SaveState;
class Channel3 {
class Ch3MasterDisabler : public MasterDisabler {
- unsigned long &waveCounter;
+ unsigned &waveCounter;
public:
- Ch3MasterDisabler(bool &m, unsigned long &wC) : MasterDisabler(m), waveCounter(wC) {}
+ Ch3MasterDisabler(bool &m, unsigned &wC) : MasterDisabler(m), waveCounter(wC) {}
void operator()() { MasterDisabler::operator()(); waveCounter = SoundUnit::COUNTER_DISABLED; }
};
@@ -41,11 +46,11 @@ class Channel3 {
Ch3MasterDisabler disableMaster;
LengthCounter lengthCounter;
- unsigned long cycleCounter;
- unsigned long soMask;
- unsigned long prevOut;
- unsigned long waveCounter;
- unsigned long lastReadTime;
+ unsigned cycleCounter;
+ unsigned soMask;
+ unsigned prevOut;
+ unsigned waveCounter;
+ unsigned lastReadTime;
unsigned char nr0;
unsigned char nr3;
@@ -57,7 +62,7 @@ class Channel3 {
bool master;
bool cgb;
- void updateWaveCounter(unsigned long cc);
+ void updateWaveCounter(unsigned cc);
public:
Channel3();
@@ -72,8 +77,8 @@ public:
void setNr2(unsigned data);
void setNr3(unsigned data) { nr3 = data; }
void setNr4(unsigned data);
- void setSo(unsigned long soMask);
- void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles);
+ void setSo(unsigned soMask);
+ void update(uint_least32_t *buf, unsigned soBaseVol, unsigned cycles);
unsigned waveRamRead(unsigned index) const {
if (master) {
@@ -96,6 +101,8 @@ public:
waveRam[index] = data;
}
+
+ void loadOrSave(loadsave& state);
};
}
diff --git a/libgambatte/src/sound/channel4.cpp b/libgambatte/src/sound/channel4.cpp
index 4e10974..b646dff 100644
--- a/libgambatte/src/sound/channel4.cpp
+++ b/libgambatte/src/sound/channel4.cpp
@@ -20,7 +20,11 @@
#include "../savestate.h"
#include <algorithm>
-static unsigned long toPeriod(const unsigned nr3) {
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
+static unsigned toPeriod(const unsigned nr3) {
unsigned s = (nr3 >> 4) + 3;
unsigned r = nr3 & 7;
@@ -41,15 +45,15 @@ nr3(0),
master(false)
{}
-void Channel4::Lfsr::updateBackupCounter(const unsigned long cc) {
+void Channel4::Lfsr::updateBackupCounter(const unsigned cc) {
/*if (backupCounter <= cc) {
const unsigned long period = toPeriod(nr3);
backupCounter = cc - (cc - backupCounter) % period + period;
}*/
if (backupCounter <= cc) {
- const unsigned long period = toPeriod(nr3);
- unsigned long periods = (cc - backupCounter) / period + 1;
+ const unsigned period = toPeriod(nr3);
+ unsigned periods = (cc - backupCounter) / period + 1;
backupCounter += periods * period;
@@ -75,7 +79,7 @@ void Channel4::Lfsr::updateBackupCounter(const unsigned long cc) {
}
}
-void Channel4::Lfsr::reviveCounter(const unsigned long cc) {
+void Channel4::Lfsr::reviveCounter(const unsigned cc) {
updateBackupCounter(cc);
counter = backupCounter;
}
@@ -121,7 +125,7 @@ inline void Channel4::Lfsr::event() {
counter += period * nextStateDistance[reg & 0x3F];*/
}
-void Channel4::Lfsr::nr3Change(const unsigned newNr3, const unsigned long cc) {
+void Channel4::Lfsr::nr3Change(const unsigned newNr3, const unsigned cc) {
updateBackupCounter(cc);
nr3 = newNr3;
@@ -129,7 +133,7 @@ void Channel4::Lfsr::nr3Change(const unsigned newNr3, const unsigned long cc) {
// counter = backupCounter + toPeriod(nr3) * (nextStateDistance[reg & 0x3F] - 1);
}
-void Channel4::Lfsr::nr4Init(unsigned long cc) {
+void Channel4::Lfsr::nr4Init(unsigned cc) {
disableMaster();
updateBackupCounter(cc);
master = true;
@@ -138,19 +142,19 @@ void Channel4::Lfsr::nr4Init(unsigned long cc) {
// counter = backupCounter + toPeriod(nr3) * (nextStateDistance[reg & 0x3F] - 1);
}
-void Channel4::Lfsr::reset(const unsigned long cc) {
+void Channel4::Lfsr::reset(const unsigned cc) {
nr3 = 0;
disableMaster();
backupCounter = cc + toPeriod(nr3);
}
-void Channel4::Lfsr::resetCounters(const unsigned long oldCc) {
+void Channel4::Lfsr::resetCounters(const unsigned oldCc) {
updateBackupCounter(oldCc);
backupCounter -= COUNTER_MAX;
SoundUnit::resetCounters(oldCc);
}
-void Channel4::Lfsr::saveState(SaveState &state, const unsigned long cc) {
+void Channel4::Lfsr::saveState(SaveState &state, const unsigned cc) {
updateBackupCounter(cc);
state.spu.ch4.lfsr.counter = backupCounter;
state.spu.ch4.lfsr.reg = reg;
@@ -219,7 +223,7 @@ void Channel4::setNr4(const unsigned data) {
setEvent();
}
-void Channel4::setSo(const unsigned long soMask) {
+void Channel4::setSo(const unsigned soMask) {
this->soMask = soMask;
staticOutputTest(cycleCounter);
setEvent();
@@ -258,15 +262,15 @@ void Channel4::loadState(const SaveState &state) {
master = state.spu.ch4.master;
}
-void Channel4::update(uint_least32_t *buf, const unsigned long soBaseVol, unsigned long cycles) {
- const unsigned long outBase = envelopeUnit.dacIsOn() ? soBaseVol & soMask : 0;
- const unsigned long outLow = outBase * (0 - 15ul);
- const unsigned long endCycles = cycleCounter + cycles;
+void Channel4::update(uint_least32_t *buf, const unsigned soBaseVol, unsigned cycles) {
+ const unsigned outBase = envelopeUnit.dacIsOn() ? soBaseVol & soMask : 0;
+ const unsigned outLow = outBase * (0 - 15ul);
+ const unsigned endCycles = cycleCounter + cycles;
for (;;) {
- const unsigned long outHigh = /*master ? */outBase * (envelopeUnit.getVolume() * 2 - 15ul)/* : outLow*/;
- const unsigned long nextMajorEvent = nextEventUnit->getCounter() < endCycles ? nextEventUnit->getCounter() : endCycles;
- unsigned long out = lfsr.isHighState() ? outHigh : outLow;
+ const unsigned outHigh = /*master ? */outBase * (envelopeUnit.getVolume() * 2 - 15ul)/* : outLow*/;
+ const unsigned nextMajorEvent = nextEventUnit->getCounter() < endCycles ? nextEventUnit->getCounter() : endCycles;
+ unsigned out = lfsr.isHighState() ? outHigh : outLow;
while (lfsr.getCounter() <= nextMajorEvent) {
*buf += out - prevOut;
@@ -301,4 +305,23 @@ void Channel4::update(uint_least32_t *buf, const unsigned long soBaseVol, unsign
}
}
+void Channel4::loadOrSave(loadsave& state) {
+ //DisableMaster has no state.
+ lengthCounter.loadOrSave(state);
+ envelopeUnit.loadOrSave(state);
+ lfsr.loadOrSave(state);
+
+ state.startEnumeration();
+ state.enumerate<SoundUnit*>(nextEventUnit, NULL, 0);
+ state.enumerate<SoundUnit*>(nextEventUnit, &lengthCounter, 1);
+ state.enumerate<SoundUnit*>(nextEventUnit, &envelopeUnit, 2);
+ state.endEnumeration();
+
+ state(cycleCounter);
+ state(soMask);
+ state(prevOut);
+ state(nr4);
+ state(master);
+}
+
}
diff --git a/libgambatte/src/sound/channel4.h b/libgambatte/src/sound/channel4.h
index f1742dc..5d5aac8 100644
--- a/libgambatte/src/sound/channel4.h
+++ b/libgambatte/src/sound/channel4.h
@@ -24,6 +24,11 @@
#include "length_counter.h"
#include "envelope_unit.h"
#include "static_output_tester.h"
+#include "loadsave.h"
+
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
namespace gambatte {
@@ -31,26 +36,33 @@ struct SaveState;
class Channel4 {
class Lfsr : public SoundUnit {
- unsigned long backupCounter;
+ unsigned backupCounter;
unsigned short reg;
unsigned char nr3;
bool master;
- void updateBackupCounter(unsigned long cc);
+ void updateBackupCounter(unsigned cc);
public:
Lfsr();
void event();
bool isHighState() const { return ~reg & 1; }
- void nr3Change(unsigned newNr3, unsigned long cc);
- void nr4Init(unsigned long cc);
- void reset(unsigned long cc);
- void saveState(SaveState &state, const unsigned long cc);
+ void nr3Change(unsigned newNr3, unsigned cc);
+ void nr4Init(unsigned cc);
+ void reset(unsigned cc);
+ void saveState(SaveState &state, const unsigned cc);
void loadState(const SaveState &state);
- void resetCounters(unsigned long oldCc);
+ void resetCounters(unsigned oldCc);
void disableMaster() { killCounter(); master = false; reg = 0xFF; }
void killCounter() { counter = COUNTER_DISABLED; }
- void reviveCounter(unsigned long cc);
+ void reviveCounter(unsigned cc);
+ void loadOrSave(loadsave& state) {
+ loadOrSave2(state);
+ state(backupCounter);
+ state(reg);
+ state(nr3);
+ state(master);
+ }
};
class Ch4MasterDisabler : public MasterDisabler {
@@ -70,9 +82,9 @@ class Channel4 {
SoundUnit *nextEventUnit;
- unsigned long cycleCounter;
- unsigned long soMask;
- unsigned long prevOut;
+ unsigned cycleCounter;
+ unsigned soMask;
+ unsigned prevOut;
unsigned char nr4;
bool master;
@@ -86,15 +98,17 @@ public:
void setNr3(unsigned data) { lfsr.nr3Change(data, cycleCounter); /*setEvent();*/ }
void setNr4(unsigned data);
- void setSo(unsigned long soMask);
+ void setSo(unsigned soMask);
bool isActive() const { return master; }
- void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles);
+ void update(uint_least32_t *buf, unsigned soBaseVol, unsigned cycles);
void reset();
void init(bool cgb);
void saveState(SaveState &state);
void loadState(const SaveState &state);
+
+ void loadOrSave(loadsave& state);
};
}
diff --git a/libgambatte/src/sound/duty_unit.cpp b/libgambatte/src/sound/duty_unit.cpp
index 391f9e2..80156eb 100644
--- a/libgambatte/src/sound/duty_unit.cpp
+++ b/libgambatte/src/sound/duty_unit.cpp
@@ -19,6 +19,10 @@
#include "duty_unit.h"
#include <algorithm>
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
static inline bool toOutState(const unsigned duty, const unsigned pos) {
static const unsigned char duties[4] = { 0x80, 0x81, 0xE1, 0x7E };
@@ -31,9 +35,9 @@ static inline unsigned toPeriod(const unsigned freq) {
namespace gambatte {
-void DutyUnit::updatePos(const unsigned long cc) {
+void DutyUnit::updatePos(const unsigned cc) {
if (cc >= nextPosUpdate) {
- const unsigned long inc = (cc - nextPosUpdate) / period + 1;
+ const unsigned inc = (cc - nextPosUpdate) / period + 1;
nextPosUpdate += period * inc;
pos += inc;
pos &= 7;
@@ -59,7 +63,7 @@ void DutyUnit::setCounter() {
counter = COUNTER_DISABLED;
}
-void DutyUnit::setFreq(const unsigned newFreq, const unsigned long cc) {
+void DutyUnit::setFreq(const unsigned newFreq, const unsigned cc) {
updatePos(cc);
period = toPeriod(newFreq);
setCounter();
@@ -77,17 +81,17 @@ void DutyUnit::event() {
counter += inc;
}
-void DutyUnit::nr1Change(const unsigned newNr1, const unsigned long cc) {
+void DutyUnit::nr1Change(const unsigned newNr1, const unsigned cc) {
updatePos(cc);
setDuty(newNr1);
setCounter();
}
-void DutyUnit::nr3Change(const unsigned newNr3, const unsigned long cc) {
+void DutyUnit::nr3Change(const unsigned newNr3, const unsigned cc) {
setFreq((getFreq() & 0x700) | newNr3, cc);
}
-void DutyUnit::nr4Change(const unsigned newNr4, const unsigned long cc) {
+void DutyUnit::nr4Change(const unsigned newNr4, const unsigned cc) {
setFreq((newNr4 << 8 & 0x700) | (getFreq() & 0xFF), cc);
if (newNr4 & 0x80) {
@@ -112,14 +116,14 @@ void DutyUnit::reset() {
setCounter();
}
-void DutyUnit::saveState(SaveState::SPU::Duty &dstate, const unsigned long cc) {
+void DutyUnit::saveState(SaveState::SPU::Duty &dstate, const unsigned cc) {
updatePos(cc);
dstate.nextPosUpdate = nextPosUpdate;
dstate.nr3 = getFreq() & 0xFF;
dstate.pos = pos;
}
-void DutyUnit::loadState(const SaveState::SPU::Duty &dstate, const unsigned nr1, const unsigned nr4, const unsigned long cc) {
+void DutyUnit::loadState(const SaveState::SPU::Duty &dstate, const unsigned nr1, const unsigned nr4, const unsigned cc) {
nextPosUpdate = std::max(dstate.nextPosUpdate, cc);
pos = dstate.pos & 7;
setDuty(nr1);
@@ -128,7 +132,7 @@ void DutyUnit::loadState(const SaveState::SPU::Duty &dstate, const unsigned nr1,
setCounter();
}
-void DutyUnit::resetCounters(const unsigned long oldCc) {
+void DutyUnit::resetCounters(const unsigned oldCc) {
if (nextPosUpdate == COUNTER_DISABLED)
return;
@@ -142,11 +146,21 @@ void DutyUnit::killCounter() {
setCounter();
}
-void DutyUnit::reviveCounter(const unsigned long cc) {
+void DutyUnit::reviveCounter(const unsigned cc) {
updatePos(cc);
high = toOutState(duty, pos);
enableEvents = true;
setCounter();
}
+void DutyUnit::loadOrSave(loadsave& state) {
+ loadOrSave2(state);
+ state(nextPosUpdate);
+ state(period);
+ state(pos);
+ state(duty);
+ state(high);
+ state(enableEvents);
+}
+
}
diff --git a/libgambatte/src/sound/duty_unit.h b/libgambatte/src/sound/duty_unit.h
index dba86ce..d4fb4af 100644
--- a/libgambatte/src/sound/duty_unit.h
+++ b/libgambatte/src/sound/duty_unit.h
@@ -19,14 +19,19 @@
#ifndef DUTY_UNIT_H
#define DUTY_UNIT_H
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
#include "sound_unit.h"
#include "master_disabler.h"
#include "../savestate.h"
+#include "../loadsave.h"
namespace gambatte {
class DutyUnit : public SoundUnit {
- unsigned long nextPosUpdate;
+ unsigned nextPosUpdate;
unsigned short period;
unsigned char pos;
unsigned char duty;
@@ -35,25 +40,27 @@ class DutyUnit : public SoundUnit {
void setCounter();
void setDuty(unsigned nr1);
- void updatePos(unsigned long cc);
+ void updatePos(unsigned cc);
public:
DutyUnit();
void event();
bool isHighState() const { return high; }
- void nr1Change(unsigned newNr1, unsigned long cc);
- void nr3Change(unsigned newNr3, unsigned long cc);
- void nr4Change(unsigned newNr4, unsigned long cc);
+ void nr1Change(unsigned newNr1, unsigned cc);
+ void nr3Change(unsigned newNr3, unsigned cc);
+ void nr4Change(unsigned newNr4, unsigned cc);
void reset();
- void saveState(SaveState::SPU::Duty &dstate, unsigned long cc);
- void loadState(const SaveState::SPU::Duty &dstate, unsigned nr1, unsigned nr4, unsigned long cc);
- void resetCounters(unsigned long oldCc);
+ void saveState(SaveState::SPU::Duty &dstate, unsigned cc);
+ void loadState(const SaveState::SPU::Duty &dstate, unsigned nr1, unsigned nr4, unsigned cc);
+ void resetCounters(unsigned oldCc);
void killCounter();
- void reviveCounter(unsigned long cc);
-
+ void reviveCounter(unsigned cc);
+
+ void loadOrSave(loadsave& state);
+
//intended for use by SweepUnit only.
unsigned getFreq() const { return 2048 - (period >> 1); }
- void setFreq(unsigned newFreq, unsigned long cc);
+ void setFreq(unsigned newFreq, unsigned cc);
};
class DutyMasterDisabler : public MasterDisabler {
diff --git a/libgambatte/src/sound/envelope_unit.cpp b/libgambatte/src/sound/envelope_unit.cpp
index b3d9712..2dfe42f 100644
--- a/libgambatte/src/sound/envelope_unit.cpp
+++ b/libgambatte/src/sound/envelope_unit.cpp
@@ -19,12 +19,16 @@
#include "envelope_unit.h"
#include <algorithm>
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
namespace gambatte {
EnvelopeUnit::VolOnOffEvent EnvelopeUnit::nullEvent;
void EnvelopeUnit::event() {
- const unsigned long period = nr2 & 7;
+ const unsigned period = nr2 & 7;
if (period) {
unsigned newVol = volume;
@@ -63,9 +67,9 @@ bool EnvelopeUnit::nr2Change(const unsigned newNr2) {
return !(newNr2 & 0xF8);
}
-bool EnvelopeUnit::nr4Init(const unsigned long cc) {
+bool EnvelopeUnit::nr4Init(const unsigned cc) {
{
- unsigned long period = nr2 & 7;
+ unsigned period = nr2 & 7;
if (!period)
period = 8;
@@ -97,10 +101,17 @@ void EnvelopeUnit::saveState(SaveState::SPU::Env &estate) const {
estate.volume = volume;
}
-void EnvelopeUnit::loadState(const SaveState::SPU::Env &estate, const unsigned nr2, const unsigned long cc) {
+void EnvelopeUnit::loadState(const SaveState::SPU::Env &estate, const unsigned nr2, const unsigned cc) {
counter = std::max(estate.counter, cc);
volume = estate.volume;
this->nr2 = nr2;
}
+void EnvelopeUnit::loadOrSave(loadsave& state)
+{
+ loadOrSave2(state);
+ state(nr2);
+ state(volume);
+}
+
}
diff --git a/libgambatte/src/sound/envelope_unit.h b/libgambatte/src/sound/envelope_unit.h
index 80216f4..139ae61 100644
--- a/libgambatte/src/sound/envelope_unit.h
+++ b/libgambatte/src/sound/envelope_unit.h
@@ -19,8 +19,13 @@
#ifndef ENVELOPE_UNIT_H
#define ENVELOPE_UNIT_H
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
#include "sound_unit.h"
#include "../savestate.h"
+#include "../loadsave.h"
namespace gambatte {
@@ -28,7 +33,7 @@ class EnvelopeUnit : public SoundUnit {
public:
struct VolOnOffEvent {
virtual ~VolOnOffEvent() {}
- virtual void operator()(unsigned long /*cc*/) {}
+ virtual void operator()(unsigned /*cc*/) {}
};
private:
@@ -43,10 +48,11 @@ public:
bool dacIsOn() const { return nr2 & 0xF8; }
unsigned getVolume() const { return volume; }
bool nr2Change(unsigned newNr2);
- bool nr4Init(unsigned long cycleCounter);
+ bool nr4Init(unsigned cycleCounter);
void reset();
void saveState(SaveState::SPU::Env &estate) const;
- void loadState(const SaveState::SPU::Env &estate, unsigned nr2, unsigned long cc);
+ void loadState(const SaveState::SPU::Env &estate, unsigned nr2, unsigned cc);
+ void loadOrSave(loadsave& state);
};
}
diff --git a/libgambatte/src/sound/length_counter.cpp b/libgambatte/src/sound/length_counter.cpp
index 0503150..88e2a01 100644
--- a/libgambatte/src/sound/length_counter.cpp
+++ b/libgambatte/src/sound/length_counter.cpp
@@ -20,6 +20,10 @@
#include "master_disabler.h"
#include <algorithm>
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
namespace gambatte {
LengthCounter::LengthCounter(MasterDisabler &disabler, const unsigned mask) :
@@ -36,12 +40,12 @@ void LengthCounter::event() {
disableMaster();
}
-void LengthCounter::nr1Change(const unsigned newNr1, const unsigned nr4, const unsigned long cycleCounter) {
+void LengthCounter::nr1Change(const unsigned newNr1, const unsigned nr4, const unsigned cycleCounter) {
lengthCounter = (~newNr1 & lengthMask) + 1;
- counter = (nr4 & 0x40) ?( (cycleCounter >> 13) + lengthCounter) << 13 : static_cast<unsigned long>(COUNTER_DISABLED);
+ counter = (nr4 & 0x40) ?( (cycleCounter >> 13) + lengthCounter) << 13 : static_cast<unsigned>(COUNTER_DISABLED);
}
-void LengthCounter::nr4Change(const unsigned oldNr4, const unsigned newNr4, const unsigned long cycleCounter) {
+void LengthCounter::nr4Change(const unsigned oldNr4, const unsigned newNr4, const unsigned cycleCounter) {
if (counter != COUNTER_DISABLED)
lengthCounter = (counter >> 13) - (cycleCounter >> 13);
@@ -83,9 +87,16 @@ void LengthCounter::saveState(SaveState::SPU::LCounter &lstate) const {
lstate.lengthCounter = lengthCounter;
}
-void LengthCounter::loadState(const SaveState::SPU::LCounter &lstate, const unsigned long cc) {
+void LengthCounter::loadState(const SaveState::SPU::LCounter &lstate, const unsigned cc) {
counter = std::max(lstate.counter, cc);
lengthCounter = lstate.lengthCounter;
}
+void LengthCounter::loadOrSave(loadsave& state)
+{
+ loadOrSave2(state);
+ state(lengthCounter);
+ state(cgb);
+}
+
}
diff --git a/libgambatte/src/sound/length_counter.h b/libgambatte/src/sound/length_counter.h
index 8ae4571..79bc840 100644
--- a/libgambatte/src/sound/length_counter.h
+++ b/libgambatte/src/sound/length_counter.h
@@ -19,8 +19,13 @@
#ifndef LENGTH_COUNTER_H
#define LENGTH_COUNTER_H
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
#include "sound_unit.h"
#include "../savestate.h"
+#include "../loadsave.h"
namespace gambatte {
@@ -35,12 +40,14 @@ class LengthCounter : public SoundUnit {
public:
LengthCounter(MasterDisabler &disabler, unsigned lengthMask);
void event();
- void nr1Change(unsigned newNr1, unsigned nr4, unsigned long cc);
- void nr4Change(unsigned oldNr4, unsigned newNr4, unsigned long cc);
+ void nr1Change(unsigned newNr1, unsigned nr4, unsigned cc);
+ void nr4Change(unsigned oldNr4, unsigned newNr4, unsigned cc);
// void reset();
void init(bool cgb);
void saveState(SaveState::SPU::LCounter &lstate) const;
- void loadState(const SaveState::SPU::LCounter &lstate, unsigned long cc);
+ void loadState(const SaveState::SPU::LCounter &lstate, unsigned cc);
+
+ void loadOrSave(loadsave& state);
};
}
diff --git a/libgambatte/src/sound/sound_unit.h b/libgambatte/src/sound/sound_unit.h
index 7fcf59c..c8234ea 100644
--- a/libgambatte/src/sound/sound_unit.h
+++ b/libgambatte/src/sound/sound_unit.h
@@ -19,19 +19,28 @@
#ifndef SOUND_UNIT_H
#define SOUND_UNIT_H
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
+#include "../loadsave.h"
+
namespace gambatte {
class SoundUnit {
protected:
- unsigned long counter;
+ unsigned counter;
public:
enum { COUNTER_MAX = 0x80000000u, COUNTER_DISABLED = 0xFFFFFFFFu };
SoundUnit() : counter(COUNTER_DISABLED) {}
virtual ~SoundUnit() {}
virtual void event() = 0;
- unsigned long getCounter() const { return counter; }
- virtual void resetCounters(unsigned long /*oldCc*/) { if (counter != COUNTER_DISABLED) counter -= COUNTER_MAX; }
+ unsigned getCounter() const { return counter; }
+ virtual void resetCounters(unsigned /*oldCc*/) { if (counter != COUNTER_DISABLED) counter -= COUNTER_MAX; }
+ void loadOrSave2(loadsave& state) {
+ state(counter);
+ }
};
}
diff --git a/libgambatte/src/sound/static_output_tester.h b/libgambatte/src/sound/static_output_tester.h
index 2f79b8d..9836d3f 100644
--- a/libgambatte/src/sound/static_output_tester.h
+++ b/libgambatte/src/sound/static_output_tester.h
@@ -19,6 +19,10 @@
#ifndef STATIC_OUTPUT_TESTER_H
#define STATIC_OUTPUT_TESTER_H
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
#include "envelope_unit.h"
namespace gambatte {
@@ -29,11 +33,11 @@ class StaticOutputTester : public EnvelopeUnit::VolOnOffEvent {
Unit &unit;
public:
StaticOutputTester(const Channel &ch, Unit &unit) : ch(ch), unit(unit) {}
- void operator()(unsigned long cc);
+ void operator()(unsigned cc);
};
template<class Channel, class Unit>
-void StaticOutputTester<Channel, Unit>::operator()(const unsigned long cc) {
+void StaticOutputTester<Channel, Unit>::operator()(const unsigned cc) {
if (ch.soMask && ch.master && ch.envelopeUnit.getVolume())
unit.reviveCounter(cc);
else
diff --git a/libgambatte/src/state_osd_elements.cpp b/libgambatte/src/state_osd_elements.cpp
index 66b3594..e06df4f 100644
--- a/libgambatte/src/state_osd_elements.cpp
+++ b/libgambatte/src/state_osd_elements.cpp
@@ -22,6 +22,10 @@
#include <fstream>
#include <cstring>
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
namespace {
using namespace gambatte;
@@ -71,7 +75,7 @@ life(4 * 60) {
print(pixels + 1 * w() + 1, w(), 0xE0E0E0ul, txt);*/
print(pixels, w(), ShadeFill(), txt);
- print(pixels + 1 * w() + 1, w(), 0xE0E0E0ul, txt);
+ print(pixels + 1 * w() + 1, w(), 0xE0E0E0u, txt);
}
ShadedTextOsdElment::~ShadedTextOsdElment() {
@@ -142,7 +146,7 @@ life(4 * 60) {
static const char txt[] = { E,m,p,t,bitmapfont::y,0 };
- print(pixels + 3 + (StateSaver::SS_HEIGHT / 2 - bitmapfont::HEIGHT / 2) * StateSaver::SS_WIDTH, StateSaver::SS_WIDTH, 0x808080ul, txt);
+ print(pixels + 3 + (StateSaver::SS_HEIGHT / 2 - bitmapfont::HEIGHT / 2) * StateSaver::SS_WIDTH, StateSaver::SS_WIDTH, 0x808080u, txt);
}
}
}
diff --git a/libgambatte/src/statesaver.cpp b/libgambatte/src/statesaver.cpp
index 67f677f..fc6fa51 100644
--- a/libgambatte/src/statesaver.cpp
+++ b/libgambatte/src/statesaver.cpp
@@ -43,7 +43,7 @@ struct Saver {
const char *label;
void (*save)(std::ofstream &file, const SaveState &state);
void (*load)(std::ifstream &file, SaveState &state);
- unsigned char labelsize;
+ unsigned int labelsize;
};
static inline bool operator<(const Saver &l, const Saver &r) {
@@ -78,7 +78,7 @@ static void write(std::ofstream &file, const unsigned short data) {
file.put(data & 0xFF);
}
-static void write(std::ofstream &file, const unsigned long data) {
+static void write(std::ofstream &file, const unsigned data) {
static const char inf[] = { 0x00, 0x00, 0x04 };
file.write(inf, sizeof(inf));
@@ -137,7 +137,7 @@ static inline void read(std::ifstream &file, unsigned short &data) {
data = read(file) & 0xFFFF;
}
-static inline void read(std::ifstream &file, unsigned long &data) {
+static inline void read(std::ifstream &file, unsigned &data) {
data = read(file);
}
diff --git a/libgambatte/src/statesaver.h b/libgambatte/src/statesaver.h
index ee928b9..7a1e046 100644
--- a/libgambatte/src/statesaver.h
+++ b/libgambatte/src/statesaver.h
@@ -19,8 +19,13 @@
#ifndef STATESAVER_H
#define STATESAVER_H
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
#include "gbint.h"
#include <string>
+#include <vector>
namespace gambatte {
@@ -38,6 +43,8 @@ public:
static bool saveState(const SaveState &state,
const uint_least32_t *videoBuf, int pitch, const std::string &filename);
static bool loadState(SaveState &state, const std::string &filename);
+ static void saveState(const SaveState &state, std::vector<char>& data);
+ static bool loadState(SaveState &state, const std::vector<char>& data);
};
}
diff --git a/libgambatte/src/tima.cpp b/libgambatte/src/tima.cpp
index 9d7b7ed..b90b82a 100644
--- a/libgambatte/src/tima.cpp
+++ b/libgambatte/src/tima.cpp
@@ -19,6 +19,10 @@
#include "tima.h"
#include "savestate.h"
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
static const unsigned char timaClock[4] = { 10, 4, 6, 8 };
namespace gambatte {
@@ -50,12 +54,12 @@ void Tima::loadState(const SaveState &state, const TimaInterruptRequester timaIr
? tmatime_
: lastUpdate_ + ((256u - tima_) << timaClock[tac_ & 3]) + 3)
:
- static_cast<unsigned long>(DISABLED_TIME)
+ static_cast<unsigned>(DISABLED_TIME)
);
}
-void Tima::resetCc(const unsigned long oldCc, const unsigned long newCc, const TimaInterruptRequester timaIrq) {
- const unsigned long dec = oldCc - newCc;
+void Tima::resetCc(const unsigned oldCc, const unsigned newCc, const TimaInterruptRequester timaIrq) {
+ const unsigned dec = oldCc - newCc;
if (tac_ & 0x04) {
updateIrq(oldCc, timaIrq);
@@ -69,8 +73,8 @@ void Tima::resetCc(const unsigned long oldCc, const unsigned long newCc, const T
}
}
-void Tima::updateTima(const unsigned long cycleCounter) {
- const unsigned long ticks = (cycleCounter - lastUpdate_) >> timaClock[tac_ & 3];
+void Tima::updateTima(const unsigned cycleCounter) {
+ const unsigned ticks = (cycleCounter - lastUpdate_) >> timaClock[tac_ & 3];
lastUpdate_ += ticks << timaClock[tac_ & 3];
@@ -81,7 +85,7 @@ void Tima::updateTima(const unsigned long cycleCounter) {
tima_ = tma_;
}
- unsigned long tmp = tima_ + ticks;
+ unsigned tmp = tima_ + ticks;
while (tmp > 0x100)
tmp -= 0x100 - tma_;
@@ -101,7 +105,7 @@ void Tima::updateTima(const unsigned long cycleCounter) {
tima_ = tmp;
}
-void Tima::setTima(const unsigned data, const unsigned long cycleCounter, const TimaInterruptRequester timaIrq) {
+void Tima::setTima(const unsigned data, const unsigned cycleCounter, const TimaInterruptRequester timaIrq) {
if (tac_ & 0x04) {
updateIrq(cycleCounter, timaIrq);
updateTima(cycleCounter);
@@ -115,7 +119,7 @@ void Tima::setTima(const unsigned data, const unsigned long cycleCounter, const
tima_ = data;
}
-void Tima::setTma(const unsigned data, const unsigned long cycleCounter, const TimaInterruptRequester timaIrq) {
+void Tima::setTma(const unsigned data, const unsigned cycleCounter, const TimaInterruptRequester timaIrq) {
if (tac_ & 0x04) {
updateIrq(cycleCounter, timaIrq);
updateTima(cycleCounter);
@@ -124,9 +128,9 @@ void Tima::setTma(const unsigned data, const unsigned long cycleCounter, const T
tma_ = data;
}
-void Tima::setTac(const unsigned data, const unsigned long cycleCounter, const TimaInterruptRequester timaIrq) {
+void Tima::setTac(const unsigned data, const unsigned cycleCounter, const TimaInterruptRequester timaIrq) {
if (tac_ ^ data) {
- unsigned long nextIrqEventTime = timaIrq.nextIrqEventTime();
+ unsigned nextIrqEventTime = timaIrq.nextIrqEventTime();
if (tac_ & 0x04) {
updateIrq(cycleCounter, timaIrq);
@@ -156,7 +160,7 @@ void Tima::setTac(const unsigned data, const unsigned long cycleCounter, const T
tac_ = data;
}
-unsigned Tima::tima(unsigned long cycleCounter) {
+unsigned Tima::tima(unsigned cycleCounter) {
if (tac_ & 0x04)
updateTima(cycleCounter);
@@ -168,4 +172,13 @@ void Tima::doIrqEvent(const TimaInterruptRequester timaIrq) {
timaIrq.setNextIrqEventTime(timaIrq.nextIrqEventTime() + ((256u - tma_) << timaClock[tac_ & 3]));
}
+void Tima::loadOrSave(loadsave& state)
+{
+ state(lastUpdate_);
+ state(tmatime_);
+ state(tima_);
+ state(tma_);
+ state(tac_);
+}
+
}
diff --git a/libgambatte/src/tima.h b/libgambatte/src/tima.h
index 61cd5e5..130862a 100644
--- a/libgambatte/src/tima.h
+++ b/libgambatte/src/tima.h
@@ -19,6 +19,10 @@
#ifndef TIMA_H
#define TIMA_H
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
#include "interruptrequester.h"
namespace gambatte {
@@ -29,37 +33,39 @@ class TimaInterruptRequester {
public:
explicit TimaInterruptRequester(InterruptRequester &intreq) : intreq(intreq) {}
void flagIrq() const { intreq.flagIrq(4); }
- unsigned long nextIrqEventTime() const { return intreq.eventTime(TIMA); }
- void setNextIrqEventTime(const unsigned long time) const { intreq.setEventTime<TIMA>(time); }
+ unsigned nextIrqEventTime() const { return intreq.eventTime(TIMA); }
+ void setNextIrqEventTime(const unsigned time) const { intreq.setEventTime<TIMA>(time); }
};
class Tima {
- unsigned long lastUpdate_;
- unsigned long tmatime_;
+ unsigned lastUpdate_;
+ unsigned tmatime_;
unsigned char tima_;
unsigned char tma_;
unsigned char tac_;
- void updateIrq(const unsigned long cc, const TimaInterruptRequester timaIrq) {
+ void updateIrq(const unsigned cc, const TimaInterruptRequester timaIrq) {
while (cc >= timaIrq.nextIrqEventTime())
doIrqEvent(timaIrq);
}
- void updateTima(unsigned long cc);
+ void updateTima(unsigned cc);
public:
Tima();
void saveState(SaveState &) const;
void loadState(const SaveState &, TimaInterruptRequester timaIrq);
- void resetCc(unsigned long oldCc, unsigned long newCc, TimaInterruptRequester timaIrq);
+ void resetCc(unsigned oldCc, unsigned newCc, TimaInterruptRequester timaIrq);
- void setTima(unsigned tima, unsigned long cc, TimaInterruptRequester timaIrq);
- void setTma(unsigned tma, unsigned long cc, TimaInterruptRequester timaIrq);
- void setTac(unsigned tac, unsigned long cc, TimaInterruptRequester timaIrq);
- unsigned tima(unsigned long cc);
+ void setTima(unsigned tima, unsigned cc, TimaInterruptRequester timaIrq);
+ void setTma(unsigned tma, unsigned cc, TimaInterruptRequester timaIrq);
+ void setTac(unsigned tac, unsigned cc, TimaInterruptRequester timaIrq);
+ unsigned tima(unsigned cc);
void doIrqEvent(TimaInterruptRequester timaIrq);
+
+ void loadOrSave(loadsave& state);
};
}
diff --git a/libgambatte/src/video.cpp b/libgambatte/src/video.cpp
index 45d82e1..b6aec75 100644
--- a/libgambatte/src/video.cpp
+++ b/libgambatte/src/video.cpp
@@ -21,19 +21,23 @@
#include <cstring>
#include <algorithm>
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
namespace gambatte {
-void LCD::setDmgPalette(unsigned long *const palette, const unsigned long *const dmgColors, const unsigned data) {
+void LCD::setDmgPalette(uint_least32_t *const palette, const uint_least32_t *const dmgColors, const unsigned data) {
palette[0] = dmgColors[data & 3];
palette[1] = dmgColors[data >> 2 & 3];
palette[2] = dmgColors[data >> 4 & 3];
palette[3] = dmgColors[data >> 6 & 3];
}
-static unsigned long gbcToRgb32(const unsigned bgr15) {
- const unsigned long r = bgr15 & 0x1F;
- const unsigned long g = bgr15 >> 5 & 0x1F;
- const unsigned long b = bgr15 >> 10 & 0x1F;
+static uint_least32_t gbcToRgb32(const unsigned bgr15) {
+ const uint_least32_t r = bgr15 & 0x1F;
+ const uint_least32_t g = bgr15 >> 5 & 0x1F;
+ const uint_least32_t b = bgr15 >> 10 & 0x1F;
return ((r * 13 + g * 2 + b) >> 1) << 16 | (g * 3 + b) << 9 | (r * 3 + g * 2 + b * 11) >> 1;
}
@@ -80,7 +84,6 @@ LCD::LCD(const unsigned char *const oamram, const unsigned char *const vram, con
setDmgPaletteColor(i, (3 - (i & 3)) * 85 * 0x010101);
reset(oamram, vram, false);
- setVideoBuffer(0, 160);
}
void LCD::reset(const unsigned char *const oamram, const unsigned char *vram, const bool cgb) {
@@ -89,7 +92,7 @@ void LCD::reset(const unsigned char *const oamram, const unsigned char *vram, co
refreshPalettes();
}
-static unsigned long mode2IrqSchedule(const unsigned statReg, const LyCounter &lyCounter, const unsigned long cycleCounter) {
+static unsigned mode2IrqSchedule(const unsigned statReg, const LyCounter &lyCounter, const unsigned cycleCounter) {
if (!(statReg & 0x20))
return DISABLED_TIME;
@@ -107,16 +110,16 @@ static unsigned long mode2IrqSchedule(const unsigned statReg, const LyCounter &l
return cycleCounter + next;
}
-static inline unsigned long m0IrqTimeFromXpos166Time(const unsigned long xpos166Time, const bool cgb, const bool ds) {
+static inline unsigned m0IrqTimeFromXpos166Time(const unsigned xpos166Time, const bool cgb, const bool ds) {
return xpos166Time + cgb - ds;
}
-static inline unsigned long hdmaTimeFromM0Time(const unsigned long m0Time, const bool ds) {
+static inline unsigned hdmaTimeFromM0Time(const unsigned m0Time, const bool ds) {
return m0Time + 1 - ds;
}
-static unsigned long nextHdmaTime(const unsigned long lastM0Time,
- const unsigned long nextM0Time, const unsigned long cycleCounter, const bool ds) {
+static unsigned nextHdmaTime(const unsigned lastM0Time,
+ const unsigned nextM0Time, const unsigned cycleCounter, const bool ds) {
return cycleCounter < hdmaTimeFromM0Time(lastM0Time, ds)
? hdmaTimeFromM0Time(lastM0Time, ds)
: hdmaTimeFromM0Time(nextM0Time, ds);
@@ -152,18 +155,18 @@ void LCD::loadState(const SaveState &state, const unsigned char *const oamram) {
lycIrq.reschedule(ppu.lyCounter(), ppu.now());
eventTimes_.setm<ONESHOT_LCDSTATIRQ>(state.ppu.pendingLcdstatIrq
- ? ppu.now() + 1 : static_cast<unsigned long>(DISABLED_TIME));
+ ? ppu.now() + 1 : static_cast<unsigned>(DISABLED_TIME));
eventTimes_.setm<ONESHOT_UPDATEWY2>(state.ppu.oldWy != state.mem.ioamhram.get()[0x14A]
- ? ppu.now() + 1 : static_cast<unsigned long>(DISABLED_TIME));
+ ? ppu.now() + 1 : static_cast<unsigned>(DISABLED_TIME));
eventTimes_.set<LY_COUNT>(ppu.lyCounter().time());
eventTimes_.setm<SPRITE_MAP>(SpriteMapper::schedule(ppu.lyCounter(), ppu.now()));
eventTimes_.setm<LYC_IRQ>(lycIrq.time());
eventTimes_.setm<MODE1_IRQ>(ppu.lyCounter().nextFrameCycle(144 * 456, ppu.now()));
eventTimes_.setm<MODE2_IRQ>(mode2IrqSchedule(statReg, ppu.lyCounter(), ppu.now()));
- eventTimes_.setm<MODE0_IRQ>((statReg & 0x08) ? ppu.now() + state.ppu.nextM0Irq : static_cast<unsigned long>(DISABLED_TIME));
+ eventTimes_.setm<MODE0_IRQ>((statReg & 0x08) ? ppu.now() + state.ppu.nextM0Irq : static_cast<unsigned>(DISABLED_TIME));
eventTimes_.setm<HDMA_REQ>(state.mem.hdmaTransfer
? nextHdmaTime(ppu.lastM0Time(), nextM0Time_.predictedNextM0Time(), ppu.now(), isDoubleSpeed())
- : static_cast<unsigned long>(DISABLED_TIME));
+ : static_cast<unsigned>(DISABLED_TIME));
} else for (int i = 0; i < NUM_MEM_EVENTS; ++i)
eventTimes_.set(static_cast<MemEvent>(i), DISABLED_TIME);
@@ -212,7 +215,7 @@ struct Blend {
};
template<typename T>
-static void clear(T *buf, const unsigned long color, const int dpitch) {
+static void clear(T *buf, const uint_least32_t color, const int dpitch) {
unsigned lines = 144;
while (lines--) {
@@ -223,11 +226,11 @@ static void clear(T *buf, const unsigned long color, const int dpitch) {
}
-void LCD::updateScreen(const bool blanklcd, const unsigned long cycleCounter) {
+void LCD::updateScreen(const bool blanklcd, const unsigned cycleCounter, uint_least32_t* vbuffer, unsigned vpitch) {
update(cycleCounter);
if (blanklcd && ppu.frameBuf().fb()) {
- const unsigned long color = ppu.cgb() ? gbcToRgb32(0xFFFF) : dmgColorsRgb32[0];
+ const uint_least32_t color = ppu.cgb() ? gbcToRgb32(0xFFFF) : dmgColorsRgb32[0];
clear(ppu.frameBuf().fb(), color, ppu.frameBuf().pitch());
}
@@ -245,14 +248,15 @@ void LCD::updateScreen(const bool blanklcd, const unsigned long cycleCounter) {
} else
osdElement.reset();
}
+ ppu.frameBuf().blit(vbuffer, vpitch);
}
-void LCD::resetCc(const unsigned long oldCc, const unsigned long newCc) {
+void LCD::resetCc(const unsigned oldCc, const unsigned newCc) {
update(oldCc);
ppu.resetCc(oldCc, newCc);
if (ppu.lcdc() & 0x80) {
- const unsigned long dec = oldCc - newCc;
+ const unsigned dec = oldCc - newCc;
nextM0Time_.invalidatePredictedNextM0Time();
lycIrq.reschedule(ppu.lyCounter(), newCc);
@@ -266,7 +270,7 @@ void LCD::resetCc(const unsigned long oldCc, const unsigned long newCc) {
}
}
-void LCD::speedChange(const unsigned long cycleCounter) {
+void LCD::speedChange(const unsigned cycleCounter) {
update(cycleCounter);
ppu.speedChange(cycleCounter);
@@ -290,13 +294,13 @@ void LCD::speedChange(const unsigned long cycleCounter) {
}
}
-static inline unsigned long m0TimeOfCurrentLine(const unsigned long nextLyTime,
- const unsigned long lastM0Time, const unsigned long nextM0Time)
+static inline unsigned m0TimeOfCurrentLine(const unsigned nextLyTime,
+ const unsigned lastM0Time, const unsigned nextM0Time)
{
return nextM0Time < nextLyTime ? nextM0Time : lastM0Time;
}
-unsigned long LCD::m0TimeOfCurrentLine(const unsigned long cc) {
+unsigned LCD::m0TimeOfCurrentLine(const unsigned cc) {
if (cc >= nextM0Time_.predictedNextM0Time()) {
update(cc);
nextM0Time_.predictNextM0Time(ppu);
@@ -306,7 +310,7 @@ unsigned long LCD::m0TimeOfCurrentLine(const unsigned long cc) {
}
static bool isHdmaPeriod(const LyCounter &lyCounter,
- const unsigned long m0TimeOfCurrentLy, const unsigned long cycleCounter)
+ const unsigned m0TimeOfCurrentLy, const unsigned cycleCounter)
{
const unsigned timeToNextLy = lyCounter.time() - cycleCounter;
@@ -314,7 +318,7 @@ static bool isHdmaPeriod(const LyCounter &lyCounter,
&& cycleCounter >= hdmaTimeFromM0Time(m0TimeOfCurrentLy, lyCounter.isDoubleSpeed());
}
-void LCD::enableHdma(const unsigned long cycleCounter) {
+void LCD::enableHdma(const unsigned cycleCounter) {
if (cycleCounter >= nextM0Time_.predictedNextM0Time()) {
update(cycleCounter);
nextM0Time_.predictNextM0Time(ppu);
@@ -330,14 +334,14 @@ void LCD::enableHdma(const unsigned long cycleCounter) {
eventTimes_.setm<HDMA_REQ>(nextHdmaTime(ppu.lastM0Time(), nextM0Time_.predictedNextM0Time(), cycleCounter, isDoubleSpeed()));
}
-void LCD::disableHdma(const unsigned long cycleCounter) {
+void LCD::disableHdma(const unsigned cycleCounter) {
if (cycleCounter >= eventTimes_.nextEventTime())
update(cycleCounter);
eventTimes_.setm<HDMA_REQ>(DISABLED_TIME);
}
-bool LCD::vramAccessible(const unsigned long cycleCounter) {
+bool LCD::vramAccessible(const unsigned cycleCounter) {
if (cycleCounter >= eventTimes_.nextEventTime())
update(cycleCounter);
@@ -346,7 +350,7 @@ bool LCD::vramAccessible(const unsigned long cycleCounter) {
|| cycleCounter + isDoubleSpeed() - ppu.cgb() + 2 >= m0TimeOfCurrentLine(cycleCounter);
}
-bool LCD::cgbpAccessible(const unsigned long cycleCounter) {
+bool LCD::cgbpAccessible(const unsigned cycleCounter) {
if (cycleCounter >= eventTimes_.nextEventTime())
update(cycleCounter);
@@ -356,27 +360,27 @@ bool LCD::cgbpAccessible(const unsigned long cycleCounter) {
}
static void doCgbColorChange(unsigned char *const pdata,
- unsigned long *const palette, unsigned index, const unsigned data) {
+ uint_least32_t *const palette, unsigned index, const unsigned data) {
pdata[index] = data;
index >>= 1;
palette[index] = gbcToRgb32(pdata[index << 1] | pdata[(index << 1) + 1] << 8);
}
-void LCD::doCgbBgColorChange(unsigned index, const unsigned data, const unsigned long cycleCounter) {
+void LCD::doCgbBgColorChange(unsigned index, const unsigned data, const unsigned cycleCounter) {
if (cgbpAccessible(cycleCounter)) {
update(cycleCounter);
doCgbColorChange(bgpData, ppu.bgPalette(), index, data);
}
}
-void LCD::doCgbSpColorChange(unsigned index, const unsigned data, const unsigned long cycleCounter) {
+void LCD::doCgbSpColorChange(unsigned index, const unsigned data, const unsigned cycleCounter) {
if (cgbpAccessible(cycleCounter)) {
update(cycleCounter);
doCgbColorChange(objpData, ppu.spPalette(), index, data);
}
}
-bool LCD::oamReadable(const unsigned long cycleCounter) {
+bool LCD::oamReadable(const unsigned cycleCounter) {
if (!(ppu.lcdc() & 0x80) || ppu.inactivePeriodAfterDisplayEnable(cycleCounter))
return true;
@@ -389,7 +393,7 @@ bool LCD::oamReadable(const unsigned long cycleCounter) {
return ppu.lyCounter().ly() >= 144 || cycleCounter + isDoubleSpeed() - ppu.cgb() + 2 >= m0TimeOfCurrentLine(cycleCounter);
}
-bool LCD::oamWritable(const unsigned long cycleCounter) {
+bool LCD::oamWritable(const unsigned cycleCounter) {
if (!(ppu.lcdc() & 0x80) || ppu.inactivePeriodAfterDisplayEnable(cycleCounter))
return true;
@@ -417,13 +421,13 @@ void LCD::mode3CyclesChange() {
}
}
-void LCD::wxChange(const unsigned newValue, const unsigned long cycleCounter) {
+void LCD::wxChange(const unsigned newValue, const unsigned cycleCounter) {
update(cycleCounter + isDoubleSpeed() + 1);
ppu.setWx(newValue);
mode3CyclesChange();
}
-void LCD::wyChange(const unsigned newValue, const unsigned long cycleCounter) {
+void LCD::wyChange(const unsigned newValue, const unsigned cycleCounter) {
update(cycleCounter + 1);
ppu.setWy(newValue);
// mode3CyclesChange(); // should be safe to wait until after wy2 delay, because no mode3 events are close to when wy1 is read.
@@ -438,18 +442,18 @@ void LCD::wyChange(const unsigned newValue, const unsigned long cycleCounter) {
}
}
-void LCD::scxChange(const unsigned newScx, const unsigned long cycleCounter) {
+void LCD::scxChange(const unsigned newScx, const unsigned cycleCounter) {
update(cycleCounter + ppu.cgb() + isDoubleSpeed());
ppu.setScx(newScx);
mode3CyclesChange();
}
-void LCD::scyChange(const unsigned newValue, const unsigned long cycleCounter) {
+void LCD::scyChange(const unsigned newValue, const unsigned cycleCounter) {
update(cycleCounter + ppu.cgb() + isDoubleSpeed());
ppu.setScy(newValue);
}
-void LCD::oamChange(const unsigned long cycleCounter) {
+void LCD::oamChange(const unsigned cycleCounter) {
if (ppu.lcdc() & 0x80) {
update(cycleCounter);
ppu.oamChange(cycleCounter);
@@ -457,7 +461,7 @@ void LCD::oamChange(const unsigned long cycleCounter) {
}
}
-void LCD::oamChange(const unsigned char *const oamram, const unsigned long cycleCounter) {
+void LCD::oamChange(const unsigned char *const oamram, const unsigned cycleCounter) {
update(cycleCounter);
ppu.oamChange(oamram, cycleCounter);
@@ -465,7 +469,7 @@ void LCD::oamChange(const unsigned char *const oamram, const unsigned long cycle
eventTimes_.setm<SPRITE_MAP>(SpriteMapper::schedule(ppu.lyCounter(), cycleCounter));
}
-void LCD::lcdcChange(const unsigned data, const unsigned long cycleCounter) {
+void LCD::lcdcChange(const unsigned data, const unsigned cycleCounter) {
const unsigned oldLcdc = ppu.lcdc();
update(cycleCounter);
@@ -545,7 +549,7 @@ static LyCnt const getLycCmpLy(LyCounter const &lyCounter, unsigned long cc) {
}
}
-void LCD::lcdstatChange(unsigned const data, unsigned long const cycleCounter) {
+void LCD::lcdstatChange(const unsigned data, const unsigned cycleCounter) {
if (cycleCounter >= eventTimes_.nextEventTime())
update(cycleCounter);
@@ -614,7 +618,7 @@ void LCD::lcdstatChange(unsigned const data, unsigned long const cycleCounter) {
m0Irq_.statRegChange(data, eventTimes_(MODE0_IRQ), cycleCounter, ppu.cgb());
}
-void LCD::lycRegChange(unsigned const data, unsigned long const cycleCounter) {
+void LCD::lycRegChange(const unsigned data, const unsigned cycleCounter) {
unsigned const old = lycIrq.lycReg();
if (data == old)
@@ -654,7 +658,7 @@ void LCD::lycRegChange(unsigned const data, unsigned long const cycleCounter) {
}
}
-unsigned LCD::getStat(unsigned const lycReg, unsigned long const cycleCounter) {
+unsigned LCD::getStat(const unsigned lycReg, const unsigned cycleCounter) {
unsigned stat = 0;
if (ppu.lcdc() & 0x80) {
@@ -698,7 +702,7 @@ inline void LCD::doMode2IrqEvent() {
m2IrqStatReg_ = statReg;
if (!(statReg & 0x08)) {
- unsigned long nextTime = eventTimes_(MODE2_IRQ) + ppu.lyCounter().lineTime();
+ unsigned nextTime = eventTimes_(MODE2_IRQ) + ppu.lyCounter().lineTime();
if (ly == 0) {
nextTime -= 4;
@@ -752,7 +756,7 @@ inline void LCD::event() {
eventTimes_.setm<MODE0_IRQ>((statReg & 0x08)
? m0IrqTimeFromXpos166Time(ppu.predictedNextXposTime(166), ppu.cgb(), isDoubleSpeed())
- : static_cast<unsigned long>(DISABLED_TIME));
+ : static_cast<unsigned>(DISABLED_TIME));
break;
case ONESHOT_LCDSTATIRQ:
@@ -776,7 +780,7 @@ inline void LCD::event() {
}
}
-void LCD::update(const unsigned long cycleCounter) {
+void LCD::update(const unsigned cycleCounter) {
if (!(ppu.lcdc() & 0x80))
return;
@@ -788,15 +792,11 @@ void LCD::update(const unsigned long cycleCounter) {
ppu.update(cycleCounter);
}
-void LCD::setVideoBuffer(uint_least32_t *const videoBuf, const int pitch) {
- ppu.setFrameBuf(videoBuf, pitch);
-}
-
-void LCD::setDmgPaletteColor(const unsigned index, const unsigned long rgb32) {
+void LCD::setDmgPaletteColor(const unsigned index, const uint_least32_t rgb32) {
dmgColorsRgb32[index] = rgb32;
}
-void LCD::setDmgPaletteColor(const unsigned palNum, const unsigned colorNum, const unsigned long rgb32) {
+void LCD::setDmgPaletteColor(const unsigned palNum, const unsigned colorNum, const uint_least32_t rgb32) {
if (palNum > 2 || colorNum > 3)
return;
@@ -804,4 +804,18 @@ void LCD::setDmgPaletteColor(const unsigned palNum, const unsigned colorNum, con
refreshPalettes();
}
+void LCD::loadOrSave(loadsave& state) {
+ ppu.loadOrSave(state);
+ state(dmgColorsRgb32, 12);
+ state(bgpData, 64);
+ state(objpData, 64);
+ eventTimes_.loadOrSave(state);
+ m0Irq_.loadOrSave(state);
+ lycIrq.loadOrSave(state);
+ nextM0Time_.loadOrSave(state);
+ state(statReg);
+ state(m2IrqStatReg_);
+ state(m1IrqStatReg_);
+}
+
}
diff --git a/libgambatte/src/video.h b/libgambatte/src/video.h
index 14a5939..a830d88 100644
--- a/libgambatte/src/video.h
+++ b/libgambatte/src/video.h
@@ -19,6 +19,10 @@
#ifndef VIDEO_H
#define VIDEO_H
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
#include "video/ppu.h"
#include "video/lyc_irq.h"
#include "video/next_m0_time.h"
@@ -36,7 +40,7 @@ public:
explicit VideoInterruptRequester(InterruptRequester * intreq) : intreq(intreq) {}
void flagHdmaReq() const { gambatte::flagHdmaReq(intreq); }
void flagIrq(const unsigned bit) const { intreq->flagIrq(bit); }
- void setNextEventTime(const unsigned long time) const { intreq->setEventTime<VIDEO>(time); }
+ void setNextEventTime(const unsigned time) const { intreq->setEventTime<VIDEO>(time); }
};
class M0Irq {
@@ -50,15 +54,21 @@ public:
statReg_ = statReg;
lycReg_ = lycReg;
}
-
+
+ void loadOrSave(loadsave& state)
+ {
+ state(statReg_);
+ state(lycReg_);
+ }
+
void statRegChange(const unsigned statReg,
- const unsigned long nextM0IrqTime, const unsigned long cc, const bool cgb) {
+ const unsigned nextM0IrqTime, const unsigned cc, const bool cgb) {
if (nextM0IrqTime - cc > cgb * 2U)
statReg_ = statReg;
}
void lycRegChange(const unsigned lycReg,
- const unsigned long nextM0IrqTime, const unsigned long cc, const bool ds, const bool cgb) {
+ const unsigned nextM0IrqTime, const unsigned cc, const bool ds, const bool cgb) {
if (nextM0IrqTime - cc > cgb * 5 + 1U - ds)
lycReg_ = lycReg;
}
@@ -94,7 +104,7 @@ class LCD {
VideoInterruptRequester memEventRequester_;
void setMemEvent() {
- const unsigned long nmet = nextMemEventTime();
+ const unsigned nmet = nextMemEventTime();
eventMin_.setValue<MEM_EVENT>(nmet);
memEventRequester_.setNextEventTime(nmet);
}
@@ -103,23 +113,28 @@ class LCD {
explicit EventTimes(const VideoInterruptRequester memEventRequester) : memEventRequester_(memEventRequester) {}
Event nextEvent() const { return static_cast<Event>(eventMin_.min()); }
- unsigned long nextEventTime() const { return eventMin_.minValue(); }
- unsigned long operator()(const Event e) const { return eventMin_.value(e); }
- template<Event e> void set(const unsigned long time) { eventMin_.setValue<e>(time); }
- void set(const Event e, const unsigned long time) { eventMin_.setValue(e, time); }
+ unsigned nextEventTime() const { return eventMin_.minValue(); }
+ unsigned operator()(const Event e) const { return eventMin_.value(e); }
+ template<Event e> void set(const unsigned time) { eventMin_.setValue<e>(time); }
+ void set(const Event e, const unsigned time) { eventMin_.setValue(e, time); }
MemEvent nextMemEvent() const { return static_cast<MemEvent>(memEventMin_.min()); }
- unsigned long nextMemEventTime() const { return memEventMin_.minValue(); }
- unsigned long operator()(const MemEvent e) const { return memEventMin_.value(e); }
- template<MemEvent e> void setm(const unsigned long time) { memEventMin_.setValue<e>(time); setMemEvent(); }
- void set(const MemEvent e, const unsigned long time) { memEventMin_.setValue(e, time); setMemEvent(); }
+ unsigned nextMemEventTime() const { return memEventMin_.minValue(); }
+ unsigned operator()(const MemEvent e) const { return memEventMin_.value(e); }
+ template<MemEvent e> void setm(const unsigned time) { memEventMin_.setValue<e>(time); setMemEvent(); }
+ void set(const MemEvent e, const unsigned time) { memEventMin_.setValue(e, time); setMemEvent(); }
void flagIrq(const unsigned bit) { memEventRequester_.flagIrq(bit); }
void flagHdmaReq() { memEventRequester_.flagHdmaReq(); }
+
+ void loadOrSave(loadsave& state) {
+ eventMin_.loadOrSave(state);
+ memEventMin_.loadOrSave(state);
+ }
};
PPU ppu;
- unsigned long dmgColorsRgb32[3 * 4];
+ uint_least32_t dmgColorsRgb32[3 * 4];
unsigned char bgpData[8 * 8];
unsigned char objpData[8 * 8];
@@ -134,8 +149,8 @@ class LCD {
unsigned char m2IrqStatReg_;
unsigned char m1IrqStatReg_;
- static void setDmgPalette(unsigned long *palette, const unsigned long *dmgColors, unsigned data);
- void setDmgPaletteColor(unsigned index, unsigned long rgb32);
+ static void setDmgPalette(uint_least32_t *palette, const uint_least32_t *dmgColors, unsigned data);
+ void setDmgPaletteColor(unsigned index, uint_least32_t rgb32);
void refreshPalettes();
void setDBuffer();
@@ -143,12 +158,12 @@ class LCD {
void doMode2IrqEvent();
void event();
- unsigned long m0TimeOfCurrentLine(unsigned long cc);
- bool cgbpAccessible(unsigned long cycleCounter);
+ unsigned m0TimeOfCurrentLine(unsigned cc);
+ bool cgbpAccessible(unsigned cycleCounter);
void mode3CyclesChange();
- void doCgbBgColorChange(unsigned index, unsigned data, unsigned long cycleCounter);
- void doCgbSpColorChange(unsigned index, unsigned data, unsigned long cycleCounter);
+ void doCgbBgColorChange(unsigned index, unsigned data, unsigned cycleCounter);
+ void doCgbSpColorChange(unsigned index, unsigned data, unsigned cycleCounter);
public:
LCD(const unsigned char *oamram, const unsigned char *vram_in, VideoInterruptRequester memEventRequester);
@@ -156,65 +171,66 @@ public:
void setStatePtrs(SaveState &state);
void saveState(SaveState &state) const;
void loadState(const SaveState &state, const unsigned char *oamram);
- void setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned long rgb32);
- void setVideoBuffer(uint_least32_t *videoBuf, int pitch);
+ void setDmgPaletteColor(unsigned palNum, unsigned colorNum, uint_least32_t rgb32);
+
+ void loadOrSave(loadsave& state);
void setOsdElement(std::auto_ptr<OsdElement> osdElement) { this->osdElement = osdElement; }
- void dmgBgPaletteChange(const unsigned data, const unsigned long cycleCounter) {
+ void dmgBgPaletteChange(const unsigned data, const unsigned cycleCounter) {
update(cycleCounter);
bgpData[0] = data;
setDmgPalette(ppu.bgPalette(), dmgColorsRgb32, data);
}
- void dmgSpPalette1Change(const unsigned data, const unsigned long cycleCounter) {
+ void dmgSpPalette1Change(const unsigned data, const unsigned cycleCounter) {
update(cycleCounter);
objpData[0] = data;
setDmgPalette(ppu.spPalette(), dmgColorsRgb32 + 4, data);
}
- void dmgSpPalette2Change(const unsigned data, const unsigned long cycleCounter) {
+ void dmgSpPalette2Change(const unsigned data, const unsigned cycleCounter) {
update(cycleCounter);
objpData[1] = data;
setDmgPalette(ppu.spPalette() + 4, dmgColorsRgb32 + 8, data);
}
- void cgbBgColorChange(unsigned index, const unsigned data, const unsigned long cycleCounter) {
+ void cgbBgColorChange(unsigned index, const unsigned data, const unsigned cycleCounter) {
if (bgpData[index] != data)
doCgbBgColorChange(index, data, cycleCounter);
}
- void cgbSpColorChange(unsigned index, const unsigned data, const unsigned long cycleCounter) {
+ void cgbSpColorChange(unsigned index, const unsigned data, const unsigned cycleCounter) {
if (objpData[index] != data)
doCgbSpColorChange(index, data, cycleCounter);
}
- unsigned cgbBgColorRead(const unsigned index, const unsigned long cycleCounter) {
+ unsigned cgbBgColorRead(const unsigned index, const unsigned cycleCounter) {
return ppu.cgb() & cgbpAccessible(cycleCounter) ? bgpData[index] : 0xFF;
}
- unsigned cgbSpColorRead(const unsigned index, const unsigned long cycleCounter) {
+ unsigned cgbSpColorRead(const unsigned index, const unsigned cycleCounter) {
return ppu.cgb() & cgbpAccessible(cycleCounter) ? objpData[index] : 0xFF;
}
- void updateScreen(bool blanklcd, unsigned long cc);
- void resetCc(unsigned long oldCC, unsigned long newCc);
- void speedChange(unsigned long cycleCounter);
- bool vramAccessible(unsigned long cycleCounter);
- bool oamReadable(unsigned long cycleCounter);
- bool oamWritable(unsigned long cycleCounter);
- void wxChange(unsigned newValue, unsigned long cycleCounter);
- void wyChange(unsigned newValue, unsigned long cycleCounter);
- void oamChange(unsigned long cycleCounter);
- void oamChange(const unsigned char *oamram, unsigned long cycleCounter);
- void scxChange(unsigned newScx, unsigned long cycleCounter);
- void scyChange(unsigned newValue, unsigned long cycleCounter);
+ void updateScreen(bool blanklcd, unsigned cc, uint_least32_t* vbuffer, unsigned vpitch);
+ void resetCc(unsigned oldCC, unsigned newCc);
+ void speedChange(unsigned cycleCounter);
+ bool vramAccessible(unsigned cycleCounter);
+ bool oamReadable(unsigned cycleCounter);
+ bool oamWritable(unsigned cycleCounter);
+ void wxChange(unsigned newValue, unsigned cycleCounter);
+ void wyChange(unsigned newValue, unsigned cycleCounter);
+ void oamChange(unsigned cycleCounter);
+ void oamChange(const unsigned char *oamram, unsigned cycleCounter);
+ void scxChange(unsigned newScx, unsigned cycleCounter);
+ void scyChange(unsigned newValue, unsigned cycleCounter);
- void vramChange(const unsigned long cycleCounter) { update(cycleCounter); }
+ void vramChange(const unsigned cycleCounter) { update(cycleCounter); }
- unsigned getStat(unsigned lycReg, unsigned long cycleCounter);
+ unsigned getStat(unsigned lycReg, unsigned cycleCounter);
- unsigned getLyReg(const unsigned long cycleCounter) {
+ unsigned getLyReg(const unsigned cycleCounter) {
unsigned lyReg = 0;
if (ppu.lcdc() & 0x80) {
@@ -236,17 +252,17 @@ public:
return lyReg;
}
- unsigned long nextMode1IrqTime() const { return eventTimes_(MODE1_IRQ); }
+ unsigned nextMode1IrqTime() const { return eventTimes_(MODE1_IRQ); }
- void lcdcChange(unsigned data, unsigned long cycleCounter);
- void lcdstatChange(unsigned data, unsigned long cycleCounter);
- void lycRegChange(unsigned data, unsigned long cycleCounter);
+ void lcdcChange(unsigned data, unsigned cycleCounter);
+ void lcdstatChange(unsigned data, unsigned cycleCounter);
+ void lycRegChange(unsigned data, unsigned cycleCounter);
- void enableHdma(unsigned long cycleCounter);
- void disableHdma(unsigned long cycleCounter);
+ void enableHdma(unsigned cycleCounter);
+ void disableHdma(unsigned cycleCounter);
bool hdmaIsEnabled() const { return eventTimes_(HDMA_REQ) != DISABLED_TIME; }
- void update(unsigned long cycleCounter);
+ void update(unsigned cycleCounter);
bool isCgb() const { return ppu.cgb(); }
bool isDoubleSpeed() const { return ppu.lyCounter().isDoubleSpeed(); }
diff --git a/libgambatte/src/video/ly_counter.cpp b/libgambatte/src/video/ly_counter.cpp
index 4cb3918..6046852 100644
--- a/libgambatte/src/video/ly_counter.cpp
+++ b/libgambatte/src/video/ly_counter.cpp
@@ -19,6 +19,10 @@
#include "ly_counter.h"
#include "../savestate.h"
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
namespace gambatte {
LyCounter::LyCounter()
@@ -37,8 +41,8 @@ void LyCounter::doEvent() {
time_ = time_ + lineTime_;
}
-unsigned long LyCounter::nextLineCycle(const unsigned lineCycle, const unsigned long cycleCounter) const {
- unsigned long tmp = time_ + (lineCycle << ds);
+unsigned LyCounter::nextLineCycle(const unsigned lineCycle, const unsigned cycleCounter) const {
+ unsigned tmp = time_ + (lineCycle << ds);
if (tmp - cycleCounter > lineTime_)
tmp -= lineTime_;
@@ -46,8 +50,8 @@ unsigned long LyCounter::nextLineCycle(const unsigned lineCycle, const unsigned
return tmp;
}
-unsigned long LyCounter::nextFrameCycle(const unsigned long frameCycle, const unsigned long cycleCounter) const {
- unsigned long tmp = time_ + (((153U - ly()) * 456U + frameCycle) << ds);
+unsigned LyCounter::nextFrameCycle(const unsigned frameCycle, const unsigned cycleCounter) const {
+ unsigned tmp = time_ + (((153U - ly()) * 456U + frameCycle) << ds);
if (tmp - cycleCounter > 70224U << ds)
tmp -= 70224U << ds;
@@ -55,7 +59,7 @@ unsigned long LyCounter::nextFrameCycle(const unsigned long frameCycle, const un
return tmp;
}
-void LyCounter::reset(const unsigned long videoCycles, const unsigned long lastUpdate) {
+void LyCounter::reset(const unsigned videoCycles, const unsigned lastUpdate) {
ly_ = videoCycles / 456;
time_ = lastUpdate + ((456 - (videoCycles - ly_ * 456ul)) << isDoubleSpeed());
}
diff --git a/libgambatte/src/video/ly_counter.h b/libgambatte/src/video/ly_counter.h
index d376540..0af06cc 100644
--- a/libgambatte/src/video/ly_counter.h
+++ b/libgambatte/src/video/ly_counter.h
@@ -18,13 +18,18 @@
***************************************************************************/
#ifndef LY_COUNTER_H
#define LY_COUNTER_H
+#include "../loadsave.h"
+
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
namespace gambatte {
struct SaveState;
class LyCounter {
- unsigned long time_;
+ unsigned time_;
unsigned short lineTime_;
unsigned char ly_;
bool ds;
@@ -34,21 +39,28 @@ public:
void doEvent();
bool isDoubleSpeed() const { return ds; }
- unsigned long frameCycles(const unsigned long cc) const {
+ unsigned frameCycles(const unsigned cc) const {
return ly_ * 456ul + lineCycles(cc);
}
- unsigned lineCycles(const unsigned long cc) const {
+ unsigned lineCycles(const unsigned cc) const {
return 456u - ((time_ - cc) >> isDoubleSpeed());
}
unsigned lineTime() const { return lineTime_; }
unsigned ly() const { return ly_; }
- unsigned long nextLineCycle(unsigned lineCycle, unsigned long cycleCounter) const;
- unsigned long nextFrameCycle(unsigned long frameCycle, unsigned long cycleCounter) const;
- void reset(unsigned long videoCycles, unsigned long lastUpdate);
+ unsigned nextLineCycle(unsigned lineCycle, unsigned cycleCounter) const;
+ unsigned nextFrameCycle(unsigned frameCycle, unsigned cycleCounter) const;
+ void reset(unsigned videoCycles, unsigned lastUpdate);
void setDoubleSpeed(bool ds_in);
- unsigned long time() const { return time_; }
+ unsigned time() const { return time_; }
+
+ void loadOrSave(loadsave& state) {
+ state(time_);
+ state(lineTime_);
+ state(ly_);
+ state(ds);
+ }
};
}
diff --git a/libgambatte/src/video/lyc_irq.cpp b/libgambatte/src/video/lyc_irq.cpp
index a16cb81..d840dc0 100644
--- a/libgambatte/src/video/lyc_irq.cpp
+++ b/libgambatte/src/video/lyc_irq.cpp
@@ -22,6 +22,10 @@
#include "savestate.h"
#include <algorithm>
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
namespace gambatte {
LycIrq::LycIrq() :
@@ -34,14 +38,14 @@ LycIrq::LycIrq() :
{
}
-static unsigned long schedule(const unsigned statReg, const unsigned lycReg, const LyCounter &lyCounter, const unsigned long cc) {
+static unsigned schedule(const unsigned statReg, const unsigned lycReg, const LyCounter &lyCounter, const unsigned cc) {
return (statReg & 0x40) && lycReg < 154
? lyCounter.nextFrameCycle(lycReg ? lycReg * 456 : 153 * 456 + 8, cc)
- : static_cast<unsigned long>(DISABLED_TIME);
+ : static_cast<unsigned>(DISABLED_TIME);
}
-void LycIrq::regChange(const unsigned statReg, const unsigned lycReg, const LyCounter &lyCounter, const unsigned long cc) {
- const unsigned long timeSrc = schedule(statReg, lycReg, lyCounter, cc);
+void LycIrq::regChange(const unsigned statReg, const unsigned lycReg, const LyCounter &lyCounter, const unsigned cc) {
+ const unsigned timeSrc = schedule(statReg, lycReg, lyCounter, cc);
statRegSrc_ = statReg;
lycRegSrc_ = lycReg;
time_ = std::min(time_, timeSrc);
@@ -89,7 +93,7 @@ void LycIrq::saveState(SaveState &state) const {
state.ppu.lyc = lycReg_;
}
-void LycIrq::reschedule(const LyCounter & lyCounter, const unsigned long cc) {
+void LycIrq::reschedule(const LyCounter & lyCounter, const unsigned cc) {
time_ = std::min(schedule(statReg_ , lycReg_ , lyCounter, cc),
schedule(statRegSrc_, lycRegSrc_, lyCounter, cc));
}
diff --git a/libgambatte/src/video/lyc_irq.h b/libgambatte/src/video/lyc_irq.h
index 18607b9..97f4a8e 100644
--- a/libgambatte/src/video/lyc_irq.h
+++ b/libgambatte/src/video/lyc_irq.h
@@ -19,20 +19,26 @@
#ifndef VIDEO_LYC_IRQ_H
#define VIDEO_LYC_IRQ_H
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
+#include "../loadsave.h"
+
namespace gambatte {
struct SaveState;
class LyCounter;
class LycIrq {
- unsigned long time_;
+ unsigned time_;
unsigned char lycRegSrc_;
unsigned char statRegSrc_;
unsigned char lycReg_;
unsigned char statReg_;
bool cgb_;
- void regChange(unsigned statReg, unsigned lycReg, const LyCounter &lyCounter, unsigned long cc);
+ void regChange(unsigned statReg, unsigned lycReg, const LyCounter &lyCounter, unsigned cc);
public:
LycIrq();
@@ -40,18 +46,27 @@ public:
unsigned lycReg() const { return lycRegSrc_; }
void loadState(const SaveState &state);
void saveState(SaveState &state) const;
- unsigned long time() const { return time_; }
+ unsigned time() const { return time_; }
void setCgb(const bool cgb) { cgb_ = cgb; }
void lcdReset();
- void reschedule(const LyCounter & lyCounter, unsigned long cc);
+ void reschedule(const LyCounter & lyCounter, unsigned cc);
- void statRegChange(unsigned statReg, const LyCounter &lyCounter, unsigned long cc) {
+ void statRegChange(unsigned statReg, const LyCounter &lyCounter, unsigned cc) {
regChange(statReg, lycRegSrc_, lyCounter, cc);
}
- void lycRegChange(unsigned lycReg, const LyCounter &lyCounter, unsigned long cc) {
+ void lycRegChange(unsigned lycReg, const LyCounter &lyCounter, unsigned cc) {
regChange(statRegSrc_, lycReg, lyCounter, cc);
}
+
+ void loadOrSave(loadsave& state) {
+ state(time_);
+ state(lycRegSrc_);
+ state(statRegSrc_);
+ state(lycReg_);
+ state(statReg_);
+ state(cgb_);
+ }
};
}
diff --git a/libgambatte/src/video/next_m0_time.h b/libgambatte/src/video/next_m0_time.h
index 148f471..1b0c338 100644
--- a/libgambatte/src/video/next_m0_time.h
+++ b/libgambatte/src/video/next_m0_time.h
@@ -1,6 +1,12 @@
#ifndef NEXT_M0_TIME_H_
#define NEXT_M0_TIME_H_
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
+#include "../loadsave.h"
+
namespace gambatte {
class NextM0Time {
@@ -11,6 +17,10 @@ public:
void predictNextM0Time(const class PPU &v);
void invalidatePredictedNextM0Time() { predictedNextM0Time_ = 0; }
unsigned predictedNextM0Time() const { return predictedNextM0Time_; }
+
+ void loadOrSave(loadsave& state) {
+ state(predictedNextM0Time_);
+ }
};
}
diff --git a/libgambatte/src/video/ppu.cpp b/libgambatte/src/video/ppu.cpp
index 42fb822..1985be9 100644
--- a/libgambatte/src/video/ppu.cpp
+++ b/libgambatte/src/video/ppu.cpp
@@ -22,6 +22,10 @@
#include <cstring>
#include <cstddef>
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
namespace {
using namespace gambatte;
@@ -427,7 +431,7 @@ namespace M3Loop {
const unsigned attrib = p.spriteList[i].attrib;
unsigned spword = p.spwordList[i];
- const unsigned long *const spPalette = p.spPalette + (attrib >> 2 & 4);
+ const uint_least32_t *const spPalette = p.spPalette + (attrib >> 2 & 4);
uint_least32_t *d = dst + pos;
if (!(attrib & 0x80)) {
@@ -543,7 +547,7 @@ namespace M3Loop {
xpos += n;
do {
- const unsigned long *const bgPalette = p.bgPalette + (nattrib & 7) * 4;
+ const uint_least32_t *const bgPalette = p.bgPalette + (nattrib & 7) * 4;
dst[0] = bgPalette[ ntileword & 0x0003 ];
dst[1] = bgPalette[(ntileword & 0x000C) >> 2];
dst[2] = bgPalette[(ntileword & 0x0030) >> 4];
@@ -581,7 +585,7 @@ namespace M3Loop {
uint_least32_t *const dst = dbufline + (xpos - 8);
const unsigned tileword = p.ntileword;
const unsigned attrib = p.nattrib;
- const unsigned long *const bgPalette = p.bgPalette + (attrib & 7) * 4;
+ const uint_least32_t *const bgPalette = p.bgPalette + (attrib & 7) * 4;
dst[0] = bgPalette[ tileword & 0x0003 ];
dst[1] = bgPalette[(tileword & 0x000C) >> 2];
@@ -617,7 +621,7 @@ namespace M3Loop {
const unsigned char id = p.spriteList[i].oampos;
const unsigned sattrib = p.spriteList[i].attrib;
unsigned spword = p.spwordList[i];
- const unsigned long *const spPalette = p.spPalette + (sattrib & 7) * 4;
+ const uint_least32_t *const spPalette = p.spPalette + (sattrib & 7) * 4;
if (!((attrib | sattrib) & bgenmask)) {
unsigned char *const idt = idtab + pos;
@@ -772,7 +776,7 @@ namespace M3Loop {
}
const unsigned twdata = tileword & ((p.lcdc & 1) | p.cgb) * 3;
- unsigned long pixel = p.bgPalette[twdata + (p.attrib & 7) * 4];
+ uint_least32_t pixel = p.bgPalette[twdata + (p.attrib & 7) * 4];
int i = static_cast<int>(p.nextSprite) - 1;
if (i >= 0 && static_cast<int>(p.spriteList[i].spx) > xpos - 8) {
@@ -831,8 +835,8 @@ namespace M3Loop {
plotPixel(p);
}
- static unsigned long nextM2Time(const PPUPriv &p) {
- unsigned long nextm2 = p.lyCounter.isDoubleSpeed()
+ static unsigned nextM2Time(const PPUPriv &p) {
+ unsigned nextm2 = p.lyCounter.isDoubleSpeed()
? p.lyCounter.time() + (weMasterCheckPriorToLyIncLineCycle(true ) + M2_DS_OFFSET) * 2 - 456 * 2
: p.lyCounter.time() + weMasterCheckPriorToLyIncLineCycle(p.cgb) - 456 ;
@@ -845,11 +849,11 @@ namespace M3Loop {
static void xpos168(PPUPriv &p) {
p.lastM0Time = p.now - (p.cycles << p.lyCounter.isDoubleSpeed());
- const unsigned long nextm2 = nextM2Time(p);
+ const unsigned nextm2 = nextM2Time(p);
p.cycles = p.now >= nextm2
- ? (static_cast<long>(p.now - nextm2) >> p.lyCounter.isDoubleSpeed())
- : -(static_cast<long>(nextm2 - p.now) >> p.lyCounter.isDoubleSpeed());
+ ? (static_cast<signed>(p.now - nextm2) >> p.lyCounter.isDoubleSpeed())
+ : -(static_cast<signed>(nextm2 - p.now) >> p.lyCounter.isDoubleSpeed());
nextCall(0, p.lyCounter.ly() == 143 ? M2::Ly0::f0_ : M2::LyNon0::f0_, p);
}
@@ -1533,8 +1537,8 @@ std::size_t upperBound(const T a[], const K e) {
struct CycleState {
const PPUState *state;
- long cycle;
- operator long() const { return cycle; }
+ signed cycle;
+ operator signed() const { return cycle; }
};
static const PPUState * decodeM3LoopState(const unsigned state) {
@@ -1564,8 +1568,8 @@ static const PPUState * decodeM3LoopState(const unsigned state) {
return 0;
}
-static long cyclesUntilM0Upperbound(const PPUPriv &p) {
- long cycles = 168 - p.xpos + 6;
+static signed cyclesUntilM0Upperbound(const PPUPriv &p) {
+ signed cycles = 168 - p.xpos + 6;
for (unsigned i = p.nextSprite; i < 10 && p.spriteList[i].spx < 168; ++i)
cycles += 11;
@@ -1605,17 +1609,17 @@ static void loadSpriteList(PPUPriv &p, const SaveState &ss) {
void PPU::loadState(const SaveState &ss, const unsigned char *const oamram) {
const PPUState *const m3loopState = decodeM3LoopState(ss.ppu.state);
- const long videoCycles = std::min(ss.ppu.videoCycles, 70223UL);
+ const signed videoCycles = std::min(ss.ppu.videoCycles, 70223u);
const bool ds = p_.cgb & ss.mem.ioamhram.get()[0x14D] >> 7;
- const long vcycs = videoCycles - ds * M2_DS_OFFSET < 0
+ const signed vcycs = videoCycles - ds * M2_DS_OFFSET < 0
? videoCycles - ds * M2_DS_OFFSET + 70224
: videoCycles - ds * M2_DS_OFFSET;
- const long lineCycles = static_cast<unsigned long>(vcycs) % 456;
+ const signed lineCycles = static_cast<unsigned>(vcycs) % 456;
p_.now = ss.cpu.cycleCounter;
p_.lcdc = ss.mem.ioamhram.get()[0x140];
p_.lyCounter.setDoubleSpeed(ds);
- p_.lyCounter.reset(std::min(ss.ppu.videoCycles, 70223ul), ss.cpu.cycleCounter);
+ p_.lyCounter.reset(std::min(ss.ppu.videoCycles, 70223u), ss.cpu.cycleCounter);
p_.spriteMapper.loadState(ss, oamram);
p_.winYPos = ss.ppu.winYPos;
p_.scy = ss.mem.ioamhram.get()[0x142];
@@ -1639,10 +1643,10 @@ void PPU::loadState(const SaveState &ss, const unsigned char *const oamram) {
loadSpriteList(p_, ss);
if (m3loopState && videoCycles < 144 * 456L && p_.xpos < 168
- && lineCycles + cyclesUntilM0Upperbound(p_) < static_cast<long>(weMasterCheckPriorToLyIncLineCycle(p_.cgb))) {
+ && lineCycles + cyclesUntilM0Upperbound(p_) < static_cast<signed>(weMasterCheckPriorToLyIncLineCycle(p_.cgb))) {
p_.nextCallPtr = m3loopState;
p_.cycles = -1;
- } else if (vcycs < 143 * 456L + static_cast<long>(m3StartLineCycle(p_.cgb)) + MAX_M3START_CYCLES) {
+ } else if (vcycs < 143 * 456L + static_cast<signed>(m3StartLineCycle(p_.cgb)) + MAX_M3START_CYCLES) {
const struct CycleState lineCycleStates[] = {
{ &M3Start::f0_, m3StartLineCycle(p_.cgb) },
{ &M3Start::f1_, m3StartLineCycle(p_.cgb) + MAX_M3START_CYCLES },
@@ -1673,9 +1677,9 @@ void PPU::reset(const unsigned char *const oamram, const unsigned char *const vr
p_.spriteMapper.reset(oamram, cgb);
}
-void PPU::resetCc(const unsigned long oldCc, const unsigned long newCc) {
- const unsigned long dec = oldCc - newCc;
- const unsigned long videoCycles = p_.lcdc & 0x80 ? p_.lyCounter.frameCycles(p_.now) : 0;
+void PPU::resetCc(const unsigned oldCc, const unsigned newCc) {
+ const unsigned dec = oldCc - newCc;
+ const unsigned videoCycles = p_.lcdc & 0x80 ? p_.lyCounter.frameCycles(p_.now) : 0;
p_.now -= dec;
p_.lastM0Time = p_.lastM0Time ? p_.lastM0Time - dec : p_.lastM0Time;
@@ -1683,8 +1687,8 @@ void PPU::resetCc(const unsigned long oldCc, const unsigned long newCc) {
p_.spriteMapper.resetCycleCounter(oldCc, newCc);
}
-void PPU::speedChange(const unsigned long cycleCounter) {
- const unsigned long videoCycles = p_.lcdc & 0x80 ? p_.lyCounter.frameCycles(p_.now) : 0;
+void PPU::speedChange(const unsigned cycleCounter) {
+ const unsigned videoCycles = p_.lcdc & 0x80 ? p_.lyCounter.frameCycles(p_.now) : 0;
p_.spriteMapper.preSpeedChange(cycleCounter);
p_.lyCounter.setDoubleSpeed(!p_.lyCounter.isDoubleSpeed());
@@ -1699,11 +1703,11 @@ void PPU::speedChange(const unsigned long cycleCounter) {
}
}
-unsigned long PPU::predictedNextXposTime(const unsigned xpos) const {
+unsigned PPU::predictedNextXposTime(const unsigned xpos) const {
return p_.now + (p_.nextCallPtr->predictCyclesUntilXpos_f(p_, xpos, -p_.cycles) << p_.lyCounter.isDoubleSpeed());
}
-void PPU::setLcdc(const unsigned lcdc, const unsigned long cc) {
+void PPU::setLcdc(const unsigned lcdc, const unsigned cc) {
if ((p_.lcdc ^ lcdc) & lcdc & 0x80) {
p_.now = cc;
p_.lastM0Time = 0;
@@ -1733,7 +1737,7 @@ void PPU::setLcdc(const unsigned lcdc, const unsigned long cc) {
p_.lcdc = lcdc;
}
-void PPU::update(const unsigned long cc) {
+void PPU::update(const unsigned cc) {
const int cycles = (cc - p_.now) >> p_.lyCounter.isDoubleSpeed();
p_.now += cycles << p_.lyCounter.isDoubleSpeed();
@@ -1745,4 +1749,69 @@ void PPU::update(const unsigned long cc) {
}
}
+void PPUPriv::loadOrSave(loadsave& state)
+{
+ state(bgPalette, 32);
+ state(spPalette, 32);
+
+ state.startEnumeration();
+ state.enumerate<const PPUState*>(nextCallPtr, NULL, 0);
+ state.enumerate<const PPUState*>(nextCallPtr, &M2::Ly0::f0_, 1);
+ state.enumerate<const PPUState*>(nextCallPtr, &M2::LyNon0::f0_, 2);
+ state.enumerate<const PPUState*>(nextCallPtr, &M2::LyNon0::f1_, 3);
+ state.enumerate<const PPUState*>(nextCallPtr, &M3Start::f0_, 4);
+ state.enumerate<const PPUState*>(nextCallPtr, &M3Start::f1_, 5);
+ state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::Tile::f0_, 6);
+ state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::Tile::f1_, 7);
+ state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::Tile::f2_, 8);
+ state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::Tile::f3_, 9);
+ state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::Tile::f4_, 10);
+ state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::Tile::f5_, 11);
+ state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::LoadSprites::f0_, 12);
+ state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::LoadSprites::f1_, 13);
+ state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::LoadSprites::f2_, 14);
+ state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::LoadSprites::f3_, 15);
+ state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::LoadSprites::f4_, 16);
+ state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::LoadSprites::f5_, 17);
+ state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::StartWindowDraw::f0_, 18);
+ state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::StartWindowDraw::f1_, 19);
+ state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::StartWindowDraw::f2_, 20);
+ state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::StartWindowDraw::f3_, 21);
+ state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::StartWindowDraw::f4_, 22);
+ state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::StartWindowDraw::f5_, 23);
+ state.endEnumeration();
+
+ state(now);
+ state(lastM0Time);
+ state(cycles);
+ state(tileword);
+ state(ntileword);
+ lyCounter.loadOrSave(state);
+ spriteMapper.loadOrSave(state);
+ framebuf.loadOrSave(state);
+
+ for(size_t i = 0; i < 11; i++)
+ spriteList[i].loadOrSave(state);
+ state(spwordList, 11);
+ state(lcdc);
+ state(scy);
+ state(scx);
+ state(wy);
+ state(wy2);
+ state(wx);
+ state(winDrawState);
+ state(wscx);
+ state(winYPos);
+ state(reg0);
+ state(reg1);
+ state(attrib);
+ state(nattrib);
+ state(nextSprite);
+ state(currentSprite);
+ state(xpos);
+ state(endx);
+ state(cgb);
+ state(weMaster);
+}
+
}
diff --git a/libgambatte/src/video/ppu.h b/libgambatte/src/video/ppu.h
index b3d9190..df9a6c1 100644
--- a/libgambatte/src/video/ppu.h
+++ b/libgambatte/src/video/ppu.h
@@ -19,26 +19,45 @@
#ifndef PPU_H
#define PPU_H
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
+#include <cstring>
#include "video/ly_counter.h"
#include "video/sprite_mapper.h"
#include "gbint.h"
+#include "../loadsave.h"
namespace gambatte {
class PPUFrameBuf {
- uint_least32_t *buf_;
+ mutable uint_least32_t buf_[160*144];
uint_least32_t *fbline_;
- int pitch_;
static uint_least32_t * nullfbline() { static uint_least32_t nullfbline_[160]; return nullfbline_; }
public:
- PPUFrameBuf() : buf_(0), fbline_(nullfbline()), pitch_(0) {}
+ PPUFrameBuf() : fbline_(nullfbline()) { memset(buf_, 0, sizeof(buf_)); }
uint_least32_t * fb() const { return buf_; }
uint_least32_t * fbline() const { return fbline_; }
- int pitch() const { return pitch_; }
- void setBuf(uint_least32_t *const buf, const int pitch) { buf_ = buf; pitch_ = pitch; fbline_ = nullfbline(); }
- void setFbline(const unsigned ly) { fbline_ = buf_ ? buf_ + static_cast<long>(ly) * static_cast<long>(pitch_) : nullfbline(); }
+ int pitch() const { return 160; }
+ void setFbline(const unsigned ly) { fbline_ = buf_ ? buf_ + ly * 160ULL : nullfbline(); }
+ void blit(uint_least32_t *const buf, const int pitch) const {
+ for(unsigned i = 0; i < 144; i++)
+ memcpy(buf + i * static_cast<signed>(pitch), buf_ + i * 160, 160 * sizeof(buf[0]));
+ }
+ void loadOrSave(loadsave& state) {
+ state(buf_, 160*144);
+ bool var = (fbline_ != nullfbline());
+ state(var);
+ if(var) {
+ unsigned x = fbline_ - buf_;
+ state(x);
+ fbline_ = buf_ + x;
+ } else
+ fbline_ = nullfbline();
+ }
};
struct PPUState {
@@ -49,26 +68,33 @@ struct PPUState {
// The PPU loop accesses a lot of state at once, so it's difficult to split this up much beyond grouping stuff into smaller structs.
struct PPUPriv {
- unsigned long bgPalette[8 * 4];
- unsigned long spPalette[8 * 4];
- struct Sprite { unsigned char spx, oampos, line, attrib; } spriteList[11];
- unsigned short spwordList[11];
- unsigned char nextSprite;
- unsigned char currentSprite;
+ uint_least32_t bgPalette[8 * 4];
+ uint_least32_t spPalette[8 * 4];
const unsigned char *vram;
const PPUState *nextCallPtr;
- unsigned long now;
- unsigned long lastM0Time;
- long cycles;
+ unsigned now;
+ unsigned lastM0Time;
+ signed cycles;
unsigned tileword;
unsigned ntileword;
- SpriteMapper spriteMapper;
LyCounter lyCounter;
+ SpriteMapper spriteMapper;
PPUFrameBuf framebuf;
+
+ struct Sprite {
+ unsigned char spx, oampos, line, attrib;
+ void loadOrSave(loadsave& state) {
+ state(spx);
+ state(oampos);
+ state(line);
+ state(attrib);
+ }
+ } spriteList[11];
+ unsigned short spwordList[11];
unsigned char lcdc;
unsigned char scy;
@@ -83,6 +109,8 @@ struct PPUPriv {
unsigned char reg1;
unsigned char attrib;
unsigned char nattrib;
+ unsigned char nextSprite;
+ unsigned char currentSprite;
unsigned char xpos;
unsigned char endx;
@@ -90,6 +118,7 @@ struct PPUPriv {
bool weMaster;
PPUPriv(NextM0Time &nextM0Time, const unsigned char *oamram, const unsigned char *vram);
+ void loadOrSave(loadsave& state);
};
class PPU {
@@ -100,34 +129,38 @@ public:
{
}
- unsigned long * bgPalette() { return p_.bgPalette; }
+ uint_least32_t * bgPalette() { return p_.bgPalette; }
bool cgb() const { return p_.cgb; }
void doLyCountEvent() { p_.lyCounter.doEvent(); }
- unsigned long doSpriteMapEvent(unsigned long time) { return p_.spriteMapper.doEvent(time); }
+ unsigned doSpriteMapEvent(unsigned time) { return p_.spriteMapper.doEvent(time); }
const PPUFrameBuf & frameBuf() const { return p_.framebuf; }
- bool inactivePeriodAfterDisplayEnable(unsigned long cc) const { return p_.spriteMapper.inactivePeriodAfterDisplayEnable(cc); }
- unsigned long lastM0Time() const { return p_.lastM0Time; }
+ bool inactivePeriodAfterDisplayEnable(unsigned cc) const { return p_.spriteMapper.inactivePeriodAfterDisplayEnable(cc); }
+ unsigned lastM0Time() const { return p_.lastM0Time; }
unsigned lcdc() const { return p_.lcdc; }
void loadState(const SaveState &state, const unsigned char *oamram);
const LyCounter & lyCounter() const { return p_.lyCounter; }
- unsigned long now() const { return p_.now; }
- void oamChange(unsigned long cc) { p_.spriteMapper.oamChange(cc); }
- void oamChange(const unsigned char *oamram, unsigned long cc) { p_.spriteMapper.oamChange(oamram, cc); }
- unsigned long predictedNextXposTime(unsigned xpos) const;
+ unsigned now() const { return p_.now; }
+ void oamChange(unsigned cc) { p_.spriteMapper.oamChange(cc); }
+ void oamChange(const unsigned char *oamram, unsigned cc) { p_.spriteMapper.oamChange(oamram, cc); }
+ unsigned predictedNextXposTime(unsigned xpos) const;
void reset(const unsigned char *oamram, const unsigned char *vram, bool cgb);
- void resetCc(unsigned long oldCc, unsigned long newCc);
+ void resetCc(unsigned oldCc, unsigned newCc);
void saveState(SaveState &ss) const;
- void setFrameBuf(uint_least32_t *buf, unsigned pitch) { p_.framebuf.setBuf(buf, pitch); }
- void setLcdc(unsigned lcdc, unsigned long cc);
+ void flipDisplay(uint_least32_t *buf, unsigned pitch) { p_.framebuf.blit(buf, pitch); }
+ void setLcdc(unsigned lcdc, unsigned cc);
void setScx(const unsigned scx) { p_.scx = scx; }
void setScy(const unsigned scy) { p_.scy = scy; }
void setStatePtrs(SaveState &ss) { p_.spriteMapper.setStatePtrs(ss); }
void setWx(const unsigned wx) { p_.wx = wx; }
void setWy(const unsigned wy) { p_.wy = wy; }
void updateWy2() { p_.wy2 = p_.wy; }
- void speedChange(unsigned long cycleCounter);
- unsigned long * spPalette() { return p_.spPalette; }
- void update(unsigned long cc);
+ void speedChange(unsigned cycleCounter);
+ uint_least32_t * spPalette() { return p_.spPalette; }
+ void update(unsigned cc);
+
+ void loadOrSave(loadsave& state) {
+ p_.loadOrSave(state);
+ }
};
}
diff --git a/libgambatte/src/video/sprite_mapper.cpp b/libgambatte/src/video/sprite_mapper.cpp
index 81dd1fd..11c76ad 100644
--- a/libgambatte/src/video/sprite_mapper.cpp
+++ b/libgambatte/src/video/sprite_mapper.cpp
@@ -23,6 +23,10 @@
#include <cstring>
#include <algorithm>
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
namespace gambatte {
SpriteMapper::OamReader::OamReader(const LyCounter &lyCounter, const unsigned char *oamram)
@@ -47,7 +51,7 @@ void SpriteMapper::OamReader::reset(const unsigned char *const oamram, const boo
}
}
-static unsigned toPosCycles(const unsigned long cc, const LyCounter &lyCounter) {
+static unsigned toPosCycles(const unsigned cc, const LyCounter &lyCounter) {
unsigned lc = lyCounter.lineCycles(cc) + 3 - lyCounter.isDoubleSpeed() * 3u;
if (lc >= 456)
@@ -56,7 +60,7 @@ static unsigned toPosCycles(const unsigned long cc, const LyCounter &lyCounter)
return lc;
}
-void SpriteMapper::OamReader::update(const unsigned long cc) {
+void SpriteMapper::OamReader::update(const unsigned cc) {
if (cc > lu) {
if (changed()) {
const unsigned lulc = toPosCycles(lu, lyCounter);
@@ -100,7 +104,7 @@ void SpriteMapper::OamReader::update(const unsigned long cc) {
}
}
-void SpriteMapper::OamReader::change(const unsigned long cc) {
+void SpriteMapper::OamReader::change(const unsigned cc) {
update(cc);
lastChange = std::min(toPosCycles(lu, lyCounter), 80u);
}
@@ -117,7 +121,7 @@ void SpriteMapper::OamReader::loadState(const SaveState &ss, const unsigned char
change(lu);
}
-void SpriteMapper::OamReader::enableDisplay(const unsigned long cc) {
+void SpriteMapper::OamReader::enableDisplay(const unsigned cc) {
std::memset(buf, 0x00, sizeof(buf));
std::fill(szbuf, szbuf + 40, false);
lu = cc + (80 << lyCounter.isDoubleSpeed());
@@ -130,6 +134,7 @@ SpriteMapper::SpriteMapper(NextM0Time &nextM0Time,
nextM0Time_(nextM0Time),
oamReader(lyCounter, oamram)
{
+ memset(spritemap, 0, sizeof(spritemap));
clearMap();
}
@@ -173,10 +178,10 @@ void SpriteMapper::sortLine(const unsigned ly) const {
insertionSort(spritemap + ly * 10, spritemap + ly * 10 + num[ly], SpxLess(posbuf()));
}
-unsigned long SpriteMapper::doEvent(const unsigned long time) {
+unsigned SpriteMapper::doEvent(const unsigned time) {
oamReader.update(time);
mapSprites();
- return oamReader.changed() ? time + oamReader.lyCounter.lineTime() : static_cast<unsigned long>(DISABLED_TIME);
+ return oamReader.changed() ? time + oamReader.lyCounter.lineTime() : static_cast<unsigned>(DISABLED_TIME);
}
}
diff --git a/libgambatte/src/video/sprite_mapper.h b/libgambatte/src/video/sprite_mapper.h
index 22bf619..2358d2d 100644
--- a/libgambatte/src/video/sprite_mapper.h
+++ b/libgambatte/src/video/sprite_mapper.h
@@ -19,8 +19,13 @@
#ifndef SPRITE_MAPPER_H
#define SPRITE_MAPPER_H
+//
+// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
+// - Make it rerecording-friendly.
+
#include "ly_counter.h"
#include "../savestate.h"
+#include "../loadsave.h"
namespace gambatte {
class NextM0Time;
@@ -35,7 +40,7 @@ class SpriteMapper {
private:
const unsigned char *oamram;
- unsigned long lu;
+ unsigned lu;
unsigned char lastChange;
bool largeSpritesSrc;
bool cgb_;
@@ -43,20 +48,30 @@ class SpriteMapper {
public:
OamReader(const LyCounter &lyCounter, const unsigned char *oamram);
void reset(const unsigned char *oamram, bool cgb);
- void change(unsigned long cc);
- void change(const unsigned char *oamram, unsigned long cc) { change(cc); this->oamram = oamram; }
+ void change(unsigned cc);
+ void change(const unsigned char *oamram, unsigned cc) { change(cc); this->oamram = oamram; }
bool changed() const { return lastChange != 0xFF; }
bool largeSprites(unsigned spNr) const { return szbuf[spNr]; }
const unsigned char *oam() const { return oamram; }
- void resetCycleCounter(const unsigned long oldCc, const unsigned long newCc) { lu -= oldCc - newCc; }
+ void resetCycleCounter(const unsigned oldCc, const unsigned newCc) { lu -= oldCc - newCc; }
void setLargeSpritesSrc(const bool src) { largeSpritesSrc = src; }
- void update(unsigned long cc);
+ void update(unsigned cc);
const unsigned char *spritePosBuf() const { return buf; }
void setStatePtrs(SaveState &state);
- void enableDisplay(unsigned long cc);
+ void enableDisplay(unsigned cc);
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; }
+ bool inactivePeriodAfterDisplayEnable(const unsigned cc) const { return cc < lu; }
+
+ void loadOrSave(loadsave& state) {
+ state(buf, 80);
+ for(unsigned i = 0; i < 40; i++)
+ state(szbuf[i]);
+ state(lu);
+ state(lastChange);
+ state(largeSpritesSrc);
+ state(cgb_);
+ }
};
enum { NEED_SORTING_MASK = 0x80 };
@@ -89,22 +104,22 @@ public:
const LyCounter &lyCounter,
const unsigned char *oamram_in);
void reset(const unsigned char *oamram, bool cgb);
- unsigned long doEvent(unsigned long time);
+ unsigned doEvent(unsigned time);
bool largeSprites(unsigned spNr) const { return oamReader.largeSprites(spNr); }
unsigned numSprites(const unsigned ly) const { return num[ly] & ~NEED_SORTING_MASK; }
- void oamChange(unsigned long cc) { oamReader.change(cc); }
- void oamChange(const unsigned char *oamram, unsigned long cc) { oamReader.change(oamram, cc); }
+ void oamChange(unsigned cc) { oamReader.change(cc); }
+ void oamChange(const unsigned char *oamram, unsigned cc) { oamReader.change(oamram, cc); }
const unsigned char *oamram() const { return oamReader.oam(); }
const unsigned char *posbuf() const { return oamReader.spritePosBuf(); }
- void preSpeedChange(const unsigned long cc) { oamReader.update(cc); }
- void postSpeedChange(const unsigned long cc) { oamReader.change(cc); }
+ void preSpeedChange(const unsigned cc) { oamReader.update(cc); }
+ void postSpeedChange(const unsigned cc) { oamReader.change(cc); }
- void resetCycleCounter(const unsigned long oldCc, const unsigned long newCc) {
+ void resetCycleCounter(const unsigned oldCc, const unsigned newCc) {
oamReader.update(oldCc);
oamReader.resetCycleCounter(oldCc, newCc);
}
- static unsigned long schedule(const LyCounter &lyCounter, const unsigned long cycleCounter) {
+ static unsigned schedule(const LyCounter &lyCounter, const unsigned cycleCounter) {
return lyCounter.nextLineCycle(80, cycleCounter);
}
@@ -118,10 +133,16 @@ public:
}
void setStatePtrs(SaveState &state) { oamReader.setStatePtrs(state); }
- void enableDisplay(unsigned long cc) { oamReader.enableDisplay(cc); }
+ void enableDisplay(unsigned cc) { oamReader.enableDisplay(cc); }
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); }
+ bool inactivePeriodAfterDisplayEnable(unsigned cc) const { return oamReader.inactivePeriodAfterDisplayEnable(cc); }
+
+ void loadOrSave(loadsave& state) {
+ state(spritemap, 1440);
+ state(num, 144);
+ oamReader.loadOrSave(state);
+ }
};
}
--
1.8.5