Actually call on_reset callback
[lsnes.git] / libgambatte-patches / svn364 / 0001-Changes-to-make-libgambatte-rerecording-friendly.patch
bloba091440453fcd17ce5c4ebbc1df09540d1cfcabd
1 From f4fcace8e1261f4e0e01c7db174dc148eab1180f Mon Sep 17 00:00:00 2001
2 From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
3 Date: Fri, 18 Jan 2013 21:06:30 +0200
4 Subject: [PATCH 1/3] Changes to make libgambatte rerecording friendly
6 ---
7 Makefile | 10 +
8 libgambatte/Makefile | 18 ++
9 libgambatte/include/gambatte.h | 72 +++++++-
10 libgambatte/src/bitmap_font.cpp | 10 +-
11 libgambatte/src/bitmap_font.h | 6 +-
12 libgambatte/src/cpu.cpp | 45 ++++-
13 libgambatte/src/cpu.h | 31 +++-
14 libgambatte/src/file/file.cpp | 34 ++++
15 libgambatte/src/file/file.h | 10 +
16 libgambatte/src/gambatte.cpp | 124 +++++++++++--
17 libgambatte/src/initstate.cpp | 8 +-
18 libgambatte/src/initstate.h | 8 +-
19 libgambatte/src/interrupter.cpp | 17 +-
20 libgambatte/src/interrupter.h | 18 +-
21 libgambatte/src/interruptrequester.cpp | 23 ++-
22 libgambatte/src/interruptrequester.h | 27 ++-
23 libgambatte/src/loadsave.cpp | 266 +++++++++++++++++++++++++++
24 libgambatte/src/loadsave.h | 160 ++++++++++++++++
25 libgambatte/src/mem/cartridge.cpp | 160 +++++++++++++---
26 libgambatte/src/mem/cartridge.h | 37 +++-
27 libgambatte/src/mem/memptrs.cpp | 32 +++-
28 libgambatte/src/mem/memptrs.h | 17 +-
29 libgambatte/src/mem/rtc.cpp | 57 +++++-
30 libgambatte/src/mem/rtc.h | 11 +-
31 libgambatte/src/memory.cpp | 108 +++++++----
32 libgambatte/src/memory.h | 80 +++++---
33 libgambatte/src/minkeeper.h | 30 ++-
34 libgambatte/src/savestate.h | 54 +++---
35 libgambatte/src/sound.cpp | 36 +++-
36 libgambatte/src/sound.h | 17 +-
37 libgambatte/src/sound/channel1.cpp | 44 ++++-
38 libgambatte/src/sound/channel1.h | 25 ++-
39 libgambatte/src/sound/channel2.cpp | 41 ++++-
40 libgambatte/src/sound/channel2.h | 17 +-
41 libgambatte/src/sound/channel3.cpp | 41 ++++-
42 libgambatte/src/sound/channel3.h | 27 ++-
43 libgambatte/src/sound/channel4.cpp | 59 ++++--
44 libgambatte/src/sound/channel4.h | 40 ++--
45 libgambatte/src/sound/duty_unit.cpp | 34 +++-
46 libgambatte/src/sound/duty_unit.h | 29 +--
47 libgambatte/src/sound/envelope_unit.cpp | 19 +-
48 libgambatte/src/sound/envelope_unit.h | 12 +-
49 libgambatte/src/sound/length_counter.cpp | 19 +-
50 libgambatte/src/sound/length_counter.h | 13 +-
51 libgambatte/src/sound/sound_unit.h | 15 +-
52 libgambatte/src/sound/static_output_tester.h | 8 +-
53 libgambatte/src/state_osd_elements.cpp | 8 +-
54 libgambatte/src/statesaver.cpp | 6 +-
55 libgambatte/src/statesaver.h | 7 +
56 libgambatte/src/tima.cpp | 35 ++--
57 libgambatte/src/tima.h | 28 +--
58 libgambatte/src/video.cpp | 120 ++++++------
59 libgambatte/src/video.h | 118 +++++++-----
60 libgambatte/src/video/ly_counter.cpp | 14 +-
61 libgambatte/src/video/ly_counter.h | 26 ++-
62 libgambatte/src/video/lyc_irq.cpp | 14 +-
63 libgambatte/src/video/lyc_irq.h | 27 ++-
64 libgambatte/src/video/next_m0_time.h | 10 +
65 libgambatte/src/video/ppu.cpp | 125 ++++++++++---
66 libgambatte/src/video/ppu.h | 93 +++++++---
67 libgambatte/src/video/sprite_mapper.cpp | 17 +-
68 libgambatte/src/video/sprite_mapper.h | 53 ++++--
69 62 files changed, 2098 insertions(+), 572 deletions(-)
70 create mode 100644 Makefile
71 create mode 100644 libgambatte/Makefile
72 create mode 100644 libgambatte/src/loadsave.cpp
73 create mode 100644 libgambatte/src/loadsave.h
75 diff --git a/Makefile b/Makefile
76 new file mode 100644
77 index 0000000..2714a5b
78 --- /dev/null
79 +++ b/Makefile
80 @@ -0,0 +1,10 @@
81 +all: libgambatte/__all_files__
83 +libgambatte/__all_files__: forcelook
84 + $(MAKE) -C libgambatte
86 +clean: forcelook
87 + $(MAKE) -C libgambatte clean
89 +forcelook:
90 + @true
91 diff --git a/libgambatte/Makefile b/libgambatte/Makefile
92 new file mode 100644
93 index 0000000..7c3724e
94 --- /dev/null
95 +++ b/libgambatte/Makefile
96 @@ -0,0 +1,18 @@
97 +all: libgambatte.$(ARCHIVE_SUFFIX)
99 +REALAR=$(CROSS_PREFIX)ar
101 +OBJECTS=$(patsubst %.cpp,%.$(OBJECT_SUFFIX),$(wildcard src/*.cpp src/video/*.cpp src/mem/*.cpp src/sound/*.cpp src/file/file.cpp))
103 +%.o: %.cpp
104 + $(gambatte_compiler) $(CFLAGS) -Wno-deprecated-declarations -DHAVE_CSTDINT -I../common -Iinclude -Isrc -c -o $@ $<
106 +libgambatte.$(ARCHIVE_SUFFIX): $(OBJECTS)
107 + $(REALAR) crvs $@ $^
108 + $(REALRANLIB) $@
110 +clean: forcelook
111 + rm -f $(OBJECTS) libgambatte.$(ARCHIVE_SUFFIX)
113 +forcelook:
114 + @true
115 \ No newline at end of file
116 diff --git a/libgambatte/include/gambatte.h b/libgambatte/include/gambatte.h
117 index e997b25..11a54a9 100644
118 --- a/libgambatte/include/gambatte.h
119 +++ b/libgambatte/include/gambatte.h
120 @@ -23,6 +23,11 @@
121 #include "loadres.h"
122 #include "gbint.h"
123 #include <string>
124 +#include <vector>
127 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
128 +// - Make it rerecording-friendly.
130 namespace gambatte {
131 enum { BG_PALETTE = 0, SP1_PALETTE = 1, SP2_PALETTE = 2 };
132 @@ -31,7 +36,7 @@ class GB {
133 public:
134 GB();
135 ~GB();
138 enum LoadFlag {
139 FORCE_DMG = 1, /**< Treat the ROM as not having CGB support regardless of what its header advertises. */
140 GBA_CGB = 2, /**< Use GBA intial CPU register values when in CGB mode. */
141 @@ -45,7 +50,15 @@ public:
142 * @return 0 on success, negative value on failure.
144 LoadRes load(const std::string &romfile, unsigned flags = 0);
146 + /** Load ROM image.
148 + * @param image Raw ROM image data.
149 + * @param isize Size of raw ROM image data.
150 + * @param flags ORed combination of LoadFlags.
151 + * @return 0 on success, negative value on failure.
152 + */
153 + LoadRes load(const unsigned char* image, size_t isize, unsigned flags = 0);
155 /** Emulates until at least 'samples' stereo sound samples are produced in the supplied buffer,
156 * or until a video frame has been drawn.
158 @@ -67,7 +80,7 @@ public:
159 * @param samples in: number of stereo samples to produce, out: actual number of samples produced
160 * @return sample number at which the video frame was produced. -1 means no frame was produced.
162 - long runFor(gambatte::uint_least32_t *videoBuf, int pitch,
163 + signed runFor(gambatte::uint_least32_t *videoBuf, int pitch,
164 gambatte::uint_least32_t *soundBuf, unsigned &samples);
166 /** Reset to initial state.
167 @@ -121,7 +134,18 @@ public:
168 * @return success
170 bool loadState(const std::string &filepath);
173 + /** Save savestate to given buffer.
174 + */
175 + void saveState(std::vector<char>& data, const std::vector<char>& cmpdata);
176 + /** Save savestate to given buffer.
177 + */
178 + void saveState(std::vector<char>& data);
179 + /** Load savestate from given buffer.
180 + */
181 + void loadState(const std::vector<char>& data);
184 /** Selects which state slot to save state to or load state from.
185 * There are 10 such slots, numbered from 0 to 9 (periodically extended for all n).
187 @@ -145,14 +169,52 @@ public:
188 * @param codes Game Shark codes in format 01HHHHHH;01HHHHHH;... where H is [0-9]|[A-F]
190 void setGameShark(const std::string &codes);
193 + /** Set RTC base time.
194 + */
195 + void setRtcBase(time_t time);
197 + /** Get RTC base time.
198 + */
199 + time_t getRtcBase();
201 + /** Get pointer and size to Work RAM.
202 + * @return The pointer and size of Work RAM.
203 + */
204 + std::pair<unsigned char*, size_t> getWorkRam();
206 + /** Get pointer and size to Save RAM.
207 + * @return The pointer and size of Save RAM.
208 + */
209 + std::pair<unsigned char*, size_t> getSaveRam();
211 + /** Get pointer and size to I/O RAM.
212 + * @return The pointer and size of I/O RAM.
213 + */
214 + std::pair<unsigned char*, size_t> getIoRam();
216 + /** Get pointer and size to Video RAM.
217 + * @return The pointer and size of Video RAM.
218 + */
219 + std::pair<unsigned char*, size_t> getVideoRam();
221 + /** Function to get wall time. */
222 + void set_walltime_fn(time_t (*_walltime)());
224 + /** Get version. */
225 + static std::string version();
226 private:
227 + void preload_common();
228 + void postload_common(const unsigned flags);
229 struct Priv;
230 Priv *const p_;
231 + time_t (*walltime)();
233 GB(const GB &);
234 GB & operator=(const GB &);
238 +#define GAMBATTE_USES_LOADRES
240 #endif
241 diff --git a/libgambatte/src/bitmap_font.cpp b/libgambatte/src/bitmap_font.cpp
242 index 7d6f14d..13b591b 100644
243 --- a/libgambatte/src/bitmap_font.cpp
244 +++ b/libgambatte/src/bitmap_font.cpp
245 @@ -68,6 +68,10 @@
246 gnome dot org.
250 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
251 +// - Make it rerecording-friendly.
253 #include "bitmap_font.h"
255 static const unsigned char n0_bits[] = { 0x68,
256 @@ -285,10 +289,10 @@ unsigned getWidth(const char *chars) {
258 namespace {
259 class Rgb32Fill {
260 - const unsigned long color;
261 + const uint_least32_t color;
263 public:
264 - explicit Rgb32Fill(unsigned long color) : color(color) {}
265 + explicit Rgb32Fill(uint_least32_t color) : color(color) {}
267 void operator()(gambatte::uint_least32_t *dest, unsigned /*pitch*/) const {
268 *dest = color;
269 @@ -296,7 +300,7 @@ public:
273 -void print(gambatte::uint_least32_t *dest, const unsigned pitch, const unsigned long color, const char *chars) {
274 +void print(gambatte::uint_least32_t *dest, const unsigned pitch, const uint_least32_t color, const char *chars) {
275 print(dest, pitch, Rgb32Fill(color), chars);
278 diff --git a/libgambatte/src/bitmap_font.h b/libgambatte/src/bitmap_font.h
279 index eca12aa..26d4fdc 100644
280 --- a/libgambatte/src/bitmap_font.h
281 +++ b/libgambatte/src/bitmap_font.h
282 @@ -19,6 +19,10 @@
283 #ifndef BITMAP_FONT_H
284 #define BITMAP_FONT_H
287 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
288 +// - Make it rerecording-friendly.
290 #include "gbint.h"
292 namespace bitmapfont {
293 @@ -42,7 +46,7 @@ unsigned getWidth(const char *chars);
294 template<class RandomAccessIterator, class Fill>
295 void print(RandomAccessIterator dest, unsigned pitch, Fill fill, const char *chars);
297 -void print(gambatte::uint_least32_t *dest, unsigned pitch, unsigned long color, const char *chars);
298 +void print(gambatte::uint_least32_t *dest, unsigned pitch, uint_least32_t color, const char *chars);
299 void utoa(unsigned u, char *a);
301 // --- INTERFACE END ---
302 diff --git a/libgambatte/src/cpu.cpp b/libgambatte/src/cpu.cpp
303 index 554a724..2d7f495 100644
304 --- a/libgambatte/src/cpu.cpp
305 +++ b/libgambatte/src/cpu.cpp
306 @@ -20,10 +20,14 @@
307 #include "memory.h"
308 #include "savestate.h"
311 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
312 +// - Make it rerecording-friendly.
314 namespace gambatte {
316 -CPU::CPU()
317 -: memory(Interrupter(SP, PC_)),
318 +CPU::CPU(time_t (**_getCurrentTime)())
319 +: memory(Interrupter(SP, PC_), _getCurrentTime),
320 cycleCounter_(0),
321 PC_(0x100),
322 SP(0xFFFE),
323 @@ -42,10 +46,10 @@ CPU::CPU()
327 -long CPU::runFor(const unsigned long cycles) {
328 +signed CPU::runFor(const unsigned cycles) {
329 process(cycles/* << memory.isDoubleSpeed()*/);
331 - const long csb = memory.cyclesSinceBlit(cycleCounter_);
332 + const signed csb = memory.cyclesSinceBlit(cycleCounter_);
334 if (cycleCounter_ & 0x80000000)
335 cycleCounter_ = memory.resetCounters(cycleCounter_);
336 @@ -124,6 +128,27 @@ void CPU::loadState(const SaveState &state) {
337 skip = state.cpu.skip;
340 +void CPU::loadOrSave(loadsave& state)
342 + memory.loadOrSave(state);
343 + state(cycleCounter_);
344 + state(PC_);
345 + state(SP);
346 + state(HF1);
347 + state(HF2);
348 + state(ZF);
349 + state(CF);
350 + state(A_);
351 + state(B);
352 + state(C);
353 + state(D);
354 + state(E);
355 + state(H);
356 + state(L);
357 + state(skip);
361 #define BC() ( B << 8 | C )
362 #define DE() ( D << 8 | E )
363 #define HL() ( H << 8 | L )
364 @@ -503,18 +528,18 @@ void CPU::loadState(const SaveState &state) {
365 PC_MOD(ret_var_h << 8 | ret_var_l); \
366 } while (0)
368 -void CPU::process(const unsigned long cycles) {
369 +void CPU::process(const unsigned cycles) {
370 memory.setEndtime(cycleCounter_, cycles);
372 unsigned char A = A_;
373 - unsigned long cycleCounter = cycleCounter_;
374 + unsigned cycleCounter = cycleCounter_;
376 while (memory.isActive()) {
377 unsigned short PC = PC_;
380 if (memory.halted()) {
381 if (cycleCounter < memory.nextEventTime()) {
382 - const unsigned long cycles = memory.nextEventTime() - cycleCounter;
383 + const unsigned cycles = memory.nextEventTime() - cycleCounter;
384 cycleCounter += cycles + (-cycles & 3);
386 } else while (cycleCounter < memory.nextEventTime()) {
387 @@ -612,7 +637,7 @@ void CPU::process(const unsigned long cycles) {
388 cycleCounter = memory.stop(cycleCounter);
390 if (cycleCounter < memory.nextEventTime()) {
391 - const unsigned long cycles = memory.nextEventTime() - cycleCounter;
392 + const unsigned cycles = memory.nextEventTime() - cycleCounter;
393 cycleCounter += cycles + (-cycles & 3);
396 @@ -1140,7 +1165,7 @@ void CPU::process(const unsigned long cycles) {
397 memory.halt();
399 if (cycleCounter < memory.nextEventTime()) {
400 - const unsigned long cycles = memory.nextEventTime() - cycleCounter;
401 + const unsigned cycles = memory.nextEventTime() - cycleCounter;
402 cycleCounter += cycles + (-cycles & 3);
405 diff --git a/libgambatte/src/cpu.h b/libgambatte/src/cpu.h
406 index e0f7fc7..fd4bd64 100644
407 --- a/libgambatte/src/cpu.h
408 +++ b/libgambatte/src/cpu.h
409 @@ -19,14 +19,19 @@
410 #ifndef CPU_H
411 #define CPU_H
414 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
415 +// - Make it rerecording-friendly.
417 #include "memory.h"
418 +#include "loadsave.h"
420 namespace gambatte {
422 class CPU {
423 Memory memory;
425 - unsigned long cycleCounter_;
426 + unsigned cycleCounter_;
428 unsigned short PC_;
429 unsigned short SP;
430 @@ -37,20 +42,22 @@ class CPU {
432 bool skip;
434 - void process(unsigned long cycles);
435 + void process(unsigned cycles);
437 public:
439 - CPU();
440 + CPU(time_t (**_getCurrentTime)());
441 // void halt();
443 // unsigned interrupt(unsigned address, unsigned cycleCounter);
445 - long runFor(unsigned long cycles);
446 + signed runFor(unsigned cycles);
447 void setStatePtrs(SaveState &state);
448 void saveState(SaveState &state);
449 void loadState(const SaveState &state);
452 + void loadOrSave(loadsave& state);
454 void loadSavedata() { memory.loadSavedata(); }
455 void saveSavedata() { memory.saveSavedata(); }
457 @@ -77,7 +84,11 @@ public:
458 LoadRes load(std::string const &romfile, bool forceDmg, bool multicartCompat) {
459 return memory.loadROM(romfile, forceDmg, multicartCompat);
463 + LoadRes load(const unsigned char* image, size_t isize, bool forceDmg, bool multicartCompat) {
464 + return memory.loadROM(image, isize, forceDmg, multicartCompat);
467 bool loaded() const { return memory.loaded(); }
468 char const * romTitle() const { return memory.romTitle(); }
469 PakInfo const pakInfo(bool multicartCompat) const { return memory.pakInfo(multicartCompat); }
470 @@ -93,6 +104,14 @@ public:
472 void setGameGenie(const std::string &codes) { memory.setGameGenie(codes); }
473 void setGameShark(const std::string &codes) { memory.setGameShark(codes); }
475 + void setRtcBase(time_t time) { memory.setRtcBase(time); }
476 + time_t getRtcBase() { return memory.getRtcBase(); }
477 + std::pair<unsigned char*, size_t> getWorkRam() { return memory.getWorkRam(); }
478 + std::pair<unsigned char*, size_t> getSaveRam() { return memory.getSaveRam(); }
479 + std::pair<unsigned char*, size_t> getIoRam() { return memory.getIoRam(); }
480 + std::pair<unsigned char*, size_t> getVideoRam() { return memory.getVideoRam(); };
485 diff --git a/libgambatte/src/file/file.cpp b/libgambatte/src/file/file.cpp
486 index 2337327..2fe3ad4 100644
487 --- a/libgambatte/src/file/file.cpp
488 +++ b/libgambatte/src/file/file.cpp
489 @@ -20,7 +20,41 @@ Free Software Foundation, Inc.,
490 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
491 ***************************************************************************/
492 #include "stdfile.h"
493 +#include <cstring>
496 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
497 +// - Make it rerecording-friendly.
499 std::auto_ptr<gambatte::File> gambatte::newFileInstance(const std::string &filepath) {
500 return std::auto_ptr<File>(new StdFile(filepath.c_str()));
503 +namespace
505 + struct MemoryFile : public gambatte::File
507 + MemoryFile(const unsigned char* image, size_t isize) : buf(image), bufsize(isize),
508 + ptr(0), xfail(false) {}
509 + ~MemoryFile() {}
510 + void rewind() { ptr = 0; xfail = false; }
511 + std::size_t size() const { return bufsize; }
512 + void read(char *buffer, std::size_t amount) {
513 + if(amount > bufsize - ptr) {
514 + memcpy(buffer, buf, bufsize - ptr);
515 + xfail = true;
516 + } else
517 + memcpy(buffer, buf, amount);
519 + bool fail() const { return xfail; }
520 + private:
521 + const unsigned char* buf;
522 + size_t bufsize;
523 + size_t ptr;
524 + bool xfail;
525 + };
528 +std::auto_ptr<gambatte::File> gambatte::newFileInstance(const unsigned char* image, size_t isize) {
529 + return std::auto_ptr<File>(new MemoryFile(image, isize));
531 diff --git a/libgambatte/src/file/file.h b/libgambatte/src/file/file.h
532 index 045cd25..392319e 100644
533 --- a/libgambatte/src/file/file.h
534 +++ b/libgambatte/src/file/file.h
535 @@ -22,9 +22,18 @@ Free Software Foundation, Inc.,
536 #ifndef GAMBATTE_FILE_H
537 #define GAMBATTE_FILE_H
541 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
542 +// - Make it rerecording-friendly.
544 #include <memory>
545 #include <string>
548 +// Modified 2012-07-10 by H. Ilari Liusvaara
549 +// - New API methods.
551 namespace gambatte {
553 class File {
554 @@ -37,6 +46,7 @@ public:
557 std::auto_ptr<File> newFileInstance(const std::string &filepath);
558 +std::auto_ptr<File> newFileInstance(const unsigned char* image, size_t isize);
562 diff --git a/libgambatte/src/gambatte.cpp b/libgambatte/src/gambatte.cpp
563 index 83a68f7..9c4fcdc 100644
564 --- a/libgambatte/src/gambatte.cpp
565 +++ b/libgambatte/src/gambatte.cpp
566 @@ -25,6 +25,10 @@
567 #include <sstream>
568 #include <cstring>
571 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
572 +// - Make it rerecording-friendly.
574 static const std::string itos(const int i) {
575 std::stringstream ss;
576 ss << i;
577 @@ -35,16 +39,24 @@ static const std::string statePath(const std::string &basePath, const int stateN
578 return basePath + "_" + itos(stateNo) + ".gqs";
581 +namespace
583 + time_t default_walltime()
585 + return time(0);
589 namespace gambatte {
590 struct GB::Priv {
591 CPU cpu;
592 int stateNo;
593 unsigned loadflags;
595 - Priv() : stateNo(1), loadflags(0) {}
596 + Priv(time_t (**_getCurrentTime)()) : stateNo(1), loadflags(0), cpu(_getCurrentTime) {}
599 -GB::GB() : p_(new Priv) {}
600 +GB::GB() : p_(new Priv(&walltime)), walltime(default_walltime) {}
602 GB::~GB() {
603 if (p_->cpu.loaded())
604 @@ -53,7 +65,7 @@ GB::~GB() {
605 delete p_;
608 -long GB::runFor(gambatte::uint_least32_t *const videoBuf, const int pitch,
609 +signed GB::runFor(gambatte::uint_least32_t *const videoBuf, const int pitch,
610 gambatte::uint_least32_t *const soundBuf, unsigned &samples) {
611 if (!p_->cpu.loaded()) {
612 samples = 0;
613 @@ -62,10 +74,10 @@ long GB::runFor(gambatte::uint_least32_t *const videoBuf, const int pitch,
615 p_->cpu.setVideoBuffer(videoBuf, pitch);
616 p_->cpu.setSoundBuffer(soundBuf);
617 - const long cyclesSinceBlit = p_->cpu.runFor(samples * 2);
618 + const signed cyclesSinceBlit = p_->cpu.runFor(samples * 2);
619 samples = p_->cpu.fillSoundBuffer();
621 - return cyclesSinceBlit < 0 ? cyclesSinceBlit : static_cast<long>(samples) - (cyclesSinceBlit >> 1);
622 + return cyclesSinceBlit < 0 ? cyclesSinceBlit : static_cast<signed>(samples) - (cyclesSinceBlit >> 1);
625 void GB::reset() {
626 @@ -74,7 +86,7 @@ void GB::reset() {
628 SaveState state;
629 p_->cpu.setStatePtrs(state);
630 - setInitState(state, p_->cpu.isCgb(), p_->loadflags & GBA_CGB);
631 + setInitState(state, p_->cpu.isCgb(), p_->loadflags & GBA_CGB, walltime());
632 p_->cpu.loadState(state);
633 p_->cpu.loadSavedata();
635 @@ -88,27 +100,46 @@ void GB::setSaveDir(const std::string &sdir) {
636 p_->cpu.setSaveDir(sdir);
639 -LoadRes GB::load(std::string const &romfile, unsigned const flags) {
640 +void GB::preload_common()
642 if (p_->cpu.loaded())
643 p_->cpu.saveSavedata();
646 +void GB::postload_common(const unsigned flags)
648 + SaveState state;
649 + p_->cpu.setStatePtrs(state);
650 + setInitState(state, p_->cpu.isCgb(), flags & GBA_CGB, walltime());
651 + p_->cpu.loadState(state);
652 + p_->cpu.loadSavedata();
654 + p_->stateNo = 1;
655 + p_->cpu.setOsdElement(std::auto_ptr<OsdElement>());
658 +LoadRes GB::load(std::string const &romfile, unsigned const flags) {
659 + preload_common();
661 LoadRes const loadres = p_->cpu.load(romfile, flags & FORCE_DMG, flags & MULTICART_COMPAT);
663 - if (loadres == LOADRES_OK) {
664 - SaveState state;
665 - p_->cpu.setStatePtrs(state);
666 - p_->loadflags = flags;
667 - setInitState(state, p_->cpu.isCgb(), flags & GBA_CGB);
668 - p_->cpu.loadState(state);
669 - p_->cpu.loadSavedata();
671 - p_->stateNo = 1;
672 - p_->cpu.setOsdElement(std::auto_ptr<OsdElement>());
674 + if (loadres == LOADRES_OK)
675 + postload_common(flags);
677 return loadres;
680 +LoadRes GB::load(const unsigned char* image, size_t isize, unsigned flags) {
681 + preload_common();
683 + LoadRes const loadres = p_->cpu.load(image, isize, flags & FORCE_DMG, flags & MULTICART_COMPAT);
685 + if (loadres == LOADRES_OK)
686 + postload_common(flags);
688 + return loadres;
691 bool GB::isCgb() const {
692 return p_->cpu.isCgb();
694 @@ -171,6 +202,29 @@ bool GB::saveState(const gambatte::uint_least32_t *const videoBuf, const int pit
695 return false;
698 +void GB::saveState(std::vector<char>& data, const std::vector<char>& cmpdata) {
699 + if (p_->cpu.loaded()) {
700 + loadsave_save l(cmpdata);
701 + p_->cpu.loadOrSave(l);
702 + data = l.get();
706 +void GB::saveState(std::vector<char>& data) {
707 + if (p_->cpu.loaded()) {
708 + loadsave_save l;
709 + p_->cpu.loadOrSave(l);
710 + data = l.get();
714 +void GB::loadState(const std::vector<char>& data) {
715 + if (p_->cpu.loaded()) {
716 + loadsave_load l(data);
717 + p_->cpu.loadOrSave(l);
721 void GB::selectState(int n) {
722 n -= (n / 10) * 10;
723 p_->stateNo = n < 0 ? n + 10 : n;
724 @@ -202,4 +256,38 @@ void GB::setGameShark(const std::string &codes) {
725 p_->cpu.setGameShark(codes);
728 +void GB::setRtcBase(time_t time) {
729 + p_->cpu.setRtcBase(time);
732 +time_t GB::getRtcBase() {
733 + return p_->cpu.getRtcBase();
736 +std::pair<unsigned char*, size_t> GB::getWorkRam() {
737 + return p_->cpu.getWorkRam();
740 +std::pair<unsigned char*, size_t> GB::getSaveRam() {
741 + return p_->cpu.getSaveRam();
744 +std::pair<unsigned char*, size_t> GB::getIoRam() {
745 + return p_->cpu.getIoRam();
748 +std::pair<unsigned char*, size_t> GB::getVideoRam() {
749 + return p_->cpu.getVideoRam();
752 +void GB::set_walltime_fn(time_t (*_walltime)())
754 + walltime = _walltime;
757 +std::string GB::version()
759 + return "SVN364";
763 diff --git a/libgambatte/src/initstate.cpp b/libgambatte/src/initstate.cpp
764 index 2c28a35..2183f6f 100644
765 --- a/libgambatte/src/initstate.cpp
766 +++ b/libgambatte/src/initstate.cpp
767 @@ -24,6 +24,10 @@
768 #include <cstring>
769 #include <ctime>
772 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
773 +// - Make it rerecording-friendly.
775 namespace {
777 static void setInitialCgbWram(unsigned char *const wram) {
778 @@ -1147,7 +1151,7 @@ static void setInitialDmgIoamhram(unsigned char *const ioamhram) {
780 } // anon namespace
782 -void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbMode) {
783 +void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbMode, time_t starttime) {
784 static const unsigned char cgbObjpDump[0x40] = {
785 0x00, 0x00, 0xF2, 0xAB,
786 0x61, 0xC2, 0xD9, 0xBA,
787 @@ -1308,7 +1312,7 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM
788 state.spu.ch4.nr4 = 0;
789 state.spu.ch4.master = false;
791 - state.rtc.baseTime = std::time(0);
792 + state.rtc.baseTime = starttime;
793 state.rtc.haltTime = state.rtc.baseTime;
794 state.rtc.dataDh = 0;
795 state.rtc.dataDl = 0;
796 diff --git a/libgambatte/src/initstate.h b/libgambatte/src/initstate.h
797 index a3792b8..54a303e 100644
798 --- a/libgambatte/src/initstate.h
799 +++ b/libgambatte/src/initstate.h
800 @@ -19,8 +19,14 @@
801 #ifndef INITSTATE_H
802 #define INITSTATE_H
805 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
806 +// - Make it rerecording-friendly.
808 +#include <ctime>
810 namespace gambatte {
811 -void setInitState(struct SaveState &state, bool cgb, bool gbaCgbMode);
812 +void setInitState(struct SaveState &state, bool cgb, bool gbaCgbMode, time_t starttime);
815 #endif
816 diff --git a/libgambatte/src/interrupter.cpp b/libgambatte/src/interrupter.cpp
817 index 63e13b2..1592443 100644
818 --- a/libgambatte/src/interrupter.cpp
819 +++ b/libgambatte/src/interrupter.cpp
820 @@ -19,6 +19,10 @@
821 #include "interrupter.h"
822 #include "memory.h"
825 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
826 +// - Make it rerecording-friendly.
828 namespace gambatte {
830 Interrupter::Interrupter(unsigned short &SP_in, unsigned short &PC_in) :
831 @@ -26,7 +30,7 @@ Interrupter::Interrupter(unsigned short &SP_in, unsigned short &PC_in) :
832 PC(PC_in)
835 -unsigned long Interrupter::interrupt(const unsigned address, unsigned long cycleCounter, Memory &memory) {
836 +unsigned Interrupter::interrupt(const unsigned address, unsigned cycleCounter, Memory &memory) {
837 cycleCounter += 8;
838 SP = (SP - 1) & 0xFFFF;
839 memory.write(SP, PC >> 8, cycleCounter);
840 @@ -61,11 +65,20 @@ void Interrupter::setGameShark(const std::string &codes) {
844 -void Interrupter::applyVblankCheats(const unsigned long cycleCounter, Memory &memory) {
845 +void Interrupter::applyVblankCheats(const unsigned cycleCounter, Memory &memory) {
846 for (std::size_t i = 0, size = gsCodes.size(); i < size; ++i) {
847 if (gsCodes[i].type == 0x01)
848 memory.write(gsCodes[i].address, gsCodes[i].value, cycleCounter);
852 +void Interrupter::loadOrSave(loadsave& state) {
853 + unsigned gssize = gsCodes.size();
854 + state(gssize);
855 + if(!state.saving())
856 + gsCodes.resize(gssize);
857 + for(unsigned i = 0; i < gssize; i++)
858 + gsCodes[i].loadOrSave(state);
862 diff --git a/libgambatte/src/interrupter.h b/libgambatte/src/interrupter.h
863 index 1ccbd34..323e43f 100644
864 --- a/libgambatte/src/interrupter.h
865 +++ b/libgambatte/src/interrupter.h
866 @@ -19,8 +19,14 @@
867 #ifndef INTERRUPTER_H
868 #define INTERRUPTER_H
871 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
872 +// - Make it rerecording-friendly.
874 #include <string>
875 #include <vector>
876 +#include "loadsave.h"
879 namespace gambatte {
881 @@ -28,6 +34,12 @@ struct GsCode {
882 unsigned short address;
883 unsigned char value;
884 unsigned char type;
886 + void loadOrSave(loadsave& state) {
887 + state(address);
888 + state(value);
889 + state(type);
893 class Interrupter {
894 @@ -35,11 +47,13 @@ class Interrupter {
895 unsigned short &PC;
896 std::vector<GsCode> gsCodes;
898 - void applyVblankCheats(unsigned long cc, class Memory &mem);
899 + void applyVblankCheats(unsigned cc, class Memory &mem);
900 public:
901 Interrupter(unsigned short &SP, unsigned short &PC);
902 - unsigned long interrupt(const unsigned address, unsigned long cycleCounter, class Memory &memory);
903 + unsigned interrupt(const unsigned address, unsigned cycleCounter, class Memory &memory);
904 void setGameShark(const std::string &codes);
906 + void loadOrSave(loadsave& state);
910 diff --git a/libgambatte/src/interruptrequester.cpp b/libgambatte/src/interruptrequester.cpp
911 index fa9e68f..4f53238 100644
912 --- a/libgambatte/src/interruptrequester.cpp
913 +++ b/libgambatte/src/interruptrequester.cpp
914 @@ -19,6 +19,10 @@
915 #include "interruptrequester.h"
916 #include "savestate.h"
919 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
920 +// - Make it rerecording-friendly.
922 namespace gambatte {
924 InterruptRequester::InterruptRequester() : minIntTime(0), ifreg_(0), iereg_(0) {}
925 @@ -35,17 +39,26 @@ void InterruptRequester::loadState(const SaveState &state) {
926 iereg_ = state.mem.ioamhram.get()[0x1FF] & 0x1F;
927 intFlags.set(state.mem.IME, state.mem.halted);
929 - eventTimes.setValue<INTERRUPTS>(intFlags.imeOrHalted() && pendingIrqs() ? minIntTime : static_cast<unsigned long>(DISABLED_TIME));
930 + eventTimes.setValue<INTERRUPTS>(intFlags.imeOrHalted() && pendingIrqs() ? minIntTime : static_cast<unsigned>(DISABLED_TIME));
933 +void InterruptRequester::loadOrSave(loadsave& state)
935 + eventTimes.loadOrSave(state);
936 + state(minIntTime);
937 + state(ifreg_);
938 + state(iereg_);
939 + intFlags.loadOrSave(state);
942 -void InterruptRequester::resetCc(const unsigned long oldCc, const unsigned long newCc) {
943 +void InterruptRequester::resetCc(const unsigned oldCc, const unsigned newCc) {
944 minIntTime = minIntTime < oldCc ? 0 : minIntTime - (oldCc - newCc);
946 if (eventTimes.value(INTERRUPTS) != DISABLED_TIME)
947 eventTimes.setValue<INTERRUPTS>(minIntTime);
950 -void InterruptRequester::ei(const unsigned long cc) {
951 +void InterruptRequester::ei(const unsigned cc) {
952 intFlags.setIme();
953 minIntTime = cc + 1;
955 @@ -90,14 +103,14 @@ void InterruptRequester::setIereg(const unsigned iereg) {
956 iereg_ = iereg & 0x1F;
958 if (intFlags.imeOrHalted())
959 - eventTimes.setValue<INTERRUPTS>(pendingIrqs() ? minIntTime : static_cast<unsigned long>(DISABLED_TIME));
960 + eventTimes.setValue<INTERRUPTS>(pendingIrqs() ? minIntTime : static_cast<unsigned>(DISABLED_TIME));
963 void InterruptRequester::setIfreg(const unsigned ifreg) {
964 ifreg_ = ifreg;
966 if (intFlags.imeOrHalted())
967 - eventTimes.setValue<INTERRUPTS>(pendingIrqs() ? minIntTime : static_cast<unsigned long>(DISABLED_TIME));
968 + eventTimes.setValue<INTERRUPTS>(pendingIrqs() ? minIntTime : static_cast<unsigned>(DISABLED_TIME));
972 diff --git a/libgambatte/src/interruptrequester.h b/libgambatte/src/interruptrequester.h
973 index 00e1f8a..7ac9388 100644
974 --- a/libgambatte/src/interruptrequester.h
975 +++ b/libgambatte/src/interruptrequester.h
976 @@ -19,8 +19,13 @@
977 #ifndef INTERRUPT_REQUESTER_H
978 #define INTERRUPT_REQUESTER_H
981 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
982 +// - Make it rerecording-friendly.
984 #include "counterdef.h"
985 #include "minkeeper.h"
986 +#include "loadsave.h"
988 namespace gambatte {
989 struct SaveState;
990 @@ -28,7 +33,7 @@ enum MemEventId { UNHALT, END, BLIT, SERIAL, OAM, DMA, TIMA, VIDEO, INTERRUPTS }
992 class InterruptRequester {
993 MinKeeper<INTERRUPTS + 1> eventTimes;
994 - unsigned long minIntTime;
995 + unsigned minIntTime;
996 unsigned ifreg_;
997 unsigned iereg_;
999 @@ -38,7 +43,9 @@ class InterruptRequester {
1001 public:
1002 IntFlags() : flags_(0) {}
1005 + void loadOrSave(loadsave& state) { state(flags_); }
1007 bool ime() const { return flags_ & IME_MASK; }
1008 bool halted() const { return flags_ & HALTED_MASK; }
1009 bool imeOrHalted() const { return flags_; }
1010 @@ -57,15 +64,17 @@ public:
1012 void saveState(SaveState &) const;
1013 void loadState(const SaveState &);
1015 - void resetCc(unsigned long oldCc, unsigned long newCc);
1017 + void loadOrSave(loadsave& state);
1019 + void resetCc(unsigned oldCc, unsigned newCc);
1021 unsigned ifreg() const { return ifreg_; }
1022 unsigned pendingIrqs() const { return ifreg_ & iereg_; }
1023 bool ime() const { return intFlags.ime(); }
1024 bool halted() const { return intFlags.halted(); }
1026 - void ei(unsigned long cc);
1027 + void ei(unsigned cc);
1028 void di();
1029 void halt();
1030 void unhalt();
1031 @@ -75,10 +84,10 @@ public:
1032 void setIfreg(unsigned ifreg);
1034 MemEventId minEventId() const { return static_cast<MemEventId>(eventTimes.min()); }
1035 - unsigned long minEventTime() const { return eventTimes.minValue(); }
1036 - template<MemEventId id> void setEventTime(unsigned long value) { eventTimes.setValue<id>(value); }
1037 - void setEventTime(const MemEventId id, unsigned long value) { eventTimes.setValue(id, value); }
1038 - unsigned long eventTime(MemEventId id) const { return eventTimes.value(id); }
1039 + unsigned minEventTime() const { return eventTimes.minValue(); }
1040 + template<MemEventId id> void setEventTime(unsigned value) { eventTimes.setValue<id>(value); }
1041 + void setEventTime(const MemEventId id, unsigned value) { eventTimes.setValue(id, value); }
1042 + unsigned eventTime(MemEventId id) const { return eventTimes.value(id); }
1045 inline void flagHdmaReq(InterruptRequester *const intreq) { intreq->setEventTime<DMA>(0); }
1046 diff --git a/libgambatte/src/loadsave.cpp b/libgambatte/src/loadsave.cpp
1047 new file mode 100644
1048 index 0000000..37ea71a
1049 --- /dev/null
1050 +++ b/libgambatte/src/loadsave.cpp
1051 @@ -0,0 +1,266 @@
1052 +/***************************************************************************
1053 + * Copyright (C) 2012 by H. Ilari Liusvaara *
1054 + * ilari.liusvaara@elisanet.fi *
1055 + * *
1056 + * This program is free software; you can redistribute it and/or modify *
1057 + * it under the terms of the GNU General Public License version 2 as *
1058 + * published by the Free Software Foundation. *
1059 + * *
1060 + * This program is distributed in the hope that it will be useful, *
1061 + * but WITHOUT ANY WARRANTY; without even the implied warranty of *
1062 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
1063 + * GNU General Public License version 2 for more details. *
1064 + * *
1065 + * You should have received a copy of the GNU General Public License *
1066 + * version 2 along with this program; if not, write to the *
1067 + * Free Software Foundation, Inc., *
1068 + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
1069 + ***************************************************************************/
1070 +#include "loadsave.h"
1071 +#include <stdexcept>
1072 +#include <cstring>
1073 +#include <iostream>
1075 +namespace gambatte {
1077 +loadsave::~loadsave() throw() {}
1079 +loadsave_load::loadsave_load(const std::vector<char>& _memory)
1080 + : memory(_memory)
1082 + ptr = 0;
1085 +template<typename T> void loadsave_load::do_op(T& x)
1087 + unsigned long long v = 0;
1088 + if(ptr + sizeof(T) > memory.size())
1089 + throw std::runtime_error("Loadstate overflow");
1090 + for(size_t i = 0; i < sizeof(T); i++)
1091 + v |= ((unsigned long long)(unsigned char)memory[ptr++] << (8 * (sizeof(T) - i - 1)));
1092 + x = (T)v;
1095 +template<typename T> void loadsave_load::do_op(T& x, unsigned char _tag)
1097 + if(ptr + 1 > memory.size())
1098 + throw std::runtime_error("Loadstate overflow");
1099 + unsigned char _rtag = memory[ptr++];
1100 + if(_rtag != _tag) {
1101 + std::cerr << "Wrong type tag: expected=" << (int)_tag << ", got=" << (int)_rtag << std::endl;
1102 + throw std::runtime_error("Loadstate desynced");
1104 + do_op(x);
1107 +template<typename T> void loadsave_load::do_op(T* x, size_t s, unsigned char _tag)
1109 + if(ptr + 1 > memory.size())
1110 + throw std::runtime_error("Loadstate overflow");
1111 + unsigned char _rtag = memory[ptr++];
1112 + if(_rtag != _tag) {
1113 + std::cerr << "Wrong type tag: expected=" << (int)_tag << ", got=" << (int)_rtag << std::endl;
1114 + throw std::runtime_error("Loadstate desynced");
1116 + unsigned size;
1117 + do_op(size);
1118 + if(size != s) {
1119 + std::cerr << "Wrong number of entries: expected=" << s << ", got=" << size << std::endl;
1120 + throw std::runtime_error("Loadstate desynced");
1122 + for(size_t i = 0; i < s; i++)
1123 + do_op(x[i]);
1126 +void loadsave_load::operator()(bool& x)
1128 + char c;
1129 + do_op(c, 0);
1130 + x = (c != 0);
1134 +loadsave_load::~loadsave_load() throw() {}
1135 +void loadsave_load::operator()(signed char& x) { do_op(x, 1); }
1136 +void loadsave_load::operator()(unsigned char& x) { do_op(x, 2); }
1137 +void loadsave_load::operator()(signed short& x) { do_op(x, 3); }
1138 +void loadsave_load::operator()(unsigned short& x) { do_op(x, 4); }
1139 +void loadsave_load::operator()(signed int& x) { do_op(x, 5); }
1140 +void loadsave_load::operator()(unsigned int& x) { do_op(x, 6); }
1141 +void loadsave_load::operator()(signed long long& x) { do_op(x, 7); }
1142 +void loadsave_load::operator()(unsigned long long& x) { do_op(x, 8); }
1143 +void loadsave_load::operator()(signed char* x, size_t s) { do_op(x, s, 9); }
1144 +void loadsave_load::operator()(unsigned char* x, size_t s) { do_op(x, s, 10); }
1145 +void loadsave_load::operator()(signed short* x, size_t s) { do_op(x, s, 11); }
1146 +void loadsave_load::operator()(unsigned short* x, size_t s) { do_op(x, s, 12); }
1147 +void loadsave_load::operator()(signed int* x, size_t s) { do_op(x, s, 13); }
1148 +void loadsave_load::operator()(unsigned int* x, size_t s) { do_op(x, s, 14); }
1149 +void loadsave_load::operator()(signed long long* x, size_t s) { do_op(x, s, 15); }
1150 +void loadsave_load::operator()(unsigned long long* x, size_t s) { do_op(x, s, 16); }
1152 +void loadsave_load::tag(unsigned short _tag)
1154 + unsigned short _rtag;
1155 + do_op(_rtag, 18);
1156 + if(_tag != _rtag) {
1157 + std::cerr << "Wrong inner tag: expected=" << (int)_tag << ", got=" << (int)_rtag << std::endl;
1158 + throw std::runtime_error("Loadstate desynced");
1162 +void loadsave_load::operator()(unsigned char*& ptr, unsigned char* abase)
1164 + char x;
1165 + do_op(x, 17);
1166 + if(!x)
1167 + ptr = NULL;
1168 + else {
1169 + unsigned y;
1170 + do_op(y);
1171 + ptr = abase + y;
1175 +void loadsave_load::operator()(const unsigned char*& ptr, unsigned char* abase)
1177 + char x;
1178 + do_op(x, 19);
1179 + if(!x)
1180 + ptr = NULL;
1181 + else {
1182 + unsigned y;
1183 + do_op(y);
1184 + ptr = abase + y;
1189 +bool loadsave_load::saving() { return false; }
1191 +#define BLOCKBYTES 65500
1193 +void loadsave_save::pushbytes(char* bytes, size_t amount)
1195 + if(!nextptr || memory[nextptr - 1].second + amount > BLOCKBYTES) {
1196 + memory.push_back(std::make_pair(new char[BLOCKBYTES], (size_t)0));
1197 + nextptr++;
1199 + if(cmp.size())
1200 + try { if(cmp.size() < used + amount || memcmp(&cmp[used], bytes, amount)) throw 42; } catch(...) {}
1201 + memcpy(memory[nextptr - 1].first + memory[nextptr - 1].second, bytes, amount);
1202 + memory[nextptr - 1].second += amount;
1203 + used += amount;
1206 +template<typename T> void loadsave_save::do_op(T& x)
1208 + unsigned long long v = x;
1209 + char buf[sizeof(T)];
1210 + for(size_t i = 0; i < sizeof(T); i++)
1211 + buf[i] = v >> (8 * (sizeof(T) - i - 1));
1212 + pushbytes(buf, sizeof(T));
1215 +template<typename T> void loadsave_save::do_op(T& x, unsigned char _tag)
1217 + pushbytes((char*)&_tag, 1);
1218 + do_op(x);
1221 +template<typename T> void loadsave_save::do_op(T* x, size_t s, unsigned char _tag)
1223 + pushbytes((char*)&_tag, 1);
1224 + unsigned size = s;
1225 + do_op(size);
1226 + for(size_t i = 0; i < s; i++)
1227 + do_op(x[i]);
1230 +loadsave_save::loadsave_save()
1232 + used = 0;
1233 + nextptr = 0;
1236 +loadsave_save::loadsave_save(const std::vector<char>& _memory)
1238 + used = 0;
1239 + nextptr = 0;
1240 + cmp = _memory;
1243 +loadsave_save::~loadsave_save() throw()
1245 + for(auto i : memory)
1246 + delete[] i.first;
1249 +void loadsave_save::operator()(bool& x)
1251 + char y = x ? 1 : 0;
1252 + char z = 0;
1253 + pushbytes(&z, 1);
1254 + pushbytes(&y, 1);
1257 +void loadsave_save::operator()(signed char& x) { do_op(x, 1); }
1258 +void loadsave_save::operator()(unsigned char& x) { do_op(x, 2); }
1259 +void loadsave_save::operator()(signed short& x) { do_op(x, 3); }
1260 +void loadsave_save::operator()(unsigned short& x) { do_op(x, 4); }
1261 +void loadsave_save::operator()(signed int& x) { do_op(x, 5); }
1262 +void loadsave_save::operator()(unsigned int& x) { do_op(x, 6); }
1263 +void loadsave_save::operator()(signed long long& x) { do_op(x, 7); }
1264 +void loadsave_save::operator()(unsigned long long& x) { do_op(x, 8); }
1265 +void loadsave_save::operator()(signed char* x, size_t s) { do_op(x, s, 9); }
1266 +void loadsave_save::operator()(unsigned char* x, size_t s) { do_op(x, s, 10); }
1267 +void loadsave_save::operator()(signed short* x, size_t s) { do_op(x, s, 11); }
1268 +void loadsave_save::operator()(unsigned short* x, size_t s) { do_op(x, s, 12); }
1269 +void loadsave_save::operator()(signed int* x, size_t s) { do_op(x, s, 13); }
1270 +void loadsave_save::operator()(unsigned int* x, size_t s) { do_op(x, s, 14); }
1271 +void loadsave_save::operator()(signed long long* x, size_t s) { do_op(x, s, 15); }
1272 +void loadsave_save::operator()(unsigned long long* x, size_t s) { do_op(x, s, 16); }
1273 +bool loadsave_save::saving() { return true; }
1275 +void loadsave_save::operator()(unsigned char*& ptr, unsigned char* abase)
1277 + if(!ptr) {
1278 + char x = 0;
1279 + do_op(x, 17);
1280 + } else {
1281 + char x = 1;
1282 + unsigned y = ptr - abase;
1283 + do_op(x, 17);
1284 + do_op(y);
1288 +void loadsave_save::operator()(const unsigned char*& ptr, unsigned char* abase)
1290 + if(!ptr) {
1291 + char x = 0;
1292 + do_op(x, 19);
1293 + } else {
1294 + char x = 1;
1295 + unsigned y = ptr - abase;
1296 + do_op(x, 19);
1297 + do_op(y);
1301 +void loadsave_save::tag(unsigned short _tag)
1303 + do_op(_tag, 18);
1306 +std::vector<char> loadsave_save::get()
1308 + std::vector<char> x;
1309 + x.resize(used);
1310 + size_t ptr = 0;
1311 + for(auto i : memory) {
1312 + memcpy(&x[ptr], i.first, i.second);
1313 + ptr += i.second;
1315 + return x;
1318 diff --git a/libgambatte/src/loadsave.h b/libgambatte/src/loadsave.h
1319 new file mode 100644
1320 index 0000000..10ebf63
1321 --- /dev/null
1322 +++ b/libgambatte/src/loadsave.h
1323 @@ -0,0 +1,160 @@
1324 +#ifndef _loadsave__hpp__included__
1325 +#define _loadsave__hpp__included__
1326 +/***************************************************************************
1327 + * Copyright (C) 2012 by H. Ilari Liusvaara *
1328 + * ilari.liusvaara@elisanet.fi *
1329 + * *
1330 + * This program is free software; you can redistribute it and/or modify *
1331 + * it under the terms of the GNU General Public License version 2 as *
1332 + * published by the Free Software Foundation. *
1333 + * *
1334 + * This program is distributed in the hope that it will be useful, *
1335 + * but WITHOUT ANY WARRANTY; without even the implied warranty of *
1336 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
1337 + * GNU General Public License version 2 for more details. *
1338 + * *
1339 + * You should have received a copy of the GNU General Public License *
1340 + * version 2 along with this program; if not, write to the *
1341 + * Free Software Foundation, Inc., *
1342 + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
1343 + ***************************************************************************/
1345 +#include <cstdint>
1346 +#include <vector>
1347 +#include <cstdlib>
1348 +#include <stdexcept>
1350 +namespace gambatte {
1351 + class loadsave
1353 + private:
1354 + unsigned enumVal;
1355 + bool enumAssigned;
1356 + public:
1357 + virtual ~loadsave() throw();
1358 + virtual void operator()(bool& x) = 0;
1359 + virtual void operator()(signed char& x) = 0;
1360 + virtual void operator()(unsigned char& x) = 0;
1361 + virtual void operator()(signed short& x) = 0;
1362 + virtual void operator()(unsigned short& x) = 0;
1363 + virtual void operator()(signed int& x) = 0;
1364 + virtual void operator()(unsigned int& x) = 0;
1365 + virtual void operator()(signed long long& x) = 0;
1366 + virtual void operator()(unsigned long long& x) = 0;
1367 + virtual void operator()(signed char* x, size_t s) = 0;
1368 + virtual void operator()(unsigned char* x, size_t s) = 0;
1369 + virtual void operator()(signed short* x, size_t s) = 0;
1370 + virtual void operator()(unsigned short* x, size_t s) = 0;
1371 + virtual void operator()(signed int* x, size_t s) = 0;
1372 + virtual void operator()(unsigned int* x, size_t s) = 0;
1373 + virtual void operator()(long long* x, size_t s) = 0;
1374 + virtual void operator()(unsigned long long* x, size_t s) = 0;
1375 + virtual void operator()(unsigned char*& ptr, unsigned char* abase) = 0;
1376 + virtual void operator()(const unsigned char*& ptr, unsigned char* abase) = 0;
1377 + virtual void tag(unsigned short tag) = 0;
1378 + void time(time_t& t) {
1379 + unsigned long long t_ = t;
1380 + (*this)(t_);
1381 + t = t_;
1383 + void startEnumeration() {
1384 + enumAssigned = false;
1385 + enumVal = 0xFFFFFFFFU;
1386 + if(!saving())
1387 + (*this)(enumVal);
1389 + template<typename T> void enumerate(T& ptr, T candiate, unsigned symbol) {
1390 + if(saving()) {
1391 + if(ptr == candiate) {
1392 + enumVal = symbol;
1393 + enumAssigned = true;
1395 + } else {
1396 + if(enumVal == symbol) {
1397 + ptr = candiate;
1398 + enumAssigned = true;
1402 + void endEnumeration() {
1403 + if(saving())
1404 + (*this)(enumVal);
1405 + if(!enumAssigned)
1406 + throw std::runtime_error("Enumeration missing a choice");
1408 + virtual bool saving() = 0;
1409 + };
1411 + class loadsave_load : public loadsave
1413 + const std::vector<char>& memory;
1414 + size_t ptr;
1415 + template<typename T> inline void do_op(T& x);
1416 + template<typename T> inline void do_op(T& x, unsigned char _tag);
1417 + template<typename T> void do_op(T* x, size_t s, unsigned char _tag);
1418 + public:
1419 + loadsave_load(const std::vector<char>& _memory);
1420 + ~loadsave_load() throw();
1421 + void operator()(bool& x);
1422 + void operator()(signed char& x);
1423 + void operator()(unsigned char& x);
1424 + void operator()(signed short& x);
1425 + void operator()(unsigned short& x);
1426 + void operator()(signed int& x);
1427 + void operator()(unsigned int& x);
1428 + void operator()(signed long long& x);
1429 + void operator()(unsigned long long& x);
1430 + void operator()(signed char* x, size_t s);
1431 + void operator()(unsigned char* x, size_t s);
1432 + void operator()(signed short* x, size_t s);
1433 + void operator()(unsigned short* x, size_t s);
1434 + void operator()(signed int* x, size_t s);
1435 + void operator()(unsigned int* x, size_t s);
1436 + void operator()(signed long long* x, size_t s);
1437 + void operator()(unsigned long long* x, size_t s);
1438 + void operator()(unsigned char*& ptr, unsigned char* abase);
1439 + void operator()(const unsigned char*& ptr, unsigned char* abase);
1440 + void tag(unsigned short _tag);
1441 + bool saving();
1442 + };
1444 + class loadsave_save : public loadsave
1446 + std::vector<std::pair<char*, size_t>> memory;
1447 + size_t nextptr;
1448 + size_t used;
1449 + inline void pushbytes(char* bytes, size_t amount);
1450 + template<typename T> inline void do_op(T& x);
1451 + template<typename T> inline void do_op(T& x, unsigned char _tag);
1452 + template<typename T> void do_op(T* x, size_t s, unsigned char _tag);
1453 + std::vector<char> cmp;
1454 + public:
1455 + loadsave_save();
1456 + loadsave_save(const std::vector<char>& _memory);
1457 + ~loadsave_save() throw();
1458 + void operator()(bool& x);
1459 + void operator()(signed char& x);
1460 + void operator()(unsigned char& x);
1461 + void operator()(signed short& x);
1462 + void operator()(unsigned short& x);
1463 + void operator()(signed int& x);
1464 + void operator()(unsigned int& x);
1465 + void operator()(signed long long& x);
1466 + void operator()(unsigned long long& x);
1467 + void operator()(signed char* x, size_t s);
1468 + void operator()(unsigned char* x, size_t s);
1469 + void operator()(signed short* x, size_t s);
1470 + void operator()(unsigned short* x, size_t s);
1471 + void operator()(signed int* x, size_t s);
1472 + void operator()(unsigned int* x, size_t s);
1473 + void operator()(signed long long* x, size_t s);
1474 + void operator()(unsigned long long* x, size_t s);
1475 + void operator()(unsigned char*& ptr, unsigned char* abase);
1476 + void operator()(const unsigned char*& ptr, unsigned char* abase);
1477 + void tag(unsigned short _tag);
1478 + bool saving();
1479 + std::vector<char> get();
1480 + };
1483 +#endif
1484 diff --git a/libgambatte/src/mem/cartridge.cpp b/libgambatte/src/mem/cartridge.cpp
1485 index 6c3749c..b10f080 100644
1486 --- a/libgambatte/src/mem/cartridge.cpp
1487 +++ b/libgambatte/src/mem/cartridge.cpp
1488 @@ -23,6 +23,10 @@
1489 #include <cstring>
1490 #include <fstream>
1493 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
1494 +// - Make it rerecording-friendly.
1496 namespace gambatte {
1498 namespace {
1499 @@ -36,6 +40,8 @@ public:
1500 virtual bool isAddressWithinAreaRombankCanBeMappedTo(unsigned addr, unsigned bank) const {
1501 return (addr< 0x4000) == (bank == 0);
1503 + void loadOrSave(loadsave& state) {
1507 class Mbc0 : public DefaultMbc {
1508 @@ -64,6 +70,10 @@ public:
1509 enableRam = ss.enableRam;
1510 memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, 0);
1513 + void loadOrSave(loadsave& state) {
1514 + state(enableRam);
1518 static inline unsigned rambanks(const MemPtrs &memptrs) {
1519 @@ -95,6 +105,13 @@ public:
1523 + void loadOrSave(loadsave& state) {
1524 + state(rombank);
1525 + state(rambank);
1526 + state(enableRam);
1527 + state(rambankMode);
1530 virtual void romWrite(const unsigned P, const unsigned data) {
1531 switch (P >> 13 & 3) {
1532 case 0:
1533 @@ -168,6 +185,12 @@ public:
1537 + void loadOrSave(loadsave& state) {
1538 + state(rombank);
1539 + state(enableRam);
1540 + state(rombank0Mode);
1543 virtual void romWrite(const unsigned P, const unsigned data) {
1544 switch (P >> 13 & 3) {
1545 case 0:
1546 @@ -221,6 +244,11 @@ public:
1550 + void loadOrSave(loadsave& state) {
1551 + state(rombank);
1552 + state(enableRam);
1555 virtual void romWrite(const unsigned P, const unsigned data) {
1556 switch (P & 0x6100) {
1557 case 0x0000:
1558 @@ -277,6 +305,12 @@ public:
1562 + void loadOrSave(loadsave& state) {
1563 + state(rombank);
1564 + state(rambank);
1565 + state(enableRam);
1568 virtual void romWrite(const unsigned P, const unsigned data) {
1569 switch (P >> 13 & 3) {
1570 case 0:
1571 @@ -338,6 +372,13 @@ public:
1575 + void loadOrSave(loadsave& state) {
1576 + state(rombank);
1577 + state(rambank);
1578 + state(enableRam);
1579 + state(rambankMode);
1582 virtual void romWrite(const unsigned P, const unsigned data) {
1583 switch (P >> 13 & 3) {
1584 case 0:
1585 @@ -396,6 +437,12 @@ public:
1589 + void loadOrSave(loadsave& state) {
1590 + state(rombank);
1591 + state(rambank);
1592 + state(enableRam);
1595 virtual void romWrite(const unsigned P, const unsigned data) {
1596 switch (P >> 13 & 3) {
1597 case 0:
1598 @@ -510,7 +557,15 @@ static bool presumedMulti64Mbc1(unsigned char const header[], unsigned const rom
1600 LoadRes Cartridge::loadROM(std::string const &romfile, bool const forceDmg, bool const multicartCompat) {
1601 const std::auto_ptr<File> rom(newFileInstance(romfile));
1602 + return loadROM(rom.get(), forceDmg, multicartCompat, romfile);
1605 +LoadRes Cartridge::loadROM(const unsigned char* image, size_t isize, const bool forceDmg, const bool multicartCompat) {
1606 + const std::auto_ptr<File> rom(newFileInstance(image, isize));
1607 + return loadROM(rom.get(), forceDmg, multicartCompat, "");
1610 +LoadRes Cartridge::loadROM(File* rom, const bool forceDmg, const bool multicartCompat, const std::string& filename) {
1611 if (rom->fail())
1612 return LOADRES_IO_ERROR;
1614 @@ -593,8 +648,15 @@ LoadRes Cartridge::loadROM(std::string const &romfile, bool const forceDmg, bool
1615 if (rom->fail())
1616 return LOADRES_IO_ERROR;
1618 - defaultSaveBasePath = stripExtension(romfile);
1620 + if(filename != "") {
1621 + defaultSaveBasePath = stripExtension(filename);
1622 + memoryCartridge = false;
1623 + } else {
1624 + defaultSaveBasePath = "";
1625 + memoryCartridge = true;
1627 + clearMemorySavedData();
1629 switch (type) {
1630 case PLAIN: mbc.reset(new Mbc0(memptrs)); break;
1631 case MBC1:
1632 @@ -632,45 +694,69 @@ void Cartridge::loadSavedata() {
1633 const std::string &sbp = saveBasePath();
1635 if (hasBattery(memptrs.romdata()[0x147])) {
1636 - std::ifstream file((sbp + ".sav").c_str(), std::ios::binary | std::ios::in);
1637 + if(memoryCartridge) {
1638 + if(memoryCartridgeSram.size())
1639 + memcpy(memptrs.rambankdata(), &memoryCartridgeSram[0], memptrs.rambankdataend() - memptrs.rambankdata());
1640 + } else {
1641 + std::ifstream file((sbp + ".sav").c_str(), std::ios::binary | std::ios::in);
1643 - if (file.is_open()) {
1644 - file.read(reinterpret_cast<char*>(memptrs.rambankdata()), memptrs.rambankdataend() - memptrs.rambankdata());
1645 - enforce8bit(memptrs.rambankdata(), memptrs.rambankdataend() - memptrs.rambankdata());
1646 + if (file.is_open()) {
1647 + file.read(reinterpret_cast<char*>(memptrs.rambankdata()), memptrs.rambankdataend() - memptrs.rambankdata());
1648 + enforce8bit(memptrs.rambankdata(), memptrs.rambankdataend() - memptrs.rambankdata());
1653 if (hasRtc(memptrs.romdata()[0x147])) {
1654 - std::ifstream file((sbp + ".rtc").c_str(), std::ios::binary | std::ios::in);
1655 + if(memoryCartridge) {
1656 + rtc.setBaseTime(memoryCartridgeRtcBase);
1657 + } else {
1658 + std::ifstream file((sbp + ".rtc").c_str(), std::ios::binary | std::ios::in);
1660 - if (file.is_open()) {
1661 - unsigned long basetime = file.get() & 0xFF;
1662 + if (file.is_open()) {
1663 + unsigned long basetime = file.get() & 0xFF;
1665 - basetime = basetime << 8 | (file.get() & 0xFF);
1666 - basetime = basetime << 8 | (file.get() & 0xFF);
1667 - basetime = basetime << 8 | (file.get() & 0xFF);
1668 + basetime = basetime << 8 | (file.get() & 0xFF);
1669 + basetime = basetime << 8 | (file.get() & 0xFF);
1670 + basetime = basetime << 8 | (file.get() & 0xFF);
1672 - rtc.setBaseTime(basetime);
1673 + rtc.setBaseTime(basetime);
1679 +void Cartridge::clearMemorySavedData()
1681 + memoryCartridgeRtcBase = 0;
1682 + memoryCartridgeSram.resize(0);
1685 void Cartridge::saveSavedata() {
1686 const std::string &sbp = saveBasePath();
1688 if (hasBattery(memptrs.romdata()[0x147])) {
1689 - std::ofstream file((sbp + ".sav").c_str(), std::ios::binary | std::ios::out);
1690 - file.write(reinterpret_cast<const char*>(memptrs.rambankdata()), memptrs.rambankdataend() - memptrs.rambankdata());
1691 + if(memoryCartridge) {
1692 + memoryCartridgeSram.resize(memptrs.rambankdataend() - memptrs.rambankdata());
1693 + memcpy(&memoryCartridgeSram[0], memptrs.rambankdata(), memptrs.rambankdataend() - memptrs.rambankdata());
1694 + } else {
1695 + std::ofstream file((sbp + ".sav").c_str(), std::ios::binary | std::ios::out);
1696 + file.write(reinterpret_cast<const char*>(memptrs.rambankdata()), memptrs.rambankdataend() - memptrs.rambankdata());
1700 if (hasRtc(memptrs.romdata()[0x147])) {
1701 - std::ofstream file((sbp + ".rtc").c_str(), std::ios::binary | std::ios::out);
1702 - const unsigned long basetime = rtc.getBaseTime();
1703 + if(memoryCartridge) {
1704 + memoryCartridgeRtcBase = rtc.getBaseTime();
1705 + } else {
1706 + std::ofstream file((sbp + ".rtc").c_str(), std::ios::binary | std::ios::out);
1707 + const unsigned long basetime = rtc.getBaseTime();
1709 - file.put(basetime >> 24 & 0xFF);
1710 - file.put(basetime >> 16 & 0xFF);
1711 - file.put(basetime >> 8 & 0xFF);
1712 - file.put(basetime & 0xFF);
1713 + file.put(basetime >> 24 & 0xFF);
1714 + file.put(basetime >> 16 & 0xFF);
1715 + file.put(basetime >> 8 & 0xFF);
1716 + file.put(basetime & 0xFF);
1721 @@ -727,4 +813,36 @@ PakInfo const Cartridge::pakInfo(bool const multipakCompat) const {
1722 return PakInfo();
1725 +std::pair<unsigned char*, size_t> Cartridge::getSaveRam() {
1726 + size_t sramsize = memptrs.rambankdataend() - memptrs.rambankdata();
1727 + return std::make_pair(memptrs.rambankdata(), sramsize);
1730 +std::pair<unsigned char*, size_t> Cartridge::getVideoRam() {
1731 + size_t vramsize = memptrs.vramdataend() - memptrs.vramdata();
1732 + return std::make_pair(memptrs.vramdata(), vramsize);
1735 +std::pair<unsigned char*, size_t> Cartridge::getWorkRam() {
1736 + size_t worksize = memptrs.wramdataend() - memptrs.wramdata(0);
1737 + return std::make_pair(memptrs.wramdata(0), worksize);
1740 +Cartridge::Cartridge(time_t (**_getCurrentTime)())
1741 + : rtc(_getCurrentTime) {
1742 + memoryCartridge = true;
1745 +void Cartridge::loadOrSave(loadsave& state) {
1746 + memptrs.loadOrSave(state);
1747 + rtc.loadOrSave(state);
1748 + mbc->loadOrSave(state);
1749 + unsigned ggsize = ggUndoList.size();
1750 + state(ggsize);
1751 + if(!state.saving())
1752 + ggUndoList.resize(ggsize);
1753 + for(size_t i = 0; i < ggsize; i++)
1754 + ggUndoList[i].loadOrSave(state);
1758 diff --git a/libgambatte/src/mem/cartridge.h b/libgambatte/src/mem/cartridge.h
1759 index 9519eea..642cc83 100644
1760 --- a/libgambatte/src/mem/cartridge.h
1761 +++ b/libgambatte/src/mem/cartridge.h
1762 @@ -26,9 +26,16 @@
1763 #include <memory>
1764 #include <string>
1765 #include <vector>
1766 +#include "../loadsave.h"
1769 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
1770 +// - Make it rerecording-friendly.
1772 namespace gambatte {
1774 +class File;
1776 class Mbc {
1777 public:
1778 virtual ~Mbc() {}
1779 @@ -36,13 +43,19 @@ public:
1780 virtual void saveState(SaveState::Mem &ss) const = 0;
1781 virtual void loadState(const SaveState::Mem &ss) = 0;
1782 virtual bool isAddressWithinAreaRombankCanBeMappedTo(unsigned address, unsigned rombank) const = 0;
1783 + virtual void loadOrSave(loadsave& state) = 0;
1786 class Cartridge {
1787 struct AddrData {
1788 - unsigned long addr;
1789 + unsigned addr;
1790 unsigned char data;
1791 - AddrData(unsigned long addr, unsigned data) : addr(addr), data(data) {}
1792 + AddrData(unsigned addr, unsigned data) : addr(addr), data(data) {}
1793 + AddrData() {}
1794 + void loadOrSave(loadsave& state) {
1795 + state(addr);
1796 + state(data);
1800 MemPtrs memptrs;
1801 @@ -51,14 +64,22 @@ class Cartridge {
1802 std::string defaultSaveBasePath;
1803 std::string saveDir;
1804 std::vector<AddrData> ggUndoList;
1806 + bool memoryCartridge;
1807 + time_t memoryCartridgeRtcBase;
1808 + std::vector<unsigned char> memoryCartridgeSram;
1810 void applyGameGenie(const std::string &code);
1813 + LoadRes loadROM(File* rom, const bool forceDmg, const bool multicartCompat, const std::string& filename);
1814 + void clearMemorySavedData();
1815 public:
1816 + Cartridge(time_t (**_getCurrentTime)());
1817 void setStatePtrs(SaveState &);
1818 void saveState(SaveState &) const;
1819 void loadState(const SaveState &);
1821 + void loadOrSave(loadsave& state);
1823 bool loaded() const { return mbc.get(); }
1825 const unsigned char * rmem(unsigned area) const { return memptrs.rmem(area); }
1826 @@ -88,9 +109,17 @@ public:
1827 const std::string saveBasePath() const;
1828 void setSaveDir(const std::string &dir);
1829 LoadRes loadROM(const std::string &romfile, bool forceDmg, bool multicartCompat);
1830 + LoadRes loadROM(const unsigned char* image, size_t isize, bool forceDmg, bool multicartCompat);
1831 char const * romTitle() const { return reinterpret_cast<const char *>(memptrs.romdata() + 0x134); }
1832 class PakInfo const pakInfo(bool multicartCompat) const;
1833 void setGameGenie(const std::string &codes);
1835 + void setRtcBase(time_t time) { rtc.setBaseTime(time); }
1836 + time_t getRtcBase() { return rtc.getBaseTime(); }
1837 + std::pair<unsigned char*, size_t> getWorkRam();
1838 + std::pair<unsigned char*, size_t> getSaveRam();
1839 + std::pair<unsigned char*, size_t> getVideoRam();
1844 diff --git a/libgambatte/src/mem/memptrs.cpp b/libgambatte/src/mem/memptrs.cpp
1845 index fab2e35..d61cefd 100644
1846 --- a/libgambatte/src/mem/memptrs.cpp
1847 +++ b/libgambatte/src/mem/memptrs.cpp
1848 @@ -20,6 +20,10 @@
1849 #include <algorithm>
1850 #include <cstring>
1853 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
1854 +// - Make it rerecording-friendly.
1856 namespace gambatte {
1858 MemPtrs::MemPtrs()
1859 @@ -34,7 +38,11 @@ MemPtrs::~MemPtrs() {
1861 void MemPtrs::reset(const unsigned rombanks, const unsigned rambanks, const unsigned wrambanks) {
1862 delete []memchunk_;
1863 - memchunk_ = new unsigned char[0x4000 + rombanks * 0x4000ul + 0x4000 + rambanks * 0x2000ul + wrambanks * 0x1000ul + 0x4000];
1864 + memchunk_size = 0x4000 + rombanks * 0x4000ul + 0x4000 + rambanks * 0x2000ul + wrambanks * 0x1000ul + 0x4000;
1865 + memchunk_ = new unsigned char[memchunk_size];
1867 + //FIXME: Make this random.
1868 + memset(memchunk_, 0, memchunk_size);
1870 romdata_[0] = romdata();
1871 rambankdata_ = romdata_[0] + rombanks * 0x4000ul + 0x4000;
1872 @@ -136,4 +144,26 @@ void MemPtrs::disconnectOamDmaAreas() {
1876 +void MemPtrs::loadOrSave(loadsave& state)
1878 + state(memchunk_, 0x4000);
1879 + state(romdataend(), memchunk_size - (romdataend() - memchunk_));
1880 + int oamDmaSrc_2 = oamDmaSrc_;
1881 + state(oamDmaSrc_2);
1882 + oamDmaSrc_ = (OamDmaSrc)oamDmaSrc_2;
1883 + //Rmem is constant.
1884 + for(unsigned i = 0; i < 0x10; i++)
1885 + state(wmem_[i], memchunk_);
1886 + for(unsigned i = 0; i < 0x10; i++)
1887 + state(rmem_[i], memchunk_);
1888 + state(romdata_[0], memchunk_);
1889 + state(romdata_[1], memchunk_);
1890 + state(rambankdata_, memchunk_);
1891 + state(rsrambankptr_, memchunk_);
1892 + state(wsrambankptr_, memchunk_);
1893 + state(wramdataend_, memchunk_);
1894 + state(vrambankptr_, memchunk_);
1895 + //memchunk_size is cart constant, not saved.
1899 diff --git a/libgambatte/src/mem/memptrs.h b/libgambatte/src/mem/memptrs.h
1900 index b4e1abe..5c6406e 100644
1901 --- a/libgambatte/src/mem/memptrs.h
1902 +++ b/libgambatte/src/mem/memptrs.h
1903 @@ -19,6 +19,12 @@
1904 #ifndef MEMPTRS_H
1905 #define MEMPTRS_H
1907 +#include "../loadsave.h"
1910 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
1911 +// - Make it rerecording-friendly.
1913 namespace gambatte {
1915 enum OamDmaSrc { OAM_DMA_SRC_ROM, OAM_DMA_SRC_SRAM, OAM_DMA_SRC_VRAM,
1916 @@ -28,15 +34,16 @@ class MemPtrs {
1917 const unsigned char *rmem_[0x10];
1918 unsigned char *wmem_[0x10];
1920 + unsigned char *memchunk_;
1921 unsigned char *romdata_[2];
1922 unsigned char *wramdata_[2];
1923 + unsigned char *rambankdata_;
1924 + unsigned char *wramdataend_;
1925 unsigned char *vrambankptr_;
1926 unsigned char *rsrambankptr_;
1927 unsigned char *wsrambankptr_;
1928 - unsigned char *memchunk_;
1929 - unsigned char *rambankdata_;
1930 - unsigned char *wramdataend_;
1932 + unsigned memchunk_size;
1934 OamDmaSrc oamDmaSrc_;
1936 MemPtrs(const MemPtrs &);
1937 @@ -74,6 +81,8 @@ public:
1938 void setVrambank(unsigned bank) { vrambankptr_ = vramdata() + bank * 0x2000ul - 0x8000; }
1939 void setWrambank(unsigned bank);
1940 void setOamDmaSrc(OamDmaSrc oamDmaSrc);
1942 + void loadOrSave(loadsave& state);
1945 inline bool isCgb(const MemPtrs &memptrs) {
1946 diff --git a/libgambatte/src/mem/rtc.cpp b/libgambatte/src/mem/rtc.cpp
1947 index a834248..59f8fdc 100644
1948 --- a/libgambatte/src/mem/rtc.cpp
1949 +++ b/libgambatte/src/mem/rtc.cpp
1950 @@ -19,9 +19,13 @@
1951 #include "rtc.h"
1952 #include "../savestate.h"
1955 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
1956 +// - Make it rerecording-friendly.
1958 namespace gambatte {
1960 -Rtc::Rtc()
1961 +Rtc::Rtc(time_t (**_getCurrentTime)())
1962 : activeData(NULL),
1963 activeSet(NULL),
1964 baseTime(0),
1965 @@ -33,12 +37,13 @@ Rtc::Rtc()
1966 dataM(0),
1967 dataS(0),
1968 enabled(false),
1969 - lastLatchData(false)
1970 + lastLatchData(false),
1971 + getCurrentTime(_getCurrentTime)
1975 void Rtc::doLatch() {
1976 - std::time_t tmp = ((dataDh & 0x40) ? haltTime : std::time(0)) - baseTime;
1977 + std::time_t tmp = ((dataDh & 0x40) ? haltTime : (*getCurrentTime)()) - baseTime;
1979 while (tmp > 0x1FF * 86400) {
1980 baseTime += 0x1FF * 86400;
1981 @@ -113,44 +118,76 @@ void Rtc::loadState(const SaveState &state) {
1984 void Rtc::setDh(const unsigned new_dh) {
1985 - const std::time_t unixtime = (dataDh & 0x40) ? haltTime : std::time(0);
1986 + const std::time_t unixtime = (dataDh & 0x40) ? haltTime : (*getCurrentTime)();
1987 const std::time_t old_highdays = ((unixtime - baseTime) / 86400) & 0x100;
1988 baseTime += old_highdays * 86400;
1989 baseTime -= ((new_dh & 0x1) << 8) * 86400;
1991 if ((dataDh ^ new_dh) & 0x40) {
1992 if (new_dh & 0x40)
1993 - haltTime = std::time(0);
1994 + haltTime = (*getCurrentTime)();
1995 else
1996 - baseTime += std::time(0) - haltTime;
1997 + baseTime += (*getCurrentTime)() - haltTime;
2001 void Rtc::setDl(const unsigned new_lowdays) {
2002 - const std::time_t unixtime = (dataDh & 0x40) ? haltTime : std::time(0);
2003 + const std::time_t unixtime = (dataDh & 0x40) ? haltTime : (*getCurrentTime)();
2004 const std::time_t old_lowdays = ((unixtime - baseTime) / 86400) & 0xFF;
2005 baseTime += old_lowdays * 86400;
2006 baseTime -= new_lowdays * 86400;
2009 void Rtc::setH(const unsigned new_hours) {
2010 - const std::time_t unixtime = (dataDh & 0x40) ? haltTime : std::time(0);
2011 + const std::time_t unixtime = (dataDh & 0x40) ? haltTime : (*getCurrentTime)();
2012 const std::time_t old_hours = ((unixtime - baseTime) / 3600) % 24;
2013 baseTime += old_hours * 3600;
2014 baseTime -= new_hours * 3600;
2017 void Rtc::setM(const unsigned new_minutes) {
2018 - const std::time_t unixtime = (dataDh & 0x40) ? haltTime : std::time(0);
2019 + const std::time_t unixtime = (dataDh & 0x40) ? haltTime : (*getCurrentTime)();
2020 const std::time_t old_minutes = ((unixtime - baseTime) / 60) % 60;
2021 baseTime += old_minutes * 60;
2022 baseTime -= new_minutes * 60;
2025 void Rtc::setS(const unsigned new_seconds) {
2026 - const std::time_t unixtime = (dataDh & 0x40) ? haltTime : std::time(0);
2027 + const std::time_t unixtime = (dataDh & 0x40) ? haltTime : (*getCurrentTime)();
2028 baseTime += (unixtime - baseTime) % 60;
2029 baseTime -= new_seconds;
2032 +void Rtc::loadOrSave(loadsave& state)
2034 + state.startEnumeration();
2035 + state.enumerate<unsigned char*>(activeData, NULL, 0);
2036 + state.enumerate(activeData, &dataDh, 1);
2037 + state.enumerate(activeData, &dataDl, 2);
2038 + state.enumerate(activeData, &dataH, 3);
2039 + state.enumerate(activeData, &dataM, 4);
2040 + state.enumerate(activeData, &dataS, 5);
2041 + state.endEnumeration();
2043 + state.startEnumeration();
2044 + state.enumerate<void (gambatte::Rtc::*)(unsigned int)>(activeSet, NULL, 0);
2045 + state.enumerate(activeSet, &Rtc::setDh, 1);
2046 + state.enumerate(activeSet, &Rtc::setDl, 2);
2047 + state.enumerate(activeSet, &Rtc::setH, 3);
2048 + state.enumerate(activeSet, &Rtc::setM, 4);
2049 + state.enumerate(activeSet, &Rtc::setS, 5);
2050 + state.endEnumeration();
2052 + state.time(baseTime);
2053 + state.time(haltTime);
2054 + state(index);
2055 + state(dataDh);
2056 + state(dataDl);
2057 + state(dataH);
2058 + state(dataM);
2059 + state(dataS);
2060 + state(enabled);
2061 + state(lastLatchData);
2065 diff --git a/libgambatte/src/mem/rtc.h b/libgambatte/src/mem/rtc.h
2066 index 022d235..ac967b1 100644
2067 --- a/libgambatte/src/mem/rtc.h
2068 +++ b/libgambatte/src/mem/rtc.h
2069 @@ -20,6 +20,12 @@
2070 #define RTC_H
2072 #include <ctime>
2073 +#include "../loadsave.h"
2076 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
2077 +// - Make it rerecording-friendly.
2080 namespace gambatte {
2082 @@ -39,6 +45,7 @@ private:
2083 unsigned char dataS;
2084 bool enabled;
2085 bool lastLatchData;
2086 + time_t (**getCurrentTime)();
2088 void doLatch();
2089 void doSwapActive();
2090 @@ -49,7 +56,7 @@ private:
2091 void setS(unsigned new_seconds);
2093 public:
2094 - Rtc();
2095 + Rtc(time_t (**_getCurrentTime)());
2097 const unsigned char* getActive() const { return activeData; }
2098 std::time_t getBaseTime() const { return baseTime; }
2099 @@ -84,6 +91,8 @@ public:
2100 (this->*activeSet)(data);
2101 *activeData = data;
2104 + void loadOrSave(loadsave& state);
2108 diff --git a/libgambatte/src/memory.cpp b/libgambatte/src/memory.cpp
2109 index ed3c66b..917d818 100644
2110 --- a/libgambatte/src/memory.cpp
2111 +++ b/libgambatte/src/memory.cpp
2112 @@ -23,9 +23,13 @@
2113 #include "savestate.h"
2114 #include <cstring>
2117 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
2118 +// - Make it rerecording-friendly.
2120 namespace gambatte {
2122 -Memory::Memory(const Interrupter &interrupter_in)
2123 +Memory::Memory(const Interrupter &interrupter_in, time_t (**_getCurrentTime)())
2124 : getInput(0),
2125 divLastUpdate(0),
2126 lastOamDmaUpdate(DISABLED_TIME),
2127 @@ -35,7 +39,8 @@ Memory::Memory(const Interrupter &interrupter_in)
2128 dmaDestination(0),
2129 oamDmaPos(0xFE),
2130 serialCnt(0),
2131 - blanklcd(false)
2132 + blanklcd(false),
2133 + cart(_getCurrentTime)
2135 intreq.setEventTime<BLIT>(144*456ul);
2136 intreq.setEventTime<END>(0);
2137 @@ -49,7 +54,7 @@ void Memory::setStatePtrs(SaveState &state) {
2138 sound.setStatePtrs(state);
2141 -unsigned long Memory::saveState(SaveState &state, unsigned long cycleCounter) {
2142 +unsigned Memory::saveState(SaveState &state, unsigned cycleCounter) {
2143 cycleCounter = resetCounters(cycleCounter);
2144 nontrivial_ff_read(0xFF05, cycleCounter);
2145 nontrivial_ff_read(0xFF0F, cycleCounter);
2146 @@ -72,7 +77,7 @@ unsigned long Memory::saveState(SaveState &state, unsigned long cycleCounter) {
2147 return cycleCounter;
2150 -static inline int serialCntFrom(const unsigned long cyclesUntilDone, const bool cgbFast) {
2151 +static inline int serialCntFrom(const unsigned cyclesUntilDone, const bool cgbFast) {
2152 return cgbFast ? (cyclesUntilDone + 0xF) >> 4 : (cyclesUntilDone + 0x1FF) >> 9;
2155 @@ -113,14 +118,14 @@ void Memory::loadState(const SaveState &state) {
2156 std::memset(cart.vramdata() + 0x2000, 0, 0x2000);
2159 -void Memory::setEndtime(const unsigned long cycleCounter, const unsigned long inc) {
2160 +void Memory::setEndtime(const unsigned cycleCounter, const unsigned inc) {
2161 if (intreq.eventTime(BLIT) <= cycleCounter)
2162 intreq.setEventTime<BLIT>(intreq.eventTime(BLIT) + (70224 << isDoubleSpeed()));
2164 intreq.setEventTime<END>(cycleCounter + (inc << isDoubleSpeed()));
2167 -void Memory::updateSerial(const unsigned long cc) {
2168 +void Memory::updateSerial(const unsigned cc) {
2169 if (intreq.eventTime(SERIAL) != DISABLED_TIME) {
2170 if (intreq.eventTime(SERIAL) <= cc) {
2171 ioamhram[0x101] = (((ioamhram[0x101] + 1) << serialCnt) - 1) & 0xFF;
2172 @@ -135,18 +140,18 @@ void Memory::updateSerial(const unsigned long cc) {
2176 -void Memory::updateTimaIrq(const unsigned long cc) {
2177 +void Memory::updateTimaIrq(const unsigned cc) {
2178 while (intreq.eventTime(TIMA) <= cc)
2179 tima.doIrqEvent(TimaInterruptRequester(intreq));
2182 -void Memory::updateIrqs(const unsigned long cc) {
2183 +void Memory::updateIrqs(const unsigned cc) {
2184 updateSerial(cc);
2185 updateTimaIrq(cc);
2186 display.update(cc);
2189 -unsigned long Memory::event(unsigned long cycleCounter) {
2190 +unsigned Memory::event(unsigned cycleCounter) {
2191 if (lastOamDmaUpdate != DISABLED_TIME)
2192 updateOamDma(cycleCounter);
2194 @@ -167,10 +172,10 @@ unsigned long Memory::event(unsigned long cycleCounter) {
2195 case BLIT:
2197 const bool lcden = ioamhram[0x140] >> 7 & 1;
2198 - unsigned long blitTime = intreq.eventTime(BLIT);
2199 + unsigned blitTime = intreq.eventTime(BLIT);
2201 if (lcden | blanklcd) {
2202 - display.updateScreen(blanklcd, cycleCounter);
2203 + display.updateScreen(blanklcd, cycleCounter, videoBuf_, pitch_);
2204 intreq.setEventTime<BLIT>(DISABLED_TIME);
2205 intreq.setEventTime<END>(DISABLED_TIME);
2207 @@ -188,7 +193,7 @@ unsigned long Memory::event(unsigned long cycleCounter) {
2208 break;
2209 case OAM:
2210 intreq.setEventTime<OAM>(lastOamDmaUpdate == DISABLED_TIME ?
2211 - static_cast<unsigned long>(DISABLED_TIME) : intreq.eventTime(OAM) + 0xA0 * 4);
2212 + static_cast<unsigned>(DISABLED_TIME) : intreq.eventTime(OAM) + 0xA0 * 4);
2213 break;
2214 case DMA:
2216 @@ -200,7 +205,7 @@ unsigned long Memory::event(unsigned long cycleCounter) {
2218 ackDmaReq(&intreq);
2220 - if ((static_cast<unsigned long>(dmaDest) + length) & 0x10000) {
2221 + if ((static_cast<unsigned>(dmaDest) + length) & 0x10000) {
2222 length = 0x10000 - dmaDest;
2223 ioamhram[0x155] |= 0x80;
2225 @@ -211,7 +216,7 @@ unsigned long Memory::event(unsigned long cycleCounter) {
2226 dmaLength = 0;
2229 - unsigned long lOamDmaUpdate = lastOamDmaUpdate;
2230 + unsigned lOamDmaUpdate = lastOamDmaUpdate;
2231 lastOamDmaUpdate = DISABLED_TIME;
2233 while (length--) {
2234 @@ -292,7 +297,7 @@ unsigned long Memory::event(unsigned long cycleCounter) {
2235 return cycleCounter;
2238 -unsigned long Memory::stop(unsigned long cycleCounter) {
2239 +unsigned Memory::stop(unsigned cycleCounter) {
2240 cycleCounter += 4 << isDoubleSpeed();
2242 if (ioamhram[0x14D] & isCgb()) {
2243 @@ -315,31 +320,31 @@ unsigned long Memory::stop(unsigned long cycleCounter) {
2244 return cycleCounter;
2247 -static void decCycles(unsigned long &counter, const unsigned long dec) {
2248 +static void decCycles(unsigned &counter, const unsigned dec) {
2249 if (counter != DISABLED_TIME)
2250 counter -= dec;
2253 -void Memory::decEventCycles(const MemEventId eventId, const unsigned long dec) {
2254 +void Memory::decEventCycles(const MemEventId eventId, const unsigned dec) {
2255 if (intreq.eventTime(eventId) != DISABLED_TIME)
2256 intreq.setEventTime(eventId, intreq.eventTime(eventId) - dec);
2259 -unsigned long Memory::resetCounters(unsigned long cycleCounter) {
2260 +unsigned Memory::resetCounters(unsigned cycleCounter) {
2261 if (lastOamDmaUpdate != DISABLED_TIME)
2262 updateOamDma(cycleCounter);
2264 updateIrqs(cycleCounter);
2266 - const unsigned long oldCC = cycleCounter;
2267 + const unsigned oldCC = cycleCounter;
2270 - const unsigned long divinc = (cycleCounter - divLastUpdate) >> 8;
2271 + const unsigned divinc = (cycleCounter - divLastUpdate) >> 8;
2272 ioamhram[0x104] = (ioamhram[0x104] + divinc) & 0xFF;
2273 divLastUpdate += divinc << 8;
2276 - const unsigned long dec = cycleCounter < 0x10000 ? 0 : (cycleCounter & ~0x7FFFul) - 0x8000;
2277 + const unsigned dec = cycleCounter < 0x10000 ? 0 : (cycleCounter & ~0x7FFFul) - 0x8000;
2279 decCycles(divLastUpdate, dec);
2280 decCycles(lastOamDmaUpdate, dec);
2281 @@ -378,7 +383,7 @@ void Memory::updateInput() {
2282 ioamhram[0x100] &= button;
2285 -void Memory::updateOamDma(const unsigned long cycleCounter) {
2286 +void Memory::updateOamDma(const unsigned cycleCounter) {
2287 const unsigned char *const oamDmaSrc = oamDmaSrcPtr();
2288 unsigned cycles = (cycleCounter - lastOamDmaUpdate) >> 2;
2290 @@ -426,17 +431,17 @@ const unsigned char * Memory::oamDmaSrcPtr() const {
2291 return ioamhram[0x146] == 0xFF && !isCgb() ? oamDmaSrcZero() : cart.rdisabledRam();
2294 -void Memory::startOamDma(const unsigned long cycleCounter) {
2295 +void Memory::startOamDma(const unsigned cycleCounter) {
2296 display.oamChange(cart.rdisabledRam(), cycleCounter);
2299 -void Memory::endOamDma(const unsigned long cycleCounter) {
2300 +void Memory::endOamDma(const unsigned cycleCounter) {
2301 oamDmaPos = 0xFE;
2302 cart.setOamDmaSrc(OAM_DMA_SRC_OFF);
2303 display.oamChange(ioamhram, cycleCounter);
2306 -unsigned Memory::nontrivial_ff_read(const unsigned P, const unsigned long cycleCounter) {
2307 +unsigned Memory::nontrivial_ff_read(const unsigned P, const unsigned cycleCounter) {
2308 if (lastOamDmaUpdate != DISABLED_TIME)
2309 updateOamDma(cycleCounter);
2311 @@ -450,7 +455,7 @@ unsigned Memory::nontrivial_ff_read(const unsigned P, const unsigned long cycleC
2312 break;
2313 case 0x04:
2315 - const unsigned long divcycles = (cycleCounter - divLastUpdate) >> 8;
2316 + const unsigned divcycles = (cycleCounter - divLastUpdate) >> 8;
2317 ioamhram[0x104] = (ioamhram[0x104] + divcycles) & 0xFF;
2318 divLastUpdate += divcycles << 8;
2320 @@ -529,7 +534,7 @@ static bool isInOamDmaConflictArea(const OamDmaSrc oamDmaSrc, const unsigned add
2321 return addr < a[oamDmaSrc].areaUpper && addr - a[oamDmaSrc].exceptAreaLower >= a[oamDmaSrc].exceptAreaWidth;
2324 -unsigned Memory::nontrivial_read(const unsigned P, const unsigned long cycleCounter) {
2325 +unsigned Memory::nontrivial_read(const unsigned P, const unsigned cycleCounter) {
2326 if (P < 0xFF80) {
2327 if (lastOamDmaUpdate != DISABLED_TIME) {
2328 updateOamDma(cycleCounter);
2329 @@ -568,7 +573,7 @@ unsigned Memory::nontrivial_read(const unsigned P, const unsigned long cycleCoun
2330 return ioamhram[P - 0xFE00];
2333 -void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned long cycleCounter) {
2334 +void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned cycleCounter) {
2335 if (lastOamDmaUpdate != DISABLED_TIME)
2336 updateOamDma(cycleCounter);
2338 @@ -585,7 +590,7 @@ void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned
2339 serialCnt = 8;
2340 intreq.setEventTime<SERIAL>((data & 0x81) == 0x81
2341 ? (data & isCgb() * 2 ? (cycleCounter & ~0x7ul) + 0x10 * 8 : (cycleCounter & ~0xFFul) + 0x200 * 8)
2342 - : static_cast<unsigned long>(DISABLED_TIME));
2343 + : static_cast<unsigned>(DISABLED_TIME));
2345 data |= 0x7E - isCgb() * 2;
2346 break;
2347 @@ -946,7 +951,7 @@ void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned
2348 ioamhram[P - 0xFE00] = data;
2351 -void Memory::nontrivial_write(const unsigned P, const unsigned data, const unsigned long cycleCounter) {
2352 +void Memory::nontrivial_write(const unsigned P, const unsigned data, const unsigned cycleCounter) {
2353 if (lastOamDmaUpdate != DISABLED_TIME) {
2354 updateOamDma(cycleCounter);
2356 @@ -983,24 +988,57 @@ void Memory::nontrivial_write(const unsigned P, const unsigned data, const unsig
2357 ioamhram[P - 0xFE00] = data;
2360 +void Memory::postLoadRom()
2362 + sound.init(cart.isCgb());
2363 + display.reset(ioamhram, cart.vramdata(), cart.isCgb());
2364 + interrupter.setGameShark(std::string());
2367 LoadRes Memory::loadROM(std::string const &romfile, bool const forceDmg, bool const multicartCompat) {
2368 if (LoadRes const fail = cart.loadROM(romfile, forceDmg, multicartCompat))
2369 return fail;
2371 - sound.init(cart.isCgb());
2372 - display.reset(ioamhram, cart.vramdata(), cart.isCgb());
2373 - interrupter.setGameShark(std::string());
2374 + postLoadRom();
2376 return LOADRES_OK;
2379 -unsigned Memory::fillSoundBuffer(const unsigned long cycleCounter) {
2380 +LoadRes Memory::loadROM(const unsigned char* image, size_t isize, const bool forceDmg, const bool multicartCompat) {
2381 + if (LoadRes fail = cart.loadROM(image, isize, forceDmg, multicartCompat))
2382 + return fail;
2384 + postLoadRom();
2386 + return LOADRES_OK;
2389 +unsigned Memory::fillSoundBuffer(const unsigned cycleCounter) {
2390 sound.generate_samples(cycleCounter, isDoubleSpeed());
2391 return sound.fillBuffer();
2394 -void Memory::setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned long rgb32) {
2395 +void Memory::setDmgPaletteColor(unsigned palNum, unsigned colorNum, uint_least32_t rgb32) {
2396 display.setDmgPaletteColor(palNum, colorNum, rgb32);
2399 +void Memory::loadOrSave(loadsave& state)
2401 + state(ioamhram, 0x200);
2402 + //Don't save getInput, it has no state.
2403 + state(divLastUpdate);
2404 + state(lastOamDmaUpdate);
2405 + intreq.loadOrSave(state);
2406 + cart.loadOrSave(state);
2407 + tima.loadOrSave(state);
2408 + display.loadOrSave(state);
2409 + sound.loadOrSave(state);
2410 + interrupter.loadOrSave(state);
2411 + state(dmaSource);
2412 + state(dmaDestination);
2413 + state(oamDmaPos);
2414 + state(serialCnt);
2415 + state(blanklcd);
2419 diff --git a/libgambatte/src/memory.h b/libgambatte/src/memory.h
2420 index 02ed524..0278b33 100644
2421 --- a/libgambatte/src/memory.h
2422 +++ b/libgambatte/src/memory.h
2423 @@ -19,6 +19,10 @@
2424 #ifndef MEMORY_H
2425 #define MEMORY_H
2428 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
2429 +// - Make it rerecording-friendly.
2431 #include "mem/cartridge.h"
2432 #include "interrupter.h"
2433 #include "pakinfo.h"
2434 @@ -35,8 +39,8 @@ class Memory {
2435 unsigned char ioamhram[0x200];
2437 InputGetter *getInput;
2438 - unsigned long divLastUpdate;
2439 - unsigned long lastOamDmaUpdate;
2440 + unsigned divLastUpdate;
2441 + unsigned lastOamDmaUpdate;
2443 InterruptRequester intreq;
2444 Tima tima;
2445 @@ -50,35 +54,41 @@ class Memory {
2446 unsigned char serialCnt;
2447 bool blanklcd;
2449 + uint_least32_t* videoBuf_;
2450 + unsigned pitch_;
2452 void updateInput();
2453 - void decEventCycles(MemEventId eventId, unsigned long dec);
2454 + void decEventCycles(MemEventId eventId, unsigned dec);
2456 void oamDmaInitSetup();
2457 - void updateOamDma(unsigned long cycleCounter);
2458 - void startOamDma(unsigned long cycleCounter);
2459 - void endOamDma(unsigned long cycleCounter);
2460 + void updateOamDma(unsigned cycleCounter);
2461 + void startOamDma(unsigned cycleCounter);
2462 + void endOamDma(unsigned cycleCounter);
2463 const unsigned char * oamDmaSrcPtr() const;
2465 - unsigned nontrivial_ff_read(unsigned P, unsigned long cycleCounter);
2466 - unsigned nontrivial_read(unsigned P, unsigned long cycleCounter);
2467 - void nontrivial_ff_write(unsigned P, unsigned data, unsigned long cycleCounter);
2468 - void nontrivial_write(unsigned P, unsigned data, unsigned long cycleCounter);
2469 + unsigned nontrivial_ff_read(unsigned P, unsigned cycleCounter);
2470 + unsigned nontrivial_read(unsigned P, unsigned cycleCounter);
2471 + void nontrivial_ff_write(unsigned P, unsigned data, unsigned cycleCounter);
2472 + void nontrivial_write(unsigned P, unsigned data, unsigned cycleCounter);
2474 - void updateSerial(unsigned long cc);
2475 - void updateTimaIrq(unsigned long cc);
2476 - void updateIrqs(unsigned long cc);
2477 + void updateSerial(unsigned cc);
2478 + void updateTimaIrq(unsigned cc);
2479 + void updateIrqs(unsigned cc);
2481 bool isDoubleSpeed() const { return display.isDoubleSpeed(); }
2483 + void postLoadRom();
2484 public:
2485 - explicit Memory(const Interrupter &interrupter);
2486 + explicit Memory(const Interrupter &interrupter, time_t (**_getCurrentTime)());
2488 bool loaded() const { return cart.loaded(); }
2489 char const * romTitle() const { return cart.romTitle(); }
2490 PakInfo const pakInfo(bool multicartCompat) const { return cart.pakInfo(multicartCompat); }
2492 + void loadOrSave(loadsave& state);
2494 void setStatePtrs(SaveState &state);
2495 - unsigned long saveState(SaveState &state, unsigned long cc);
2496 + unsigned saveState(SaveState &state, unsigned cc);
2497 void loadState(const SaveState &state/*, unsigned long oldCc*/);
2498 void loadSavedata() { cart.loadSavedata(); }
2499 void saveSavedata() { cart.saveSavedata(); }
2500 @@ -88,67 +98,77 @@ public:
2501 display.setOsdElement(osdElement);
2504 - unsigned long stop(unsigned long cycleCounter);
2505 + unsigned stop(unsigned cycleCounter);
2506 bool isCgb() const { return display.isCgb(); }
2507 bool ime() const { return intreq.ime(); }
2508 bool halted() const { return intreq.halted(); }
2509 - unsigned long nextEventTime() const { return intreq.minEventTime(); }
2510 + unsigned nextEventTime() const { return intreq.minEventTime(); }
2512 bool isActive() const { return intreq.eventTime(END) != DISABLED_TIME; }
2514 - long cyclesSinceBlit(const unsigned long cc) const {
2515 - return cc < intreq.eventTime(BLIT) ? -1 : static_cast<long>((cc - intreq.eventTime(BLIT)) >> isDoubleSpeed());
2516 + signed cyclesSinceBlit(const unsigned cc) const {
2517 + return cc < intreq.eventTime(BLIT) ? -1 : static_cast<signed>((cc - intreq.eventTime(BLIT)) >> isDoubleSpeed());
2520 void halt() { intreq.halt(); }
2521 - void ei(unsigned long cycleCounter) { if (!ime()) { intreq.ei(cycleCounter); } }
2522 + void ei(unsigned cycleCounter) { if (!ime()) { intreq.ei(cycleCounter); } }
2524 void di() { intreq.di(); }
2526 - unsigned ff_read(const unsigned P, const unsigned long cycleCounter) {
2527 + unsigned ff_read(const unsigned P, const unsigned cycleCounter) {
2528 return P < 0xFF80 ? nontrivial_ff_read(P, cycleCounter) : ioamhram[P - 0xFE00];
2531 - unsigned read(const unsigned P, const unsigned long cycleCounter) {
2532 + unsigned read(const unsigned P, const unsigned cycleCounter) {
2533 return cart.rmem(P >> 12) ? cart.rmem(P >> 12)[P] : nontrivial_read(P, cycleCounter);
2536 - void write(const unsigned P, const unsigned data, const unsigned long cycleCounter) {
2537 + void write(const unsigned P, const unsigned data, const unsigned cycleCounter) {
2538 if (cart.wmem(P >> 12)) {
2539 cart.wmem(P >> 12)[P] = data;
2540 } else
2541 nontrivial_write(P, data, cycleCounter);
2544 - void ff_write(const unsigned P, const unsigned data, const unsigned long cycleCounter) {
2545 + void ff_write(const unsigned P, const unsigned data, const unsigned cycleCounter) {
2546 if (P - 0xFF80u < 0x7Fu) {
2547 ioamhram[P - 0xFE00] = data;
2548 } else
2549 nontrivial_ff_write(P, data, cycleCounter);
2552 - unsigned long event(unsigned long cycleCounter);
2553 - unsigned long resetCounters(unsigned long cycleCounter);
2554 + unsigned event(unsigned cycleCounter);
2555 + unsigned resetCounters(unsigned cycleCounter);
2557 LoadRes loadROM(const std::string &romfile, bool forceDmg, bool multicartCompat);
2558 + LoadRes loadROM(const unsigned char* image, size_t isize, bool forceDmg, bool multicartCompat);
2559 void setSaveDir(const std::string &dir) { cart.setSaveDir(dir); }
2561 void setInputGetter(InputGetter *getInput) {
2562 this->getInput = getInput;
2565 - void setEndtime(unsigned long cc, unsigned long inc);
2566 + void setEndtime(unsigned cc, unsigned inc);
2568 void setSoundBuffer(uint_least32_t *const buf) { sound.setBuffer(buf); }
2569 - unsigned fillSoundBuffer(unsigned long cc);
2570 + unsigned fillSoundBuffer(unsigned cc);
2572 void setVideoBuffer(uint_least32_t *const videoBuf, const int pitch) {
2573 - display.setVideoBuffer(videoBuf, pitch);
2574 + videoBuf_ = videoBuf;
2575 + pitch_ = pitch;
2578 - void setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned long rgb32);
2579 + void setDmgPaletteColor(unsigned palNum, unsigned colorNum, uint_least32_t rgb32);
2580 void setGameGenie(const std::string &codes) { cart.setGameGenie(codes); }
2581 void setGameShark(const std::string &codes) { interrupter.setGameShark(codes); }
2583 + void setRtcBase(time_t time) { cart.setRtcBase(time); }
2584 + time_t getRtcBase() { return cart.getRtcBase(); }
2585 + std::pair<unsigned char*, size_t> getWorkRam() { return cart.getWorkRam(); }
2586 + std::pair<unsigned char*, size_t> getSaveRam() { return cart.getSaveRam(); }
2587 + std::pair<unsigned char*, size_t> getIoRam() { return std::make_pair(ioamhram, sizeof(ioamhram)); }
2588 + std::pair<unsigned char*, size_t> getVideoRam() { return cart.getVideoRam(); };
2593 diff --git a/libgambatte/src/minkeeper.h b/libgambatte/src/minkeeper.h
2594 index bf2de9c..794f558 100644
2595 --- a/libgambatte/src/minkeeper.h
2596 +++ b/libgambatte/src/minkeeper.h
2597 @@ -19,7 +19,12 @@
2598 #ifndef MINKEEPER_H
2599 #define MINKEEPER_H
2602 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
2603 +// - Make it rerecording-friendly.
2605 #include <algorithm>
2606 +#include "loadsave.h"
2608 namespace MinKeeperUtil {
2609 template<int n> struct CeiledLog2 { enum { R = 1 + CeiledLog2<(n + 1) / 2>::R }; };
2610 @@ -81,36 +86,43 @@ class MinKeeper {
2613 static UpdateValueLut updateValueLut;
2614 - unsigned long values[ids];
2615 - unsigned long minValue_;
2616 + unsigned values[ids];
2617 + unsigned minValue_;
2618 int a[Sum<LEVELS>::R];
2620 template<int id> static void updateValue(MinKeeper<ids> &m);
2622 public:
2623 - explicit MinKeeper(unsigned long initValue = 0xFFFFFFFF);
2625 + explicit MinKeeper(unsigned initValue = 0xFFFFFFFFULL);
2627 + void loadOrSave(gambatte::loadsave& state) {
2628 + state(values, ids);
2629 + state(minValue_);
2630 + //updateValueLut is constant for our purposes.
2631 + state(a, Sum<LEVELS>::R);
2634 int min() const { return a[0]; }
2635 - unsigned long minValue() const { return minValue_; }
2636 + unsigned minValue() const { return minValue_; }
2638 template<int id>
2639 - void setValue(const unsigned long cnt) {
2640 + void setValue(const unsigned cnt) {
2641 values[id] = cnt;
2642 updateValue<id / 2>(*this);
2645 - void setValue(const int id, const unsigned long cnt) {
2646 + void setValue(const int id, const unsigned cnt) {
2647 values[id] = cnt;
2648 updateValueLut.call(id >> 1, *this);
2651 - unsigned long value(const int id) const { return values[id]; }
2652 + unsigned value(const int id) const { return values[id]; }
2655 template<int ids> typename MinKeeper<ids>::UpdateValueLut MinKeeper<ids>::updateValueLut;
2657 template<int ids>
2658 -MinKeeper<ids>::MinKeeper(const unsigned long initValue) {
2659 +MinKeeper<ids>::MinKeeper(const unsigned initValue) {
2660 std::fill(values, values + ids, initValue);
2662 for (int i = 0; i < Num<LEVELS-1>::R; ++i) {
2663 diff --git a/libgambatte/src/savestate.h b/libgambatte/src/savestate.h
2664 index 6f91124..9477c75 100644
2665 --- a/libgambatte/src/savestate.h
2666 +++ b/libgambatte/src/savestate.h
2667 @@ -19,6 +19,12 @@
2668 #ifndef SAVESTATE_H
2669 #define SAVESTATE_H
2672 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
2673 +// - Make it rerecording-friendly.
2675 +#include <ctime>
2677 namespace gambatte {
2679 class SaverList;
2680 @@ -27,20 +33,20 @@ struct SaveState {
2681 template<typename T>
2682 class Ptr {
2683 T *ptr;
2684 - unsigned long sz;
2685 + unsigned sz;
2687 public:
2688 Ptr() : ptr(0), sz(0) {}
2689 const T* get() const { return ptr; }
2690 - unsigned long getSz() const { return sz; }
2691 - void set(T *ptr, const unsigned long sz) { this->ptr = ptr; this->sz = sz; }
2692 + unsigned getSz() const { return sz; }
2693 + void set(T *ptr, const unsigned sz) { this->ptr = ptr; this->sz = sz; }
2695 friend class SaverList;
2696 - friend void setInitState(SaveState &, bool, bool);
2697 + friend void setInitState(SaveState &, bool, bool, time_t);
2700 struct CPU {
2701 - unsigned long cycleCounter;
2702 + unsigned cycleCounter;
2703 unsigned short PC;
2704 unsigned short SP;
2705 unsigned char A;
2706 @@ -59,13 +65,13 @@ struct SaveState {
2707 Ptr<unsigned char> sram;
2708 Ptr<unsigned char> wram;
2709 Ptr<unsigned char> ioamhram;
2710 - unsigned long divLastUpdate;
2711 - unsigned long timaLastUpdate;
2712 - unsigned long tmatime;
2713 - unsigned long nextSerialtime;
2714 - unsigned long lastOamDmaUpdate;
2715 - unsigned long minIntTime;
2716 - unsigned long unhaltTime;
2717 + unsigned divLastUpdate;
2718 + unsigned timaLastUpdate;
2719 + unsigned tmatime;
2720 + unsigned nextSerialtime;
2721 + unsigned lastOamDmaUpdate;
2722 + unsigned minIntTime;
2723 + unsigned unhaltTime;
2724 unsigned short rombank;
2725 unsigned short dmaSource;
2726 unsigned short dmaDestination;
2727 @@ -85,8 +91,8 @@ struct SaveState {
2728 Ptr<unsigned char> oamReaderBuf;
2729 Ptr<bool> oamReaderSzbuf;
2731 - unsigned long videoCycles;
2732 - unsigned long enableDisplayM0Time;
2733 + unsigned videoCycles;
2734 + unsigned enableDisplayM0Time;
2735 unsigned short lastM0Time;
2736 unsigned short nextM0Irq;
2737 unsigned short tileword;
2738 @@ -115,24 +121,24 @@ struct SaveState {
2740 struct SPU {
2741 struct Duty {
2742 - unsigned long nextPosUpdate;
2743 + unsigned nextPosUpdate;
2744 unsigned char nr3;
2745 unsigned char pos;
2748 struct Env {
2749 - unsigned long counter;
2750 + unsigned counter;
2751 unsigned char volume;
2754 struct LCounter {
2755 - unsigned long counter;
2756 + unsigned counter;
2757 unsigned short lengthCounter;
2760 struct {
2761 struct {
2762 - unsigned long counter;
2763 + unsigned counter;
2764 unsigned short shadow;
2765 unsigned char nr0;
2766 bool negging;
2767 @@ -155,8 +161,8 @@ struct SaveState {
2768 struct {
2769 Ptr<unsigned char> waveRam;
2770 LCounter lcounter;
2771 - unsigned long waveCounter;
2772 - unsigned long lastReadTime;
2773 + unsigned waveCounter;
2774 + unsigned lastReadTime;
2775 unsigned char nr3;
2776 unsigned char nr4;
2777 unsigned char wavePos;
2778 @@ -166,7 +172,7 @@ struct SaveState {
2780 struct {
2781 struct {
2782 - unsigned long counter;
2783 + unsigned counter;
2784 unsigned short reg;
2785 } lfsr;
2786 Env env;
2787 @@ -175,12 +181,12 @@ struct SaveState {
2788 bool master;
2789 } ch4;
2791 - unsigned long cycleCounter;
2792 + unsigned cycleCounter;
2793 } spu;
2795 struct RTC {
2796 - unsigned long baseTime;
2797 - unsigned long haltTime;
2798 + unsigned baseTime;
2799 + unsigned haltTime;
2800 unsigned char dataDh;
2801 unsigned char dataDl;
2802 unsigned char dataH;
2803 diff --git a/libgambatte/src/sound.cpp b/libgambatte/src/sound.cpp
2804 index 8c1e7ae..366a2c4 100644
2805 --- a/libgambatte/src/sound.cpp
2806 +++ b/libgambatte/src/sound.cpp
2807 @@ -21,6 +21,10 @@
2808 #include <cstring>
2809 #include <algorithm>
2812 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
2813 +// - Make it rerecording-friendly.
2816 Frame Sequencer
2818 @@ -89,7 +93,7 @@ void PSG::loadState(const SaveState &state) {
2819 enabled = state.mem.ioamhram.get()[0x126] >> 7 & 1;
2822 -void PSG::accumulate_channels(const unsigned long cycles) {
2823 +void PSG::accumulate_channels(const unsigned cycles) {
2824 uint_least32_t *const buf = buffer + bufferPos;
2826 std::memset(buf, 0, cycles * sizeof(uint_least32_t));
2827 @@ -99,17 +103,16 @@ void PSG::accumulate_channels(const unsigned long cycles) {
2828 ch4.update(buf, soVol, cycles);
2831 -void PSG::generate_samples(const unsigned long cycleCounter, const unsigned doubleSpeed) {
2832 - const unsigned long cycles = (cycleCounter - lastUpdate) >> (1 + doubleSpeed);
2833 +void PSG::generate_samples(const unsigned cycleCounter, const unsigned doubleSpeed) {
2834 + const unsigned cycles = (cycleCounter - lastUpdate) >> (1 + doubleSpeed);
2835 lastUpdate += cycles << (1 + doubleSpeed);
2837 if (cycles)
2838 accumulate_channels(cycles);
2840 bufferPos += cycles;
2843 -void PSG::resetCounter(const unsigned long newCc, const unsigned long oldCc, const unsigned doubleSpeed) {
2844 +void PSG::resetCounter(const unsigned newCc, const unsigned oldCc, const unsigned doubleSpeed) {
2845 generate_samples(oldCc, doubleSpeed);
2846 lastUpdate = newCc - (oldCc - lastUpdate);
2848 @@ -155,11 +158,11 @@ unsigned PSG::fillBuffer() {
2851 #ifdef WORDS_BIGENDIAN
2852 -static const unsigned long so1Mul = 0x00000001;
2853 -static const unsigned long so2Mul = 0x00010000;
2854 +static const unsigned so1Mul = 0x00000001;
2855 +static const unsigned so2Mul = 0x00010000;
2856 #else
2857 -static const unsigned long so1Mul = 0x00010000;
2858 -static const unsigned long so2Mul = 0x00000001;
2859 +static const unsigned so1Mul = 0x00010000;
2860 +static const unsigned so2Mul = 0x00000001;
2861 #endif
2863 void PSG::set_so_volume(const unsigned nr50) {
2864 @@ -167,7 +170,7 @@ void PSG::set_so_volume(const unsigned nr50) {
2867 void PSG::map_so(const unsigned nr51) {
2868 - const unsigned long tmp = nr51 * so1Mul + (nr51 >> 4) * so2Mul;
2869 + const unsigned tmp = nr51 * so1Mul + (nr51 >> 4) * so2Mul;
2871 ch1.setSo((tmp & 0x00010001) * 0xFFFF);
2872 ch2.setSo((tmp >> 1 & 0x00010001) * 0xFFFF);
2873 @@ -179,4 +182,17 @@ unsigned PSG::getStatus() const {
2874 return ch1.isActive() | ch2.isActive() << 1 | ch3.isActive() << 2 | ch4.isActive() << 3;
2877 +void PSG::loadOrSave(loadsave& state)
2879 + ch1.loadOrSave(state);
2880 + ch2.loadOrSave(state);
2881 + ch3.loadOrSave(state);
2882 + ch4.loadOrSave(state);
2883 + state(lastUpdate);
2884 + state(soVol);
2885 + state(rsum);
2886 + state(bufferPos);
2887 + state(enabled);
2891 diff --git a/libgambatte/src/sound.h b/libgambatte/src/sound.h
2892 index f929cd5..62df442 100644
2893 --- a/libgambatte/src/sound.h
2894 +++ b/libgambatte/src/sound.h
2895 @@ -19,10 +19,15 @@
2896 #ifndef SOUND_H
2897 #define SOUND_H
2900 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
2901 +// - Make it rerecording-friendly.
2903 #include "sound/channel1.h"
2904 #include "sound/channel2.h"
2905 #include "sound/channel3.h"
2906 #include "sound/channel4.h"
2907 +#include "loadsave.h"
2909 namespace gambatte {
2911 @@ -34,8 +39,8 @@ class PSG {
2913 uint_least32_t *buffer;
2915 - unsigned long lastUpdate;
2916 - unsigned long soVol;
2917 + unsigned lastUpdate;
2918 + unsigned soVol;
2920 uint_least32_t rsum;
2922 @@ -43,7 +48,7 @@ class PSG {
2924 bool enabled;
2926 - void accumulate_channels(unsigned long cycles);
2927 + void accumulate_channels(unsigned cycles);
2929 public:
2930 PSG();
2931 @@ -53,8 +58,10 @@ public:
2932 void saveState(SaveState &state);
2933 void loadState(const SaveState &state);
2935 - void generate_samples(unsigned long cycleCounter, unsigned doubleSpeed);
2936 - void resetCounter(unsigned long newCc, unsigned long oldCc, unsigned doubleSpeed);
2937 + void loadOrSave(loadsave& state);
2939 + void generate_samples(unsigned cycleCounter, unsigned doubleSpeed);
2940 + void resetCounter(unsigned newCc, unsigned oldCc, unsigned doubleSpeed);
2941 unsigned fillBuffer();
2942 void setBuffer(uint_least32_t *const buf) { buffer = buf; bufferPos = 0; }
2944 diff --git a/libgambatte/src/sound/channel1.cpp b/libgambatte/src/sound/channel1.cpp
2945 index eebc4d4..6b9ccf1 100644
2946 --- a/libgambatte/src/sound/channel1.cpp
2947 +++ b/libgambatte/src/sound/channel1.cpp
2948 @@ -20,6 +20,9 @@
2949 #include "../savestate.h"
2950 #include <algorithm>
2953 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
2954 +// - Make it rerecording-friendly.
2956 namespace gambatte {
2958 @@ -47,7 +50,7 @@ unsigned Channel1::SweepUnit::calcFreq() {
2961 void Channel1::SweepUnit::event() {
2962 - const unsigned long period = nr0 >> 4 & 0x07;
2963 + const unsigned period = nr0 >> 4 & 0x07;
2965 if (period) {
2966 const unsigned freq = calcFreq();
2967 @@ -70,7 +73,7 @@ void Channel1::SweepUnit::nr0Change(const unsigned newNr0) {
2968 nr0 = newNr0;
2971 -void Channel1::SweepUnit::nr4Init(const unsigned long cc) {
2972 +void Channel1::SweepUnit::nr4Init(const unsigned cc) {
2973 negging = false;
2974 shadow = dutyUnit.getFreq();
2976 @@ -172,7 +175,7 @@ void Channel1::setNr4(const unsigned data) {
2977 setEvent();
2980 -void Channel1::setSo(const unsigned long soMask) {
2981 +void Channel1::setSo(const unsigned soMask) {
2982 this->soMask = soMask;
2983 staticOutputTest(cycleCounter);
2984 setEvent();
2985 @@ -215,15 +218,15 @@ void Channel1::loadState(const SaveState &state) {
2986 master = state.spu.ch1.master;
2989 -void Channel1::update(uint_least32_t *buf, const unsigned long soBaseVol, unsigned long cycles) {
2990 - const unsigned long outBase = envelopeUnit.dacIsOn() ? soBaseVol & soMask : 0;
2991 - const unsigned long outLow = outBase * (0 - 15ul);
2992 - const unsigned long endCycles = cycleCounter + cycles;
2993 +void Channel1::update(uint_least32_t *buf, const unsigned soBaseVol, unsigned cycles) {
2994 + const unsigned outBase = envelopeUnit.dacIsOn() ? soBaseVol & soMask : 0;
2995 + const unsigned outLow = outBase * (0 - 15ul);
2996 + const unsigned endCycles = cycleCounter + cycles;
2998 for (;;) {
2999 - const unsigned long outHigh = master ? outBase * (envelopeUnit.getVolume() * 2 - 15ul) : outLow;
3000 - const unsigned long nextMajorEvent = nextEventUnit->getCounter() < endCycles ? nextEventUnit->getCounter() : endCycles;
3001 - unsigned long out = dutyUnit.isHighState() ? outHigh : outLow;
3002 + const unsigned outHigh = master ? outBase * (envelopeUnit.getVolume() * 2 - 15ul) : outLow;
3003 + const unsigned nextMajorEvent = nextEventUnit->getCounter() < endCycles ? nextEventUnit->getCounter() : endCycles;
3004 + unsigned out = dutyUnit.isHighState() ? outHigh : outLow;
3006 while (dutyUnit.getCounter() <= nextMajorEvent) {
3007 *buf = out - prevOut;
3008 @@ -259,4 +262,25 @@ void Channel1::update(uint_least32_t *buf, const unsigned long soBaseVol, unsign
3012 +void Channel1::loadOrSave(loadsave& state) {
3013 + //disableMaster has no state.
3014 + lengthCounter.loadOrSave(state);
3015 + dutyUnit.loadOrSave(state);
3016 + envelopeUnit.loadOrSave(state);
3017 + sweepUnit.loadOrSave(state);
3019 + state.startEnumeration();
3020 + state.enumerate<SoundUnit*>(nextEventUnit, NULL, 0);
3021 + state.enumerate<SoundUnit*>(nextEventUnit, &sweepUnit, 1);
3022 + state.enumerate<SoundUnit*>(nextEventUnit, &envelopeUnit, 2);
3023 + state.enumerate<SoundUnit*>(nextEventUnit, &lengthCounter, 3);
3024 + state.endEnumeration();
3026 + state(cycleCounter);
3027 + state(soMask);
3028 + state(prevOut);
3029 + state(nr4);
3030 + state(master);
3034 diff --git a/libgambatte/src/sound/channel1.h b/libgambatte/src/sound/channel1.h
3035 index f61e002..03875f5 100644
3036 --- a/libgambatte/src/sound/channel1.h
3037 +++ b/libgambatte/src/sound/channel1.h
3038 @@ -19,12 +19,17 @@
3039 #ifndef SOUND_CHANNEL1_H
3040 #define SOUND_CHANNEL1_H
3043 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
3044 +// - Make it rerecording-friendly.
3046 #include "gbint.h"
3047 #include "master_disabler.h"
3048 #include "length_counter.h"
3049 #include "duty_unit.h"
3050 #include "envelope_unit.h"
3051 #include "static_output_tester.h"
3052 +#include "loadsave.h"
3054 namespace gambatte {
3056 @@ -44,10 +49,16 @@ class Channel1 {
3057 SweepUnit(MasterDisabler &disabler, DutyUnit &dutyUnit);
3058 void event();
3059 void nr0Change(unsigned newNr0);
3060 - void nr4Init(unsigned long cycleCounter);
3061 + void nr4Init(unsigned cycleCounter);
3062 void reset();
3063 void saveState(SaveState &state) const;
3064 void loadState(const SaveState &state);
3065 + void loadOrSave(loadsave& state) {
3066 + loadOrSave2(state);
3067 + state(shadow);
3068 + state(nr0);
3069 + state(negging);
3073 friend class StaticOutputTester<Channel1,DutyUnit>;
3074 @@ -61,9 +72,9 @@ class Channel1 {
3076 SoundUnit *nextEventUnit;
3078 - unsigned long cycleCounter;
3079 - unsigned long soMask;
3080 - unsigned long prevOut;
3081 + unsigned cycleCounter;
3082 + unsigned soMask;
3083 + unsigned prevOut;
3085 unsigned char nr4;
3086 bool master;
3087 @@ -78,15 +89,17 @@ public:
3088 void setNr3(unsigned data);
3089 void setNr4(unsigned data);
3091 - void setSo(unsigned long soMask);
3092 + void setSo(unsigned soMask);
3093 bool isActive() const { return master; }
3095 - void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles);
3096 + void update(uint_least32_t *buf, unsigned soBaseVol, unsigned cycles);
3098 void reset();
3099 void init(bool cgb);
3100 void saveState(SaveState &state);
3101 void loadState(const SaveState &state);
3103 + void loadOrSave(loadsave& state);
3107 diff --git a/libgambatte/src/sound/channel2.cpp b/libgambatte/src/sound/channel2.cpp
3108 index a46a11c..6bd5b04 100644
3109 --- a/libgambatte/src/sound/channel2.cpp
3110 +++ b/libgambatte/src/sound/channel2.cpp
3111 @@ -19,6 +19,10 @@
3112 #include "channel2.h"
3113 #include "../savestate.h"
3116 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
3117 +// - Make it rerecording-friendly.
3119 namespace gambatte {
3121 Channel2::Channel2() :
3122 @@ -80,7 +84,7 @@ void Channel2::setNr4(const unsigned data) {
3123 setEvent();
3126 -void Channel2::setSo(const unsigned long soMask) {
3127 +void Channel2::setSo(const unsigned soMask) {
3128 this->soMask = soMask;
3129 staticOutputTest(cycleCounter);
3130 setEvent();
3131 @@ -119,16 +123,15 @@ void Channel2::loadState(const SaveState &state) {
3132 master = state.spu.ch2.master;
3135 -void Channel2::update(uint_least32_t *buf, const unsigned long soBaseVol, unsigned long cycles) {
3136 - const unsigned long outBase = envelopeUnit.dacIsOn() ? soBaseVol & soMask : 0;
3137 - const unsigned long outLow = outBase * (0 - 15ul);
3138 - const unsigned long endCycles = cycleCounter + cycles;
3139 +void Channel2::update(uint_least32_t *buf, const unsigned soBaseVol, unsigned cycles) {
3140 + const unsigned outBase = envelopeUnit.dacIsOn() ? soBaseVol & soMask : 0;
3141 + const unsigned outLow = outBase * (0 - 15ul);
3142 + const unsigned endCycles = cycleCounter + cycles;
3144 for (;;) {
3145 - const unsigned long outHigh = master ? outBase * (envelopeUnit.getVolume() * 2 - 15ul) : outLow;
3146 - const unsigned long nextMajorEvent = nextEventUnit->getCounter() < endCycles ? nextEventUnit->getCounter() : endCycles;
3147 - unsigned long out = dutyUnit.isHighState() ? outHigh : outLow;
3149 + const unsigned outHigh = master ? outBase * (envelopeUnit.getVolume() * 2 - 15ul) : outLow;
3150 + const unsigned nextMajorEvent = nextEventUnit->getCounter() < endCycles ? nextEventUnit->getCounter() : endCycles;
3151 + unsigned out = dutyUnit.isHighState() ? outHigh : outLow;
3152 while (dutyUnit.getCounter() <= nextMajorEvent) {
3153 *buf += out - prevOut;
3154 prevOut = out;
3155 @@ -162,4 +165,24 @@ void Channel2::update(uint_least32_t *buf, const unsigned long soBaseVol, unsign
3159 +void Channel2::loadOrSave(loadsave& state)
3161 + //disableMaster has no state.
3162 + lengthCounter.loadOrSave(state);
3163 + dutyUnit.loadOrSave(state);
3164 + envelopeUnit.loadOrSave(state);
3166 + state.startEnumeration();
3167 + state.enumerate<SoundUnit*>(nextEventUnit, NULL, 0);
3168 + state.enumerate<SoundUnit*>(nextEventUnit, &lengthCounter, 1);
3169 + state.enumerate<SoundUnit*>(nextEventUnit, &envelopeUnit, 2);
3170 + state.endEnumeration();
3172 + state(cycleCounter);
3173 + state(soMask);
3174 + state(prevOut);
3175 + state(nr4);
3176 + state(master);
3180 diff --git a/libgambatte/src/sound/channel2.h b/libgambatte/src/sound/channel2.h
3181 index 2be39b7..71148b0 100644
3182 --- a/libgambatte/src/sound/channel2.h
3183 +++ b/libgambatte/src/sound/channel2.h
3184 @@ -24,6 +24,11 @@
3185 #include "duty_unit.h"
3186 #include "envelope_unit.h"
3187 #include "static_output_tester.h"
3188 +#include "loadsave.h"
3191 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
3192 +// - Make it rerecording-friendly.
3194 namespace gambatte {
3196 @@ -40,9 +45,9 @@ class Channel2 {
3198 SoundUnit *nextEventUnit;
3200 - unsigned long cycleCounter;
3201 - unsigned long soMask;
3202 - unsigned long prevOut;
3203 + unsigned cycleCounter;
3204 + unsigned soMask;
3205 + unsigned prevOut;
3207 unsigned char nr4;
3208 bool master;
3209 @@ -56,16 +61,18 @@ public:
3210 void setNr3(unsigned data);
3211 void setNr4(unsigned data);
3213 - void setSo(unsigned long soMask);
3214 + void setSo(unsigned soMask);
3215 // void deactivate() { disableMaster(); setEvent(); }
3216 bool isActive() const { return master; }
3218 - void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles);
3219 + void update(uint_least32_t *buf, unsigned soBaseVol, unsigned cycles);
3221 void reset();
3222 void init(bool cgb);
3223 void saveState(SaveState &state);
3224 void loadState(const SaveState &state);
3226 + void loadOrSave(loadsave& state);
3230 diff --git a/libgambatte/src/sound/channel3.cpp b/libgambatte/src/sound/channel3.cpp
3231 index 6eb41da..ec2c8ed 100644
3232 --- a/libgambatte/src/sound/channel3.cpp
3233 +++ b/libgambatte/src/sound/channel3.cpp
3234 @@ -21,6 +21,10 @@
3235 #include <cstring>
3236 #include <algorithm>
3239 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
3240 +// - Make it rerecording-friendly.
3242 static inline unsigned toPeriod(const unsigned nr3, const unsigned nr4) {
3243 return 0x800 - ((nr4 << 8 & 0x700) | nr3);
3245 @@ -80,7 +84,7 @@ void Channel3::setNr4(const unsigned data) {
3249 -void Channel3::setSo(const unsigned long soMask) {
3250 +void Channel3::setSo(const unsigned soMask) {
3251 this->soMask = soMask;
3254 @@ -128,10 +132,10 @@ void Channel3::loadState(const SaveState &state) {
3255 setNr2(state.mem.ioamhram.get()[0x11C]);
3258 -void Channel3::updateWaveCounter(const unsigned long cc) {
3259 +void Channel3::updateWaveCounter(const unsigned cc) {
3260 if (cc >= waveCounter) {
3261 const unsigned period = toPeriod(nr3, nr4);
3262 - const unsigned long periods = (cc - waveCounter) / period;
3263 + const unsigned periods = (cc - waveCounter) / period;
3265 lastReadTime = waveCounter + periods * period;
3266 waveCounter = lastReadTime + period;
3267 @@ -143,15 +147,15 @@ void Channel3::updateWaveCounter(const unsigned long cc) {
3271 -void Channel3::update(uint_least32_t *buf, const unsigned long soBaseVol, unsigned long cycles) {
3272 - const unsigned long outBase = (nr0/* & 0x80*/) ? soBaseVol & soMask : 0;
3273 +void Channel3::update(uint_least32_t *buf, const unsigned soBaseVol, unsigned cycles) {
3274 + const unsigned outBase = (nr0/* & 0x80*/) ? soBaseVol & soMask : 0;
3276 if (outBase && rShift != 4) {
3277 - const unsigned long endCycles = cycleCounter + cycles;
3278 + const unsigned endCycles = cycleCounter + cycles;
3280 for (;;) {
3281 - const unsigned long nextMajorEvent = lengthCounter.getCounter() < endCycles ? lengthCounter.getCounter() : endCycles;
3282 - unsigned long out = outBase * (master ? ((sampleBuf >> (~wavePos << 2 & 4) & 0xF) >> rShift) * 2 - 15ul : 0 - 15ul);
3283 + const unsigned nextMajorEvent = lengthCounter.getCounter() < endCycles ? lengthCounter.getCounter() : endCycles;
3284 + unsigned out = outBase * (master ? ((sampleBuf >> (~wavePos << 2 & 4) & 0xF) >> rShift) * 2 - 15ul : 0 - 15ul);
3286 while (waveCounter <= nextMajorEvent) {
3287 *buf += out - prevOut;
3288 @@ -180,7 +184,7 @@ void Channel3::update(uint_least32_t *buf, const unsigned long soBaseVol, unsign
3289 break;
3291 } else {
3292 - unsigned long const out = outBase * (0 - 15ul);
3293 + unsigned const out = outBase * (0 - 15ul);
3294 *buf += out - prevOut;
3295 prevOut = out;
3296 cycleCounter += cycles;
3297 @@ -204,4 +208,23 @@ void Channel3::update(uint_least32_t *buf, const unsigned long soBaseVol, unsign
3301 +void Channel3::loadOrSave(loadsave& state) {
3302 + state(waveRam, 0x10);
3303 + //disableMaster has no saveable state.
3304 + lengthCounter.loadOrSave(state);
3305 + state(cycleCounter);
3306 + state(soMask);
3307 + state(prevOut);
3308 + state(waveCounter);
3309 + state(lastReadTime);
3310 + state(nr0);
3311 + state(nr3);
3312 + state(nr4);
3313 + state(wavePos);
3314 + state(rShift);
3315 + state(sampleBuf);
3316 + state(master);
3317 + state(cgb);
3321 diff --git a/libgambatte/src/sound/channel3.h b/libgambatte/src/sound/channel3.h
3322 index 287161d..9632013 100644
3323 --- a/libgambatte/src/sound/channel3.h
3324 +++ b/libgambatte/src/sound/channel3.h
3325 @@ -19,9 +19,14 @@
3326 #ifndef SOUND_CHANNEL3_H
3327 #define SOUND_CHANNEL3_H
3330 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
3331 +// - Make it rerecording-friendly.
3333 #include "gbint.h"
3334 #include "master_disabler.h"
3335 #include "length_counter.h"
3336 +#include "loadsave.h"
3338 namespace gambatte {
3340 @@ -29,10 +34,10 @@ struct SaveState;
3342 class Channel3 {
3343 class Ch3MasterDisabler : public MasterDisabler {
3344 - unsigned long &waveCounter;
3345 + unsigned &waveCounter;
3347 public:
3348 - Ch3MasterDisabler(bool &m, unsigned long &wC) : MasterDisabler(m), waveCounter(wC) {}
3349 + Ch3MasterDisabler(bool &m, unsigned &wC) : MasterDisabler(m), waveCounter(wC) {}
3350 void operator()() { MasterDisabler::operator()(); waveCounter = SoundUnit::COUNTER_DISABLED; }
3353 @@ -41,11 +46,11 @@ class Channel3 {
3354 Ch3MasterDisabler disableMaster;
3355 LengthCounter lengthCounter;
3357 - unsigned long cycleCounter;
3358 - unsigned long soMask;
3359 - unsigned long prevOut;
3360 - unsigned long waveCounter;
3361 - unsigned long lastReadTime;
3362 + unsigned cycleCounter;
3363 + unsigned soMask;
3364 + unsigned prevOut;
3365 + unsigned waveCounter;
3366 + unsigned lastReadTime;
3368 unsigned char nr0;
3369 unsigned char nr3;
3370 @@ -57,7 +62,7 @@ class Channel3 {
3371 bool master;
3372 bool cgb;
3374 - void updateWaveCounter(unsigned long cc);
3375 + void updateWaveCounter(unsigned cc);
3377 public:
3378 Channel3();
3379 @@ -72,8 +77,8 @@ public:
3380 void setNr2(unsigned data);
3381 void setNr3(unsigned data) { nr3 = data; }
3382 void setNr4(unsigned data);
3383 - void setSo(unsigned long soMask);
3384 - void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles);
3385 + void setSo(unsigned soMask);
3386 + void update(uint_least32_t *buf, unsigned soBaseVol, unsigned cycles);
3388 unsigned waveRamRead(unsigned index) const {
3389 if (master) {
3390 @@ -96,6 +101,8 @@ public:
3392 waveRam[index] = data;
3395 + void loadOrSave(loadsave& state);
3399 diff --git a/libgambatte/src/sound/channel4.cpp b/libgambatte/src/sound/channel4.cpp
3400 index 4e10974..b646dff 100644
3401 --- a/libgambatte/src/sound/channel4.cpp
3402 +++ b/libgambatte/src/sound/channel4.cpp
3403 @@ -20,7 +20,11 @@
3404 #include "../savestate.h"
3405 #include <algorithm>
3407 -static unsigned long toPeriod(const unsigned nr3) {
3409 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
3410 +// - Make it rerecording-friendly.
3412 +static unsigned toPeriod(const unsigned nr3) {
3413 unsigned s = (nr3 >> 4) + 3;
3414 unsigned r = nr3 & 7;
3416 @@ -41,15 +45,15 @@ nr3(0),
3417 master(false)
3420 -void Channel4::Lfsr::updateBackupCounter(const unsigned long cc) {
3421 +void Channel4::Lfsr::updateBackupCounter(const unsigned cc) {
3422 /*if (backupCounter <= cc) {
3423 const unsigned long period = toPeriod(nr3);
3424 backupCounter = cc - (cc - backupCounter) % period + period;
3427 if (backupCounter <= cc) {
3428 - const unsigned long period = toPeriod(nr3);
3429 - unsigned long periods = (cc - backupCounter) / period + 1;
3430 + const unsigned period = toPeriod(nr3);
3431 + unsigned periods = (cc - backupCounter) / period + 1;
3433 backupCounter += periods * period;
3435 @@ -75,7 +79,7 @@ void Channel4::Lfsr::updateBackupCounter(const unsigned long cc) {
3439 -void Channel4::Lfsr::reviveCounter(const unsigned long cc) {
3440 +void Channel4::Lfsr::reviveCounter(const unsigned cc) {
3441 updateBackupCounter(cc);
3442 counter = backupCounter;
3444 @@ -121,7 +125,7 @@ inline void Channel4::Lfsr::event() {
3445 counter += period * nextStateDistance[reg & 0x3F];*/
3448 -void Channel4::Lfsr::nr3Change(const unsigned newNr3, const unsigned long cc) {
3449 +void Channel4::Lfsr::nr3Change(const unsigned newNr3, const unsigned cc) {
3450 updateBackupCounter(cc);
3451 nr3 = newNr3;
3453 @@ -129,7 +133,7 @@ void Channel4::Lfsr::nr3Change(const unsigned newNr3, const unsigned long cc) {
3454 // counter = backupCounter + toPeriod(nr3) * (nextStateDistance[reg & 0x3F] - 1);
3457 -void Channel4::Lfsr::nr4Init(unsigned long cc) {
3458 +void Channel4::Lfsr::nr4Init(unsigned cc) {
3459 disableMaster();
3460 updateBackupCounter(cc);
3461 master = true;
3462 @@ -138,19 +142,19 @@ void Channel4::Lfsr::nr4Init(unsigned long cc) {
3463 // counter = backupCounter + toPeriod(nr3) * (nextStateDistance[reg & 0x3F] - 1);
3466 -void Channel4::Lfsr::reset(const unsigned long cc) {
3467 +void Channel4::Lfsr::reset(const unsigned cc) {
3468 nr3 = 0;
3469 disableMaster();
3470 backupCounter = cc + toPeriod(nr3);
3473 -void Channel4::Lfsr::resetCounters(const unsigned long oldCc) {
3474 +void Channel4::Lfsr::resetCounters(const unsigned oldCc) {
3475 updateBackupCounter(oldCc);
3476 backupCounter -= COUNTER_MAX;
3477 SoundUnit::resetCounters(oldCc);
3480 -void Channel4::Lfsr::saveState(SaveState &state, const unsigned long cc) {
3481 +void Channel4::Lfsr::saveState(SaveState &state, const unsigned cc) {
3482 updateBackupCounter(cc);
3483 state.spu.ch4.lfsr.counter = backupCounter;
3484 state.spu.ch4.lfsr.reg = reg;
3485 @@ -219,7 +223,7 @@ void Channel4::setNr4(const unsigned data) {
3486 setEvent();
3489 -void Channel4::setSo(const unsigned long soMask) {
3490 +void Channel4::setSo(const unsigned soMask) {
3491 this->soMask = soMask;
3492 staticOutputTest(cycleCounter);
3493 setEvent();
3494 @@ -258,15 +262,15 @@ void Channel4::loadState(const SaveState &state) {
3495 master = state.spu.ch4.master;
3498 -void Channel4::update(uint_least32_t *buf, const unsigned long soBaseVol, unsigned long cycles) {
3499 - const unsigned long outBase = envelopeUnit.dacIsOn() ? soBaseVol & soMask : 0;
3500 - const unsigned long outLow = outBase * (0 - 15ul);
3501 - const unsigned long endCycles = cycleCounter + cycles;
3502 +void Channel4::update(uint_least32_t *buf, const unsigned soBaseVol, unsigned cycles) {
3503 + const unsigned outBase = envelopeUnit.dacIsOn() ? soBaseVol & soMask : 0;
3504 + const unsigned outLow = outBase * (0 - 15ul);
3505 + const unsigned endCycles = cycleCounter + cycles;
3507 for (;;) {
3508 - const unsigned long outHigh = /*master ? */outBase * (envelopeUnit.getVolume() * 2 - 15ul)/* : outLow*/;
3509 - const unsigned long nextMajorEvent = nextEventUnit->getCounter() < endCycles ? nextEventUnit->getCounter() : endCycles;
3510 - unsigned long out = lfsr.isHighState() ? outHigh : outLow;
3511 + const unsigned outHigh = /*master ? */outBase * (envelopeUnit.getVolume() * 2 - 15ul)/* : outLow*/;
3512 + const unsigned nextMajorEvent = nextEventUnit->getCounter() < endCycles ? nextEventUnit->getCounter() : endCycles;
3513 + unsigned out = lfsr.isHighState() ? outHigh : outLow;
3515 while (lfsr.getCounter() <= nextMajorEvent) {
3516 *buf += out - prevOut;
3517 @@ -301,4 +305,23 @@ void Channel4::update(uint_least32_t *buf, const unsigned long soBaseVol, unsign
3521 +void Channel4::loadOrSave(loadsave& state) {
3522 + //DisableMaster has no state.
3523 + lengthCounter.loadOrSave(state);
3524 + envelopeUnit.loadOrSave(state);
3525 + lfsr.loadOrSave(state);
3527 + state.startEnumeration();
3528 + state.enumerate<SoundUnit*>(nextEventUnit, NULL, 0);
3529 + state.enumerate<SoundUnit*>(nextEventUnit, &lengthCounter, 1);
3530 + state.enumerate<SoundUnit*>(nextEventUnit, &envelopeUnit, 2);
3531 + state.endEnumeration();
3533 + state(cycleCounter);
3534 + state(soMask);
3535 + state(prevOut);
3536 + state(nr4);
3537 + state(master);
3541 diff --git a/libgambatte/src/sound/channel4.h b/libgambatte/src/sound/channel4.h
3542 index f1742dc..5d5aac8 100644
3543 --- a/libgambatte/src/sound/channel4.h
3544 +++ b/libgambatte/src/sound/channel4.h
3545 @@ -24,6 +24,11 @@
3546 #include "length_counter.h"
3547 #include "envelope_unit.h"
3548 #include "static_output_tester.h"
3549 +#include "loadsave.h"
3552 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
3553 +// - Make it rerecording-friendly.
3555 namespace gambatte {
3557 @@ -31,26 +36,33 @@ struct SaveState;
3559 class Channel4 {
3560 class Lfsr : public SoundUnit {
3561 - unsigned long backupCounter;
3562 + unsigned backupCounter;
3563 unsigned short reg;
3564 unsigned char nr3;
3565 bool master;
3567 - void updateBackupCounter(unsigned long cc);
3568 + void updateBackupCounter(unsigned cc);
3570 public:
3571 Lfsr();
3572 void event();
3573 bool isHighState() const { return ~reg & 1; }
3574 - void nr3Change(unsigned newNr3, unsigned long cc);
3575 - void nr4Init(unsigned long cc);
3576 - void reset(unsigned long cc);
3577 - void saveState(SaveState &state, const unsigned long cc);
3578 + void nr3Change(unsigned newNr3, unsigned cc);
3579 + void nr4Init(unsigned cc);
3580 + void reset(unsigned cc);
3581 + void saveState(SaveState &state, const unsigned cc);
3582 void loadState(const SaveState &state);
3583 - void resetCounters(unsigned long oldCc);
3584 + void resetCounters(unsigned oldCc);
3585 void disableMaster() { killCounter(); master = false; reg = 0xFF; }
3586 void killCounter() { counter = COUNTER_DISABLED; }
3587 - void reviveCounter(unsigned long cc);
3588 + void reviveCounter(unsigned cc);
3589 + void loadOrSave(loadsave& state) {
3590 + loadOrSave2(state);
3591 + state(backupCounter);
3592 + state(reg);
3593 + state(nr3);
3594 + state(master);
3598 class Ch4MasterDisabler : public MasterDisabler {
3599 @@ -70,9 +82,9 @@ class Channel4 {
3601 SoundUnit *nextEventUnit;
3603 - unsigned long cycleCounter;
3604 - unsigned long soMask;
3605 - unsigned long prevOut;
3606 + unsigned cycleCounter;
3607 + unsigned soMask;
3608 + unsigned prevOut;
3610 unsigned char nr4;
3611 bool master;
3612 @@ -86,15 +98,17 @@ public:
3613 void setNr3(unsigned data) { lfsr.nr3Change(data, cycleCounter); /*setEvent();*/ }
3614 void setNr4(unsigned data);
3616 - void setSo(unsigned long soMask);
3617 + void setSo(unsigned soMask);
3618 bool isActive() const { return master; }
3620 - void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles);
3621 + void update(uint_least32_t *buf, unsigned soBaseVol, unsigned cycles);
3623 void reset();
3624 void init(bool cgb);
3625 void saveState(SaveState &state);
3626 void loadState(const SaveState &state);
3628 + void loadOrSave(loadsave& state);
3632 diff --git a/libgambatte/src/sound/duty_unit.cpp b/libgambatte/src/sound/duty_unit.cpp
3633 index 391f9e2..80156eb 100644
3634 --- a/libgambatte/src/sound/duty_unit.cpp
3635 +++ b/libgambatte/src/sound/duty_unit.cpp
3636 @@ -19,6 +19,10 @@
3637 #include "duty_unit.h"
3638 #include <algorithm>
3641 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
3642 +// - Make it rerecording-friendly.
3644 static inline bool toOutState(const unsigned duty, const unsigned pos) {
3645 static const unsigned char duties[4] = { 0x80, 0x81, 0xE1, 0x7E };
3647 @@ -31,9 +35,9 @@ static inline unsigned toPeriod(const unsigned freq) {
3649 namespace gambatte {
3651 -void DutyUnit::updatePos(const unsigned long cc) {
3652 +void DutyUnit::updatePos(const unsigned cc) {
3653 if (cc >= nextPosUpdate) {
3654 - const unsigned long inc = (cc - nextPosUpdate) / period + 1;
3655 + const unsigned inc = (cc - nextPosUpdate) / period + 1;
3656 nextPosUpdate += period * inc;
3657 pos += inc;
3658 pos &= 7;
3659 @@ -59,7 +63,7 @@ void DutyUnit::setCounter() {
3660 counter = COUNTER_DISABLED;
3663 -void DutyUnit::setFreq(const unsigned newFreq, const unsigned long cc) {
3664 +void DutyUnit::setFreq(const unsigned newFreq, const unsigned cc) {
3665 updatePos(cc);
3666 period = toPeriod(newFreq);
3667 setCounter();
3668 @@ -77,17 +81,17 @@ void DutyUnit::event() {
3669 counter += inc;
3672 -void DutyUnit::nr1Change(const unsigned newNr1, const unsigned long cc) {
3673 +void DutyUnit::nr1Change(const unsigned newNr1, const unsigned cc) {
3674 updatePos(cc);
3675 setDuty(newNr1);
3676 setCounter();
3679 -void DutyUnit::nr3Change(const unsigned newNr3, const unsigned long cc) {
3680 +void DutyUnit::nr3Change(const unsigned newNr3, const unsigned cc) {
3681 setFreq((getFreq() & 0x700) | newNr3, cc);
3684 -void DutyUnit::nr4Change(const unsigned newNr4, const unsigned long cc) {
3685 +void DutyUnit::nr4Change(const unsigned newNr4, const unsigned cc) {
3686 setFreq((newNr4 << 8 & 0x700) | (getFreq() & 0xFF), cc);
3688 if (newNr4 & 0x80) {
3689 @@ -112,14 +116,14 @@ void DutyUnit::reset() {
3690 setCounter();
3693 -void DutyUnit::saveState(SaveState::SPU::Duty &dstate, const unsigned long cc) {
3694 +void DutyUnit::saveState(SaveState::SPU::Duty &dstate, const unsigned cc) {
3695 updatePos(cc);
3696 dstate.nextPosUpdate = nextPosUpdate;
3697 dstate.nr3 = getFreq() & 0xFF;
3698 dstate.pos = pos;
3701 -void DutyUnit::loadState(const SaveState::SPU::Duty &dstate, const unsigned nr1, const unsigned nr4, const unsigned long cc) {
3702 +void DutyUnit::loadState(const SaveState::SPU::Duty &dstate, const unsigned nr1, const unsigned nr4, const unsigned cc) {
3703 nextPosUpdate = std::max(dstate.nextPosUpdate, cc);
3704 pos = dstate.pos & 7;
3705 setDuty(nr1);
3706 @@ -128,7 +132,7 @@ void DutyUnit::loadState(const SaveState::SPU::Duty &dstate, const unsigned nr1,
3707 setCounter();
3710 -void DutyUnit::resetCounters(const unsigned long oldCc) {
3711 +void DutyUnit::resetCounters(const unsigned oldCc) {
3712 if (nextPosUpdate == COUNTER_DISABLED)
3713 return;
3715 @@ -142,11 +146,21 @@ void DutyUnit::killCounter() {
3716 setCounter();
3719 -void DutyUnit::reviveCounter(const unsigned long cc) {
3720 +void DutyUnit::reviveCounter(const unsigned cc) {
3721 updatePos(cc);
3722 high = toOutState(duty, pos);
3723 enableEvents = true;
3724 setCounter();
3727 +void DutyUnit::loadOrSave(loadsave& state) {
3728 + loadOrSave2(state);
3729 + state(nextPosUpdate);
3730 + state(period);
3731 + state(pos);
3732 + state(duty);
3733 + state(high);
3734 + state(enableEvents);
3738 diff --git a/libgambatte/src/sound/duty_unit.h b/libgambatte/src/sound/duty_unit.h
3739 index dba86ce..d4fb4af 100644
3740 --- a/libgambatte/src/sound/duty_unit.h
3741 +++ b/libgambatte/src/sound/duty_unit.h
3742 @@ -19,14 +19,19 @@
3743 #ifndef DUTY_UNIT_H
3744 #define DUTY_UNIT_H
3747 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
3748 +// - Make it rerecording-friendly.
3750 #include "sound_unit.h"
3751 #include "master_disabler.h"
3752 #include "../savestate.h"
3753 +#include "../loadsave.h"
3755 namespace gambatte {
3757 class DutyUnit : public SoundUnit {
3758 - unsigned long nextPosUpdate;
3759 + unsigned nextPosUpdate;
3760 unsigned short period;
3761 unsigned char pos;
3762 unsigned char duty;
3763 @@ -35,25 +40,27 @@ class DutyUnit : public SoundUnit {
3765 void setCounter();
3766 void setDuty(unsigned nr1);
3767 - void updatePos(unsigned long cc);
3768 + void updatePos(unsigned cc);
3770 public:
3771 DutyUnit();
3772 void event();
3773 bool isHighState() const { return high; }
3774 - void nr1Change(unsigned newNr1, unsigned long cc);
3775 - void nr3Change(unsigned newNr3, unsigned long cc);
3776 - void nr4Change(unsigned newNr4, unsigned long cc);
3777 + void nr1Change(unsigned newNr1, unsigned cc);
3778 + void nr3Change(unsigned newNr3, unsigned cc);
3779 + void nr4Change(unsigned newNr4, unsigned cc);
3780 void reset();
3781 - void saveState(SaveState::SPU::Duty &dstate, unsigned long cc);
3782 - void loadState(const SaveState::SPU::Duty &dstate, unsigned nr1, unsigned nr4, unsigned long cc);
3783 - void resetCounters(unsigned long oldCc);
3784 + void saveState(SaveState::SPU::Duty &dstate, unsigned cc);
3785 + void loadState(const SaveState::SPU::Duty &dstate, unsigned nr1, unsigned nr4, unsigned cc);
3786 + void resetCounters(unsigned oldCc);
3787 void killCounter();
3788 - void reviveCounter(unsigned long cc);
3790 + void reviveCounter(unsigned cc);
3792 + void loadOrSave(loadsave& state);
3794 //intended for use by SweepUnit only.
3795 unsigned getFreq() const { return 2048 - (period >> 1); }
3796 - void setFreq(unsigned newFreq, unsigned long cc);
3797 + void setFreq(unsigned newFreq, unsigned cc);
3800 class DutyMasterDisabler : public MasterDisabler {
3801 diff --git a/libgambatte/src/sound/envelope_unit.cpp b/libgambatte/src/sound/envelope_unit.cpp
3802 index b3d9712..2dfe42f 100644
3803 --- a/libgambatte/src/sound/envelope_unit.cpp
3804 +++ b/libgambatte/src/sound/envelope_unit.cpp
3805 @@ -19,12 +19,16 @@
3806 #include "envelope_unit.h"
3807 #include <algorithm>
3810 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
3811 +// - Make it rerecording-friendly.
3813 namespace gambatte {
3815 EnvelopeUnit::VolOnOffEvent EnvelopeUnit::nullEvent;
3817 void EnvelopeUnit::event() {
3818 - const unsigned long period = nr2 & 7;
3819 + const unsigned period = nr2 & 7;
3821 if (period) {
3822 unsigned newVol = volume;
3823 @@ -63,9 +67,9 @@ bool EnvelopeUnit::nr2Change(const unsigned newNr2) {
3824 return !(newNr2 & 0xF8);
3827 -bool EnvelopeUnit::nr4Init(const unsigned long cc) {
3828 +bool EnvelopeUnit::nr4Init(const unsigned cc) {
3830 - unsigned long period = nr2 & 7;
3831 + unsigned period = nr2 & 7;
3833 if (!period)
3834 period = 8;
3835 @@ -97,10 +101,17 @@ void EnvelopeUnit::saveState(SaveState::SPU::Env &estate) const {
3836 estate.volume = volume;
3839 -void EnvelopeUnit::loadState(const SaveState::SPU::Env &estate, const unsigned nr2, const unsigned long cc) {
3840 +void EnvelopeUnit::loadState(const SaveState::SPU::Env &estate, const unsigned nr2, const unsigned cc) {
3841 counter = std::max(estate.counter, cc);
3842 volume = estate.volume;
3843 this->nr2 = nr2;
3846 +void EnvelopeUnit::loadOrSave(loadsave& state)
3848 + loadOrSave2(state);
3849 + state(nr2);
3850 + state(volume);
3854 diff --git a/libgambatte/src/sound/envelope_unit.h b/libgambatte/src/sound/envelope_unit.h
3855 index 80216f4..139ae61 100644
3856 --- a/libgambatte/src/sound/envelope_unit.h
3857 +++ b/libgambatte/src/sound/envelope_unit.h
3858 @@ -19,8 +19,13 @@
3859 #ifndef ENVELOPE_UNIT_H
3860 #define ENVELOPE_UNIT_H
3863 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
3864 +// - Make it rerecording-friendly.
3866 #include "sound_unit.h"
3867 #include "../savestate.h"
3868 +#include "../loadsave.h"
3870 namespace gambatte {
3872 @@ -28,7 +33,7 @@ class EnvelopeUnit : public SoundUnit {
3873 public:
3874 struct VolOnOffEvent {
3875 virtual ~VolOnOffEvent() {}
3876 - virtual void operator()(unsigned long /*cc*/) {}
3877 + virtual void operator()(unsigned /*cc*/) {}
3880 private:
3881 @@ -43,10 +48,11 @@ public:
3882 bool dacIsOn() const { return nr2 & 0xF8; }
3883 unsigned getVolume() const { return volume; }
3884 bool nr2Change(unsigned newNr2);
3885 - bool nr4Init(unsigned long cycleCounter);
3886 + bool nr4Init(unsigned cycleCounter);
3887 void reset();
3888 void saveState(SaveState::SPU::Env &estate) const;
3889 - void loadState(const SaveState::SPU::Env &estate, unsigned nr2, unsigned long cc);
3890 + void loadState(const SaveState::SPU::Env &estate, unsigned nr2, unsigned cc);
3891 + void loadOrSave(loadsave& state);
3895 diff --git a/libgambatte/src/sound/length_counter.cpp b/libgambatte/src/sound/length_counter.cpp
3896 index 0503150..88e2a01 100644
3897 --- a/libgambatte/src/sound/length_counter.cpp
3898 +++ b/libgambatte/src/sound/length_counter.cpp
3899 @@ -20,6 +20,10 @@
3900 #include "master_disabler.h"
3901 #include <algorithm>
3904 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
3905 +// - Make it rerecording-friendly.
3907 namespace gambatte {
3909 LengthCounter::LengthCounter(MasterDisabler &disabler, const unsigned mask) :
3910 @@ -36,12 +40,12 @@ void LengthCounter::event() {
3911 disableMaster();
3914 -void LengthCounter::nr1Change(const unsigned newNr1, const unsigned nr4, const unsigned long cycleCounter) {
3915 +void LengthCounter::nr1Change(const unsigned newNr1, const unsigned nr4, const unsigned cycleCounter) {
3916 lengthCounter = (~newNr1 & lengthMask) + 1;
3917 - counter = (nr4 & 0x40) ?( (cycleCounter >> 13) + lengthCounter) << 13 : static_cast<unsigned long>(COUNTER_DISABLED);
3918 + counter = (nr4 & 0x40) ?( (cycleCounter >> 13) + lengthCounter) << 13 : static_cast<unsigned>(COUNTER_DISABLED);
3921 -void LengthCounter::nr4Change(const unsigned oldNr4, const unsigned newNr4, const unsigned long cycleCounter) {
3922 +void LengthCounter::nr4Change(const unsigned oldNr4, const unsigned newNr4, const unsigned cycleCounter) {
3923 if (counter != COUNTER_DISABLED)
3924 lengthCounter = (counter >> 13) - (cycleCounter >> 13);
3926 @@ -83,9 +87,16 @@ void LengthCounter::saveState(SaveState::SPU::LCounter &lstate) const {
3927 lstate.lengthCounter = lengthCounter;
3930 -void LengthCounter::loadState(const SaveState::SPU::LCounter &lstate, const unsigned long cc) {
3931 +void LengthCounter::loadState(const SaveState::SPU::LCounter &lstate, const unsigned cc) {
3932 counter = std::max(lstate.counter, cc);
3933 lengthCounter = lstate.lengthCounter;
3936 +void LengthCounter::loadOrSave(loadsave& state)
3938 + loadOrSave2(state);
3939 + state(lengthCounter);
3940 + state(cgb);
3944 diff --git a/libgambatte/src/sound/length_counter.h b/libgambatte/src/sound/length_counter.h
3945 index 8ae4571..79bc840 100644
3946 --- a/libgambatte/src/sound/length_counter.h
3947 +++ b/libgambatte/src/sound/length_counter.h
3948 @@ -19,8 +19,13 @@
3949 #ifndef LENGTH_COUNTER_H
3950 #define LENGTH_COUNTER_H
3953 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
3954 +// - Make it rerecording-friendly.
3956 #include "sound_unit.h"
3957 #include "../savestate.h"
3958 +#include "../loadsave.h"
3960 namespace gambatte {
3962 @@ -35,12 +40,14 @@ class LengthCounter : public SoundUnit {
3963 public:
3964 LengthCounter(MasterDisabler &disabler, unsigned lengthMask);
3965 void event();
3966 - void nr1Change(unsigned newNr1, unsigned nr4, unsigned long cc);
3967 - void nr4Change(unsigned oldNr4, unsigned newNr4, unsigned long cc);
3968 + void nr1Change(unsigned newNr1, unsigned nr4, unsigned cc);
3969 + void nr4Change(unsigned oldNr4, unsigned newNr4, unsigned cc);
3970 // void reset();
3971 void init(bool cgb);
3972 void saveState(SaveState::SPU::LCounter &lstate) const;
3973 - void loadState(const SaveState::SPU::LCounter &lstate, unsigned long cc);
3974 + void loadState(const SaveState::SPU::LCounter &lstate, unsigned cc);
3976 + void loadOrSave(loadsave& state);
3980 diff --git a/libgambatte/src/sound/sound_unit.h b/libgambatte/src/sound/sound_unit.h
3981 index 7fcf59c..c8234ea 100644
3982 --- a/libgambatte/src/sound/sound_unit.h
3983 +++ b/libgambatte/src/sound/sound_unit.h
3984 @@ -19,19 +19,28 @@
3985 #ifndef SOUND_UNIT_H
3986 #define SOUND_UNIT_H
3989 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
3990 +// - Make it rerecording-friendly.
3992 +#include "../loadsave.h"
3994 namespace gambatte {
3996 class SoundUnit {
3997 protected:
3998 - unsigned long counter;
3999 + unsigned counter;
4000 public:
4001 enum { COUNTER_MAX = 0x80000000u, COUNTER_DISABLED = 0xFFFFFFFFu };
4003 SoundUnit() : counter(COUNTER_DISABLED) {}
4004 virtual ~SoundUnit() {}
4005 virtual void event() = 0;
4006 - unsigned long getCounter() const { return counter; }
4007 - virtual void resetCounters(unsigned long /*oldCc*/) { if (counter != COUNTER_DISABLED) counter -= COUNTER_MAX; }
4008 + unsigned getCounter() const { return counter; }
4009 + virtual void resetCounters(unsigned /*oldCc*/) { if (counter != COUNTER_DISABLED) counter -= COUNTER_MAX; }
4010 + void loadOrSave2(loadsave& state) {
4011 + state(counter);
4016 diff --git a/libgambatte/src/sound/static_output_tester.h b/libgambatte/src/sound/static_output_tester.h
4017 index 2f79b8d..9836d3f 100644
4018 --- a/libgambatte/src/sound/static_output_tester.h
4019 +++ b/libgambatte/src/sound/static_output_tester.h
4020 @@ -19,6 +19,10 @@
4021 #ifndef STATIC_OUTPUT_TESTER_H
4022 #define STATIC_OUTPUT_TESTER_H
4025 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
4026 +// - Make it rerecording-friendly.
4028 #include "envelope_unit.h"
4030 namespace gambatte {
4031 @@ -29,11 +33,11 @@ class StaticOutputTester : public EnvelopeUnit::VolOnOffEvent {
4032 Unit &unit;
4033 public:
4034 StaticOutputTester(const Channel &ch, Unit &unit) : ch(ch), unit(unit) {}
4035 - void operator()(unsigned long cc);
4036 + void operator()(unsigned cc);
4039 template<class Channel, class Unit>
4040 -void StaticOutputTester<Channel, Unit>::operator()(const unsigned long cc) {
4041 +void StaticOutputTester<Channel, Unit>::operator()(const unsigned cc) {
4042 if (ch.soMask && ch.master && ch.envelopeUnit.getVolume())
4043 unit.reviveCounter(cc);
4044 else
4045 diff --git a/libgambatte/src/state_osd_elements.cpp b/libgambatte/src/state_osd_elements.cpp
4046 index 66b3594..e06df4f 100644
4047 --- a/libgambatte/src/state_osd_elements.cpp
4048 +++ b/libgambatte/src/state_osd_elements.cpp
4049 @@ -22,6 +22,10 @@
4050 #include <fstream>
4051 #include <cstring>
4054 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
4055 +// - Make it rerecording-friendly.
4057 namespace {
4059 using namespace gambatte;
4060 @@ -71,7 +75,7 @@ life(4 * 60) {
4061 print(pixels + 1 * w() + 1, w(), 0xE0E0E0ul, txt);*/
4063 print(pixels, w(), ShadeFill(), txt);
4064 - print(pixels + 1 * w() + 1, w(), 0xE0E0E0ul, txt);
4065 + print(pixels + 1 * w() + 1, w(), 0xE0E0E0u, txt);
4068 ShadedTextOsdElment::~ShadedTextOsdElment() {
4069 @@ -142,7 +146,7 @@ life(4 * 60) {
4071 static const char txt[] = { E,m,p,t,bitmapfont::y,0 };
4073 - print(pixels + 3 + (StateSaver::SS_HEIGHT / 2 - bitmapfont::HEIGHT / 2) * StateSaver::SS_WIDTH, StateSaver::SS_WIDTH, 0x808080ul, txt);
4074 + print(pixels + 3 + (StateSaver::SS_HEIGHT / 2 - bitmapfont::HEIGHT / 2) * StateSaver::SS_WIDTH, StateSaver::SS_WIDTH, 0x808080u, txt);
4078 diff --git a/libgambatte/src/statesaver.cpp b/libgambatte/src/statesaver.cpp
4079 index 67f677f..fc6fa51 100644
4080 --- a/libgambatte/src/statesaver.cpp
4081 +++ b/libgambatte/src/statesaver.cpp
4082 @@ -43,7 +43,7 @@ struct Saver {
4083 const char *label;
4084 void (*save)(std::ofstream &file, const SaveState &state);
4085 void (*load)(std::ifstream &file, SaveState &state);
4086 - unsigned char labelsize;
4087 + unsigned int labelsize;
4090 static inline bool operator<(const Saver &l, const Saver &r) {
4091 @@ -78,7 +78,7 @@ static void write(std::ofstream &file, const unsigned short data) {
4092 file.put(data & 0xFF);
4095 -static void write(std::ofstream &file, const unsigned long data) {
4096 +static void write(std::ofstream &file, const unsigned data) {
4097 static const char inf[] = { 0x00, 0x00, 0x04 };
4099 file.write(inf, sizeof(inf));
4100 @@ -137,7 +137,7 @@ static inline void read(std::ifstream &file, unsigned short &data) {
4101 data = read(file) & 0xFFFF;
4104 -static inline void read(std::ifstream &file, unsigned long &data) {
4105 +static inline void read(std::ifstream &file, unsigned &data) {
4106 data = read(file);
4109 diff --git a/libgambatte/src/statesaver.h b/libgambatte/src/statesaver.h
4110 index ee928b9..7a1e046 100644
4111 --- a/libgambatte/src/statesaver.h
4112 +++ b/libgambatte/src/statesaver.h
4113 @@ -19,8 +19,13 @@
4114 #ifndef STATESAVER_H
4115 #define STATESAVER_H
4118 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
4119 +// - Make it rerecording-friendly.
4121 #include "gbint.h"
4122 #include <string>
4123 +#include <vector>
4125 namespace gambatte {
4127 @@ -38,6 +43,8 @@ public:
4128 static bool saveState(const SaveState &state,
4129 const uint_least32_t *videoBuf, int pitch, const std::string &filename);
4130 static bool loadState(SaveState &state, const std::string &filename);
4131 + static void saveState(const SaveState &state, std::vector<char>& data);
4132 + static bool loadState(SaveState &state, const std::vector<char>& data);
4136 diff --git a/libgambatte/src/tima.cpp b/libgambatte/src/tima.cpp
4137 index 9d7b7ed..b90b82a 100644
4138 --- a/libgambatte/src/tima.cpp
4139 +++ b/libgambatte/src/tima.cpp
4140 @@ -19,6 +19,10 @@
4141 #include "tima.h"
4142 #include "savestate.h"
4145 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
4146 +// - Make it rerecording-friendly.
4148 static const unsigned char timaClock[4] = { 10, 4, 6, 8 };
4150 namespace gambatte {
4151 @@ -50,12 +54,12 @@ void Tima::loadState(const SaveState &state, const TimaInterruptRequester timaIr
4152 ? tmatime_
4153 : lastUpdate_ + ((256u - tima_) << timaClock[tac_ & 3]) + 3)
4155 - static_cast<unsigned long>(DISABLED_TIME)
4156 + static_cast<unsigned>(DISABLED_TIME)
4160 -void Tima::resetCc(const unsigned long oldCc, const unsigned long newCc, const TimaInterruptRequester timaIrq) {
4161 - const unsigned long dec = oldCc - newCc;
4162 +void Tima::resetCc(const unsigned oldCc, const unsigned newCc, const TimaInterruptRequester timaIrq) {
4163 + const unsigned dec = oldCc - newCc;
4165 if (tac_ & 0x04) {
4166 updateIrq(oldCc, timaIrq);
4167 @@ -69,8 +73,8 @@ void Tima::resetCc(const unsigned long oldCc, const unsigned long newCc, const T
4171 -void Tima::updateTima(const unsigned long cycleCounter) {
4172 - const unsigned long ticks = (cycleCounter - lastUpdate_) >> timaClock[tac_ & 3];
4173 +void Tima::updateTima(const unsigned cycleCounter) {
4174 + const unsigned ticks = (cycleCounter - lastUpdate_) >> timaClock[tac_ & 3];
4176 lastUpdate_ += ticks << timaClock[tac_ & 3];
4178 @@ -81,7 +85,7 @@ void Tima::updateTima(const unsigned long cycleCounter) {
4179 tima_ = tma_;
4182 - unsigned long tmp = tima_ + ticks;
4183 + unsigned tmp = tima_ + ticks;
4185 while (tmp > 0x100)
4186 tmp -= 0x100 - tma_;
4187 @@ -101,7 +105,7 @@ void Tima::updateTima(const unsigned long cycleCounter) {
4188 tima_ = tmp;
4191 -void Tima::setTima(const unsigned data, const unsigned long cycleCounter, const TimaInterruptRequester timaIrq) {
4192 +void Tima::setTima(const unsigned data, const unsigned cycleCounter, const TimaInterruptRequester timaIrq) {
4193 if (tac_ & 0x04) {
4194 updateIrq(cycleCounter, timaIrq);
4195 updateTima(cycleCounter);
4196 @@ -115,7 +119,7 @@ void Tima::setTima(const unsigned data, const unsigned long cycleCounter, const
4197 tima_ = data;
4200 -void Tima::setTma(const unsigned data, const unsigned long cycleCounter, const TimaInterruptRequester timaIrq) {
4201 +void Tima::setTma(const unsigned data, const unsigned cycleCounter, const TimaInterruptRequester timaIrq) {
4202 if (tac_ & 0x04) {
4203 updateIrq(cycleCounter, timaIrq);
4204 updateTima(cycleCounter);
4205 @@ -124,9 +128,9 @@ void Tima::setTma(const unsigned data, const unsigned long cycleCounter, const T
4206 tma_ = data;
4209 -void Tima::setTac(const unsigned data, const unsigned long cycleCounter, const TimaInterruptRequester timaIrq) {
4210 +void Tima::setTac(const unsigned data, const unsigned cycleCounter, const TimaInterruptRequester timaIrq) {
4211 if (tac_ ^ data) {
4212 - unsigned long nextIrqEventTime = timaIrq.nextIrqEventTime();
4213 + unsigned nextIrqEventTime = timaIrq.nextIrqEventTime();
4215 if (tac_ & 0x04) {
4216 updateIrq(cycleCounter, timaIrq);
4217 @@ -156,7 +160,7 @@ void Tima::setTac(const unsigned data, const unsigned long cycleCounter, const T
4218 tac_ = data;
4221 -unsigned Tima::tima(unsigned long cycleCounter) {
4222 +unsigned Tima::tima(unsigned cycleCounter) {
4223 if (tac_ & 0x04)
4224 updateTima(cycleCounter);
4226 @@ -168,4 +172,13 @@ void Tima::doIrqEvent(const TimaInterruptRequester timaIrq) {
4227 timaIrq.setNextIrqEventTime(timaIrq.nextIrqEventTime() + ((256u - tma_) << timaClock[tac_ & 3]));
4230 +void Tima::loadOrSave(loadsave& state)
4232 + state(lastUpdate_);
4233 + state(tmatime_);
4234 + state(tima_);
4235 + state(tma_);
4236 + state(tac_);
4240 diff --git a/libgambatte/src/tima.h b/libgambatte/src/tima.h
4241 index 61cd5e5..130862a 100644
4242 --- a/libgambatte/src/tima.h
4243 +++ b/libgambatte/src/tima.h
4244 @@ -19,6 +19,10 @@
4245 #ifndef TIMA_H
4246 #define TIMA_H
4249 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
4250 +// - Make it rerecording-friendly.
4252 #include "interruptrequester.h"
4254 namespace gambatte {
4255 @@ -29,37 +33,39 @@ class TimaInterruptRequester {
4256 public:
4257 explicit TimaInterruptRequester(InterruptRequester &intreq) : intreq(intreq) {}
4258 void flagIrq() const { intreq.flagIrq(4); }
4259 - unsigned long nextIrqEventTime() const { return intreq.eventTime(TIMA); }
4260 - void setNextIrqEventTime(const unsigned long time) const { intreq.setEventTime<TIMA>(time); }
4261 + unsigned nextIrqEventTime() const { return intreq.eventTime(TIMA); }
4262 + void setNextIrqEventTime(const unsigned time) const { intreq.setEventTime<TIMA>(time); }
4265 class Tima {
4266 - unsigned long lastUpdate_;
4267 - unsigned long tmatime_;
4268 + unsigned lastUpdate_;
4269 + unsigned tmatime_;
4271 unsigned char tima_;
4272 unsigned char tma_;
4273 unsigned char tac_;
4275 - void updateIrq(const unsigned long cc, const TimaInterruptRequester timaIrq) {
4276 + void updateIrq(const unsigned cc, const TimaInterruptRequester timaIrq) {
4277 while (cc >= timaIrq.nextIrqEventTime())
4278 doIrqEvent(timaIrq);
4281 - void updateTima(unsigned long cc);
4282 + void updateTima(unsigned cc);
4284 public:
4285 Tima();
4286 void saveState(SaveState &) const;
4287 void loadState(const SaveState &, TimaInterruptRequester timaIrq);
4288 - void resetCc(unsigned long oldCc, unsigned long newCc, TimaInterruptRequester timaIrq);
4289 + void resetCc(unsigned oldCc, unsigned newCc, TimaInterruptRequester timaIrq);
4291 - void setTima(unsigned tima, unsigned long cc, TimaInterruptRequester timaIrq);
4292 - void setTma(unsigned tma, unsigned long cc, TimaInterruptRequester timaIrq);
4293 - void setTac(unsigned tac, unsigned long cc, TimaInterruptRequester timaIrq);
4294 - unsigned tima(unsigned long cc);
4295 + void setTima(unsigned tima, unsigned cc, TimaInterruptRequester timaIrq);
4296 + void setTma(unsigned tma, unsigned cc, TimaInterruptRequester timaIrq);
4297 + void setTac(unsigned tac, unsigned cc, TimaInterruptRequester timaIrq);
4298 + unsigned tima(unsigned cc);
4300 void doIrqEvent(TimaInterruptRequester timaIrq);
4302 + void loadOrSave(loadsave& state);
4306 diff --git a/libgambatte/src/video.cpp b/libgambatte/src/video.cpp
4307 index 45d82e1..b6aec75 100644
4308 --- a/libgambatte/src/video.cpp
4309 +++ b/libgambatte/src/video.cpp
4310 @@ -21,19 +21,23 @@
4311 #include <cstring>
4312 #include <algorithm>
4315 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
4316 +// - Make it rerecording-friendly.
4318 namespace gambatte {
4320 -void LCD::setDmgPalette(unsigned long *const palette, const unsigned long *const dmgColors, const unsigned data) {
4321 +void LCD::setDmgPalette(uint_least32_t *const palette, const uint_least32_t *const dmgColors, const unsigned data) {
4322 palette[0] = dmgColors[data & 3];
4323 palette[1] = dmgColors[data >> 2 & 3];
4324 palette[2] = dmgColors[data >> 4 & 3];
4325 palette[3] = dmgColors[data >> 6 & 3];
4328 -static unsigned long gbcToRgb32(const unsigned bgr15) {
4329 - const unsigned long r = bgr15 & 0x1F;
4330 - const unsigned long g = bgr15 >> 5 & 0x1F;
4331 - const unsigned long b = bgr15 >> 10 & 0x1F;
4332 +static uint_least32_t gbcToRgb32(const unsigned bgr15) {
4333 + const uint_least32_t r = bgr15 & 0x1F;
4334 + const uint_least32_t g = bgr15 >> 5 & 0x1F;
4335 + const uint_least32_t b = bgr15 >> 10 & 0x1F;
4337 return ((r * 13 + g * 2 + b) >> 1) << 16 | (g * 3 + b) << 9 | (r * 3 + g * 2 + b * 11) >> 1;
4339 @@ -80,7 +84,6 @@ LCD::LCD(const unsigned char *const oamram, const unsigned char *const vram, con
4340 setDmgPaletteColor(i, (3 - (i & 3)) * 85 * 0x010101);
4342 reset(oamram, vram, false);
4343 - setVideoBuffer(0, 160);
4346 void LCD::reset(const unsigned char *const oamram, const unsigned char *vram, const bool cgb) {
4347 @@ -89,7 +92,7 @@ void LCD::reset(const unsigned char *const oamram, const unsigned char *vram, co
4348 refreshPalettes();
4351 -static unsigned long mode2IrqSchedule(const unsigned statReg, const LyCounter &lyCounter, const unsigned long cycleCounter) {
4352 +static unsigned mode2IrqSchedule(const unsigned statReg, const LyCounter &lyCounter, const unsigned cycleCounter) {
4353 if (!(statReg & 0x20))
4354 return DISABLED_TIME;
4356 @@ -107,16 +110,16 @@ static unsigned long mode2IrqSchedule(const unsigned statReg, const LyCounter &l
4357 return cycleCounter + next;
4360 -static inline unsigned long m0IrqTimeFromXpos166Time(const unsigned long xpos166Time, const bool cgb, const bool ds) {
4361 +static inline unsigned m0IrqTimeFromXpos166Time(const unsigned xpos166Time, const bool cgb, const bool ds) {
4362 return xpos166Time + cgb - ds;
4365 -static inline unsigned long hdmaTimeFromM0Time(const unsigned long m0Time, const bool ds) {
4366 +static inline unsigned hdmaTimeFromM0Time(const unsigned m0Time, const bool ds) {
4367 return m0Time + 1 - ds;
4370 -static unsigned long nextHdmaTime(const unsigned long lastM0Time,
4371 - const unsigned long nextM0Time, const unsigned long cycleCounter, const bool ds) {
4372 +static unsigned nextHdmaTime(const unsigned lastM0Time,
4373 + const unsigned nextM0Time, const unsigned cycleCounter, const bool ds) {
4374 return cycleCounter < hdmaTimeFromM0Time(lastM0Time, ds)
4375 ? hdmaTimeFromM0Time(lastM0Time, ds)
4376 : hdmaTimeFromM0Time(nextM0Time, ds);
4377 @@ -152,18 +155,18 @@ void LCD::loadState(const SaveState &state, const unsigned char *const oamram) {
4378 lycIrq.reschedule(ppu.lyCounter(), ppu.now());
4380 eventTimes_.setm<ONESHOT_LCDSTATIRQ>(state.ppu.pendingLcdstatIrq
4381 - ? ppu.now() + 1 : static_cast<unsigned long>(DISABLED_TIME));
4382 + ? ppu.now() + 1 : static_cast<unsigned>(DISABLED_TIME));
4383 eventTimes_.setm<ONESHOT_UPDATEWY2>(state.ppu.oldWy != state.mem.ioamhram.get()[0x14A]
4384 - ? ppu.now() + 1 : static_cast<unsigned long>(DISABLED_TIME));
4385 + ? ppu.now() + 1 : static_cast<unsigned>(DISABLED_TIME));
4386 eventTimes_.set<LY_COUNT>(ppu.lyCounter().time());
4387 eventTimes_.setm<SPRITE_MAP>(SpriteMapper::schedule(ppu.lyCounter(), ppu.now()));
4388 eventTimes_.setm<LYC_IRQ>(lycIrq.time());
4389 eventTimes_.setm<MODE1_IRQ>(ppu.lyCounter().nextFrameCycle(144 * 456, ppu.now()));
4390 eventTimes_.setm<MODE2_IRQ>(mode2IrqSchedule(statReg, ppu.lyCounter(), ppu.now()));
4391 - eventTimes_.setm<MODE0_IRQ>((statReg & 0x08) ? ppu.now() + state.ppu.nextM0Irq : static_cast<unsigned long>(DISABLED_TIME));
4392 + eventTimes_.setm<MODE0_IRQ>((statReg & 0x08) ? ppu.now() + state.ppu.nextM0Irq : static_cast<unsigned>(DISABLED_TIME));
4393 eventTimes_.setm<HDMA_REQ>(state.mem.hdmaTransfer
4394 ? nextHdmaTime(ppu.lastM0Time(), nextM0Time_.predictedNextM0Time(), ppu.now(), isDoubleSpeed())
4395 - : static_cast<unsigned long>(DISABLED_TIME));
4396 + : static_cast<unsigned>(DISABLED_TIME));
4397 } else for (int i = 0; i < NUM_MEM_EVENTS; ++i)
4398 eventTimes_.set(static_cast<MemEvent>(i), DISABLED_TIME);
4400 @@ -212,7 +215,7 @@ struct Blend {
4403 template<typename T>
4404 -static void clear(T *buf, const unsigned long color, const int dpitch) {
4405 +static void clear(T *buf, const uint_least32_t color, const int dpitch) {
4406 unsigned lines = 144;
4408 while (lines--) {
4409 @@ -223,11 +226,11 @@ static void clear(T *buf, const unsigned long color, const int dpitch) {
4413 -void LCD::updateScreen(const bool blanklcd, const unsigned long cycleCounter) {
4414 +void LCD::updateScreen(const bool blanklcd, const unsigned cycleCounter, uint_least32_t* vbuffer, unsigned vpitch) {
4415 update(cycleCounter);
4417 if (blanklcd && ppu.frameBuf().fb()) {
4418 - const unsigned long color = ppu.cgb() ? gbcToRgb32(0xFFFF) : dmgColorsRgb32[0];
4419 + const uint_least32_t color = ppu.cgb() ? gbcToRgb32(0xFFFF) : dmgColorsRgb32[0];
4420 clear(ppu.frameBuf().fb(), color, ppu.frameBuf().pitch());
4423 @@ -245,14 +248,15 @@ void LCD::updateScreen(const bool blanklcd, const unsigned long cycleCounter) {
4424 } else
4425 osdElement.reset();
4427 + ppu.frameBuf().blit(vbuffer, vpitch);
4430 -void LCD::resetCc(const unsigned long oldCc, const unsigned long newCc) {
4431 +void LCD::resetCc(const unsigned oldCc, const unsigned newCc) {
4432 update(oldCc);
4433 ppu.resetCc(oldCc, newCc);
4435 if (ppu.lcdc() & 0x80) {
4436 - const unsigned long dec = oldCc - newCc;
4437 + const unsigned dec = oldCc - newCc;
4439 nextM0Time_.invalidatePredictedNextM0Time();
4440 lycIrq.reschedule(ppu.lyCounter(), newCc);
4441 @@ -266,7 +270,7 @@ void LCD::resetCc(const unsigned long oldCc, const unsigned long newCc) {
4445 -void LCD::speedChange(const unsigned long cycleCounter) {
4446 +void LCD::speedChange(const unsigned cycleCounter) {
4447 update(cycleCounter);
4448 ppu.speedChange(cycleCounter);
4450 @@ -290,13 +294,13 @@ void LCD::speedChange(const unsigned long cycleCounter) {
4454 -static inline unsigned long m0TimeOfCurrentLine(const unsigned long nextLyTime,
4455 - const unsigned long lastM0Time, const unsigned long nextM0Time)
4456 +static inline unsigned m0TimeOfCurrentLine(const unsigned nextLyTime,
4457 + const unsigned lastM0Time, const unsigned nextM0Time)
4459 return nextM0Time < nextLyTime ? nextM0Time : lastM0Time;
4462 -unsigned long LCD::m0TimeOfCurrentLine(const unsigned long cc) {
4463 +unsigned LCD::m0TimeOfCurrentLine(const unsigned cc) {
4464 if (cc >= nextM0Time_.predictedNextM0Time()) {
4465 update(cc);
4466 nextM0Time_.predictNextM0Time(ppu);
4467 @@ -306,7 +310,7 @@ unsigned long LCD::m0TimeOfCurrentLine(const unsigned long cc) {
4470 static bool isHdmaPeriod(const LyCounter &lyCounter,
4471 - const unsigned long m0TimeOfCurrentLy, const unsigned long cycleCounter)
4472 + const unsigned m0TimeOfCurrentLy, const unsigned cycleCounter)
4474 const unsigned timeToNextLy = lyCounter.time() - cycleCounter;
4476 @@ -314,7 +318,7 @@ static bool isHdmaPeriod(const LyCounter &lyCounter,
4477 && cycleCounter >= hdmaTimeFromM0Time(m0TimeOfCurrentLy, lyCounter.isDoubleSpeed());
4480 -void LCD::enableHdma(const unsigned long cycleCounter) {
4481 +void LCD::enableHdma(const unsigned cycleCounter) {
4482 if (cycleCounter >= nextM0Time_.predictedNextM0Time()) {
4483 update(cycleCounter);
4484 nextM0Time_.predictNextM0Time(ppu);
4485 @@ -330,14 +334,14 @@ void LCD::enableHdma(const unsigned long cycleCounter) {
4486 eventTimes_.setm<HDMA_REQ>(nextHdmaTime(ppu.lastM0Time(), nextM0Time_.predictedNextM0Time(), cycleCounter, isDoubleSpeed()));
4489 -void LCD::disableHdma(const unsigned long cycleCounter) {
4490 +void LCD::disableHdma(const unsigned cycleCounter) {
4491 if (cycleCounter >= eventTimes_.nextEventTime())
4492 update(cycleCounter);
4494 eventTimes_.setm<HDMA_REQ>(DISABLED_TIME);
4497 -bool LCD::vramAccessible(const unsigned long cycleCounter) {
4498 +bool LCD::vramAccessible(const unsigned cycleCounter) {
4499 if (cycleCounter >= eventTimes_.nextEventTime())
4500 update(cycleCounter);
4502 @@ -346,7 +350,7 @@ bool LCD::vramAccessible(const unsigned long cycleCounter) {
4503 || cycleCounter + isDoubleSpeed() - ppu.cgb() + 2 >= m0TimeOfCurrentLine(cycleCounter);
4506 -bool LCD::cgbpAccessible(const unsigned long cycleCounter) {
4507 +bool LCD::cgbpAccessible(const unsigned cycleCounter) {
4508 if (cycleCounter >= eventTimes_.nextEventTime())
4509 update(cycleCounter);
4511 @@ -356,27 +360,27 @@ bool LCD::cgbpAccessible(const unsigned long cycleCounter) {
4514 static void doCgbColorChange(unsigned char *const pdata,
4515 - unsigned long *const palette, unsigned index, const unsigned data) {
4516 + uint_least32_t *const palette, unsigned index, const unsigned data) {
4517 pdata[index] = data;
4518 index >>= 1;
4519 palette[index] = gbcToRgb32(pdata[index << 1] | pdata[(index << 1) + 1] << 8);
4522 -void LCD::doCgbBgColorChange(unsigned index, const unsigned data, const unsigned long cycleCounter) {
4523 +void LCD::doCgbBgColorChange(unsigned index, const unsigned data, const unsigned cycleCounter) {
4524 if (cgbpAccessible(cycleCounter)) {
4525 update(cycleCounter);
4526 doCgbColorChange(bgpData, ppu.bgPalette(), index, data);
4530 -void LCD::doCgbSpColorChange(unsigned index, const unsigned data, const unsigned long cycleCounter) {
4531 +void LCD::doCgbSpColorChange(unsigned index, const unsigned data, const unsigned cycleCounter) {
4532 if (cgbpAccessible(cycleCounter)) {
4533 update(cycleCounter);
4534 doCgbColorChange(objpData, ppu.spPalette(), index, data);
4538 -bool LCD::oamReadable(const unsigned long cycleCounter) {
4539 +bool LCD::oamReadable(const unsigned cycleCounter) {
4540 if (!(ppu.lcdc() & 0x80) || ppu.inactivePeriodAfterDisplayEnable(cycleCounter))
4541 return true;
4543 @@ -389,7 +393,7 @@ bool LCD::oamReadable(const unsigned long cycleCounter) {
4544 return ppu.lyCounter().ly() >= 144 || cycleCounter + isDoubleSpeed() - ppu.cgb() + 2 >= m0TimeOfCurrentLine(cycleCounter);
4547 -bool LCD::oamWritable(const unsigned long cycleCounter) {
4548 +bool LCD::oamWritable(const unsigned cycleCounter) {
4549 if (!(ppu.lcdc() & 0x80) || ppu.inactivePeriodAfterDisplayEnable(cycleCounter))
4550 return true;
4552 @@ -417,13 +421,13 @@ void LCD::mode3CyclesChange() {
4556 -void LCD::wxChange(const unsigned newValue, const unsigned long cycleCounter) {
4557 +void LCD::wxChange(const unsigned newValue, const unsigned cycleCounter) {
4558 update(cycleCounter + isDoubleSpeed() + 1);
4559 ppu.setWx(newValue);
4560 mode3CyclesChange();
4563 -void LCD::wyChange(const unsigned newValue, const unsigned long cycleCounter) {
4564 +void LCD::wyChange(const unsigned newValue, const unsigned cycleCounter) {
4565 update(cycleCounter + 1);
4566 ppu.setWy(newValue);
4567 // mode3CyclesChange(); // should be safe to wait until after wy2 delay, because no mode3 events are close to when wy1 is read.
4568 @@ -438,18 +442,18 @@ void LCD::wyChange(const unsigned newValue, const unsigned long cycleCounter) {
4572 -void LCD::scxChange(const unsigned newScx, const unsigned long cycleCounter) {
4573 +void LCD::scxChange(const unsigned newScx, const unsigned cycleCounter) {
4574 update(cycleCounter + ppu.cgb() + isDoubleSpeed());
4575 ppu.setScx(newScx);
4576 mode3CyclesChange();
4579 -void LCD::scyChange(const unsigned newValue, const unsigned long cycleCounter) {
4580 +void LCD::scyChange(const unsigned newValue, const unsigned cycleCounter) {
4581 update(cycleCounter + ppu.cgb() + isDoubleSpeed());
4582 ppu.setScy(newValue);
4585 -void LCD::oamChange(const unsigned long cycleCounter) {
4586 +void LCD::oamChange(const unsigned cycleCounter) {
4587 if (ppu.lcdc() & 0x80) {
4588 update(cycleCounter);
4589 ppu.oamChange(cycleCounter);
4590 @@ -457,7 +461,7 @@ void LCD::oamChange(const unsigned long cycleCounter) {
4594 -void LCD::oamChange(const unsigned char *const oamram, const unsigned long cycleCounter) {
4595 +void LCD::oamChange(const unsigned char *const oamram, const unsigned cycleCounter) {
4596 update(cycleCounter);
4597 ppu.oamChange(oamram, cycleCounter);
4599 @@ -465,7 +469,7 @@ void LCD::oamChange(const unsigned char *const oamram, const unsigned long cycle
4600 eventTimes_.setm<SPRITE_MAP>(SpriteMapper::schedule(ppu.lyCounter(), cycleCounter));
4603 -void LCD::lcdcChange(const unsigned data, const unsigned long cycleCounter) {
4604 +void LCD::lcdcChange(const unsigned data, const unsigned cycleCounter) {
4605 const unsigned oldLcdc = ppu.lcdc();
4606 update(cycleCounter);
4608 @@ -545,7 +549,7 @@ static LyCnt const getLycCmpLy(LyCounter const &lyCounter, unsigned long cc) {
4612 -void LCD::lcdstatChange(unsigned const data, unsigned long const cycleCounter) {
4613 +void LCD::lcdstatChange(const unsigned data, const unsigned cycleCounter) {
4614 if (cycleCounter >= eventTimes_.nextEventTime())
4615 update(cycleCounter);
4617 @@ -614,7 +618,7 @@ void LCD::lcdstatChange(unsigned const data, unsigned long const cycleCounter) {
4618 m0Irq_.statRegChange(data, eventTimes_(MODE0_IRQ), cycleCounter, ppu.cgb());
4621 -void LCD::lycRegChange(unsigned const data, unsigned long const cycleCounter) {
4622 +void LCD::lycRegChange(const unsigned data, const unsigned cycleCounter) {
4623 unsigned const old = lycIrq.lycReg();
4625 if (data == old)
4626 @@ -654,7 +658,7 @@ void LCD::lycRegChange(unsigned const data, unsigned long const cycleCounter) {
4630 -unsigned LCD::getStat(unsigned const lycReg, unsigned long const cycleCounter) {
4631 +unsigned LCD::getStat(const unsigned lycReg, const unsigned cycleCounter) {
4632 unsigned stat = 0;
4634 if (ppu.lcdc() & 0x80) {
4635 @@ -698,7 +702,7 @@ inline void LCD::doMode2IrqEvent() {
4636 m2IrqStatReg_ = statReg;
4638 if (!(statReg & 0x08)) {
4639 - unsigned long nextTime = eventTimes_(MODE2_IRQ) + ppu.lyCounter().lineTime();
4640 + unsigned nextTime = eventTimes_(MODE2_IRQ) + ppu.lyCounter().lineTime();
4642 if (ly == 0) {
4643 nextTime -= 4;
4644 @@ -752,7 +756,7 @@ inline void LCD::event() {
4646 eventTimes_.setm<MODE0_IRQ>((statReg & 0x08)
4647 ? m0IrqTimeFromXpos166Time(ppu.predictedNextXposTime(166), ppu.cgb(), isDoubleSpeed())
4648 - : static_cast<unsigned long>(DISABLED_TIME));
4649 + : static_cast<unsigned>(DISABLED_TIME));
4650 break;
4652 case ONESHOT_LCDSTATIRQ:
4653 @@ -776,7 +780,7 @@ inline void LCD::event() {
4657 -void LCD::update(const unsigned long cycleCounter) {
4658 +void LCD::update(const unsigned cycleCounter) {
4659 if (!(ppu.lcdc() & 0x80))
4660 return;
4662 @@ -788,15 +792,11 @@ void LCD::update(const unsigned long cycleCounter) {
4663 ppu.update(cycleCounter);
4666 -void LCD::setVideoBuffer(uint_least32_t *const videoBuf, const int pitch) {
4667 - ppu.setFrameBuf(videoBuf, pitch);
4670 -void LCD::setDmgPaletteColor(const unsigned index, const unsigned long rgb32) {
4671 +void LCD::setDmgPaletteColor(const unsigned index, const uint_least32_t rgb32) {
4672 dmgColorsRgb32[index] = rgb32;
4675 -void LCD::setDmgPaletteColor(const unsigned palNum, const unsigned colorNum, const unsigned long rgb32) {
4676 +void LCD::setDmgPaletteColor(const unsigned palNum, const unsigned colorNum, const uint_least32_t rgb32) {
4677 if (palNum > 2 || colorNum > 3)
4678 return;
4680 @@ -804,4 +804,18 @@ void LCD::setDmgPaletteColor(const unsigned palNum, const unsigned colorNum, con
4681 refreshPalettes();
4684 +void LCD::loadOrSave(loadsave& state) {
4685 + ppu.loadOrSave(state);
4686 + state(dmgColorsRgb32, 12);
4687 + state(bgpData, 64);
4688 + state(objpData, 64);
4689 + eventTimes_.loadOrSave(state);
4690 + m0Irq_.loadOrSave(state);
4691 + lycIrq.loadOrSave(state);
4692 + nextM0Time_.loadOrSave(state);
4693 + state(statReg);
4694 + state(m2IrqStatReg_);
4695 + state(m1IrqStatReg_);
4699 diff --git a/libgambatte/src/video.h b/libgambatte/src/video.h
4700 index 14a5939..a830d88 100644
4701 --- a/libgambatte/src/video.h
4702 +++ b/libgambatte/src/video.h
4703 @@ -19,6 +19,10 @@
4704 #ifndef VIDEO_H
4705 #define VIDEO_H
4708 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
4709 +// - Make it rerecording-friendly.
4711 #include "video/ppu.h"
4712 #include "video/lyc_irq.h"
4713 #include "video/next_m0_time.h"
4714 @@ -36,7 +40,7 @@ public:
4715 explicit VideoInterruptRequester(InterruptRequester * intreq) : intreq(intreq) {}
4716 void flagHdmaReq() const { gambatte::flagHdmaReq(intreq); }
4717 void flagIrq(const unsigned bit) const { intreq->flagIrq(bit); }
4718 - void setNextEventTime(const unsigned long time) const { intreq->setEventTime<VIDEO>(time); }
4719 + void setNextEventTime(const unsigned time) const { intreq->setEventTime<VIDEO>(time); }
4722 class M0Irq {
4723 @@ -50,15 +54,21 @@ public:
4724 statReg_ = statReg;
4725 lycReg_ = lycReg;
4729 + void loadOrSave(loadsave& state)
4731 + state(statReg_);
4732 + state(lycReg_);
4735 void statRegChange(const unsigned statReg,
4736 - const unsigned long nextM0IrqTime, const unsigned long cc, const bool cgb) {
4737 + const unsigned nextM0IrqTime, const unsigned cc, const bool cgb) {
4738 if (nextM0IrqTime - cc > cgb * 2U)
4739 statReg_ = statReg;
4742 void lycRegChange(const unsigned lycReg,
4743 - const unsigned long nextM0IrqTime, const unsigned long cc, const bool ds, const bool cgb) {
4744 + const unsigned nextM0IrqTime, const unsigned cc, const bool ds, const bool cgb) {
4745 if (nextM0IrqTime - cc > cgb * 5 + 1U - ds)
4746 lycReg_ = lycReg;
4748 @@ -94,7 +104,7 @@ class LCD {
4749 VideoInterruptRequester memEventRequester_;
4751 void setMemEvent() {
4752 - const unsigned long nmet = nextMemEventTime();
4753 + const unsigned nmet = nextMemEventTime();
4754 eventMin_.setValue<MEM_EVENT>(nmet);
4755 memEventRequester_.setNextEventTime(nmet);
4757 @@ -103,23 +113,28 @@ class LCD {
4758 explicit EventTimes(const VideoInterruptRequester memEventRequester) : memEventRequester_(memEventRequester) {}
4760 Event nextEvent() const { return static_cast<Event>(eventMin_.min()); }
4761 - unsigned long nextEventTime() const { return eventMin_.minValue(); }
4762 - unsigned long operator()(const Event e) const { return eventMin_.value(e); }
4763 - template<Event e> void set(const unsigned long time) { eventMin_.setValue<e>(time); }
4764 - void set(const Event e, const unsigned long time) { eventMin_.setValue(e, time); }
4765 + unsigned nextEventTime() const { return eventMin_.minValue(); }
4766 + unsigned operator()(const Event e) const { return eventMin_.value(e); }
4767 + template<Event e> void set(const unsigned time) { eventMin_.setValue<e>(time); }
4768 + void set(const Event e, const unsigned time) { eventMin_.setValue(e, time); }
4770 MemEvent nextMemEvent() const { return static_cast<MemEvent>(memEventMin_.min()); }
4771 - unsigned long nextMemEventTime() const { return memEventMin_.minValue(); }
4772 - unsigned long operator()(const MemEvent e) const { return memEventMin_.value(e); }
4773 - template<MemEvent e> void setm(const unsigned long time) { memEventMin_.setValue<e>(time); setMemEvent(); }
4774 - void set(const MemEvent e, const unsigned long time) { memEventMin_.setValue(e, time); setMemEvent(); }
4775 + unsigned nextMemEventTime() const { return memEventMin_.minValue(); }
4776 + unsigned operator()(const MemEvent e) const { return memEventMin_.value(e); }
4777 + template<MemEvent e> void setm(const unsigned time) { memEventMin_.setValue<e>(time); setMemEvent(); }
4778 + void set(const MemEvent e, const unsigned time) { memEventMin_.setValue(e, time); setMemEvent(); }
4780 void flagIrq(const unsigned bit) { memEventRequester_.flagIrq(bit); }
4781 void flagHdmaReq() { memEventRequester_.flagHdmaReq(); }
4783 + void loadOrSave(loadsave& state) {
4784 + eventMin_.loadOrSave(state);
4785 + memEventMin_.loadOrSave(state);
4789 PPU ppu;
4790 - unsigned long dmgColorsRgb32[3 * 4];
4791 + uint_least32_t dmgColorsRgb32[3 * 4];
4792 unsigned char bgpData[8 * 8];
4793 unsigned char objpData[8 * 8];
4795 @@ -134,8 +149,8 @@ class LCD {
4796 unsigned char m2IrqStatReg_;
4797 unsigned char m1IrqStatReg_;
4799 - static void setDmgPalette(unsigned long *palette, const unsigned long *dmgColors, unsigned data);
4800 - void setDmgPaletteColor(unsigned index, unsigned long rgb32);
4801 + static void setDmgPalette(uint_least32_t *palette, const uint_least32_t *dmgColors, unsigned data);
4802 + void setDmgPaletteColor(unsigned index, uint_least32_t rgb32);
4804 void refreshPalettes();
4805 void setDBuffer();
4806 @@ -143,12 +158,12 @@ class LCD {
4807 void doMode2IrqEvent();
4808 void event();
4810 - unsigned long m0TimeOfCurrentLine(unsigned long cc);
4811 - bool cgbpAccessible(unsigned long cycleCounter);
4812 + unsigned m0TimeOfCurrentLine(unsigned cc);
4813 + bool cgbpAccessible(unsigned cycleCounter);
4815 void mode3CyclesChange();
4816 - void doCgbBgColorChange(unsigned index, unsigned data, unsigned long cycleCounter);
4817 - void doCgbSpColorChange(unsigned index, unsigned data, unsigned long cycleCounter);
4818 + void doCgbBgColorChange(unsigned index, unsigned data, unsigned cycleCounter);
4819 + void doCgbSpColorChange(unsigned index, unsigned data, unsigned cycleCounter);
4821 public:
4822 LCD(const unsigned char *oamram, const unsigned char *vram_in, VideoInterruptRequester memEventRequester);
4823 @@ -156,65 +171,66 @@ public:
4824 void setStatePtrs(SaveState &state);
4825 void saveState(SaveState &state) const;
4826 void loadState(const SaveState &state, const unsigned char *oamram);
4827 - void setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned long rgb32);
4828 - void setVideoBuffer(uint_least32_t *videoBuf, int pitch);
4829 + void setDmgPaletteColor(unsigned palNum, unsigned colorNum, uint_least32_t rgb32);
4831 + void loadOrSave(loadsave& state);
4833 void setOsdElement(std::auto_ptr<OsdElement> osdElement) { this->osdElement = osdElement; }
4835 - void dmgBgPaletteChange(const unsigned data, const unsigned long cycleCounter) {
4836 + void dmgBgPaletteChange(const unsigned data, const unsigned cycleCounter) {
4837 update(cycleCounter);
4838 bgpData[0] = data;
4839 setDmgPalette(ppu.bgPalette(), dmgColorsRgb32, data);
4842 - void dmgSpPalette1Change(const unsigned data, const unsigned long cycleCounter) {
4843 + void dmgSpPalette1Change(const unsigned data, const unsigned cycleCounter) {
4844 update(cycleCounter);
4845 objpData[0] = data;
4846 setDmgPalette(ppu.spPalette(), dmgColorsRgb32 + 4, data);
4849 - void dmgSpPalette2Change(const unsigned data, const unsigned long cycleCounter) {
4850 + void dmgSpPalette2Change(const unsigned data, const unsigned cycleCounter) {
4851 update(cycleCounter);
4852 objpData[1] = data;
4853 setDmgPalette(ppu.spPalette() + 4, dmgColorsRgb32 + 8, data);
4856 - void cgbBgColorChange(unsigned index, const unsigned data, const unsigned long cycleCounter) {
4857 + void cgbBgColorChange(unsigned index, const unsigned data, const unsigned cycleCounter) {
4858 if (bgpData[index] != data)
4859 doCgbBgColorChange(index, data, cycleCounter);
4862 - void cgbSpColorChange(unsigned index, const unsigned data, const unsigned long cycleCounter) {
4863 + void cgbSpColorChange(unsigned index, const unsigned data, const unsigned cycleCounter) {
4864 if (objpData[index] != data)
4865 doCgbSpColorChange(index, data, cycleCounter);
4868 - unsigned cgbBgColorRead(const unsigned index, const unsigned long cycleCounter) {
4869 + unsigned cgbBgColorRead(const unsigned index, const unsigned cycleCounter) {
4870 return ppu.cgb() & cgbpAccessible(cycleCounter) ? bgpData[index] : 0xFF;
4873 - unsigned cgbSpColorRead(const unsigned index, const unsigned long cycleCounter) {
4874 + unsigned cgbSpColorRead(const unsigned index, const unsigned cycleCounter) {
4875 return ppu.cgb() & cgbpAccessible(cycleCounter) ? objpData[index] : 0xFF;
4878 - void updateScreen(bool blanklcd, unsigned long cc);
4879 - void resetCc(unsigned long oldCC, unsigned long newCc);
4880 - void speedChange(unsigned long cycleCounter);
4881 - bool vramAccessible(unsigned long cycleCounter);
4882 - bool oamReadable(unsigned long cycleCounter);
4883 - bool oamWritable(unsigned long cycleCounter);
4884 - void wxChange(unsigned newValue, unsigned long cycleCounter);
4885 - void wyChange(unsigned newValue, unsigned long cycleCounter);
4886 - void oamChange(unsigned long cycleCounter);
4887 - void oamChange(const unsigned char *oamram, unsigned long cycleCounter);
4888 - void scxChange(unsigned newScx, unsigned long cycleCounter);
4889 - void scyChange(unsigned newValue, unsigned long cycleCounter);
4890 + void updateScreen(bool blanklcd, unsigned cc, uint_least32_t* vbuffer, unsigned vpitch);
4891 + void resetCc(unsigned oldCC, unsigned newCc);
4892 + void speedChange(unsigned cycleCounter);
4893 + bool vramAccessible(unsigned cycleCounter);
4894 + bool oamReadable(unsigned cycleCounter);
4895 + bool oamWritable(unsigned cycleCounter);
4896 + void wxChange(unsigned newValue, unsigned cycleCounter);
4897 + void wyChange(unsigned newValue, unsigned cycleCounter);
4898 + void oamChange(unsigned cycleCounter);
4899 + void oamChange(const unsigned char *oamram, unsigned cycleCounter);
4900 + void scxChange(unsigned newScx, unsigned cycleCounter);
4901 + void scyChange(unsigned newValue, unsigned cycleCounter);
4903 - void vramChange(const unsigned long cycleCounter) { update(cycleCounter); }
4904 + void vramChange(const unsigned cycleCounter) { update(cycleCounter); }
4906 - unsigned getStat(unsigned lycReg, unsigned long cycleCounter);
4907 + unsigned getStat(unsigned lycReg, unsigned cycleCounter);
4909 - unsigned getLyReg(const unsigned long cycleCounter) {
4910 + unsigned getLyReg(const unsigned cycleCounter) {
4911 unsigned lyReg = 0;
4913 if (ppu.lcdc() & 0x80) {
4914 @@ -236,17 +252,17 @@ public:
4915 return lyReg;
4918 - unsigned long nextMode1IrqTime() const { return eventTimes_(MODE1_IRQ); }
4919 + unsigned nextMode1IrqTime() const { return eventTimes_(MODE1_IRQ); }
4921 - void lcdcChange(unsigned data, unsigned long cycleCounter);
4922 - void lcdstatChange(unsigned data, unsigned long cycleCounter);
4923 - void lycRegChange(unsigned data, unsigned long cycleCounter);
4924 + void lcdcChange(unsigned data, unsigned cycleCounter);
4925 + void lcdstatChange(unsigned data, unsigned cycleCounter);
4926 + void lycRegChange(unsigned data, unsigned cycleCounter);
4928 - void enableHdma(unsigned long cycleCounter);
4929 - void disableHdma(unsigned long cycleCounter);
4930 + void enableHdma(unsigned cycleCounter);
4931 + void disableHdma(unsigned cycleCounter);
4932 bool hdmaIsEnabled() const { return eventTimes_(HDMA_REQ) != DISABLED_TIME; }
4934 - void update(unsigned long cycleCounter);
4935 + void update(unsigned cycleCounter);
4937 bool isCgb() const { return ppu.cgb(); }
4938 bool isDoubleSpeed() const { return ppu.lyCounter().isDoubleSpeed(); }
4939 diff --git a/libgambatte/src/video/ly_counter.cpp b/libgambatte/src/video/ly_counter.cpp
4940 index 4cb3918..6046852 100644
4941 --- a/libgambatte/src/video/ly_counter.cpp
4942 +++ b/libgambatte/src/video/ly_counter.cpp
4943 @@ -19,6 +19,10 @@
4944 #include "ly_counter.h"
4945 #include "../savestate.h"
4948 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
4949 +// - Make it rerecording-friendly.
4951 namespace gambatte {
4953 LyCounter::LyCounter()
4954 @@ -37,8 +41,8 @@ void LyCounter::doEvent() {
4955 time_ = time_ + lineTime_;
4958 -unsigned long LyCounter::nextLineCycle(const unsigned lineCycle, const unsigned long cycleCounter) const {
4959 - unsigned long tmp = time_ + (lineCycle << ds);
4960 +unsigned LyCounter::nextLineCycle(const unsigned lineCycle, const unsigned cycleCounter) const {
4961 + unsigned tmp = time_ + (lineCycle << ds);
4963 if (tmp - cycleCounter > lineTime_)
4964 tmp -= lineTime_;
4965 @@ -46,8 +50,8 @@ unsigned long LyCounter::nextLineCycle(const unsigned lineCycle, const unsigned
4966 return tmp;
4969 -unsigned long LyCounter::nextFrameCycle(const unsigned long frameCycle, const unsigned long cycleCounter) const {
4970 - unsigned long tmp = time_ + (((153U - ly()) * 456U + frameCycle) << ds);
4971 +unsigned LyCounter::nextFrameCycle(const unsigned frameCycle, const unsigned cycleCounter) const {
4972 + unsigned tmp = time_ + (((153U - ly()) * 456U + frameCycle) << ds);
4974 if (tmp - cycleCounter > 70224U << ds)
4975 tmp -= 70224U << ds;
4976 @@ -55,7 +59,7 @@ unsigned long LyCounter::nextFrameCycle(const unsigned long frameCycle, const un
4977 return tmp;
4980 -void LyCounter::reset(const unsigned long videoCycles, const unsigned long lastUpdate) {
4981 +void LyCounter::reset(const unsigned videoCycles, const unsigned lastUpdate) {
4982 ly_ = videoCycles / 456;
4983 time_ = lastUpdate + ((456 - (videoCycles - ly_ * 456ul)) << isDoubleSpeed());
4985 diff --git a/libgambatte/src/video/ly_counter.h b/libgambatte/src/video/ly_counter.h
4986 index d376540..0af06cc 100644
4987 --- a/libgambatte/src/video/ly_counter.h
4988 +++ b/libgambatte/src/video/ly_counter.h
4989 @@ -18,13 +18,18 @@
4990 ***************************************************************************/
4991 #ifndef LY_COUNTER_H
4992 #define LY_COUNTER_H
4993 +#include "../loadsave.h"
4996 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
4997 +// - Make it rerecording-friendly.
4999 namespace gambatte {
5001 struct SaveState;
5003 class LyCounter {
5004 - unsigned long time_;
5005 + unsigned time_;
5006 unsigned short lineTime_;
5007 unsigned char ly_;
5008 bool ds;
5009 @@ -34,21 +39,28 @@ public:
5010 void doEvent();
5011 bool isDoubleSpeed() const { return ds; }
5013 - unsigned long frameCycles(const unsigned long cc) const {
5014 + unsigned frameCycles(const unsigned cc) const {
5015 return ly_ * 456ul + lineCycles(cc);
5018 - unsigned lineCycles(const unsigned long cc) const {
5019 + unsigned lineCycles(const unsigned cc) const {
5020 return 456u - ((time_ - cc) >> isDoubleSpeed());
5023 unsigned lineTime() const { return lineTime_; }
5024 unsigned ly() const { return ly_; }
5025 - unsigned long nextLineCycle(unsigned lineCycle, unsigned long cycleCounter) const;
5026 - unsigned long nextFrameCycle(unsigned long frameCycle, unsigned long cycleCounter) const;
5027 - void reset(unsigned long videoCycles, unsigned long lastUpdate);
5028 + unsigned nextLineCycle(unsigned lineCycle, unsigned cycleCounter) const;
5029 + unsigned nextFrameCycle(unsigned frameCycle, unsigned cycleCounter) const;
5030 + void reset(unsigned videoCycles, unsigned lastUpdate);
5031 void setDoubleSpeed(bool ds_in);
5032 - unsigned long time() const { return time_; }
5033 + unsigned time() const { return time_; }
5035 + void loadOrSave(loadsave& state) {
5036 + state(time_);
5037 + state(lineTime_);
5038 + state(ly_);
5039 + state(ds);
5044 diff --git a/libgambatte/src/video/lyc_irq.cpp b/libgambatte/src/video/lyc_irq.cpp
5045 index a16cb81..d840dc0 100644
5046 --- a/libgambatte/src/video/lyc_irq.cpp
5047 +++ b/libgambatte/src/video/lyc_irq.cpp
5048 @@ -22,6 +22,10 @@
5049 #include "savestate.h"
5050 #include <algorithm>
5053 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
5054 +// - Make it rerecording-friendly.
5056 namespace gambatte {
5058 LycIrq::LycIrq() :
5059 @@ -34,14 +38,14 @@ LycIrq::LycIrq() :
5063 -static unsigned long schedule(const unsigned statReg, const unsigned lycReg, const LyCounter &lyCounter, const unsigned long cc) {
5064 +static unsigned schedule(const unsigned statReg, const unsigned lycReg, const LyCounter &lyCounter, const unsigned cc) {
5065 return (statReg & 0x40) && lycReg < 154
5066 ? lyCounter.nextFrameCycle(lycReg ? lycReg * 456 : 153 * 456 + 8, cc)
5067 - : static_cast<unsigned long>(DISABLED_TIME);
5068 + : static_cast<unsigned>(DISABLED_TIME);
5071 -void LycIrq::regChange(const unsigned statReg, const unsigned lycReg, const LyCounter &lyCounter, const unsigned long cc) {
5072 - const unsigned long timeSrc = schedule(statReg, lycReg, lyCounter, cc);
5073 +void LycIrq::regChange(const unsigned statReg, const unsigned lycReg, const LyCounter &lyCounter, const unsigned cc) {
5074 + const unsigned timeSrc = schedule(statReg, lycReg, lyCounter, cc);
5075 statRegSrc_ = statReg;
5076 lycRegSrc_ = lycReg;
5077 time_ = std::min(time_, timeSrc);
5078 @@ -89,7 +93,7 @@ void LycIrq::saveState(SaveState &state) const {
5079 state.ppu.lyc = lycReg_;
5082 -void LycIrq::reschedule(const LyCounter & lyCounter, const unsigned long cc) {
5083 +void LycIrq::reschedule(const LyCounter & lyCounter, const unsigned cc) {
5084 time_ = std::min(schedule(statReg_ , lycReg_ , lyCounter, cc),
5085 schedule(statRegSrc_, lycRegSrc_, lyCounter, cc));
5087 diff --git a/libgambatte/src/video/lyc_irq.h b/libgambatte/src/video/lyc_irq.h
5088 index 18607b9..97f4a8e 100644
5089 --- a/libgambatte/src/video/lyc_irq.h
5090 +++ b/libgambatte/src/video/lyc_irq.h
5091 @@ -19,20 +19,26 @@
5092 #ifndef VIDEO_LYC_IRQ_H
5093 #define VIDEO_LYC_IRQ_H
5096 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
5097 +// - Make it rerecording-friendly.
5099 +#include "../loadsave.h"
5101 namespace gambatte {
5103 struct SaveState;
5104 class LyCounter;
5106 class LycIrq {
5107 - unsigned long time_;
5108 + unsigned time_;
5109 unsigned char lycRegSrc_;
5110 unsigned char statRegSrc_;
5111 unsigned char lycReg_;
5112 unsigned char statReg_;
5113 bool cgb_;
5115 - void regChange(unsigned statReg, unsigned lycReg, const LyCounter &lyCounter, unsigned long cc);
5116 + void regChange(unsigned statReg, unsigned lycReg, const LyCounter &lyCounter, unsigned cc);
5118 public:
5119 LycIrq();
5120 @@ -40,18 +46,27 @@ public:
5121 unsigned lycReg() const { return lycRegSrc_; }
5122 void loadState(const SaveState &state);
5123 void saveState(SaveState &state) const;
5124 - unsigned long time() const { return time_; }
5125 + unsigned time() const { return time_; }
5126 void setCgb(const bool cgb) { cgb_ = cgb; }
5127 void lcdReset();
5128 - void reschedule(const LyCounter & lyCounter, unsigned long cc);
5129 + void reschedule(const LyCounter & lyCounter, unsigned cc);
5131 - void statRegChange(unsigned statReg, const LyCounter &lyCounter, unsigned long cc) {
5132 + void statRegChange(unsigned statReg, const LyCounter &lyCounter, unsigned cc) {
5133 regChange(statReg, lycRegSrc_, lyCounter, cc);
5136 - void lycRegChange(unsigned lycReg, const LyCounter &lyCounter, unsigned long cc) {
5137 + void lycRegChange(unsigned lycReg, const LyCounter &lyCounter, unsigned cc) {
5138 regChange(statRegSrc_, lycReg, lyCounter, cc);
5141 + void loadOrSave(loadsave& state) {
5142 + state(time_);
5143 + state(lycRegSrc_);
5144 + state(statRegSrc_);
5145 + state(lycReg_);
5146 + state(statReg_);
5147 + state(cgb_);
5152 diff --git a/libgambatte/src/video/next_m0_time.h b/libgambatte/src/video/next_m0_time.h
5153 index 148f471..1b0c338 100644
5154 --- a/libgambatte/src/video/next_m0_time.h
5155 +++ b/libgambatte/src/video/next_m0_time.h
5156 @@ -1,6 +1,12 @@
5157 #ifndef NEXT_M0_TIME_H_
5158 #define NEXT_M0_TIME_H_
5161 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
5162 +// - Make it rerecording-friendly.
5164 +#include "../loadsave.h"
5166 namespace gambatte {
5168 class NextM0Time {
5169 @@ -11,6 +17,10 @@ public:
5170 void predictNextM0Time(const class PPU &v);
5171 void invalidatePredictedNextM0Time() { predictedNextM0Time_ = 0; }
5172 unsigned predictedNextM0Time() const { return predictedNextM0Time_; }
5174 + void loadOrSave(loadsave& state) {
5175 + state(predictedNextM0Time_);
5180 diff --git a/libgambatte/src/video/ppu.cpp b/libgambatte/src/video/ppu.cpp
5181 index 42fb822..1985be9 100644
5182 --- a/libgambatte/src/video/ppu.cpp
5183 +++ b/libgambatte/src/video/ppu.cpp
5184 @@ -22,6 +22,10 @@
5185 #include <cstring>
5186 #include <cstddef>
5189 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
5190 +// - Make it rerecording-friendly.
5192 namespace {
5194 using namespace gambatte;
5195 @@ -427,7 +431,7 @@ namespace M3Loop {
5197 const unsigned attrib = p.spriteList[i].attrib;
5198 unsigned spword = p.spwordList[i];
5199 - const unsigned long *const spPalette = p.spPalette + (attrib >> 2 & 4);
5200 + const uint_least32_t *const spPalette = p.spPalette + (attrib >> 2 & 4);
5201 uint_least32_t *d = dst + pos;
5203 if (!(attrib & 0x80)) {
5204 @@ -543,7 +547,7 @@ namespace M3Loop {
5205 xpos += n;
5207 do {
5208 - const unsigned long *const bgPalette = p.bgPalette + (nattrib & 7) * 4;
5209 + const uint_least32_t *const bgPalette = p.bgPalette + (nattrib & 7) * 4;
5210 dst[0] = bgPalette[ ntileword & 0x0003 ];
5211 dst[1] = bgPalette[(ntileword & 0x000C) >> 2];
5212 dst[2] = bgPalette[(ntileword & 0x0030) >> 4];
5213 @@ -581,7 +585,7 @@ namespace M3Loop {
5214 uint_least32_t *const dst = dbufline + (xpos - 8);
5215 const unsigned tileword = p.ntileword;
5216 const unsigned attrib = p.nattrib;
5217 - const unsigned long *const bgPalette = p.bgPalette + (attrib & 7) * 4;
5218 + const uint_least32_t *const bgPalette = p.bgPalette + (attrib & 7) * 4;
5220 dst[0] = bgPalette[ tileword & 0x0003 ];
5221 dst[1] = bgPalette[(tileword & 0x000C) >> 2];
5222 @@ -617,7 +621,7 @@ namespace M3Loop {
5223 const unsigned char id = p.spriteList[i].oampos;
5224 const unsigned sattrib = p.spriteList[i].attrib;
5225 unsigned spword = p.spwordList[i];
5226 - const unsigned long *const spPalette = p.spPalette + (sattrib & 7) * 4;
5227 + const uint_least32_t *const spPalette = p.spPalette + (sattrib & 7) * 4;
5229 if (!((attrib | sattrib) & bgenmask)) {
5230 unsigned char *const idt = idtab + pos;
5231 @@ -772,7 +776,7 @@ namespace M3Loop {
5234 const unsigned twdata = tileword & ((p.lcdc & 1) | p.cgb) * 3;
5235 - unsigned long pixel = p.bgPalette[twdata + (p.attrib & 7) * 4];
5236 + uint_least32_t pixel = p.bgPalette[twdata + (p.attrib & 7) * 4];
5237 int i = static_cast<int>(p.nextSprite) - 1;
5239 if (i >= 0 && static_cast<int>(p.spriteList[i].spx) > xpos - 8) {
5240 @@ -831,8 +835,8 @@ namespace M3Loop {
5241 plotPixel(p);
5244 - static unsigned long nextM2Time(const PPUPriv &p) {
5245 - unsigned long nextm2 = p.lyCounter.isDoubleSpeed()
5246 + static unsigned nextM2Time(const PPUPriv &p) {
5247 + unsigned nextm2 = p.lyCounter.isDoubleSpeed()
5248 ? p.lyCounter.time() + (weMasterCheckPriorToLyIncLineCycle(true ) + M2_DS_OFFSET) * 2 - 456 * 2
5249 : p.lyCounter.time() + weMasterCheckPriorToLyIncLineCycle(p.cgb) - 456 ;
5251 @@ -845,11 +849,11 @@ namespace M3Loop {
5252 static void xpos168(PPUPriv &p) {
5253 p.lastM0Time = p.now - (p.cycles << p.lyCounter.isDoubleSpeed());
5255 - const unsigned long nextm2 = nextM2Time(p);
5256 + const unsigned nextm2 = nextM2Time(p);
5258 p.cycles = p.now >= nextm2
5259 - ? (static_cast<long>(p.now - nextm2) >> p.lyCounter.isDoubleSpeed())
5260 - : -(static_cast<long>(nextm2 - p.now) >> p.lyCounter.isDoubleSpeed());
5261 + ? (static_cast<signed>(p.now - nextm2) >> p.lyCounter.isDoubleSpeed())
5262 + : -(static_cast<signed>(nextm2 - p.now) >> p.lyCounter.isDoubleSpeed());
5264 nextCall(0, p.lyCounter.ly() == 143 ? M2::Ly0::f0_ : M2::LyNon0::f0_, p);
5266 @@ -1533,8 +1537,8 @@ std::size_t upperBound(const T a[], const K e) {
5268 struct CycleState {
5269 const PPUState *state;
5270 - long cycle;
5271 - operator long() const { return cycle; }
5272 + signed cycle;
5273 + operator signed() const { return cycle; }
5276 static const PPUState * decodeM3LoopState(const unsigned state) {
5277 @@ -1564,8 +1568,8 @@ static const PPUState * decodeM3LoopState(const unsigned state) {
5278 return 0;
5281 -static long cyclesUntilM0Upperbound(const PPUPriv &p) {
5282 - long cycles = 168 - p.xpos + 6;
5283 +static signed cyclesUntilM0Upperbound(const PPUPriv &p) {
5284 + signed cycles = 168 - p.xpos + 6;
5286 for (unsigned i = p.nextSprite; i < 10 && p.spriteList[i].spx < 168; ++i)
5287 cycles += 11;
5288 @@ -1605,17 +1609,17 @@ static void loadSpriteList(PPUPriv &p, const SaveState &ss) {
5290 void PPU::loadState(const SaveState &ss, const unsigned char *const oamram) {
5291 const PPUState *const m3loopState = decodeM3LoopState(ss.ppu.state);
5292 - const long videoCycles = std::min(ss.ppu.videoCycles, 70223UL);
5293 + const signed videoCycles = std::min(ss.ppu.videoCycles, 70223u);
5294 const bool ds = p_.cgb & ss.mem.ioamhram.get()[0x14D] >> 7;
5295 - const long vcycs = videoCycles - ds * M2_DS_OFFSET < 0
5296 + const signed vcycs = videoCycles - ds * M2_DS_OFFSET < 0
5297 ? videoCycles - ds * M2_DS_OFFSET + 70224
5298 : videoCycles - ds * M2_DS_OFFSET;
5299 - const long lineCycles = static_cast<unsigned long>(vcycs) % 456;
5300 + const signed lineCycles = static_cast<unsigned>(vcycs) % 456;
5302 p_.now = ss.cpu.cycleCounter;
5303 p_.lcdc = ss.mem.ioamhram.get()[0x140];
5304 p_.lyCounter.setDoubleSpeed(ds);
5305 - p_.lyCounter.reset(std::min(ss.ppu.videoCycles, 70223ul), ss.cpu.cycleCounter);
5306 + p_.lyCounter.reset(std::min(ss.ppu.videoCycles, 70223u), ss.cpu.cycleCounter);
5307 p_.spriteMapper.loadState(ss, oamram);
5308 p_.winYPos = ss.ppu.winYPos;
5309 p_.scy = ss.mem.ioamhram.get()[0x142];
5310 @@ -1639,10 +1643,10 @@ void PPU::loadState(const SaveState &ss, const unsigned char *const oamram) {
5311 loadSpriteList(p_, ss);
5313 if (m3loopState && videoCycles < 144 * 456L && p_.xpos < 168
5314 - && lineCycles + cyclesUntilM0Upperbound(p_) < static_cast<long>(weMasterCheckPriorToLyIncLineCycle(p_.cgb))) {
5315 + && lineCycles + cyclesUntilM0Upperbound(p_) < static_cast<signed>(weMasterCheckPriorToLyIncLineCycle(p_.cgb))) {
5316 p_.nextCallPtr = m3loopState;
5317 p_.cycles = -1;
5318 - } else if (vcycs < 143 * 456L + static_cast<long>(m3StartLineCycle(p_.cgb)) + MAX_M3START_CYCLES) {
5319 + } else if (vcycs < 143 * 456L + static_cast<signed>(m3StartLineCycle(p_.cgb)) + MAX_M3START_CYCLES) {
5320 const struct CycleState lineCycleStates[] = {
5321 { &M3Start::f0_, m3StartLineCycle(p_.cgb) },
5322 { &M3Start::f1_, m3StartLineCycle(p_.cgb) + MAX_M3START_CYCLES },
5323 @@ -1673,9 +1677,9 @@ void PPU::reset(const unsigned char *const oamram, const unsigned char *const vr
5324 p_.spriteMapper.reset(oamram, cgb);
5327 -void PPU::resetCc(const unsigned long oldCc, const unsigned long newCc) {
5328 - const unsigned long dec = oldCc - newCc;
5329 - const unsigned long videoCycles = p_.lcdc & 0x80 ? p_.lyCounter.frameCycles(p_.now) : 0;
5330 +void PPU::resetCc(const unsigned oldCc, const unsigned newCc) {
5331 + const unsigned dec = oldCc - newCc;
5332 + const unsigned videoCycles = p_.lcdc & 0x80 ? p_.lyCounter.frameCycles(p_.now) : 0;
5334 p_.now -= dec;
5335 p_.lastM0Time = p_.lastM0Time ? p_.lastM0Time - dec : p_.lastM0Time;
5336 @@ -1683,8 +1687,8 @@ void PPU::resetCc(const unsigned long oldCc, const unsigned long newCc) {
5337 p_.spriteMapper.resetCycleCounter(oldCc, newCc);
5340 -void PPU::speedChange(const unsigned long cycleCounter) {
5341 - const unsigned long videoCycles = p_.lcdc & 0x80 ? p_.lyCounter.frameCycles(p_.now) : 0;
5342 +void PPU::speedChange(const unsigned cycleCounter) {
5343 + const unsigned videoCycles = p_.lcdc & 0x80 ? p_.lyCounter.frameCycles(p_.now) : 0;
5345 p_.spriteMapper.preSpeedChange(cycleCounter);
5346 p_.lyCounter.setDoubleSpeed(!p_.lyCounter.isDoubleSpeed());
5347 @@ -1699,11 +1703,11 @@ void PPU::speedChange(const unsigned long cycleCounter) {
5351 -unsigned long PPU::predictedNextXposTime(const unsigned xpos) const {
5352 +unsigned PPU::predictedNextXposTime(const unsigned xpos) const {
5353 return p_.now + (p_.nextCallPtr->predictCyclesUntilXpos_f(p_, xpos, -p_.cycles) << p_.lyCounter.isDoubleSpeed());
5356 -void PPU::setLcdc(const unsigned lcdc, const unsigned long cc) {
5357 +void PPU::setLcdc(const unsigned lcdc, const unsigned cc) {
5358 if ((p_.lcdc ^ lcdc) & lcdc & 0x80) {
5359 p_.now = cc;
5360 p_.lastM0Time = 0;
5361 @@ -1733,7 +1737,7 @@ void PPU::setLcdc(const unsigned lcdc, const unsigned long cc) {
5362 p_.lcdc = lcdc;
5365 -void PPU::update(const unsigned long cc) {
5366 +void PPU::update(const unsigned cc) {
5367 const int cycles = (cc - p_.now) >> p_.lyCounter.isDoubleSpeed();
5369 p_.now += cycles << p_.lyCounter.isDoubleSpeed();
5370 @@ -1745,4 +1749,69 @@ void PPU::update(const unsigned long cc) {
5374 +void PPUPriv::loadOrSave(loadsave& state)
5376 + state(bgPalette, 32);
5377 + state(spPalette, 32);
5379 + state.startEnumeration();
5380 + state.enumerate<const PPUState*>(nextCallPtr, NULL, 0);
5381 + state.enumerate<const PPUState*>(nextCallPtr, &M2::Ly0::f0_, 1);
5382 + state.enumerate<const PPUState*>(nextCallPtr, &M2::LyNon0::f0_, 2);
5383 + state.enumerate<const PPUState*>(nextCallPtr, &M2::LyNon0::f1_, 3);
5384 + state.enumerate<const PPUState*>(nextCallPtr, &M3Start::f0_, 4);
5385 + state.enumerate<const PPUState*>(nextCallPtr, &M3Start::f1_, 5);
5386 + state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::Tile::f0_, 6);
5387 + state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::Tile::f1_, 7);
5388 + state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::Tile::f2_, 8);
5389 + state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::Tile::f3_, 9);
5390 + state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::Tile::f4_, 10);
5391 + state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::Tile::f5_, 11);
5392 + state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::LoadSprites::f0_, 12);
5393 + state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::LoadSprites::f1_, 13);
5394 + state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::LoadSprites::f2_, 14);
5395 + state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::LoadSprites::f3_, 15);
5396 + state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::LoadSprites::f4_, 16);
5397 + state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::LoadSprites::f5_, 17);
5398 + state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::StartWindowDraw::f0_, 18);
5399 + state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::StartWindowDraw::f1_, 19);
5400 + state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::StartWindowDraw::f2_, 20);
5401 + state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::StartWindowDraw::f3_, 21);
5402 + state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::StartWindowDraw::f4_, 22);
5403 + state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::StartWindowDraw::f5_, 23);
5404 + state.endEnumeration();
5406 + state(now);
5407 + state(lastM0Time);
5408 + state(cycles);
5409 + state(tileword);
5410 + state(ntileword);
5411 + lyCounter.loadOrSave(state);
5412 + spriteMapper.loadOrSave(state);
5413 + framebuf.loadOrSave(state);
5415 + for(size_t i = 0; i < 11; i++)
5416 + spriteList[i].loadOrSave(state);
5417 + state(spwordList, 11);
5418 + state(lcdc);
5419 + state(scy);
5420 + state(scx);
5421 + state(wy);
5422 + state(wy2);
5423 + state(wx);
5424 + state(winDrawState);
5425 + state(wscx);
5426 + state(winYPos);
5427 + state(reg0);
5428 + state(reg1);
5429 + state(attrib);
5430 + state(nattrib);
5431 + state(nextSprite);
5432 + state(currentSprite);
5433 + state(xpos);
5434 + state(endx);
5435 + state(cgb);
5436 + state(weMaster);
5440 diff --git a/libgambatte/src/video/ppu.h b/libgambatte/src/video/ppu.h
5441 index b3d9190..df9a6c1 100644
5442 --- a/libgambatte/src/video/ppu.h
5443 +++ b/libgambatte/src/video/ppu.h
5444 @@ -19,26 +19,45 @@
5445 #ifndef PPU_H
5446 #define PPU_H
5449 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
5450 +// - Make it rerecording-friendly.
5452 +#include <cstring>
5453 #include "video/ly_counter.h"
5454 #include "video/sprite_mapper.h"
5455 #include "gbint.h"
5456 +#include "../loadsave.h"
5458 namespace gambatte {
5460 class PPUFrameBuf {
5461 - uint_least32_t *buf_;
5462 + mutable uint_least32_t buf_[160*144];
5463 uint_least32_t *fbline_;
5464 - int pitch_;
5466 static uint_least32_t * nullfbline() { static uint_least32_t nullfbline_[160]; return nullfbline_; }
5468 public:
5469 - PPUFrameBuf() : buf_(0), fbline_(nullfbline()), pitch_(0) {}
5470 + PPUFrameBuf() : fbline_(nullfbline()) { memset(buf_, 0, sizeof(buf_)); }
5471 uint_least32_t * fb() const { return buf_; }
5472 uint_least32_t * fbline() const { return fbline_; }
5473 - int pitch() const { return pitch_; }
5474 - void setBuf(uint_least32_t *const buf, const int pitch) { buf_ = buf; pitch_ = pitch; fbline_ = nullfbline(); }
5475 - void setFbline(const unsigned ly) { fbline_ = buf_ ? buf_ + static_cast<long>(ly) * static_cast<long>(pitch_) : nullfbline(); }
5476 + int pitch() const { return 160; }
5477 + void setFbline(const unsigned ly) { fbline_ = buf_ ? buf_ + ly * 160ULL : nullfbline(); }
5478 + void blit(uint_least32_t *const buf, const int pitch) const {
5479 + for(unsigned i = 0; i < 144; i++)
5480 + memcpy(buf + i * static_cast<signed>(pitch), buf_ + i * 160, 160 * sizeof(buf[0]));
5482 + void loadOrSave(loadsave& state) {
5483 + state(buf_, 160*144);
5484 + bool var = (fbline_ != nullfbline());
5485 + state(var);
5486 + if(var) {
5487 + unsigned x = fbline_ - buf_;
5488 + state(x);
5489 + fbline_ = buf_ + x;
5490 + } else
5491 + fbline_ = nullfbline();
5495 struct PPUState {
5496 @@ -49,26 +68,33 @@ struct PPUState {
5498 // The PPU loop accesses a lot of state at once, so it's difficult to split this up much beyond grouping stuff into smaller structs.
5499 struct PPUPriv {
5500 - unsigned long bgPalette[8 * 4];
5501 - unsigned long spPalette[8 * 4];
5502 - struct Sprite { unsigned char spx, oampos, line, attrib; } spriteList[11];
5503 - unsigned short spwordList[11];
5504 - unsigned char nextSprite;
5505 - unsigned char currentSprite;
5506 + uint_least32_t bgPalette[8 * 4];
5507 + uint_least32_t spPalette[8 * 4];
5509 const unsigned char *vram;
5510 const PPUState *nextCallPtr;
5512 - unsigned long now;
5513 - unsigned long lastM0Time;
5514 - long cycles;
5515 + unsigned now;
5516 + unsigned lastM0Time;
5517 + signed cycles;
5519 unsigned tileword;
5520 unsigned ntileword;
5522 - SpriteMapper spriteMapper;
5523 LyCounter lyCounter;
5524 + SpriteMapper spriteMapper;
5525 PPUFrameBuf framebuf;
5527 + struct Sprite {
5528 + unsigned char spx, oampos, line, attrib;
5529 + void loadOrSave(loadsave& state) {
5530 + state(spx);
5531 + state(oampos);
5532 + state(line);
5533 + state(attrib);
5535 + } spriteList[11];
5536 + unsigned short spwordList[11];
5538 unsigned char lcdc;
5539 unsigned char scy;
5540 @@ -83,6 +109,8 @@ struct PPUPriv {
5541 unsigned char reg1;
5542 unsigned char attrib;
5543 unsigned char nattrib;
5544 + unsigned char nextSprite;
5545 + unsigned char currentSprite;
5546 unsigned char xpos;
5547 unsigned char endx;
5549 @@ -90,6 +118,7 @@ struct PPUPriv {
5550 bool weMaster;
5552 PPUPriv(NextM0Time &nextM0Time, const unsigned char *oamram, const unsigned char *vram);
5553 + void loadOrSave(loadsave& state);
5556 class PPU {
5557 @@ -100,34 +129,38 @@ public:
5561 - unsigned long * bgPalette() { return p_.bgPalette; }
5562 + uint_least32_t * bgPalette() { return p_.bgPalette; }
5563 bool cgb() const { return p_.cgb; }
5564 void doLyCountEvent() { p_.lyCounter.doEvent(); }
5565 - unsigned long doSpriteMapEvent(unsigned long time) { return p_.spriteMapper.doEvent(time); }
5566 + unsigned doSpriteMapEvent(unsigned time) { return p_.spriteMapper.doEvent(time); }
5567 const PPUFrameBuf & frameBuf() const { return p_.framebuf; }
5568 - bool inactivePeriodAfterDisplayEnable(unsigned long cc) const { return p_.spriteMapper.inactivePeriodAfterDisplayEnable(cc); }
5569 - unsigned long lastM0Time() const { return p_.lastM0Time; }
5570 + bool inactivePeriodAfterDisplayEnable(unsigned cc) const { return p_.spriteMapper.inactivePeriodAfterDisplayEnable(cc); }
5571 + unsigned lastM0Time() const { return p_.lastM0Time; }
5572 unsigned lcdc() const { return p_.lcdc; }
5573 void loadState(const SaveState &state, const unsigned char *oamram);
5574 const LyCounter & lyCounter() const { return p_.lyCounter; }
5575 - unsigned long now() const { return p_.now; }
5576 - void oamChange(unsigned long cc) { p_.spriteMapper.oamChange(cc); }
5577 - void oamChange(const unsigned char *oamram, unsigned long cc) { p_.spriteMapper.oamChange(oamram, cc); }
5578 - unsigned long predictedNextXposTime(unsigned xpos) const;
5579 + unsigned now() const { return p_.now; }
5580 + void oamChange(unsigned cc) { p_.spriteMapper.oamChange(cc); }
5581 + void oamChange(const unsigned char *oamram, unsigned cc) { p_.spriteMapper.oamChange(oamram, cc); }
5582 + unsigned predictedNextXposTime(unsigned xpos) const;
5583 void reset(const unsigned char *oamram, const unsigned char *vram, bool cgb);
5584 - void resetCc(unsigned long oldCc, unsigned long newCc);
5585 + void resetCc(unsigned oldCc, unsigned newCc);
5586 void saveState(SaveState &ss) const;
5587 - void setFrameBuf(uint_least32_t *buf, unsigned pitch) { p_.framebuf.setBuf(buf, pitch); }
5588 - void setLcdc(unsigned lcdc, unsigned long cc);
5589 + void flipDisplay(uint_least32_t *buf, unsigned pitch) { p_.framebuf.blit(buf, pitch); }
5590 + void setLcdc(unsigned lcdc, unsigned cc);
5591 void setScx(const unsigned scx) { p_.scx = scx; }
5592 void setScy(const unsigned scy) { p_.scy = scy; }
5593 void setStatePtrs(SaveState &ss) { p_.spriteMapper.setStatePtrs(ss); }
5594 void setWx(const unsigned wx) { p_.wx = wx; }
5595 void setWy(const unsigned wy) { p_.wy = wy; }
5596 void updateWy2() { p_.wy2 = p_.wy; }
5597 - void speedChange(unsigned long cycleCounter);
5598 - unsigned long * spPalette() { return p_.spPalette; }
5599 - void update(unsigned long cc);
5600 + void speedChange(unsigned cycleCounter);
5601 + uint_least32_t * spPalette() { return p_.spPalette; }
5602 + void update(unsigned cc);
5604 + void loadOrSave(loadsave& state) {
5605 + p_.loadOrSave(state);
5610 diff --git a/libgambatte/src/video/sprite_mapper.cpp b/libgambatte/src/video/sprite_mapper.cpp
5611 index 81dd1fd..11c76ad 100644
5612 --- a/libgambatte/src/video/sprite_mapper.cpp
5613 +++ b/libgambatte/src/video/sprite_mapper.cpp
5614 @@ -23,6 +23,10 @@
5615 #include <cstring>
5616 #include <algorithm>
5619 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
5620 +// - Make it rerecording-friendly.
5622 namespace gambatte {
5624 SpriteMapper::OamReader::OamReader(const LyCounter &lyCounter, const unsigned char *oamram)
5625 @@ -47,7 +51,7 @@ void SpriteMapper::OamReader::reset(const unsigned char *const oamram, const boo
5629 -static unsigned toPosCycles(const unsigned long cc, const LyCounter &lyCounter) {
5630 +static unsigned toPosCycles(const unsigned cc, const LyCounter &lyCounter) {
5631 unsigned lc = lyCounter.lineCycles(cc) + 3 - lyCounter.isDoubleSpeed() * 3u;
5633 if (lc >= 456)
5634 @@ -56,7 +60,7 @@ static unsigned toPosCycles(const unsigned long cc, const LyCounter &lyCounter)
5635 return lc;
5638 -void SpriteMapper::OamReader::update(const unsigned long cc) {
5639 +void SpriteMapper::OamReader::update(const unsigned cc) {
5640 if (cc > lu) {
5641 if (changed()) {
5642 const unsigned lulc = toPosCycles(lu, lyCounter);
5643 @@ -100,7 +104,7 @@ void SpriteMapper::OamReader::update(const unsigned long cc) {
5647 -void SpriteMapper::OamReader::change(const unsigned long cc) {
5648 +void SpriteMapper::OamReader::change(const unsigned cc) {
5649 update(cc);
5650 lastChange = std::min(toPosCycles(lu, lyCounter), 80u);
5652 @@ -117,7 +121,7 @@ void SpriteMapper::OamReader::loadState(const SaveState &ss, const unsigned char
5653 change(lu);
5656 -void SpriteMapper::OamReader::enableDisplay(const unsigned long cc) {
5657 +void SpriteMapper::OamReader::enableDisplay(const unsigned cc) {
5658 std::memset(buf, 0x00, sizeof(buf));
5659 std::fill(szbuf, szbuf + 40, false);
5660 lu = cc + (80 << lyCounter.isDoubleSpeed());
5661 @@ -130,6 +134,7 @@ SpriteMapper::SpriteMapper(NextM0Time &nextM0Time,
5662 nextM0Time_(nextM0Time),
5663 oamReader(lyCounter, oamram)
5665 + memset(spritemap, 0, sizeof(spritemap));
5666 clearMap();
5669 @@ -173,10 +178,10 @@ void SpriteMapper::sortLine(const unsigned ly) const {
5670 insertionSort(spritemap + ly * 10, spritemap + ly * 10 + num[ly], SpxLess(posbuf()));
5673 -unsigned long SpriteMapper::doEvent(const unsigned long time) {
5674 +unsigned SpriteMapper::doEvent(const unsigned time) {
5675 oamReader.update(time);
5676 mapSprites();
5677 - return oamReader.changed() ? time + oamReader.lyCounter.lineTime() : static_cast<unsigned long>(DISABLED_TIME);
5678 + return oamReader.changed() ? time + oamReader.lyCounter.lineTime() : static_cast<unsigned>(DISABLED_TIME);
5682 diff --git a/libgambatte/src/video/sprite_mapper.h b/libgambatte/src/video/sprite_mapper.h
5683 index 22bf619..2358d2d 100644
5684 --- a/libgambatte/src/video/sprite_mapper.h
5685 +++ b/libgambatte/src/video/sprite_mapper.h
5686 @@ -19,8 +19,13 @@
5687 #ifndef SPRITE_MAPPER_H
5688 #define SPRITE_MAPPER_H
5691 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
5692 +// - Make it rerecording-friendly.
5694 #include "ly_counter.h"
5695 #include "../savestate.h"
5696 +#include "../loadsave.h"
5698 namespace gambatte {
5699 class NextM0Time;
5700 @@ -35,7 +40,7 @@ class SpriteMapper {
5702 private:
5703 const unsigned char *oamram;
5704 - unsigned long lu;
5705 + unsigned lu;
5706 unsigned char lastChange;
5707 bool largeSpritesSrc;
5708 bool cgb_;
5709 @@ -43,20 +48,30 @@ class SpriteMapper {
5710 public:
5711 OamReader(const LyCounter &lyCounter, const unsigned char *oamram);
5712 void reset(const unsigned char *oamram, bool cgb);
5713 - void change(unsigned long cc);
5714 - void change(const unsigned char *oamram, unsigned long cc) { change(cc); this->oamram = oamram; }
5715 + void change(unsigned cc);
5716 + void change(const unsigned char *oamram, unsigned cc) { change(cc); this->oamram = oamram; }
5717 bool changed() const { return lastChange != 0xFF; }
5718 bool largeSprites(unsigned spNr) const { return szbuf[spNr]; }
5719 const unsigned char *oam() const { return oamram; }
5720 - void resetCycleCounter(const unsigned long oldCc, const unsigned long newCc) { lu -= oldCc - newCc; }
5721 + void resetCycleCounter(const unsigned oldCc, const unsigned newCc) { lu -= oldCc - newCc; }
5722 void setLargeSpritesSrc(const bool src) { largeSpritesSrc = src; }
5723 - void update(unsigned long cc);
5724 + void update(unsigned cc);
5725 const unsigned char *spritePosBuf() const { return buf; }
5726 void setStatePtrs(SaveState &state);
5727 - void enableDisplay(unsigned long cc);
5728 + void enableDisplay(unsigned cc);
5729 void saveState(SaveState &state) const { state.ppu.enableDisplayM0Time = lu; }
5730 void loadState(const SaveState &ss, const unsigned char *oamram);
5731 - bool inactivePeriodAfterDisplayEnable(const unsigned long cc) const { return cc < lu; }
5732 + bool inactivePeriodAfterDisplayEnable(const unsigned cc) const { return cc < lu; }
5734 + void loadOrSave(loadsave& state) {
5735 + state(buf, 80);
5736 + for(unsigned i = 0; i < 40; i++)
5737 + state(szbuf[i]);
5738 + state(lu);
5739 + state(lastChange);
5740 + state(largeSpritesSrc);
5741 + state(cgb_);
5745 enum { NEED_SORTING_MASK = 0x80 };
5746 @@ -89,22 +104,22 @@ public:
5747 const LyCounter &lyCounter,
5748 const unsigned char *oamram_in);
5749 void reset(const unsigned char *oamram, bool cgb);
5750 - unsigned long doEvent(unsigned long time);
5751 + unsigned doEvent(unsigned time);
5752 bool largeSprites(unsigned spNr) const { return oamReader.largeSprites(spNr); }
5753 unsigned numSprites(const unsigned ly) const { return num[ly] & ~NEED_SORTING_MASK; }
5754 - void oamChange(unsigned long cc) { oamReader.change(cc); }
5755 - void oamChange(const unsigned char *oamram, unsigned long cc) { oamReader.change(oamram, cc); }
5756 + void oamChange(unsigned cc) { oamReader.change(cc); }
5757 + void oamChange(const unsigned char *oamram, unsigned cc) { oamReader.change(oamram, cc); }
5758 const unsigned char *oamram() const { return oamReader.oam(); }
5759 const unsigned char *posbuf() const { return oamReader.spritePosBuf(); }
5760 - void preSpeedChange(const unsigned long cc) { oamReader.update(cc); }
5761 - void postSpeedChange(const unsigned long cc) { oamReader.change(cc); }
5762 + void preSpeedChange(const unsigned cc) { oamReader.update(cc); }
5763 + void postSpeedChange(const unsigned cc) { oamReader.change(cc); }
5765 - void resetCycleCounter(const unsigned long oldCc, const unsigned long newCc) {
5766 + void resetCycleCounter(const unsigned oldCc, const unsigned newCc) {
5767 oamReader.update(oldCc);
5768 oamReader.resetCycleCounter(oldCc, newCc);
5771 - static unsigned long schedule(const LyCounter &lyCounter, const unsigned long cycleCounter) {
5772 + static unsigned schedule(const LyCounter &lyCounter, const unsigned cycleCounter) {
5773 return lyCounter.nextLineCycle(80, cycleCounter);
5776 @@ -118,10 +133,16 @@ public:
5779 void setStatePtrs(SaveState &state) { oamReader.setStatePtrs(state); }
5780 - void enableDisplay(unsigned long cc) { oamReader.enableDisplay(cc); }
5781 + void enableDisplay(unsigned cc) { oamReader.enableDisplay(cc); }
5782 void saveState(SaveState &state) const { oamReader.saveState(state); }
5783 void loadState(const SaveState &state, const unsigned char *const oamram) { oamReader.loadState(state, oamram); mapSprites(); }
5784 - bool inactivePeriodAfterDisplayEnable(unsigned long cc) const { return oamReader.inactivePeriodAfterDisplayEnable(cc); }
5785 + bool inactivePeriodAfterDisplayEnable(unsigned cc) const { return oamReader.inactivePeriodAfterDisplayEnable(cc); }
5787 + void loadOrSave(loadsave& state) {
5788 + state(spritemap, 1440);
5789 + state(num, 144);
5790 + oamReader.loadOrSave(state);
5796 1.8.5