5797 lines
192 KiB
Diff
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
|
|
|