From 148f2f6e1142f9d2caa9612dd9a68069426537f8 Mon Sep 17 00:00:00 2001 From: Ilari Liusvaara Date: Fri, 9 Aug 2013 20:13:11 +0300 Subject: [PATCH 1/4] Changes to make libgambatte rerecording friendly --- Makefile | 10 + libgambatte/Makefile | 18 ++ libgambatte/include/gambatte.h | 68 ++++++- libgambatte/src/bitmap_font.cpp | 10 +- libgambatte/src/bitmap_font.h | 6 +- libgambatte/src/cpu.cpp | 42 ++++- libgambatte/src/cpu.h | 27 ++- libgambatte/src/file/file.cpp | 34 ++++ libgambatte/src/file/file.h | 10 + libgambatte/src/gambatte.cpp | 127 +++++++++++-- libgambatte/src/initstate.cpp | 8 +- libgambatte/src/initstate.h | 8 +- libgambatte/src/interrupter.cpp | 17 +- libgambatte/src/interrupter.h | 17 +- libgambatte/src/interruptrequester.cpp | 23 ++- libgambatte/src/interruptrequester.h | 23 ++- libgambatte/src/loadsave.cpp | 266 +++++++++++++++++++++++++++ libgambatte/src/loadsave.h | 160 ++++++++++++++++ libgambatte/src/mem/cartridge.cpp | 159 +++++++++++++--- libgambatte/src/mem/cartridge.h | 51 +++-- libgambatte/src/mem/memptrs.cpp | 38 +++- libgambatte/src/mem/memptrs.h | 8 + libgambatte/src/mem/rtc.cpp | 56 +++++- libgambatte/src/mem/rtc.h | 12 +- libgambatte/src/memory.cpp | 104 +++++++---- libgambatte/src/memory.h | 87 +++++---- libgambatte/src/minkeeper.h | 29 ++- libgambatte/src/savestate.h | 47 ++--- libgambatte/src/sound.cpp | 33 +++- libgambatte/src/sound.h | 20 +- libgambatte/src/sound/channel1.cpp | 45 ++++- libgambatte/src/sound/channel1.h | 29 ++- libgambatte/src/sound/channel2.cpp | 40 +++- libgambatte/src/sound/channel2.h | 16 +- libgambatte/src/sound/channel3.cpp | 41 ++++- libgambatte/src/sound/channel3.h | 26 ++- libgambatte/src/sound/channel4.cpp | 59 ++++-- libgambatte/src/sound/channel4.h | 39 ++-- libgambatte/src/sound/duty_unit.cpp | 34 +++- libgambatte/src/sound/duty_unit.h | 27 ++- 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 | 12 +- 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 | 4 +- libgambatte/src/statesaver.h | 5 + libgambatte/src/tima.cpp | 35 ++-- libgambatte/src/tima.h | 28 +-- libgambatte/src/video.cpp | 146 ++++++++------- libgambatte/src/video.h | 123 +++++++------ libgambatte/src/video/ly_counter.cpp | 14 +- libgambatte/src/video/ly_counter.h | 26 ++- libgambatte/src/video/lyc_irq.cpp | 16 +- libgambatte/src/video/lyc_irq.h | 27 ++- libgambatte/src/video/m0_irq.h | 9 +- libgambatte/src/video/next_m0_time.h | 9 + libgambatte/src/video/ppu.cpp | 120 +++++++++--- libgambatte/src/video/ppu.h | 92 ++++++--- libgambatte/src/video/sprite_mapper.cpp | 17 +- libgambatte/src/video/sprite_mapper.h | 53 ++++-- 63 files changed, 2099 insertions(+), 592 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 f1a4633..2901fdf 100644 --- a/libgambatte/include/gambatte.h +++ b/libgambatte/include/gambatte.h @@ -24,6 +24,11 @@ #include "loadres.h" #include #include +#include + +// +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara +// - Make it rerecording-friendly. namespace gambatte { @@ -52,6 +57,15 @@ public: */ LoadRes load(std::string const &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' audio samples are produced in the * supplied audio buffer, or until a video frame has been drawn. @@ -80,8 +94,8 @@ public: * @return sample offset in audioBuf at which the video frame was completed, or -1 * if no new video frame was completed. */ - std::ptrdiff_t runFor(gambatte::uint_least32_t *videoBuf, std::ptrdiff_t pitch, - gambatte::uint_least32_t *audioBuf, std::size_t &samples); + signed runFor(gambatte::uint_least32_t *videoBuf, std::ptrdiff_t pitch, + gambatte::uint_least32_t *soundBuf, unsigned &samples); /** * Reset to initial state. @@ -93,7 +107,7 @@ public: * @param palNum 0 <= palNum < 3. One of BG_PALETTE, SP1_PALETTE and SP2_PALETTE. * @param colorNum 0 <= colorNum < 4 */ - void setDmgPaletteColor(int palNum, int colorNum, unsigned long rgb32); + void setDmgPaletteColor(int palNum, int colorNum, uint_least32_t rgb32); /** Sets the callback used for getting input state. */ void setInputGetter(InputGetter *getInput); @@ -149,6 +163,16 @@ public: */ bool loadState(std::string const &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). @@ -180,9 +204,45 @@ public: */ void setGameShark(std::string const &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(GB const &); GB & operator=(GB const &); @@ -190,4 +250,6 @@ private: } +#define GAMBATTE_USES_LOADRES + #endif diff --git a/libgambatte/src/bitmap_font.cpp b/libgambatte/src/bitmap_font.cpp index 614f94e..aec5d2e 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" #include @@ -288,7 +292,7 @@ namespace { class Rgb32Fill { public: - explicit Rgb32Fill(unsigned long color) + explicit Rgb32Fill(uint_least32_t color) : color_(color) { } @@ -298,12 +302,12 @@ public: } private: - unsigned long const color_; + uint_least32_t const color_; }; } -void print(gambatte::uint_least32_t *dest, std::ptrdiff_t pitch, unsigned long color, char const *chars) { +void print(gambatte::uint_least32_t *dest, std::ptrdiff_t pitch, uint_least32_t color, char const *chars) { print(dest, pitch, Rgb32Fill(color), chars); } diff --git a/libgambatte/src/bitmap_font.h b/libgambatte/src/bitmap_font.h index db83db8..81bc5d0 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" #include @@ -44,7 +48,7 @@ std::size_t getWidth(char const *chars); template void print(RandomAccessIterator dest, std::ptrdiff_t pitch, Fill fill, char const *chars); -void print(gambatte::uint_least32_t *dest, std::ptrdiff_t pitch, unsigned long color, char const *chars); +void print(gambatte::uint_least32_t *dest, std::ptrdiff_t pitch, uint_least32_t color, char const *chars); void utoa(unsigned u, char *a); // --- INTERFACE END --- diff --git a/libgambatte/src/cpu.cpp b/libgambatte/src/cpu.cpp index fee943a..06400c5 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() -: mem_(Interrupter(sp, pc_)) +CPU::CPU(time_t (**_getCurrentTime)()) +: mem_(Interrupter(sp, pc_), _getCurrentTime) , cycleCounter_(0) , pc_(0x100) , sp(0xFFFE) @@ -42,10 +46,10 @@ CPU::CPU() { } -long CPU::runFor(unsigned long const cycles) { +signed CPU::runFor(unsigned const cycles) { process(cycles); - long const csb = mem_.cyclesSinceBlit(cycleCounter_); + signed const csb = mem_.cyclesSinceBlit(cycleCounter_); if (cycleCounter_ & 0x80000000) cycleCounter_ = mem_.resetCounters(cycleCounter_); @@ -121,6 +125,26 @@ void CPU::loadState(SaveState const &state) { skip_ = state.cpu.skip; } +void CPU::loadOrSave(loadsave& state) +{ + mem_.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_); +} + // The main reasons for the use of macros is to more conveniently be able to tweak // which variables are local and which are not, combined with the fact that at the // time they were written GCC had a tendency to not be able to keep hot variables @@ -485,18 +509,18 @@ void CPU::loadState(SaveState const &state) { PC_MOD(high << 8 | low); \ } while (0) -void CPU::process(unsigned long const cycles) { +void CPU::process(unsigned const cycles) { mem_.setEndtime(cycleCounter_, cycles); unsigned char a = a_; - unsigned long cycleCounter = cycleCounter_; + unsigned cycleCounter = cycleCounter_; while (mem_.isActive()) { unsigned short pc = pc_; if (mem_.halted()) { if (cycleCounter < mem_.nextEventTime()) { - unsigned long cycles = mem_.nextEventTime() - cycleCounter; + unsigned cycles = mem_.nextEventTime() - cycleCounter; cycleCounter += cycles + (-cycles & 3); } } else while (cycleCounter < mem_.nextEventTime()) { @@ -591,7 +615,7 @@ void CPU::process(unsigned long const cycles) { cycleCounter = mem_.stop(cycleCounter); if (cycleCounter < mem_.nextEventTime()) { - unsigned long cycles = mem_.nextEventTime() - cycleCounter; + unsigned cycles = mem_.nextEventTime() - cycleCounter; cycleCounter += cycles + (-cycles & 3); } @@ -1005,7 +1029,7 @@ void CPU::process(unsigned long const cycles) { mem_.halt(); if (cycleCounter < mem_.nextEventTime()) { - unsigned long cycles = mem_.nextEventTime() - cycleCounter; + unsigned cycles = mem_.nextEventTime() - cycleCounter; cycleCounter += cycles + (-cycles & 3); } } diff --git a/libgambatte/src/cpu.h b/libgambatte/src/cpu.h index 01b82b9..9f9a11e 100644 --- a/libgambatte/src/cpu.h +++ b/libgambatte/src/cpu.h @@ -19,16 +19,22 @@ #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 { public: - CPU(); - long runFor(unsigned long cycles); + CPU(time_t (**_getCurrentTime)()); + signed runFor(unsigned cycles); void setStatePtrs(SaveState &state); void saveState(SaveState &state); + void loadOrSave(loadsave& state); void loadState(SaveState const &state); void loadSavedata() { mem_.loadSavedata(); } void saveSavedata() { mem_.saveSavedata(); } @@ -57,6 +63,10 @@ public: return mem_.loadROM(romfile, forceDmg, multicartCompat); } + LoadRes load(const unsigned char* image, size_t isize, bool forceDmg, bool multicartCompat) { + return mem_.loadROM(image, isize, forceDmg, multicartCompat); + } + bool loaded() const { return mem_.loaded(); } char const * romTitle() const { return mem_.romTitle(); } PakInfo const pakInfo(bool multicartCompat) const { return mem_.pakInfo(multicartCompat); } @@ -64,23 +74,30 @@ public: std::size_t fillSoundBuffer() { return mem_.fillSoundBuffer(cycleCounter_); } bool isCgb() const { return mem_.isCgb(); } - void setDmgPaletteColor(int palNum, int colorNum, unsigned long rgb32) { + void setDmgPaletteColor(int palNum, int colorNum, uint_least32_t rgb32) { mem_.setDmgPaletteColor(palNum, colorNum, rgb32); } void setGameGenie(std::string const &codes) { mem_.setGameGenie(codes); } void setGameShark(std::string const &codes) { mem_.setGameShark(codes); } + void setRtcBase(time_t time) { mem_.setRtcBase(time); } + time_t getRtcBase() { return mem_.getRtcBase(); } + std::pair getWorkRam() { return mem_.getWorkRam(); } + std::pair getSaveRam() { return mem_.getSaveRam(); } + std::pair getIoRam() { return mem_.getIoRam(); } + std::pair getVideoRam() { return mem_.getVideoRam(); }; + private: Memory mem_; - unsigned long cycleCounter_; + unsigned cycleCounter_; unsigned short pc_; unsigned short sp; unsigned hf1, hf2, zf, cf; unsigned char a_, b, c, d, e, /*f,*/ h, l; bool skip_; - void process(unsigned long cycles); + void process(unsigned cycles); }; } diff --git a/libgambatte/src/file/file.cpp b/libgambatte/src/file/file.cpp index e6922b2..130e0e2 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. transfer_ptr gambatte::newFileInstance(std::string const &filepath) { return transfer_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; + }; +} + +transfer_ptr gambatte::newFileInstance(const unsigned char* image, size_t isize) { + return transfer_ptr(new MemoryFile(image, isize)); +} diff --git a/libgambatte/src/file/file.h b/libgambatte/src/file/file.h index efde793..ed04461 100644 --- a/libgambatte/src/file/file.h +++ b/libgambatte/src/file/file.h @@ -22,6 +22,15 @@ 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. +// +// Modified 2012-07-10 by H. Ilari Liusvaara +// - New API methods. + +#include #include "transfer_ptr.h" #include @@ -37,6 +46,7 @@ public: }; transfer_ptr newFileInstance(std::string const &filepath); +transfer_ptr newFileInstance(const unsigned char* image, size_t isize); } diff --git a/libgambatte/src/gambatte.cpp b/libgambatte/src/gambatte.cpp index ffe35b3..0204557 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 std::string const itos(int i) { std::stringstream ss; ss << i; @@ -35,6 +39,14 @@ static std::string const statePath(std::string const &basePath, int stateNo) { return basePath + "_" + itos(stateNo) + ".gqs"; } +namespace +{ + time_t default_walltime() + { + return time(0); + } +} + namespace gambatte { struct GB::Priv { @@ -42,10 +54,10 @@ struct GB::Priv { 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()) @@ -54,8 +66,8 @@ GB::~GB() { delete p_; } -std::ptrdiff_t GB::runFor(gambatte::uint_least32_t *const videoBuf, std::ptrdiff_t const pitch, - gambatte::uint_least32_t *const soundBuf, std::size_t &samples) { +signed GB::runFor(gambatte::uint_least32_t *const videoBuf, std::ptrdiff_t const pitch, + gambatte::uint_least32_t *const soundBuf, unsigned &samples) { if (!p_->cpu.loaded()) { samples = 0; return -1; @@ -64,10 +76,10 @@ std::ptrdiff_t GB::runFor(gambatte::uint_least32_t *const videoBuf, std::ptrdiff p_->cpu.setVideoBuffer(videoBuf, pitch); p_->cpu.setSoundBuffer(soundBuf); - long const cyclesSinceBlit = p_->cpu.runFor(samples * 2); + signed const cyclesSinceBlit = p_->cpu.runFor(samples * 2); samples = p_->cpu.fillSoundBuffer(); return cyclesSinceBlit >= 0 - ? static_cast(samples) - (cyclesSinceBlit >> 1) + ? static_cast(samples) - (cyclesSinceBlit >> 1) : cyclesSinceBlit; } @@ -77,7 +89,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(); } @@ -91,24 +103,44 @@ void GB::setSaveDir(std::string const &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(transfer_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(transfer_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; } @@ -126,7 +158,7 @@ void GB::saveSavedata() { p_->cpu.saveSavedata(); } -void GB::setDmgPaletteColor(int palNum, int colorNum, unsigned long rgb32) { +void GB::setDmgPaletteColor(int palNum, int colorNum, uint_least32_t rgb32) { p_->cpu.setDmgPaletteColor(palNum, colorNum, rgb32); } @@ -176,6 +208,29 @@ bool GB::saveState(gambatte::uint_least32_t const *videoBuf, std::ptrdiff_t pitc 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; @@ -209,4 +264,38 @@ void GB::setGameShark(std::string const &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 "r537"; +} + } diff --git a/libgambatte/src/initstate.cpp b/libgambatte/src/initstate.cpp index 2a31f44..496a7d2 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, bool const cgb, bool const gbaCgbMode) { +void gambatte::setInitState(SaveState &state, bool const cgb, bool const gbaCgbMode, time_t starttime) { static unsigned char const cgbObjpDump[0x40] = { 0x00, 0x00, 0xF2, 0xAB, 0x61, 0xC2, 0xD9, 0xBA, @@ -1310,7 +1314,7 @@ void gambatte::setInitState(SaveState &state, bool const cgb, bool const 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 bdb33dd..f4d4a02 100644 --- a/libgambatte/src/initstate.h +++ b/libgambatte/src/initstate.h @@ -19,9 +19,15 @@ #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); } diff --git a/libgambatte/src/interrupter.cpp b/libgambatte/src/interrupter.cpp index ac958f9..f0770a8 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, unsigned short &pc) @@ -27,7 +31,7 @@ Interrupter::Interrupter(unsigned short &sp, unsigned short &pc) { } -unsigned long Interrupter::interrupt(unsigned const address, unsigned long cc, Memory &memory) { +unsigned Interrupter::interrupt(unsigned const address, unsigned cc, Memory &memory) { cc += 8; sp_ = (sp_ - 1) & 0xFFFF; memory.write(sp_, pc_ >> 8, cc); @@ -66,11 +70,20 @@ void Interrupter::setGameShark(std::string const &codes) { } } -void Interrupter::applyVblankCheats(unsigned long const cc, Memory &memory) { +void Interrupter::applyVblankCheats(unsigned const cc, 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, cc); } } +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 fa7314d..328f7a8 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 Memory; @@ -35,15 +47,16 @@ class Memory; class Interrupter { public: Interrupter(unsigned short &sp, unsigned short &pc); - unsigned long interrupt(unsigned address, unsigned long cycleCounter, Memory &memory); + unsigned interrupt(unsigned address, unsigned cycleCounter, Memory &memory); void setGameShark(std::string const &codes); + void loadOrSave(loadsave& state); private: unsigned short &sp_; unsigned short &pc_; std::vector gsCodes_; - void applyVblankCheats(unsigned long cc, Memory &mem); + void applyVblankCheats(unsigned cc, Memory &mem); }; } diff --git a/libgambatte/src/interruptrequester.cpp b/libgambatte/src/interruptrequester.cpp index 400733e..5091a81 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() @@ -34,6 +38,15 @@ void InterruptRequester::saveState(SaveState &state) const { state.mem.halted = halted(); } +void InterruptRequester::loadOrSave(loadsave& state) +{ + eventTimes_.loadOrSave(state); + state(minIntTime_); + state(ifreg_); + state(iereg_); + intFlags_.loadOrSave(state); +} + void InterruptRequester::loadState(SaveState const &state) { minIntTime_ = state.mem.minIntTime; ifreg_ = state.mem.ioamhram.get()[0x10F]; @@ -42,17 +55,17 @@ void InterruptRequester::loadState(SaveState const &state) { eventTimes_.setValue(intFlags_.imeOrHalted() && pendingIrqs() ? minIntTime_ - : static_cast(disabled_time)); + : static_cast(disabled_time)); } -void InterruptRequester::resetCc(unsigned long oldCc, unsigned long newCc) { +void InterruptRequester::resetCc(unsigned oldCc, unsigned newCc) { minIntTime_ = minIntTime_ < oldCc ? 0 : minIntTime_ - (oldCc - newCc); if (eventTimes_.value(intevent_interrupts) != disabled_time) eventTimes_.setValue(minIntTime_); } -void InterruptRequester::ei(unsigned long cc) { +void InterruptRequester::ei(unsigned cc) { intFlags_.setIme(); minIntTime_ = cc + 1; @@ -99,7 +112,7 @@ void InterruptRequester::setIereg(unsigned iereg) { if (intFlags_.imeOrHalted()) { eventTimes_.setValue(pendingIrqs() ? minIntTime_ - : static_cast(disabled_time)); + : static_cast(disabled_time)); } } @@ -109,7 +122,7 @@ void InterruptRequester::setIfreg(unsigned ifreg) { if (intFlags_.imeOrHalted()) { eventTimes_.setValue(pendingIrqs() ? minIntTime_ - : static_cast(disabled_time)); + : static_cast(disabled_time)); } } diff --git a/libgambatte/src/interruptrequester.h b/libgambatte/src/interruptrequester.h index 24da184..0d423dc 100644 --- a/libgambatte/src/interruptrequester.h +++ b/libgambatte/src/interruptrequester.h @@ -19,11 +19,17 @@ #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; enum IntEventId { intevent_unhalt, @@ -41,12 +47,12 @@ public: InterruptRequester(); void saveState(SaveState &) const; void loadState(SaveState const &); - void resetCc(unsigned long oldCc, unsigned long newCc); + 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(); @@ -54,12 +60,13 @@ public: void ackIrq(unsigned bit); void setIereg(unsigned iereg); void setIfreg(unsigned ifreg); + void loadOrSave(loadsave& state); IntEventId 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(IntEventId id, unsigned long value) { eventTimes_.setValue(id, value); } - unsigned long eventTime(IntEventId id) const { return eventTimes_.value(id); } + unsigned minEventTime() const { return eventTimes_.minValue(); } + template void setEventTime(unsigned value) { eventTimes_.setValue(value); } + void setEventTime(IntEventId id, unsigned value) { eventTimes_.setValue(id, value); } + unsigned eventTime(IntEventId id) const { return eventTimes_.value(id); } private: class IntFlags { @@ -74,13 +81,15 @@ private: void unsetHalted() { flags_ &= ~flag_halted; } void set(bool ime, bool halted) { flags_ = halted * flag_halted + ime * flag_ime; } + void loadOrSave(loadsave& state) { state(flags_); } + private: unsigned char flags_; enum { flag_ime = 1, flag_halted = 2 }; }; MinKeeper eventTimes_; - unsigned long minIntTime_; + unsigned minIntTime_; unsigned ifreg_; unsigned iereg_; IntFlags intFlags_; 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 75629f6..d593dc5 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 { @@ -61,6 +67,9 @@ public: enableRam_ = ss.enableRam; memptrs_.setRambank(enableRam_ ? MemPtrs::read_en | MemPtrs::write_en : 0, 0); } + void loadOrSave(loadsave& state) { + state(enableRam_); + } private: MemPtrs &memptrs_; @@ -130,6 +139,12 @@ public: setRombank(); } + void loadOrSave(loadsave& state) { + state(rombank_); + state(rambank_); + state(enableRam_); + state(rambankMode_); + } private: MemPtrs &memptrs_; unsigned char rombank_; @@ -197,6 +212,11 @@ public: virtual bool isAddressWithinAreaRombankCanBeMappedTo(unsigned addr, unsigned bank) const { return (addr < 0x4000) == ((bank & 0xF) == 0); } + void loadOrSave(loadsave& state) { + state(rombank_); + state(enableRam_); + state(rombank0Mode_); + } private: MemPtrs &memptrs_; @@ -251,6 +271,10 @@ public: memptrs_.setRambank(enableRam_ ? MemPtrs::read_en | MemPtrs::write_en : 0, 0); memptrs_.setRombank(rombank_ & (rombanks(memptrs_) - 1)); } + void loadOrSave(loadsave& state) { + state(rombank_); + state(enableRam_); + } private: MemPtrs &memptrs_; @@ -304,7 +328,12 @@ public: setRambank(); memptrs_.setRombank(rombank_ & (rombanks(memptrs_) - 1)); } - + void loadOrSave(loadsave& state) { + rtc_->loadOrSave(state); + state(rombank_); + state(rambank_); + state(enableRam_); + } private: MemPtrs &memptrs_; Rtc *const rtc_; @@ -374,7 +403,12 @@ public: setRambank(); setRombank(); } - + void loadOrSave(loadsave& state) { + state(rombank_); + state(rambank_); + state(enableRam_); + state(rambankMode_); + } private: MemPtrs &memptrs_; unsigned char rombank_; @@ -437,6 +471,11 @@ public: setRambank(); setRombank(); } + void loadOrSave(loadsave& state) { + state(rombank_); + state(rambank_); + state(enableRam_); + } private: MemPtrs &memptrs_; @@ -536,6 +575,15 @@ LoadRes Cartridge::loadROM(std::string const &romfile, bool const multicartCompat) { scoped_ptr const 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 scoped_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; @@ -626,7 +674,14 @@ LoadRes Cartridge::loadROM(std::string const &romfile, 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 type_plain: mbc_.reset(new Mbc0(memptrs_)); break; @@ -667,43 +722,67 @@ void Cartridge::loadSavedata() { std::string const &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 (file) { - unsigned long basetime = file.get() & 0xFF; - basetime = basetime << 8 | (file.get() & 0xFF); - basetime = basetime << 8 | (file.get() & 0xFF); - basetime = basetime << 8 | (file.get() & 0xFF); - rtc_.setBaseTime(basetime); + if(memoryCartridge) { + rtc_.setBaseTime(memoryCartridgeRtcBase); + } else { + std::ifstream file((sbp + ".rtc").c_str(), std::ios::binary | std::ios::in); + if (file) { + unsigned basetime = file.get() & 0xFF; + basetime = basetime << 8 | (file.get() & 0xFF); + basetime = basetime << 8 | (file.get() & 0xFF); + basetime = basetime << 8 | (file.get() & 0xFF); + rtc_.setBaseTime(basetime); + } } } } +void Cartridge::clearMemorySavedData() +{ + memoryCartridgeRtcBase = 0; + memoryCartridgeSram.resize(0); +} + void Cartridge::saveSavedata() { std::string const &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); - unsigned long const basetime = rtc_.baseTime(); - file.put(basetime >> 24 & 0xFF); - file.put(basetime >> 16 & 0xFF); - file.put(basetime >> 8 & 0xFF); - file.put(basetime & 0xFF); + if(memoryCartridge) { + memoryCartridgeRtcBase = rtc_.getBaseTime(); + } else { + std::ofstream file((sbp + ".rtc").c_str(), std::ios::binary | std::ios::out); + const unsigned basetime = rtc_.getBaseTime(); + file.put(basetime >> 24 & 0xFF); + file.put(basetime >> 16 & 0xFF); + file.put(basetime >> 8 & 0xFF); + file.put(basetime & 0xFF); + } } } @@ -764,4 +843,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 4c2d7b4..dd342b6 100644 --- a/libgambatte/src/mem/cartridge.h +++ b/libgambatte/src/mem/cartridge.h @@ -26,9 +26,16 @@ #include "scoped_ptr.h" #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,23 +43,16 @@ public: virtual void saveState(SaveState::Mem &ss) const = 0; virtual void loadState(SaveState::Mem const &ss) = 0; virtual bool isAddressWithinAreaRombankCanBeMappedTo(unsigned address, unsigned rombank) const = 0; + virtual void loadOrSave(loadsave& state) = 0; }; class Cartridge { public: + Cartridge(time_t (**_getCurrentTime)()); void setStatePtrs(SaveState &); void saveState(SaveState &) const; void loadState(SaveState const &); bool loaded() const { return mbc_.get(); } - unsigned char const * rmem(unsigned area) const { return memptrs_.rmem(area); } - unsigned char * wmem(unsigned area) const { return memptrs_.wmem(area); } - unsigned char * vramdata() const { return memptrs_.vramdata(); } - unsigned char * romdata(unsigned area) const { return memptrs_.romdata(area); } - unsigned char * wramdata(unsigned area) const { return memptrs_.wramdata(area); } - unsigned char const * rdisabledRam() const { return memptrs_.rdisabledRam(); } - unsigned char const * rsrambankptr() const { return memptrs_.rsrambankptr(); } - unsigned char * wsrambankptr() const { return memptrs_.wsrambankptr(); } - unsigned char * vrambankptr() const { return memptrs_.vrambankptr(); } OamDmaSrc oamDmaSrc() const { return memptrs_.oamDmaSrc(); } void setVrambank(unsigned bank) { memptrs_.setVrambank(bank); } void setWrambank(unsigned bank) { memptrs_.setWrambank(bank); } @@ -65,16 +65,40 @@ public: void saveSavedata(); std::string const saveBasePath() const; void setSaveDir(std::string const &dir); - LoadRes loadROM(std::string const &romfile, bool forceDmg, bool multicartCompat); char const * romTitle() const { return reinterpret_cast(memptrs_.romdata() + 0x134); } class PakInfo const pakInfo(bool multicartCompat) const; void setGameGenie(std::string const &codes); + const unsigned char * rmem(unsigned area) const { return memptrs_.rmem(area); } + unsigned char * wmem(unsigned area) const { return memptrs_.wmem(area); } + unsigned char * vramdata() const { return memptrs_.vramdata(); } + unsigned char * romdata(unsigned area) const { return memptrs_.romdata(area); } + unsigned char * wramdata(unsigned area) const { return memptrs_.wramdata(area); } + const unsigned char * rdisabledRam() const { return memptrs_.rdisabledRam(); } + const unsigned char * rsrambankptr() const { return memptrs_.rsrambankptr(); } + unsigned char * wsrambankptr() const { return memptrs_.wsrambankptr(); } + unsigned char * vrambankptr() const { return memptrs_.vrambankptr(); } + + void loadOrSave(loadsave& state); + void setRtcBase(time_t time) { rtc_.setBaseTime(time); } + time_t getRtcBase() { return rtc_.getBaseTime(); } + std::pair getWorkRam(); + std::pair getSaveRam(); + std::pair getVideoRam(); + LoadRes loadROM(const std::string &romfile, bool forceDmg, bool multicartCompat); + LoadRes loadROM(const unsigned char* image, size_t isize, bool forceDmg, bool multicartCompat); + LoadRes loadROM(File* rom, const bool forceDmg, const bool multicartCompat, const std::string& filename); + void clearMemorySavedData(); private: 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_; @@ -83,6 +107,9 @@ private: std::string defaultSaveBasePath_; std::string saveDir_; std::vector ggUndoList_; + bool memoryCartridge; + time_t memoryCartridgeRtcBase; + std::vector memoryCartridgeSram; void applyGameGenie(std::string const &code); }; diff --git a/libgambatte/src/mem/memptrs.cpp b/libgambatte/src/mem/memptrs.cpp index e29b855..f351d94 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() @@ -43,13 +47,11 @@ MemPtrs::~MemPtrs() { void MemPtrs::reset(unsigned const rombanks, unsigned const rambanks, unsigned const 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; @@ -155,4 +157,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 bd0e176..6553955 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, @@ -60,6 +66,7 @@ public: void setWrambank(unsigned bank); void setOamDmaSrc(OamDmaSrc oamDmaSrc); + void loadOrSave(loadsave& state); private: unsigned char const *rmem_[0x10]; unsigned char *wmem_[0x10]; @@ -72,6 +79,7 @@ private: unsigned char *rambankdata_; unsigned char *wramdataend_; OamDmaSrc oamDmaSrc_; + size_t memchunk_size; MemPtrs(MemPtrs const &); MemPtrs & operator=(MemPtrs const &); diff --git a/libgambatte/src/mem/rtc.cpp b/libgambatte/src/mem/rtc.cpp index 1a46867..4a98bbe 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_(0) , activeSet_(0) , baseTime_(0) @@ -34,11 +38,12 @@ Rtc::Rtc() , dataS_(0) , enabled_(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; @@ -112,44 +117,77 @@ void Rtc::loadState(SaveState const &state) { } void Rtc::setDh(unsigned const newDh) { - std::time_t const unixtime = dataDh_ & 0x40 ? haltTime_ : std::time(0); + std::time_t const unixtime = dataDh_ & 0x40 ? haltTime_ : (*getCurrentTime)(); std::time_t const oldHighdays = ((unixtime - baseTime_) / 86400) & 0x100; baseTime_ += oldHighdays * 86400; baseTime_ -= ((newDh & 0x1) << 8) * 86400; if ((dataDh_ ^ newDh) & 0x40) { if (newDh & 0x40) - haltTime_ = std::time(0); + haltTime_ = (*getCurrentTime)(); else - baseTime_ += std::time(0) - haltTime_; + baseTime_ += (*getCurrentTime)() - haltTime_; + } } void Rtc::setDl(unsigned const newLowdays) { - std::time_t const unixtime = dataDh_ & 0x40 ? haltTime_ : std::time(0); + std::time_t const unixtime = dataDh_ & 0x40 ? haltTime_ : (*getCurrentTime)(); std::time_t const oldLowdays = ((unixtime - baseTime_) / 86400) & 0xFF; baseTime_ += oldLowdays * 86400; baseTime_ -= newLowdays * 86400; } void Rtc::setH(unsigned const newHours) { - std::time_t const unixtime = dataDh_ & 0x40 ? haltTime_ : std::time(0); + std::time_t const unixtime = dataDh_ & 0x40 ? haltTime_ : (*getCurrentTime)(); std::time_t const oldHours = ((unixtime - baseTime_) / 3600) % 24; baseTime_ += oldHours * 3600; baseTime_ -= newHours * 3600; } void Rtc::setM(unsigned const newMinutes) { - std::time_t const unixtime = dataDh_ & 0x40 ? haltTime_ : std::time(0); + std::time_t const unixtime = dataDh_ & 0x40 ? haltTime_ : (*getCurrentTime)(); std::time_t const oldMinutes = ((unixtime - baseTime_) / 60) % 60; baseTime_ += oldMinutes * 60; baseTime_ -= newMinutes * 60; } void Rtc::setS(unsigned const newSeconds) { - std::time_t const unixtime = dataDh_ & 0x40 ? haltTime_ : std::time(0); + std::time_t const unixtime = dataDh_ & 0x40 ? haltTime_ : (*getCurrentTime)(); baseTime_ += (unixtime - baseTime_) % 60; baseTime_ -= newSeconds; } +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 2408067..81da102 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 { @@ -27,7 +33,7 @@ struct SaveState; class Rtc { public: - Rtc(); + Rtc(time_t (**_getCurrentTime)()); unsigned char const * activeData() const { return activeData_; } std::time_t baseTime() const { return baseTime_; } void setBaseTime(std::time_t baseTime) { baseTime_ = baseTime; } @@ -56,6 +62,9 @@ public: *activeData_ = data; } + std::time_t getBaseTime() const { return baseTime_; } + + void loadOrSave(loadsave& state); private: unsigned char *activeData_; void (Rtc::*activeSet_)(unsigned); @@ -77,6 +86,7 @@ private: void setH(unsigned newHours); void setM(unsigned newMinutes); void setS(unsigned newSeconds); + time_t (**getCurrentTime)(); }; } diff --git a/libgambatte/src/memory.cpp b/libgambatte/src/memory.cpp index 529e9f2..cc76f96 100644 --- a/libgambatte/src/memory.cpp +++ b/libgambatte/src/memory.cpp @@ -23,9 +23,13 @@ #include "video.h" #include +// +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara +// - Make it rerecording-friendly. + namespace gambatte { -Memory::Memory(Interrupter const &interrupter) +Memory::Memory(Interrupter const &interrupter, time_t (**_getCurrentTime)()) : getInput_(0) , divLastUpdate_(0) , lastOamDmaUpdate_(disabled_time) @@ -36,6 +40,7 @@ Memory::Memory(Interrupter const &interrupter) , oamDmaPos_(0xFE) , serialCnt_(0) , blanklcd_(false) +, cart_(_getCurrentTime) { intreq_.setEventTime(144 * 456ul); intreq_.setEventTime(0); @@ -49,7 +54,7 @@ void Memory::setStatePtrs(SaveState &state) { psg_.setStatePtrs(state); } -unsigned long Memory::saveState(SaveState &state, unsigned long cc) { +unsigned Memory::saveState(SaveState &state, unsigned cc) { cc = resetCounters(cc); nontrivial_ff_read(0x05, cc); nontrivial_ff_read(0x0F, cc); @@ -72,7 +77,7 @@ unsigned long Memory::saveState(SaveState &state, unsigned long cc) { return cc; } -static int serialCntFrom(unsigned long cyclesUntilDone, bool cgbFast) { +static int serialCntFrom(unsigned cyclesUntilDone, bool cgbFast) { return cgbFast ? (cyclesUntilDone + 0xF) >> 4 : (cyclesUntilDone + 0x1FF) >> 9; } @@ -118,7 +123,7 @@ void Memory::loadState(SaveState const &state) { std::memset(cart_.vramdata() + 0x2000, 0, 0x2000); } -void Memory::setEndtime(unsigned long cc, unsigned long inc) { +void Memory::setEndtime(unsigned cc, unsigned inc) { if (intreq_.eventTime(intevent_blit) <= cc) { intreq_.setEventTime(intreq_.eventTime(intevent_blit) + (70224 << isDoubleSpeed())); @@ -127,7 +132,7 @@ void Memory::setEndtime(unsigned long cc, unsigned long inc) { intreq_.setEventTime(cc + (inc << isDoubleSpeed())); } -void Memory::updateSerial(unsigned long const cc) { +void Memory::updateSerial(unsigned const cc) { if (intreq_.eventTime(intevent_serial) != disabled_time) { if (intreq_.eventTime(intevent_serial) <= cc) { ioamhram_[0x101] = (((ioamhram_[0x101] + 1) << serialCnt_) - 1) & 0xFF; @@ -143,18 +148,18 @@ void Memory::updateSerial(unsigned long const cc) { } } -void Memory::updateTimaIrq(unsigned long cc) { +void Memory::updateTimaIrq(unsigned cc) { while (intreq_.eventTime(intevent_tima) <= cc) tima_.doIrqEvent(TimaInterruptRequester(intreq_)); } -void Memory::updateIrqs(unsigned long cc) { +void Memory::updateIrqs(unsigned cc) { updateSerial(cc); updateTimaIrq(cc); lcd_.update(cc); } -unsigned long Memory::event(unsigned long cc) { +unsigned Memory::event(unsigned cc) { if (lastOamDmaUpdate_ != disabled_time) updateOamDma(cc); @@ -177,10 +182,10 @@ unsigned long Memory::event(unsigned long cc) { case intevent_blit: { bool const lcden = ioamhram_[0x140] & lcdc_en; - unsigned long blitTime = intreq_.eventTime(intevent_blit); + unsigned blitTime = intreq_.eventTime(intevent_blit); if (lcden | blanklcd_) { - lcd_.updateScreen(blanklcd_, cc); + lcd_.updateScreen(blanklcd_, cc, videoBuf_, pitch_); intreq_.setEventTime(disabled_time); intreq_.setEventTime(disabled_time); @@ -198,7 +203,7 @@ unsigned long Memory::event(unsigned long cc) { break; case intevent_oam: intreq_.setEventTime(lastOamDmaUpdate_ == disabled_time - ? static_cast(disabled_time) + ? static_cast(disabled_time) : intreq_.eventTime(intevent_oam) + 0xA0 * 4); break; case intevent_dma: @@ -211,7 +216,7 @@ unsigned long Memory::event(unsigned long cc) { ackDmaReq(intreq_); - if ((static_cast(dmaDest) + length) & 0x10000) { + if ((static_cast(dmaDest) + length) & 0x10000) { length = 0x10000 - dmaDest; ioamhram_[0x155] |= 0x80; } @@ -222,7 +227,7 @@ unsigned long Memory::event(unsigned long cc) { dmaLength = 0; { - unsigned long lOamDmaUpdate = lastOamDmaUpdate_; + unsigned lOamDmaUpdate = lastOamDmaUpdate_; lastOamDmaUpdate_ = disabled_time; while (length--) { @@ -304,7 +309,7 @@ unsigned long Memory::event(unsigned long cc) { return cc; } -unsigned long Memory::stop(unsigned long cc) { +unsigned Memory::stop(unsigned cc) { cc += 4 + 4 * isDoubleSpeed(); if (ioamhram_[0x14D] & isCgb()) { @@ -328,29 +333,29 @@ unsigned long Memory::stop(unsigned long cc) { return cc; } -static void decCycles(unsigned long &counter, unsigned long dec) { +static void decCycles(unsigned &counter, unsigned dec) { if (counter != disabled_time) counter -= dec; } -void Memory::decEventCycles(IntEventId eventId, unsigned long dec) { +void Memory::decEventCycles(IntEventId eventId, unsigned dec) { if (intreq_.eventTime(eventId) != disabled_time) intreq_.setEventTime(eventId, intreq_.eventTime(eventId) - dec); } -unsigned long Memory::resetCounters(unsigned long cc) { +unsigned Memory::resetCounters(unsigned cc) { if (lastOamDmaUpdate_ != disabled_time) updateOamDma(cc); updateIrqs(cc); { - unsigned long divinc = (cc - divLastUpdate_) >> 8; + unsigned divinc = (cc - divLastUpdate_) >> 8; ioamhram_[0x104] = (ioamhram_[0x104] + divinc) & 0xFF; divLastUpdate_ += divinc << 8; } - unsigned long const dec = cc < 0x10000 + unsigned const dec = cc < 0x10000 ? 0 : (cc & ~0x7FFFul) - 0x8000; decCycles(divLastUpdate_, dec); @@ -361,7 +366,7 @@ unsigned long Memory::resetCounters(unsigned long cc) { decEventCycles(intevent_end, dec); decEventCycles(intevent_unhalt, dec); - unsigned long const oldCC = cc; + unsigned const oldCC = cc; cc -= dec; intreq_.resetCc(oldCC, cc); tima_.resetCc(oldCC, cc, TimaInterruptRequester(intreq_)); @@ -388,7 +393,7 @@ void Memory::updateInput() { ioamhram_[0x100] &= button; } -void Memory::updateOamDma(unsigned long const cc) { +void Memory::updateOamDma(unsigned const cc) { unsigned char const *const oamDmaSrc = oamDmaSrcPtr(); unsigned cycles = (cc - lastOamDmaUpdate_) >> 2; @@ -441,17 +446,17 @@ unsigned char const * Memory::oamDmaSrcPtr() const { return ioamhram_[0x146] == 0xFF && !isCgb() ? oamDmaSrcZero() : cart_.rdisabledRam(); } -void Memory::startOamDma(unsigned long cc) { +void Memory::startOamDma(unsigned cc) { lcd_.oamChange(cart_.rdisabledRam(), cc); } -void Memory::endOamDma(unsigned long cc) { +void Memory::endOamDma(unsigned cc) { oamDmaPos_ = 0xFE; cart_.setOamDmaSrc(oam_dma_src_off); lcd_.oamChange(ioamhram_, cc); } -unsigned Memory::nontrivial_ff_read(unsigned const p, unsigned long const cc) { +unsigned Memory::nontrivial_ff_read(unsigned const p, unsigned const cc) { if (lastOamDmaUpdate_ != disabled_time) updateOamDma(cc); @@ -465,7 +470,7 @@ unsigned Memory::nontrivial_ff_read(unsigned const p, unsigned long const cc) { break; case 0x04: { - unsigned long divcycles = (cc - divLastUpdate_) >> 8; + unsigned divcycles = (cc - divLastUpdate_) >> 8; ioamhram_[0x104] = (ioamhram_[0x104] + divcycles) & 0xFF; divLastUpdate_ += divcycles << 8; } @@ -545,7 +550,7 @@ static bool isInOamDmaConflictArea(OamDmaSrc const oamDmaSrc, unsigned const p, && p - a[oamDmaSrc].exceptAreaLower >= a[oamDmaSrc].exceptAreaWidth; } -unsigned Memory::nontrivial_read(unsigned const p, unsigned long const cc) { +unsigned Memory::nontrivial_read(unsigned const p, unsigned const cc) { if (p < 0xFF80) { if (lastOamDmaUpdate_ != disabled_time) { updateOamDma(cc); @@ -585,7 +590,7 @@ unsigned Memory::nontrivial_read(unsigned const p, unsigned long const cc) { return ioamhram_[p - 0xFE00]; } -void Memory::nontrivial_ff_write(unsigned const p, unsigned data, unsigned long const cc) { +void Memory::nontrivial_ff_write(unsigned const p, unsigned data, unsigned const cc) { if (lastOamDmaUpdate_ != disabled_time) updateOamDma(cc); @@ -1005,7 +1010,7 @@ void Memory::nontrivial_ff_write(unsigned const p, unsigned data, unsigned long ioamhram_[p + 0x100] = data; } -void Memory::nontrivial_write(unsigned const p, unsigned const data, unsigned long const cc) { +void Memory::nontrivial_write(unsigned const p, unsigned const data, unsigned const cc) { if (lastOamDmaUpdate_ != disabled_time) { updateOamDma(cc); @@ -1043,20 +1048,53 @@ void Memory::nontrivial_write(unsigned const p, unsigned const data, unsigned lo ioamhram_[p - 0xFE00] = data; } -LoadRes Memory::loadROM(std::string const &romfile, bool const forceDmg, bool const multicartCompat) { - if (LoadRes const fail = cart_.loadROM(romfile, forceDmg, multicartCompat)) - return fail; - +void Memory::postLoadRom() +{ psg_.init(cart_.isCgb()); lcd_.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; + postLoadRom(); return LOADRES_OK; } -std::size_t Memory::fillSoundBuffer(unsigned long cc) { +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(unsigned cc) { psg_.generateSamples(cc, isDoubleSpeed()); return psg_.fillBuffer(); } +void Memory::setDmgPaletteColor(unsigned palNum, unsigned colorNum, uint_least32_t rgb32) { + lcd_.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); + lcd_.loadOrSave(state); + psg_.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 e612dc3..a531930 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" @@ -33,12 +37,13 @@ class FilterInfo; class Memory { public: - explicit Memory(Interrupter const &interrupter); + explicit Memory(Interrupter const &interrupter, time_t (**gettime)()); bool loaded() const { return cart_.loaded(); } char const * romTitle() const { return cart_.romTitle(); } PakInfo const pakInfo(bool multicartCompat) const { return cart_.pakInfo(multicartCompat); } void setStatePtrs(SaveState &state); - unsigned long saveState(SaveState &state, unsigned long cc); + unsigned saveState(SaveState &state, unsigned cc); + void loadOrSave(loadsave& state); void loadState(SaveState const &state); void loadSavedata() { cart_.loadSavedata(); } void saveSavedata() { cart_.saveSavedata(); } @@ -48,14 +53,14 @@ public: lcd_.setOsdElement(osdElement); } - unsigned long stop(unsigned long cycleCounter); + unsigned stop(unsigned cycleCounter); bool isCgb() const { return lcd_.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(intevent_end) != disabled_time; } - long cyclesSinceBlit(unsigned long cc) const { + signed cyclesSinceBlit(unsigned cc) const { if (cc < intreq_.eventTime(intevent_blit)) return -1; @@ -63,48 +68,55 @@ public: } 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(unsigned p, unsigned long cc) { + unsigned ff_read(unsigned p, unsigned cc) { return p < 0x80 ? nontrivial_ff_read(p, cc) : ioamhram_[p + 0x100]; } - unsigned read(unsigned p, unsigned long cc) { + unsigned read(unsigned p, unsigned cc) { return cart_.rmem(p >> 12) ? cart_.rmem(p >> 12)[p] : nontrivial_read(p, cc); } - void write(unsigned p, unsigned data, unsigned long cc) { + void write(unsigned p, unsigned data, unsigned cc) { if (cart_.wmem(p >> 12)) { cart_.wmem(p >> 12)[p] = data; } else nontrivial_write(p, data, cc); } - void ff_write(unsigned p, unsigned data, unsigned long cc) { + void ff_write(unsigned p, unsigned data, unsigned cc) { if (p - 0x80u < 0x7Fu) { ioamhram_[p + 0x100] = data; } else nontrivial_ff_write(p, data, cc); } - unsigned long event(unsigned long cycleCounter); - unsigned long resetCounters(unsigned long cycleCounter); - LoadRes loadROM(std::string const &romfile, bool forceDmg, bool multicartCompat); - void setSaveDir(std::string const &dir) { cart_.setSaveDir(dir); } - void setInputGetter(InputGetter *getInput) { getInput_ = getInput; } - void setEndtime(unsigned long cc, unsigned long inc); - void setSoundBuffer(uint_least32_t *buf) { psg_.setBuffer(buf); } - std::size_t fillSoundBuffer(unsigned long cc); + LoadRes loadROM(const std::string &romfile, bool forceDmg, bool multicartCompat); + LoadRes loadROM(const unsigned char* image, size_t isize, bool forceDmg, bool multicartCompat); - void setVideoBuffer(uint_least32_t *videoBuf, std::ptrdiff_t pitch) { - lcd_.setVideoBuffer(videoBuf, pitch); + void setVideoBuffer(uint_least32_t *const videoBuf, std::ptrdiff_t pitch) { + videoBuf_ = videoBuf; + pitch_ = pitch; } - void setDmgPaletteColor(int palNum, int colorNum, unsigned long rgb32) { - lcd_.setDmgPaletteColor(palNum, colorNum, rgb32); - } + 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(); }; + + unsigned event(unsigned cycleCounter); + unsigned resetCounters(unsigned cycleCounter); + void setSaveDir(std::string const &dir) { cart_.setSaveDir(dir); } + void setInputGetter(InputGetter *getInput) { getInput_ = getInput; } + void setEndtime(unsigned cc, unsigned inc); + void setSoundBuffer(uint_least32_t *buf) { psg_.setBuffer(buf); } + unsigned fillSoundBuffer(unsigned cc); + void setDmgPaletteColor(unsigned palNum, unsigned colorNum, uint_least32_t rgb32); void setGameGenie(std::string const &codes) { cart_.setGameGenie(codes); } void setGameShark(std::string const &codes) { interrupter_.setGameShark(codes); } @@ -112,8 +124,8 @@ private: Cartridge cart_; unsigned char ioamhram_[0x200]; InputGetter *getInput_; - unsigned long divLastUpdate_; - unsigned long lastOamDmaUpdate_; + unsigned divLastUpdate_; + unsigned lastOamDmaUpdate_; InterruptRequester intreq_; Tima tima_; LCD lcd_; @@ -124,22 +136,25 @@ private: unsigned char oamDmaPos_; unsigned char serialCnt_; bool blanklcd_; + uint_least32_t* videoBuf_; + unsigned pitch_; void updateInput(); - void decEventCycles(IntEventId eventId, unsigned long dec); + void decEventCycles(IntEventId 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); unsigned char const * 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); - void updateSerial(unsigned long cc); - void updateTimaIrq(unsigned long cc); - void updateIrqs(unsigned long cc); + 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 cc); + void updateTimaIrq(unsigned cc); + void updateIrqs(unsigned cc); bool isDoubleSpeed() const { return lcd_.isDoubleSpeed(); } + void postLoadRom(); }; } diff --git a/libgambatte/src/minkeeper.h b/libgambatte/src/minkeeper.h index 7ac5eea..f37edef 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 }; }; @@ -39,22 +44,29 @@ template class T> struct Sum { enum { r = 0 }; }; template class MinKeeper { public: - explicit MinKeeper(unsigned long initValue = 0xFFFFFFFF); + explicit MinKeeper(unsigned initValue = 0xFFFFFFFF); int min() const { return a_[0]; } - unsigned long minValue() const { return minValue_; } + unsigned minValue() const { return minValue_; } template - void setValue(unsigned long cnt) { + void setValue(unsigned cnt) { values_[id] = cnt; updateValue(*this); } - void setValue(int id, unsigned long cnt) { + void setValue(int id, unsigned cnt) { values_[id] = cnt; updateValueLut.call(id >> 1, *this); } - unsigned long value(int id) const { return values_[id]; } + unsigned value(int id) const { return values_[id]; } + + void loadOrSave(gambatte::loadsave& state) { + state(values_, ids); + state(minValue_); + //updateValueLut is constant for our purposes. + state(a_, Sum::r); + } private: enum { levels = MinKeeperUtil::CeiledLog2::r }; @@ -101,8 +113,9 @@ private: }; 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); @@ -111,7 +124,7 @@ private: template typename MinKeeper::UpdateValueLut MinKeeper::updateValueLut; template -MinKeeper::MinKeeper(unsigned long const initValue) { +MinKeeper::MinKeeper(unsigned const 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 52a8945..6ca7bc5 100644 --- a/libgambatte/src/savestate.h +++ b/libgambatte/src/savestate.h @@ -19,6 +19,11 @@ #ifndef SAVESTATE_H #define SAVESTATE_H +// +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara +// - Make it rerecording-friendly. + +#include #include namespace gambatte { @@ -35,7 +40,7 @@ struct SaveState { void set(T *p, std::size_t size) { ptr = p; size_ = size; } friend class SaverList; - friend void setInitState(SaveState &, bool, bool); + friend void setInitState(SaveState &, bool, bool, time_t); private: T *ptr; @@ -43,7 +48,7 @@ struct SaveState { }; struct CPU { - unsigned long cycleCounter; + unsigned cycleCounter; unsigned short pc; unsigned short sp; unsigned char a; @@ -62,13 +67,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; @@ -88,8 +93,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; @@ -118,24 +123,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; @@ -158,8 +163,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; @@ -169,7 +174,7 @@ struct SaveState { struct { struct { - unsigned long counter; + unsigned counter; unsigned short reg; } lfsr; Env env; @@ -178,12 +183,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 d02759c..9824f96 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(SaveState const &state) { enabled_ = state.mem.ioamhram.get()[0x126] >> 7 & 1; } -void PSG::accumulateChannels(unsigned long const cycles) { +void PSG::accumulateChannels(unsigned const cycles) { uint_least32_t *const buf = buffer_ + bufferPos_; std::memset(buf, 0, cycles * sizeof *buf); ch1_.update(buf, soVol_, cycles); @@ -98,8 +102,8 @@ void PSG::accumulateChannels(unsigned long const cycles) { ch4_.update(buf, soVol_, cycles); } -void PSG::generateSamples(unsigned long const cycleCounter, bool const doubleSpeed) { - unsigned long const cycles = (cycleCounter - lastUpdate_) >> (1 + doubleSpeed); +void PSG::generateSamples(unsigned const cycleCounter, bool const doubleSpeed) { + unsigned const cycles = (cycleCounter - lastUpdate_) >> (1 + doubleSpeed); lastUpdate_ += cycles << (1 + doubleSpeed); if (cycles) @@ -108,12 +112,12 @@ void PSG::generateSamples(unsigned long const cycleCounter, bool const doubleSpe bufferPos_ += cycles; } -void PSG::resetCounter(unsigned long newCc, unsigned long oldCc, bool doubleSpeed) { +void PSG::resetCounter(unsigned newCc, unsigned oldCc, bool doubleSpeed) { generateSamples(oldCc, doubleSpeed); lastUpdate_ = newCc - (oldCc - lastUpdate_); } -std::size_t PSG::fillBuffer() { +unsigned PSG::fillBuffer() { uint_least32_t sum = rsum_; uint_least32_t *b = buffer_; std::size_t n = bufferPos_; @@ -164,8 +168,8 @@ static bool isBigEndianSampleOrder() { return u.uc[0]; } -static unsigned long so1Mul() { return isBigEndianSampleOrder() ? 0x00000001 : 0x00010000; } -static unsigned long so2Mul() { return isBigEndianSampleOrder() ? 0x00010000 : 0x00000001; } +static unsigned so1Mul() { return isBigEndianSampleOrder() ? 0x00000001 : 0x00010000; } +static unsigned so2Mul() { return isBigEndianSampleOrder() ? 0x00010000 : 0x00000001; } void PSG::setSoVolume(unsigned nr50) { soVol_ = ((nr50 & 0x7) + 1) * so1Mul() * 64 @@ -173,7 +177,7 @@ void PSG::setSoVolume(unsigned nr50) { } void PSG::mapSo(unsigned nr51) { - unsigned long so = nr51 * so1Mul() + (nr51 >> 4) * so2Mul(); + unsigned so = nr51 * so1Mul() + (nr51 >> 4) * so2Mul(); ch1_.setSo((so & 0x00010001) * 0xFFFF); ch2_.setSo((so >> 1 & 0x00010001) * 0xFFFF); ch3_.setSo((so >> 2 & 0x00010001) * 0xFFFF); @@ -187,4 +191,17 @@ unsigned PSG::getStatus() const { | 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 6c9ce33..d7009f0 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,10 +39,11 @@ public: void setStatePtrs(SaveState &state); void saveState(SaveState &state); void loadState(SaveState const &state); + void loadOrSave(loadsave& state); - void generateSamples(unsigned long cycleCounter, bool doubleSpeed); - void resetCounter(unsigned long newCc, unsigned long oldCc, bool doubleSpeed); - std::size_t fillBuffer(); + void generateSamples(unsigned cycleCounter, bool doubleSpeed); + void resetCounter(unsigned newCc, unsigned oldCc, bool doubleSpeed); + unsigned fillBuffer(); void setBuffer(uint_least32_t *buf) { buffer_ = buf; bufferPos_ = 0; } bool isEnabled() const { return enabled_; } @@ -77,13 +83,13 @@ private: Channel3 ch3_; Channel4 ch4_; uint_least32_t *buffer_; - std::size_t bufferPos_; - unsigned long lastUpdate_; - unsigned long soVol_; + unsigned lastUpdate_; + unsigned soVol_; + unsigned bufferPos_; uint_least32_t rsum_; bool enabled_; - void accumulateChannels(unsigned long cycles); + void accumulateChannels(unsigned cycles); }; } diff --git a/libgambatte/src/sound/channel1.cpp b/libgambatte/src/sound/channel1.cpp index 182b1a8..c517965 100644 --- a/libgambatte/src/sound/channel1.cpp +++ b/libgambatte/src/sound/channel1.cpp @@ -20,6 +20,10 @@ #include "../savestate.h" #include +// +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara +// - Make it rerecording-friendly. + namespace gambatte { Channel1::SweepUnit::SweepUnit(MasterDisabler &disabler, DutyUnit &dutyUnit) @@ -47,7 +51,7 @@ unsigned Channel1::SweepUnit::calcFreq() { } void Channel1::SweepUnit::event() { - unsigned long const period = nr0_ >> 4 & 0x07; + unsigned const period = nr0_ >> 4 & 0x07; if (period) { unsigned const freq = calcFreq(); @@ -70,7 +74,7 @@ void Channel1::SweepUnit::nr0Change(unsigned newNr0) { nr0_ = newNr0; } -void Channel1::SweepUnit::nr4Init(unsigned long const cc) { +void Channel1::SweepUnit::nr4Init(unsigned const cc) { negging_ = false; shadow_ = dutyUnit_.freq(); @@ -168,7 +172,7 @@ void Channel1::setNr4(unsigned const data) { setEvent(); } -void Channel1::setSo(unsigned long soMask) { +void Channel1::setSo(unsigned soMask) { soMask_ = soMask; staticOutputTest_(cycleCounter_); setEvent(); @@ -212,17 +216,17 @@ void Channel1::loadState(SaveState const &state) { master_ = state.spu.ch1.master; } -void Channel1::update(uint_least32_t *buf, unsigned long const soBaseVol, unsigned long cycles) { - unsigned long const outBase = envelopeUnit_.dacIsOn() ? soBaseVol & soMask_ : 0; - unsigned long const outLow = outBase * (0 - 15ul); - unsigned long const endCycles = cycleCounter_ + cycles; +void Channel1::update(uint_least32_t *buf, unsigned const soBaseVol, unsigned cycles) { + unsigned const outBase = envelopeUnit_.dacIsOn() ? soBaseVol & soMask_ : 0; + unsigned const outLow = outBase * (0 - 15ul); + unsigned const endCycles = cycleCounter_ + cycles; for (;;) { - unsigned long const outHigh = master_ + unsigned const outHigh = master_ ? outBase * (envelopeUnit_.getVolume() * 2 - 15ul) : outLow; - unsigned long const nextMajorEvent = std::min(nextEventUnit_->counter(), endCycles); - unsigned long out = dutyUnit_.isHighState() ? outHigh : outLow; + unsigned const nextMajorEvent = std::min(nextEventUnit_->counter(), endCycles); + unsigned out = dutyUnit_.isHighState() ? outHigh : outLow; while (dutyUnit_.counter() <= nextMajorEvent) { *buf = out - prevOut_; @@ -257,4 +261,25 @@ void Channel1::update(uint_least32_t *buf, unsigned long const 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 b364811..b80ae24 100644 --- a/libgambatte/src/sound/channel1.h +++ b/libgambatte/src/sound/channel1.h @@ -19,12 +19,20 @@ #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 "gbint.h" #include "length_counter.h" #include "master_disabler.h" #include "static_output_tester.h" +#include "loadsave.h" namespace gambatte { @@ -38,9 +46,10 @@ public: void setNr2(unsigned data); void setNr3(unsigned data); void setNr4(unsigned data); - void setSo(unsigned long soMask); + void loadOrSave(loadsave& state); + 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); @@ -52,11 +61,18 @@ private: SweepUnit(MasterDisabler &disabler, DutyUnit &dutyUnit); virtual void event(); void nr0Change(unsigned newNr0); - void nr4Init(unsigned long cycleCounter); + void nr4Init(unsigned cycleCounter); void reset(); void saveState(SaveState &state) const; void loadState(SaveState const &state); + void loadOrSave(loadsave& state) { + loadOrSave2(state); + state(shadow_); + state(nr0_); + state(negging_); + } + private: MasterDisabler &disableMaster_; DutyUnit &dutyUnit_; @@ -76,13 +92,14 @@ private: EnvelopeUnit envelopeUnit_; SweepUnit sweepUnit_; SoundUnit *nextEventUnit_; - unsigned long cycleCounter_; - unsigned long soMask_; - unsigned long prevOut_; + unsigned cycleCounter_; + unsigned soMask_; + unsigned prevOut_; unsigned char nr4_; bool master_; void setEvent(); + }; } diff --git a/libgambatte/src/sound/channel2.cpp b/libgambatte/src/sound/channel2.cpp index 7604b10..f154249 100644 --- a/libgambatte/src/sound/channel2.cpp +++ b/libgambatte/src/sound/channel2.cpp @@ -20,6 +20,10 @@ #include "../savestate.h" #include +// +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara +// - Make it rerecording-friendly. + namespace gambatte { Channel2::Channel2() @@ -76,7 +80,7 @@ void Channel2::setNr4(unsigned const data) { setEvent(); } -void Channel2::setSo(unsigned long soMask) { +void Channel2::setSo(unsigned soMask) { soMask_ = soMask; staticOutputTest_(cycleCounter_); setEvent(); @@ -116,17 +120,17 @@ void Channel2::loadState(SaveState const &state) { master_ = state.spu.ch2.master; } -void Channel2::update(uint_least32_t *buf, unsigned long const soBaseVol, unsigned long cycles) { - unsigned long const outBase = envelopeUnit_.dacIsOn() ? soBaseVol & soMask_ : 0; - unsigned long const outLow = outBase * (0 - 15ul); - unsigned long const endCycles = cycleCounter_ + cycles; +void Channel2::update(uint_least32_t *buf, unsigned const soBaseVol, unsigned cycles) { + unsigned const outBase = envelopeUnit_.dacIsOn() ? soBaseVol & soMask_ : 0; + unsigned const outLow = outBase * (0 - 15ul); + unsigned const endCycles = cycleCounter_ + cycles; for (;;) { - unsigned long const outHigh = master_ + unsigned const outHigh = master_ ? outBase * (envelopeUnit_.getVolume() * 2 - 15ul) : outLow; - unsigned long const nextMajorEvent = std::min(nextEventUnit->counter(), endCycles); - unsigned long out = dutyUnit_.isHighState() ? outHigh : outLow; + unsigned const nextMajorEvent = std::min(nextEventUnit->counter(), endCycles); + unsigned out = dutyUnit_.isHighState() ? outHigh : outLow; while (dutyUnit_.counter() <= nextMajorEvent) { *buf += out - prevOut_; @@ -160,4 +164,24 @@ void Channel2::update(uint_least32_t *buf, unsigned long const 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 02833aa..193c520 100644 --- a/libgambatte/src/sound/channel2.h +++ b/libgambatte/src/sound/channel2.h @@ -24,6 +24,11 @@ #include "gbint.h" #include "length_counter.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 { @@ -36,9 +41,10 @@ public: void setNr2(unsigned data); void setNr3(unsigned data); void setNr4(unsigned data); - void setSo(unsigned long soMask); + void loadOrSave(loadsave& state); + 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); @@ -53,9 +59,9 @@ private: DutyUnit dutyUnit_; EnvelopeUnit envelopeUnit_; SoundUnit *nextEventUnit; - unsigned long cycleCounter_; - unsigned long soMask_; - unsigned long prevOut_; + unsigned cycleCounter_; + unsigned soMask_; + unsigned prevOut_; unsigned char nr4_; bool master_; diff --git a/libgambatte/src/sound/channel3.cpp b/libgambatte/src/sound/channel3.cpp index a582b0c..c583949 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(unsigned nr3, unsigned nr4) { return 0x800 - ((nr4 << 8 & 0x700) | nr3); } @@ -79,7 +83,7 @@ void Channel3::setNr4(unsigned const data) { } } -void Channel3::setSo(unsigned long soMask) { +void Channel3::setSo(unsigned soMask) { soMask_ = soMask; } @@ -127,10 +131,10 @@ void Channel3::loadState(SaveState const &state) { setNr2(state.mem.ioamhram.get()[0x11C]); } -void Channel3::updateWaveCounter(unsigned long const cc) { +void Channel3::updateWaveCounter(unsigned const cc) { if (cc >= waveCounter_) { unsigned const period = toPeriod(nr3_, nr4_); - unsigned long const periods = (cc - waveCounter_) / period; + unsigned const periods = (cc - waveCounter_) / period; lastReadTime_ = waveCounter_ + periods * period; waveCounter_ = lastReadTime_ + period; @@ -142,16 +146,16 @@ void Channel3::updateWaveCounter(unsigned long const cc) { } } -void Channel3::update(uint_least32_t *buf, unsigned long const soBaseVol, unsigned long cycles) { - unsigned long const outBase = nr0_/* & 0x80*/ ? soBaseVol & soMask_ : 0; +void Channel3::update(uint_least32_t *buf, unsigned const soBaseVol, unsigned cycles) { + unsigned const outBase = nr0_/* & 0x80*/ ? soBaseVol & soMask_ : 0; if (outBase && rshift_ != 4) { - unsigned long const endCycles = cycleCounter_ + cycles; + unsigned const endCycles = cycleCounter_ + cycles; for (;;) { - unsigned long const nextMajorEvent = + unsigned const nextMajorEvent = std::min(lengthCounter_.counter(), endCycles); - unsigned long out = master_ + unsigned out = master_ ? ((sampleBuf_ >> (~wavePos_ << 2 & 4) & 0xF) >> rshift_) * 2 - 15ul : 0 - 15ul; out *= outBase; @@ -184,7 +188,7 @@ void Channel3::update(uint_least32_t *buf, unsigned long const soBaseVol, unsign break; } } else { - unsigned long const out = outBase * (0 - 15ul); + unsigned const out = outBase * (0 - 15ul); *buf += out - prevOut_; prevOut_ = out; cycleCounter_ += cycles; @@ -208,4 +212,23 @@ void Channel3::update(uint_least32_t *buf, unsigned long const 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 2a5e16a..5bd6195 100644 --- a/libgambatte/src/sound/channel3.h +++ b/libgambatte/src/sound/channel3.h @@ -19,8 +19,13 @@ #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 "length_counter.h" +#include "loadsave.h" #include "master_disabler.h" namespace gambatte { @@ -41,8 +46,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_) { @@ -66,10 +71,11 @@ public: waveRam_[index] = data; } + void loadOrSave(loadsave& state); private: class Ch3MasterDisabler : public MasterDisabler { public: - Ch3MasterDisabler(bool &m, unsigned long &wC) : MasterDisabler(m), waveCounter_(wC) {} + Ch3MasterDisabler(bool &m, unsigned &wC) : MasterDisabler(m), waveCounter_(wC) {} virtual void operator()() { MasterDisabler::operator()(); @@ -77,17 +83,17 @@ private: } private: - unsigned long &waveCounter_; + unsigned &waveCounter_; }; unsigned char waveRam_[0x10]; 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_; unsigned char nr4_; @@ -97,7 +103,7 @@ private: bool master_; bool cgb_; - void updateWaveCounter(unsigned long cc); + void updateWaveCounter(unsigned cc); }; } diff --git a/libgambatte/src/sound/channel4.cpp b/libgambatte/src/sound/channel4.cpp index 7c4bc18..bc0a312 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(unsigned const nr3) { +// +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara +// - Make it rerecording-friendly. + +static unsigned toPeriod(unsigned const nr3) { unsigned s = (nr3 >> 4) + 3; unsigned r = nr3 & 7; @@ -42,10 +46,10 @@ Channel4::Lfsr::Lfsr() { } -void Channel4::Lfsr::updateBackupCounter(unsigned long const cc) { +void Channel4::Lfsr::updateBackupCounter(unsigned const cc) { if (backupCounter_ <= cc) { - unsigned long const period = toPeriod(nr3_); - unsigned long periods = (cc - backupCounter_) / period + 1; + unsigned const period = toPeriod(nr3_); + unsigned periods = (cc - backupCounter_) / period + 1; backupCounter_ += periods * period; if (master_ && nr3_ < 0xE0) { @@ -70,7 +74,7 @@ void Channel4::Lfsr::updateBackupCounter(unsigned long const cc) { } } -void Channel4::Lfsr::reviveCounter(unsigned long cc) { +void Channel4::Lfsr::reviveCounter(unsigned cc) { updateBackupCounter(cc); counter_ = backupCounter_; } @@ -89,12 +93,12 @@ inline void Channel4::Lfsr::event() { backupCounter_ = counter_; } -void Channel4::Lfsr::nr3Change(unsigned newNr3, unsigned long cc) { +void Channel4::Lfsr::nr3Change(unsigned newNr3, unsigned cc) { updateBackupCounter(cc); nr3_ = newNr3; } -void Channel4::Lfsr::nr4Init(unsigned long cc) { +void Channel4::Lfsr::nr4Init(unsigned cc) { disableMaster(); updateBackupCounter(cc); master_ = true; @@ -102,19 +106,19 @@ void Channel4::Lfsr::nr4Init(unsigned long cc) { counter_ = backupCounter_; } -void Channel4::Lfsr::reset(unsigned long cc) { +void Channel4::Lfsr::reset(unsigned cc) { nr3_ = 0; disableMaster(); backupCounter_ = cc + toPeriod(nr3_); } -void Channel4::Lfsr::resetCounters(unsigned long oldCc) { +void Channel4::Lfsr::resetCounters(unsigned oldCc) { updateBackupCounter(oldCc); backupCounter_ -= counter_max; SoundUnit::resetCounters(oldCc); } -void Channel4::Lfsr::saveState(SaveState &state, unsigned long cc) { +void Channel4::Lfsr::saveState(SaveState &state, unsigned cc) { updateBackupCounter(cc); state.spu.ch4.lfsr.counter = backupCounter_; state.spu.ch4.lfsr.reg = reg_; @@ -179,7 +183,7 @@ void Channel4::setNr4(unsigned const data) { setEvent(); } -void Channel4::setSo(unsigned long soMask) { +void Channel4::setSo(unsigned soMask) { soMask_ = soMask; staticOutputTest_(cycleCounter_); setEvent(); @@ -218,15 +222,15 @@ void Channel4::loadState(SaveState const &state) { master_ = state.spu.ch4.master; } -void Channel4::update(uint_least32_t *buf, unsigned long const soBaseVol, unsigned long cycles) { - unsigned long const outBase = envelopeUnit_.dacIsOn() ? soBaseVol & soMask_ : 0; - unsigned long const outLow = outBase * (0 - 15ul); - unsigned long const endCycles = cycleCounter_ + cycles; +void Channel4::update(uint_least32_t *buf, unsigned const soBaseVol, unsigned cycles) { + unsigned const outBase = envelopeUnit_.dacIsOn() ? soBaseVol & soMask_ : 0; + unsigned const outLow = outBase * (0 - 15ul); + unsigned const endCycles = cycleCounter_ + cycles; for (;;) { - unsigned long const outHigh = outBase * (envelopeUnit_.getVolume() * 2 - 15ul); - unsigned long const nextMajorEvent = std::min(nextEventUnit_->counter(), endCycles); - unsigned long out = lfsr_.isHighState() ? outHigh : outLow; + unsigned const outHigh = outBase * (envelopeUnit_.getVolume() * 2 - 15ul); + unsigned const nextMajorEvent = std::min(nextEventUnit_->counter(), endCycles); + unsigned out = lfsr_.isHighState() ? outHigh : outLow; while (lfsr_.counter() <= nextMajorEvent) { *buf += out - prevOut_; @@ -260,4 +264,23 @@ void Channel4::update(uint_least32_t *buf, unsigned long const 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 9070b59..cb18afa 100644 --- a/libgambatte/src/sound/channel4.h +++ b/libgambatte/src/sound/channel4.h @@ -24,6 +24,11 @@ #include "length_counter.h" #include "master_disabler.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 { @@ -36,9 +41,10 @@ public: void setNr2(unsigned data); void setNr3(unsigned data) { lfsr_.nr3Change(data, cycleCounter_); } void setNr4(unsigned data); - void setSo(unsigned long soMask); + void loadOrSave(loadsave& state); + 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); @@ -49,24 +55,31 @@ private: public: Lfsr(); virtual void event(); - virtual void resetCounters(unsigned long oldCc); + void loadOrSave(loadsave& state) { + loadOrSave2(state); + state(backupCounter_); + state(reg_); + state(nr3_); + state(master_); + } + virtual void resetCounters(unsigned oldCc); 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, unsigned long cc); + void nr3Change(unsigned newNr3, unsigned cc); + void nr4Init(unsigned cc); + void reset(unsigned cc); + void saveState(SaveState &state, unsigned cc); void loadState(SaveState const &state); void disableMaster() { killCounter(); master_ = false; reg_ = 0x7FFF; } void killCounter() { counter_ = counter_disabled; } - void reviveCounter(unsigned long cc); + void reviveCounter(unsigned cc); private: - unsigned long backupCounter_; + unsigned backupCounter_; unsigned short reg_; unsigned char nr3_; bool master_; - void updateBackupCounter(unsigned long cc); + void updateBackupCounter(unsigned cc); }; class Ch4MasterDisabler : public MasterDisabler { @@ -86,9 +99,9 @@ private: EnvelopeUnit envelopeUnit_; Lfsr lfsr_; SoundUnit *nextEventUnit_; - unsigned long cycleCounter_; - unsigned long soMask_; - unsigned long prevOut_; + unsigned cycleCounter_; + unsigned soMask_; + unsigned prevOut_; unsigned char nr4_; bool master_; diff --git a/libgambatte/src/sound/duty_unit.cpp b/libgambatte/src/sound/duty_unit.cpp index df37b66..4258508 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(unsigned duty, unsigned pos) { static unsigned char const duties[4] = { 0x80, 0x81, 0xE1, 0x7E }; return duties[duty] >> pos & 1; @@ -40,9 +44,9 @@ DutyUnit::DutyUnit() { } -void DutyUnit::updatePos(unsigned long const cc) { +void DutyUnit::updatePos(unsigned const cc) { if (cc >= nextPosUpdate_) { - unsigned long const inc = (cc - nextPosUpdate_) / period_ + 1; + unsigned const inc = (cc - nextPosUpdate_) / period_ + 1; nextPosUpdate_ += period_ * inc; pos_ += inc; pos_ &= 7; @@ -68,7 +72,7 @@ void DutyUnit::setCounter() { counter_ = counter_disabled; } -void DutyUnit::setFreq(unsigned newFreq, unsigned long cc) { +void DutyUnit::setFreq(unsigned newFreq, unsigned cc) { updatePos(cc); period_ = toPeriod(newFreq); setCounter(); @@ -86,17 +90,17 @@ void DutyUnit::event() { counter_ += inc; } -void DutyUnit::nr1Change(unsigned newNr1, unsigned long cc) { +void DutyUnit::nr1Change(unsigned newNr1, unsigned cc) { updatePos(cc); setDuty(newNr1); setCounter(); } -void DutyUnit::nr3Change(unsigned newNr3, unsigned long cc) { +void DutyUnit::nr3Change(unsigned newNr3, unsigned cc) { setFreq((freq() & 0x700) | newNr3, cc); } -void DutyUnit::nr4Change(unsigned const newNr4, unsigned long const cc) { +void DutyUnit::nr4Change(unsigned const newNr4, unsigned const cc) { setFreq((newNr4 << 8 & 0x700) | (freq() & 0xFF), cc); if (newNr4 & 0x80) { @@ -112,7 +116,7 @@ void DutyUnit::reset() { setCounter(); } -void DutyUnit::saveState(SaveState::SPU::Duty &dstate, unsigned long const cc) { +void DutyUnit::saveState(SaveState::SPU::Duty &dstate, unsigned const cc) { updatePos(cc); dstate.nextPosUpdate = nextPosUpdate_; dstate.nr3 = freq() & 0xFF; @@ -120,7 +124,7 @@ void DutyUnit::saveState(SaveState::SPU::Duty &dstate, unsigned long const cc) { } void DutyUnit::loadState(const SaveState::SPU::Duty &dstate, - unsigned const nr1, unsigned const nr4, unsigned long const cc) { + unsigned const nr1, unsigned const nr4, unsigned const cc) { nextPosUpdate_ = std::max(dstate.nextPosUpdate, cc); pos_ = dstate.pos & 7; setDuty(nr1); @@ -129,7 +133,7 @@ void DutyUnit::loadState(const SaveState::SPU::Duty &dstate, setCounter(); } -void DutyUnit::resetCounters(unsigned long const oldCc) { +void DutyUnit::resetCounters(unsigned const oldCc) { if (nextPosUpdate_ == counter_disabled) return; @@ -143,11 +147,21 @@ void DutyUnit::killCounter() { setCounter(); } -void DutyUnit::reviveCounter(unsigned long const cc) { +void DutyUnit::reviveCounter(unsigned const 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 7539c1b..a3a565c 100644 --- a/libgambatte/src/sound/duty_unit.h +++ b/libgambatte/src/sound/duty_unit.h @@ -19,9 +19,14 @@ #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 { @@ -29,23 +34,25 @@ class DutyUnit : public SoundUnit { public: DutyUnit(); virtual void event(); - virtual void resetCounters(unsigned long oldCc); + virtual void resetCounters(unsigned oldCc); 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(SaveState::SPU::Duty const &dstate, unsigned nr1, unsigned nr4, unsigned long cc); + void saveState(SaveState::SPU::Duty &dstate, unsigned cc); + void loadState(SaveState::SPU::Duty const &dstate, unsigned nr1, unsigned nr4, unsigned cc); void killCounter(); - void reviveCounter(unsigned long cc); + void reviveCounter(unsigned cc); + + void loadOrSave(loadsave& state); //intended for use by SweepUnit only. unsigned freq() const { return 2048 - (period_ >> 1); } - void setFreq(unsigned newFreq, unsigned long cc); + void setFreq(unsigned newFreq, unsigned cc); private: - unsigned long nextPosUpdate_; + unsigned nextPosUpdate_; unsigned short period_; unsigned char pos_; unsigned char duty_; @@ -54,7 +61,7 @@ private: void setCounter(); void setDuty(unsigned nr1); - void updatePos(unsigned long cc); + void updatePos(unsigned cc); }; class DutyMasterDisabler : public MasterDisabler { diff --git a/libgambatte/src/sound/envelope_unit.cpp b/libgambatte/src/sound/envelope_unit.cpp index 248b6b6..67b563c 100644 --- a/libgambatte/src/sound/envelope_unit.cpp +++ b/libgambatte/src/sound/envelope_unit.cpp @@ -19,6 +19,10 @@ #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_; @@ -39,14 +43,14 @@ void EnvelopeUnit::saveState(SaveState::SPU::Env &estate) const { estate.volume = volume_; } -void EnvelopeUnit::loadState(SaveState::SPU::Env const &estate, unsigned nr2, unsigned long cc) { +void EnvelopeUnit::loadState(SaveState::SPU::Env const &estate, unsigned nr2, unsigned cc) { counter_ = std::max(estate.counter, cc); volume_ = estate.volume; nr2_ = nr2; } void EnvelopeUnit::event() { - unsigned long const period = nr2_ & 7; + unsigned const period = nr2_ & 7; if (period) { unsigned newVol = volume_; @@ -81,9 +85,9 @@ bool EnvelopeUnit::nr2Change(unsigned const newNr2) { return !(newNr2 & 0xF8); } -bool EnvelopeUnit::nr4Init(unsigned long const cc) { +bool EnvelopeUnit::nr4Init(unsigned const cc) { { - unsigned long period = nr2_ & 7; + unsigned period = nr2_ & 7; if (!period) period = 8; @@ -98,4 +102,11 @@ bool EnvelopeUnit::nr4Init(unsigned long const cc) { return !(nr2_ & 0xF8); } +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 e1d092d..133667d 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*/) {} }; explicit EnvelopeUnit(VolOnOffEvent &volOnOffEvent = nullEvent_); @@ -36,10 +41,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(SaveState::SPU::Env const &estate, unsigned nr2, unsigned long cc); + void loadOrSave(loadsave& state); + void loadState(SaveState::SPU::Env const &estate, unsigned nr2, unsigned cc); private: static VolOnOffEvent nullEvent_; diff --git a/libgambatte/src/sound/length_counter.cpp b/libgambatte/src/sound/length_counter.cpp index e4dadcb..9aa3422 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, unsigned const mask) @@ -37,14 +41,14 @@ void LengthCounter::event() { disableMaster_(); } -void LengthCounter::nr1Change(unsigned const newNr1, unsigned const nr4, unsigned long const cc) { +void LengthCounter::nr1Change(unsigned const newNr1, unsigned const nr4, unsigned const cc) { lengthCounter_ = (~newNr1 & lengthMask_) + 1; counter_ = nr4 & 0x40 ? ((cc >> 13) + lengthCounter_) << 13 - : static_cast(counter_disabled); + : static_cast(counter_disabled); } -void LengthCounter::nr4Change(unsigned const oldNr4, unsigned const newNr4, unsigned long const cc) { +void LengthCounter::nr4Change(unsigned const oldNr4, unsigned const newNr4, unsigned const cc) { if (counter_ != counter_disabled) lengthCounter_ = (counter_ >> 13) - (cc >> 13); @@ -79,9 +83,16 @@ void LengthCounter::saveState(SaveState::SPU::LCounter &lstate) const { lstate.lengthCounter = lengthCounter_; } -void LengthCounter::loadState(SaveState::SPU::LCounter const &lstate, unsigned long const cc) { +void LengthCounter::loadState(SaveState::SPU::LCounter const &lstate, unsigned const 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 47f0c96..3311802 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 { @@ -29,12 +34,13 @@ class MasterDisabler; class LengthCounter : public SoundUnit { public: LengthCounter(MasterDisabler &disabler, unsigned lengthMask); + void loadOrSave(loadsave& state); virtual 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 init(bool cgb); void saveState(SaveState::SPU::LCounter &lstate) const; - void loadState(SaveState::SPU::LCounter const &lstate, unsigned long cc); + void loadState(SaveState::SPU::LCounter const &lstate, unsigned cc); private: MasterDisabler &disableMaster_; diff --git a/libgambatte/src/sound/sound_unit.h b/libgambatte/src/sound/sound_unit.h index a504ce7..6c503d6 100644 --- a/libgambatte/src/sound/sound_unit.h +++ b/libgambatte/src/sound/sound_unit.h @@ -19,6 +19,12 @@ #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 { @@ -27,17 +33,20 @@ public: virtual ~SoundUnit() {} virtual void event() = 0; + void loadOrSave2(loadsave& state) { + state(counter_); + } - virtual void resetCounters(unsigned long /*oldCc*/) { + virtual void resetCounters(unsigned /*oldCc*/) { if (counter_ != counter_disabled) counter_ -= counter_max; } - unsigned long counter() const { return counter_; } + unsigned counter() const { return counter_; } protected: SoundUnit() : counter_(counter_disabled) {} - unsigned long counter_; + unsigned counter_; }; } diff --git a/libgambatte/src/sound/static_output_tester.h b/libgambatte/src/sound/static_output_tester.h index 84a1ec8..00c2004 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 { @@ -27,7 +31,7 @@ template class StaticOutputTester : public EnvelopeUnit::VolOnOffEvent { public: StaticOutputTester(Channel const &ch, Unit &unit) : ch_(ch), unit_(unit) {} - void operator()(unsigned long cc); + void operator()(unsigned cc); private: Channel const &ch_; @@ -35,7 +39,7 @@ private: }; template -void StaticOutputTester::operator()(unsigned long cc) { +void StaticOutputTester::operator()(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 361ce1f..e74bca8 100644 --- a/libgambatte/src/state_osd_elements.cpp +++ b/libgambatte/src/state_osd_elements.cpp @@ -23,6 +23,10 @@ #include #include +// +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara +// - Make it rerecording-friendly. + namespace { using namespace gambatte; @@ -72,7 +76,7 @@ ShadedTextOsdElment::ShadedTextOsdElment(unsigned width, const char *txt) print(pixels + 1 * w() + 1, w(), 0xE0E0E0ul, txt);*/ bitmapfont::print(pixels.get() , w(), ShadeFill(), txt); - bitmapfont::print(pixels.get() + 1 * w() + 1, w(), 0xE0E0E0ul , txt); + bitmapfont::print(pixels.get() + 1 * w() + 1, w(), 0xE0E0E0u , txt); } const uint_least32_t* ShadedTextOsdElment::update() { @@ -132,7 +136,7 @@ SaveStateOsdElement::SaveStateOsdElement(const std::string &fileName, unsigned s using namespace bitmapfont; 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); + StateSaver::ss_width, 0x808080u, txt); } } diff --git a/libgambatte/src/statesaver.cpp b/libgambatte/src/statesaver.cpp index 198ef05..a1dcb55 100644 --- a/libgambatte/src/statesaver.cpp +++ b/libgambatte/src/statesaver.cpp @@ -77,7 +77,7 @@ static void write(std::ofstream &file, unsigned short data) { file.put(data & 0xFF); } -static void write(std::ofstream &file, unsigned long data) { +static void write(std::ofstream &file, unsigned data) { static char const inf[] = { 0x00, 0x00, 0x04 }; file.write(inf, sizeof inf); put32(file, data); @@ -130,7 +130,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 e62f1ef..89662f6 100644 --- a/libgambatte/src/statesaver.h +++ b/libgambatte/src/statesaver.h @@ -19,9 +19,14 @@ #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 +#include namespace gambatte { diff --git a/libgambatte/src/tima.cpp b/libgambatte/src/tima.cpp index 649f24b..bb056ac 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 unsigned char const timaClock[4] = { 10, 4, 6, 8 }; namespace gambatte { @@ -44,7 +48,7 @@ void Tima::loadState(SaveState const &state, TimaInterruptRequester timaIrq) { tma_ = state.mem.ioamhram.get()[0x106]; tac_ = state.mem.ioamhram.get()[0x107]; - unsigned long nextIrqEventTime = disabled_time; + unsigned nextIrqEventTime = disabled_time; if (tac_ & 4) { nextIrqEventTime = tmatime_ != disabled_time && tmatime_ > state.cpu.cycleCounter ? tmatime_ @@ -54,12 +58,12 @@ void Tima::loadState(SaveState const &state, TimaInterruptRequester timaIrq) { timaIrq.setNextIrqEventTime(nextIrqEventTime); } -void Tima::resetCc(unsigned long const oldCc, unsigned long const newCc, TimaInterruptRequester timaIrq) { +void Tima::resetCc(unsigned const oldCc, unsigned const newCc, TimaInterruptRequester timaIrq) { if (tac_ & 0x04) { updateIrq(oldCc, timaIrq); updateTima(oldCc); - unsigned long const dec = oldCc - newCc; + unsigned const dec = oldCc - newCc; lastUpdate_ -= dec; timaIrq.setNextIrqEventTime(timaIrq.nextIrqEventTime() - dec); @@ -68,8 +72,8 @@ void Tima::resetCc(unsigned long const oldCc, unsigned long const newCc, TimaInt } } -void Tima::updateTima(unsigned long const cc) { - unsigned long const ticks = (cc - lastUpdate_) >> timaClock[tac_ & 3]; +void Tima::updateTima(unsigned const cc) { + unsigned const ticks = (cc - lastUpdate_) >> timaClock[tac_ & 3]; lastUpdate_ += ticks << timaClock[tac_ & 3]; if (cc >= tmatime_) { @@ -79,7 +83,7 @@ void Tima::updateTima(unsigned long const cc) { tima_ = tma_; } - unsigned long tmp = tima_ + ticks; + unsigned tmp = tima_ + ticks; while (tmp > 0x100) tmp -= 0x100 - tma_; @@ -98,7 +102,7 @@ void Tima::updateTima(unsigned long const cc) { tima_ = tmp; } -void Tima::setTima(unsigned const data, unsigned long const cc, TimaInterruptRequester timaIrq) { +void Tima::setTima(unsigned const data, unsigned const cc, TimaInterruptRequester timaIrq) { if (tac_ & 0x04) { updateIrq(cc, timaIrq); updateTima(cc); @@ -112,7 +116,7 @@ void Tima::setTima(unsigned const data, unsigned long const cc, TimaInterruptReq tima_ = data; } -void Tima::setTma(unsigned const data, unsigned long const cc, TimaInterruptRequester timaIrq) { +void Tima::setTma(unsigned const data, unsigned const cc, TimaInterruptRequester timaIrq) { if (tac_ & 0x04) { updateIrq(cc, timaIrq); updateTima(cc); @@ -121,9 +125,9 @@ void Tima::setTma(unsigned const data, unsigned long const cc, TimaInterruptRequ tma_ = data; } -void Tima::setTac(unsigned const data, unsigned long const cc, TimaInterruptRequester timaIrq) { +void Tima::setTac(unsigned const data, unsigned const cc, TimaInterruptRequester timaIrq) { if (tac_ ^ data) { - unsigned long nextIrqEventTime = timaIrq.nextIrqEventTime(); + unsigned nextIrqEventTime = timaIrq.nextIrqEventTime(); if (tac_ & 0x04) { updateIrq(cc, timaIrq); @@ -153,7 +157,7 @@ void Tima::setTac(unsigned const data, unsigned long const cc, TimaInterruptRequ tac_ = data; } -unsigned Tima::tima(unsigned long cc) { +unsigned Tima::tima(unsigned cc) { if (tac_ & 0x04) updateTima(cc); @@ -166,4 +170,13 @@ void Tima::doIrqEvent(TimaInterruptRequester timaIrq) { + ((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 93afeb9..ce1f51e 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 { @@ -27,8 +31,8 @@ class TimaInterruptRequester { public: explicit TimaInterruptRequester(InterruptRequester &intreq) : intreq_(intreq) {} void flagIrq() const { intreq_.flagIrq(4); } - unsigned long nextIrqEventTime() const { return intreq_.eventTime(intevent_tima); } - void setNextIrqEventTime(unsigned long time) const { intreq_.setEventTime(time); } + unsigned nextIrqEventTime() const { return intreq_.eventTime(intevent_tima); } + void setNextIrqEventTime(unsigned time) const { intreq_.setEventTime(time); } private: InterruptRequester &intreq_; @@ -39,26 +43,26 @@ public: Tima(); void saveState(SaveState &) const; void loadState(const SaveState &, TimaInterruptRequester timaIrq); - void resetCc(unsigned long oldCc, unsigned long 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 resetCc(unsigned oldCc, unsigned newCc, TimaInterruptRequester timaIrq); + 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 updateTima(unsigned cc); + void loadOrSave(loadsave& state); private: - unsigned long lastUpdate_; - unsigned long tmatime_; + unsigned lastUpdate_; + unsigned tmatime_; unsigned char tima_; unsigned char tma_; unsigned char tac_; - void updateIrq(unsigned long const cc, TimaInterruptRequester timaIrq) { + void updateIrq(unsigned const cc, TimaInterruptRequester timaIrq) { while (cc >= timaIrq.nextIrqEventTime()) doIrqEvent(timaIrq); } - void updateTima(unsigned long cc); }; } diff --git a/libgambatte/src/video.cpp b/libgambatte/src/video.cpp index 3205e90..5cb4b08 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 palette[], unsigned long const dmgColors[], unsigned data) { +void LCD::setDmgPalette(uint_least32_t palette[], uint_least32_t const dmgColors[], 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(unsigned const bgr15) { - unsigned long const r = bgr15 & 0x1F; - unsigned long const g = bgr15 >> 5 & 0x1F; - unsigned long const b = bgr15 >> 10 & 0x1F; +static uint_least32_t gbcToRgb32(unsigned const bgr15) { + uint_least32_t const r = bgr15 & 0x1F; + uint_least32_t const g = bgr15 >> 5 & 0x1F; + uint_least32_t const b = bgr15 >> 10 & 0x1F; return ((r * 13 + g * 2 + b) >> 1) << 16 | (g * 3 + b) << 9 @@ -85,7 +89,6 @@ LCD::LCD(unsigned char const *oamram, unsigned char const *vram, dmgColorsRgb32_[i] = (3 - (i & 3)) * 85 * 0x010101ul; reset(oamram, vram, false); - setVideoBuffer(0, 160); } void LCD::reset(unsigned char const *oamram, unsigned char const *vram, bool cgb) { @@ -94,8 +97,8 @@ void LCD::reset(unsigned char const *oamram, unsigned char const *vram, bool cgb refreshPalettes(); } -static unsigned long mode2IrqSchedule(unsigned const statReg, - LyCounter const &lyCounter, unsigned long const cc) { +static unsigned mode2IrqSchedule(unsigned const statReg, + LyCounter const &lyCounter, unsigned const cc) { if (!(statReg & lcdstat_m2irqen)) return disabled_time; @@ -113,16 +116,16 @@ static unsigned long mode2IrqSchedule(unsigned const statReg, return cc + next; } -static unsigned long m0IrqTimeFromXpos166Time(unsigned long xpos166Time, bool cgb, bool ds) { +static unsigned m0IrqTimeFromXpos166Time(unsigned xpos166Time, bool cgb, bool ds) { return xpos166Time + cgb - ds; } -static unsigned long hdmaTimeFromM0Time(unsigned long m0Time, bool ds) { +static unsigned hdmaTimeFromM0Time(unsigned m0Time, bool ds) { return m0Time + 1 - ds; } -static unsigned long nextHdmaTime(unsigned long lastM0Time, - unsigned long nextM0Time, unsigned long cc, bool ds) { +static unsigned nextHdmaTime(unsigned lastM0Time, + unsigned nextM0Time, unsigned cc, bool ds) { return cc < hdmaTimeFromM0Time(lastM0Time, ds) ? hdmaTimeFromM0Time(lastM0Time, ds) : hdmaTimeFromM0Time(nextM0Time, ds); @@ -160,11 +163,11 @@ void LCD::loadState(SaveState const &state, unsigned char const *const oamram) { eventTimes_.setm( state.ppu.pendingLcdstatIrq ? ppu_.now() + 1 - : static_cast(disabled_time)); + : static_cast(disabled_time)); eventTimes_.setm( state.ppu.oldWy != state.mem.ioamhram.get()[0x14A] ? ppu_.now() + 1 - : static_cast(disabled_time)); + : static_cast(disabled_time)); eventTimes_.set(ppu_.lyCounter().time()); eventTimes_.setm( SpriteMapper::schedule(ppu_.lyCounter(), ppu_.now())); @@ -175,11 +178,11 @@ void LCD::loadState(SaveState const &state, unsigned char const *const oamram) { mode2IrqSchedule(statReg_, ppu_.lyCounter(), ppu_.now())); eventTimes_.setm(statReg_ & lcdstat_m0irqen ? ppu_.now() + state.ppu.nextM0Irq - : static_cast(disabled_time)); + : 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_memevents; ++i) eventTimes_.set(MemEvent(i), disabled_time); @@ -230,7 +233,7 @@ struct Blend { }; template -static void clear(T *buf, unsigned long color, std::ptrdiff_t dpitch) { +static void clear(T *buf, uint_least32_t color, std::ptrdiff_t dpitch) { unsigned lines = 144; while (lines--) { @@ -241,11 +244,11 @@ static void clear(T *buf, unsigned long color, std::ptrdiff_t dpitch) { } -void LCD::updateScreen(bool const blanklcd, unsigned long const cycleCounter) { +void LCD::updateScreen(bool const blanklcd, unsigned const cycleCounter, uint_least32_t* vbuffer, unsigned vpitch) { update(cycleCounter); if (blanklcd && ppu_.frameBuf().fb()) { - unsigned long color = ppu_.cgb() ? gbcToRgb32(0xFFFF) : dmgColorsRgb32_[0]; + uint_least32_t color = ppu_.cgb() ? gbcToRgb32(0xFFFF) : dmgColorsRgb32_[0]; clear(ppu_.frameBuf().fb(), color, ppu_.frameBuf().pitch()); } @@ -268,14 +271,15 @@ void LCD::updateScreen(bool const blanklcd, unsigned long const cycleCounter) { } else osdElement_.reset(); } + ppu_.frameBuf().blit(vbuffer, vpitch); } -void LCD::resetCc(unsigned long const oldCc, unsigned long const newCc) { +void LCD::resetCc(unsigned const oldCc, unsigned const newCc) { update(oldCc); ppu_.resetCc(oldCc, newCc); if (ppu_.lcdc() & lcdc_en) { - unsigned long const dec = oldCc - newCc; + unsigned const dec = oldCc - newCc; nextM0Time_.invalidatePredictedNextM0Time(); lycIrq_.reschedule(ppu_.lyCounter(), newCc); @@ -289,7 +293,7 @@ void LCD::resetCc(unsigned long const oldCc, unsigned long const newCc) { } } -void LCD::speedChange(unsigned long const cc) { +void LCD::speedChange(unsigned const cc) { update(cc); ppu_.speedChange(cc); @@ -316,14 +320,14 @@ void LCD::speedChange(unsigned long const cc) { } } -static unsigned long m0TimeOfCurrentLine( - unsigned long nextLyTime, - unsigned long lastM0Time, - unsigned long nextM0Time) { +static unsigned m0TimeOfCurrentLine( + unsigned nextLyTime, + unsigned lastM0Time, + unsigned nextM0Time) { return nextM0Time < nextLyTime ? nextM0Time : lastM0Time; } -unsigned long LCD::m0TimeOfCurrentLine(unsigned long const cc) { +unsigned LCD::m0TimeOfCurrentLine(unsigned const cc) { if (cc >= nextM0Time_.predictedNextM0Time()) { update(cc); nextM0Time_.predictNextM0Time(ppu_); @@ -334,20 +338,20 @@ unsigned long LCD::m0TimeOfCurrentLine(unsigned long const cc) { } static bool isHdmaPeriod(LyCounter const &lyCounter, - unsigned long m0TimeOfCurrentLy, unsigned long cc) { + unsigned m0TimeOfCurrentLy, unsigned cc) { int timeToNextLy = lyCounter.time() - cc; return lyCounter.ly() < 144 && timeToNextLy > 4 && cc >= hdmaTimeFromM0Time(m0TimeOfCurrentLy, lyCounter.isDoubleSpeed()); } -void LCD::enableHdma(unsigned long const cycleCounter) { +void LCD::enableHdma(unsigned const cycleCounter) { if (cycleCounter >= nextM0Time_.predictedNextM0Time()) { update(cycleCounter); nextM0Time_.predictNextM0Time(ppu_); } else if (cycleCounter >= eventTimes_.nextEventTime()) update(cycleCounter); - unsigned long const m0TimeCurLy = + unsigned const m0TimeCurLy = gambatte::m0TimeOfCurrentLine(ppu_.lyCounter().time(), ppu_.lastM0Time(), nextM0Time_.predictedNextM0Time()); @@ -359,14 +363,14 @@ void LCD::enableHdma(unsigned long const cycleCounter) { cycleCounter, isDoubleSpeed())); } -void LCD::disableHdma(unsigned long const cycleCounter) { +void LCD::disableHdma(unsigned const cycleCounter) { if (cycleCounter >= eventTimes_.nextEventTime()) update(cycleCounter); eventTimes_.setm(disabled_time); } -bool LCD::vramAccessible(unsigned long const cc) { +bool LCD::vramAccessible(unsigned const cc) { if (cc >= eventTimes_.nextEventTime()) update(cc); @@ -376,7 +380,7 @@ bool LCD::vramAccessible(unsigned long const cc) { || cc + isDoubleSpeed() - ppu_.cgb() + 2 >= m0TimeOfCurrentLine(cc); } -bool LCD::cgbpAccessible(unsigned long const cc) { +bool LCD::cgbpAccessible(unsigned const cc) { if (cc >= eventTimes_.nextEventTime()) update(cc); @@ -387,27 +391,27 @@ bool LCD::cgbpAccessible(unsigned long const cc) { } static void doCgbColorChange(unsigned char *pdata, - unsigned long *palette, unsigned index, unsigned data) { + uint_least32_t *palette, unsigned index, unsigned data) { pdata[index] = data; index >>= 1; palette[index] = gbcToRgb32(pdata[index * 2] | pdata[index * 2 + 1] << 8); } -void LCD::doCgbBgColorChange(unsigned index, unsigned data, unsigned long cc) { +void LCD::doCgbBgColorChange(unsigned index, unsigned data, unsigned cc) { if (cgbpAccessible(cc)) { update(cc); doCgbColorChange(bgpData_, ppu_.bgPalette(), index, data); } } -void LCD::doCgbSpColorChange(unsigned index, unsigned data, unsigned long cc) { +void LCD::doCgbSpColorChange(unsigned index, unsigned data, unsigned cc) { if (cgbpAccessible(cc)) { update(cc); doCgbColorChange(objpData_, ppu_.spPalette(), index, data); } } -bool LCD::oamReadable(unsigned long const cc) { +bool LCD::oamReadable(unsigned const cc) { if (!(ppu_.lcdc() & lcdc_en) || ppu_.inactivePeriodAfterDisplayEnable(cc)) return true; @@ -421,7 +425,7 @@ bool LCD::oamReadable(unsigned long const cc) { || cc + isDoubleSpeed() - ppu_.cgb() + 2 >= m0TimeOfCurrentLine(cc); } -bool LCD::oamWritable(unsigned long const cc) { +bool LCD::oamWritable(unsigned const cc) { if (!(ppu_.lcdc() & lcdc_en) || ppu_.inactivePeriodAfterDisplayEnable(cc)) return true; @@ -442,7 +446,7 @@ void LCD::mode3CyclesChange() { if (eventTimes_(memevent_m0irq) != disabled_time && eventTimes_(memevent_m0irq) > m0IrqTimeFromXpos166Time(ppu_.now(), ppu_.cgb(), ds)) { - unsigned long t = m0IrqTimeFromXpos166Time(ppu_.predictedNextXposTime(166), + unsigned t = m0IrqTimeFromXpos166Time(ppu_.predictedNextXposTime(166), ppu_.cgb(), ds); eventTimes_.setm(t); } @@ -455,13 +459,13 @@ void LCD::mode3CyclesChange() { } } -void LCD::wxChange(unsigned newValue, unsigned long cycleCounter) { +void LCD::wxChange(unsigned newValue, unsigned cycleCounter) { update(cycleCounter + isDoubleSpeed() + 1); ppu_.setWx(newValue); mode3CyclesChange(); } -void LCD::wyChange(unsigned const newValue, unsigned long const cc) { +void LCD::wyChange(unsigned const newValue, unsigned const cc) { update(cc + 1); ppu_.setWy(newValue); @@ -479,18 +483,18 @@ void LCD::wyChange(unsigned const newValue, unsigned long const cc) { } } -void LCD::scxChange(unsigned newScx, unsigned long cycleCounter) { +void LCD::scxChange(unsigned newScx, unsigned cycleCounter) { update(cycleCounter + ppu_.cgb() + isDoubleSpeed()); ppu_.setScx(newScx); mode3CyclesChange(); } -void LCD::scyChange(unsigned newValue, unsigned long cycleCounter) { +void LCD::scyChange(unsigned newValue, unsigned cycleCounter) { update(cycleCounter + ppu_.cgb() + isDoubleSpeed()); ppu_.setScy(newValue); } -void LCD::oamChange(unsigned long cc) { +void LCD::oamChange(unsigned cc) { if (ppu_.lcdc() & lcdc_en) { update(cc); ppu_.oamChange(cc); @@ -498,7 +502,7 @@ void LCD::oamChange(unsigned long cc) { } } -void LCD::oamChange(unsigned char const *oamram, unsigned long cc) { +void LCD::oamChange(unsigned char const *oamram, unsigned cc) { update(cc); ppu_.oamChange(oamram, cc); @@ -506,7 +510,7 @@ void LCD::oamChange(unsigned char const *oamram, unsigned long cc) { eventTimes_.setm(SpriteMapper::schedule(ppu_.lyCounter(), cc)); } -void LCD::lcdcChange(unsigned const data, unsigned long const cc) { +void LCD::lcdcChange(unsigned const data, unsigned const cc) { unsigned const oldLcdc = ppu_.lcdc(); update(cc); @@ -547,7 +551,7 @@ void LCD::lcdcChange(unsigned const data, unsigned long const cc) { | (data & (lcdc_tdsel | lcdc_obj2x)), cc); if ((oldLcdc ^ data) & lcdc_obj2x) { - unsigned long t = SpriteMapper::schedule(ppu_.lyCounter(), cc); + unsigned t = SpriteMapper::schedule(ppu_.lyCounter(), cc); eventTimes_.setm(t); } @@ -560,7 +564,7 @@ void LCD::lcdcChange(unsigned const data, unsigned long const cc) { ppu_.setLcdc(data, cc); if ((oldLcdc ^ data) & lcdc_obj2x) { - unsigned long t = SpriteMapper::schedule(ppu_.lyCounter(), cc); + unsigned t = SpriteMapper::schedule(ppu_.lyCounter(), cc); eventTimes_.setm(t); } @@ -578,7 +582,7 @@ struct LyCnt { LyCnt(unsigned ly, int timeToNextLy) : ly(ly), timeToNextLy(timeToNextLy) {} }; -static LyCnt const getLycCmpLy(LyCounter const &lyCounter, unsigned long cc) { +static LyCnt const getLycCmpLy(LyCounter const &lyCounter, unsigned cc) { unsigned ly = lyCounter.ly(); int timeToNextLy = lyCounter.time() - cc; @@ -596,7 +600,7 @@ static LyCnt const getLycCmpLy(LyCounter const &lyCounter, unsigned long cc) { } // anon ns -inline bool LCD::statChangeTriggersStatIrqDmg(unsigned const old, unsigned long const cc) { +inline bool LCD::statChangeTriggersStatIrqDmg(unsigned const old, unsigned const cc) { LyCnt const lycCmp = getLycCmpLy(ppu_.lyCounter(), cc); if (ppu_.lyCounter().ly() < 144) { @@ -624,7 +628,7 @@ static bool statChangeTriggersM2IrqCgb(unsigned const old, } inline bool LCD::statChangeTriggersM0LycOrM1StatIrqCgb( - unsigned const old, unsigned const data, unsigned long const cc) { + unsigned const old, unsigned const data, unsigned const cc) { unsigned const ly = ppu_.lyCounter().ly(); int const timeToNextLy = ppu_.lyCounter().time() - cc; LyCnt const lycCmp = getLycCmpLy(ppu_.lyCounter(), cc); @@ -652,7 +656,7 @@ inline bool LCD::statChangeTriggersM0LycOrM1StatIrqCgb( } inline bool LCD::statChangeTriggersStatIrqCgb( - unsigned const old, unsigned const data, unsigned long const cc) { + unsigned const old, unsigned const data, unsigned const cc) { if (!(data & ~old & ( lcdstat_lycirqen | lcdstat_m2irqen | lcdstat_m1irqen @@ -666,13 +670,13 @@ inline bool LCD::statChangeTriggersStatIrqCgb( || statChangeTriggersM2IrqCgb(old, data, ly, timeToNextLy); } -inline bool LCD::statChangeTriggersStatIrq(unsigned old, unsigned data, unsigned long cc) { +inline bool LCD::statChangeTriggersStatIrq(unsigned old, unsigned data, unsigned cc) { return ppu_.cgb() ? statChangeTriggersStatIrqCgb(old, data, cc) : statChangeTriggersStatIrqDmg(old, cc); } -void LCD::lcdstatChange(unsigned const data, unsigned long const cc) { +void LCD::lcdstatChange(unsigned const data, unsigned const cc) { if (cc >= eventTimes_.nextEventTime()) update(cc); @@ -706,7 +710,7 @@ void LCD::lcdstatChange(unsigned const data, unsigned long const cc) { static unsigned incLy(unsigned ly) { return ly == 153 ? 0 : ly + 1; } -inline bool LCD::lycRegChangeStatTriggerBlockedByM0OrM1Irq(unsigned long const cc) { +inline bool LCD::lycRegChangeStatTriggerBlockedByM0OrM1Irq(unsigned const cc) { int const timeToNextLy = ppu_.lyCounter().time() - cc; if (ppu_.lyCounter().ly() < 144) { return (statReg_ & lcdstat_m0irqen) @@ -721,7 +725,7 @@ inline bool LCD::lycRegChangeStatTriggerBlockedByM0OrM1Irq(unsigned long const c } bool LCD::lycRegChangeTriggersStatIrq( - unsigned const old, unsigned const data, unsigned long const cc) { + unsigned const old, unsigned const data, unsigned const cc) { if (!(statReg_ & lcdstat_lycirqen) || data >= 154 || lycRegChangeStatTriggerBlockedByM0OrM1Irq(cc)) { return false; @@ -739,7 +743,7 @@ bool LCD::lycRegChangeTriggersStatIrq( return data == lycCmp.ly; } -void LCD::lycRegChange(unsigned const data, unsigned long const cc) { +void LCD::lycRegChange(unsigned const data, unsigned const cc) { unsigned const old = lycIrq_.lycReg(); if (data == old) return; @@ -762,7 +766,7 @@ void LCD::lycRegChange(unsigned const data, unsigned long const cc) { } } -unsigned LCD::getStat(unsigned const lycReg, unsigned long const cc) { +unsigned LCD::getStat(unsigned const lycReg, unsigned const cc) { unsigned stat = 0; if (ppu_.lcdc() & lcdc_en) { @@ -815,7 +819,7 @@ inline void LCD::doMode2IrqEvent() { m2IrqStatReg_ = statReg_; if (!(statReg_ & lcdstat_m0irqen)) { - unsigned long nextTime = eventTimes_(memevent_m2irq) + ppu_.lyCounter().lineTime(); + unsigned nextTime = eventTimes_(memevent_m2irq) + ppu_.lyCounter().lineTime(); if (ly == 0) { nextTime -= 4; } else if (ly == 143) @@ -878,7 +882,7 @@ inline void LCD::event() { eventTimes_.setm(statReg_ & lcdstat_m0irqen ? m0IrqTimeFromXpos166Time(ppu_.predictedNextXposTime(166), ppu_.cgb(), isDoubleSpeed()) - : static_cast(disabled_time)); + : static_cast(disabled_time)); break; case memevent_oneshot_statirq: @@ -902,7 +906,7 @@ inline void LCD::event() { } } -void LCD::update(unsigned long const cycleCounter) { +void LCD::update(unsigned const cycleCounter) { if (!(ppu_.lcdc() & lcdc_en)) return; @@ -914,11 +918,11 @@ void LCD::update(unsigned long const cycleCounter) { ppu_.update(cycleCounter); } -void LCD::setVideoBuffer(uint_least32_t *videoBuf, std::ptrdiff_t pitch) { - ppu_.setFrameBuf(videoBuf, pitch); +void LCD::setDmgPaletteColor(unsigned index, uint_least32_t rgb32) { + dmgColorsRgb32_[index] = rgb32; } -void LCD::setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned long rgb32) { +void LCD::setDmgPaletteColor(unsigned palNum, unsigned colorNum, uint_least32_t rgb32) { if (palNum > 2 || colorNum > 3) return; @@ -926,4 +930,18 @@ void LCD::setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned long r 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 dbef702..105e761 100644 --- a/libgambatte/src/video.h +++ b/libgambatte/src/video.h @@ -19,6 +19,13 @@ #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" #include "interruptrequester.h" #include "minkeeper.h" #include "osd_element.h" @@ -39,7 +46,7 @@ public: void flagHdmaReq() const { gambatte::flagHdmaReq(intreq_); } void flagIrq(unsigned bit) const { intreq_.flagIrq(bit); } - void setNextEventTime(unsigned long time) const { intreq_.setEventTime(time); } + void setNextEventTime(unsigned time) const { intreq_.setEventTime(time); } private: InterruptRequester &intreq_; @@ -52,63 +59,63 @@ public: void reset(unsigned char const *oamram, unsigned char const *vram, bool cgb); void setStatePtrs(SaveState &state); void saveState(SaveState &state) const; + void loadOrSave(loadsave& state); void loadState(SaveState const &state, unsigned char const *oamram); - void setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned long rgb32); - void setVideoBuffer(uint_least32_t *videoBuf, std::ptrdiff_t pitch); + void setDmgPaletteColor(unsigned palNum, unsigned colorNum, uint_least32_t rgb32); void setOsdElement(transfer_ptr osdElement) { osdElement_ = osdElement; } - void dmgBgPaletteChange(unsigned data, unsigned long cycleCounter) { + void dmgBgPaletteChange(unsigned data, unsigned cycleCounter) { update(cycleCounter); bgpData_[0] = data; setDmgPalette(ppu_.bgPalette(), dmgColorsRgb32_, data); } - void dmgSpPalette1Change(unsigned data, unsigned long cycleCounter) { + void dmgSpPalette1Change(unsigned data, unsigned cycleCounter) { update(cycleCounter); objpData_[0] = data; setDmgPalette(ppu_.spPalette(), dmgColorsRgb32_ + 4, data); } - void dmgSpPalette2Change(unsigned data, unsigned long cycleCounter) { + void dmgSpPalette2Change(unsigned data, unsigned cycleCounter) { update(cycleCounter); objpData_[1] = data; setDmgPalette(ppu_.spPalette() + 4, dmgColorsRgb32_ + 8, data); } - void cgbBgColorChange(unsigned index, unsigned data, unsigned long cycleCounter) { + void cgbBgColorChange(unsigned index, unsigned data, unsigned cycleCounter) { if (bgpData_[index] != data) doCgbBgColorChange(index, data, cycleCounter); } - void cgbSpColorChange(unsigned index, unsigned data, unsigned long cycleCounter) { + void cgbSpColorChange(unsigned index, unsigned data, unsigned cycleCounter) { if (objpData_[index] != data) doCgbSpColorChange(index, data, cycleCounter); } - unsigned cgbBgColorRead(unsigned index, unsigned long cycleCounter) { + unsigned cgbBgColorRead(unsigned index, unsigned cycleCounter) { return ppu_.cgb() & cgbpAccessible(cycleCounter) ? bgpData_[index] : 0xFF; } - unsigned cgbSpColorRead(unsigned index, unsigned long cycleCounter) { + unsigned cgbSpColorRead(unsigned index, unsigned cycleCounter) { return ppu_.cgb() & cgbpAccessible(cycleCounter) ? objpData_[index] : 0xFF; } - void updateScreen(bool blanklcd, unsigned long cc); - void resetCc(unsigned long oldCC, unsigned long newCc); - void speedChange(unsigned long cycleCounter); - bool vramAccessible(unsigned long cycleCounter); - bool oamReadable(unsigned long cycleCounter); - bool oamWritable(unsigned long cycleCounter); - void wxChange(unsigned newValue, unsigned long cycleCounter); - void wyChange(unsigned newValue, unsigned long cycleCounter); - void oamChange(unsigned long cycleCounter); - void oamChange(const unsigned char *oamram, unsigned long cycleCounter); - void scxChange(unsigned newScx, unsigned long cycleCounter); - void scyChange(unsigned newValue, unsigned long cycleCounter); - void vramChange(unsigned long cycleCounter) { update(cycleCounter); } - unsigned getStat(unsigned lycReg, unsigned long cycleCounter); - - unsigned getLyReg(unsigned long const cc) { + void updateScreen(bool blanklcd, unsigned cc, uint_least32_t* vbuffer, unsigned vpitch); + void resetCc(unsigned oldCC, unsigned newCc); + void speedChange(unsigned cycleCounter); + bool vramAccessible(unsigned cycleCounter); + bool oamReadable(unsigned cycleCounter); + bool oamWritable(unsigned cycleCounter); + void wxChange(unsigned newValue, unsigned cycleCounter); + void wyChange(unsigned newValue, unsigned cycleCounter); + void oamChange(unsigned cycleCounter); + void oamChange(const unsigned char *oamram, unsigned cycleCounter); + void scxChange(unsigned newScx, unsigned cycleCounter); + void scyChange(unsigned newValue, unsigned cycleCounter); + void vramChange(unsigned cycleCounter) { update(cycleCounter); } + unsigned getStat(unsigned lycReg, unsigned cycleCounter); + + unsigned getLyReg(unsigned const cc) { unsigned lyReg = 0; if (ppu_.lcdc() & lcdc_en) { @@ -130,14 +137,14 @@ public: return lyReg; } - unsigned long nextMode1IrqTime() const { return eventTimes_(memevent_m1irq); } - void lcdcChange(unsigned data, unsigned long cycleCounter); - void lcdstatChange(unsigned data, unsigned long cycleCounter); - void lycRegChange(unsigned data, unsigned long cycleCounter); - void enableHdma(unsigned long cycleCounter); - void disableHdma(unsigned long cycleCounter); + unsigned nextMode1IrqTime() const { return eventTimes_(memevent_m1irq); } + void lcdcChange(unsigned data, unsigned cycleCounter); + void lcdstatChange(unsigned data, unsigned cycleCounter); + void lycRegChange(unsigned data, unsigned cycleCounter); + void enableHdma(unsigned cycleCounter); + void disableHdma(unsigned cycleCounter); bool hdmaIsEnabled() const { return eventTimes_(memevent_hdma) != disabled_time; } - void update(unsigned long cycleCounter); + void update(unsigned cycleCounter); bool isCgb() const { return ppu_.cgb(); } bool isDoubleSpeed() const { return ppu_.lyCounter().isDoubleSpeed(); } @@ -165,36 +172,41 @@ private: } Event nextEvent() const { return static_cast(eventMin_.min()); } - unsigned long nextEventTime() const { return eventMin_.minValue(); } - unsigned long operator()(Event e) const { return eventMin_.value(e); } - template void set(unsigned long time) { eventMin_.setValue(time); } - void set(Event e, unsigned long time) { eventMin_.setValue(e, time); } + unsigned nextEventTime() const { return eventMin_.minValue(); } + unsigned operator()(Event e) const { return eventMin_.value(e); } + template void set(unsigned time) { eventMin_.setValue(time); } + void set(Event e, unsigned time) { eventMin_.setValue(e, time); } MemEvent nextMemEvent() const { return static_cast(memEventMin_.min()); } - unsigned long nextMemEventTime() const { return memEventMin_.minValue(); } - unsigned long operator()(MemEvent e) const { return memEventMin_.value(e); } + unsigned nextMemEventTime() const { return memEventMin_.minValue(); } + unsigned operator()(MemEvent e) const { return memEventMin_.value(e); } template - void setm(unsigned long time) { memEventMin_.setValue(time); setMemEvent(); } - void set(MemEvent e, unsigned long time) { memEventMin_.setValue(e, time); setMemEvent(); } + void setm(unsigned time) { memEventMin_.setValue(time); setMemEvent(); } + void set(MemEvent e, unsigned time) { memEventMin_.setValue(e, time); setMemEvent(); } void flagIrq(unsigned bit) { memEventRequester_.flagIrq(bit); } void flagHdmaReq() { memEventRequester_.flagHdmaReq(); } + void loadOrSave(loadsave& state) { + eventMin_.loadOrSave(state); + memEventMin_.loadOrSave(state); + } private: MinKeeper eventMin_; MinKeeper memEventMin_; VideoInterruptRequester memEventRequester_; void setMemEvent() { - unsigned long nmet = nextMemEventTime(); + unsigned nmet = nextMemEventTime(); eventMin_.setValue(nmet); memEventRequester_.setNextEventTime(nmet); } + }; PPU ppu_; - unsigned long dmgColorsRgb32_[3 * 4]; + uint_least32_t dmgColorsRgb32_[3 * 4]; unsigned char bgpData_[8 * 8]; unsigned char objpData_[8 * 8]; EventTimes eventTimes_; @@ -206,24 +218,25 @@ private: unsigned char m2IrqStatReg_; unsigned char m1IrqStatReg_; - static void setDmgPalette(unsigned long palette[], - unsigned long const dmgColors[], + static void setDmgPalette(uint_least32_t palette[], + uint_least32_t const dmgColors[], unsigned data); + void setDmgPaletteColor(unsigned index, uint_least32_t rgb32); void refreshPalettes(); void setDBuffer(); void doMode2IrqEvent(); void event(); - unsigned long m0TimeOfCurrentLine(unsigned long cc); - bool cgbpAccessible(unsigned long cycleCounter); - bool lycRegChangeStatTriggerBlockedByM0OrM1Irq(unsigned long cc); - bool lycRegChangeTriggersStatIrq(unsigned old, unsigned data, unsigned long cc); - bool statChangeTriggersM0LycOrM1StatIrqCgb(unsigned old, unsigned data, unsigned long cc); - bool statChangeTriggersStatIrqCgb(unsigned old, unsigned data, unsigned long cc); - bool statChangeTriggersStatIrqDmg(unsigned old, unsigned long cc); - bool statChangeTriggersStatIrq(unsigned old, unsigned data, unsigned long cc); + unsigned m0TimeOfCurrentLine(unsigned cc); + bool cgbpAccessible(unsigned cycleCounter); + bool lycRegChangeStatTriggerBlockedByM0OrM1Irq(unsigned cc); + bool lycRegChangeTriggersStatIrq(unsigned old, unsigned data, unsigned cc); + bool statChangeTriggersM0LycOrM1StatIrqCgb(unsigned old, unsigned data, unsigned cc); + bool statChangeTriggersStatIrqCgb(unsigned old, unsigned data, unsigned cc); + bool statChangeTriggersStatIrqDmg(unsigned old, unsigned cc); + bool statChangeTriggersStatIrq(unsigned old, unsigned data, unsigned cc); void mode3CyclesChange(); - void doCgbBgColorChange(unsigned index, unsigned data, unsigned long cycleCounter); - void doCgbSpColorChange(unsigned index, unsigned data, unsigned long cycleCounter); + void doCgbBgColorChange(unsigned index, unsigned data, unsigned cycleCounter); + void doCgbSpColorChange(unsigned index, unsigned data, unsigned cycleCounter); }; } diff --git a/libgambatte/src/video/ly_counter.cpp b/libgambatte/src/video/ly_counter.cpp index 0eb2961..d930d66 100644 --- a/libgambatte/src/video/ly_counter.cpp +++ b/libgambatte/src/video/ly_counter.cpp @@ -19,6 +19,10 @@ #include "ly_counter.h" #include "../savestate.h" +// +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara +// - Make it rerecording-friendly. + namespace gambatte { LyCounter::LyCounter() @@ -39,23 +43,23 @@ void LyCounter::doEvent() { time_ = time_ + lineTime_; } -unsigned long LyCounter::nextLineCycle(unsigned const lineCycle, unsigned long const cc) const { - unsigned long tmp = time_ + (lineCycle << ds_); +unsigned LyCounter::nextLineCycle(unsigned const lineCycle, unsigned const cc) const { + unsigned tmp = time_ + (lineCycle << ds_); if (tmp - cc > lineTime_) tmp -= lineTime_; return tmp; } -unsigned long LyCounter::nextFrameCycle(unsigned long const frameCycle, unsigned long const cc) const { - unsigned long tmp = time_ + (((153U - ly()) * 456U + frameCycle) << ds_); +unsigned LyCounter::nextFrameCycle(unsigned const frameCycle, unsigned const cc) const { + unsigned tmp = time_ + (((153U - ly()) * 456U + frameCycle) << ds_); if (tmp - cc > 70224U << ds_) tmp -= 70224U << ds_; return tmp; } -void LyCounter::reset(unsigned long videoCycles, unsigned long lastUpdate) { +void LyCounter::reset(unsigned videoCycles, unsigned lastUpdate) { ly_ = videoCycles / 456; time_ = lastUpdate + ((456 - (videoCycles - ly_ * 456ul)) << isDoubleSpeed()); } diff --git a/libgambatte/src/video/ly_counter.h b/libgambatte/src/video/ly_counter.h index 1defd1a..7c4d834 100644 --- a/libgambatte/src/video/ly_counter.h +++ b/libgambatte/src/video/ly_counter.h @@ -18,6 +18,11 @@ ***************************************************************************/ #ifndef LY_COUNTER_H #define LY_COUNTER_H +#include "../loadsave.h" + +// +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara +// - Make it rerecording-friendly. namespace gambatte { @@ -29,24 +34,31 @@ public: void doEvent(); bool isDoubleSpeed() const { return ds_; } - unsigned long frameCycles(unsigned long cc) const { + unsigned frameCycles(unsigned cc) const { return ly_ * 456ul + lineCycles(cc); } - unsigned lineCycles(unsigned long cc) const { + unsigned lineCycles(unsigned cc) const { return 456u - ((time_ - cc) >> isDoubleSpeed()); } unsigned lineTime() const { return lineTime_; } unsigned ly() const { return ly_; } - unsigned long nextLineCycle(unsigned lineCycle, unsigned long cycleCounter) const; - unsigned long nextFrameCycle(unsigned long frameCycle, unsigned long cycleCounter) const; - void reset(unsigned long videoCycles, unsigned long lastUpdate); + + void loadOrSave(loadsave& state) { + state(time_); + state(lineTime_); + state(ly_); + state(ds_); + } + unsigned nextLineCycle(unsigned lineCycle, unsigned cycleCounter) const; + unsigned nextFrameCycle(unsigned frameCycle, unsigned cycleCounter) const; + void reset(unsigned videoCycles, unsigned lastUpdate); void setDoubleSpeed(bool ds); - unsigned long time() const { return time_; } + unsigned time() const { return time_; } private: - unsigned long time_; + unsigned time_; unsigned short lineTime_; unsigned char ly_; bool ds_; diff --git a/libgambatte/src/video/lyc_irq.cpp b/libgambatte/src/video/lyc_irq.cpp index 3c468e2..127d589 100644 --- a/libgambatte/src/video/lyc_irq.cpp +++ b/libgambatte/src/video/lyc_irq.cpp @@ -23,6 +23,10 @@ #include "savestate.h" #include +// +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara +// - Make it rerecording-friendly. + namespace gambatte { LycIrq::LycIrq() @@ -35,16 +39,16 @@ LycIrq::LycIrq() { } -static unsigned long schedule(unsigned statReg, - unsigned lycReg, LyCounter const &lyCounter, unsigned long cc) { +static unsigned schedule(unsigned statReg, + unsigned lycReg, LyCounter const &lyCounter, unsigned cc) { return (statReg & lcdstat_lycirqen) && lycReg < 154 ? lyCounter.nextFrameCycle(lycReg ? lycReg * 456 : 153 * 456 + 8, cc) - : static_cast(disabled_time); + : static_cast(disabled_time); } void LycIrq::regChange(unsigned const statReg, - unsigned const lycReg, LyCounter const &lyCounter, unsigned long const cc) { - unsigned long const timeSrc = schedule(statReg, lycReg, lyCounter, cc); + unsigned const lycReg, LyCounter const &lyCounter, unsigned const cc) { + unsigned const timeSrc = schedule(statReg, lycReg, lyCounter, cc); statRegSrc_ = statReg; lycRegSrc_ = lycReg; time_ = std::min(time_, timeSrc); @@ -95,7 +99,7 @@ void LycIrq::saveState(SaveState &state) const { state.ppu.lyc = lycReg_; } -void LycIrq::reschedule(LyCounter const &lyCounter, unsigned long cc) { +void LycIrq::reschedule(LyCounter const &lyCounter, unsigned cc) { time_ = std::min(schedule(statReg_ , lycReg_ , lyCounter, cc), schedule(statRegSrc_, lycRegSrc_, lyCounter, cc)); } diff --git a/libgambatte/src/video/lyc_irq.h b/libgambatte/src/video/lyc_irq.h index 6dd7911..a24a9ba 100644 --- a/libgambatte/src/video/lyc_irq.h +++ b/libgambatte/src/video/lyc_irq.h @@ -19,6 +19,12 @@ #ifndef VIDEO_LYC_IRQ_H #define VIDEO_LYC_IRQ_H +// +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara +// - Make it rerecording-friendly. + +#include "../loadsave.h" + namespace gambatte { struct SaveState; @@ -31,21 +37,30 @@ public: unsigned lycReg() const { return lycRegSrc_; } void loadState(SaveState const &state); void saveState(SaveState &state) const; - unsigned long time() const { return time_; } + void loadOrSave(loadsave& state) { + state(time_); + state(lycRegSrc_); + state(statRegSrc_); + state(lycReg_); + state(statReg_); + state(cgb_); + } + + unsigned time() const { return time_; } void setCgb(bool cgb) { cgb_ = cgb; } void lcdReset(); - void reschedule(LyCounter const &lyCounter, unsigned long cc); + void reschedule(LyCounter const &lyCounter, unsigned cc); - void statRegChange(unsigned statReg, LyCounter const &lyCounter, unsigned long cc) { + void statRegChange(unsigned statReg, LyCounter const &lyCounter, unsigned cc) { regChange(statReg, lycRegSrc_, lyCounter, cc); } - void lycRegChange(unsigned lycReg, LyCounter const &lyCounter, unsigned long cc) { + void lycRegChange(unsigned lycReg, LyCounter const &lyCounter, unsigned cc) { regChange(statRegSrc_, lycReg, lyCounter, cc); } private: - unsigned long time_; + unsigned time_; unsigned char lycRegSrc_; unsigned char statRegSrc_; unsigned char lycReg_; @@ -53,7 +68,7 @@ private: bool cgb_; void regChange(unsigned statReg, unsigned lycReg, - LyCounter const &lyCounter, unsigned long cc); + LyCounter const &lyCounter, unsigned cc); }; } diff --git a/libgambatte/src/video/m0_irq.h b/libgambatte/src/video/m0_irq.h index f07f62f..fb0af1e 100644 --- a/libgambatte/src/video/m0_irq.h +++ b/libgambatte/src/video/m0_irq.h @@ -20,13 +20,13 @@ public: } void statRegChange(unsigned statReg, - unsigned long nextM0IrqTime, unsigned long cc, bool cgb) { + unsigned nextM0IrqTime, unsigned cc, bool cgb) { if (nextM0IrqTime - cc > cgb * 2U) statReg_ = statReg; } void lycRegChange(unsigned lycReg, - unsigned long nextM0IrqTime, unsigned long cc, + unsigned nextM0IrqTime, unsigned cc, bool ds, bool cgb) { if (nextM0IrqTime - cc > cgb * 5 + 1U - ds) lycReg_ = lycReg; @@ -53,6 +53,11 @@ public: unsigned statReg() const { return statReg_; } + void loadOrSave(loadsave& state) + { + state(statReg_); + state(lycReg_); + } private: unsigned char statReg_; unsigned char lycReg_; diff --git a/libgambatte/src/video/next_m0_time.h b/libgambatte/src/video/next_m0_time.h index 5cea59d..9c7c0e3 100644 --- a/libgambatte/src/video/next_m0_time.h +++ b/libgambatte/src/video/next_m0_time.h @@ -1,6 +1,12 @@ #ifndef NEXT_M0_TIME_H_ #define NEXT_M0_TIME_H_ +// +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara +// - Make it rerecording-friendly. + +#include "../loadsave.h" + namespace gambatte { class NextM0Time { @@ -10,6 +16,9 @@ public: void invalidatePredictedNextM0Time() { predictedNextM0Time_ = 0; } unsigned predictedNextM0Time() const { return predictedNextM0Time_; } + void loadOrSave(loadsave& state) { + state(predictedNextM0Time_); + } private: unsigned predictedNextM0Time_; }; diff --git a/libgambatte/src/video/ppu.cpp b/libgambatte/src/video/ppu.cpp index fd0dc07..485b0d4 100644 --- a/libgambatte/src/video/ppu.cpp +++ b/libgambatte/src/video/ppu.cpp @@ -449,7 +449,7 @@ static void doFullTilesUnrolledDmg(PPUPriv &p, int const xend, uint_least32_t *c unsigned const attrib = p.spriteList[i].attrib; unsigned spword = p.spwordList[i]; - unsigned long const *const spPalette = p.spPalette + (attrib >> 2 & 4); + const uint_least32_t *const spPalette = p.spPalette + (attrib >> 2 & 4); uint_least32_t *d = dst + pos; if (!(attrib & attr_bgpriority)) { @@ -564,7 +564,7 @@ static void doFullTilesUnrolledCgb(PPUPriv &p, int const xend, uint_least32_t *c xpos += n; do { - unsigned long const *const bgPalette = p.bgPalette + (nattrib & 7) * 4; + uint_least32_t const *const bgPalette = p.bgPalette + (nattrib & 7) * 4; dst[0] = bgPalette[ ntileword & 0x0003 ]; dst[1] = bgPalette[(ntileword & 0x000C) >> 2]; dst[2] = bgPalette[(ntileword & 0x0030) >> 4]; @@ -603,7 +603,7 @@ static void doFullTilesUnrolledCgb(PPUPriv &p, int const xend, uint_least32_t *c uint_least32_t *const dst = dbufline + (xpos - 8); unsigned const tileword = p.ntileword; unsigned const attrib = p.nattrib; - unsigned long const *const bgPalette = p.bgPalette + (attrib & 7) * 4; + uint_least32_t const *const bgPalette = p.bgPalette + (attrib & 7) * 4; dst[0] = bgPalette[ tileword & 0x0003 ]; dst[1] = bgPalette[(tileword & 0x000C) >> 2]; @@ -639,7 +639,7 @@ static void doFullTilesUnrolledCgb(PPUPriv &p, int const xend, uint_least32_t *c unsigned char const id = p.spriteList[i].oampos; unsigned const sattrib = p.spriteList[i].attrib; unsigned spword = p.spwordList[i]; - unsigned long const *const spPalette = p.spPalette + (sattrib & 7) * 4; + uint_least32_t const *const spPalette = p.spPalette + (sattrib & 7) * 4; if (!((attrib | sattrib) & bgprioritymask)) { unsigned char *const idt = idtab + pos; @@ -805,7 +805,7 @@ static void plotPixel(PPUPriv &p) { } unsigned const twdata = tileword & ((p.lcdc & 1) | p.cgb) * 3; - unsigned long pixel = p.bgPalette[twdata + (p.attrib & 7) * 4]; + uint_least32_t pixel = p.bgPalette[twdata + (p.attrib & 7) * 4]; int i = static_cast(p.nextSprite) - 1; if (i >= 0 && int(p.spriteList[i].spx) > xpos - 8) { @@ -866,8 +866,8 @@ static void plotPixelIfNoSprite(PPUPriv &p) { plotPixel(p); } -static unsigned long nextM2Time(PPUPriv const &p) { - unsigned long nextm2 = p.lyCounter.isDoubleSpeed() +static unsigned nextM2Time(PPUPriv const &p) { + unsigned nextm2 = p.lyCounter.isDoubleSpeed() ? p.lyCounter.time() + (weMasterCheckPriorToLyIncLineCycle(true ) + m2_ds_offset) * 2 - 456 * 2 : p.lyCounter.time() + weMasterCheckPriorToLyIncLineCycle(p.cgb) - 456 ; if (p.lyCounter.ly() == 143) @@ -879,11 +879,11 @@ static unsigned long nextM2Time(PPUPriv const &p) { static void xpos168(PPUPriv &p) { p.lastM0Time = p.now - (p.cycles << p.lyCounter.isDoubleSpeed()); - unsigned long const nextm2 = nextM2Time(p); + unsigned const nextm2 = nextM2Time(p); p.cycles = p.now >= nextm2 - ? long((p.now - nextm2) >> p.lyCounter.isDoubleSpeed()) - : -long((nextm2 - p.now) >> p.lyCounter.isDoubleSpeed()); + ? signed((p.now - nextm2) >> p.lyCounter.isDoubleSpeed()) + : -signed((nextm2 - p.now) >> p.lyCounter.isDoubleSpeed()); nextCall(0, p.lyCounter.ly() == 143 ? M2_Ly0::f0_ : M2_LyNon0::f0_, p); } @@ -1576,8 +1576,8 @@ std::size_t upperBound(T const a[], K e) { struct CycleState { PPUState const *state; - long cycle; - operator long() const { return cycle; } + signed cycle; + operator signed() const { return cycle; } }; static PPUState const * decodeM3LoopState(unsigned state) { @@ -1607,8 +1607,8 @@ static PPUState const * decodeM3LoopState(unsigned state) { return 0; } -static long cyclesUntilM0Upperbound(PPUPriv const &p) { - long cycles = 168 - p.xpos + 6; +static signed cyclesUntilM0Upperbound(PPUPriv const &p) { + signed cycles = 168 - p.xpos + 6; for (unsigned i = p.nextSprite; i < 10 && p.spriteList[i].spx < 168; ++i) cycles += 11; @@ -1647,17 +1647,17 @@ static void loadSpriteList(PPUPriv &p, SaveState const &ss) { void PPU::loadState(SaveState const &ss, unsigned char const *const oamram) { PPUState const *const m3loopState = decodeM3LoopState(ss.ppu.state); - long const videoCycles = std::min(ss.ppu.videoCycles, 70223UL); + signed const videoCycles = std::min(ss.ppu.videoCycles, 70223U); bool const ds = p_.cgb & ss.mem.ioamhram.get()[0x14D] >> 7; - long const vcycs = videoCycles - ds * m2_ds_offset < 0 + signed const vcycs = videoCycles - ds * m2_ds_offset < 0 ? videoCycles - ds * m2_ds_offset + 70224 : videoCycles - ds * m2_ds_offset; - long const lineCycles = static_cast(vcycs) % 456; + signed const lineCycles = static_cast(vcycs) % 456; p_.now = ss.cpu.cycleCounter; p_.lcdc = ss.mem.ioamhram.get()[0x140]; p_.lyCounter.setDoubleSpeed(ds); - p_.lyCounter.reset(std::min(ss.ppu.videoCycles, 70223ul), ss.cpu.cycleCounter); + p_.lyCounter.reset(std::min(ss.ppu.videoCycles, 70223u), ss.cpu.cycleCounter); p_.spriteMapper.loadState(ss, oamram); p_.winYPos = ss.ppu.winYPos; p_.scy = ss.mem.ioamhram.get()[0x142]; @@ -1684,7 +1684,7 @@ void PPU::loadState(SaveState const &ss, unsigned char const *const oamram) { && lineCycles + cyclesUntilM0Upperbound(p_) < weMasterCheckPriorToLyIncLineCycle(p_.cgb)) { p_.nextCallPtr = m3loopState; p_.cycles = -1; - } else if (vcycs < 143 * 456L + static_cast(m3StartLineCycle(p_.cgb)) + max_m3start_cycles) { + } else if (vcycs < 143 * 456L + static_cast(m3StartLineCycle(p_.cgb)) + max_m3start_cycles) { CycleState const lineCycleStates[] = { { &M3Start::f0_, m3StartLineCycle(p_.cgb) }, { &M3Start::f1_, m3StartLineCycle(p_.cgb) + max_m3start_cycles }, @@ -1715,9 +1715,9 @@ void PPU::reset(unsigned char const *oamram, unsigned char const *vram, bool cgb p_.spriteMapper.reset(oamram, cgb); } -void PPU::resetCc(unsigned long const oldCc, unsigned long const newCc) { - unsigned long const dec = oldCc - newCc; - unsigned long const videoCycles = lcdcEn(p_) ? p_.lyCounter.frameCycles(p_.now) : 0; +void PPU::resetCc(unsigned const oldCc, unsigned const newCc) { + unsigned const dec = oldCc - newCc; + unsigned const videoCycles = lcdcEn(p_) ? p_.lyCounter.frameCycles(p_.now) : 0; p_.now -= dec; p_.lastM0Time = p_.lastM0Time ? p_.lastM0Time - dec : p_.lastM0Time; @@ -1725,8 +1725,8 @@ void PPU::resetCc(unsigned long const oldCc, unsigned long const newCc) { p_.spriteMapper.resetCycleCounter(oldCc, newCc); } -void PPU::speedChange(unsigned long const cycleCounter) { - unsigned long const videoCycles = lcdcEn(p_) ? p_.lyCounter.frameCycles(p_.now) : 0; +void PPU::speedChange(unsigned const cycleCounter) { + unsigned const videoCycles = lcdcEn(p_) ? p_.lyCounter.frameCycles(p_.now) : 0; p_.spriteMapper.preSpeedChange(cycleCounter); p_.lyCounter.setDoubleSpeed(!p_.lyCounter.isDoubleSpeed()); @@ -1741,12 +1741,12 @@ void PPU::speedChange(unsigned long const cycleCounter) { } } -unsigned long PPU::predictedNextXposTime(unsigned xpos) const { +unsigned PPU::predictedNextXposTime(unsigned xpos) const { return p_.now + (p_.nextCallPtr->predictCyclesUntilXpos_f(p_, xpos, -p_.cycles) << p_.lyCounter.isDoubleSpeed()); } -void PPU::setLcdc(unsigned const lcdc, unsigned long const cc) { +void PPU::setLcdc(unsigned const lcdc, unsigned const cc) { if ((p_.lcdc ^ lcdc) & lcdc & lcdc_en) { p_.now = cc; p_.lastM0Time = 0; @@ -1776,7 +1776,7 @@ void PPU::setLcdc(unsigned const lcdc, unsigned long const cc) { p_.lcdc = lcdc; } -void PPU::update(unsigned long const cc) { +void PPU::update(unsigned const cc) { int const cycles = (cc - p_.now) >> p_.lyCounter.isDoubleSpeed(); p_.now += cycles << p_.lyCounter.isDoubleSpeed(); @@ -1788,4 +1788,70 @@ void PPU::update(unsigned long const cc) { } } +void PPUPriv::loadOrSave(loadsave& state) +{ + state(bgPalette, 32); + state(spPalette, 32); + + state.startEnumeration(); + state.enumerate(nextCallPtr, NULL, 0); + state.enumerate(nextCallPtr, &M2_Ly0::f0_, 1); + state.enumerate(nextCallPtr, &M2_LyNon0::f0_, 2); + state.enumerate(nextCallPtr, &M2_LyNon0::f1_, 3); + state.enumerate(nextCallPtr, &M3Start::f0_, 4); + state.enumerate(nextCallPtr, &M3Start::f1_, 5); + state.enumerate(nextCallPtr, &M3Loop::Tile::f0_, 6); + state.enumerate(nextCallPtr, &M3Loop::Tile::f1_, 7); + state.enumerate(nextCallPtr, &M3Loop::Tile::f2_, 8); + state.enumerate(nextCallPtr, &M3Loop::Tile::f3_, 9); + state.enumerate(nextCallPtr, &M3Loop::Tile::f4_, 10); + state.enumerate(nextCallPtr, &M3Loop::Tile::f5_, 11); + state.enumerate(nextCallPtr, &M3Loop::LoadSprites::f0_, 12); + state.enumerate(nextCallPtr, &M3Loop::LoadSprites::f1_, 13); + state.enumerate(nextCallPtr, &M3Loop::LoadSprites::f2_, 14); + state.enumerate(nextCallPtr, &M3Loop::LoadSprites::f3_, 15); + state.enumerate(nextCallPtr, &M3Loop::LoadSprites::f4_, 16); + state.enumerate(nextCallPtr, &M3Loop::LoadSprites::f5_, 17); + state.enumerate(nextCallPtr, &M3Loop::StartWindowDraw::f0_, 18); + state.enumerate(nextCallPtr, &M3Loop::StartWindowDraw::f1_, 19); + state.enumerate(nextCallPtr, &M3Loop::StartWindowDraw::f2_, 20); + state.enumerate(nextCallPtr, &M3Loop::StartWindowDraw::f3_, 21); + state.enumerate(nextCallPtr, &M3Loop::StartWindowDraw::f4_, 22); + state.enumerate(nextCallPtr, &M3Loop::StartWindowDraw::f5_, 23); + state.endEnumeration(); + + state(now); + state(lastM0Time); + state(cycles); + state(tileword); + state(ntileword); + lyCounter.loadOrSave(state); + spriteMapper.loadOrSave(state); + framebuf.loadOrSave(state); + + for(size_t i = 0; i < 11; i++) + spriteList[i].loadOrSave(state); + state(spwordList, 11); + state(lcdc); + state(scy); + state(scx); + state(wy); + state(wy2); + state(wx); + state(winDrawState); + state(wscx); + state(winYPos); + state(reg0); + state(reg1); + state(attrib); + state(nattrib); + state(nextSprite); + state(currentSprite); + state(xpos); + state(endx); + state(cgb); + state(weMaster); +} + + } diff --git a/libgambatte/src/video/ppu.h b/libgambatte/src/video/ppu.h index 1bd9a22..ea711b7 100644 --- a/libgambatte/src/video/ppu.h +++ b/libgambatte/src/video/ppu.h @@ -19,27 +19,50 @@ #ifndef PPU_H #define PPU_H +// +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara +// - Make it rerecording-friendly. + +#include +#include "video/ly_counter.h" +#include "video/sprite_mapper.h" #include "lcddef.h" #include "ly_counter.h" #include "sprite_mapper.h" #include "gbint.h" +#include "../loadsave.h" #include namespace gambatte { class PPUFrameBuf { public: - PPUFrameBuf() : buf_(0), fbline_(nullfbline()), pitch_(0) {} + PPUFrameBuf() : fbline_(nullfbline()), pitch_(160) { memset(buf_, 0, sizeof(buf_)); } uint_least32_t * fb() const { return buf_; } uint_least32_t * fbline() const { return fbline_; } std::ptrdiff_t pitch() const { return pitch_; } - void setBuf(uint_least32_t *buf, std::ptrdiff_t pitch) { buf_ = buf; pitch_ = pitch; fbline_ = nullfbline(); } void setFbline(unsigned ly) { fbline_ = buf_ ? buf_ + std::ptrdiff_t(ly) * pitch_ : nullfbline(); } + void blit(uint_least32_t *const buf, const int pitch) const { + for(unsigned i = 0; i < 144; i++) + memcpy(buf + i * static_cast(pitch), buf_ + i * 160, 160 * sizeof(buf[0])); + } + void loadOrSave(loadsave& state) { + state(buf_, 160*144); + state(pitch_); + bool var = (fbline_ != nullfbline()); + state(var); + if(var) { + unsigned x = fbline_ - buf_; + state(x); + fbline_ = buf_ + x; + } else + fbline_ = nullfbline(); + } private: - uint_least32_t *buf_; + mutable uint_least32_t buf_[160*144]; uint_least32_t *fbline_; - std::ptrdiff_t pitch_; + int pitch_; static uint_least32_t * nullfbline() { static uint_least32_t nullfbline_[160]; return nullfbline_; } }; @@ -53,27 +76,34 @@ struct PPUState { }; struct PPUPriv { - unsigned long bgPalette[8 * 4]; - unsigned long spPalette[8 * 4]; - struct Sprite { unsigned char spx, oampos, line, attrib; } spriteList[11]; - unsigned short spwordList[11]; - unsigned char nextSprite; - unsigned char currentSprite; + uint_least32_t bgPalette[8 * 4]; + uint_least32_t spPalette[8 * 4]; unsigned char const *vram; PPUState const *nextCallPtr; - unsigned long now; - unsigned long lastM0Time; - long cycles; + unsigned now; + unsigned lastM0Time; + signed cycles; unsigned tileword; unsigned ntileword; - SpriteMapper spriteMapper; LyCounter lyCounter; + SpriteMapper spriteMapper; PPUFrameBuf framebuf; + struct Sprite { + unsigned char spx, oampos, line, attrib; + void loadOrSave(loadsave& state) { + state(spx); + state(oampos); + state(line); + state(attrib); + } + } spriteList[11]; + unsigned short spwordList[11]; + unsigned char lcdc; unsigned char scy; unsigned char scx; @@ -87,12 +117,15 @@ struct PPUPriv { unsigned char reg1; unsigned char attrib; unsigned char nattrib; + unsigned char nextSprite; + unsigned char currentSprite; unsigned char xpos; unsigned char endx; bool cgb; bool weMaster; + void loadOrSave(loadsave& state); PPUPriv(NextM0Time &nextM0Time, unsigned char const *oamram, unsigned char const *vram); }; @@ -103,39 +136,42 @@ public: { } - unsigned long * bgPalette() { return p_.bgPalette; } + uint_least32_t * bgPalette() { return p_.bgPalette; } bool cgb() const { return p_.cgb; } void doLyCountEvent() { p_.lyCounter.doEvent(); } - unsigned long doSpriteMapEvent(unsigned long time) { return p_.spriteMapper.doEvent(time); } + unsigned doSpriteMapEvent(unsigned time) { return p_.spriteMapper.doEvent(time); } PPUFrameBuf const & frameBuf() const { return p_.framebuf; } - bool inactivePeriodAfterDisplayEnable(unsigned long cc) const { + bool inactivePeriodAfterDisplayEnable(unsigned cc) const { return p_.spriteMapper.inactivePeriodAfterDisplayEnable(cc); } - unsigned long lastM0Time() const { return p_.lastM0Time; } + unsigned lastM0Time() const { return p_.lastM0Time; } unsigned lcdc() const { return p_.lcdc; } void loadState(SaveState const &state, unsigned char const *oamram); LyCounter const & lyCounter() const { return p_.lyCounter; } - unsigned long now() const { return p_.now; } - void oamChange(unsigned long cc) { p_.spriteMapper.oamChange(cc); } - void oamChange(unsigned char const *oamram, unsigned long cc) { p_.spriteMapper.oamChange(oamram, cc); } - unsigned long predictedNextXposTime(unsigned xpos) const; + unsigned now() const { return p_.now; } + void oamChange(unsigned cc) { p_.spriteMapper.oamChange(cc); } + void oamChange(unsigned char const *oamram, unsigned cc) { p_.spriteMapper.oamChange(oamram, cc); } + unsigned predictedNextXposTime(unsigned xpos) const; void reset(unsigned char const *oamram, unsigned char const *vram, bool cgb); - void resetCc(unsigned long oldCc, unsigned long newCc); + void resetCc(unsigned oldCc, unsigned newCc); void saveState(SaveState &ss) const; - void setFrameBuf(uint_least32_t *buf, std::ptrdiff_t pitch) { p_.framebuf.setBuf(buf, pitch); } - void setLcdc(unsigned lcdc, unsigned long cc); + void flipDisplay(uint_least32_t *buf, unsigned pitch) { p_.framebuf.blit(buf, pitch); } + void setLcdc(unsigned lcdc, unsigned cc); void setScx(unsigned scx) { p_.scx = scx; } void setScy(unsigned scy) { p_.scy = scy; } void setStatePtrs(SaveState &ss) { p_.spriteMapper.setStatePtrs(ss); } void setWx(unsigned wx) { p_.wx = wx; } void setWy(unsigned wy) { p_.wy = wy; } void updateWy2() { p_.wy2 = p_.wy; } - void speedChange(unsigned long cycleCounter); - unsigned long * spPalette() { return p_.spPalette; } - void update(unsigned long cc); + void speedChange(unsigned cycleCounter); + uint_least32_t * spPalette() { return p_.spPalette; } + void update(unsigned cc); + void loadOrSave(loadsave& state) { + p_.loadOrSave(state); + } private: PPUPriv p_; }; diff --git a/libgambatte/src/video/sprite_mapper.cpp b/libgambatte/src/video/sprite_mapper.cpp index 05ec6a8..ec1536e 100644 --- a/libgambatte/src/video/sprite_mapper.cpp +++ b/libgambatte/src/video/sprite_mapper.cpp @@ -39,6 +39,10 @@ private: } +// +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara +// - Make it rerecording-friendly. + namespace gambatte { SpriteMapper::OamReader::OamReader(LyCounter const &lyCounter, unsigned char const *oamram) @@ -65,7 +69,7 @@ void SpriteMapper::OamReader::reset(unsigned char const *const oamram, bool cons } } -static unsigned toPosCycles(unsigned long const cc, LyCounter const &lyCounter) { +static unsigned toPosCycles(unsigned const cc, LyCounter const &lyCounter) { unsigned lc = lyCounter.lineCycles(cc) + 3 - lyCounter.isDoubleSpeed() * 3u; if (lc >= 456) lc -= 456; @@ -73,7 +77,7 @@ static unsigned toPosCycles(unsigned long const cc, LyCounter const &lyCounter) return lc; } -void SpriteMapper::OamReader::update(unsigned long const cc) { +void SpriteMapper::OamReader::update(unsigned const cc) { if (cc > lu_) { if (changed()) { unsigned const lulc = toPosCycles(lu_, lyCounter_); @@ -115,7 +119,7 @@ void SpriteMapper::OamReader::update(unsigned long const cc) { } } -void SpriteMapper::OamReader::change(unsigned long cc) { +void SpriteMapper::OamReader::change(unsigned cc) { update(cc); lastChange_ = std::min(toPosCycles(lu_, lyCounter_), 80u); } @@ -132,7 +136,7 @@ void SpriteMapper::OamReader::loadState(SaveState const &ss, unsigned char const change(lu_); } -void SpriteMapper::OamReader::enableDisplay(unsigned long cc) { +void SpriteMapper::OamReader::enableDisplay(unsigned cc) { std::memset(buf_, 0x00, sizeof buf_); std::fill(szbuf_, szbuf_ + 40, false); lu_ = cc + (80 << lyCounter_.isDoubleSpeed()); @@ -145,6 +149,7 @@ SpriteMapper::SpriteMapper(NextM0Time &nextM0Time, : nextM0Time_(nextM0Time) , oamReader_(lyCounter, oamram) { + memset(spritemap_, 0, sizeof(spritemap_)); clearMap(); } @@ -188,12 +193,12 @@ void SpriteMapper::sortLine(unsigned const ly) const { SpxLess(posbuf() + 1)); } -unsigned long SpriteMapper::doEvent(unsigned long const time) { +unsigned SpriteMapper::doEvent(unsigned const time) { oamReader_.update(time); mapSprites(); return oamReader_.changed() ? time + oamReader_.lineTime() - : static_cast(disabled_time); + : static_cast(disabled_time); } } diff --git a/libgambatte/src/video/sprite_mapper.h b/libgambatte/src/video/sprite_mapper.h index 7d8dbe1..341bce6 100644 --- a/libgambatte/src/video/sprite_mapper.h +++ b/libgambatte/src/video/sprite_mapper.h @@ -19,8 +19,13 @@ #ifndef SPRITE_MAPPER_H #define SPRITE_MAPPER_H +// +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara +// - Make it rerecording-friendly. + #include "ly_counter.h" #include "../savestate.h" +#include "../loadsave.h" namespace gambatte { @@ -32,17 +37,17 @@ public: LyCounter const &lyCounter, unsigned char const *oamram); void reset(unsigned char const *oamram, bool cgb); - unsigned long doEvent(unsigned long time); + unsigned doEvent(unsigned time); bool largeSprites(unsigned spNo) const { return oamReader_.largeSprites(spNo); } unsigned numSprites(unsigned ly) const { return num_[ly] & ~need_sorting_mask; } - void oamChange(unsigned long cc) { oamReader_.change(cc); } - void oamChange(unsigned char const *oamram, unsigned long cc) { oamReader_.change(oamram, cc); } + void oamChange(unsigned cc) { oamReader_.change(cc); } + void oamChange(unsigned char const *oamram, unsigned cc) { oamReader_.change(oamram, cc); } unsigned char const * oamram() const { return oamReader_.oam(); } unsigned char const * posbuf() const { return oamReader_.spritePosBuf(); } - void preSpeedChange(unsigned long cc) { oamReader_.update(cc); } - void postSpeedChange(unsigned long cc) { oamReader_.change(cc); } + void preSpeedChange(unsigned cc) { oamReader_.update(cc); } + void postSpeedChange(unsigned cc) { oamReader_.change(cc); } - void resetCycleCounter(unsigned long oldCc, unsigned long newCc) { + void resetCycleCounter(unsigned oldCc, unsigned newCc) { oamReader_.update(oldCc); oamReader_.resetCycleCounter(oldCc, newCc); } @@ -57,7 +62,7 @@ public: } void setStatePtrs(SaveState &state) { oamReader_.setStatePtrs(state); } - void enableDisplay(unsigned long cc) { oamReader_.enableDisplay(cc); } + void enableDisplay(unsigned cc) { oamReader_.enableDisplay(cc); } void saveState(SaveState &state) const { oamReader_.saveState(state); } void loadState(SaveState const &state, unsigned char const *oamram) { @@ -65,33 +70,48 @@ public: mapSprites(); } - bool inactivePeriodAfterDisplayEnable(unsigned long cc) const { + bool inactivePeriodAfterDisplayEnable(unsigned cc) const { return oamReader_.inactivePeriodAfterDisplayEnable(cc); } - static unsigned long schedule(LyCounter const &lyCounter, unsigned long cc) { + static unsigned schedule(LyCounter const &lyCounter, unsigned cc) { return lyCounter.nextLineCycle(80, cc); } + void loadOrSave(loadsave& state) { + state(spritemap_, 1440); + state(num_, 144); + oamReader_.loadOrSave(state); + } private: class OamReader { public: + void loadOrSave(loadsave& state) { + state(buf_, 80); + for(unsigned i = 0; i < 40; i++) + state(szbuf_[i]); + state(lu_); + state(lastChange_); + state(largeSpritesSrc_); + state(cgb_); + } + OamReader(LyCounter const &lyCounter, unsigned char const *oamram); void reset(unsigned char const *oamram, bool cgb); - void change(unsigned long cc); - void change(unsigned char const *oamram, unsigned long cc) { change(cc); oamram_ = oamram; } + void change(unsigned cc); + void change(unsigned char const *oamram, unsigned cc) { change(cc); oamram_ = oamram; } bool changed() const { return lastChange_ != 0xFF; } bool largeSprites(unsigned spNo) const { return szbuf_[spNo]; } unsigned char const * oam() const { return oamram_; } - void resetCycleCounter(unsigned long oldCc, unsigned long newCc) { lu_ -= oldCc - newCc; } + void resetCycleCounter(unsigned oldCc, unsigned newCc) { lu_ -= oldCc - newCc; } void setLargeSpritesSrc(bool src) { largeSpritesSrc_ = src; } - void update(unsigned long cc); + void update(unsigned cc); unsigned char const * spritePosBuf() const { return buf_; } void setStatePtrs(SaveState &state); - void enableDisplay(unsigned long cc); + void enableDisplay(unsigned cc); void saveState(SaveState &state) const { state.ppu.enableDisplayM0Time = lu_; } void loadState(SaveState const &ss, unsigned char const *oamram); - bool inactivePeriodAfterDisplayEnable(unsigned long cc) const { return cc < lu_; } + bool inactivePeriodAfterDisplayEnable(unsigned cc) const { return cc < lu_; } unsigned lineTime() const { return lyCounter_.lineTime(); } private: @@ -99,7 +119,7 @@ private: bool szbuf_[40]; LyCounter const &lyCounter_; unsigned char const *oamram_; - unsigned long lu_; + unsigned lu_; unsigned char lastChange_; bool largeSpritesSrc_; bool cgb_; @@ -115,6 +135,7 @@ private: void clearMap(); void mapSprites(); void sortLine(unsigned ly) const; + }; } -- 1.8.5