From 0d7e4ec1a23f6c3c6f0eeb16b866a21308130e9a Mon Sep 17 00:00:00 2001 From: Ilari Liusvaara Date: Mon, 24 Sep 2012 17:49:40 +0300 Subject: [PATCH] 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 63443a0..7deeac5 100644 --- a/libgambatte/include/gambatte.h +++ b/libgambatte/include/gambatte.h @@ -23,6 +23,11 @@ #include "loadres.h" #include "gbint.h" #include +#include + +// +// 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& data, const std::vector& cmpdata); + /** Save savestate to given buffer. + */ + void saveState(std::vector& data); + /** Load savestate from given buffer. + */ + void loadState(const std::vector& 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 getWorkRam(); + + /** Get pointer and size to Save RAM. + * @return The pointer and size of Save RAM. + */ + std::pair getSaveRam(); + + /** Get pointer and size to I/O RAM. + * @return The pointer and size of I/O RAM. + */ + std::pair getIoRam(); + + /** Get pointer and size to Video RAM. + * @return The pointer and size of Video RAM. + */ + std::pair 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 7ef835f..0e40bc4 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 35b29fa..34e3f6b 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 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 d36d1d8..baf6cf2 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 45ca0c7..ff97a3e 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 getWorkRam() { return memory.getWorkRam(); } + std::pair getSaveRam() { return memory.getSaveRam(); } + std::pair getIoRam() { return memory.getIoRam(); } + std::pair getVideoRam() { return memory.getVideoRam(); }; + }; } diff --git a/libgambatte/src/file/file.cpp b/libgambatte/src/file/file.cpp index fff66d2..5b5739e 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 + +// +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara +// - Make it rerecording-friendly. std::auto_ptr gambatte::newFileInstance(const std::string &filepath) { return std::auto_ptr(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::newFileInstance(const unsigned char* image, size_t isize) { + return std::auto_ptr(new MemoryFile(image, isize)); +} diff --git a/libgambatte/src/file/file.h b/libgambatte/src/file/file.h index 23b23c0..1a95543 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 #include +// +// Modified 2012-07-10 by H. Ilari Liusvaara +// - New API methods. + namespace gambatte { class File { @@ -37,6 +46,7 @@ public: }; std::auto_ptr newFileInstance(const std::string &filepath); +std::auto_ptr newFileInstance(const unsigned char* image, size_t isize); } diff --git a/libgambatte/src/gambatte.cpp b/libgambatte/src/gambatte.cpp index e415cc2..8cc4539 100644 --- a/libgambatte/src/gambatte.cpp +++ b/libgambatte/src/gambatte.cpp @@ -25,6 +25,10 @@ #include #include +// +// 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(samples) - (cyclesSinceBlit >> 1); + return cyclesSinceBlit < 0 ? cyclesSinceBlit : static_cast(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()); +} + +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()); - } + 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& data, const std::vector& cmpdata) { + if (p_->cpu.loaded()) { + loadsave_save l(cmpdata); + p_->cpu.loadOrSave(l); + data = l.get(); + } +} + +void GB::saveState(std::vector& data) { + if (p_->cpu.loaded()) { + loadsave_save l; + p_->cpu.loadOrSave(l); + data = l.get(); + } +} + +void GB::loadState(const std::vector& 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 GB::getWorkRam() { + return p_->cpu.getWorkRam(); +} + +std::pair GB::getSaveRam() { + return p_->cpu.getSaveRam(); +} + +std::pair GB::getIoRam() { + return p_->cpu.getIoRam(); +} + +std::pair GB::getVideoRam() { + return p_->cpu.getVideoRam(); +} + +void GB::set_walltime_fn(time_t (*_walltime)()) +{ + walltime = _walltime; +} + +std::string GB::version() +{ + return "SVN358"; +} + } diff --git a/libgambatte/src/initstate.cpp b/libgambatte/src/initstate.cpp index 0d61e66..fdad344 100644 --- a/libgambatte/src/initstate.cpp +++ b/libgambatte/src/initstate.cpp @@ -24,6 +24,10 @@ #include #include +// +// 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 0aba307..171b6d0 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 + 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 66db0c7..230bdc2 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 d8f2f10..e0619e3 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 #include +#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 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 914f617..f63c0a1 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(intFlags.imeOrHalted() && pendingIrqs() ? minIntTime : static_cast(DISABLED_TIME)); + eventTimes.setValue(intFlags.imeOrHalted() && pendingIrqs() ? minIntTime : static_cast(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(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(pendingIrqs() ? minIntTime : static_cast(DISABLED_TIME)); + eventTimes.setValue(pendingIrqs() ? minIntTime : static_cast(DISABLED_TIME)); } void InterruptRequester::setIfreg(const unsigned ifreg) { ifreg_ = ifreg; if (intFlags.imeOrHalted()) - eventTimes.setValue(pendingIrqs() ? minIntTime : static_cast(DISABLED_TIME)); + eventTimes.setValue(pendingIrqs() ? minIntTime : static_cast(DISABLED_TIME)); } } diff --git a/libgambatte/src/interruptrequester.h b/libgambatte/src/interruptrequester.h index 78c9d3f..61326fb 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 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(eventTimes.min()); } - unsigned long minEventTime() const { return eventTimes.minValue(); } - template void setEventTime(unsigned long value) { eventTimes.setValue(value); } - void setEventTime(const MemEventId id, unsigned long value) { eventTimes.setValue(id, value); } - unsigned long eventTime(MemEventId id) const { return eventTimes.value(id); } + unsigned minEventTime() const { return eventTimes.minValue(); } + template void setEventTime(unsigned value) { eventTimes.setValue(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(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 +#include +#include + +namespace gambatte { + +loadsave::~loadsave() throw() {} + +loadsave_load::loadsave_load(const std::vector& _memory) + : memory(_memory) +{ + ptr = 0; +} + +template 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 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 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 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 void loadsave_save::do_op(T& x, unsigned char _tag) +{ + pushbytes((char*)&_tag, 1); + do_op(x); +} + +template 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& _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 loadsave_save::get() +{ + std::vector 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 +#include +#include +#include + +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 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& memory; + size_t ptr; + template inline void do_op(T& x); + template inline void do_op(T& x, unsigned char _tag); + template void do_op(T* x, size_t s, unsigned char _tag); + public: + loadsave_load(const std::vector& _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> memory; + size_t nextptr; + size_t used; + inline void pushbytes(char* bytes, size_t amount); + template inline void do_op(T& x); + template inline void do_op(T& x, unsigned char _tag); + template void do_op(T* x, size_t s, unsigned char _tag); + std::vector cmp; + public: + loadsave_save(); + loadsave_save(const std::vector& _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 get(); + }; +} + +#endif diff --git a/libgambatte/src/mem/cartridge.cpp b/libgambatte/src/mem/cartridge.cpp index 258a5a0..b91ea93 100644 --- a/libgambatte/src/mem/cartridge.cpp +++ b/libgambatte/src/mem/cartridge.cpp @@ -23,6 +23,10 @@ #include #include +// +// 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 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 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(memptrs.rambankdata()), memptrs.rambankdataend() - memptrs.rambankdata()); - enforce8bit(memptrs.rambankdata(), memptrs.rambankdataend() - memptrs.rambankdata()); + if (file.is_open()) { + file.read(reinterpret_cast(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(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(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 Cartridge::getSaveRam() { + size_t sramsize = memptrs.rambankdataend() - memptrs.rambankdata(); + return std::make_pair(memptrs.rambankdata(), sramsize); +} + +std::pair Cartridge::getVideoRam() { + size_t vramsize = memptrs.vramdataend() - memptrs.vramdata(); + return std::make_pair(memptrs.vramdata(), vramsize); +} + +std::pair 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 f9a427a..77abf60 100644 --- a/libgambatte/src/mem/cartridge.h +++ b/libgambatte/src/mem/cartridge.h @@ -26,9 +26,16 @@ #include #include #include +#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 ggUndoList; - + bool memoryCartridge; + time_t memoryCartridgeRtcBase; + std::vector 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(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 getWorkRam(); + std::pair getSaveRam(); + std::pair getVideoRam(); + }; } diff --git a/libgambatte/src/mem/memptrs.cpp b/libgambatte/src/mem/memptrs.cpp index c43229c..b3073b5 100644 --- a/libgambatte/src/mem/memptrs.cpp +++ b/libgambatte/src/mem/memptrs.cpp @@ -20,6 +20,10 @@ #include #include +// +// 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 e7c7d10..262e5ef 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 993db29..62e0dac 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(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(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 c5b3b5a..206b46e 100644 --- a/libgambatte/src/mem/rtc.h +++ b/libgambatte/src/mem/rtc.h @@ -20,6 +20,12 @@ #define RTC_H #include +#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 c2c2470..6bff44e 100644 --- a/libgambatte/src/memory.cpp +++ b/libgambatte/src/memory.cpp @@ -23,9 +23,13 @@ #include "savestate.h" #include +// +// 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(144*456ul); intreq.setEventTime(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(intreq.eventTime(BLIT) + (70224 << isDoubleSpeed())); intreq.setEventTime(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(DISABLED_TIME); intreq.setEventTime(DISABLED_TIME); @@ -188,7 +193,7 @@ unsigned long Memory::event(unsigned long cycleCounter) { break; case OAM: intreq.setEventTime(lastOamDmaUpdate == DISABLED_TIME ? - static_cast(DISABLED_TIME) : intreq.eventTime(OAM) + 0xA0 * 4); + static_cast(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(dmaDest) + length) & 0x10000) { + if ((static_cast(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((data & 0x81) == 0x81 ? (data & isCgb() * 2 ? (cycleCounter & ~0x7ul) + 0x10 * 8 : (cycleCounter & ~0xFFul) + 0x200 * 8) - : static_cast(DISABLED_TIME)); + : static_cast(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 f7837f6..4463798 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((cc - intreq.eventTime(BLIT)) >> isDoubleSpeed()); + signed cyclesSinceBlit(const unsigned cc) const { + return cc < intreq.eventTime(BLIT) ? -1 : static_cast((cc - intreq.eventTime(BLIT)) >> isDoubleSpeed()); } void halt() { intreq.halt(); } - void ei(unsigned long cycleCounter) { if (!ime()) { intreq.ei(cycleCounter); } } + void 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 getWorkRam() { return cart.getWorkRam(); } + std::pair getSaveRam() { return cart.getSaveRam(); } + std::pair getIoRam() { return std::make_pair(ioamhram, sizeof(ioamhram)); } + std::pair getVideoRam() { return cart.getVideoRam(); }; + }; } diff --git a/libgambatte/src/minkeeper.h b/libgambatte/src/minkeeper.h index e05572e..795e0b0 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 +#include "loadsave.h" namespace MinKeeperUtil { template 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::R]; template static void updateValue(MinKeeper &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::R); + } + int min() const { return a[0]; } - unsigned long minValue() const { return minValue_; } + unsigned minValue() const { return minValue_; } template - void setValue(const unsigned long cnt) { + void setValue(const unsigned cnt) { values[id] = cnt; updateValue(*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 typename MinKeeper::UpdateValueLut MinKeeper::updateValueLut; template -MinKeeper::MinKeeper(const unsigned long initValue) { +MinKeeper::MinKeeper(const unsigned initValue) { std::fill(values, values + ids, initValue); for (int i = 0; i < Num::R; ++i) { diff --git a/libgambatte/src/savestate.h b/libgambatte/src/savestate.h index 1916c90..d85220d 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 + namespace gambatte { class SaverList; @@ -27,20 +33,20 @@ struct SaveState { template 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 sram; Ptr wram; Ptr 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 oamReaderBuf; Ptr 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 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 14d994e..5bf1e81 100644 --- a/libgambatte/src/sound.cpp +++ b/libgambatte/src/sound.cpp @@ -21,6 +21,10 @@ #include #include +// +// 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 71d5cdc..7fd3f82 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 82e623a..e282d0a 100644 --- a/libgambatte/src/sound/channel1.cpp +++ b/libgambatte/src/sound/channel1.cpp @@ -20,6 +20,9 @@ #include "../savestate.h" #include +// +// 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(nextEventUnit, NULL, 0); + state.enumerate(nextEventUnit, &sweepUnit, 1); + state.enumerate(nextEventUnit, &envelopeUnit, 2); + state.enumerate(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 9c77277..1f4eba9 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; @@ -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 91e15ee..16905bd 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(nextEventUnit, NULL, 0); + state.enumerate(nextEventUnit, &lengthCounter, 1); + state.enumerate(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 d6ba821..b654da9 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 a1bde94..a3f3aa4 100644 --- a/libgambatte/src/sound/channel3.cpp +++ b/libgambatte/src/sound/channel3.cpp @@ -21,6 +21,10 @@ #include #include +// +// 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; @@ -181,7 +185,7 @@ void Channel3::update(uint_least32_t *buf, const unsigned long soBaseVol, unsign } } else { if (outBase) { - const unsigned long out = outBase * (0 - 15ul); + const unsigned out = outBase * (0 - 15ul); *buf += out - prevOut; prevOut = out; @@ -208,4 +212,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 e80ec68..8fe0fe3 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 35c3c00..9d89db7 100644 --- a/libgambatte/src/sound/channel4.cpp +++ b/libgambatte/src/sound/channel4.cpp @@ -20,7 +20,11 @@ #include "../savestate.h" #include -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(nextEventUnit, NULL, 0); + state.enumerate(nextEventUnit, &lengthCounter, 1); + state.enumerate(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 00b0b5a..d52d9f7 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 23b8e52..83cedbf 100644 --- a/libgambatte/src/sound/duty_unit.cpp +++ b/libgambatte/src/sound/duty_unit.cpp @@ -19,6 +19,10 @@ #include "duty_unit.h" #include +// +// 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 f0fc49c..cf7f3b3 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 7bcb783..6036256 100644 --- a/libgambatte/src/sound/envelope_unit.cpp +++ b/libgambatte/src/sound/envelope_unit.cpp @@ -19,12 +19,16 @@ #include "envelope_unit.h" #include +// +// 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 fc2551c..aa0cac4 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 d295dc0..0fd9503 100644 --- a/libgambatte/src/sound/length_counter.cpp +++ b/libgambatte/src/sound/length_counter.cpp @@ -20,6 +20,10 @@ #include "master_disabler.h" #include +// +// 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(COUNTER_DISABLED); + counter = (nr4 & 0x40) ?( (cycleCounter >> 13) + lengthCounter) << 13 : static_cast(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 3360ab1..f21fc9d 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 b1ca691..55b69a9 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 ac785e5..4ef164f 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 -void StaticOutputTester::operator()(const unsigned long cc) { +void StaticOutputTester::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 4ec0c52..ffd91a3 100644 --- a/libgambatte/src/state_osd_elements.cpp +++ b/libgambatte/src/state_osd_elements.cpp @@ -22,6 +22,10 @@ #include #include +// +// 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 4564eeb..023ddf9 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 6491bac..b2887ca 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 +#include 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& data); + static bool loadState(SaveState &state, const std::vector& data); }; } diff --git a/libgambatte/src/tima.cpp b/libgambatte/src/tima.cpp index 44ce31c..56b6e72 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(DISABLED_TIME) + static_cast(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 48e9ccd..2857934 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(time); } + unsigned nextIrqEventTime() const { return intreq.eventTime(TIMA); } + void setNextIrqEventTime(const unsigned time) const { intreq.setEventTime(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 3aac3e4..095f2cf 100644 --- a/libgambatte/src/video.cpp +++ b/libgambatte/src/video.cpp @@ -21,19 +21,23 @@ #include #include +// +// 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(state.ppu.pendingLcdstatIrq - ? ppu.now() + 1 : static_cast(DISABLED_TIME)); + ? ppu.now() + 1 : static_cast(DISABLED_TIME)); eventTimes_.setm(state.ppu.oldWy != state.mem.ioamhram.get()[0x14A] - ? ppu.now() + 1 : static_cast(DISABLED_TIME)); + ? ppu.now() + 1 : static_cast(DISABLED_TIME)); eventTimes_.set(ppu.lyCounter().time()); eventTimes_.setm(SpriteMapper::schedule(ppu.lyCounter(), ppu.now())); eventTimes_.setm(lycIrq.time()); eventTimes_.setm(ppu.lyCounter().nextFrameCycle(144 * 456, ppu.now())); eventTimes_.setm(mode2IrqSchedule(statReg, ppu.lyCounter(), ppu.now())); - eventTimes_.setm((statReg & 0x08) ? ppu.now() + state.ppu.nextM0Irq : static_cast(DISABLED_TIME)); + eventTimes_.setm((statReg & 0x08) ? ppu.now() + state.ppu.nextM0Irq : static_cast(DISABLED_TIME)); eventTimes_.setm(state.mem.hdmaTransfer ? nextHdmaTime(ppu.lastM0Time(), nextM0Time_.predictedNextM0Time(), ppu.now(), isDoubleSpeed()) - : static_cast(DISABLED_TIME)); + : static_cast(DISABLED_TIME)); } else for (int i = 0; i < NUM_MEM_EVENTS; ++i) eventTimes_.set(static_cast(i), DISABLED_TIME); @@ -212,7 +215,7 @@ struct Blend { }; template -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(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(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(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((statReg & 0x08) ? m0IrqTimeFromXpos166Time(ppu.predictedNextXposTime(166), ppu.cgb(), isDoubleSpeed()) - : static_cast(DISABLED_TIME)); + : static_cast(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 b7790f9..89ef7bf 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