Lua: Fix type confusion between signed and unsigned
[lsnes.git] / libgambatte-patches / svn320 / 0001-Changes-to-make-libgambatte-rerecording-friendly.patch
blobf9955ded4c12241ff5c79ceb9c6e9f8e3235a2a0
1 From 24eb71f288305085f52961b2c1bcb9273aebed49 Mon Sep 17 00:00:00 2001
2 From: Ilari Liusvaara <ilari.liusvaara@elisanet.fi>
3 Date: Thu, 12 Jul 2012 20:49:57 +0300
4 Subject: [PATCH] Changes to make libgambatte rerecording-friendly
6 ---
7 Makefile | 10 +
8 libgambatte/Makefile | 18 ++
9 libgambatte/include/gambatte.h | 68 ++++++-
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 | 129 +++++++++++--
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 | 155 +++++++++++++--
26 libgambatte/src/mem/cartridge.h | 36 +++-
27 libgambatte/src/mem/memptrs.cpp | 32 +++-
28 libgambatte/src/mem/memptrs.h | 11 +-
29 libgambatte/src/mem/rtc.cpp | 57 +++++-
30 libgambatte/src/mem/rtc.h | 11 +-
31 libgambatte/src/memory.cpp | 110 +++++++----
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 | 111 +++++++----
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 | 123 +++++++++---
66 libgambatte/src/video/ppu.h | 84 ++++++---
67 libgambatte/src/video/sprite_mapper.cpp | 17 +-
68 libgambatte/src/video/sprite_mapper.h | 53 ++++--
69 62 files changed, 2152 insertions(+), 603 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 b13e4be..1db8086 100644
118 --- a/libgambatte/include/gambatte.h
119 +++ b/libgambatte/include/gambatte.h
120 @@ -22,6 +22,11 @@
121 #include "inputgetter.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 @@ -44,7 +49,15 @@ public:
133 * @return 0 on success, negative value on failure.
135 int load(const std::string &romfile, unsigned flags = 0);
137 + /** Load ROM image.
139 + * @param image Raw ROM image data.
140 + * @param isize Size of raw ROM image data.
141 + * @param flags ORed combination of LoadFlags.
142 + * @return 0 on success, negative value on failure.
143 + */
144 + int load(const unsigned char* image, size_t isize, unsigned flags = 0);
146 /** Emulates until at least 'samples' stereo sound samples are produced in the supplied buffer,
147 * or until a video frame has been drawn.
149 @@ -66,7 +79,7 @@ public:
150 * @param samples in: number of stereo samples to produce, out: actual number of samples produced
151 * @return sample number at which the video frame was produced. -1 means no frame was produced.
153 - long runFor(gambatte::uint_least32_t *videoBuf, int pitch,
154 + signed runFor(gambatte::uint_least32_t *videoBuf, int pitch,
155 gambatte::uint_least32_t *soundBuf, unsigned &samples);
157 /** Reset to initial state.
158 @@ -113,7 +126,18 @@ public:
159 /** Loads emulator state from the file given by 'filepath'.
161 void loadState(const std::string &filepath);
164 + /** Save savestate to given buffer.
165 + */
166 + void saveState(std::vector<char>& data, const std::vector<char>& cmpdata);
167 + /** Save savestate to given buffer.
168 + */
169 + void saveState(std::vector<char>& data);
170 + /** Load savestate from given buffer.
171 + */
172 + void loadState(const std::vector<char>& data);
175 /** Selects which state slot to save state to or load state from.
176 * There are 10 such slots, numbered from 0 to 9 (periodically extended for all n).
178 @@ -134,10 +158,46 @@ public:
179 * @param codes Game Shark codes in format 01HHHHHH;01HHHHHH;... where H is [0-9]|[A-F]
181 void setGameShark(const std::string &codes);
184 + /** Set RTC base time.
185 + */
186 + void setRtcBase(time_t time);
188 + /** Get RTC base time.
189 + */
190 + time_t getRtcBase();
192 + /** Get pointer and size to Work RAM.
193 + * @return The pointer and size of Work RAM.
194 + */
195 + std::pair<unsigned char*, size_t> getWorkRam();
197 + /** Get pointer and size to Save RAM.
198 + * @return The pointer and size of Save RAM.
199 + */
200 + std::pair<unsigned char*, size_t> getSaveRam();
202 + /** Get pointer and size to I/O RAM.
203 + * @return The pointer and size of I/O RAM.
204 + */
205 + std::pair<unsigned char*, size_t> getIoRam();
207 + /** Get pointer and size to Video RAM.
208 + * @return The pointer and size of Video RAM.
209 + */
210 + std::pair<unsigned char*, size_t> getVideoRam();
212 + /** Function to get wall time. */
213 + void set_walltime_fn(time_t (*_walltime)());
215 + /** Get version. */
216 + static std::string version();
217 private:
218 + void preload_common();
219 + void postload_common(const unsigned flags);
220 struct Priv;
221 Priv *const p_;
222 + time_t (*walltime)();
224 void loadState(const std::string &filepath, bool osdMessage);
225 GB(const GB &);
226 diff --git a/libgambatte/src/bitmap_font.cpp b/libgambatte/src/bitmap_font.cpp
227 index 7ef835f..0e40bc4 100644
228 --- a/libgambatte/src/bitmap_font.cpp
229 +++ b/libgambatte/src/bitmap_font.cpp
230 @@ -68,6 +68,10 @@
231 gnome dot org.
235 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
236 +// - Make it rerecording-friendly.
238 #include "bitmap_font.h"
240 static const unsigned char n0_bits[] = { 0x68,
241 @@ -285,10 +289,10 @@ unsigned getWidth(const char *chars) {
243 namespace {
244 class Rgb32Fill {
245 - const unsigned long color;
246 + const uint_least32_t color;
248 public:
249 - explicit Rgb32Fill(unsigned long color) : color(color) {}
250 + explicit Rgb32Fill(uint_least32_t color) : color(color) {}
252 void operator()(gambatte::uint_least32_t *dest, unsigned /*pitch*/) const {
253 *dest = color;
254 @@ -296,7 +300,7 @@ public:
258 -void print(gambatte::uint_least32_t *dest, const unsigned pitch, const unsigned long color, const char *chars) {
259 +void print(gambatte::uint_least32_t *dest, const unsigned pitch, const uint_least32_t color, const char *chars) {
260 print(dest, pitch, Rgb32Fill(color), chars);
263 diff --git a/libgambatte/src/bitmap_font.h b/libgambatte/src/bitmap_font.h
264 index 35b29fa..34e3f6b 100644
265 --- a/libgambatte/src/bitmap_font.h
266 +++ b/libgambatte/src/bitmap_font.h
267 @@ -19,6 +19,10 @@
268 #ifndef BITMAP_FONT_H
269 #define BITMAP_FONT_H
272 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
273 +// - Make it rerecording-friendly.
275 #include "gbint.h"
277 namespace bitmapfont {
278 @@ -42,7 +46,7 @@ unsigned getWidth(const char *chars);
279 template<class RandomAccessIterator, class Fill>
280 void print(RandomAccessIterator dest, unsigned pitch, Fill fill, const char *chars);
282 -void print(gambatte::uint_least32_t *dest, unsigned pitch, unsigned long color, const char *chars);
283 +void print(gambatte::uint_least32_t *dest, unsigned pitch, uint_least32_t color, const char *chars);
284 void utoa(unsigned u, char *a);
286 // --- INTERFACE END ---
287 diff --git a/libgambatte/src/cpu.cpp b/libgambatte/src/cpu.cpp
288 index 2b19eeb..9c63ddb 100644
289 --- a/libgambatte/src/cpu.cpp
290 +++ b/libgambatte/src/cpu.cpp
291 @@ -20,10 +20,14 @@
292 #include "memory.h"
293 #include "savestate.h"
296 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
297 +// - Make it rerecording-friendly.
299 namespace gambatte {
301 -CPU::CPU()
302 -: memory(Interrupter(SP, PC_)),
303 +CPU::CPU(time_t (**_getCurrentTime)())
304 +: memory(Interrupter(SP, PC_), _getCurrentTime),
305 cycleCounter_(0),
306 PC_(0x100),
307 SP(0xFFFE),
308 @@ -42,10 +46,10 @@ CPU::CPU()
312 -long CPU::runFor(const unsigned long cycles) {
313 +signed CPU::runFor(const unsigned cycles) {
314 process(cycles/* << memory.isDoubleSpeed()*/);
316 - const long csb = memory.cyclesSinceBlit(cycleCounter_);
317 + const signed csb = memory.cyclesSinceBlit(cycleCounter_);
319 if (cycleCounter_ & 0x80000000)
320 cycleCounter_ = memory.resetCounters(cycleCounter_);
321 @@ -124,6 +128,27 @@ void CPU::loadState(const SaveState &state) {
322 skip = state.cpu.skip;
325 +void CPU::loadOrSave(loadsave& state)
327 + memory.loadOrSave(state);
328 + state(cycleCounter_);
329 + state(PC_);
330 + state(SP);
331 + state(HF1);
332 + state(HF2);
333 + state(ZF);
334 + state(CF);
335 + state(A_);
336 + state(B);
337 + state(C);
338 + state(D);
339 + state(E);
340 + state(H);
341 + state(L);
342 + state(skip);
346 #define BC() ( B << 8 | C )
347 #define DE() ( D << 8 | E )
348 #define HL() ( H << 8 | L )
349 @@ -503,18 +528,18 @@ void CPU::loadState(const SaveState &state) {
350 PC_MOD(ret_var_h << 8 | ret_var_l); \
351 } while (0)
353 -void CPU::process(const unsigned long cycles) {
354 +void CPU::process(const unsigned cycles) {
355 memory.setEndtime(cycleCounter_, cycles);
357 unsigned char A = A_;
358 - unsigned long cycleCounter = cycleCounter_;
359 + unsigned cycleCounter = cycleCounter_;
361 while (memory.isActive()) {
362 unsigned short PC = PC_;
365 if (memory.halted()) {
366 if (cycleCounter < memory.nextEventTime()) {
367 - const unsigned long cycles = memory.nextEventTime() - cycleCounter;
368 + const unsigned cycles = memory.nextEventTime() - cycleCounter;
369 cycleCounter += cycles + (-cycles & 3);
371 } else while (cycleCounter < memory.nextEventTime()) {
372 @@ -612,7 +637,7 @@ void CPU::process(const unsigned long cycles) {
373 cycleCounter = memory.stop(cycleCounter);
375 if (cycleCounter < memory.nextEventTime()) {
376 - const unsigned long cycles = memory.nextEventTime() - cycleCounter;
377 + const unsigned cycles = memory.nextEventTime() - cycleCounter;
378 cycleCounter += cycles + (-cycles & 3);
381 @@ -1140,7 +1165,7 @@ void CPU::process(const unsigned long cycles) {
382 memory.halt();
384 if (cycleCounter < memory.nextEventTime()) {
385 - const unsigned long cycles = memory.nextEventTime() - cycleCounter;
386 + const unsigned cycles = memory.nextEventTime() - cycleCounter;
387 cycleCounter += cycles + (-cycles & 3);
390 diff --git a/libgambatte/src/cpu.h b/libgambatte/src/cpu.h
391 index 238e639..98f25c6 100644
392 --- a/libgambatte/src/cpu.h
393 +++ b/libgambatte/src/cpu.h
394 @@ -19,14 +19,19 @@
395 #ifndef CPU_H
396 #define CPU_H
399 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
400 +// - Make it rerecording-friendly.
402 #include "memory.h"
403 +#include "loadsave.h"
405 namespace gambatte {
407 class CPU {
408 Memory memory;
410 - unsigned long cycleCounter_;
411 + unsigned cycleCounter_;
413 unsigned short PC_;
414 unsigned short SP;
415 @@ -37,20 +42,22 @@ class CPU {
417 bool skip;
419 - void process(unsigned long cycles);
420 + void process(unsigned cycles);
422 public:
424 - CPU();
425 + CPU(time_t (**_getCurrentTime)());
426 // void halt();
428 // unsigned interrupt(unsigned address, unsigned cycleCounter);
430 - long runFor(unsigned long cycles);
431 + signed runFor(unsigned cycles);
432 void setStatePtrs(SaveState &state);
433 void saveState(SaveState &state);
434 void loadState(const SaveState &state);
437 + void loadOrSave(loadsave& state);
439 void loadSavedata() { memory.loadSavedata(); }
440 void saveSavedata() { memory.saveSavedata(); }
442 @@ -77,7 +84,11 @@ public:
443 int load(const std::string &romfile, bool forceDmg, bool multicartCompat) {
444 return memory.loadROM(romfile, forceDmg, multicartCompat);
448 + int load(const unsigned char* image, size_t isize, bool forceDmg, bool multicartCompat) {
449 + return memory.loadROM(image, isize, forceDmg, multicartCompat);
452 bool loaded() const { return memory.loaded(); }
453 const char * romTitle() const { return memory.romTitle(); }
455 @@ -92,6 +103,14 @@ public:
457 void setGameGenie(const std::string &codes) { memory.setGameGenie(codes); }
458 void setGameShark(const std::string &codes) { memory.setGameShark(codes); }
460 + void setRtcBase(time_t time) { memory.setRtcBase(time); }
461 + time_t getRtcBase() { return memory.getRtcBase(); }
462 + std::pair<unsigned char*, size_t> getWorkRam() { return memory.getWorkRam(); }
463 + std::pair<unsigned char*, size_t> getSaveRam() { return memory.getSaveRam(); }
464 + std::pair<unsigned char*, size_t> getIoRam() { return memory.getIoRam(); }
465 + std::pair<unsigned char*, size_t> getVideoRam() { return memory.getVideoRam(); };
470 diff --git a/libgambatte/src/file/file.cpp b/libgambatte/src/file/file.cpp
471 index fff66d2..5b5739e 100644
472 --- a/libgambatte/src/file/file.cpp
473 +++ b/libgambatte/src/file/file.cpp
474 @@ -20,7 +20,41 @@ Free Software Foundation, Inc.,
475 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
476 ***************************************************************************/
477 #include "stdfile.h"
478 +#include <cstring>
481 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
482 +// - Make it rerecording-friendly.
484 std::auto_ptr<gambatte::File> gambatte::newFileInstance(const std::string &filepath) {
485 return std::auto_ptr<File>(new StdFile(filepath.c_str()));
488 +namespace
490 + struct MemoryFile : public gambatte::File
492 + MemoryFile(const unsigned char* image, size_t isize) : buf(image), bufsize(isize),
493 + ptr(0), xfail(false) {}
494 + ~MemoryFile() {}
495 + void rewind() { ptr = 0; xfail = false; }
496 + std::size_t size() const { return bufsize; }
497 + void read(char *buffer, std::size_t amount) {
498 + if(amount > bufsize - ptr) {
499 + memcpy(buffer, buf, bufsize - ptr);
500 + xfail = true;
501 + } else
502 + memcpy(buffer, buf, amount);
504 + bool fail() const { return xfail; }
505 + private:
506 + const unsigned char* buf;
507 + size_t bufsize;
508 + size_t ptr;
509 + bool xfail;
510 + };
513 +std::auto_ptr<gambatte::File> gambatte::newFileInstance(const unsigned char* image, size_t isize) {
514 + return std::auto_ptr<File>(new MemoryFile(image, isize));
516 diff --git a/libgambatte/src/file/file.h b/libgambatte/src/file/file.h
517 index 23b23c0..1a95543 100644
518 --- a/libgambatte/src/file/file.h
519 +++ b/libgambatte/src/file/file.h
520 @@ -22,9 +22,18 @@ Free Software Foundation, Inc.,
521 #ifndef GAMBATTE_FILE_H
522 #define GAMBATTE_FILE_H
526 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
527 +// - Make it rerecording-friendly.
529 #include <memory>
530 #include <string>
533 +// Modified 2012-07-10 by H. Ilari Liusvaara
534 +// - New API methods.
536 namespace gambatte {
538 class File {
539 @@ -37,6 +46,7 @@ public:
542 std::auto_ptr<File> newFileInstance(const std::string &filepath);
543 +std::auto_ptr<File> newFileInstance(const unsigned char* image, size_t isize);
547 diff --git a/libgambatte/src/gambatte.cpp b/libgambatte/src/gambatte.cpp
548 index d33b3f2..589a608 100644
549 --- a/libgambatte/src/gambatte.cpp
550 +++ b/libgambatte/src/gambatte.cpp
551 @@ -25,6 +25,10 @@
552 #include <sstream>
553 #include <cstring>
556 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
557 +// - Make it rerecording-friendly.
559 static const std::string itos(const int i) {
560 std::stringstream ss;
561 ss << i;
562 @@ -35,16 +39,24 @@ static const std::string statePath(const std::string &basePath, const int stateN
563 return basePath + "_" + itos(stateNo) + ".gqs";
566 +namespace
568 + time_t default_walltime()
570 + return time(0);
574 namespace gambatte {
575 struct GB::Priv {
576 CPU cpu;
577 int stateNo;
578 bool gbaCgbMode;
580 - Priv() : stateNo(1), gbaCgbMode(false) {}
581 + Priv(time_t (**_getCurrentTime)()) : stateNo(1), gbaCgbMode(false), cpu(_getCurrentTime) {}
584 -GB::GB() : p_(new Priv) {}
585 +GB::GB() : p_(new Priv(&walltime)), walltime(default_walltime) {}
587 GB::~GB() {
588 if (p_->cpu.loaded())
589 @@ -53,7 +65,7 @@ GB::~GB() {
590 delete p_;
593 -long GB::runFor(gambatte::uint_least32_t *const videoBuf, const int pitch,
594 +signed GB::runFor(gambatte::uint_least32_t *const videoBuf, const int pitch,
595 gambatte::uint_least32_t *const soundBuf, unsigned &samples) {
596 if (!p_->cpu.loaded()) {
597 samples = 0;
598 @@ -62,10 +74,10 @@ long GB::runFor(gambatte::uint_least32_t *const videoBuf, const int pitch,
600 p_->cpu.setVideoBuffer(videoBuf, pitch);
601 p_->cpu.setSoundBuffer(soundBuf);
602 - const long cyclesSinceBlit = p_->cpu.runFor(samples * 2);
603 + const signed cyclesSinceBlit = p_->cpu.runFor(samples * 2);
604 samples = p_->cpu.fillSoundBuffer();
606 - return cyclesSinceBlit < 0 ? cyclesSinceBlit : static_cast<long>(samples) - (cyclesSinceBlit >> 1);
607 + return cyclesSinceBlit < 0 ? cyclesSinceBlit : static_cast<signed>(samples) - (cyclesSinceBlit >> 1);
610 void GB::reset() {
611 @@ -74,7 +86,7 @@ void GB::reset() {
613 SaveState state;
614 p_->cpu.setStatePtrs(state);
615 - setInitState(state, p_->cpu.isCgb(), p_->gbaCgbMode);
616 + setInitState(state, p_->cpu.isCgb(), p_->gbaCgbMode, walltime());
617 p_->cpu.loadState(state);
618 p_->cpu.loadSavedata();
620 @@ -88,23 +100,43 @@ void GB::setSaveDir(const std::string &sdir) {
621 p_->cpu.setSaveDir(sdir);
624 -int GB::load(const std::string &romfile, const unsigned flags) {
625 +void GB::preload_common()
627 if (p_->cpu.loaded())
628 p_->cpu.saveSavedata();
632 +void GB::postload_common(const unsigned flags)
634 + SaveState state;
635 + p_->cpu.setStatePtrs(state);
636 + setInitState(state, p_->cpu.isCgb(), p_->gbaCgbMode = flags & GBA_CGB, walltime());
637 + p_->cpu.loadState(state);
638 + p_->cpu.loadSavedata();
640 + p_->stateNo = 1;
641 + p_->cpu.setOsdElement(std::auto_ptr<OsdElement>());
644 +int GB::load(const std::string &romfile, const unsigned flags) {
645 + preload_common();
647 const int failed = p_->cpu.load(romfile, flags & FORCE_DMG, flags & MULTICART_COMPAT);
649 - if (!failed) {
650 - SaveState state;
651 - p_->cpu.setStatePtrs(state);
652 - setInitState(state, p_->cpu.isCgb(), p_->gbaCgbMode = flags & GBA_CGB);
653 - p_->cpu.loadState(state);
654 - p_->cpu.loadSavedata();
656 - p_->stateNo = 1;
657 - p_->cpu.setOsdElement(std::auto_ptr<OsdElement>());
661 + if (!failed)
662 + postload_common(flags);
664 + return failed;
667 +int GB::load(const unsigned char* image, size_t isize, unsigned flags) {
668 + preload_common();
670 + const int failed = p_->cpu.load(image, isize, flags & FORCE_DMG, flags & MULTICART_COMPAT);
672 + if (!failed)
673 + postload_common(flags);
675 return failed;
678 @@ -160,6 +192,29 @@ void GB::loadState(const std::string &filepath) {
679 loadState(filepath, false);
682 +void GB::saveState(std::vector<char>& data, const std::vector<char>& cmpdata) {
683 + if (p_->cpu.loaded()) {
684 + loadsave_save l(cmpdata);
685 + p_->cpu.loadOrSave(l);
686 + data = l.get();
690 +void GB::saveState(std::vector<char>& data) {
691 + if (p_->cpu.loaded()) {
692 + loadsave_save l;
693 + p_->cpu.loadOrSave(l);
694 + data = l.get();
698 +void GB::loadState(const std::vector<char>& data) {
699 + if (p_->cpu.loaded()) {
700 + loadsave_load l(data);
701 + p_->cpu.loadOrSave(l);
705 void GB::selectState(int n) {
706 n -= (n / 10) * 10;
707 p_->stateNo = n < 0 ? n + 10 : n;
708 @@ -189,4 +244,38 @@ void GB::setGameShark(const std::string &codes) {
709 p_->cpu.setGameShark(codes);
712 +void GB::setRtcBase(time_t time) {
713 + p_->cpu.setRtcBase(time);
716 +time_t GB::getRtcBase() {
717 + return p_->cpu.getRtcBase();
720 +std::pair<unsigned char*, size_t> GB::getWorkRam() {
721 + return p_->cpu.getWorkRam();
724 +std::pair<unsigned char*, size_t> GB::getSaveRam() {
725 + return p_->cpu.getSaveRam();
728 +std::pair<unsigned char*, size_t> GB::getIoRam() {
729 + return p_->cpu.getIoRam();
732 +std::pair<unsigned char*, size_t> GB::getVideoRam() {
733 + return p_->cpu.getVideoRam();
736 +void GB::set_walltime_fn(time_t (*_walltime)())
738 + walltime = _walltime;
741 +std::string GB::version()
743 + return "SVN320";
747 diff --git a/libgambatte/src/initstate.cpp b/libgambatte/src/initstate.cpp
748 index 0d61e66..fdad344 100644
749 --- a/libgambatte/src/initstate.cpp
750 +++ b/libgambatte/src/initstate.cpp
751 @@ -24,6 +24,10 @@
752 #include <cstring>
753 #include <ctime>
756 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
757 +// - Make it rerecording-friendly.
759 namespace {
761 static void setInitialCgbWram(unsigned char *const wram) {
762 @@ -1147,7 +1151,7 @@ static void setInitialDmgIoamhram(unsigned char *const ioamhram) {
764 } // anon namespace
766 -void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbMode) {
767 +void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbMode, time_t starttime) {
768 static const unsigned char cgbObjpDump[0x40] = {
769 0x00, 0x00, 0xF2, 0xAB,
770 0x61, 0xC2, 0xD9, 0xBA,
771 @@ -1308,7 +1312,7 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM
772 state.spu.ch4.nr4 = 0;
773 state.spu.ch4.master = false;
775 - state.rtc.baseTime = std::time(0);
776 + state.rtc.baseTime = starttime;
777 state.rtc.haltTime = state.rtc.baseTime;
778 state.rtc.dataDh = 0;
779 state.rtc.dataDl = 0;
780 diff --git a/libgambatte/src/initstate.h b/libgambatte/src/initstate.h
781 index 0aba307..171b6d0 100644
782 --- a/libgambatte/src/initstate.h
783 +++ b/libgambatte/src/initstate.h
784 @@ -19,8 +19,14 @@
785 #ifndef INITSTATE_H
786 #define INITSTATE_H
789 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
790 +// - Make it rerecording-friendly.
792 +#include <ctime>
794 namespace gambatte {
795 -void setInitState(struct SaveState &state, bool cgb, bool gbaCgbMode);
796 +void setInitState(struct SaveState &state, bool cgb, bool gbaCgbMode, time_t starttime);
799 #endif
800 diff --git a/libgambatte/src/interrupter.cpp b/libgambatte/src/interrupter.cpp
801 index 66db0c7..230bdc2 100644
802 --- a/libgambatte/src/interrupter.cpp
803 +++ b/libgambatte/src/interrupter.cpp
804 @@ -19,6 +19,10 @@
805 #include "interrupter.h"
806 #include "memory.h"
809 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
810 +// - Make it rerecording-friendly.
812 namespace gambatte {
814 Interrupter::Interrupter(unsigned short &SP_in, unsigned short &PC_in) :
815 @@ -26,7 +30,7 @@ Interrupter::Interrupter(unsigned short &SP_in, unsigned short &PC_in) :
816 PC(PC_in)
819 -unsigned long Interrupter::interrupt(const unsigned address, unsigned long cycleCounter, Memory &memory) {
820 +unsigned Interrupter::interrupt(const unsigned address, unsigned cycleCounter, Memory &memory) {
821 cycleCounter += 8;
822 SP = (SP - 1) & 0xFFFF;
823 memory.write(SP, PC >> 8, cycleCounter);
824 @@ -61,11 +65,20 @@ void Interrupter::setGameShark(const std::string &codes) {
828 -void Interrupter::applyVblankCheats(const unsigned long cycleCounter, Memory &memory) {
829 +void Interrupter::applyVblankCheats(const unsigned cycleCounter, Memory &memory) {
830 for (std::size_t i = 0, size = gsCodes.size(); i < size; ++i) {
831 if (gsCodes[i].type == 0x01)
832 memory.write(gsCodes[i].address, gsCodes[i].value, cycleCounter);
836 +void Interrupter::loadOrSave(loadsave& state) {
837 + unsigned gssize = gsCodes.size();
838 + state(gssize);
839 + if(!state.saving())
840 + gsCodes.resize(gssize);
841 + for(unsigned i = 0; i < gssize; i++)
842 + gsCodes[i].loadOrSave(state);
846 diff --git a/libgambatte/src/interrupter.h b/libgambatte/src/interrupter.h
847 index d8f2f10..e0619e3 100644
848 --- a/libgambatte/src/interrupter.h
849 +++ b/libgambatte/src/interrupter.h
850 @@ -19,8 +19,14 @@
851 #ifndef INTERRUPTER_H
852 #define INTERRUPTER_H
855 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
856 +// - Make it rerecording-friendly.
858 #include <string>
859 #include <vector>
860 +#include "loadsave.h"
863 namespace gambatte {
865 @@ -28,6 +34,12 @@ struct GsCode {
866 unsigned short address;
867 unsigned char value;
868 unsigned char type;
870 + void loadOrSave(loadsave& state) {
871 + state(address);
872 + state(value);
873 + state(type);
877 class Interrupter {
878 @@ -35,11 +47,13 @@ class Interrupter {
879 unsigned short &PC;
880 std::vector<GsCode> gsCodes;
882 - void applyVblankCheats(unsigned long cc, class Memory &mem);
883 + void applyVblankCheats(unsigned cc, class Memory &mem);
884 public:
885 Interrupter(unsigned short &SP, unsigned short &PC);
886 - unsigned long interrupt(const unsigned address, unsigned long cycleCounter, class Memory &memory);
887 + unsigned interrupt(const unsigned address, unsigned cycleCounter, class Memory &memory);
888 void setGameShark(const std::string &codes);
890 + void loadOrSave(loadsave& state);
894 diff --git a/libgambatte/src/interruptrequester.cpp b/libgambatte/src/interruptrequester.cpp
895 index 914f617..f63c0a1 100644
896 --- a/libgambatte/src/interruptrequester.cpp
897 +++ b/libgambatte/src/interruptrequester.cpp
898 @@ -19,6 +19,10 @@
899 #include "interruptrequester.h"
900 #include "savestate.h"
903 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
904 +// - Make it rerecording-friendly.
906 namespace gambatte {
908 InterruptRequester::InterruptRequester() : minIntTime(0), ifreg_(0), iereg_(0) {}
909 @@ -35,17 +39,26 @@ void InterruptRequester::loadState(const SaveState &state) {
910 iereg_ = state.mem.ioamhram.get()[0x1FF] & 0x1F;
911 intFlags.set(state.mem.IME, state.mem.halted);
913 - eventTimes.setValue<INTERRUPTS>(intFlags.imeOrHalted() && pendingIrqs() ? minIntTime : static_cast<unsigned long>(DISABLED_TIME));
914 + eventTimes.setValue<INTERRUPTS>(intFlags.imeOrHalted() && pendingIrqs() ? minIntTime : static_cast<unsigned>(DISABLED_TIME));
917 +void InterruptRequester::loadOrSave(loadsave& state)
919 + eventTimes.loadOrSave(state);
920 + state(minIntTime);
921 + state(ifreg_);
922 + state(iereg_);
923 + intFlags.loadOrSave(state);
926 -void InterruptRequester::resetCc(const unsigned long oldCc, const unsigned long newCc) {
927 +void InterruptRequester::resetCc(const unsigned oldCc, const unsigned newCc) {
928 minIntTime = minIntTime < oldCc ? 0 : minIntTime - (oldCc - newCc);
930 if (eventTimes.value(INTERRUPTS) != DISABLED_TIME)
931 eventTimes.setValue<INTERRUPTS>(minIntTime);
934 -void InterruptRequester::ei(const unsigned long cc) {
935 +void InterruptRequester::ei(const unsigned cc) {
936 intFlags.setIme();
937 minIntTime = cc + 1;
939 @@ -90,14 +103,14 @@ void InterruptRequester::setIereg(const unsigned iereg) {
940 iereg_ = iereg & 0x1F;
942 if (intFlags.imeOrHalted())
943 - eventTimes.setValue<INTERRUPTS>(pendingIrqs() ? minIntTime : static_cast<unsigned long>(DISABLED_TIME));
944 + eventTimes.setValue<INTERRUPTS>(pendingIrqs() ? minIntTime : static_cast<unsigned>(DISABLED_TIME));
947 void InterruptRequester::setIfreg(const unsigned ifreg) {
948 ifreg_ = ifreg;
950 if (intFlags.imeOrHalted())
951 - eventTimes.setValue<INTERRUPTS>(pendingIrqs() ? minIntTime : static_cast<unsigned long>(DISABLED_TIME));
952 + eventTimes.setValue<INTERRUPTS>(pendingIrqs() ? minIntTime : static_cast<unsigned>(DISABLED_TIME));
956 diff --git a/libgambatte/src/interruptrequester.h b/libgambatte/src/interruptrequester.h
957 index 78c9d3f..61326fb 100644
958 --- a/libgambatte/src/interruptrequester.h
959 +++ b/libgambatte/src/interruptrequester.h
960 @@ -19,8 +19,13 @@
961 #ifndef INTERRUPT_REQUESTER_H
962 #define INTERRUPT_REQUESTER_H
965 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
966 +// - Make it rerecording-friendly.
968 #include "counterdef.h"
969 #include "minkeeper.h"
970 +#include "loadsave.h"
972 namespace gambatte {
973 struct SaveState;
974 @@ -28,7 +33,7 @@ enum MemEventId { UNHALT, END, BLIT, SERIAL, OAM, DMA, TIMA, VIDEO, INTERRUPTS }
976 class InterruptRequester {
977 MinKeeper<INTERRUPTS + 1> eventTimes;
978 - unsigned long minIntTime;
979 + unsigned minIntTime;
980 unsigned ifreg_;
981 unsigned iereg_;
983 @@ -38,7 +43,9 @@ class InterruptRequester {
985 public:
986 IntFlags() : flags_(0) {}
989 + void loadOrSave(loadsave& state) { state(flags_); }
991 bool ime() const { return flags_ & IME_MASK; }
992 bool halted() const { return flags_ & HALTED_MASK; }
993 bool imeOrHalted() const { return flags_; }
994 @@ -57,15 +64,17 @@ public:
996 void saveState(SaveState &) const;
997 void loadState(const SaveState &);
999 - void resetCc(unsigned long oldCc, unsigned long newCc);
1001 + void loadOrSave(loadsave& state);
1003 + void resetCc(unsigned oldCc, unsigned newCc);
1005 unsigned ifreg() const { return ifreg_; }
1006 unsigned pendingIrqs() const { return ifreg_ & iereg_; }
1007 bool ime() const { return intFlags.ime(); }
1008 bool halted() const { return intFlags.halted(); }
1010 - void ei(unsigned long cc);
1011 + void ei(unsigned cc);
1012 void di();
1013 void halt();
1014 void unhalt();
1015 @@ -75,10 +84,10 @@ public:
1016 void setIfreg(unsigned ifreg);
1018 MemEventId minEventId() const { return static_cast<MemEventId>(eventTimes.min()); }
1019 - unsigned long minEventTime() const { return eventTimes.minValue(); }
1020 - template<MemEventId id> void setEventTime(unsigned long value) { eventTimes.setValue<id>(value); }
1021 - void setEventTime(const MemEventId id, unsigned long value) { eventTimes.setValue(id, value); }
1022 - unsigned long eventTime(MemEventId id) const { return eventTimes.value(id); }
1023 + unsigned minEventTime() const { return eventTimes.minValue(); }
1024 + template<MemEventId id> void setEventTime(unsigned value) { eventTimes.setValue<id>(value); }
1025 + void setEventTime(const MemEventId id, unsigned value) { eventTimes.setValue(id, value); }
1026 + unsigned eventTime(MemEventId id) const { return eventTimes.value(id); }
1029 inline void flagHdmaReq(InterruptRequester *const intreq) { intreq->setEventTime<DMA>(0); }
1030 diff --git a/libgambatte/src/loadsave.cpp b/libgambatte/src/loadsave.cpp
1031 new file mode 100644
1032 index 0000000..37ea71a
1033 --- /dev/null
1034 +++ b/libgambatte/src/loadsave.cpp
1035 @@ -0,0 +1,266 @@
1036 +/***************************************************************************
1037 + * Copyright (C) 2012 by H. Ilari Liusvaara *
1038 + * ilari.liusvaara@elisanet.fi *
1039 + * *
1040 + * This program is free software; you can redistribute it and/or modify *
1041 + * it under the terms of the GNU General Public License version 2 as *
1042 + * published by the Free Software Foundation. *
1043 + * *
1044 + * This program is distributed in the hope that it will be useful, *
1045 + * but WITHOUT ANY WARRANTY; without even the implied warranty of *
1046 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
1047 + * GNU General Public License version 2 for more details. *
1048 + * *
1049 + * You should have received a copy of the GNU General Public License *
1050 + * version 2 along with this program; if not, write to the *
1051 + * Free Software Foundation, Inc., *
1052 + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
1053 + ***************************************************************************/
1054 +#include "loadsave.h"
1055 +#include <stdexcept>
1056 +#include <cstring>
1057 +#include <iostream>
1059 +namespace gambatte {
1061 +loadsave::~loadsave() throw() {}
1063 +loadsave_load::loadsave_load(const std::vector<char>& _memory)
1064 + : memory(_memory)
1066 + ptr = 0;
1069 +template<typename T> void loadsave_load::do_op(T& x)
1071 + unsigned long long v = 0;
1072 + if(ptr + sizeof(T) > memory.size())
1073 + throw std::runtime_error("Loadstate overflow");
1074 + for(size_t i = 0; i < sizeof(T); i++)
1075 + v |= ((unsigned long long)(unsigned char)memory[ptr++] << (8 * (sizeof(T) - i - 1)));
1076 + x = (T)v;
1079 +template<typename T> void loadsave_load::do_op(T& x, unsigned char _tag)
1081 + if(ptr + 1 > memory.size())
1082 + throw std::runtime_error("Loadstate overflow");
1083 + unsigned char _rtag = memory[ptr++];
1084 + if(_rtag != _tag) {
1085 + std::cerr << "Wrong type tag: expected=" << (int)_tag << ", got=" << (int)_rtag << std::endl;
1086 + throw std::runtime_error("Loadstate desynced");
1088 + do_op(x);
1091 +template<typename T> void loadsave_load::do_op(T* x, size_t s, unsigned char _tag)
1093 + if(ptr + 1 > memory.size())
1094 + throw std::runtime_error("Loadstate overflow");
1095 + unsigned char _rtag = memory[ptr++];
1096 + if(_rtag != _tag) {
1097 + std::cerr << "Wrong type tag: expected=" << (int)_tag << ", got=" << (int)_rtag << std::endl;
1098 + throw std::runtime_error("Loadstate desynced");
1100 + unsigned size;
1101 + do_op(size);
1102 + if(size != s) {
1103 + std::cerr << "Wrong number of entries: expected=" << s << ", got=" << size << std::endl;
1104 + throw std::runtime_error("Loadstate desynced");
1106 + for(size_t i = 0; i < s; i++)
1107 + do_op(x[i]);
1110 +void loadsave_load::operator()(bool& x)
1112 + char c;
1113 + do_op(c, 0);
1114 + x = (c != 0);
1118 +loadsave_load::~loadsave_load() throw() {}
1119 +void loadsave_load::operator()(signed char& x) { do_op(x, 1); }
1120 +void loadsave_load::operator()(unsigned char& x) { do_op(x, 2); }
1121 +void loadsave_load::operator()(signed short& x) { do_op(x, 3); }
1122 +void loadsave_load::operator()(unsigned short& x) { do_op(x, 4); }
1123 +void loadsave_load::operator()(signed int& x) { do_op(x, 5); }
1124 +void loadsave_load::operator()(unsigned int& x) { do_op(x, 6); }
1125 +void loadsave_load::operator()(signed long long& x) { do_op(x, 7); }
1126 +void loadsave_load::operator()(unsigned long long& x) { do_op(x, 8); }
1127 +void loadsave_load::operator()(signed char* x, size_t s) { do_op(x, s, 9); }
1128 +void loadsave_load::operator()(unsigned char* x, size_t s) { do_op(x, s, 10); }
1129 +void loadsave_load::operator()(signed short* x, size_t s) { do_op(x, s, 11); }
1130 +void loadsave_load::operator()(unsigned short* x, size_t s) { do_op(x, s, 12); }
1131 +void loadsave_load::operator()(signed int* x, size_t s) { do_op(x, s, 13); }
1132 +void loadsave_load::operator()(unsigned int* x, size_t s) { do_op(x, s, 14); }
1133 +void loadsave_load::operator()(signed long long* x, size_t s) { do_op(x, s, 15); }
1134 +void loadsave_load::operator()(unsigned long long* x, size_t s) { do_op(x, s, 16); }
1136 +void loadsave_load::tag(unsigned short _tag)
1138 + unsigned short _rtag;
1139 + do_op(_rtag, 18);
1140 + if(_tag != _rtag) {
1141 + std::cerr << "Wrong inner tag: expected=" << (int)_tag << ", got=" << (int)_rtag << std::endl;
1142 + throw std::runtime_error("Loadstate desynced");
1146 +void loadsave_load::operator()(unsigned char*& ptr, unsigned char* abase)
1148 + char x;
1149 + do_op(x, 17);
1150 + if(!x)
1151 + ptr = NULL;
1152 + else {
1153 + unsigned y;
1154 + do_op(y);
1155 + ptr = abase + y;
1159 +void loadsave_load::operator()(const unsigned char*& ptr, unsigned char* abase)
1161 + char x;
1162 + do_op(x, 19);
1163 + if(!x)
1164 + ptr = NULL;
1165 + else {
1166 + unsigned y;
1167 + do_op(y);
1168 + ptr = abase + y;
1173 +bool loadsave_load::saving() { return false; }
1175 +#define BLOCKBYTES 65500
1177 +void loadsave_save::pushbytes(char* bytes, size_t amount)
1179 + if(!nextptr || memory[nextptr - 1].second + amount > BLOCKBYTES) {
1180 + memory.push_back(std::make_pair(new char[BLOCKBYTES], (size_t)0));
1181 + nextptr++;
1183 + if(cmp.size())
1184 + try { if(cmp.size() < used + amount || memcmp(&cmp[used], bytes, amount)) throw 42; } catch(...) {}
1185 + memcpy(memory[nextptr - 1].first + memory[nextptr - 1].second, bytes, amount);
1186 + memory[nextptr - 1].second += amount;
1187 + used += amount;
1190 +template<typename T> void loadsave_save::do_op(T& x)
1192 + unsigned long long v = x;
1193 + char buf[sizeof(T)];
1194 + for(size_t i = 0; i < sizeof(T); i++)
1195 + buf[i] = v >> (8 * (sizeof(T) - i - 1));
1196 + pushbytes(buf, sizeof(T));
1199 +template<typename T> void loadsave_save::do_op(T& x, unsigned char _tag)
1201 + pushbytes((char*)&_tag, 1);
1202 + do_op(x);
1205 +template<typename T> void loadsave_save::do_op(T* x, size_t s, unsigned char _tag)
1207 + pushbytes((char*)&_tag, 1);
1208 + unsigned size = s;
1209 + do_op(size);
1210 + for(size_t i = 0; i < s; i++)
1211 + do_op(x[i]);
1214 +loadsave_save::loadsave_save()
1216 + used = 0;
1217 + nextptr = 0;
1220 +loadsave_save::loadsave_save(const std::vector<char>& _memory)
1222 + used = 0;
1223 + nextptr = 0;
1224 + cmp = _memory;
1227 +loadsave_save::~loadsave_save() throw()
1229 + for(auto i : memory)
1230 + delete[] i.first;
1233 +void loadsave_save::operator()(bool& x)
1235 + char y = x ? 1 : 0;
1236 + char z = 0;
1237 + pushbytes(&z, 1);
1238 + pushbytes(&y, 1);
1241 +void loadsave_save::operator()(signed char& x) { do_op(x, 1); }
1242 +void loadsave_save::operator()(unsigned char& x) { do_op(x, 2); }
1243 +void loadsave_save::operator()(signed short& x) { do_op(x, 3); }
1244 +void loadsave_save::operator()(unsigned short& x) { do_op(x, 4); }
1245 +void loadsave_save::operator()(signed int& x) { do_op(x, 5); }
1246 +void loadsave_save::operator()(unsigned int& x) { do_op(x, 6); }
1247 +void loadsave_save::operator()(signed long long& x) { do_op(x, 7); }
1248 +void loadsave_save::operator()(unsigned long long& x) { do_op(x, 8); }
1249 +void loadsave_save::operator()(signed char* x, size_t s) { do_op(x, s, 9); }
1250 +void loadsave_save::operator()(unsigned char* x, size_t s) { do_op(x, s, 10); }
1251 +void loadsave_save::operator()(signed short* x, size_t s) { do_op(x, s, 11); }
1252 +void loadsave_save::operator()(unsigned short* x, size_t s) { do_op(x, s, 12); }
1253 +void loadsave_save::operator()(signed int* x, size_t s) { do_op(x, s, 13); }
1254 +void loadsave_save::operator()(unsigned int* x, size_t s) { do_op(x, s, 14); }
1255 +void loadsave_save::operator()(signed long long* x, size_t s) { do_op(x, s, 15); }
1256 +void loadsave_save::operator()(unsigned long long* x, size_t s) { do_op(x, s, 16); }
1257 +bool loadsave_save::saving() { return true; }
1259 +void loadsave_save::operator()(unsigned char*& ptr, unsigned char* abase)
1261 + if(!ptr) {
1262 + char x = 0;
1263 + do_op(x, 17);
1264 + } else {
1265 + char x = 1;
1266 + unsigned y = ptr - abase;
1267 + do_op(x, 17);
1268 + do_op(y);
1272 +void loadsave_save::operator()(const unsigned char*& ptr, unsigned char* abase)
1274 + if(!ptr) {
1275 + char x = 0;
1276 + do_op(x, 19);
1277 + } else {
1278 + char x = 1;
1279 + unsigned y = ptr - abase;
1280 + do_op(x, 19);
1281 + do_op(y);
1285 +void loadsave_save::tag(unsigned short _tag)
1287 + do_op(_tag, 18);
1290 +std::vector<char> loadsave_save::get()
1292 + std::vector<char> x;
1293 + x.resize(used);
1294 + size_t ptr = 0;
1295 + for(auto i : memory) {
1296 + memcpy(&x[ptr], i.first, i.second);
1297 + ptr += i.second;
1299 + return x;
1302 diff --git a/libgambatte/src/loadsave.h b/libgambatte/src/loadsave.h
1303 new file mode 100644
1304 index 0000000..10ebf63
1305 --- /dev/null
1306 +++ b/libgambatte/src/loadsave.h
1307 @@ -0,0 +1,160 @@
1308 +#ifndef _loadsave__hpp__included__
1309 +#define _loadsave__hpp__included__
1310 +/***************************************************************************
1311 + * Copyright (C) 2012 by H. Ilari Liusvaara *
1312 + * ilari.liusvaara@elisanet.fi *
1313 + * *
1314 + * This program is free software; you can redistribute it and/or modify *
1315 + * it under the terms of the GNU General Public License version 2 as *
1316 + * published by the Free Software Foundation. *
1317 + * *
1318 + * This program is distributed in the hope that it will be useful, *
1319 + * but WITHOUT ANY WARRANTY; without even the implied warranty of *
1320 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
1321 + * GNU General Public License version 2 for more details. *
1322 + * *
1323 + * You should have received a copy of the GNU General Public License *
1324 + * version 2 along with this program; if not, write to the *
1325 + * Free Software Foundation, Inc., *
1326 + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
1327 + ***************************************************************************/
1329 +#include <cstdint>
1330 +#include <vector>
1331 +#include <cstdlib>
1332 +#include <stdexcept>
1334 +namespace gambatte {
1335 + class loadsave
1337 + private:
1338 + unsigned enumVal;
1339 + bool enumAssigned;
1340 + public:
1341 + virtual ~loadsave() throw();
1342 + virtual void operator()(bool& x) = 0;
1343 + virtual void operator()(signed char& x) = 0;
1344 + virtual void operator()(unsigned char& x) = 0;
1345 + virtual void operator()(signed short& x) = 0;
1346 + virtual void operator()(unsigned short& x) = 0;
1347 + virtual void operator()(signed int& x) = 0;
1348 + virtual void operator()(unsigned int& x) = 0;
1349 + virtual void operator()(signed long long& x) = 0;
1350 + virtual void operator()(unsigned long long& x) = 0;
1351 + virtual void operator()(signed char* x, size_t s) = 0;
1352 + virtual void operator()(unsigned char* x, size_t s) = 0;
1353 + virtual void operator()(signed short* x, size_t s) = 0;
1354 + virtual void operator()(unsigned short* x, size_t s) = 0;
1355 + virtual void operator()(signed int* x, size_t s) = 0;
1356 + virtual void operator()(unsigned int* x, size_t s) = 0;
1357 + virtual void operator()(long long* x, size_t s) = 0;
1358 + virtual void operator()(unsigned long long* x, size_t s) = 0;
1359 + virtual void operator()(unsigned char*& ptr, unsigned char* abase) = 0;
1360 + virtual void operator()(const unsigned char*& ptr, unsigned char* abase) = 0;
1361 + virtual void tag(unsigned short tag) = 0;
1362 + void time(time_t& t) {
1363 + unsigned long long t_ = t;
1364 + (*this)(t_);
1365 + t = t_;
1367 + void startEnumeration() {
1368 + enumAssigned = false;
1369 + enumVal = 0xFFFFFFFFU;
1370 + if(!saving())
1371 + (*this)(enumVal);
1373 + template<typename T> void enumerate(T& ptr, T candiate, unsigned symbol) {
1374 + if(saving()) {
1375 + if(ptr == candiate) {
1376 + enumVal = symbol;
1377 + enumAssigned = true;
1379 + } else {
1380 + if(enumVal == symbol) {
1381 + ptr = candiate;
1382 + enumAssigned = true;
1386 + void endEnumeration() {
1387 + if(saving())
1388 + (*this)(enumVal);
1389 + if(!enumAssigned)
1390 + throw std::runtime_error("Enumeration missing a choice");
1392 + virtual bool saving() = 0;
1393 + };
1395 + class loadsave_load : public loadsave
1397 + const std::vector<char>& memory;
1398 + size_t ptr;
1399 + template<typename T> inline void do_op(T& x);
1400 + template<typename T> inline void do_op(T& x, unsigned char _tag);
1401 + template<typename T> void do_op(T* x, size_t s, unsigned char _tag);
1402 + public:
1403 + loadsave_load(const std::vector<char>& _memory);
1404 + ~loadsave_load() throw();
1405 + void operator()(bool& x);
1406 + void operator()(signed char& x);
1407 + void operator()(unsigned char& x);
1408 + void operator()(signed short& x);
1409 + void operator()(unsigned short& x);
1410 + void operator()(signed int& x);
1411 + void operator()(unsigned int& x);
1412 + void operator()(signed long long& x);
1413 + void operator()(unsigned long long& x);
1414 + void operator()(signed char* x, size_t s);
1415 + void operator()(unsigned char* x, size_t s);
1416 + void operator()(signed short* x, size_t s);
1417 + void operator()(unsigned short* x, size_t s);
1418 + void operator()(signed int* x, size_t s);
1419 + void operator()(unsigned int* x, size_t s);
1420 + void operator()(signed long long* x, size_t s);
1421 + void operator()(unsigned long long* x, size_t s);
1422 + void operator()(unsigned char*& ptr, unsigned char* abase);
1423 + void operator()(const unsigned char*& ptr, unsigned char* abase);
1424 + void tag(unsigned short _tag);
1425 + bool saving();
1426 + };
1428 + class loadsave_save : public loadsave
1430 + std::vector<std::pair<char*, size_t>> memory;
1431 + size_t nextptr;
1432 + size_t used;
1433 + inline void pushbytes(char* bytes, size_t amount);
1434 + template<typename T> inline void do_op(T& x);
1435 + template<typename T> inline void do_op(T& x, unsigned char _tag);
1436 + template<typename T> void do_op(T* x, size_t s, unsigned char _tag);
1437 + std::vector<char> cmp;
1438 + public:
1439 + loadsave_save();
1440 + loadsave_save(const std::vector<char>& _memory);
1441 + ~loadsave_save() throw();
1442 + void operator()(bool& x);
1443 + void operator()(signed char& x);
1444 + void operator()(unsigned char& x);
1445 + void operator()(signed short& x);
1446 + void operator()(unsigned short& x);
1447 + void operator()(signed int& x);
1448 + void operator()(unsigned int& x);
1449 + void operator()(signed long long& x);
1450 + void operator()(unsigned long long& x);
1451 + void operator()(signed char* x, size_t s);
1452 + void operator()(unsigned char* x, size_t s);
1453 + void operator()(signed short* x, size_t s);
1454 + void operator()(unsigned short* x, size_t s);
1455 + void operator()(signed int* x, size_t s);
1456 + void operator()(unsigned int* x, size_t s);
1457 + void operator()(signed long long* x, size_t s);
1458 + void operator()(unsigned long long* x, size_t s);
1459 + void operator()(unsigned char*& ptr, unsigned char* abase);
1460 + void operator()(const unsigned char*& ptr, unsigned char* abase);
1461 + void tag(unsigned short _tag);
1462 + bool saving();
1463 + std::vector<char> get();
1464 + };
1467 +#endif
1468 diff --git a/libgambatte/src/mem/cartridge.cpp b/libgambatte/src/mem/cartridge.cpp
1469 index 452efa4..dcfed26 100644
1470 --- a/libgambatte/src/mem/cartridge.cpp
1471 +++ b/libgambatte/src/mem/cartridge.cpp
1472 @@ -23,6 +23,10 @@
1473 #include <cstring>
1474 #include <fstream>
1477 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
1478 +// - Make it rerecording-friendly.
1480 namespace gambatte {
1482 namespace {
1483 @@ -36,6 +40,8 @@ public:
1484 virtual bool isAddressWithinAreaRombankCanBeMappedTo(unsigned addr, unsigned bank) const {
1485 return (addr< 0x4000) == (bank == 0);
1487 + void loadOrSave(loadsave& state) {
1491 class Mbc0 : public DefaultMbc {
1492 @@ -64,6 +70,10 @@ public:
1493 enableRam = ss.enableRam;
1494 memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, 0);
1497 + void loadOrSave(loadsave& state) {
1498 + state(enableRam);
1502 static inline unsigned rambanks(const MemPtrs &memptrs) {
1503 @@ -95,6 +105,13 @@ public:
1507 + void loadOrSave(loadsave& state) {
1508 + state(rombank);
1509 + state(rambank);
1510 + state(enableRam);
1511 + state(rambankMode);
1514 virtual void romWrite(const unsigned P, const unsigned data) {
1515 switch (P >> 13 & 3) {
1516 case 0:
1517 @@ -168,6 +185,12 @@ public:
1521 + void loadOrSave(loadsave& state) {
1522 + state(rombank);
1523 + state(enableRam);
1524 + state(rombank0Mode);
1527 virtual void romWrite(const unsigned P, const unsigned data) {
1528 switch (P >> 13 & 3) {
1529 case 0:
1530 @@ -221,6 +244,11 @@ public:
1534 + void loadOrSave(loadsave& state) {
1535 + state(rombank);
1536 + state(enableRam);
1539 virtual void romWrite(const unsigned P, const unsigned data) {
1540 switch (P & 0x6100) {
1541 case 0x0000:
1542 @@ -277,6 +305,12 @@ public:
1546 + void loadOrSave(loadsave& state) {
1547 + state(rombank);
1548 + state(rambank);
1549 + state(enableRam);
1552 virtual void romWrite(const unsigned P, const unsigned data) {
1553 switch (P >> 13 & 3) {
1554 case 0:
1555 @@ -338,6 +372,13 @@ public:
1559 + void loadOrSave(loadsave& state) {
1560 + state(rombank);
1561 + state(rambank);
1562 + state(enableRam);
1563 + state(rambankMode);
1566 virtual void romWrite(const unsigned P, const unsigned data) {
1567 switch (P >> 13 & 3) {
1568 case 0:
1569 @@ -396,6 +437,12 @@ public:
1573 + void loadOrSave(loadsave& state) {
1574 + state(rombank);
1575 + state(rambank);
1576 + state(enableRam);
1579 virtual void romWrite(const unsigned P, const unsigned data) {
1580 switch (P >> 13 & 3) {
1581 case 0:
1582 @@ -505,7 +552,15 @@ static unsigned pow2ceil(unsigned n) {
1584 int Cartridge::loadROM(const std::string &romfile, const bool forceDmg, const bool multicartCompat) {
1585 const std::auto_ptr<File> rom(newFileInstance(romfile));
1586 + return loadROM(rom.get(), forceDmg, multicartCompat, romfile);
1589 +int Cartridge::loadROM(const unsigned char* image, size_t isize, const bool forceDmg, const bool multicartCompat) {
1590 + const std::auto_ptr<File> rom(newFileInstance(image, isize));
1591 + return loadROM(rom.get(), forceDmg, multicartCompat, "");
1594 +int Cartridge::loadROM(File* rom, const bool forceDmg, const bool multicartCompat, const std::string& filename) {
1595 if (rom->fail())
1596 return -1;
1598 @@ -613,8 +668,15 @@ int Cartridge::loadROM(const std::string &romfile, const bool forceDmg, const bo
1599 if (rom->fail())
1600 return -1;
1602 - defaultSaveBasePath = stripExtension(romfile);
1604 + if(filename != "") {
1605 + defaultSaveBasePath = stripExtension(filename);
1606 + memoryCartridge = false;
1607 + } else {
1608 + defaultSaveBasePath = "";
1609 + memoryCartridge = true;
1611 + clearMemorySavedData();
1613 switch (type) {
1614 case PLAIN: mbc.reset(new Mbc0(memptrs)); break;
1615 case MBC1:
1616 @@ -653,45 +715,69 @@ void Cartridge::loadSavedata() {
1617 const std::string &sbp = saveBasePath();
1619 if (hasBattery(memptrs.romdata()[0x147])) {
1620 - std::ifstream file((sbp + ".sav").c_str(), std::ios::binary | std::ios::in);
1621 + if(memoryCartridge) {
1622 + if(memoryCartridgeSram.size())
1623 + memcpy(memptrs.rambankdata(), &memoryCartridgeSram[0], memptrs.rambankdataend() - memptrs.rambankdata());
1624 + } else {
1625 + std::ifstream file((sbp + ".sav").c_str(), std::ios::binary | std::ios::in);
1627 - if (file.is_open()) {
1628 - file.read(reinterpret_cast<char*>(memptrs.rambankdata()), memptrs.rambankdataend() - memptrs.rambankdata());
1629 - enforce8bit(memptrs.rambankdata(), memptrs.rambankdataend() - memptrs.rambankdata());
1630 + if (file.is_open()) {
1631 + file.read(reinterpret_cast<char*>(memptrs.rambankdata()), memptrs.rambankdataend() - memptrs.rambankdata());
1632 + enforce8bit(memptrs.rambankdata(), memptrs.rambankdataend() - memptrs.rambankdata());
1637 if (hasRtc(memptrs.romdata()[0x147])) {
1638 - std::ifstream file((sbp + ".rtc").c_str(), std::ios::binary | std::ios::in);
1639 + if(memoryCartridge) {
1640 + rtc.setBaseTime(memoryCartridgeRtcBase);
1641 + } else {
1642 + std::ifstream file((sbp + ".rtc").c_str(), std::ios::binary | std::ios::in);
1644 - if (file.is_open()) {
1645 - unsigned long basetime = file.get() & 0xFF;
1646 + if (file.is_open()) {
1647 + unsigned long basetime = file.get() & 0xFF;
1649 - basetime = basetime << 8 | (file.get() & 0xFF);
1650 - basetime = basetime << 8 | (file.get() & 0xFF);
1651 - basetime = basetime << 8 | (file.get() & 0xFF);
1652 + basetime = basetime << 8 | (file.get() & 0xFF);
1653 + basetime = basetime << 8 | (file.get() & 0xFF);
1654 + basetime = basetime << 8 | (file.get() & 0xFF);
1656 - rtc.setBaseTime(basetime);
1657 + rtc.setBaseTime(basetime);
1663 +void Cartridge::clearMemorySavedData()
1665 + memoryCartridgeRtcBase = 0;
1666 + memoryCartridgeSram.resize(0);
1669 void Cartridge::saveSavedata() {
1670 const std::string &sbp = saveBasePath();
1672 if (hasBattery(memptrs.romdata()[0x147])) {
1673 - std::ofstream file((sbp + ".sav").c_str(), std::ios::binary | std::ios::out);
1674 - file.write(reinterpret_cast<const char*>(memptrs.rambankdata()), memptrs.rambankdataend() - memptrs.rambankdata());
1675 + if(memoryCartridge) {
1676 + memoryCartridgeSram.resize(memptrs.rambankdataend() - memptrs.rambankdata());
1677 + memcpy(&memoryCartridgeSram[0], memptrs.rambankdata(), memptrs.rambankdataend() - memptrs.rambankdata());
1678 + } else {
1679 + std::ofstream file((sbp + ".sav").c_str(), std::ios::binary | std::ios::out);
1680 + file.write(reinterpret_cast<const char*>(memptrs.rambankdata()), memptrs.rambankdataend() - memptrs.rambankdata());
1684 if (hasRtc(memptrs.romdata()[0x147])) {
1685 - std::ofstream file((sbp + ".rtc").c_str(), std::ios::binary | std::ios::out);
1686 - const unsigned long basetime = rtc.getBaseTime();
1687 + if(memoryCartridge) {
1688 + memoryCartridgeRtcBase = rtc.getBaseTime();
1689 + } else {
1690 + std::ofstream file((sbp + ".rtc").c_str(), std::ios::binary | std::ios::out);
1691 + const unsigned long basetime = rtc.getBaseTime();
1693 - file.put(basetime >> 24 & 0xFF);
1694 - file.put(basetime >> 16 & 0xFF);
1695 - file.put(basetime >> 8 & 0xFF);
1696 - file.put(basetime & 0xFF);
1697 + file.put(basetime >> 24 & 0xFF);
1698 + file.put(basetime >> 16 & 0xFF);
1699 + file.put(basetime >> 8 & 0xFF);
1700 + file.put(basetime & 0xFF);
1705 @@ -737,4 +823,31 @@ void Cartridge::setGameGenie(const std::string &codes) {
1709 +std::pair<unsigned char*, size_t> Cartridge::getSaveRam() {
1710 + size_t sramsize = memptrs.rambankdataend() - memptrs.rambankdata();
1711 + return std::make_pair(memptrs.rambankdata(), sramsize);
1714 +std::pair<unsigned char*, size_t> Cartridge::getWorkRam() {
1715 + size_t worksize = memptrs.wramdataend() - memptrs.wramdata(0);
1716 + return std::make_pair(memptrs.wramdata(0), worksize);
1719 +Cartridge::Cartridge(time_t (**_getCurrentTime)())
1720 + : rtc(_getCurrentTime) {
1721 + memoryCartridge = true;
1724 +void Cartridge::loadOrSave(loadsave& state) {
1725 + memptrs.loadOrSave(state);
1726 + rtc.loadOrSave(state);
1727 + mbc->loadOrSave(state);
1728 + unsigned ggsize = ggUndoList.size();
1729 + state(ggsize);
1730 + if(!state.saving())
1731 + ggUndoList.resize(ggsize);
1732 + for(size_t i = 0; i < ggsize; i++)
1733 + ggUndoList[i].loadOrSave(state);
1737 diff --git a/libgambatte/src/mem/cartridge.h b/libgambatte/src/mem/cartridge.h
1738 index bbb3662..3ae779e 100644
1739 --- a/libgambatte/src/mem/cartridge.h
1740 +++ b/libgambatte/src/mem/cartridge.h
1741 @@ -25,9 +25,16 @@
1742 #include <memory>
1743 #include <string>
1744 #include <vector>
1745 +#include "../loadsave.h"
1748 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
1749 +// - Make it rerecording-friendly.
1751 namespace gambatte {
1753 +class File;
1755 class Mbc {
1756 public:
1757 virtual ~Mbc() {}
1758 @@ -35,13 +42,19 @@ public:
1759 virtual void saveState(SaveState::Mem &ss) const = 0;
1760 virtual void loadState(const SaveState::Mem &ss) = 0;
1761 virtual bool isAddressWithinAreaRombankCanBeMappedTo(unsigned address, unsigned rombank) const = 0;
1762 + virtual void loadOrSave(loadsave& state) = 0;
1765 class Cartridge {
1766 struct AddrData {
1767 - unsigned long addr;
1768 + unsigned addr;
1769 unsigned char data;
1770 - AddrData(unsigned long addr, unsigned data) : addr(addr), data(data) {}
1771 + AddrData(unsigned addr, unsigned data) : addr(addr), data(data) {}
1772 + AddrData() {}
1773 + void loadOrSave(loadsave& state) {
1774 + state(addr);
1775 + state(data);
1779 MemPtrs memptrs;
1780 @@ -50,14 +63,22 @@ class Cartridge {
1781 std::string defaultSaveBasePath;
1782 std::string saveDir;
1783 std::vector<AddrData> ggUndoList;
1785 + bool memoryCartridge;
1786 + time_t memoryCartridgeRtcBase;
1787 + std::vector<unsigned char> memoryCartridgeSram;
1789 void applyGameGenie(const std::string &code);
1792 + int loadROM(File* rom, const bool forceDmg, const bool multicartCompat, const std::string& filename);
1793 + void clearMemorySavedData();
1794 public:
1795 + Cartridge(time_t (**_getCurrentTime)());
1796 void setStatePtrs(SaveState &);
1797 void saveState(SaveState &) const;
1798 void loadState(const SaveState &);
1800 + void loadOrSave(loadsave& state);
1802 bool loaded() const { return mbc.get(); }
1804 const unsigned char * rmem(unsigned area) const { return memptrs.rmem(area); }
1805 @@ -84,8 +105,15 @@ public:
1806 const std::string saveBasePath() const;
1807 void setSaveDir(const std::string &dir);
1808 int loadROM(const std::string &romfile, bool forceDmg, bool multicartCompat);
1809 + int loadROM(const unsigned char* image, size_t isize, bool forceDmg, bool multicartCompat);
1810 const char * romTitle() const { return reinterpret_cast<const char *>(memptrs.romdata() + 0x134); }
1811 void setGameGenie(const std::string &codes);
1813 + void setRtcBase(time_t time) { rtc.setBaseTime(time); }
1814 + time_t getRtcBase() { return rtc.getBaseTime(); }
1815 + std::pair<unsigned char*, size_t> getWorkRam();
1816 + std::pair<unsigned char*, size_t> getSaveRam();
1821 diff --git a/libgambatte/src/mem/memptrs.cpp b/libgambatte/src/mem/memptrs.cpp
1822 index 243810c..049a8ba 100644
1823 --- a/libgambatte/src/mem/memptrs.cpp
1824 +++ b/libgambatte/src/mem/memptrs.cpp
1825 @@ -20,6 +20,10 @@
1826 #include <algorithm>
1827 #include <cstring>
1830 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
1831 +// - Make it rerecording-friendly.
1833 namespace gambatte {
1835 MemPtrs::MemPtrs() :
1836 @@ -39,7 +43,11 @@ MemPtrs::~MemPtrs() {
1838 void MemPtrs::reset(const unsigned rombanks, const unsigned rambanks, const unsigned wrambanks) {
1839 delete []memchunk_;
1840 - memchunk_ = new unsigned char[0x4000 + rombanks * 0x4000ul + rambanks * 0x2000ul + wrambanks * 0x1000ul + 0x4000];
1841 + memchunk_size = 0x4000 + rombanks * 0x4000ul + rambanks * 0x2000ul + wrambanks * 0x1000ul + 0x4000;
1842 + memchunk_ = new unsigned char[memchunk_size];
1844 + //FIXME: Make this random.
1845 + memset(memchunk_, 0, memchunk_size);
1847 romdata_[0] = romdata();
1848 rambankdata_ = romdata_[0] + rombanks * 0x4000ul;
1849 @@ -141,4 +149,26 @@ void MemPtrs::disconnectOamDmaAreas() {
1853 +void MemPtrs::loadOrSave(loadsave& state)
1855 + state(memchunk_, 0x4000);
1856 + state(rambankdata_, memchunk_size - (rambankdata_ - memchunk_));
1857 + int oamDmaSrc_2 = oamDmaSrc_;
1858 + state(oamDmaSrc_2);
1859 + oamDmaSrc_ = (OamDmaSrc)oamDmaSrc_2;
1860 + //Rmem is constant.
1861 + for(unsigned i = 0; i < 0x10; i++)
1862 + state(wmem_[i], memchunk_);
1863 + for(unsigned i = 0; i < 0x10; i++)
1864 + state(rmem_[i], memchunk_);
1865 + state(romdata_[0], memchunk_);
1866 + state(romdata_[1], memchunk_);
1867 + state(rambankdata_, memchunk_);
1868 + state(rdisabledRam_, memchunk_);
1869 + state(wdisabledRam_, memchunk_);
1870 + state(rsrambankptr_, memchunk_);
1871 + state(wsrambankptr_, memchunk_);
1872 + //memchunk_size is cart constant, not saved.
1876 diff --git a/libgambatte/src/mem/memptrs.h b/libgambatte/src/mem/memptrs.h
1877 index 62c0fa9..2fc2789 100644
1878 --- a/libgambatte/src/mem/memptrs.h
1879 +++ b/libgambatte/src/mem/memptrs.h
1880 @@ -19,6 +19,12 @@
1881 #ifndef MEMPTRS_H
1882 #define MEMPTRS_H
1884 +#include "../loadsave.h"
1887 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
1888 +// - Make it rerecording-friendly.
1890 namespace gambatte {
1892 enum OamDmaSrc { OAM_DMA_SRC_ROM, OAM_DMA_SRC_SRAM, OAM_DMA_SRC_VRAM,
1893 @@ -36,7 +42,8 @@ class MemPtrs {
1894 unsigned char *wdisabledRam_;
1895 unsigned char *rsrambankptr_;
1896 unsigned char *wsrambankptr_;
1898 + unsigned memchunk_size;
1900 OamDmaSrc oamDmaSrc_;
1902 MemPtrs(const MemPtrs &);
1903 @@ -68,6 +75,8 @@ public:
1904 void setRambank(unsigned ramFlags, unsigned rambank);
1905 void setWrambank(unsigned bank);
1906 void setOamDmaSrc(OamDmaSrc oamDmaSrc);
1908 + void loadOrSave(loadsave& state);
1911 inline bool isCgb(const MemPtrs &memptrs) {
1912 diff --git a/libgambatte/src/mem/rtc.cpp b/libgambatte/src/mem/rtc.cpp
1913 index 993db29..62e0dac 100644
1914 --- a/libgambatte/src/mem/rtc.cpp
1915 +++ b/libgambatte/src/mem/rtc.cpp
1916 @@ -19,9 +19,13 @@
1917 #include "rtc.h"
1918 #include "../savestate.h"
1921 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
1922 +// - Make it rerecording-friendly.
1924 namespace gambatte {
1926 -Rtc::Rtc()
1927 +Rtc::Rtc(time_t (**_getCurrentTime)())
1928 : activeData(NULL),
1929 activeSet(NULL),
1930 baseTime(0),
1931 @@ -33,12 +37,13 @@ Rtc::Rtc()
1932 dataM(0),
1933 dataS(0),
1934 enabled(false),
1935 - lastLatchData(false)
1936 + lastLatchData(false),
1937 + getCurrentTime(_getCurrentTime)
1941 void Rtc::doLatch() {
1942 - std::time_t tmp = ((dataDh & 0x40) ? haltTime : std::time(0)) - baseTime;
1943 + std::time_t tmp = ((dataDh & 0x40) ? haltTime : (*getCurrentTime)()) - baseTime;
1945 while (tmp > 0x1FF * 86400) {
1946 baseTime += 0x1FF * 86400;
1947 @@ -113,44 +118,76 @@ void Rtc::loadState(const SaveState &state) {
1950 void Rtc::setDh(const unsigned new_dh) {
1951 - const std::time_t unixtime = (dataDh & 0x40) ? haltTime : std::time(0);
1952 + const std::time_t unixtime = (dataDh & 0x40) ? haltTime : (*getCurrentTime)();
1953 const std::time_t old_highdays = ((unixtime - baseTime) / 86400) & 0x100;
1954 baseTime += old_highdays * 86400;
1955 baseTime -= ((new_dh & 0x1) << 8) * 86400;
1957 if ((dataDh ^ new_dh) & 0x40) {
1958 if (new_dh & 0x40)
1959 - haltTime = std::time(0);
1960 + haltTime = (*getCurrentTime)();
1961 else
1962 - baseTime += std::time(0) - haltTime;
1963 + baseTime += (*getCurrentTime)() - haltTime;
1967 void Rtc::setDl(const unsigned new_lowdays) {
1968 - const std::time_t unixtime = (dataDh & 0x40) ? haltTime : std::time(0);
1969 + const std::time_t unixtime = (dataDh & 0x40) ? haltTime : (*getCurrentTime)();
1970 const std::time_t old_lowdays = ((unixtime - baseTime) / 86400) & 0xFF;
1971 baseTime += old_lowdays * 86400;
1972 baseTime -= new_lowdays * 86400;
1975 void Rtc::setH(const unsigned new_hours) {
1976 - const std::time_t unixtime = (dataDh & 0x40) ? haltTime : std::time(0);
1977 + const std::time_t unixtime = (dataDh & 0x40) ? haltTime : (*getCurrentTime)();
1978 const std::time_t old_hours = ((unixtime - baseTime) / 3600) % 24;
1979 baseTime += old_hours * 3600;
1980 baseTime -= new_hours * 3600;
1983 void Rtc::setM(const unsigned new_minutes) {
1984 - const std::time_t unixtime = (dataDh & 0x40) ? haltTime : std::time(0);
1985 + const std::time_t unixtime = (dataDh & 0x40) ? haltTime : (*getCurrentTime)();
1986 const std::time_t old_minutes = ((unixtime - baseTime) / 60) % 60;
1987 baseTime += old_minutes * 60;
1988 baseTime -= new_minutes * 60;
1991 void Rtc::setS(const unsigned new_seconds) {
1992 - const std::time_t unixtime = (dataDh & 0x40) ? haltTime : std::time(0);
1993 + const std::time_t unixtime = (dataDh & 0x40) ? haltTime : (*getCurrentTime)();
1994 baseTime += (unixtime - baseTime) % 60;
1995 baseTime -= new_seconds;
1998 +void Rtc::loadOrSave(loadsave& state)
2000 + state.startEnumeration();
2001 + state.enumerate<unsigned char*>(activeData, NULL, 0);
2002 + state.enumerate(activeData, &dataDh, 1);
2003 + state.enumerate(activeData, &dataDl, 2);
2004 + state.enumerate(activeData, &dataH, 3);
2005 + state.enumerate(activeData, &dataM, 4);
2006 + state.enumerate(activeData, &dataS, 5);
2007 + state.endEnumeration();
2009 + state.startEnumeration();
2010 + state.enumerate<void (gambatte::Rtc::*)(unsigned int)>(activeSet, NULL, 0);
2011 + state.enumerate(activeSet, &Rtc::setDh, 1);
2012 + state.enumerate(activeSet, &Rtc::setDl, 2);
2013 + state.enumerate(activeSet, &Rtc::setH, 3);
2014 + state.enumerate(activeSet, &Rtc::setM, 4);
2015 + state.enumerate(activeSet, &Rtc::setS, 5);
2016 + state.endEnumeration();
2018 + state.time(baseTime);
2019 + state.time(haltTime);
2020 + state(index);
2021 + state(dataDh);
2022 + state(dataDl);
2023 + state(dataH);
2024 + state(dataM);
2025 + state(dataS);
2026 + state(enabled);
2027 + state(lastLatchData);
2031 diff --git a/libgambatte/src/mem/rtc.h b/libgambatte/src/mem/rtc.h
2032 index c5b3b5a..206b46e 100644
2033 --- a/libgambatte/src/mem/rtc.h
2034 +++ b/libgambatte/src/mem/rtc.h
2035 @@ -20,6 +20,12 @@
2036 #define RTC_H
2038 #include <ctime>
2039 +#include "../loadsave.h"
2042 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
2043 +// - Make it rerecording-friendly.
2046 namespace gambatte {
2048 @@ -39,6 +45,7 @@ private:
2049 unsigned char dataS;
2050 bool enabled;
2051 bool lastLatchData;
2052 + time_t (**getCurrentTime)();
2054 void doLatch();
2055 void doSwapActive();
2056 @@ -49,7 +56,7 @@ private:
2057 void setS(unsigned new_seconds);
2059 public:
2060 - Rtc();
2061 + Rtc(time_t (**_getCurrentTime)());
2063 const unsigned char* getActive() const { return activeData; }
2064 std::time_t getBaseTime() const { return baseTime; }
2065 @@ -84,6 +91,8 @@ public:
2066 (this->*activeSet)(data);
2067 *activeData = data;
2070 + void loadOrSave(loadsave& state);
2074 diff --git a/libgambatte/src/memory.cpp b/libgambatte/src/memory.cpp
2075 index 01abb57..edba55b 100644
2076 --- a/libgambatte/src/memory.cpp
2077 +++ b/libgambatte/src/memory.cpp
2078 @@ -23,9 +23,13 @@
2079 #include "savestate.h"
2080 #include <cstring>
2083 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
2084 +// - Make it rerecording-friendly.
2086 namespace gambatte {
2088 -Memory::Memory(const Interrupter &interrupter_in)
2089 +Memory::Memory(const Interrupter &interrupter_in, time_t (**_getCurrentTime)())
2090 : vrambank(vram),
2091 getInput(0),
2092 divLastUpdate(0),
2093 @@ -36,7 +40,8 @@ Memory::Memory(const Interrupter &interrupter_in)
2094 dmaDestination(0),
2095 oamDmaPos(0xFE),
2096 serialCnt(0),
2097 - blanklcd(false)
2098 + blanklcd(false),
2099 + cart(_getCurrentTime)
2101 intreq.setEventTime<BLIT>(144*456ul);
2102 intreq.setEventTime<END>(0);
2103 @@ -51,7 +56,7 @@ void Memory::setStatePtrs(SaveState &state) {
2104 sound.setStatePtrs(state);
2107 -unsigned long Memory::saveState(SaveState &state, unsigned long cycleCounter) {
2108 +unsigned Memory::saveState(SaveState &state, unsigned cycleCounter) {
2109 cycleCounter = resetCounters(cycleCounter);
2110 nontrivial_ff_read(0xFF05, cycleCounter);
2111 nontrivial_ff_read(0xFF0F, cycleCounter);
2112 @@ -74,7 +79,7 @@ unsigned long Memory::saveState(SaveState &state, unsigned long cycleCounter) {
2113 return cycleCounter;
2116 -static inline int serialCntFrom(const unsigned long cyclesUntilDone, const bool cgbFast) {
2117 +static inline int serialCntFrom(const unsigned cyclesUntilDone, const bool cgbFast) {
2118 return cgbFast ? (cyclesUntilDone + 0xF) >> 4 : (cyclesUntilDone + 0x1FF) >> 9;
2121 @@ -116,14 +121,14 @@ void Memory::loadState(const SaveState &state) {
2122 std::memset(vram + 0x2000, 0, 0x2000);
2125 -void Memory::setEndtime(const unsigned long cycleCounter, const unsigned long inc) {
2126 +void Memory::setEndtime(const unsigned cycleCounter, const unsigned inc) {
2127 if (intreq.eventTime(BLIT) <= cycleCounter)
2128 intreq.setEventTime<BLIT>(intreq.eventTime(BLIT) + (70224 << isDoubleSpeed()));
2130 intreq.setEventTime<END>(cycleCounter + (inc << isDoubleSpeed()));
2133 -void Memory::updateSerial(const unsigned long cc) {
2134 +void Memory::updateSerial(const unsigned cc) {
2135 if (intreq.eventTime(SERIAL) != DISABLED_TIME) {
2136 if (intreq.eventTime(SERIAL) <= cc) {
2137 ioamhram[0x101] = (((ioamhram[0x101] + 1) << serialCnt) - 1) & 0xFF;
2138 @@ -138,18 +143,18 @@ void Memory::updateSerial(const unsigned long cc) {
2142 -void Memory::updateTimaIrq(const unsigned long cc) {
2143 +void Memory::updateTimaIrq(const unsigned cc) {
2144 while (intreq.eventTime(TIMA) <= cc)
2145 tima.doIrqEvent(TimaInterruptRequester(intreq));
2148 -void Memory::updateIrqs(const unsigned long cc) {
2149 +void Memory::updateIrqs(const unsigned cc) {
2150 updateSerial(cc);
2151 updateTimaIrq(cc);
2152 display.update(cc);
2155 -unsigned long Memory::event(unsigned long cycleCounter) {
2156 +unsigned Memory::event(unsigned cycleCounter) {
2157 if (lastOamDmaUpdate != DISABLED_TIME)
2158 updateOamDma(cycleCounter);
2160 @@ -170,10 +175,10 @@ unsigned long Memory::event(unsigned long cycleCounter) {
2161 case BLIT:
2163 const bool lcden = ioamhram[0x140] >> 7 & 1;
2164 - unsigned long blitTime = intreq.eventTime(BLIT);
2165 + unsigned blitTime = intreq.eventTime(BLIT);
2167 if (lcden | blanklcd) {
2168 - display.updateScreen(blanklcd, cycleCounter);
2169 + display.updateScreen(blanklcd, cycleCounter, videoBuf_, pitch_);
2170 intreq.setEventTime<BLIT>(DISABLED_TIME);
2171 intreq.setEventTime<END>(DISABLED_TIME);
2173 @@ -191,7 +196,7 @@ unsigned long Memory::event(unsigned long cycleCounter) {
2174 break;
2175 case OAM:
2176 intreq.setEventTime<OAM>(lastOamDmaUpdate == DISABLED_TIME ?
2177 - static_cast<unsigned long>(DISABLED_TIME) : intreq.eventTime(OAM) + 0xA0 * 4);
2178 + static_cast<unsigned>(DISABLED_TIME) : intreq.eventTime(OAM) + 0xA0 * 4);
2179 break;
2180 case DMA:
2182 @@ -203,7 +208,7 @@ unsigned long Memory::event(unsigned long cycleCounter) {
2184 ackDmaReq(&intreq);
2186 - if ((static_cast<unsigned long>(dmaDest) + length) & 0x10000) {
2187 + if ((static_cast<unsigned>(dmaDest) + length) & 0x10000) {
2188 length = 0x10000 - dmaDest;
2189 ioamhram[0x155] |= 0x80;
2191 @@ -214,7 +219,7 @@ unsigned long Memory::event(unsigned long cycleCounter) {
2192 dmaLength = 0;
2195 - unsigned long lOamDmaUpdate = lastOamDmaUpdate;
2196 + unsigned lOamDmaUpdate = lastOamDmaUpdate;
2197 lastOamDmaUpdate = DISABLED_TIME;
2199 while (length--) {
2200 @@ -295,7 +300,7 @@ unsigned long Memory::event(unsigned long cycleCounter) {
2201 return cycleCounter;
2204 -unsigned long Memory::stop(unsigned long cycleCounter) {
2205 +unsigned Memory::stop(unsigned cycleCounter) {
2206 cycleCounter += 4 << isDoubleSpeed();
2208 if (ioamhram[0x14D] & isCgb()) {
2209 @@ -318,31 +323,31 @@ unsigned long Memory::stop(unsigned long cycleCounter) {
2210 return cycleCounter;
2213 -static void decCycles(unsigned long &counter, const unsigned long dec) {
2214 +static void decCycles(unsigned &counter, const unsigned dec) {
2215 if (counter != DISABLED_TIME)
2216 counter -= dec;
2219 -void Memory::decEventCycles(const MemEventId eventId, const unsigned long dec) {
2220 +void Memory::decEventCycles(const MemEventId eventId, const unsigned dec) {
2221 if (intreq.eventTime(eventId) != DISABLED_TIME)
2222 intreq.setEventTime(eventId, intreq.eventTime(eventId) - dec);
2225 -unsigned long Memory::resetCounters(unsigned long cycleCounter) {
2226 +unsigned Memory::resetCounters(unsigned cycleCounter) {
2227 if (lastOamDmaUpdate != DISABLED_TIME)
2228 updateOamDma(cycleCounter);
2230 updateIrqs(cycleCounter);
2232 - const unsigned long oldCC = cycleCounter;
2233 + const unsigned oldCC = cycleCounter;
2236 - const unsigned long divinc = (cycleCounter - divLastUpdate) >> 8;
2237 + const unsigned divinc = (cycleCounter - divLastUpdate) >> 8;
2238 ioamhram[0x104] = (ioamhram[0x104] + divinc) & 0xFF;
2239 divLastUpdate += divinc << 8;
2242 - const unsigned long dec = cycleCounter < 0x10000 ? 0 : (cycleCounter & ~0x7FFFul) - 0x8000;
2243 + const unsigned dec = cycleCounter < 0x10000 ? 0 : (cycleCounter & ~0x7FFFul) - 0x8000;
2245 decCycles(divLastUpdate, dec);
2246 decCycles(lastOamDmaUpdate, dec);
2247 @@ -381,7 +386,7 @@ void Memory::updateInput() {
2248 ioamhram[0x100] &= button;
2251 -void Memory::updateOamDma(const unsigned long cycleCounter) {
2252 +void Memory::updateOamDma(const unsigned cycleCounter) {
2253 const unsigned char *const oamDmaSrc = oamDmaSrcPtr();
2254 unsigned cycles = (cycleCounter - lastOamDmaUpdate) >> 2;
2256 @@ -429,17 +434,17 @@ const unsigned char * Memory::oamDmaSrcPtr() const {
2257 return ioamhram[0x146] == 0xFF && !isCgb() ? oamDmaSrcZero() : cart.rdisabledRam();
2260 -void Memory::startOamDma(const unsigned long cycleCounter) {
2261 +void Memory::startOamDma(const unsigned cycleCounter) {
2262 display.oamChange(cart.rdisabledRam(), cycleCounter);
2265 -void Memory::endOamDma(const unsigned long cycleCounter) {
2266 +void Memory::endOamDma(const unsigned cycleCounter) {
2267 oamDmaPos = 0xFE;
2268 cart.setOamDmaSrc(OAM_DMA_SRC_OFF);
2269 display.oamChange(ioamhram, cycleCounter);
2272 -unsigned Memory::nontrivial_ff_read(const unsigned P, const unsigned long cycleCounter) {
2273 +unsigned Memory::nontrivial_ff_read(const unsigned P, const unsigned cycleCounter) {
2274 if (lastOamDmaUpdate != DISABLED_TIME)
2275 updateOamDma(cycleCounter);
2277 @@ -453,7 +458,7 @@ unsigned Memory::nontrivial_ff_read(const unsigned P, const unsigned long cycleC
2278 break;
2279 case 0x04:
2281 - const unsigned long divcycles = (cycleCounter - divLastUpdate) >> 8;
2282 + const unsigned divcycles = (cycleCounter - divLastUpdate) >> 8;
2283 ioamhram[0x104] = (ioamhram[0x104] + divcycles) & 0xFF;
2284 divLastUpdate += divcycles << 8;
2286 @@ -532,7 +537,7 @@ static bool isInOamDmaConflictArea(const OamDmaSrc oamDmaSrc, const unsigned add
2287 return addr < a[oamDmaSrc].areaUpper && addr - a[oamDmaSrc].exceptAreaLower >= a[oamDmaSrc].exceptAreaWidth;
2290 -unsigned Memory::nontrivial_read(const unsigned P, const unsigned long cycleCounter) {
2291 +unsigned Memory::nontrivial_read(const unsigned P, const unsigned cycleCounter) {
2292 if (P < 0xFF80) {
2293 if (lastOamDmaUpdate != DISABLED_TIME) {
2294 updateOamDma(cycleCounter);
2295 @@ -571,7 +576,7 @@ unsigned Memory::nontrivial_read(const unsigned P, const unsigned long cycleCoun
2296 return ioamhram[P - 0xFE00];
2299 -void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned long cycleCounter) {
2300 +void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned cycleCounter) {
2301 if (lastOamDmaUpdate != DISABLED_TIME)
2302 updateOamDma(cycleCounter);
2304 @@ -588,7 +593,7 @@ void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned
2305 serialCnt = 8;
2306 intreq.setEventTime<SERIAL>((data & 0x81) == 0x81
2307 ? (data & isCgb() * 2 ? (cycleCounter & ~0x7ul) + 0x10 * 8 : (cycleCounter & ~0xFFul) + 0x200 * 8)
2308 - : static_cast<unsigned long>(DISABLED_TIME));
2309 + : static_cast<unsigned>(DISABLED_TIME));
2311 data |= 0x7E - isCgb() * 2;
2312 break;
2313 @@ -949,7 +954,7 @@ void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned
2314 ioamhram[P - 0xFE00] = data;
2317 -void Memory::nontrivial_write(const unsigned P, const unsigned data, const unsigned long cycleCounter) {
2318 +void Memory::nontrivial_write(const unsigned P, const unsigned data, const unsigned cycleCounter) {
2319 if (lastOamDmaUpdate != DISABLED_TIME) {
2320 updateOamDma(cycleCounter);
2322 @@ -986,24 +991,59 @@ void Memory::nontrivial_write(const unsigned P, const unsigned data, const unsig
2323 ioamhram[P - 0xFE00] = data;
2326 +void Memory::postLoadRom()
2328 + sound.init(cart.isCgb());
2329 + display.reset(ioamhram, cart.isCgb());
2330 + interrupter.setGameShark(std::string());
2333 int Memory::loadROM(const std::string &romfile, const bool forceDmg, const bool multicartCompat) {
2334 if (const int fail = cart.loadROM(romfile, forceDmg, multicartCompat))
2335 return fail;
2337 - sound.init(cart.isCgb());
2338 - display.reset(ioamhram, cart.isCgb());
2339 - interrupter.setGameShark(std::string());
2340 + postLoadRom();
2342 return 0;
2345 -unsigned Memory::fillSoundBuffer(const unsigned long cycleCounter) {
2346 +int Memory::loadROM(const unsigned char* image, size_t isize, const bool forceDmg, const bool multicartCompat) {
2347 + if (const int fail = cart.loadROM(image, isize, forceDmg, multicartCompat))
2348 + return fail;
2350 + postLoadRom();
2352 + return 0;
2355 +unsigned Memory::fillSoundBuffer(const unsigned cycleCounter) {
2356 sound.generate_samples(cycleCounter, isDoubleSpeed());
2357 return sound.fillBuffer();
2360 -void Memory::setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned long rgb32) {
2361 +void Memory::setDmgPaletteColor(unsigned palNum, unsigned colorNum, uint_least32_t rgb32) {
2362 display.setDmgPaletteColor(palNum, colorNum, rgb32);
2365 +void Memory::loadOrSave(loadsave& state)
2367 + state(ioamhram, 0x200);
2368 + state(vram, 0x2000 * 2);
2369 + state(vrambank, vram);
2370 + //Don't save getInput, it has no state.
2371 + state(divLastUpdate);
2372 + state(lastOamDmaUpdate);
2373 + intreq.loadOrSave(state);
2374 + cart.loadOrSave(state);
2375 + tima.loadOrSave(state);
2376 + display.loadOrSave(state);
2377 + sound.loadOrSave(state);
2378 + interrupter.loadOrSave(state);
2379 + state(dmaSource);
2380 + state(dmaDestination);
2381 + state(oamDmaPos);
2382 + state(serialCnt);
2383 + state(blanklcd);
2387 diff --git a/libgambatte/src/memory.h b/libgambatte/src/memory.h
2388 index 9163367..8e4c4ee 100644
2389 --- a/libgambatte/src/memory.h
2390 +++ b/libgambatte/src/memory.h
2391 @@ -19,6 +19,10 @@
2392 #ifndef MEMORY_H
2393 #define MEMORY_H
2396 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
2397 +// - Make it rerecording-friendly.
2399 #include "mem/cartridge.h"
2400 #include "video.h"
2401 #include "sound.h"
2402 @@ -35,8 +39,8 @@ class Memory {
2403 unsigned char *vrambank;
2405 InputGetter *getInput;
2406 - unsigned long divLastUpdate;
2407 - unsigned long lastOamDmaUpdate;
2408 + unsigned divLastUpdate;
2409 + unsigned lastOamDmaUpdate;
2411 InterruptRequester intreq;
2412 Cartridge cart;
2413 @@ -51,34 +55,40 @@ class Memory {
2414 unsigned char serialCnt;
2415 bool blanklcd;
2417 + uint_least32_t* videoBuf_;
2418 + unsigned pitch_;
2420 void updateInput();
2421 - void decEventCycles(MemEventId eventId, unsigned long dec);
2422 + void decEventCycles(MemEventId eventId, unsigned dec);
2424 void oamDmaInitSetup();
2425 - void updateOamDma(unsigned long cycleCounter);
2426 - void startOamDma(unsigned long cycleCounter);
2427 - void endOamDma(unsigned long cycleCounter);
2428 + void updateOamDma(unsigned cycleCounter);
2429 + void startOamDma(unsigned cycleCounter);
2430 + void endOamDma(unsigned cycleCounter);
2431 const unsigned char * oamDmaSrcPtr() const;
2433 - unsigned nontrivial_ff_read(unsigned P, unsigned long cycleCounter);
2434 - unsigned nontrivial_read(unsigned P, unsigned long cycleCounter);
2435 - void nontrivial_ff_write(unsigned P, unsigned data, unsigned long cycleCounter);
2436 - void nontrivial_write(unsigned P, unsigned data, unsigned long cycleCounter);
2437 + unsigned nontrivial_ff_read(unsigned P, unsigned cycleCounter);
2438 + unsigned nontrivial_read(unsigned P, unsigned cycleCounter);
2439 + void nontrivial_ff_write(unsigned P, unsigned data, unsigned cycleCounter);
2440 + void nontrivial_write(unsigned P, unsigned data, unsigned cycleCounter);
2442 - void updateSerial(unsigned long cc);
2443 - void updateTimaIrq(unsigned long cc);
2444 - void updateIrqs(unsigned long cc);
2445 + void updateSerial(unsigned cc);
2446 + void updateTimaIrq(unsigned cc);
2447 + void updateIrqs(unsigned cc);
2449 bool isDoubleSpeed() const { return display.isDoubleSpeed(); }
2451 + void postLoadRom();
2452 public:
2453 - explicit Memory(const Interrupter &interrupter);
2454 + explicit Memory(const Interrupter &interrupter, time_t (**_getCurrentTime)());
2456 bool loaded() const { return cart.loaded(); }
2457 const char * romTitle() const { return cart.romTitle(); }
2459 + void loadOrSave(loadsave& state);
2461 void setStatePtrs(SaveState &state);
2462 - unsigned long saveState(SaveState &state, unsigned long cc);
2463 + unsigned saveState(SaveState &state, unsigned cc);
2464 void loadState(const SaveState &state/*, unsigned long oldCc*/);
2465 void loadSavedata() { cart.loadSavedata(); }
2466 void saveSavedata() { cart.saveSavedata(); }
2467 @@ -88,67 +98,77 @@ public:
2468 display.setOsdElement(osdElement);
2471 - unsigned long stop(unsigned long cycleCounter);
2472 + unsigned stop(unsigned cycleCounter);
2473 bool isCgb() const { return display.isCgb(); }
2474 bool ime() const { return intreq.ime(); }
2475 bool halted() const { return intreq.halted(); }
2476 - unsigned long nextEventTime() const { return intreq.minEventTime(); }
2477 + unsigned nextEventTime() const { return intreq.minEventTime(); }
2479 bool isActive() const { return intreq.eventTime(END) != DISABLED_TIME; }
2481 - long cyclesSinceBlit(const unsigned long cc) const {
2482 - return cc < intreq.eventTime(BLIT) ? -1 : static_cast<long>((cc - intreq.eventTime(BLIT)) >> isDoubleSpeed());
2483 + signed cyclesSinceBlit(const unsigned cc) const {
2484 + return cc < intreq.eventTime(BLIT) ? -1 : static_cast<signed>((cc - intreq.eventTime(BLIT)) >> isDoubleSpeed());
2487 void halt() { intreq.halt(); }
2488 - void ei(unsigned long cycleCounter) { if (!ime()) { intreq.ei(cycleCounter); } }
2489 + void ei(unsigned cycleCounter) { if (!ime()) { intreq.ei(cycleCounter); } }
2491 void di() { intreq.di(); }
2493 - unsigned ff_read(const unsigned P, const unsigned long cycleCounter) {
2494 + unsigned ff_read(const unsigned P, const unsigned cycleCounter) {
2495 return P < 0xFF80 ? nontrivial_ff_read(P, cycleCounter) : ioamhram[P - 0xFE00];
2498 - unsigned read(const unsigned P, const unsigned long cycleCounter) {
2499 + unsigned read(const unsigned P, const unsigned cycleCounter) {
2500 return cart.rmem(P >> 12) ? cart.rmem(P >> 12)[P] : nontrivial_read(P, cycleCounter);
2503 - void write(const unsigned P, const unsigned data, const unsigned long cycleCounter) {
2504 + void write(const unsigned P, const unsigned data, const unsigned cycleCounter) {
2505 if (cart.wmem(P >> 12)) {
2506 cart.wmem(P >> 12)[P] = data;
2507 } else
2508 nontrivial_write(P, data, cycleCounter);
2511 - void ff_write(const unsigned P, const unsigned data, const unsigned long cycleCounter) {
2512 + void ff_write(const unsigned P, const unsigned data, const unsigned cycleCounter) {
2513 if (P - 0xFF80u < 0x7Fu) {
2514 ioamhram[P - 0xFE00] = data;
2515 } else
2516 nontrivial_ff_write(P, data, cycleCounter);
2519 - unsigned long event(unsigned long cycleCounter);
2520 - unsigned long resetCounters(unsigned long cycleCounter);
2521 + unsigned event(unsigned cycleCounter);
2522 + unsigned resetCounters(unsigned cycleCounter);
2524 int loadROM(const std::string &romfile, bool forceDmg, bool multicartCompat);
2525 + int loadROM(const unsigned char* image, size_t isize, bool forceDmg, bool multicartCompat);
2526 void setSaveDir(const std::string &dir) { cart.setSaveDir(dir); }
2528 void setInputGetter(InputGetter *getInput) {
2529 this->getInput = getInput;
2532 - void setEndtime(unsigned long cc, unsigned long inc);
2533 + void setEndtime(unsigned cc, unsigned inc);
2535 void setSoundBuffer(uint_least32_t *const buf) { sound.setBuffer(buf); }
2536 - unsigned fillSoundBuffer(unsigned long cc);
2537 + unsigned fillSoundBuffer(unsigned cc);
2539 void setVideoBuffer(uint_least32_t *const videoBuf, const int pitch) {
2540 - display.setVideoBuffer(videoBuf, pitch);
2541 + videoBuf_ = videoBuf;
2542 + pitch_ = pitch;
2545 - void setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned long rgb32);
2546 + void setDmgPaletteColor(unsigned palNum, unsigned colorNum, uint_least32_t rgb32);
2547 void setGameGenie(const std::string &codes) { cart.setGameGenie(codes); }
2548 void setGameShark(const std::string &codes) { interrupter.setGameShark(codes); }
2550 + void setRtcBase(time_t time) { cart.setRtcBase(time); }
2551 + time_t getRtcBase() { return cart.getRtcBase(); }
2552 + std::pair<unsigned char*, size_t> getWorkRam() { return cart.getWorkRam(); }
2553 + std::pair<unsigned char*, size_t> getSaveRam() { return cart.getSaveRam(); }
2554 + std::pair<unsigned char*, size_t> getIoRam() { return std::make_pair(ioamhram, sizeof(ioamhram)); }
2555 + std::pair<unsigned char*, size_t> getVideoRam() { return std::make_pair(vram, sizeof(vram)); };
2560 diff --git a/libgambatte/src/minkeeper.h b/libgambatte/src/minkeeper.h
2561 index e745007..7a2bdf8 100644
2562 --- a/libgambatte/src/minkeeper.h
2563 +++ b/libgambatte/src/minkeeper.h
2564 @@ -19,7 +19,12 @@
2565 #ifndef MINKEEPER_H
2566 #define MINKEEPER_H
2569 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
2570 +// - Make it rerecording-friendly.
2572 #include <algorithm>
2573 +#include "loadsave.h"
2575 namespace MinKeeperUtil {
2576 template<int n> struct CeiledLog2 { enum { RESULT = 1 + CeiledLog2<(n + 1) / 2>::RESULT }; };
2577 @@ -74,35 +79,42 @@ class MinKeeper {
2581 - unsigned long values[ids];
2582 - unsigned long minValue_;
2583 + unsigned values[ids];
2584 + unsigned minValue_;
2585 void (*updateValueLut[Num<LEVELS-1>::RESULT])(MinKeeper<ids>*);
2586 int a[Sum<LEVELS>::RESULT];
2588 template<int id> static void updateValue(MinKeeper<ids> *s);
2590 public:
2591 - explicit MinKeeper(unsigned long initValue = 0xFFFFFFFF);
2593 + explicit MinKeeper(unsigned initValue = 0xFFFFFFFFULL);
2595 + void loadOrSave(gambatte::loadsave& state) {
2596 + state(values, ids);
2597 + state(minValue_);
2598 + //updateValueLut is constant for our purposes.
2599 + state(a, Sum<LEVELS>::RESULT);
2602 int min() const { return a[0]; }
2603 - unsigned long minValue() const { return minValue_; }
2604 + unsigned minValue() const { return minValue_; }
2606 template<int id>
2607 - void setValue(const unsigned long cnt) {
2608 + void setValue(const unsigned cnt) {
2609 values[id] = cnt;
2610 updateValue<id / 2>(this);
2613 - void setValue(const int id, const unsigned long cnt) {
2614 + void setValue(const int id, const unsigned cnt) {
2615 values[id] = cnt;
2616 updateValueLut[id >> 1](this);
2619 - unsigned long value(const int id) const { return values[id]; }
2620 + unsigned value(const int id) const { return values[id]; }
2623 template<int ids>
2624 -MinKeeper<ids>::MinKeeper(const unsigned long initValue) {
2625 +MinKeeper<ids>::MinKeeper(const unsigned initValue) {
2626 std::fill(values, values + ids, initValue);
2628 for (int i = 0; i < Num<LEVELS-1>::RESULT; ++i) {
2629 diff --git a/libgambatte/src/savestate.h b/libgambatte/src/savestate.h
2630 index 1916c90..d85220d 100644
2631 --- a/libgambatte/src/savestate.h
2632 +++ b/libgambatte/src/savestate.h
2633 @@ -19,6 +19,12 @@
2634 #ifndef SAVESTATE_H
2635 #define SAVESTATE_H
2638 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
2639 +// - Make it rerecording-friendly.
2641 +#include <ctime>
2643 namespace gambatte {
2645 class SaverList;
2646 @@ -27,20 +33,20 @@ struct SaveState {
2647 template<typename T>
2648 class Ptr {
2649 T *ptr;
2650 - unsigned long sz;
2651 + unsigned sz;
2653 public:
2654 Ptr() : ptr(0), sz(0) {}
2655 const T* get() const { return ptr; }
2656 - unsigned long getSz() const { return sz; }
2657 - void set(T *ptr, const unsigned long sz) { this->ptr = ptr; this->sz = sz; }
2658 + unsigned getSz() const { return sz; }
2659 + void set(T *ptr, const unsigned sz) { this->ptr = ptr; this->sz = sz; }
2661 friend class SaverList;
2662 - friend void setInitState(SaveState &, bool, bool);
2663 + friend void setInitState(SaveState &, bool, bool, time_t);
2666 struct CPU {
2667 - unsigned long cycleCounter;
2668 + unsigned cycleCounter;
2669 unsigned short PC;
2670 unsigned short SP;
2671 unsigned char A;
2672 @@ -59,13 +65,13 @@ struct SaveState {
2673 Ptr<unsigned char> sram;
2674 Ptr<unsigned char> wram;
2675 Ptr<unsigned char> ioamhram;
2676 - unsigned long divLastUpdate;
2677 - unsigned long timaLastUpdate;
2678 - unsigned long tmatime;
2679 - unsigned long nextSerialtime;
2680 - unsigned long lastOamDmaUpdate;
2681 - unsigned long minIntTime;
2682 - unsigned long unhaltTime;
2683 + unsigned divLastUpdate;
2684 + unsigned timaLastUpdate;
2685 + unsigned tmatime;
2686 + unsigned nextSerialtime;
2687 + unsigned lastOamDmaUpdate;
2688 + unsigned minIntTime;
2689 + unsigned unhaltTime;
2690 unsigned short rombank;
2691 unsigned short dmaSource;
2692 unsigned short dmaDestination;
2693 @@ -85,8 +91,8 @@ struct SaveState {
2694 Ptr<unsigned char> oamReaderBuf;
2695 Ptr<bool> oamReaderSzbuf;
2697 - unsigned long videoCycles;
2698 - unsigned long enableDisplayM0Time;
2699 + unsigned videoCycles;
2700 + unsigned enableDisplayM0Time;
2701 unsigned short lastM0Time;
2702 unsigned short nextM0Irq;
2703 unsigned short tileword;
2704 @@ -115,24 +121,24 @@ struct SaveState {
2706 struct SPU {
2707 struct Duty {
2708 - unsigned long nextPosUpdate;
2709 + unsigned nextPosUpdate;
2710 unsigned char nr3;
2711 unsigned char pos;
2714 struct Env {
2715 - unsigned long counter;
2716 + unsigned counter;
2717 unsigned char volume;
2720 struct LCounter {
2721 - unsigned long counter;
2722 + unsigned counter;
2723 unsigned short lengthCounter;
2726 struct {
2727 struct {
2728 - unsigned long counter;
2729 + unsigned counter;
2730 unsigned short shadow;
2731 unsigned char nr0;
2732 bool negging;
2733 @@ -155,8 +161,8 @@ struct SaveState {
2734 struct {
2735 Ptr<unsigned char> waveRam;
2736 LCounter lcounter;
2737 - unsigned long waveCounter;
2738 - unsigned long lastReadTime;
2739 + unsigned waveCounter;
2740 + unsigned lastReadTime;
2741 unsigned char nr3;
2742 unsigned char nr4;
2743 unsigned char wavePos;
2744 @@ -166,7 +172,7 @@ struct SaveState {
2746 struct {
2747 struct {
2748 - unsigned long counter;
2749 + unsigned counter;
2750 unsigned short reg;
2751 } lfsr;
2752 Env env;
2753 @@ -175,12 +181,12 @@ struct SaveState {
2754 bool master;
2755 } ch4;
2757 - unsigned long cycleCounter;
2758 + unsigned cycleCounter;
2759 } spu;
2761 struct RTC {
2762 - unsigned long baseTime;
2763 - unsigned long haltTime;
2764 + unsigned baseTime;
2765 + unsigned haltTime;
2766 unsigned char dataDh;
2767 unsigned char dataDl;
2768 unsigned char dataH;
2769 diff --git a/libgambatte/src/sound.cpp b/libgambatte/src/sound.cpp
2770 index 14d994e..5bf1e81 100644
2771 --- a/libgambatte/src/sound.cpp
2772 +++ b/libgambatte/src/sound.cpp
2773 @@ -21,6 +21,10 @@
2774 #include <cstring>
2775 #include <algorithm>
2778 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
2779 +// - Make it rerecording-friendly.
2782 Frame Sequencer
2784 @@ -89,7 +93,7 @@ void PSG::loadState(const SaveState &state) {
2785 enabled = state.mem.ioamhram.get()[0x126] >> 7 & 1;
2788 -void PSG::accumulate_channels(const unsigned long cycles) {
2789 +void PSG::accumulate_channels(const unsigned cycles) {
2790 uint_least32_t *const buf = buffer + bufferPos;
2792 std::memset(buf, 0, cycles * sizeof(uint_least32_t));
2793 @@ -99,17 +103,16 @@ void PSG::accumulate_channels(const unsigned long cycles) {
2794 ch4.update(buf, soVol, cycles);
2797 -void PSG::generate_samples(const unsigned long cycleCounter, const unsigned doubleSpeed) {
2798 - const unsigned long cycles = (cycleCounter - lastUpdate) >> (1 + doubleSpeed);
2799 +void PSG::generate_samples(const unsigned cycleCounter, const unsigned doubleSpeed) {
2800 + const unsigned cycles = (cycleCounter - lastUpdate) >> (1 + doubleSpeed);
2801 lastUpdate += cycles << (1 + doubleSpeed);
2803 if (cycles)
2804 accumulate_channels(cycles);
2806 bufferPos += cycles;
2809 -void PSG::resetCounter(const unsigned long newCc, const unsigned long oldCc, const unsigned doubleSpeed) {
2810 +void PSG::resetCounter(const unsigned newCc, const unsigned oldCc, const unsigned doubleSpeed) {
2811 generate_samples(oldCc, doubleSpeed);
2812 lastUpdate = newCc - (oldCc - lastUpdate);
2814 @@ -155,11 +158,11 @@ unsigned PSG::fillBuffer() {
2817 #ifdef WORDS_BIGENDIAN
2818 -static const unsigned long so1Mul = 0x00000001;
2819 -static const unsigned long so2Mul = 0x00010000;
2820 +static const unsigned so1Mul = 0x00000001;
2821 +static const unsigned so2Mul = 0x00010000;
2822 #else
2823 -static const unsigned long so1Mul = 0x00010000;
2824 -static const unsigned long so2Mul = 0x00000001;
2825 +static const unsigned so1Mul = 0x00010000;
2826 +static const unsigned so2Mul = 0x00000001;
2827 #endif
2829 void PSG::set_so_volume(const unsigned nr50) {
2830 @@ -167,7 +170,7 @@ void PSG::set_so_volume(const unsigned nr50) {
2833 void PSG::map_so(const unsigned nr51) {
2834 - const unsigned long tmp = nr51 * so1Mul + (nr51 >> 4) * so2Mul;
2835 + const unsigned tmp = nr51 * so1Mul + (nr51 >> 4) * so2Mul;
2837 ch1.setSo((tmp & 0x00010001) * 0xFFFF);
2838 ch2.setSo((tmp >> 1 & 0x00010001) * 0xFFFF);
2839 @@ -179,4 +182,17 @@ unsigned PSG::getStatus() const {
2840 return ch1.isActive() | ch2.isActive() << 1 | ch3.isActive() << 2 | ch4.isActive() << 3;
2843 +void PSG::loadOrSave(loadsave& state)
2845 + ch1.loadOrSave(state);
2846 + ch2.loadOrSave(state);
2847 + ch3.loadOrSave(state);
2848 + ch4.loadOrSave(state);
2849 + state(lastUpdate);
2850 + state(soVol);
2851 + state(rsum);
2852 + state(bufferPos);
2853 + state(enabled);
2857 diff --git a/libgambatte/src/sound.h b/libgambatte/src/sound.h
2858 index 71d5cdc..7fd3f82 100644
2859 --- a/libgambatte/src/sound.h
2860 +++ b/libgambatte/src/sound.h
2861 @@ -19,10 +19,15 @@
2862 #ifndef SOUND_H
2863 #define SOUND_H
2866 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
2867 +// - Make it rerecording-friendly.
2869 #include "sound/channel1.h"
2870 #include "sound/channel2.h"
2871 #include "sound/channel3.h"
2872 #include "sound/channel4.h"
2873 +#include "loadsave.h"
2875 namespace gambatte {
2877 @@ -34,8 +39,8 @@ class PSG {
2879 uint_least32_t *buffer;
2881 - unsigned long lastUpdate;
2882 - unsigned long soVol;
2883 + unsigned lastUpdate;
2884 + unsigned soVol;
2886 uint_least32_t rsum;
2888 @@ -43,7 +48,7 @@ class PSG {
2890 bool enabled;
2892 - void accumulate_channels(unsigned long cycles);
2893 + void accumulate_channels(unsigned cycles);
2895 public:
2896 PSG();
2897 @@ -53,8 +58,10 @@ public:
2898 void saveState(SaveState &state);
2899 void loadState(const SaveState &state);
2901 - void generate_samples(unsigned long cycleCounter, unsigned doubleSpeed);
2902 - void resetCounter(unsigned long newCc, unsigned long oldCc, unsigned doubleSpeed);
2903 + void loadOrSave(loadsave& state);
2905 + void generate_samples(unsigned cycleCounter, unsigned doubleSpeed);
2906 + void resetCounter(unsigned newCc, unsigned oldCc, unsigned doubleSpeed);
2907 unsigned fillBuffer();
2908 void setBuffer(uint_least32_t *const buf) { buffer = buf; bufferPos = 0; }
2910 diff --git a/libgambatte/src/sound/channel1.cpp b/libgambatte/src/sound/channel1.cpp
2911 index 82e623a..e282d0a 100644
2912 --- a/libgambatte/src/sound/channel1.cpp
2913 +++ b/libgambatte/src/sound/channel1.cpp
2914 @@ -20,6 +20,9 @@
2915 #include "../savestate.h"
2916 #include <algorithm>
2919 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
2920 +// - Make it rerecording-friendly.
2922 namespace gambatte {
2924 @@ -47,7 +50,7 @@ unsigned Channel1::SweepUnit::calcFreq() {
2927 void Channel1::SweepUnit::event() {
2928 - const unsigned long period = nr0 >> 4 & 0x07;
2929 + const unsigned period = nr0 >> 4 & 0x07;
2931 if (period) {
2932 const unsigned freq = calcFreq();
2933 @@ -70,7 +73,7 @@ void Channel1::SweepUnit::nr0Change(const unsigned newNr0) {
2934 nr0 = newNr0;
2937 -void Channel1::SweepUnit::nr4Init(const unsigned long cc) {
2938 +void Channel1::SweepUnit::nr4Init(const unsigned cc) {
2939 negging = false;
2940 shadow = dutyUnit.getFreq();
2942 @@ -172,7 +175,7 @@ void Channel1::setNr4(const unsigned data) {
2943 setEvent();
2946 -void Channel1::setSo(const unsigned long soMask) {
2947 +void Channel1::setSo(const unsigned soMask) {
2948 this->soMask = soMask;
2949 staticOutputTest(cycleCounter);
2950 setEvent();
2951 @@ -215,15 +218,15 @@ void Channel1::loadState(const SaveState &state) {
2952 master = state.spu.ch1.master;
2955 -void Channel1::update(uint_least32_t *buf, const unsigned long soBaseVol, unsigned long cycles) {
2956 - const unsigned long outBase = envelopeUnit.dacIsOn() ? soBaseVol & soMask : 0;
2957 - const unsigned long outLow = outBase * (0 - 15ul);
2958 - const unsigned long endCycles = cycleCounter + cycles;
2959 +void Channel1::update(uint_least32_t *buf, const unsigned soBaseVol, unsigned cycles) {
2960 + const unsigned outBase = envelopeUnit.dacIsOn() ? soBaseVol & soMask : 0;
2961 + const unsigned outLow = outBase * (0 - 15ul);
2962 + const unsigned endCycles = cycleCounter + cycles;
2964 for (;;) {
2965 - const unsigned long outHigh = master ? outBase * (envelopeUnit.getVolume() * 2 - 15ul) : outLow;
2966 - const unsigned long nextMajorEvent = nextEventUnit->getCounter() < endCycles ? nextEventUnit->getCounter() : endCycles;
2967 - unsigned long out = dutyUnit.isHighState() ? outHigh : outLow;
2968 + const unsigned outHigh = master ? outBase * (envelopeUnit.getVolume() * 2 - 15ul) : outLow;
2969 + const unsigned nextMajorEvent = nextEventUnit->getCounter() < endCycles ? nextEventUnit->getCounter() : endCycles;
2970 + unsigned out = dutyUnit.isHighState() ? outHigh : outLow;
2972 while (dutyUnit.getCounter() <= nextMajorEvent) {
2973 *buf = out - prevOut;
2974 @@ -259,4 +262,25 @@ void Channel1::update(uint_least32_t *buf, const unsigned long soBaseVol, unsign
2978 +void Channel1::loadOrSave(loadsave& state) {
2979 + //disableMaster has no state.
2980 + lengthCounter.loadOrSave(state);
2981 + dutyUnit.loadOrSave(state);
2982 + envelopeUnit.loadOrSave(state);
2983 + sweepUnit.loadOrSave(state);
2985 + state.startEnumeration();
2986 + state.enumerate<SoundUnit*>(nextEventUnit, NULL, 0);
2987 + state.enumerate<SoundUnit*>(nextEventUnit, &sweepUnit, 1);
2988 + state.enumerate<SoundUnit*>(nextEventUnit, &envelopeUnit, 2);
2989 + state.enumerate<SoundUnit*>(nextEventUnit, &lengthCounter, 3);
2990 + state.endEnumeration();
2992 + state(cycleCounter);
2993 + state(soMask);
2994 + state(prevOut);
2995 + state(nr4);
2996 + state(master);
3000 diff --git a/libgambatte/src/sound/channel1.h b/libgambatte/src/sound/channel1.h
3001 index 9c77277..1f4eba9 100644
3002 --- a/libgambatte/src/sound/channel1.h
3003 +++ b/libgambatte/src/sound/channel1.h
3004 @@ -19,12 +19,17 @@
3005 #ifndef SOUND_CHANNEL1_H
3006 #define SOUND_CHANNEL1_H
3009 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
3010 +// - Make it rerecording-friendly.
3012 #include "gbint.h"
3013 #include "master_disabler.h"
3014 #include "length_counter.h"
3015 #include "duty_unit.h"
3016 #include "envelope_unit.h"
3017 #include "static_output_tester.h"
3018 +#include "loadsave.h"
3020 namespace gambatte {
3022 @@ -44,10 +49,16 @@ class Channel1 {
3023 SweepUnit(MasterDisabler &disabler, DutyUnit &dutyUnit);
3024 void event();
3025 void nr0Change(unsigned newNr0);
3026 - void nr4Init(unsigned long cycleCounter);
3027 + void nr4Init(unsigned cycleCounter);
3028 void reset();
3029 void saveState(SaveState &state) const;
3030 void loadState(const SaveState &state);
3031 + void loadOrSave(loadsave& state) {
3032 + loadOrSave2(state);
3033 + state(shadow);
3034 + state(nr0);
3035 + state(negging);
3039 friend class StaticOutputTester<Channel1,DutyUnit>;
3040 @@ -61,9 +72,9 @@ class Channel1 {
3042 SoundUnit *nextEventUnit;
3044 - unsigned long cycleCounter;
3045 - unsigned long soMask;
3046 - unsigned long prevOut;
3047 + unsigned cycleCounter;
3048 + unsigned soMask;
3049 + unsigned prevOut;
3051 unsigned char nr4;
3052 bool master;
3053 @@ -78,15 +89,17 @@ public:
3054 void setNr3(unsigned data);
3055 void setNr4(unsigned data);
3057 - void setSo(unsigned long soMask);
3058 + void setSo(unsigned soMask);
3059 bool isActive() const { return master; }
3061 - void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles);
3062 + void update(uint_least32_t *buf, unsigned soBaseVol, unsigned cycles);
3064 void reset();
3065 void init(bool cgb);
3066 void saveState(SaveState &state);
3067 void loadState(const SaveState &state);
3069 + void loadOrSave(loadsave& state);
3073 diff --git a/libgambatte/src/sound/channel2.cpp b/libgambatte/src/sound/channel2.cpp
3074 index 91e15ee..16905bd 100644
3075 --- a/libgambatte/src/sound/channel2.cpp
3076 +++ b/libgambatte/src/sound/channel2.cpp
3077 @@ -19,6 +19,10 @@
3078 #include "channel2.h"
3079 #include "../savestate.h"
3082 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
3083 +// - Make it rerecording-friendly.
3085 namespace gambatte {
3087 Channel2::Channel2() :
3088 @@ -80,7 +84,7 @@ void Channel2::setNr4(const unsigned data) {
3089 setEvent();
3092 -void Channel2::setSo(const unsigned long soMask) {
3093 +void Channel2::setSo(const unsigned soMask) {
3094 this->soMask = soMask;
3095 staticOutputTest(cycleCounter);
3096 setEvent();
3097 @@ -119,16 +123,15 @@ void Channel2::loadState(const SaveState &state) {
3098 master = state.spu.ch2.master;
3101 -void Channel2::update(uint_least32_t *buf, const unsigned long soBaseVol, unsigned long cycles) {
3102 - const unsigned long outBase = envelopeUnit.dacIsOn() ? soBaseVol & soMask : 0;
3103 - const unsigned long outLow = outBase * (0 - 15ul);
3104 - const unsigned long endCycles = cycleCounter + cycles;
3105 +void Channel2::update(uint_least32_t *buf, const unsigned soBaseVol, unsigned cycles) {
3106 + const unsigned outBase = envelopeUnit.dacIsOn() ? soBaseVol & soMask : 0;
3107 + const unsigned outLow = outBase * (0 - 15ul);
3108 + const unsigned endCycles = cycleCounter + cycles;
3110 for (;;) {
3111 - const unsigned long outHigh = master ? outBase * (envelopeUnit.getVolume() * 2 - 15ul) : outLow;
3112 - const unsigned long nextMajorEvent = nextEventUnit->getCounter() < endCycles ? nextEventUnit->getCounter() : endCycles;
3113 - unsigned long out = dutyUnit.isHighState() ? outHigh : outLow;
3115 + const unsigned outHigh = master ? outBase * (envelopeUnit.getVolume() * 2 - 15ul) : outLow;
3116 + const unsigned nextMajorEvent = nextEventUnit->getCounter() < endCycles ? nextEventUnit->getCounter() : endCycles;
3117 + unsigned out = dutyUnit.isHighState() ? outHigh : outLow;
3118 while (dutyUnit.getCounter() <= nextMajorEvent) {
3119 *buf += out - prevOut;
3120 prevOut = out;
3121 @@ -162,4 +165,24 @@ void Channel2::update(uint_least32_t *buf, const unsigned long soBaseVol, unsign
3125 +void Channel2::loadOrSave(loadsave& state)
3127 + //disableMaster has no state.
3128 + lengthCounter.loadOrSave(state);
3129 + dutyUnit.loadOrSave(state);
3130 + envelopeUnit.loadOrSave(state);
3132 + state.startEnumeration();
3133 + state.enumerate<SoundUnit*>(nextEventUnit, NULL, 0);
3134 + state.enumerate<SoundUnit*>(nextEventUnit, &lengthCounter, 1);
3135 + state.enumerate<SoundUnit*>(nextEventUnit, &envelopeUnit, 2);
3136 + state.endEnumeration();
3138 + state(cycleCounter);
3139 + state(soMask);
3140 + state(prevOut);
3141 + state(nr4);
3142 + state(master);
3146 diff --git a/libgambatte/src/sound/channel2.h b/libgambatte/src/sound/channel2.h
3147 index d6ba821..b654da9 100644
3148 --- a/libgambatte/src/sound/channel2.h
3149 +++ b/libgambatte/src/sound/channel2.h
3150 @@ -24,6 +24,11 @@
3151 #include "duty_unit.h"
3152 #include "envelope_unit.h"
3153 #include "static_output_tester.h"
3154 +#include "loadsave.h"
3157 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
3158 +// - Make it rerecording-friendly.
3160 namespace gambatte {
3162 @@ -40,9 +45,9 @@ class Channel2 {
3164 SoundUnit *nextEventUnit;
3166 - unsigned long cycleCounter;
3167 - unsigned long soMask;
3168 - unsigned long prevOut;
3169 + unsigned cycleCounter;
3170 + unsigned soMask;
3171 + unsigned prevOut;
3173 unsigned char nr4;
3174 bool master;
3175 @@ -56,16 +61,18 @@ public:
3176 void setNr3(unsigned data);
3177 void setNr4(unsigned data);
3179 - void setSo(unsigned long soMask);
3180 + void setSo(unsigned soMask);
3181 // void deactivate() { disableMaster(); setEvent(); }
3182 bool isActive() const { return master; }
3184 - void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles);
3185 + void update(uint_least32_t *buf, unsigned soBaseVol, unsigned cycles);
3187 void reset();
3188 void init(bool cgb);
3189 void saveState(SaveState &state);
3190 void loadState(const SaveState &state);
3192 + void loadOrSave(loadsave& state);
3196 diff --git a/libgambatte/src/sound/channel3.cpp b/libgambatte/src/sound/channel3.cpp
3197 index a1bde94..a3f3aa4 100644
3198 --- a/libgambatte/src/sound/channel3.cpp
3199 +++ b/libgambatte/src/sound/channel3.cpp
3200 @@ -21,6 +21,10 @@
3201 #include <cstring>
3202 #include <algorithm>
3205 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
3206 +// - Make it rerecording-friendly.
3208 static inline unsigned toPeriod(const unsigned nr3, const unsigned nr4) {
3209 return 0x800 - ((nr4 << 8 & 0x700) | nr3);
3211 @@ -80,7 +84,7 @@ void Channel3::setNr4(const unsigned data) {
3215 -void Channel3::setSo(const unsigned long soMask) {
3216 +void Channel3::setSo(const unsigned soMask) {
3217 this->soMask = soMask;
3220 @@ -128,10 +132,10 @@ void Channel3::loadState(const SaveState &state) {
3221 setNr2(state.mem.ioamhram.get()[0x11C]);
3224 -void Channel3::updateWaveCounter(const unsigned long cc) {
3225 +void Channel3::updateWaveCounter(const unsigned cc) {
3226 if (cc >= waveCounter) {
3227 const unsigned period = toPeriod(nr3, nr4);
3228 - const unsigned long periods = (cc - waveCounter) / period;
3229 + const unsigned periods = (cc - waveCounter) / period;
3231 lastReadTime = waveCounter + periods * period;
3232 waveCounter = lastReadTime + period;
3233 @@ -143,15 +147,15 @@ void Channel3::updateWaveCounter(const unsigned long cc) {
3237 -void Channel3::update(uint_least32_t *buf, const unsigned long soBaseVol, unsigned long cycles) {
3238 - const unsigned long outBase = (nr0/* & 0x80*/) ? soBaseVol & soMask : 0;
3239 +void Channel3::update(uint_least32_t *buf, const unsigned soBaseVol, unsigned cycles) {
3240 + const unsigned outBase = (nr0/* & 0x80*/) ? soBaseVol & soMask : 0;
3242 if (outBase && rShift != 4) {
3243 - const unsigned long endCycles = cycleCounter + cycles;
3244 + const unsigned endCycles = cycleCounter + cycles;
3246 for (;;) {
3247 - const unsigned long nextMajorEvent = lengthCounter.getCounter() < endCycles ? lengthCounter.getCounter() : endCycles;
3248 - unsigned long out = outBase * (master ? ((sampleBuf >> (~wavePos << 2 & 4) & 0xF) >> rShift) * 2 - 15ul : 0 - 15ul);
3249 + const unsigned nextMajorEvent = lengthCounter.getCounter() < endCycles ? lengthCounter.getCounter() : endCycles;
3250 + unsigned out = outBase * (master ? ((sampleBuf >> (~wavePos << 2 & 4) & 0xF) >> rShift) * 2 - 15ul : 0 - 15ul);
3252 while (waveCounter <= nextMajorEvent) {
3253 *buf += out - prevOut;
3254 @@ -181,7 +185,7 @@ void Channel3::update(uint_least32_t *buf, const unsigned long soBaseVol, unsign
3256 } else {
3257 if (outBase) {
3258 - const unsigned long out = outBase * (0 - 15ul);
3259 + const unsigned out = outBase * (0 - 15ul);
3261 *buf += out - prevOut;
3262 prevOut = out;
3263 @@ -208,4 +212,23 @@ void Channel3::update(uint_least32_t *buf, const unsigned long soBaseVol, unsign
3267 +void Channel3::loadOrSave(loadsave& state) {
3268 + state(waveRam, 0x10);
3269 + //disableMaster has no saveable state.
3270 + lengthCounter.loadOrSave(state);
3271 + state(cycleCounter);
3272 + state(soMask);
3273 + state(prevOut);
3274 + state(waveCounter);
3275 + state(lastReadTime);
3276 + state(nr0);
3277 + state(nr3);
3278 + state(nr4);
3279 + state(wavePos);
3280 + state(rShift);
3281 + state(sampleBuf);
3282 + state(master);
3283 + state(cgb);
3287 diff --git a/libgambatte/src/sound/channel3.h b/libgambatte/src/sound/channel3.h
3288 index e80ec68..8fe0fe3 100644
3289 --- a/libgambatte/src/sound/channel3.h
3290 +++ b/libgambatte/src/sound/channel3.h
3291 @@ -19,9 +19,14 @@
3292 #ifndef SOUND_CHANNEL3_H
3293 #define SOUND_CHANNEL3_H
3296 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
3297 +// - Make it rerecording-friendly.
3299 #include "gbint.h"
3300 #include "master_disabler.h"
3301 #include "length_counter.h"
3302 +#include "loadsave.h"
3304 namespace gambatte {
3306 @@ -29,10 +34,10 @@ struct SaveState;
3308 class Channel3 {
3309 class Ch3MasterDisabler : public MasterDisabler {
3310 - unsigned long &waveCounter;
3311 + unsigned &waveCounter;
3313 public:
3314 - Ch3MasterDisabler(bool &m, unsigned long &wC) : MasterDisabler(m), waveCounter(wC) {}
3315 + Ch3MasterDisabler(bool &m, unsigned &wC) : MasterDisabler(m), waveCounter(wC) {}
3316 void operator()() { MasterDisabler::operator()(); waveCounter = SoundUnit::COUNTER_DISABLED; }
3319 @@ -41,11 +46,11 @@ class Channel3 {
3320 Ch3MasterDisabler disableMaster;
3321 LengthCounter lengthCounter;
3323 - unsigned long cycleCounter;
3324 - unsigned long soMask;
3325 - unsigned long prevOut;
3326 - unsigned long waveCounter;
3327 - unsigned long lastReadTime;
3328 + unsigned cycleCounter;
3329 + unsigned soMask;
3330 + unsigned prevOut;
3331 + unsigned waveCounter;
3332 + unsigned lastReadTime;
3334 unsigned char nr0;
3335 unsigned char nr3;
3336 @@ -57,7 +62,7 @@ class Channel3 {
3337 bool master;
3338 bool cgb;
3340 - void updateWaveCounter(unsigned long cc);
3341 + void updateWaveCounter(unsigned cc);
3343 public:
3344 Channel3();
3345 @@ -72,8 +77,8 @@ public:
3346 void setNr2(unsigned data);
3347 void setNr3(unsigned data) { nr3 = data; }
3348 void setNr4(unsigned data);
3349 - void setSo(unsigned long soMask);
3350 - void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles);
3351 + void setSo(unsigned soMask);
3352 + void update(uint_least32_t *buf, unsigned soBaseVol, unsigned cycles);
3354 unsigned waveRamRead(unsigned index) const {
3355 if (master) {
3356 @@ -96,6 +101,8 @@ public:
3358 waveRam[index] = data;
3361 + void loadOrSave(loadsave& state);
3365 diff --git a/libgambatte/src/sound/channel4.cpp b/libgambatte/src/sound/channel4.cpp
3366 index 35c3c00..9d89db7 100644
3367 --- a/libgambatte/src/sound/channel4.cpp
3368 +++ b/libgambatte/src/sound/channel4.cpp
3369 @@ -20,7 +20,11 @@
3370 #include "../savestate.h"
3371 #include <algorithm>
3373 -static unsigned long toPeriod(const unsigned nr3) {
3375 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
3376 +// - Make it rerecording-friendly.
3378 +static unsigned toPeriod(const unsigned nr3) {
3379 unsigned s = (nr3 >> 4) + 3;
3380 unsigned r = nr3 & 7;
3382 @@ -41,15 +45,15 @@ nr3(0),
3383 master(false)
3386 -void Channel4::Lfsr::updateBackupCounter(const unsigned long cc) {
3387 +void Channel4::Lfsr::updateBackupCounter(const unsigned cc) {
3388 /*if (backupCounter <= cc) {
3389 const unsigned long period = toPeriod(nr3);
3390 backupCounter = cc - (cc - backupCounter) % period + period;
3393 if (backupCounter <= cc) {
3394 - const unsigned long period = toPeriod(nr3);
3395 - unsigned long periods = (cc - backupCounter) / period + 1;
3396 + const unsigned period = toPeriod(nr3);
3397 + unsigned periods = (cc - backupCounter) / period + 1;
3399 backupCounter += periods * period;
3401 @@ -75,7 +79,7 @@ void Channel4::Lfsr::updateBackupCounter(const unsigned long cc) {
3405 -void Channel4::Lfsr::reviveCounter(const unsigned long cc) {
3406 +void Channel4::Lfsr::reviveCounter(const unsigned cc) {
3407 updateBackupCounter(cc);
3408 counter = backupCounter;
3410 @@ -121,7 +125,7 @@ inline void Channel4::Lfsr::event() {
3411 counter += period * nextStateDistance[reg & 0x3F];*/
3414 -void Channel4::Lfsr::nr3Change(const unsigned newNr3, const unsigned long cc) {
3415 +void Channel4::Lfsr::nr3Change(const unsigned newNr3, const unsigned cc) {
3416 updateBackupCounter(cc);
3417 nr3 = newNr3;
3419 @@ -129,7 +133,7 @@ void Channel4::Lfsr::nr3Change(const unsigned newNr3, const unsigned long cc) {
3420 // counter = backupCounter + toPeriod(nr3) * (nextStateDistance[reg & 0x3F] - 1);
3423 -void Channel4::Lfsr::nr4Init(unsigned long cc) {
3424 +void Channel4::Lfsr::nr4Init(unsigned cc) {
3425 disableMaster();
3426 updateBackupCounter(cc);
3427 master = true;
3428 @@ -138,19 +142,19 @@ void Channel4::Lfsr::nr4Init(unsigned long cc) {
3429 // counter = backupCounter + toPeriod(nr3) * (nextStateDistance[reg & 0x3F] - 1);
3432 -void Channel4::Lfsr::reset(const unsigned long cc) {
3433 +void Channel4::Lfsr::reset(const unsigned cc) {
3434 nr3 = 0;
3435 disableMaster();
3436 backupCounter = cc + toPeriod(nr3);
3439 -void Channel4::Lfsr::resetCounters(const unsigned long oldCc) {
3440 +void Channel4::Lfsr::resetCounters(const unsigned oldCc) {
3441 updateBackupCounter(oldCc);
3442 backupCounter -= COUNTER_MAX;
3443 SoundUnit::resetCounters(oldCc);
3446 -void Channel4::Lfsr::saveState(SaveState &state, const unsigned long cc) {
3447 +void Channel4::Lfsr::saveState(SaveState &state, const unsigned cc) {
3448 updateBackupCounter(cc);
3449 state.spu.ch4.lfsr.counter = backupCounter;
3450 state.spu.ch4.lfsr.reg = reg;
3451 @@ -219,7 +223,7 @@ void Channel4::setNr4(const unsigned data) {
3452 setEvent();
3455 -void Channel4::setSo(const unsigned long soMask) {
3456 +void Channel4::setSo(const unsigned soMask) {
3457 this->soMask = soMask;
3458 staticOutputTest(cycleCounter);
3459 setEvent();
3460 @@ -258,15 +262,15 @@ void Channel4::loadState(const SaveState &state) {
3461 master = state.spu.ch4.master;
3464 -void Channel4::update(uint_least32_t *buf, const unsigned long soBaseVol, unsigned long cycles) {
3465 - const unsigned long outBase = envelopeUnit.dacIsOn() ? soBaseVol & soMask : 0;
3466 - const unsigned long outLow = outBase * (0 - 15ul);
3467 - const unsigned long endCycles = cycleCounter + cycles;
3468 +void Channel4::update(uint_least32_t *buf, const unsigned soBaseVol, unsigned cycles) {
3469 + const unsigned outBase = envelopeUnit.dacIsOn() ? soBaseVol & soMask : 0;
3470 + const unsigned outLow = outBase * (0 - 15ul);
3471 + const unsigned endCycles = cycleCounter + cycles;
3473 for (;;) {
3474 - const unsigned long outHigh = /*master ? */outBase * (envelopeUnit.getVolume() * 2 - 15ul)/* : outLow*/;
3475 - const unsigned long nextMajorEvent = nextEventUnit->getCounter() < endCycles ? nextEventUnit->getCounter() : endCycles;
3476 - unsigned long out = lfsr.isHighState() ? outHigh : outLow;
3477 + const unsigned outHigh = /*master ? */outBase * (envelopeUnit.getVolume() * 2 - 15ul)/* : outLow*/;
3478 + const unsigned nextMajorEvent = nextEventUnit->getCounter() < endCycles ? nextEventUnit->getCounter() : endCycles;
3479 + unsigned out = lfsr.isHighState() ? outHigh : outLow;
3481 while (lfsr.getCounter() <= nextMajorEvent) {
3482 *buf += out - prevOut;
3483 @@ -301,4 +305,23 @@ void Channel4::update(uint_least32_t *buf, const unsigned long soBaseVol, unsign
3487 +void Channel4::loadOrSave(loadsave& state) {
3488 + //DisableMaster has no state.
3489 + lengthCounter.loadOrSave(state);
3490 + envelopeUnit.loadOrSave(state);
3491 + lfsr.loadOrSave(state);
3493 + state.startEnumeration();
3494 + state.enumerate<SoundUnit*>(nextEventUnit, NULL, 0);
3495 + state.enumerate<SoundUnit*>(nextEventUnit, &lengthCounter, 1);
3496 + state.enumerate<SoundUnit*>(nextEventUnit, &envelopeUnit, 2);
3497 + state.endEnumeration();
3499 + state(cycleCounter);
3500 + state(soMask);
3501 + state(prevOut);
3502 + state(nr4);
3503 + state(master);
3507 diff --git a/libgambatte/src/sound/channel4.h b/libgambatte/src/sound/channel4.h
3508 index 00b0b5a..d52d9f7 100644
3509 --- a/libgambatte/src/sound/channel4.h
3510 +++ b/libgambatte/src/sound/channel4.h
3511 @@ -24,6 +24,11 @@
3512 #include "length_counter.h"
3513 #include "envelope_unit.h"
3514 #include "static_output_tester.h"
3515 +#include "loadsave.h"
3518 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
3519 +// - Make it rerecording-friendly.
3521 namespace gambatte {
3523 @@ -31,26 +36,33 @@ struct SaveState;
3525 class Channel4 {
3526 class Lfsr : public SoundUnit {
3527 - unsigned long backupCounter;
3528 + unsigned backupCounter;
3529 unsigned short reg;
3530 unsigned char nr3;
3531 bool master;
3533 - void updateBackupCounter(unsigned long cc);
3534 + void updateBackupCounter(unsigned cc);
3536 public:
3537 Lfsr();
3538 void event();
3539 bool isHighState() const { return ~reg & 1; }
3540 - void nr3Change(unsigned newNr3, unsigned long cc);
3541 - void nr4Init(unsigned long cc);
3542 - void reset(unsigned long cc);
3543 - void saveState(SaveState &state, const unsigned long cc);
3544 + void nr3Change(unsigned newNr3, unsigned cc);
3545 + void nr4Init(unsigned cc);
3546 + void reset(unsigned cc);
3547 + void saveState(SaveState &state, const unsigned cc);
3548 void loadState(const SaveState &state);
3549 - void resetCounters(unsigned long oldCc);
3550 + void resetCounters(unsigned oldCc);
3551 void disableMaster() { killCounter(); master = false; reg = 0xFF; }
3552 void killCounter() { counter = COUNTER_DISABLED; }
3553 - void reviveCounter(unsigned long cc);
3554 + void reviveCounter(unsigned cc);
3555 + void loadOrSave(loadsave& state) {
3556 + loadOrSave2(state);
3557 + state(backupCounter);
3558 + state(reg);
3559 + state(nr3);
3560 + state(master);
3564 class Ch4MasterDisabler : public MasterDisabler {
3565 @@ -70,9 +82,9 @@ class Channel4 {
3567 SoundUnit *nextEventUnit;
3569 - unsigned long cycleCounter;
3570 - unsigned long soMask;
3571 - unsigned long prevOut;
3572 + unsigned cycleCounter;
3573 + unsigned soMask;
3574 + unsigned prevOut;
3576 unsigned char nr4;
3577 bool master;
3578 @@ -86,15 +98,17 @@ public:
3579 void setNr3(unsigned data) { lfsr.nr3Change(data, cycleCounter); /*setEvent();*/ }
3580 void setNr4(unsigned data);
3582 - void setSo(unsigned long soMask);
3583 + void setSo(unsigned soMask);
3584 bool isActive() const { return master; }
3586 - void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles);
3587 + void update(uint_least32_t *buf, unsigned soBaseVol, unsigned cycles);
3589 void reset();
3590 void init(bool cgb);
3591 void saveState(SaveState &state);
3592 void loadState(const SaveState &state);
3594 + void loadOrSave(loadsave& state);
3598 diff --git a/libgambatte/src/sound/duty_unit.cpp b/libgambatte/src/sound/duty_unit.cpp
3599 index 23b8e52..83cedbf 100644
3600 --- a/libgambatte/src/sound/duty_unit.cpp
3601 +++ b/libgambatte/src/sound/duty_unit.cpp
3602 @@ -19,6 +19,10 @@
3603 #include "duty_unit.h"
3604 #include <algorithm>
3607 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
3608 +// - Make it rerecording-friendly.
3610 static inline bool toOutState(const unsigned duty, const unsigned pos) {
3611 static const unsigned char duties[4] = { 0x80, 0x81, 0xE1, 0x7E };
3613 @@ -31,9 +35,9 @@ static inline unsigned toPeriod(const unsigned freq) {
3615 namespace gambatte {
3617 -void DutyUnit::updatePos(const unsigned long cc) {
3618 +void DutyUnit::updatePos(const unsigned cc) {
3619 if (cc >= nextPosUpdate) {
3620 - const unsigned long inc = (cc - nextPosUpdate) / period + 1;
3621 + const unsigned inc = (cc - nextPosUpdate) / period + 1;
3622 nextPosUpdate += period * inc;
3623 pos += inc;
3624 pos &= 7;
3625 @@ -59,7 +63,7 @@ void DutyUnit::setCounter() {
3626 counter = COUNTER_DISABLED;
3629 -void DutyUnit::setFreq(const unsigned newFreq, const unsigned long cc) {
3630 +void DutyUnit::setFreq(const unsigned newFreq, const unsigned cc) {
3631 updatePos(cc);
3632 period = toPeriod(newFreq);
3633 setCounter();
3634 @@ -77,17 +81,17 @@ void DutyUnit::event() {
3635 counter += inc;
3638 -void DutyUnit::nr1Change(const unsigned newNr1, const unsigned long cc) {
3639 +void DutyUnit::nr1Change(const unsigned newNr1, const unsigned cc) {
3640 updatePos(cc);
3641 setDuty(newNr1);
3642 setCounter();
3645 -void DutyUnit::nr3Change(const unsigned newNr3, const unsigned long cc) {
3646 +void DutyUnit::nr3Change(const unsigned newNr3, const unsigned cc) {
3647 setFreq((getFreq() & 0x700) | newNr3, cc);
3650 -void DutyUnit::nr4Change(const unsigned newNr4, const unsigned long cc) {
3651 +void DutyUnit::nr4Change(const unsigned newNr4, const unsigned cc) {
3652 setFreq((newNr4 << 8 & 0x700) | (getFreq() & 0xFF), cc);
3654 if (newNr4 & 0x80) {
3655 @@ -112,14 +116,14 @@ void DutyUnit::reset() {
3656 setCounter();
3659 -void DutyUnit::saveState(SaveState::SPU::Duty &dstate, const unsigned long cc) {
3660 +void DutyUnit::saveState(SaveState::SPU::Duty &dstate, const unsigned cc) {
3661 updatePos(cc);
3662 dstate.nextPosUpdate = nextPosUpdate;
3663 dstate.nr3 = getFreq() & 0xFF;
3664 dstate.pos = pos;
3667 -void DutyUnit::loadState(const SaveState::SPU::Duty &dstate, const unsigned nr1, const unsigned nr4, const unsigned long cc) {
3668 +void DutyUnit::loadState(const SaveState::SPU::Duty &dstate, const unsigned nr1, const unsigned nr4, const unsigned cc) {
3669 nextPosUpdate = std::max(dstate.nextPosUpdate, cc);
3670 pos = dstate.pos & 7;
3671 setDuty(nr1);
3672 @@ -128,7 +132,7 @@ void DutyUnit::loadState(const SaveState::SPU::Duty &dstate, const unsigned nr1,
3673 setCounter();
3676 -void DutyUnit::resetCounters(const unsigned long oldCc) {
3677 +void DutyUnit::resetCounters(const unsigned oldCc) {
3678 if (nextPosUpdate == COUNTER_DISABLED)
3679 return;
3681 @@ -142,11 +146,21 @@ void DutyUnit::killCounter() {
3682 setCounter();
3685 -void DutyUnit::reviveCounter(const unsigned long cc) {
3686 +void DutyUnit::reviveCounter(const unsigned cc) {
3687 updatePos(cc);
3688 high = toOutState(duty, pos);
3689 enableEvents = true;
3690 setCounter();
3693 +void DutyUnit::loadOrSave(loadsave& state) {
3694 + loadOrSave2(state);
3695 + state(nextPosUpdate);
3696 + state(period);
3697 + state(pos);
3698 + state(duty);
3699 + state(high);
3700 + state(enableEvents);
3704 diff --git a/libgambatte/src/sound/duty_unit.h b/libgambatte/src/sound/duty_unit.h
3705 index f0fc49c..cf7f3b3 100644
3706 --- a/libgambatte/src/sound/duty_unit.h
3707 +++ b/libgambatte/src/sound/duty_unit.h
3708 @@ -19,14 +19,19 @@
3709 #ifndef DUTY_UNIT_H
3710 #define DUTY_UNIT_H
3713 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
3714 +// - Make it rerecording-friendly.
3716 #include "sound_unit.h"
3717 #include "master_disabler.h"
3718 #include "../savestate.h"
3719 +#include "../loadsave.h"
3721 namespace gambatte {
3723 class DutyUnit : public SoundUnit {
3724 - unsigned long nextPosUpdate;
3725 + unsigned nextPosUpdate;
3726 unsigned short period;
3727 unsigned char pos;
3728 unsigned char duty;
3729 @@ -35,25 +40,27 @@ class DutyUnit : public SoundUnit {
3731 void setCounter();
3732 void setDuty(unsigned nr1);
3733 - void updatePos(unsigned long cc);
3734 + void updatePos(unsigned cc);
3736 public:
3737 DutyUnit();
3738 void event();
3739 bool isHighState() const { return high; }
3740 - void nr1Change(unsigned newNr1, unsigned long cc);
3741 - void nr3Change(unsigned newNr3, unsigned long cc);
3742 - void nr4Change(unsigned newNr4, unsigned long cc);
3743 + void nr1Change(unsigned newNr1, unsigned cc);
3744 + void nr3Change(unsigned newNr3, unsigned cc);
3745 + void nr4Change(unsigned newNr4, unsigned cc);
3746 void reset();
3747 - void saveState(SaveState::SPU::Duty &dstate, unsigned long cc);
3748 - void loadState(const SaveState::SPU::Duty &dstate, unsigned nr1, unsigned nr4, unsigned long cc);
3749 - void resetCounters(unsigned long oldCc);
3750 + void saveState(SaveState::SPU::Duty &dstate, unsigned cc);
3751 + void loadState(const SaveState::SPU::Duty &dstate, unsigned nr1, unsigned nr4, unsigned cc);
3752 + void resetCounters(unsigned oldCc);
3753 void killCounter();
3754 - void reviveCounter(unsigned long cc);
3756 + void reviveCounter(unsigned cc);
3758 + void loadOrSave(loadsave& state);
3760 //intended for use by SweepUnit only.
3761 unsigned getFreq() const { return 2048 - (period >> 1); }
3762 - void setFreq(unsigned newFreq, unsigned long cc);
3763 + void setFreq(unsigned newFreq, unsigned cc);
3766 class DutyMasterDisabler : public MasterDisabler {
3767 diff --git a/libgambatte/src/sound/envelope_unit.cpp b/libgambatte/src/sound/envelope_unit.cpp
3768 index 7bcb783..6036256 100644
3769 --- a/libgambatte/src/sound/envelope_unit.cpp
3770 +++ b/libgambatte/src/sound/envelope_unit.cpp
3771 @@ -19,12 +19,16 @@
3772 #include "envelope_unit.h"
3773 #include <algorithm>
3776 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
3777 +// - Make it rerecording-friendly.
3779 namespace gambatte {
3781 EnvelopeUnit::VolOnOffEvent EnvelopeUnit::nullEvent;
3783 void EnvelopeUnit::event() {
3784 - const unsigned long period = nr2 & 7;
3785 + const unsigned period = nr2 & 7;
3787 if (period) {
3788 unsigned newVol = volume;
3789 @@ -63,9 +67,9 @@ bool EnvelopeUnit::nr2Change(const unsigned newNr2) {
3790 return !(newNr2 & 0xF8);
3793 -bool EnvelopeUnit::nr4Init(const unsigned long cc) {
3794 +bool EnvelopeUnit::nr4Init(const unsigned cc) {
3796 - unsigned long period = nr2 & 7;
3797 + unsigned period = nr2 & 7;
3799 if (!period)
3800 period = 8;
3801 @@ -97,10 +101,17 @@ void EnvelopeUnit::saveState(SaveState::SPU::Env &estate) const {
3802 estate.volume = volume;
3805 -void EnvelopeUnit::loadState(const SaveState::SPU::Env &estate, const unsigned nr2, const unsigned long cc) {
3806 +void EnvelopeUnit::loadState(const SaveState::SPU::Env &estate, const unsigned nr2, const unsigned cc) {
3807 counter = std::max(estate.counter, cc);
3808 volume = estate.volume;
3809 this->nr2 = nr2;
3812 +void EnvelopeUnit::loadOrSave(loadsave& state)
3814 + loadOrSave2(state);
3815 + state(nr2);
3816 + state(volume);
3820 diff --git a/libgambatte/src/sound/envelope_unit.h b/libgambatte/src/sound/envelope_unit.h
3821 index fc2551c..aa0cac4 100644
3822 --- a/libgambatte/src/sound/envelope_unit.h
3823 +++ b/libgambatte/src/sound/envelope_unit.h
3824 @@ -19,8 +19,13 @@
3825 #ifndef ENVELOPE_UNIT_H
3826 #define ENVELOPE_UNIT_H
3829 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
3830 +// - Make it rerecording-friendly.
3832 #include "sound_unit.h"
3833 #include "../savestate.h"
3834 +#include "../loadsave.h"
3836 namespace gambatte {
3838 @@ -28,7 +33,7 @@ class EnvelopeUnit : public SoundUnit {
3839 public:
3840 struct VolOnOffEvent {
3841 virtual ~VolOnOffEvent() {}
3842 - virtual void operator()(unsigned long /*cc*/) {}
3843 + virtual void operator()(unsigned /*cc*/) {}
3846 private:
3847 @@ -43,10 +48,11 @@ public:
3848 bool dacIsOn() const { return nr2 & 0xF8; }
3849 unsigned getVolume() const { return volume; }
3850 bool nr2Change(unsigned newNr2);
3851 - bool nr4Init(unsigned long cycleCounter);
3852 + bool nr4Init(unsigned cycleCounter);
3853 void reset();
3854 void saveState(SaveState::SPU::Env &estate) const;
3855 - void loadState(const SaveState::SPU::Env &estate, unsigned nr2, unsigned long cc);
3856 + void loadState(const SaveState::SPU::Env &estate, unsigned nr2, unsigned cc);
3857 + void loadOrSave(loadsave& state);
3861 diff --git a/libgambatte/src/sound/length_counter.cpp b/libgambatte/src/sound/length_counter.cpp
3862 index d295dc0..0fd9503 100644
3863 --- a/libgambatte/src/sound/length_counter.cpp
3864 +++ b/libgambatte/src/sound/length_counter.cpp
3865 @@ -20,6 +20,10 @@
3866 #include "master_disabler.h"
3867 #include <algorithm>
3870 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
3871 +// - Make it rerecording-friendly.
3873 namespace gambatte {
3875 LengthCounter::LengthCounter(MasterDisabler &disabler, const unsigned mask) :
3876 @@ -36,12 +40,12 @@ void LengthCounter::event() {
3877 disableMaster();
3880 -void LengthCounter::nr1Change(const unsigned newNr1, const unsigned nr4, const unsigned long cycleCounter) {
3881 +void LengthCounter::nr1Change(const unsigned newNr1, const unsigned nr4, const unsigned cycleCounter) {
3882 lengthCounter = (~newNr1 & lengthMask) + 1;
3883 - counter = (nr4 & 0x40) ?( (cycleCounter >> 13) + lengthCounter) << 13 : static_cast<unsigned long>(COUNTER_DISABLED);
3884 + counter = (nr4 & 0x40) ?( (cycleCounter >> 13) + lengthCounter) << 13 : static_cast<unsigned>(COUNTER_DISABLED);
3887 -void LengthCounter::nr4Change(const unsigned oldNr4, const unsigned newNr4, const unsigned long cycleCounter) {
3888 +void LengthCounter::nr4Change(const unsigned oldNr4, const unsigned newNr4, const unsigned cycleCounter) {
3889 if (counter != COUNTER_DISABLED)
3890 lengthCounter = (counter >> 13) - (cycleCounter >> 13);
3892 @@ -83,9 +87,16 @@ void LengthCounter::saveState(SaveState::SPU::LCounter &lstate) const {
3893 lstate.lengthCounter = lengthCounter;
3896 -void LengthCounter::loadState(const SaveState::SPU::LCounter &lstate, const unsigned long cc) {
3897 +void LengthCounter::loadState(const SaveState::SPU::LCounter &lstate, const unsigned cc) {
3898 counter = std::max(lstate.counter, cc);
3899 lengthCounter = lstate.lengthCounter;
3902 +void LengthCounter::loadOrSave(loadsave& state)
3904 + loadOrSave2(state);
3905 + state(lengthCounter);
3906 + state(cgb);
3910 diff --git a/libgambatte/src/sound/length_counter.h b/libgambatte/src/sound/length_counter.h
3911 index 3360ab1..f21fc9d 100644
3912 --- a/libgambatte/src/sound/length_counter.h
3913 +++ b/libgambatte/src/sound/length_counter.h
3914 @@ -19,8 +19,13 @@
3915 #ifndef LENGTH_COUNTER_H
3916 #define LENGTH_COUNTER_H
3919 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
3920 +// - Make it rerecording-friendly.
3922 #include "sound_unit.h"
3923 #include "../savestate.h"
3924 +#include "../loadsave.h"
3926 namespace gambatte {
3928 @@ -35,12 +40,14 @@ class LengthCounter : public SoundUnit {
3929 public:
3930 LengthCounter(MasterDisabler &disabler, unsigned lengthMask);
3931 void event();
3932 - void nr1Change(unsigned newNr1, unsigned nr4, unsigned long cc);
3933 - void nr4Change(unsigned oldNr4, unsigned newNr4, unsigned long cc);
3934 + void nr1Change(unsigned newNr1, unsigned nr4, unsigned cc);
3935 + void nr4Change(unsigned oldNr4, unsigned newNr4, unsigned cc);
3936 // void reset();
3937 void init(bool cgb);
3938 void saveState(SaveState::SPU::LCounter &lstate) const;
3939 - void loadState(const SaveState::SPU::LCounter &lstate, unsigned long cc);
3940 + void loadState(const SaveState::SPU::LCounter &lstate, unsigned cc);
3942 + void loadOrSave(loadsave& state);
3946 diff --git a/libgambatte/src/sound/sound_unit.h b/libgambatte/src/sound/sound_unit.h
3947 index b1ca691..55b69a9 100644
3948 --- a/libgambatte/src/sound/sound_unit.h
3949 +++ b/libgambatte/src/sound/sound_unit.h
3950 @@ -19,19 +19,28 @@
3951 #ifndef SOUND_UNIT_H
3952 #define SOUND_UNIT_H
3955 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
3956 +// - Make it rerecording-friendly.
3958 +#include "../loadsave.h"
3960 namespace gambatte {
3962 class SoundUnit {
3963 protected:
3964 - unsigned long counter;
3965 + unsigned counter;
3966 public:
3967 enum { COUNTER_MAX = 0x80000000u, COUNTER_DISABLED = 0xFFFFFFFFu };
3969 SoundUnit() : counter(COUNTER_DISABLED) {}
3970 virtual ~SoundUnit() {}
3971 virtual void event() = 0;
3972 - unsigned long getCounter() const { return counter; }
3973 - virtual void resetCounters(unsigned long /*oldCc*/) { if (counter != COUNTER_DISABLED) counter -= COUNTER_MAX; }
3974 + unsigned getCounter() const { return counter; }
3975 + virtual void resetCounters(unsigned /*oldCc*/) { if (counter != COUNTER_DISABLED) counter -= COUNTER_MAX; }
3976 + void loadOrSave2(loadsave& state) {
3977 + state(counter);
3982 diff --git a/libgambatte/src/sound/static_output_tester.h b/libgambatte/src/sound/static_output_tester.h
3983 index ac785e5..4ef164f 100644
3984 --- a/libgambatte/src/sound/static_output_tester.h
3985 +++ b/libgambatte/src/sound/static_output_tester.h
3986 @@ -19,6 +19,10 @@
3987 #ifndef STATIC_OUTPUT_TESTER_H
3988 #define STATIC_OUTPUT_TESTER_H
3991 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
3992 +// - Make it rerecording-friendly.
3994 #include "envelope_unit.h"
3996 namespace gambatte {
3997 @@ -29,11 +33,11 @@ class StaticOutputTester : public EnvelopeUnit::VolOnOffEvent {
3998 Unit &unit;
3999 public:
4000 StaticOutputTester(const Channel &ch, Unit &unit) : ch(ch), unit(unit) {}
4001 - void operator()(unsigned long cc);
4002 + void operator()(unsigned cc);
4005 template<class Channel, class Unit>
4006 -void StaticOutputTester<Channel, Unit>::operator()(const unsigned long cc) {
4007 +void StaticOutputTester<Channel, Unit>::operator()(const unsigned cc) {
4008 if (ch.soMask && ch.master && ch.envelopeUnit.getVolume())
4009 unit.reviveCounter(cc);
4010 else
4011 diff --git a/libgambatte/src/state_osd_elements.cpp b/libgambatte/src/state_osd_elements.cpp
4012 index 4ec0c52..ffd91a3 100644
4013 --- a/libgambatte/src/state_osd_elements.cpp
4014 +++ b/libgambatte/src/state_osd_elements.cpp
4015 @@ -22,6 +22,10 @@
4016 #include <fstream>
4017 #include <cstring>
4020 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
4021 +// - Make it rerecording-friendly.
4023 namespace {
4025 using namespace gambatte;
4026 @@ -71,7 +75,7 @@ life(4 * 60) {
4027 print(pixels + 1 * w() + 1, w(), 0xE0E0E0ul, txt);*/
4029 print(pixels, w(), ShadeFill(), txt);
4030 - print(pixels + 1 * w() + 1, w(), 0xE0E0E0ul, txt);
4031 + print(pixels + 1 * w() + 1, w(), 0xE0E0E0u, txt);
4034 ShadedTextOsdElment::~ShadedTextOsdElment() {
4035 @@ -142,7 +146,7 @@ life(4 * 60) {
4037 static const char txt[] = { E,m,p,t,bitmapfont::y,0 };
4039 - print(pixels + 3 + (StateSaver::SS_HEIGHT / 2 - bitmapfont::HEIGHT / 2) * StateSaver::SS_WIDTH, StateSaver::SS_WIDTH, 0x808080ul, txt);
4040 + print(pixels + 3 + (StateSaver::SS_HEIGHT / 2 - bitmapfont::HEIGHT / 2) * StateSaver::SS_WIDTH, StateSaver::SS_WIDTH, 0x808080u, txt);
4044 diff --git a/libgambatte/src/statesaver.cpp b/libgambatte/src/statesaver.cpp
4045 index 5d6c313..29fac25 100644
4046 --- a/libgambatte/src/statesaver.cpp
4047 +++ b/libgambatte/src/statesaver.cpp
4048 @@ -23,6 +23,11 @@
4049 #include <cstring>
4050 #include <algorithm>
4051 #include <fstream>
4052 +#include <sstream>
4055 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
4056 +// - Make it rerecording-friendly.
4058 namespace {
4060 @@ -41,36 +46,36 @@ enum AsciiChar {
4062 struct Saver {
4063 const char *label;
4064 - void (*save)(std::ofstream &file, const SaveState &state);
4065 - void (*load)(std::ifstream &file, SaveState &state);
4066 - unsigned char labelsize;
4067 + void (*save)(std::ostream &file, const SaveState &state);
4068 + void (*load)(std::istream &file, SaveState &state);
4069 + unsigned int labelsize;
4072 static inline bool operator<(const Saver &l, const Saver &r) {
4073 return std::strcmp(l.label, r.label) < 0;
4076 -static void put24(std::ofstream &file, const unsigned long data) {
4077 +static void put24(std::ostream &file, const unsigned long data) {
4078 file.put(data >> 16 & 0xFF);
4079 file.put(data >> 8 & 0xFF);
4080 file.put(data & 0xFF);
4083 -static void put32(std::ofstream &file, const unsigned long data) {
4084 +static void put32(std::ostream &file, const unsigned long data) {
4085 file.put(data >> 24 & 0xFF);
4086 file.put(data >> 16 & 0xFF);
4087 file.put(data >> 8 & 0xFF);
4088 file.put(data & 0xFF);
4091 -static void write(std::ofstream &file, const unsigned char data) {
4092 +static void write(std::ostream &file, const unsigned char data) {
4093 static const char inf[] = { 0x00, 0x00, 0x01 };
4095 file.write(inf, sizeof(inf));
4096 file.put(data & 0xFF);
4099 -static void write(std::ofstream &file, const unsigned short data) {
4100 +static void write(std::ostream &file, const unsigned short data) {
4101 static const char inf[] = { 0x00, 0x00, 0x02 };
4103 file.write(inf, sizeof(inf));
4104 @@ -78,30 +83,30 @@ static void write(std::ofstream &file, const unsigned short data) {
4105 file.put(data & 0xFF);
4108 -static void write(std::ofstream &file, const unsigned long data) {
4109 +static void write(std::ostream &file, const unsigned int data) {
4110 static const char inf[] = { 0x00, 0x00, 0x04 };
4112 file.write(inf, sizeof(inf));
4113 put32(file, data);
4116 -static inline void write(std::ofstream &file, const bool data) {
4117 +static inline void write(std::ostream &file, const bool data) {
4118 write(file, static_cast<unsigned char>(data));
4121 -static void write(std::ofstream &file, const unsigned char *data, const unsigned long sz) {
4122 +static void write(std::ostream &file, const unsigned char *data, const unsigned long sz) {
4123 put24(file, sz);
4124 file.write(reinterpret_cast<const char*>(data), sz);
4127 -static void write(std::ofstream &file, const bool *data, const unsigned long sz) {
4128 +static void write(std::ostream &file, const bool *data, const unsigned long sz) {
4129 put24(file, sz);
4131 for (unsigned long i = 0; i < sz; ++i)
4132 file.put(data[i]);
4135 -static unsigned long get24(std::ifstream &file) {
4136 +static unsigned long get24(std::istream &file) {
4137 unsigned long tmp = file.get() & 0xFF;
4139 tmp = tmp << 8 | (file.get() & 0xFF);
4140 @@ -109,15 +114,15 @@ static unsigned long get24(std::ifstream &file) {
4141 return tmp << 8 | (file.get() & 0xFF);
4144 -static unsigned long read(std::ifstream &file) {
4145 - unsigned long size = get24(file);
4146 +static unsigned read(std::istream &file) {
4147 + unsigned size = get24(file);
4149 if (size > 4) {
4150 file.ignore(size - 4);
4151 size = 4;
4154 - unsigned long out = 0;
4155 + unsigned out = 0;
4157 switch (size) {
4158 case 4: out = (out | (file.get() & 0xFF)) << 8;
4159 @@ -129,23 +134,23 @@ static unsigned long read(std::ifstream &file) {
4160 return out;
4163 -static inline void read(std::ifstream &file, unsigned char &data) {
4164 +static inline void read(std::istream &file, unsigned char &data) {
4165 data = read(file) & 0xFF;
4168 -static inline void read(std::ifstream &file, unsigned short &data) {
4169 +static inline void read(std::istream &file, unsigned short &data) {
4170 data = read(file) & 0xFFFF;
4173 -static inline void read(std::ifstream &file, unsigned long &data) {
4174 +static inline void read(std::istream &file, unsigned int &data) {
4175 data = read(file);
4178 -static inline void read(std::ifstream &file, bool &data) {
4179 +static inline void read(std::istream &file, bool &data) {
4180 data = read(file);
4183 -static void read(std::ifstream &file, unsigned char *data, unsigned long sz) {
4184 +static void read(std::istream &file, unsigned char *data, unsigned long sz) {
4185 const unsigned long size = get24(file);
4187 if (size < sz)
4188 @@ -160,7 +165,7 @@ static void read(std::ifstream &file, unsigned char *data, unsigned long sz) {
4192 -static void read(std::ifstream &file, bool *data, unsigned long sz) {
4193 +static void read(std::istream &file, bool *data, unsigned long sz) {
4194 const unsigned long size = get24(file);
4196 if (size < sz)
4197 @@ -193,8 +198,8 @@ public:
4200 static void pushSaver(SaverList::list_t &list, const char *label,
4201 - void (*save)(std::ofstream &file, const SaveState &state),
4202 - void (*load)(std::ifstream &file, SaveState &state), unsigned labelsize) {
4203 + void (*save)(std::ostream &file, const SaveState &state),
4204 + void (*load)(std::istream &file, SaveState &state), unsigned labelsize) {
4205 const Saver saver = { label, save, load, labelsize };
4206 list.push_back(saver);
4208 @@ -202,8 +207,8 @@ static void pushSaver(SaverList::list_t &list, const char *label,
4209 SaverList::SaverList() {
4210 #define ADD(arg) do { \
4211 struct Func { \
4212 - static void save(std::ofstream &file, const SaveState &state) { write(file, state.arg); } \
4213 - static void load(std::ifstream &file, SaveState &state) { read(file, state.arg); } \
4214 + static void save(std::ostream &file, const SaveState &state) { write(file, state.arg); } \
4215 + static void load(std::istream &file, SaveState &state) { read(file, state.arg); } \
4216 }; \
4218 pushSaver(list, label, Func::save, Func::load, sizeof label); \
4219 @@ -211,8 +216,8 @@ SaverList::SaverList() {
4221 #define ADDPTR(arg) do { \
4222 struct Func { \
4223 - static void save(std::ofstream &file, const SaveState &state) { write(file, state.arg.get(), state.arg.getSz()); } \
4224 - static void load(std::ifstream &file, SaveState &state) { read(file, state.arg.ptr, state.arg.getSz()); } \
4225 + static void save(std::ostream &file, const SaveState &state) { write(file, state.arg.get(), state.arg.getSz()); } \
4226 + static void load(std::istream &file, SaveState &state) { read(file, state.arg.ptr, state.arg.getSz()); } \
4227 }; \
4229 pushSaver(list, label, Func::save, Func::load, sizeof label); \
4230 @@ -220,8 +225,8 @@ SaverList::SaverList() {
4232 #define ADDARRAY(arg) do { \
4233 struct Func { \
4234 - static void save(std::ofstream &file, const SaveState &state) { write(file, state.arg, sizeof(state.arg)); } \
4235 - static void load(std::ifstream &file, SaveState &state) { read(file, state.arg, sizeof(state.arg)); } \
4236 + static void save(std::ostream &file, const SaveState &state) { write(file, state.arg, sizeof(state.arg)); } \
4237 + static void load(std::istream &file, SaveState &state) { read(file, state.arg, sizeof(state.arg)); } \
4238 }; \
4240 pushSaver(list, label, Func::save, Func::load, sizeof label); \
4241 @@ -373,7 +378,7 @@ static void blendPxlPairs(PxlSum *const dst, const PxlSum *const sums) {
4242 dst->g = sums[1].g * 8 + (sums[0].g - sums[1].g ) * 3;
4245 -static void writeSnapShot(std::ofstream &file, const uint_least32_t *pixels, const int pitch) {
4246 +static void writeSnapShot(std::ostream &file, const uint_least32_t *pixels, const int pitch) {
4247 put24(file, pixels ? StateSaver::SS_WIDTH * StateSaver::SS_HEIGHT * sizeof(uint_least32_t) : 0);
4249 if (pixels) {
4250 @@ -405,15 +410,9 @@ static void writeSnapShot(std::ofstream &file, const uint_least32_t *pixels, con
4252 static SaverList list;
4254 -} // anon namespace
4256 -namespace gambatte {
4258 -void StateSaver::saveState(const SaveState &state,
4259 +void saveStateI(const SaveState &state,
4260 const uint_least32_t *const videoBuf,
4261 - const int pitch, const std::string &filename) {
4262 - std::ofstream file(filename.c_str(), std::ios_base::binary);
4264 + const int pitch, std::ostream& file) {
4265 if (file.fail())
4266 return;
4268 @@ -427,9 +426,7 @@ void StateSaver::saveState(const SaveState &state,
4272 -bool StateSaver::loadState(SaveState &state, const std::string &filename) {
4273 - std::ifstream file(filename.c_str(), std::ios_base::binary);
4275 +bool loadStateI(SaveState &state, std::istream& file) {
4276 if (file.fail() || file.get() != 0)
4277 return false;
4279 @@ -465,4 +462,36 @@ bool StateSaver::loadState(SaveState &state, const std::string &filename) {
4280 return true;
4283 +} // anon namespace
4285 +namespace gambatte {
4288 +void StateSaver::saveState(const SaveState &state,
4289 + const uint_least32_t *const videoBuf,
4290 + const int pitch, const std::string &filename) {
4291 + std::ofstream file(filename.c_str(), std::ios_base::binary);
4292 + saveStateI(state, videoBuf, pitch, file);
4295 +bool StateSaver::loadState(SaveState &state, const std::string &filename) {
4296 + std::ifstream file(filename.c_str(), std::ios_base::binary);
4297 + return loadStateI(state, file);
4300 +void StateSaver::saveState(const SaveState &state,
4301 + std::vector<char>& data) {
4302 + std::ostringstream file;
4303 + saveStateI(state, NULL, 160, file);
4304 + std::string out = file.str();
4305 + data.resize(out.size());
4306 + std::copy(out.begin(), out.end(), data.begin());
4309 +bool StateSaver::loadState(SaveState &state, const std::vector<char>& data) {
4310 + std::string in(data.begin(), data.end());
4311 + std::istringstream file(in);
4312 + return loadStateI(state, file);
4316 diff --git a/libgambatte/src/statesaver.h b/libgambatte/src/statesaver.h
4317 index 2c58a42..f902403 100644
4318 --- a/libgambatte/src/statesaver.h
4319 +++ b/libgambatte/src/statesaver.h
4320 @@ -19,8 +19,13 @@
4321 #ifndef STATESAVER_H
4322 #define STATESAVER_H
4325 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
4326 +// - Make it rerecording-friendly.
4328 #include "gbint.h"
4329 #include <string>
4330 +#include <vector>
4332 namespace gambatte {
4334 @@ -38,6 +43,8 @@ public:
4335 static void saveState(const SaveState &state,
4336 const uint_least32_t *videoBuf, int pitch, const std::string &filename);
4337 static bool loadState(SaveState &state, const std::string &filename);
4338 + static void saveState(const SaveState &state, std::vector<char>& data);
4339 + static bool loadState(SaveState &state, const std::vector<char>& data);
4343 diff --git a/libgambatte/src/tima.cpp b/libgambatte/src/tima.cpp
4344 index 44ce31c..56b6e72 100644
4345 --- a/libgambatte/src/tima.cpp
4346 +++ b/libgambatte/src/tima.cpp
4347 @@ -19,6 +19,10 @@
4348 #include "tima.h"
4349 #include "savestate.h"
4352 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
4353 +// - Make it rerecording-friendly.
4355 static const unsigned char timaClock[4] = { 10, 4, 6, 8 };
4357 namespace gambatte {
4358 @@ -50,12 +54,12 @@ void Tima::loadState(const SaveState &state, const TimaInterruptRequester timaIr
4359 ? tmatime_
4360 : lastUpdate_ + ((256u - tima_) << timaClock[tac_ & 3]) + 3)
4362 - static_cast<unsigned long>(DISABLED_TIME)
4363 + static_cast<unsigned>(DISABLED_TIME)
4367 -void Tima::resetCc(const unsigned long oldCc, const unsigned long newCc, const TimaInterruptRequester timaIrq) {
4368 - const unsigned long dec = oldCc - newCc;
4369 +void Tima::resetCc(const unsigned oldCc, const unsigned newCc, const TimaInterruptRequester timaIrq) {
4370 + const unsigned dec = oldCc - newCc;
4372 if (tac_ & 0x04) {
4373 updateIrq(oldCc, timaIrq);
4374 @@ -69,8 +73,8 @@ void Tima::resetCc(const unsigned long oldCc, const unsigned long newCc, const T
4378 -void Tima::updateTima(const unsigned long cycleCounter) {
4379 - const unsigned long ticks = (cycleCounter - lastUpdate_) >> timaClock[tac_ & 3];
4380 +void Tima::updateTima(const unsigned cycleCounter) {
4381 + const unsigned ticks = (cycleCounter - lastUpdate_) >> timaClock[tac_ & 3];
4383 lastUpdate_ += ticks << timaClock[tac_ & 3];
4385 @@ -81,7 +85,7 @@ void Tima::updateTima(const unsigned long cycleCounter) {
4386 tima_ = tma_;
4389 - unsigned long tmp = tima_ + ticks;
4390 + unsigned tmp = tima_ + ticks;
4392 while (tmp > 0x100)
4393 tmp -= 0x100 - tma_;
4394 @@ -101,7 +105,7 @@ void Tima::updateTima(const unsigned long cycleCounter) {
4395 tima_ = tmp;
4398 -void Tima::setTima(const unsigned data, const unsigned long cycleCounter, const TimaInterruptRequester timaIrq) {
4399 +void Tima::setTima(const unsigned data, const unsigned cycleCounter, const TimaInterruptRequester timaIrq) {
4400 if (tac_ & 0x04) {
4401 updateIrq(cycleCounter, timaIrq);
4402 updateTima(cycleCounter);
4403 @@ -115,7 +119,7 @@ void Tima::setTima(const unsigned data, const unsigned long cycleCounter, const
4404 tima_ = data;
4407 -void Tima::setTma(const unsigned data, const unsigned long cycleCounter, const TimaInterruptRequester timaIrq) {
4408 +void Tima::setTma(const unsigned data, const unsigned cycleCounter, const TimaInterruptRequester timaIrq) {
4409 if (tac_ & 0x04) {
4410 updateIrq(cycleCounter, timaIrq);
4411 updateTima(cycleCounter);
4412 @@ -124,9 +128,9 @@ void Tima::setTma(const unsigned data, const unsigned long cycleCounter, const T
4413 tma_ = data;
4416 -void Tima::setTac(const unsigned data, const unsigned long cycleCounter, const TimaInterruptRequester timaIrq) {
4417 +void Tima::setTac(const unsigned data, const unsigned cycleCounter, const TimaInterruptRequester timaIrq) {
4418 if (tac_ ^ data) {
4419 - unsigned long nextIrqEventTime = timaIrq.nextIrqEventTime();
4420 + unsigned nextIrqEventTime = timaIrq.nextIrqEventTime();
4422 if (tac_ & 0x04) {
4423 updateIrq(cycleCounter, timaIrq);
4424 @@ -156,7 +160,7 @@ void Tima::setTac(const unsigned data, const unsigned long cycleCounter, const T
4425 tac_ = data;
4428 -unsigned Tima::tima(unsigned long cycleCounter) {
4429 +unsigned Tima::tima(unsigned cycleCounter) {
4430 if (tac_ & 0x04)
4431 updateTima(cycleCounter);
4433 @@ -168,4 +172,13 @@ void Tima::doIrqEvent(const TimaInterruptRequester timaIrq) {
4434 timaIrq.setNextIrqEventTime(timaIrq.nextIrqEventTime() + ((256u - tma_) << timaClock[tac_ & 3]));
4437 +void Tima::loadOrSave(loadsave& state)
4439 + state(lastUpdate_);
4440 + state(tmatime_);
4441 + state(tima_);
4442 + state(tma_);
4443 + state(tac_);
4447 diff --git a/libgambatte/src/tima.h b/libgambatte/src/tima.h
4448 index 48e9ccd..2857934 100644
4449 --- a/libgambatte/src/tima.h
4450 +++ b/libgambatte/src/tima.h
4451 @@ -19,6 +19,10 @@
4452 #ifndef TIMA_H
4453 #define TIMA_H
4456 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
4457 +// - Make it rerecording-friendly.
4459 #include "interruptrequester.h"
4461 namespace gambatte {
4462 @@ -29,37 +33,39 @@ class TimaInterruptRequester {
4463 public:
4464 explicit TimaInterruptRequester(InterruptRequester &intreq) : intreq(intreq) {}
4465 void flagIrq() const { intreq.flagIrq(4); }
4466 - unsigned long nextIrqEventTime() const { return intreq.eventTime(TIMA); }
4467 - void setNextIrqEventTime(const unsigned long time) const { intreq.setEventTime<TIMA>(time); }
4468 + unsigned nextIrqEventTime() const { return intreq.eventTime(TIMA); }
4469 + void setNextIrqEventTime(const unsigned time) const { intreq.setEventTime<TIMA>(time); }
4472 class Tima {
4473 - unsigned long lastUpdate_;
4474 - unsigned long tmatime_;
4475 + unsigned lastUpdate_;
4476 + unsigned tmatime_;
4478 unsigned char tima_;
4479 unsigned char tma_;
4480 unsigned char tac_;
4482 - void updateIrq(const unsigned long cc, const TimaInterruptRequester timaIrq) {
4483 + void updateIrq(const unsigned cc, const TimaInterruptRequester timaIrq) {
4484 while (cc >= timaIrq.nextIrqEventTime())
4485 doIrqEvent(timaIrq);
4488 - void updateTima(unsigned long cc);
4489 + void updateTima(unsigned cc);
4491 public:
4492 Tima();
4493 void saveState(SaveState &) const;
4494 void loadState(const SaveState &, TimaInterruptRequester timaIrq);
4495 - void resetCc(unsigned long oldCc, unsigned long newCc, TimaInterruptRequester timaIrq);
4496 + void resetCc(unsigned oldCc, unsigned newCc, TimaInterruptRequester timaIrq);
4498 - void setTima(unsigned tima, unsigned long cc, TimaInterruptRequester timaIrq);
4499 - void setTma(unsigned tma, unsigned long cc, TimaInterruptRequester timaIrq);
4500 - void setTac(unsigned tac, unsigned long cc, TimaInterruptRequester timaIrq);
4501 - unsigned tima(unsigned long cc);
4502 + void setTima(unsigned tima, unsigned cc, TimaInterruptRequester timaIrq);
4503 + void setTma(unsigned tma, unsigned cc, TimaInterruptRequester timaIrq);
4504 + void setTac(unsigned tac, unsigned cc, TimaInterruptRequester timaIrq);
4505 + unsigned tima(unsigned cc);
4507 void doIrqEvent(TimaInterruptRequester timaIrq);
4509 + void loadOrSave(loadsave& state);
4513 diff --git a/libgambatte/src/video.cpp b/libgambatte/src/video.cpp
4514 index 58fc6c1..b0e5c4e 100644
4515 --- a/libgambatte/src/video.cpp
4516 +++ b/libgambatte/src/video.cpp
4517 @@ -21,19 +21,23 @@
4518 #include <cstring>
4519 #include <algorithm>
4522 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
4523 +// - Make it rerecording-friendly.
4525 namespace gambatte {
4527 -void LCD::setDmgPalette(unsigned long *const palette, const unsigned long *const dmgColors, const unsigned data) {
4528 +void LCD::setDmgPalette(uint_least32_t *const palette, const uint_least32_t *const dmgColors, const unsigned data) {
4529 palette[0] = dmgColors[data & 3];
4530 palette[1] = dmgColors[data >> 2 & 3];
4531 palette[2] = dmgColors[data >> 4 & 3];
4532 palette[3] = dmgColors[data >> 6 & 3];
4535 -static unsigned long gbcToRgb32(const unsigned bgr15) {
4536 - const unsigned long r = bgr15 & 0x1F;
4537 - const unsigned long g = bgr15 >> 5 & 0x1F;
4538 - const unsigned long b = bgr15 >> 10 & 0x1F;
4539 +static uint_least32_t gbcToRgb32(const unsigned bgr15) {
4540 + const uint_least32_t r = bgr15 & 0x1F;
4541 + const uint_least32_t g = bgr15 >> 5 & 0x1F;
4542 + const uint_least32_t b = bgr15 >> 10 & 0x1F;
4544 return ((r * 13 + g * 2 + b) >> 1) << 16 | (g * 3 + b) << 9 | (r * 3 + g * 2 + b * 11) >> 1;
4546 @@ -80,7 +84,6 @@ LCD::LCD(const unsigned char *const oamram, const unsigned char *const vram, con
4547 setDmgPaletteColor(i, (3 - (i & 3)) * 85 * 0x010101);
4549 reset(oamram, false);
4550 - setVideoBuffer(0, 160);
4553 void LCD::reset(const unsigned char *const oamram, const bool cgb) {
4554 @@ -89,7 +92,7 @@ void LCD::reset(const unsigned char *const oamram, const bool cgb) {
4555 refreshPalettes();
4558 -static unsigned long mode2IrqSchedule(const unsigned statReg, const LyCounter &lyCounter, const unsigned long cycleCounter) {
4559 +static unsigned mode2IrqSchedule(const unsigned statReg, const LyCounter &lyCounter, const unsigned cycleCounter) {
4560 if (!(statReg & 0x20))
4561 return DISABLED_TIME;
4563 @@ -107,16 +110,16 @@ static unsigned long mode2IrqSchedule(const unsigned statReg, const LyCounter &l
4564 return cycleCounter + next;
4567 -static inline unsigned long m0IrqTimeFromXpos166Time(const unsigned long xpos166Time, const bool cgb, const bool ds) {
4568 +static inline unsigned m0IrqTimeFromXpos166Time(const unsigned xpos166Time, const bool cgb, const bool ds) {
4569 return xpos166Time + cgb - ds;
4572 -static inline unsigned long hdmaTimeFromM0Time(const unsigned long m0Time, const bool ds) {
4573 +static inline unsigned hdmaTimeFromM0Time(const unsigned m0Time, const bool ds) {
4574 return m0Time + 1 - ds;
4577 -static unsigned long nextHdmaTime(const unsigned long lastM0Time,
4578 - const unsigned long nextM0Time, const unsigned long cycleCounter, const bool ds) {
4579 +static unsigned nextHdmaTime(const unsigned lastM0Time,
4580 + const unsigned nextM0Time, const unsigned cycleCounter, const bool ds) {
4581 return cycleCounter < hdmaTimeFromM0Time(lastM0Time, ds)
4582 ? hdmaTimeFromM0Time(lastM0Time, ds)
4583 : hdmaTimeFromM0Time(nextM0Time, ds);
4584 @@ -152,18 +155,18 @@ void LCD::loadState(const SaveState &state, const unsigned char *const oamram) {
4585 lycIrq.reschedule(ppu.lyCounter(), ppu.now());
4587 eventTimes_.setm<ONESHOT_LCDSTATIRQ>(state.ppu.pendingLcdstatIrq
4588 - ? ppu.now() + 1 : static_cast<unsigned long>(DISABLED_TIME));
4589 + ? ppu.now() + 1 : static_cast<unsigned>(DISABLED_TIME));
4590 eventTimes_.setm<ONESHOT_UPDATEWY2>(state.ppu.oldWy != state.mem.ioamhram.get()[0x14A]
4591 - ? ppu.now() + 1 : static_cast<unsigned long>(DISABLED_TIME));
4592 + ? ppu.now() + 1 : static_cast<unsigned>(DISABLED_TIME));
4593 eventTimes_.set<LY_COUNT>(ppu.lyCounter().time());
4594 eventTimes_.setm<SPRITE_MAP>(SpriteMapper::schedule(ppu.lyCounter(), ppu.now()));
4595 eventTimes_.setm<LYC_IRQ>(lycIrq.time());
4596 eventTimes_.setm<MODE1_IRQ>(ppu.lyCounter().nextFrameCycle(144 * 456, ppu.now()));
4597 eventTimes_.setm<MODE2_IRQ>(mode2IrqSchedule(statReg, ppu.lyCounter(), ppu.now()));
4598 - eventTimes_.setm<MODE0_IRQ>((statReg & 0x08) ? ppu.now() + state.ppu.nextM0Irq : static_cast<unsigned long>(DISABLED_TIME));
4599 + eventTimes_.setm<MODE0_IRQ>((statReg & 0x08) ? ppu.now() + state.ppu.nextM0Irq : static_cast<unsigned>(DISABLED_TIME));
4600 eventTimes_.setm<HDMA_REQ>(state.mem.hdmaTransfer
4601 ? nextHdmaTime(ppu.lastM0Time(), nextM0Time_.predictedNextM0Time(), ppu.now(), isDoubleSpeed())
4602 - : static_cast<unsigned long>(DISABLED_TIME));
4603 + : static_cast<unsigned>(DISABLED_TIME));
4604 } else for (int i = 0; i < NUM_MEM_EVENTS; ++i)
4605 eventTimes_.set(static_cast<MemEvent>(i), DISABLED_TIME);
4607 @@ -212,7 +215,7 @@ struct Blend {
4610 template<typename T>
4611 -static void clear(T *buf, const unsigned long color, const int dpitch) {
4612 +static void clear(T *buf, const uint_least32_t color, const int dpitch) {
4613 unsigned lines = 144;
4615 while (lines--) {
4616 @@ -223,11 +226,11 @@ static void clear(T *buf, const unsigned long color, const int dpitch) {
4620 -void LCD::updateScreen(const bool blanklcd, const unsigned long cycleCounter) {
4621 +void LCD::updateScreen(const bool blanklcd, const unsigned cycleCounter, uint_least32_t* vbuffer, unsigned vpitch) {
4622 update(cycleCounter);
4624 if (blanklcd && ppu.frameBuf().fb()) {
4625 - const unsigned long color = ppu.cgb() ? gbcToRgb32(0xFFFF) : dmgColorsRgb32[0];
4626 + const uint_least32_t color = ppu.cgb() ? gbcToRgb32(0xFFFF) : dmgColorsRgb32[0];
4627 clear(ppu.frameBuf().fb(), color, ppu.frameBuf().pitch());
4630 @@ -245,14 +248,15 @@ void LCD::updateScreen(const bool blanklcd, const unsigned long cycleCounter) {
4631 } else
4632 osdElement.reset();
4634 + ppu.frameBuf().blit(vbuffer, vpitch);
4637 -void LCD::resetCc(const unsigned long oldCc, const unsigned long newCc) {
4638 +void LCD::resetCc(const unsigned oldCc, const unsigned newCc) {
4639 update(oldCc);
4640 ppu.resetCc(oldCc, newCc);
4642 if (ppu.lcdc() & 0x80) {
4643 - const unsigned long dec = oldCc - newCc;
4644 + const unsigned dec = oldCc - newCc;
4646 nextM0Time_.invalidatePredictedNextM0Time();
4647 lycIrq.reschedule(ppu.lyCounter(), newCc);
4648 @@ -266,7 +270,7 @@ void LCD::resetCc(const unsigned long oldCc, const unsigned long newCc) {
4652 -void LCD::speedChange(const unsigned long cycleCounter) {
4653 +void LCD::speedChange(const unsigned cycleCounter) {
4654 update(cycleCounter);
4655 ppu.speedChange(cycleCounter);
4657 @@ -290,13 +294,13 @@ void LCD::speedChange(const unsigned long cycleCounter) {
4661 -static inline unsigned long m0TimeOfCurrentLine(const unsigned long nextLyTime,
4662 - const unsigned long lastM0Time, const unsigned long nextM0Time)
4663 +static inline unsigned m0TimeOfCurrentLine(const unsigned nextLyTime,
4664 + const unsigned lastM0Time, const unsigned nextM0Time)
4666 return nextM0Time < nextLyTime ? nextM0Time : lastM0Time;
4669 -unsigned long LCD::m0TimeOfCurrentLine(const unsigned long cc) {
4670 +unsigned LCD::m0TimeOfCurrentLine(const unsigned cc) {
4671 if (cc >= nextM0Time_.predictedNextM0Time()) {
4672 update(cc);
4673 nextM0Time_.predictNextM0Time(ppu);
4674 @@ -306,7 +310,7 @@ unsigned long LCD::m0TimeOfCurrentLine(const unsigned long cc) {
4677 static bool isHdmaPeriod(const LyCounter &lyCounter,
4678 - const unsigned long m0TimeOfCurrentLy, const unsigned long cycleCounter)
4679 + const unsigned m0TimeOfCurrentLy, const unsigned cycleCounter)
4681 const unsigned timeToNextLy = lyCounter.time() - cycleCounter;
4683 @@ -314,7 +318,7 @@ static bool isHdmaPeriod(const LyCounter &lyCounter,
4684 && cycleCounter >= hdmaTimeFromM0Time(m0TimeOfCurrentLy, lyCounter.isDoubleSpeed());
4687 -void LCD::enableHdma(const unsigned long cycleCounter) {
4688 +void LCD::enableHdma(const unsigned cycleCounter) {
4689 if (cycleCounter >= nextM0Time_.predictedNextM0Time()) {
4690 update(cycleCounter);
4691 nextM0Time_.predictNextM0Time(ppu);
4692 @@ -330,14 +334,14 @@ void LCD::enableHdma(const unsigned long cycleCounter) {
4693 eventTimes_.setm<HDMA_REQ>(nextHdmaTime(ppu.lastM0Time(), nextM0Time_.predictedNextM0Time(), cycleCounter, isDoubleSpeed()));
4696 -void LCD::disableHdma(const unsigned long cycleCounter) {
4697 +void LCD::disableHdma(const unsigned cycleCounter) {
4698 if (cycleCounter >= eventTimes_.nextEventTime())
4699 update(cycleCounter);
4701 eventTimes_.setm<HDMA_REQ>(DISABLED_TIME);
4704 -bool LCD::vramAccessible(const unsigned long cycleCounter) {
4705 +bool LCD::vramAccessible(const unsigned cycleCounter) {
4706 if (cycleCounter >= eventTimes_.nextEventTime())
4707 update(cycleCounter);
4709 @@ -346,7 +350,7 @@ bool LCD::vramAccessible(const unsigned long cycleCounter) {
4710 || cycleCounter + isDoubleSpeed() - ppu.cgb() + 2 >= m0TimeOfCurrentLine(cycleCounter);
4713 -bool LCD::cgbpAccessible(const unsigned long cycleCounter) {
4714 +bool LCD::cgbpAccessible(const unsigned cycleCounter) {
4715 if (cycleCounter >= eventTimes_.nextEventTime())
4716 update(cycleCounter);
4718 @@ -356,27 +360,27 @@ bool LCD::cgbpAccessible(const unsigned long cycleCounter) {
4721 static void doCgbColorChange(unsigned char *const pdata,
4722 - unsigned long *const palette, unsigned index, const unsigned data) {
4723 + uint_least32_t *const palette, unsigned index, const unsigned data) {
4724 pdata[index] = data;
4725 index >>= 1;
4726 palette[index] = gbcToRgb32(pdata[index << 1] | pdata[(index << 1) + 1] << 8);
4729 -void LCD::doCgbBgColorChange(unsigned index, const unsigned data, const unsigned long cycleCounter) {
4730 +void LCD::doCgbBgColorChange(unsigned index, const unsigned data, const unsigned cycleCounter) {
4731 if (cgbpAccessible(cycleCounter)) {
4732 update(cycleCounter);
4733 doCgbColorChange(bgpData, ppu.bgPalette(), index, data);
4737 -void LCD::doCgbSpColorChange(unsigned index, const unsigned data, const unsigned long cycleCounter) {
4738 +void LCD::doCgbSpColorChange(unsigned index, const unsigned data, const unsigned cycleCounter) {
4739 if (cgbpAccessible(cycleCounter)) {
4740 update(cycleCounter);
4741 doCgbColorChange(objpData, ppu.spPalette(), index, data);
4745 -bool LCD::oamReadable(const unsigned long cycleCounter) {
4746 +bool LCD::oamReadable(const unsigned cycleCounter) {
4747 if (!(ppu.lcdc() & 0x80) || ppu.inactivePeriodAfterDisplayEnable(cycleCounter))
4748 return true;
4750 @@ -389,7 +393,7 @@ bool LCD::oamReadable(const unsigned long cycleCounter) {
4751 return ppu.lyCounter().ly() >= 144 || cycleCounter + isDoubleSpeed() - ppu.cgb() + 2 >= m0TimeOfCurrentLine(cycleCounter);
4754 -bool LCD::oamWritable(const unsigned long cycleCounter) {
4755 +bool LCD::oamWritable(const unsigned cycleCounter) {
4756 if (!(ppu.lcdc() & 0x80) || ppu.inactivePeriodAfterDisplayEnable(cycleCounter))
4757 return true;
4759 @@ -417,13 +421,13 @@ void LCD::mode3CyclesChange() {
4763 -void LCD::wxChange(const unsigned newValue, const unsigned long cycleCounter) {
4764 +void LCD::wxChange(const unsigned newValue, const unsigned cycleCounter) {
4765 update(cycleCounter + isDoubleSpeed() + 1);
4766 ppu.setWx(newValue);
4767 mode3CyclesChange();
4770 -void LCD::wyChange(const unsigned newValue, const unsigned long cycleCounter) {
4771 +void LCD::wyChange(const unsigned newValue, const unsigned cycleCounter) {
4772 update(cycleCounter + 1);
4773 ppu.setWy(newValue);
4774 // mode3CyclesChange(); // should be safe to wait until after wy2 delay, because no mode3 events are close to when wy1 is read.
4775 @@ -438,18 +442,18 @@ void LCD::wyChange(const unsigned newValue, const unsigned long cycleCounter) {
4779 -void LCD::scxChange(const unsigned newScx, const unsigned long cycleCounter) {
4780 +void LCD::scxChange(const unsigned newScx, const unsigned cycleCounter) {
4781 update(cycleCounter + ppu.cgb() + isDoubleSpeed());
4782 ppu.setScx(newScx);
4783 mode3CyclesChange();
4786 -void LCD::scyChange(const unsigned newValue, const unsigned long cycleCounter) {
4787 +void LCD::scyChange(const unsigned newValue, const unsigned cycleCounter) {
4788 update(cycleCounter + ppu.cgb() + isDoubleSpeed());
4789 ppu.setScy(newValue);
4792 -void LCD::oamChange(const unsigned long cycleCounter) {
4793 +void LCD::oamChange(const unsigned cycleCounter) {
4794 if (ppu.lcdc() & 0x80) {
4795 update(cycleCounter);
4796 ppu.oamChange(cycleCounter);
4797 @@ -457,7 +461,7 @@ void LCD::oamChange(const unsigned long cycleCounter) {
4801 -void LCD::oamChange(const unsigned char *const oamram, const unsigned long cycleCounter) {
4802 +void LCD::oamChange(const unsigned char *const oamram, const unsigned cycleCounter) {
4803 update(cycleCounter);
4804 ppu.oamChange(oamram, cycleCounter);
4806 @@ -465,7 +469,7 @@ void LCD::oamChange(const unsigned char *const oamram, const unsigned long cycle
4807 eventTimes_.setm<SPRITE_MAP>(SpriteMapper::schedule(ppu.lyCounter(), cycleCounter));
4810 -void LCD::lcdcChange(const unsigned data, const unsigned long cycleCounter) {
4811 +void LCD::lcdcChange(const unsigned data, const unsigned cycleCounter) {
4812 const unsigned oldLcdc = ppu.lcdc();
4813 update(cycleCounter);
4815 @@ -522,7 +526,7 @@ void LCD::lcdcChange(const unsigned data, const unsigned long cycleCounter) {
4816 ppu.setLcdc(data, cycleCounter);
4819 -void LCD::lcdstatChange(const unsigned data, const unsigned long cycleCounter) {
4820 +void LCD::lcdstatChange(const unsigned data, const unsigned cycleCounter) {
4821 if (cycleCounter >= eventTimes_.nextEventTime())
4822 update(cycleCounter);
4824 @@ -584,7 +588,7 @@ void LCD::lcdstatChange(const unsigned data, const unsigned long cycleCounter) {
4825 m0Irq_.statRegChange(data, eventTimes_(MODE0_IRQ), cycleCounter, ppu.cgb());
4828 -void LCD::lycRegChange(const unsigned data, const unsigned long cycleCounter) {
4829 +void LCD::lycRegChange(const unsigned data, const unsigned cycleCounter) {
4830 if (data == lycIrq.lycReg())
4831 return;
4833 @@ -628,7 +632,7 @@ void LCD::lycRegChange(const unsigned data, const unsigned long cycleCounter) {
4837 -unsigned LCD::getStat(const unsigned lycReg, const unsigned long cycleCounter) {
4838 +unsigned LCD::getStat(const unsigned lycReg, const unsigned cycleCounter) {
4839 unsigned stat = 0;
4841 if (ppu.lcdc() & 0x80) {
4842 @@ -672,7 +676,7 @@ inline void LCD::doMode2IrqEvent() {
4843 m2IrqStatReg_ = statReg;
4845 if (!(statReg & 0x08)) {
4846 - unsigned long nextTime = eventTimes_(MODE2_IRQ) + ppu.lyCounter().lineTime();
4847 + unsigned nextTime = eventTimes_(MODE2_IRQ) + ppu.lyCounter().lineTime();
4849 if (ly == 0) {
4850 nextTime -= 4;
4851 @@ -726,7 +730,7 @@ inline void LCD::event() {
4853 eventTimes_.setm<MODE0_IRQ>((statReg & 0x08)
4854 ? m0IrqTimeFromXpos166Time(ppu.predictedNextXposTime(166), ppu.cgb(), isDoubleSpeed())
4855 - : static_cast<unsigned long>(DISABLED_TIME));
4856 + : static_cast<unsigned>(DISABLED_TIME));
4857 break;
4859 case ONESHOT_LCDSTATIRQ:
4860 @@ -750,7 +754,7 @@ inline void LCD::event() {
4864 -void LCD::update(const unsigned long cycleCounter) {
4865 +void LCD::update(const unsigned cycleCounter) {
4866 if (!(ppu.lcdc() & 0x80))
4867 return;
4869 @@ -762,15 +766,11 @@ void LCD::update(const unsigned long cycleCounter) {
4870 ppu.update(cycleCounter);
4873 -void LCD::setVideoBuffer(uint_least32_t *const videoBuf, const int pitch) {
4874 - ppu.setFrameBuf(videoBuf, pitch);
4877 -void LCD::setDmgPaletteColor(const unsigned index, const unsigned long rgb32) {
4878 +void LCD::setDmgPaletteColor(const unsigned index, const uint_least32_t rgb32) {
4879 dmgColorsRgb32[index] = rgb32;
4882 -void LCD::setDmgPaletteColor(const unsigned palNum, const unsigned colorNum, const unsigned long rgb32) {
4883 +void LCD::setDmgPaletteColor(const unsigned palNum, const unsigned colorNum, const uint_least32_t rgb32) {
4884 if (palNum > 2 || colorNum > 3)
4885 return;
4887 @@ -778,4 +778,18 @@ void LCD::setDmgPaletteColor(const unsigned palNum, const unsigned colorNum, con
4888 refreshPalettes();
4891 +void LCD::loadOrSave(loadsave& state) {
4892 + ppu.loadOrSave(state);
4893 + state(dmgColorsRgb32, 12);
4894 + state(bgpData, 64);
4895 + state(objpData, 64);
4896 + eventTimes_.loadOrSave(state);
4897 + m0Irq_.loadOrSave(state);
4898 + lycIrq.loadOrSave(state);
4899 + nextM0Time_.loadOrSave(state);
4900 + state(statReg);
4901 + state(m2IrqStatReg_);
4902 + state(m1IrqStatReg_);
4906 diff --git a/libgambatte/src/video.h b/libgambatte/src/video.h
4907 index e7802f1..9fe9454 100644
4908 --- a/libgambatte/src/video.h
4909 +++ b/libgambatte/src/video.h
4910 @@ -19,6 +19,10 @@
4911 #ifndef VIDEO_H
4912 #define VIDEO_H
4915 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
4916 +// - Make it rerecording-friendly.
4918 #include "video/ppu.h"
4919 #include "video/lyc_irq.h"
4920 #include "video/next_m0_time.h"
4921 @@ -36,7 +40,7 @@ public:
4922 explicit VideoInterruptRequester(InterruptRequester * intreq) : intreq(intreq) {}
4923 void flagHdmaReq() const { gambatte::flagHdmaReq(intreq); }
4924 void flagIrq(const unsigned bit) const { intreq->flagIrq(bit); }
4925 - void setNextEventTime(const unsigned long time) const { intreq->setEventTime<VIDEO>(time); }
4926 + void setNextEventTime(const unsigned time) const { intreq->setEventTime<VIDEO>(time); }
4929 class M0Irq {
4930 @@ -50,15 +54,21 @@ public:
4931 statReg_ = statReg;
4932 lycReg_ = lycReg;
4936 + void loadOrSave(loadsave& state)
4938 + state(statReg_);
4939 + state(lycReg_);
4942 void statRegChange(const unsigned statReg,
4943 - const unsigned long nextM0IrqTime, const unsigned long cc, const bool cgb) {
4944 + const unsigned nextM0IrqTime, const unsigned cc, const bool cgb) {
4945 if (nextM0IrqTime - cc > cgb * 2U)
4946 statReg_ = statReg;
4949 void lycRegChange(const unsigned lycReg,
4950 - const unsigned long nextM0IrqTime, const unsigned long cc, const bool ds, const bool cgb) {
4951 + const unsigned nextM0IrqTime, const unsigned cc, const bool ds, const bool cgb) {
4952 if (nextM0IrqTime - cc > cgb * 5 + 1U - ds)
4953 lycReg_ = lycReg;
4955 @@ -94,7 +104,7 @@ class LCD {
4956 VideoInterruptRequester memEventRequester_;
4958 void setMemEvent() {
4959 - const unsigned long nmet = nextMemEventTime();
4960 + const unsigned nmet = nextMemEventTime();
4961 eventMin_.setValue<MEM_EVENT>(nmet);
4962 memEventRequester_.setNextEventTime(nmet);
4964 @@ -103,23 +113,28 @@ class LCD {
4965 explicit EventTimes(const VideoInterruptRequester memEventRequester) : memEventRequester_(memEventRequester) {}
4967 Event nextEvent() const { return static_cast<Event>(eventMin_.min()); }
4968 - unsigned long nextEventTime() const { return eventMin_.minValue(); }
4969 - unsigned long operator()(const Event e) const { return eventMin_.value(e); }
4970 - template<Event e> void set(const unsigned long time) { eventMin_.setValue<e>(time); }
4971 - void set(const Event e, const unsigned long time) { eventMin_.setValue(e, time); }
4972 + unsigned nextEventTime() const { return eventMin_.minValue(); }
4973 + unsigned operator()(const Event e) const { return eventMin_.value(e); }
4974 + template<Event e> void set(const unsigned time) { eventMin_.setValue<e>(time); }
4975 + void set(const Event e, const unsigned time) { eventMin_.setValue(e, time); }
4977 MemEvent nextMemEvent() const { return static_cast<MemEvent>(memEventMin_.min()); }
4978 - unsigned long nextMemEventTime() const { return memEventMin_.minValue(); }
4979 - unsigned long operator()(const MemEvent e) const { return memEventMin_.value(e); }
4980 - template<MemEvent e> void setm(const unsigned long time) { memEventMin_.setValue<e>(time); setMemEvent(); }
4981 - void set(const MemEvent e, const unsigned long time) { memEventMin_.setValue(e, time); setMemEvent(); }
4982 + unsigned nextMemEventTime() const { return memEventMin_.minValue(); }
4983 + unsigned operator()(const MemEvent e) const { return memEventMin_.value(e); }
4984 + template<MemEvent e> void setm(const unsigned time) { memEventMin_.setValue<e>(time); setMemEvent(); }
4985 + void set(const MemEvent e, const unsigned time) { memEventMin_.setValue(e, time); setMemEvent(); }
4987 void flagIrq(const unsigned bit) { memEventRequester_.flagIrq(bit); }
4988 void flagHdmaReq() { memEventRequester_.flagHdmaReq(); }
4990 + void loadOrSave(loadsave& state) {
4991 + eventMin_.loadOrSave(state);
4992 + memEventMin_.loadOrSave(state);
4996 PPU ppu;
4997 - unsigned long dmgColorsRgb32[3 * 4];
4998 + uint_least32_t dmgColorsRgb32[3 * 4];
4999 unsigned char bgpData[8 * 8];
5000 unsigned char objpData[8 * 8];
5002 @@ -134,8 +149,8 @@ class LCD {
5003 unsigned char m2IrqStatReg_;
5004 unsigned char m1IrqStatReg_;
5006 - static void setDmgPalette(unsigned long *palette, const unsigned long *dmgColors, unsigned data);
5007 - void setDmgPaletteColor(unsigned index, unsigned long rgb32);
5008 + static void setDmgPalette(uint_least32_t *palette, const uint_least32_t *dmgColors, unsigned data);
5009 + void setDmgPaletteColor(unsigned index, uint_least32_t rgb32);
5011 void refreshPalettes();
5012 void setDBuffer();
5013 @@ -143,12 +158,12 @@ class LCD {
5014 void doMode2IrqEvent();
5015 void event();
5017 - unsigned long m0TimeOfCurrentLine(unsigned long cc);
5018 - bool cgbpAccessible(unsigned long cycleCounter);
5019 + unsigned m0TimeOfCurrentLine(unsigned cc);
5020 + bool cgbpAccessible(unsigned cycleCounter);
5022 void mode3CyclesChange();
5023 - void doCgbBgColorChange(unsigned index, unsigned data, unsigned long cycleCounter);
5024 - void doCgbSpColorChange(unsigned index, unsigned data, unsigned long cycleCounter);
5025 + void doCgbBgColorChange(unsigned index, unsigned data, unsigned cycleCounter);
5026 + void doCgbSpColorChange(unsigned index, unsigned data, unsigned cycleCounter);
5028 public:
5029 LCD(const unsigned char *oamram, const unsigned char *vram_in, VideoInterruptRequester memEventRequester);
5030 @@ -156,65 +171,66 @@ public:
5031 void setStatePtrs(SaveState &state);
5032 void saveState(SaveState &state) const;
5033 void loadState(const SaveState &state, const unsigned char *oamram);
5034 - void setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned long rgb32);
5035 - void setVideoBuffer(uint_least32_t *videoBuf, int pitch);
5036 + void setDmgPaletteColor(unsigned palNum, unsigned colorNum, uint_least32_t rgb32);
5038 + void loadOrSave(loadsave& state);
5040 void setOsdElement(std::auto_ptr<OsdElement> osdElement) { this->osdElement = osdElement; }
5042 - void dmgBgPaletteChange(const unsigned data, const unsigned long cycleCounter) {
5043 + void dmgBgPaletteChange(const unsigned data, const unsigned cycleCounter) {
5044 update(cycleCounter);
5045 bgpData[0] = data;
5046 setDmgPalette(ppu.bgPalette(), dmgColorsRgb32, data);
5049 - void dmgSpPalette1Change(const unsigned data, const unsigned long cycleCounter) {
5050 + void dmgSpPalette1Change(const unsigned data, const unsigned cycleCounter) {
5051 update(cycleCounter);
5052 objpData[0] = data;
5053 setDmgPalette(ppu.spPalette(), dmgColorsRgb32 + 4, data);
5056 - void dmgSpPalette2Change(const unsigned data, const unsigned long cycleCounter) {
5057 + void dmgSpPalette2Change(const unsigned data, const unsigned cycleCounter) {
5058 update(cycleCounter);
5059 objpData[1] = data;
5060 setDmgPalette(ppu.spPalette() + 4, dmgColorsRgb32 + 8, data);
5063 - void cgbBgColorChange(unsigned index, const unsigned data, const unsigned long cycleCounter) {
5064 + void cgbBgColorChange(unsigned index, const unsigned data, const unsigned cycleCounter) {
5065 if (bgpData[index] != data)
5066 doCgbBgColorChange(index, data, cycleCounter);
5069 - void cgbSpColorChange(unsigned index, const unsigned data, const unsigned long cycleCounter) {
5070 + void cgbSpColorChange(unsigned index, const unsigned data, const unsigned cycleCounter) {
5071 if (objpData[index] != data)
5072 doCgbSpColorChange(index, data, cycleCounter);
5075 - unsigned cgbBgColorRead(const unsigned index, const unsigned long cycleCounter) {
5076 + unsigned cgbBgColorRead(const unsigned index, const unsigned cycleCounter) {
5077 return ppu.cgb() & cgbpAccessible(cycleCounter) ? bgpData[index] : 0xFF;
5080 - unsigned cgbSpColorRead(const unsigned index, const unsigned long cycleCounter) {
5081 + unsigned cgbSpColorRead(const unsigned index, const unsigned cycleCounter) {
5082 return ppu.cgb() & cgbpAccessible(cycleCounter) ? objpData[index] : 0xFF;
5085 - void updateScreen(bool blanklcd, unsigned long cc);
5086 - void resetCc(unsigned long oldCC, unsigned long newCc);
5087 - void speedChange(unsigned long cycleCounter);
5088 - bool vramAccessible(unsigned long cycleCounter);
5089 - bool oamReadable(unsigned long cycleCounter);
5090 - bool oamWritable(unsigned long cycleCounter);
5091 - void wxChange(unsigned newValue, unsigned long cycleCounter);
5092 - void wyChange(unsigned newValue, unsigned long cycleCounter);
5093 - void oamChange(unsigned long cycleCounter);
5094 - void oamChange(const unsigned char *oamram, unsigned long cycleCounter);
5095 - void scxChange(unsigned newScx, unsigned long cycleCounter);
5096 - void scyChange(unsigned newValue, unsigned long cycleCounter);
5097 + void updateScreen(bool blanklcd, unsigned cc, uint_least32_t* vbuffer, unsigned vpitch);
5098 + void resetCc(unsigned oldCC, unsigned newCc);
5099 + void speedChange(unsigned cycleCounter);
5100 + bool vramAccessible(unsigned cycleCounter);
5101 + bool oamReadable(unsigned cycleCounter);
5102 + bool oamWritable(unsigned cycleCounter);
5103 + void wxChange(unsigned newValue, unsigned cycleCounter);
5104 + void wyChange(unsigned newValue, unsigned cycleCounter);
5105 + void oamChange(unsigned cycleCounter);
5106 + void oamChange(const unsigned char *oamram, unsigned cycleCounter);
5107 + void scxChange(unsigned newScx, unsigned cycleCounter);
5108 + void scyChange(unsigned newValue, unsigned cycleCounter);
5110 - void vramChange(const unsigned long cycleCounter) { update(cycleCounter); }
5111 + void vramChange(const unsigned cycleCounter) { update(cycleCounter); }
5113 - unsigned getStat(unsigned lycReg, unsigned long cycleCounter);
5114 + unsigned getStat(unsigned lycReg, unsigned cycleCounter);
5116 - unsigned getLyReg(const unsigned long cycleCounter) {
5117 + unsigned getLyReg(const unsigned cycleCounter) {
5118 unsigned lyReg = 0;
5120 if (ppu.lcdc() & 0x80) {
5121 @@ -232,17 +248,17 @@ public:
5122 return lyReg;
5125 - unsigned long nextMode1IrqTime() const { return eventTimes_(MODE1_IRQ); }
5126 + unsigned nextMode1IrqTime() const { return eventTimes_(MODE1_IRQ); }
5128 - void lcdcChange(unsigned data, unsigned long cycleCounter);
5129 - void lcdstatChange(unsigned data, unsigned long cycleCounter);
5130 - void lycRegChange(unsigned data, unsigned long cycleCounter);
5131 + void lcdcChange(unsigned data, unsigned cycleCounter);
5132 + void lcdstatChange(unsigned data, unsigned cycleCounter);
5133 + void lycRegChange(unsigned data, unsigned cycleCounter);
5135 - void enableHdma(unsigned long cycleCounter);
5136 - void disableHdma(unsigned long cycleCounter);
5137 + void enableHdma(unsigned cycleCounter);
5138 + void disableHdma(unsigned cycleCounter);
5139 bool hdmaIsEnabled() const { return eventTimes_(HDMA_REQ) != DISABLED_TIME; }
5141 - void update(unsigned long cycleCounter);
5142 + void update(unsigned cycleCounter);
5144 bool isCgb() const { return ppu.cgb(); }
5145 bool isDoubleSpeed() const { return ppu.lyCounter().isDoubleSpeed(); }
5146 diff --git a/libgambatte/src/video/ly_counter.cpp b/libgambatte/src/video/ly_counter.cpp
5147 index 5c8828d..15037f6 100644
5148 --- a/libgambatte/src/video/ly_counter.cpp
5149 +++ b/libgambatte/src/video/ly_counter.cpp
5150 @@ -19,6 +19,10 @@
5151 #include "ly_counter.h"
5152 #include "../savestate.h"
5155 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
5156 +// - Make it rerecording-friendly.
5158 namespace gambatte {
5160 LyCounter::LyCounter()
5161 @@ -37,8 +41,8 @@ void LyCounter::doEvent() {
5162 time_ = time_ + lineTime_;
5165 -unsigned long LyCounter::nextLineCycle(const unsigned lineCycle, const unsigned long cycleCounter) const {
5166 - unsigned long tmp = time_ + (lineCycle << ds);
5167 +unsigned LyCounter::nextLineCycle(const unsigned lineCycle, const unsigned cycleCounter) const {
5168 + unsigned tmp = time_ + (lineCycle << ds);
5170 if (tmp - cycleCounter > lineTime_)
5171 tmp -= lineTime_;
5172 @@ -46,8 +50,8 @@ unsigned long LyCounter::nextLineCycle(const unsigned lineCycle, const unsigned
5173 return tmp;
5176 -unsigned long LyCounter::nextFrameCycle(const unsigned long frameCycle, const unsigned long cycleCounter) const {
5177 - unsigned long tmp = time_ + (((153U - ly()) * 456U + frameCycle) << ds);
5178 +unsigned LyCounter::nextFrameCycle(const unsigned frameCycle, const unsigned cycleCounter) const {
5179 + unsigned tmp = time_ + (((153U - ly()) * 456U + frameCycle) << ds);
5181 if (tmp - cycleCounter > 70224U << ds)
5182 tmp -= 70224U << ds;
5183 @@ -55,7 +59,7 @@ unsigned long LyCounter::nextFrameCycle(const unsigned long frameCycle, const un
5184 return tmp;
5187 -void LyCounter::reset(const unsigned long videoCycles, const unsigned long lastUpdate) {
5188 +void LyCounter::reset(const unsigned videoCycles, const unsigned lastUpdate) {
5189 ly_ = videoCycles / 456;
5190 time_ = lastUpdate + ((456 - (videoCycles - ly_ * 456ul)) << isDoubleSpeed());
5192 diff --git a/libgambatte/src/video/ly_counter.h b/libgambatte/src/video/ly_counter.h
5193 index 49ff015..5c044c9 100644
5194 --- a/libgambatte/src/video/ly_counter.h
5195 +++ b/libgambatte/src/video/ly_counter.h
5196 @@ -18,13 +18,18 @@
5197 ***************************************************************************/
5198 #ifndef LY_COUNTER_H
5199 #define LY_COUNTER_H
5200 +#include "../loadsave.h"
5203 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
5204 +// - Make it rerecording-friendly.
5206 namespace gambatte {
5208 struct SaveState;
5210 class LyCounter {
5211 - unsigned long time_;
5212 + unsigned time_;
5213 unsigned short lineTime_;
5214 unsigned char ly_;
5215 bool ds;
5216 @@ -34,21 +39,28 @@ public:
5217 void doEvent();
5218 bool isDoubleSpeed() const { return ds; }
5220 - unsigned long frameCycles(const unsigned long cc) const {
5221 + unsigned frameCycles(const unsigned cc) const {
5222 return ly_ * 456ul + lineCycles(cc);
5225 - unsigned lineCycles(const unsigned long cc) const {
5226 + unsigned lineCycles(const unsigned cc) const {
5227 return 456u - ((time_ - cc) >> isDoubleSpeed());
5230 unsigned lineTime() const { return lineTime_; }
5231 unsigned ly() const { return ly_; }
5232 - unsigned long nextLineCycle(unsigned lineCycle, unsigned long cycleCounter) const;
5233 - unsigned long nextFrameCycle(unsigned long frameCycle, unsigned long cycleCounter) const;
5234 - void reset(unsigned long videoCycles, unsigned long lastUpdate);
5235 + unsigned nextLineCycle(unsigned lineCycle, unsigned cycleCounter) const;
5236 + unsigned nextFrameCycle(unsigned frameCycle, unsigned cycleCounter) const;
5237 + void reset(unsigned videoCycles, unsigned lastUpdate);
5238 void setDoubleSpeed(bool ds_in);
5239 - unsigned long time() const { return time_; }
5240 + unsigned time() const { return time_; }
5242 + void loadOrSave(loadsave& state) {
5243 + state(time_);
5244 + state(lineTime_);
5245 + state(ly_);
5246 + state(ds);
5251 diff --git a/libgambatte/src/video/lyc_irq.cpp b/libgambatte/src/video/lyc_irq.cpp
5252 index 2b1b380..9a92044 100644
5253 --- a/libgambatte/src/video/lyc_irq.cpp
5254 +++ b/libgambatte/src/video/lyc_irq.cpp
5255 @@ -22,6 +22,10 @@
5256 #include "savestate.h"
5257 #include <algorithm>
5260 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
5261 +// - Make it rerecording-friendly.
5263 namespace gambatte {
5265 LycIrq::LycIrq() :
5266 @@ -34,14 +38,14 @@ LycIrq::LycIrq() :
5270 -static unsigned long schedule(const unsigned statReg, const unsigned lycReg, const LyCounter &lyCounter, const unsigned long cc) {
5271 +static unsigned schedule(const unsigned statReg, const unsigned lycReg, const LyCounter &lyCounter, const unsigned cc) {
5272 return (statReg & 0x40) && lycReg < 154
5273 ? lyCounter.nextFrameCycle(lycReg ? lycReg * 456 : 153 * 456 + 8, cc)
5274 - : static_cast<unsigned long>(DISABLED_TIME);
5275 + : static_cast<unsigned>(DISABLED_TIME);
5278 -void LycIrq::regChange(const unsigned statReg, const unsigned lycReg, const LyCounter &lyCounter, const unsigned long cc) {
5279 - const unsigned long timeSrc = schedule(statReg, lycReg, lyCounter, cc);
5280 +void LycIrq::regChange(const unsigned statReg, const unsigned lycReg, const LyCounter &lyCounter, const unsigned cc) {
5281 + const unsigned timeSrc = schedule(statReg, lycReg, lyCounter, cc);
5282 statRegSrc_ = statReg;
5283 lycRegSrc_ = lycReg;
5284 time_ = std::min(time_, timeSrc);
5285 @@ -89,7 +93,7 @@ void LycIrq::saveState(SaveState &state) const {
5286 state.ppu.lyc = lycReg_;
5289 -void LycIrq::reschedule(const LyCounter & lyCounter, const unsigned long cc) {
5290 +void LycIrq::reschedule(const LyCounter & lyCounter, const unsigned cc) {
5291 time_ = std::min(schedule(statReg_ , lycReg_ , lyCounter, cc),
5292 schedule(statRegSrc_, lycRegSrc_, lyCounter, cc));
5294 diff --git a/libgambatte/src/video/lyc_irq.h b/libgambatte/src/video/lyc_irq.h
5295 index 019914d..93a064f 100644
5296 --- a/libgambatte/src/video/lyc_irq.h
5297 +++ b/libgambatte/src/video/lyc_irq.h
5298 @@ -19,20 +19,26 @@
5299 #ifndef VIDEO_LYC_IRQ_H
5300 #define VIDEO_LYC_IRQ_H
5303 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
5304 +// - Make it rerecording-friendly.
5306 +#include "../loadsave.h"
5308 namespace gambatte {
5310 struct SaveState;
5311 class LyCounter;
5313 class LycIrq {
5314 - unsigned long time_;
5315 + unsigned time_;
5316 unsigned char lycRegSrc_;
5317 unsigned char statRegSrc_;
5318 unsigned char lycReg_;
5319 unsigned char statReg_;
5320 bool cgb_;
5322 - void regChange(unsigned statReg, unsigned lycReg, const LyCounter &lyCounter, unsigned long cc);
5323 + void regChange(unsigned statReg, unsigned lycReg, const LyCounter &lyCounter, unsigned cc);
5325 public:
5326 LycIrq();
5327 @@ -40,18 +46,27 @@ public:
5328 unsigned lycReg() const { return lycRegSrc_; }
5329 void loadState(const SaveState &state);
5330 void saveState(SaveState &state) const;
5331 - unsigned long time() const { return time_; }
5332 + unsigned time() const { return time_; }
5333 void setCgb(const bool cgb) { cgb_ = cgb; }
5334 void lcdReset();
5335 - void reschedule(const LyCounter & lyCounter, unsigned long cc);
5336 + void reschedule(const LyCounter & lyCounter, unsigned cc);
5338 - void statRegChange(unsigned statReg, const LyCounter &lyCounter, unsigned long cc) {
5339 + void statRegChange(unsigned statReg, const LyCounter &lyCounter, unsigned cc) {
5340 regChange(statReg, lycRegSrc_, lyCounter, cc);
5343 - void lycRegChange(unsigned lycReg, const LyCounter &lyCounter, unsigned long cc) {
5344 + void lycRegChange(unsigned lycReg, const LyCounter &lyCounter, unsigned cc) {
5345 regChange(statRegSrc_, lycReg, lyCounter, cc);
5348 + void loadOrSave(loadsave& state) {
5349 + state(time_);
5350 + state(lycRegSrc_);
5351 + state(statRegSrc_);
5352 + state(lycReg_);
5353 + state(statReg_);
5354 + state(cgb_);
5359 diff --git a/libgambatte/src/video/next_m0_time.h b/libgambatte/src/video/next_m0_time.h
5360 index 148f471..1b0c338 100644
5361 --- a/libgambatte/src/video/next_m0_time.h
5362 +++ b/libgambatte/src/video/next_m0_time.h
5363 @@ -1,6 +1,12 @@
5364 #ifndef NEXT_M0_TIME_H_
5365 #define NEXT_M0_TIME_H_
5368 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
5369 +// - Make it rerecording-friendly.
5371 +#include "../loadsave.h"
5373 namespace gambatte {
5375 class NextM0Time {
5376 @@ -11,6 +17,10 @@ public:
5377 void predictNextM0Time(const class PPU &v);
5378 void invalidatePredictedNextM0Time() { predictedNextM0Time_ = 0; }
5379 unsigned predictedNextM0Time() const { return predictedNextM0Time_; }
5381 + void loadOrSave(loadsave& state) {
5382 + state(predictedNextM0Time_);
5387 diff --git a/libgambatte/src/video/ppu.cpp b/libgambatte/src/video/ppu.cpp
5388 index cd8c057..8844692 100644
5389 --- a/libgambatte/src/video/ppu.cpp
5390 +++ b/libgambatte/src/video/ppu.cpp
5391 @@ -22,6 +22,10 @@
5392 #include <cstring>
5393 #include <cstddef>
5396 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
5397 +// - Make it rerecording-friendly.
5399 namespace {
5401 using namespace gambatte;
5402 @@ -426,7 +430,7 @@ namespace M3Loop {
5404 const unsigned attrib = p.spriteList[i].attrib;
5405 unsigned spword = p.spwordList[i];
5406 - const unsigned long *const spPalette = p.spPalette + (attrib >> 2 & 4);
5407 + const uint_least32_t *const spPalette = p.spPalette + (attrib >> 2 & 4);
5409 if (!(attrib & 0x80)) {
5410 switch (n) {
5411 @@ -588,7 +592,7 @@ namespace M3Loop {
5412 uint_least32_t *const dst = dbufline + (xpos - 8);
5413 const unsigned tileword = p.ntileword;
5414 const unsigned attrib = p.nattrib;
5415 - const unsigned long *const bgPalette = p.bgPalette + (attrib & 7) * 4;
5416 + const uint_least32_t *const bgPalette = p.bgPalette + (attrib & 7) * 4;
5418 dst[0] = bgPalette[tileword & 3];
5419 dst[1] = bgPalette[tileword >> 2 & 3];
5420 @@ -624,7 +628,7 @@ namespace M3Loop {
5421 const unsigned id = p.spriteList[i].oampos;
5422 const unsigned sattrib = p.spriteList[i].attrib;
5423 unsigned spword = p.spwordList[i];
5424 - const unsigned long *const spPalette = p.spPalette + (sattrib & 7) * 4;
5425 + const uint_least32_t *const spPalette = p.spPalette + (sattrib & 7) * 4;
5427 if (!((attrib | sattrib) & bgenmask)) {
5428 switch (n) {
5429 @@ -781,7 +785,7 @@ namespace M3Loop {
5432 const unsigned twdata = tileword & ((p.lcdc & 1) | p.cgb) * 3;
5433 - unsigned long pixel = p.bgPalette[twdata + (p.attrib & 7) * 4];
5434 + uint_least32_t pixel = p.bgPalette[twdata + (p.attrib & 7) * 4];
5435 int i = static_cast<int>(p.nextSprite) - 1;
5437 if (i >= 0 && static_cast<int>(p.spriteList[i].spx) > xpos - 8) {
5438 @@ -840,8 +844,8 @@ namespace M3Loop {
5439 plotPixel(p);
5442 - static unsigned long nextM2Time(const PPUPriv &p) {
5443 - unsigned long nextm2 = p.lyCounter.isDoubleSpeed()
5444 + static unsigned nextM2Time(const PPUPriv &p) {
5445 + unsigned nextm2 = p.lyCounter.isDoubleSpeed()
5446 ? p.lyCounter.time() + (weMasterCheckPriorToLyIncLineCycle(true ) + M2_DS_OFFSET) * 2 - 456 * 2
5447 : p.lyCounter.time() + weMasterCheckPriorToLyIncLineCycle(p.cgb) - 456 ;
5449 @@ -854,11 +858,11 @@ namespace M3Loop {
5450 static void xpos168(PPUPriv &p) {
5451 p.lastM0Time = p.now - (p.cycles << p.lyCounter.isDoubleSpeed());
5453 - const unsigned long nextm2 = nextM2Time(p);
5454 + const unsigned nextm2 = nextM2Time(p);
5456 p.cycles = p.now >= nextm2
5457 - ? (static_cast<long>(p.now - nextm2) >> p.lyCounter.isDoubleSpeed())
5458 - : -(static_cast<long>(nextm2 - p.now) >> p.lyCounter.isDoubleSpeed());
5459 + ? (static_cast<signed>(p.now - nextm2) >> p.lyCounter.isDoubleSpeed())
5460 + : -(static_cast<signed>(nextm2 - p.now) >> p.lyCounter.isDoubleSpeed());
5462 nextCall(0, p.lyCounter.ly() == 143 ? M2::Ly0::f0_ : M2::LyNon0::f0_, p);
5464 @@ -1542,8 +1546,8 @@ std::size_t upperBound(const T a[], const K e) {
5466 struct CycleState {
5467 const PPUState *state;
5468 - long cycle;
5469 - operator long() const { return cycle; }
5470 + signed cycle;
5471 + operator signed() const { return cycle; }
5474 static const PPUState * decodeM3LoopState(const unsigned state) {
5475 @@ -1573,8 +1577,8 @@ static const PPUState * decodeM3LoopState(const unsigned state) {
5476 return 0;
5479 -static long cyclesUntilM0Upperbound(const PPUPriv &p) {
5480 - long cycles = 168 - p.xpos + 6;
5481 +static signed cyclesUntilM0Upperbound(const PPUPriv &p) {
5482 + signed cycles = 168 - p.xpos + 6;
5484 for (unsigned i = p.nextSprite; i < 10 && p.spriteList[i].spx < 168; ++i)
5485 cycles += 11;
5486 @@ -1614,17 +1618,17 @@ static void loadSpriteList(PPUPriv &p, const SaveState &ss) {
5488 void PPU::loadState(const SaveState &ss, const unsigned char *const oamram) {
5489 const PPUState *const m3loopState = decodeM3LoopState(ss.ppu.state);
5490 - const long videoCycles = std::min(ss.ppu.videoCycles, 70223UL);
5491 + const signed videoCycles = std::min(ss.ppu.videoCycles, 70223u);
5492 const bool ds = p_.cgb & ss.mem.ioamhram.get()[0x14D] >> 7;
5493 - const long vcycs = videoCycles - ds * M2_DS_OFFSET < 0
5494 + const signed vcycs = videoCycles - ds * M2_DS_OFFSET < 0
5495 ? videoCycles - ds * M2_DS_OFFSET + 70224
5496 : videoCycles - ds * M2_DS_OFFSET;
5497 - const long lineCycles = static_cast<unsigned long>(vcycs) % 456;
5498 + const signed lineCycles = static_cast<unsigned>(vcycs) % 456;
5500 p_.now = ss.cpu.cycleCounter;
5501 p_.lcdc = ss.mem.ioamhram.get()[0x140];
5502 p_.lyCounter.setDoubleSpeed(ds);
5503 - p_.lyCounter.reset(std::min(ss.ppu.videoCycles, 70223ul), ss.cpu.cycleCounter);
5504 + p_.lyCounter.reset(std::min(ss.ppu.videoCycles, 70223u), ss.cpu.cycleCounter);
5505 p_.spriteMapper.loadState(ss, oamram);
5506 p_.winYPos = ss.ppu.winYPos;
5507 p_.scy = ss.mem.ioamhram.get()[0x142];
5508 @@ -1648,10 +1652,10 @@ void PPU::loadState(const SaveState &ss, const unsigned char *const oamram) {
5509 loadSpriteList(p_, ss);
5511 if (m3loopState && videoCycles < 144 * 456L && p_.xpos < 168
5512 - && lineCycles + cyclesUntilM0Upperbound(p_) < static_cast<long>(weMasterCheckPriorToLyIncLineCycle(p_.cgb))) {
5513 + && lineCycles + cyclesUntilM0Upperbound(p_) < static_cast<signed>(weMasterCheckPriorToLyIncLineCycle(p_.cgb))) {
5514 p_.nextCallPtr = m3loopState;
5515 p_.cycles = -1;
5516 - } else if (vcycs < 143 * 456L + static_cast<long>(m3StartLineCycle(p_.cgb)) + MAX_M3START_CYCLES) {
5517 + } else if (vcycs < 143 * 456L + static_cast<signed>(m3StartLineCycle(p_.cgb)) + MAX_M3START_CYCLES) {
5518 const struct CycleState lineCycleStates[] = {
5519 { &M3Start::f0_, m3StartLineCycle(p_.cgb) },
5520 { &M3Start::f1_, m3StartLineCycle(p_.cgb) + MAX_M3START_CYCLES },
5521 @@ -1681,9 +1685,9 @@ void PPU::reset(const unsigned char *const oamram, const bool cgb) {
5522 p_.spriteMapper.reset(oamram, cgb);
5525 -void PPU::resetCc(const unsigned long oldCc, const unsigned long newCc) {
5526 - const unsigned long dec = oldCc - newCc;
5527 - const unsigned long videoCycles = p_.lcdc & 0x80 ? p_.lyCounter.frameCycles(p_.now) : 0;
5528 +void PPU::resetCc(const unsigned oldCc, const unsigned newCc) {
5529 + const unsigned dec = oldCc - newCc;
5530 + const unsigned videoCycles = p_.lcdc & 0x80 ? p_.lyCounter.frameCycles(p_.now) : 0;
5532 p_.now -= dec;
5533 p_.lastM0Time = p_.lastM0Time ? p_.lastM0Time - dec : p_.lastM0Time;
5534 @@ -1691,8 +1695,8 @@ void PPU::resetCc(const unsigned long oldCc, const unsigned long newCc) {
5535 p_.spriteMapper.resetCycleCounter(oldCc, newCc);
5538 -void PPU::speedChange(const unsigned long cycleCounter) {
5539 - const unsigned long videoCycles = p_.lcdc & 0x80 ? p_.lyCounter.frameCycles(p_.now) : 0;
5540 +void PPU::speedChange(const unsigned cycleCounter) {
5541 + const unsigned videoCycles = p_.lcdc & 0x80 ? p_.lyCounter.frameCycles(p_.now) : 0;
5543 p_.spriteMapper.preSpeedChange(cycleCounter);
5544 p_.lyCounter.setDoubleSpeed(!p_.lyCounter.isDoubleSpeed());
5545 @@ -1707,11 +1711,11 @@ void PPU::speedChange(const unsigned long cycleCounter) {
5549 -unsigned long PPU::predictedNextXposTime(const unsigned xpos) const {
5550 +unsigned PPU::predictedNextXposTime(const unsigned xpos) const {
5551 return p_.now + (p_.nextCallPtr->predictCyclesUntilXpos_f(p_, xpos, -p_.cycles) << p_.lyCounter.isDoubleSpeed());
5554 -void PPU::setLcdc(const unsigned lcdc, const unsigned long cc) {
5555 +void PPU::setLcdc(const unsigned lcdc, const unsigned cc) {
5556 if ((p_.lcdc ^ lcdc) & lcdc & 0x80) {
5557 p_.now = cc;
5558 p_.lastM0Time = 0;
5559 @@ -1741,7 +1745,7 @@ void PPU::setLcdc(const unsigned lcdc, const unsigned long cc) {
5560 p_.lcdc = lcdc;
5563 -void PPU::update(const unsigned long cc) {
5564 +void PPU::update(const unsigned cc) {
5565 const int cycles = (cc - p_.now) >> p_.lyCounter.isDoubleSpeed();
5567 p_.now += cycles << p_.lyCounter.isDoubleSpeed();
5568 @@ -1753,4 +1757,69 @@ void PPU::update(const unsigned long cc) {
5572 +void PPUPriv::loadOrSave(loadsave& state)
5574 + state(bgPalette, 32);
5575 + state(spPalette, 32);
5577 + state.startEnumeration();
5578 + state.enumerate<const PPUState*>(nextCallPtr, NULL, 0);
5579 + state.enumerate<const PPUState*>(nextCallPtr, &M2::Ly0::f0_, 1);
5580 + state.enumerate<const PPUState*>(nextCallPtr, &M2::LyNon0::f0_, 2);
5581 + state.enumerate<const PPUState*>(nextCallPtr, &M2::LyNon0::f1_, 3);
5582 + state.enumerate<const PPUState*>(nextCallPtr, &M3Start::f0_, 4);
5583 + state.enumerate<const PPUState*>(nextCallPtr, &M3Start::f1_, 5);
5584 + state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::Tile::f0_, 6);
5585 + state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::Tile::f1_, 7);
5586 + state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::Tile::f2_, 8);
5587 + state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::Tile::f3_, 9);
5588 + state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::Tile::f4_, 10);
5589 + state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::Tile::f5_, 11);
5590 + state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::LoadSprites::f0_, 12);
5591 + state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::LoadSprites::f1_, 13);
5592 + state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::LoadSprites::f2_, 14);
5593 + state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::LoadSprites::f3_, 15);
5594 + state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::LoadSprites::f4_, 16);
5595 + state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::LoadSprites::f5_, 17);
5596 + state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::StartWindowDraw::f0_, 18);
5597 + state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::StartWindowDraw::f1_, 19);
5598 + state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::StartWindowDraw::f2_, 20);
5599 + state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::StartWindowDraw::f3_, 21);
5600 + state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::StartWindowDraw::f4_, 22);
5601 + state.enumerate<const PPUState*>(nextCallPtr, &M3Loop::StartWindowDraw::f5_, 23);
5602 + state.endEnumeration();
5604 + state(now);
5605 + state(lastM0Time);
5606 + state(cycles);
5607 + state(tileword);
5608 + state(ntileword);
5609 + lyCounter.loadOrSave(state);
5610 + spriteMapper.loadOrSave(state);
5611 + framebuf.loadOrSave(state);
5613 + for(size_t i = 0; i < 11; i++)
5614 + spriteList[i].loadOrSave(state);
5615 + state(spwordList, 11);
5616 + state(lcdc);
5617 + state(scy);
5618 + state(scx);
5619 + state(wy);
5620 + state(wy2);
5621 + state(wx);
5622 + state(winDrawState);
5623 + state(wscx);
5624 + state(winYPos);
5625 + state(reg0);
5626 + state(reg1);
5627 + state(attrib);
5628 + state(nattrib);
5629 + state(nextSprite);
5630 + state(currentSprite);
5631 + state(xpos);
5632 + state(endx);
5633 + state(cgb);
5634 + state(weMaster);
5638 diff --git a/libgambatte/src/video/ppu.h b/libgambatte/src/video/ppu.h
5639 index f48c922..dfd7fb5 100644
5640 --- a/libgambatte/src/video/ppu.h
5641 +++ b/libgambatte/src/video/ppu.h
5642 @@ -19,26 +19,45 @@
5643 #ifndef PPU_H
5644 #define PPU_H
5647 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
5648 +// - Make it rerecording-friendly.
5650 +#include <cstring>
5651 #include "video/ly_counter.h"
5652 #include "video/sprite_mapper.h"
5653 #include "gbint.h"
5654 +#include "../loadsave.h"
5656 namespace gambatte {
5658 class PPUFrameBuf {
5659 - uint_least32_t *buf_;
5660 + mutable uint_least32_t buf_[160*144];
5661 uint_least32_t *fbline_;
5662 - int pitch_;
5664 static uint_least32_t * nullfbline() { static uint_least32_t nullfbline_[160]; return nullfbline_; }
5666 public:
5667 - PPUFrameBuf() : buf_(0), fbline_(nullfbline()), pitch_(0) {}
5668 + PPUFrameBuf() : fbline_(nullfbline()) { memset(buf_, 0, sizeof(buf_)); }
5669 uint_least32_t * fb() const { return buf_; }
5670 uint_least32_t * fbline() const { return fbline_; }
5671 - int pitch() const { return pitch_; }
5672 - void setBuf(uint_least32_t *const buf, const int pitch) { buf_ = buf; pitch_ = pitch; fbline_ = nullfbline(); }
5673 - void setFbline(const unsigned ly) { fbline_ = buf_ ? buf_ + static_cast<long>(ly) * static_cast<long>(pitch_) : nullfbline(); }
5674 + int pitch() const { return 160; }
5675 + void setFbline(const unsigned ly) { fbline_ = buf_ ? buf_ + ly * 160ULL : nullfbline(); }
5676 + void blit(uint_least32_t *const buf, const int pitch) const {
5677 + for(unsigned i = 0; i < 144; i++)
5678 + memcpy(buf + i * static_cast<signed>(pitch), buf_ + i * 160, 160 * sizeof(buf[0]));
5680 + void loadOrSave(loadsave& state) {
5681 + state(buf_, 160*144);
5682 + bool var = (fbline_ != nullfbline());
5683 + state(var);
5684 + if(var) {
5685 + unsigned x = fbline_ - buf_;
5686 + state(x);
5687 + fbline_ = buf_ + x;
5688 + } else
5689 + fbline_ = nullfbline();
5693 struct PPUState {
5694 @@ -49,15 +68,15 @@ struct PPUState {
5696 // The PPU loop accesses a lot of state at once, so it's difficult to split this up much beyond grouping stuff into smaller structs.
5697 struct PPUPriv {
5698 - unsigned long bgPalette[8 * 4];
5699 - unsigned long spPalette[8 * 4];
5700 + uint_least32_t bgPalette[8 * 4];
5701 + uint_least32_t spPalette[8 * 4];
5703 const unsigned char *const vram;
5704 const PPUState *nextCallPtr;
5706 - unsigned long now;
5707 - unsigned long lastM0Time;
5708 - long cycles;
5709 + unsigned now;
5710 + unsigned lastM0Time;
5711 + signed cycles;
5713 unsigned tileword;
5714 unsigned ntileword;
5715 @@ -66,7 +85,15 @@ struct PPUPriv {
5716 SpriteMapper spriteMapper;
5717 PPUFrameBuf framebuf;
5719 - struct Sprite { unsigned char spx, oampos, line, attrib; } spriteList[11];
5720 + struct Sprite {
5721 + unsigned char spx, oampos, line, attrib;
5722 + void loadOrSave(loadsave& state) {
5723 + state(spx);
5724 + state(oampos);
5725 + state(line);
5726 + state(attrib);
5728 + } spriteList[11];
5729 unsigned short spwordList[11];
5731 unsigned char lcdc;
5732 @@ -91,6 +118,7 @@ struct PPUPriv {
5733 bool weMaster;
5735 PPUPriv(NextM0Time &nextM0Time, const unsigned char *oamram, const unsigned char *vram);
5736 + void loadOrSave(loadsave& state);
5739 class PPU {
5740 @@ -101,34 +129,38 @@ public:
5744 - unsigned long * bgPalette() { return p_.bgPalette; }
5745 + uint_least32_t * bgPalette() { return p_.bgPalette; }
5746 bool cgb() const { return p_.cgb; }
5747 void doLyCountEvent() { p_.lyCounter.doEvent(); }
5748 - unsigned long doSpriteMapEvent(unsigned long time) { return p_.spriteMapper.doEvent(time); }
5749 + unsigned doSpriteMapEvent(unsigned time) { return p_.spriteMapper.doEvent(time); }
5750 const PPUFrameBuf & frameBuf() const { return p_.framebuf; }
5751 - bool inactivePeriodAfterDisplayEnable(unsigned long cc) const { return p_.spriteMapper.inactivePeriodAfterDisplayEnable(cc); }
5752 - unsigned long lastM0Time() const { return p_.lastM0Time; }
5753 + bool inactivePeriodAfterDisplayEnable(unsigned cc) const { return p_.spriteMapper.inactivePeriodAfterDisplayEnable(cc); }
5754 + unsigned lastM0Time() const { return p_.lastM0Time; }
5755 unsigned lcdc() const { return p_.lcdc; }
5756 void loadState(const SaveState &state, const unsigned char *oamram);
5757 const LyCounter & lyCounter() const { return p_.lyCounter; }
5758 - unsigned long now() const { return p_.now; }
5759 - void oamChange(unsigned long cc) { p_.spriteMapper.oamChange(cc); }
5760 - void oamChange(const unsigned char *oamram, unsigned long cc) { p_.spriteMapper.oamChange(oamram, cc); }
5761 - unsigned long predictedNextXposTime(unsigned xpos) const;
5762 + unsigned now() const { return p_.now; }
5763 + void oamChange(unsigned cc) { p_.spriteMapper.oamChange(cc); }
5764 + void oamChange(const unsigned char *oamram, unsigned cc) { p_.spriteMapper.oamChange(oamram, cc); }
5765 + unsigned predictedNextXposTime(unsigned xpos) const;
5766 void reset(const unsigned char *oamram, bool cgb);
5767 - void resetCc(unsigned long oldCc, unsigned long newCc);
5768 + void resetCc(unsigned oldCc, unsigned newCc);
5769 void saveState(SaveState &ss) const;
5770 - void setFrameBuf(uint_least32_t *buf, unsigned pitch) { p_.framebuf.setBuf(buf, pitch); }
5771 - void setLcdc(unsigned lcdc, unsigned long cc);
5772 + void flipDisplay(uint_least32_t *buf, unsigned pitch) { p_.framebuf.blit(buf, pitch); }
5773 + void setLcdc(unsigned lcdc, unsigned cc);
5774 void setScx(const unsigned scx) { p_.scx = scx; }
5775 void setScy(const unsigned scy) { p_.scy = scy; }
5776 void setStatePtrs(SaveState &ss) { p_.spriteMapper.setStatePtrs(ss); }
5777 void setWx(const unsigned wx) { p_.wx = wx; }
5778 void setWy(const unsigned wy) { p_.wy = wy; }
5779 void updateWy2() { p_.wy2 = p_.wy; }
5780 - void speedChange(unsigned long cycleCounter);
5781 - unsigned long * spPalette() { return p_.spPalette; }
5782 - void update(unsigned long cc);
5783 + void speedChange(unsigned cycleCounter);
5784 + uint_least32_t * spPalette() { return p_.spPalette; }
5785 + void update(unsigned cc);
5787 + void loadOrSave(loadsave& state) {
5788 + p_.loadOrSave(state);
5793 diff --git a/libgambatte/src/video/sprite_mapper.cpp b/libgambatte/src/video/sprite_mapper.cpp
5794 index a68d14f..512bf41 100644
5795 --- a/libgambatte/src/video/sprite_mapper.cpp
5796 +++ b/libgambatte/src/video/sprite_mapper.cpp
5797 @@ -23,6 +23,10 @@
5798 #include <cstring>
5799 #include <algorithm>
5802 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
5803 +// - Make it rerecording-friendly.
5805 namespace gambatte {
5807 SpriteMapper::OamReader::OamReader(const LyCounter &lyCounter, const unsigned char *oamram)
5808 @@ -47,7 +51,7 @@ void SpriteMapper::OamReader::reset(const unsigned char *const oamram, const boo
5812 -static unsigned toPosCycles(const unsigned long cc, const LyCounter &lyCounter) {
5813 +static unsigned toPosCycles(const unsigned cc, const LyCounter &lyCounter) {
5814 unsigned lc = lyCounter.lineCycles(cc) + 3 - lyCounter.isDoubleSpeed() * 3u;
5816 if (lc >= 456)
5817 @@ -56,7 +60,7 @@ static unsigned toPosCycles(const unsigned long cc, const LyCounter &lyCounter)
5818 return lc;
5821 -void SpriteMapper::OamReader::update(const unsigned long cc) {
5822 +void SpriteMapper::OamReader::update(const unsigned cc) {
5823 if (cc > lu) {
5824 if (changed()) {
5825 const unsigned lulc = toPosCycles(lu, lyCounter);
5826 @@ -100,7 +104,7 @@ void SpriteMapper::OamReader::update(const unsigned long cc) {
5830 -void SpriteMapper::OamReader::change(const unsigned long cc) {
5831 +void SpriteMapper::OamReader::change(const unsigned cc) {
5832 update(cc);
5833 lastChange = std::min(toPosCycles(lu, lyCounter), 80u);
5835 @@ -117,7 +121,7 @@ void SpriteMapper::OamReader::loadState(const SaveState &ss, const unsigned char
5836 change(lu);
5839 -void SpriteMapper::OamReader::enableDisplay(const unsigned long cc) {
5840 +void SpriteMapper::OamReader::enableDisplay(const unsigned cc) {
5841 std::memset(buf, 0x00, sizeof(buf));
5842 std::fill(szbuf, szbuf + 40, false);
5843 lu = cc + (80 << lyCounter.isDoubleSpeed());
5844 @@ -130,6 +134,7 @@ SpriteMapper::SpriteMapper(NextM0Time &nextM0Time,
5845 nextM0Time_(nextM0Time),
5846 oamReader(lyCounter, oamram)
5848 + memset(spritemap, 0, sizeof(spritemap));
5849 clearMap();
5852 @@ -180,10 +185,10 @@ void SpriteMapper::sortLine(const unsigned ly) const {
5853 insertionSort(spritemap + ly * 10, spritemap + ly * 10 + num[ly], SpxLess(posbuf()));
5856 -unsigned long SpriteMapper::doEvent(const unsigned long time) {
5857 +unsigned SpriteMapper::doEvent(const unsigned time) {
5858 oamReader.update(time);
5859 mapSprites();
5860 - return oamReader.changed() ? time + oamReader.lyCounter.lineTime() : static_cast<unsigned long>(DISABLED_TIME);
5861 + return oamReader.changed() ? time + oamReader.lyCounter.lineTime() : static_cast<unsigned>(DISABLED_TIME);
5865 diff --git a/libgambatte/src/video/sprite_mapper.h b/libgambatte/src/video/sprite_mapper.h
5866 index b103b25..b0bbd3d 100644
5867 --- a/libgambatte/src/video/sprite_mapper.h
5868 +++ b/libgambatte/src/video/sprite_mapper.h
5869 @@ -19,8 +19,13 @@
5870 #ifndef SPRITE_MAPPER_H
5871 #define SPRITE_MAPPER_H
5874 +// Modified 2012-07-10 to 2012-07-14 by H. Ilari Liusvaara
5875 +// - Make it rerecording-friendly.
5877 #include "ly_counter.h"
5878 #include "../savestate.h"
5879 +#include "../loadsave.h"
5881 namespace gambatte {
5882 class NextM0Time;
5883 @@ -35,7 +40,7 @@ class SpriteMapper {
5885 private:
5886 const unsigned char *oamram;
5887 - unsigned long lu;
5888 + unsigned lu;
5889 unsigned char lastChange;
5890 bool largeSpritesSrc;
5891 bool cgb_;
5892 @@ -43,20 +48,30 @@ class SpriteMapper {
5893 public:
5894 OamReader(const LyCounter &lyCounter, const unsigned char *oamram);
5895 void reset(const unsigned char *oamram, bool cgb);
5896 - void change(unsigned long cc);
5897 - void change(const unsigned char *oamram, unsigned long cc) { change(cc); this->oamram = oamram; }
5898 + void change(unsigned cc);
5899 + void change(const unsigned char *oamram, unsigned cc) { change(cc); this->oamram = oamram; }
5900 bool changed() const { return lastChange != 0xFF; }
5901 bool largeSprites(unsigned spNr) const { return szbuf[spNr]; }
5902 const unsigned char *oam() const { return oamram; }
5903 - void resetCycleCounter(const unsigned long oldCc, const unsigned long newCc) { lu -= oldCc - newCc; }
5904 + void resetCycleCounter(const unsigned oldCc, const unsigned newCc) { lu -= oldCc - newCc; }
5905 void setLargeSpritesSrc(const bool src) { largeSpritesSrc = src; }
5906 - void update(unsigned long cc);
5907 + void update(unsigned cc);
5908 const unsigned char *spritePosBuf() const { return buf; }
5909 void setStatePtrs(SaveState &state);
5910 - void enableDisplay(unsigned long cc);
5911 + void enableDisplay(unsigned cc);
5912 void saveState(SaveState &state) const { state.ppu.enableDisplayM0Time = lu; }
5913 void loadState(const SaveState &ss, const unsigned char *oamram);
5914 - bool inactivePeriodAfterDisplayEnable(const unsigned long cc) const { return cc < lu; }
5915 + bool inactivePeriodAfterDisplayEnable(const unsigned cc) const { return cc < lu; }
5917 + void loadOrSave(loadsave& state) {
5918 + state(buf, 80);
5919 + for(unsigned i = 0; i < 40; i++)
5920 + state(szbuf[i]);
5921 + state(lu);
5922 + state(lastChange);
5923 + state(largeSpritesSrc);
5924 + state(cgb_);
5928 enum { NEED_SORTING_MASK = 0x80 };
5929 @@ -89,22 +104,22 @@ public:
5930 const LyCounter &lyCounter,
5931 const unsigned char *oamram_in);
5932 void reset(const unsigned char *oamram, bool cgb);
5933 - unsigned long doEvent(unsigned long time);
5934 + unsigned doEvent(unsigned time);
5935 bool largeSprites(unsigned spNr) const { return oamReader.largeSprites(spNr); }
5936 unsigned numSprites(const unsigned ly) const { return num[ly] & ~NEED_SORTING_MASK; }
5937 - void oamChange(unsigned long cc) { oamReader.change(cc); }
5938 - void oamChange(const unsigned char *oamram, unsigned long cc) { oamReader.change(oamram, cc); }
5939 + void oamChange(unsigned cc) { oamReader.change(cc); }
5940 + void oamChange(const unsigned char *oamram, unsigned cc) { oamReader.change(oamram, cc); }
5941 const unsigned char *oamram() const { return oamReader.oam(); }
5942 const unsigned char *posbuf() const { return oamReader.spritePosBuf(); }
5943 - void preSpeedChange(const unsigned long cc) { oamReader.update(cc); }
5944 - void postSpeedChange(const unsigned long cc) { oamReader.change(cc); }
5945 + void preSpeedChange(const unsigned cc) { oamReader.update(cc); }
5946 + void postSpeedChange(const unsigned cc) { oamReader.change(cc); }
5948 - void resetCycleCounter(const unsigned long oldCc, const unsigned long newCc) {
5949 + void resetCycleCounter(const unsigned oldCc, const unsigned newCc) {
5950 oamReader.update(oldCc);
5951 oamReader.resetCycleCounter(oldCc, newCc);
5954 - static unsigned long schedule(const LyCounter &lyCounter, const unsigned long cycleCounter) {
5955 + static unsigned schedule(const LyCounter &lyCounter, const unsigned cycleCounter) {
5956 return lyCounter.nextLineCycle(80, cycleCounter);
5959 @@ -118,10 +133,16 @@ public:
5962 void setStatePtrs(SaveState &state) { oamReader.setStatePtrs(state); }
5963 - void enableDisplay(unsigned long cc) { oamReader.enableDisplay(cc); }
5964 + void enableDisplay(unsigned cc) { oamReader.enableDisplay(cc); }
5965 void saveState(SaveState &state) const { oamReader.saveState(state); }
5966 void loadState(const SaveState &state, const unsigned char *const oamram) { oamReader.loadState(state, oamram); mapSprites(); }
5967 - bool inactivePeriodAfterDisplayEnable(unsigned long cc) const { return oamReader.inactivePeriodAfterDisplayEnable(cc); }
5968 + bool inactivePeriodAfterDisplayEnable(unsigned cc) const { return oamReader.inactivePeriodAfterDisplayEnable(cc); }
5970 + void loadOrSave(loadsave& state) {
5971 + state(spritemap, 1440);
5972 + state(num, 144);
5973 + oamReader.loadOrSave(state);
5979 1.7.9.48.g85da4d