Delay committing fullscreen until seeing window size change
[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;