From 2bd332c862bc31072bbc83846e4bc7bf9ced081c Mon Sep 17 00:00:00 2001 From: MrWint Date: Sat, 25 May 2019 18:08:14 +0200 Subject: [PATCH 01/38] fix psg ch3 regression introduced in r165 --- libgambatte/src/sound/channel3.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/libgambatte/src/sound/channel3.cpp b/libgambatte/src/sound/channel3.cpp index 0d0d069f9f..aeab458d6b 100644 --- a/libgambatte/src/sound/channel3.cpp +++ b/libgambatte/src/sound/channel3.cpp @@ -168,12 +168,9 @@ void Channel3::update(uint_least32_t *buf, const unsigned long soBaseVol, unsign break; } } else { - if (outBase) { - const unsigned long out = outBase * (0 - 15ul); - - *buf += out - prevOut; - prevOut = out; - } + const unsigned long out = outBase * (0 - 15ul); + *buf += out - prevOut; + prevOut = out; cycleCounter += cycles; From 289439528d203112533022c0cd40918c9d023c7b Mon Sep 17 00:00:00 2001 From: MrWint Date: Sat, 25 May 2019 18:11:32 +0200 Subject: [PATCH 02/38] c++11 narrowing conversion warnings --- libgambatte/src/video/ppu.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libgambatte/src/video/ppu.cpp b/libgambatte/src/video/ppu.cpp index c1f10175f5..fde43224fb 100644 --- a/libgambatte/src/video/ppu.cpp +++ b/libgambatte/src/video/ppu.cpp @@ -105,9 +105,9 @@ enum { WIN_DRAW_START = 1, WIN_DRAW_STARTED = 2 }; enum { M2_DS_OFFSET = 3 }; enum { MAX_M3START_CYCLES = 80 }; -static inline unsigned weMasterCheckPriorToLyIncLineCycle(const bool cgb) { return 450 - cgb; } -static inline unsigned weMasterCheckAfterLyIncLineCycle(const bool cgb) { return 454 - cgb; } -static inline unsigned m3StartLineCycle(const bool /*cgb*/) { return 83; } +static inline int weMasterCheckPriorToLyIncLineCycle(const bool cgb) { return 450 - cgb; } +static inline int weMasterCheckAfterLyIncLineCycle(const bool cgb) { return 454 - cgb; } +static inline int m3StartLineCycle(const bool /*cgb*/) { return 83; } static inline void nextCall(const int cycles, const PPUState &state, PPUPriv &p) { const int c = p.cycles - cycles; From a3ee854022ca4ff18d735676051d7bcdd8e87e67 Mon Sep 17 00:00:00 2001 From: MrWint Date: Sat, 25 May 2019 18:27:08 +0200 Subject: [PATCH 03/38] more ideomatic use of sizeof --- libgambatte/libgambatte.vcxproj | 2 +- libgambatte/src/gambatte.cpp | 2 +- libgambatte/src/initstate.cpp | 22 +++++++++++----------- libgambatte/src/newstate.h | 10 +++++----- libgambatte/src/sound.cpp | 2 +- libgambatte/src/video.cpp | 2 +- libgambatte/src/video/ppu.cpp | 6 +++--- libgambatte/src/video/sprite_mapper.cpp | 4 ++-- output/dll/libgambatte.dll | Bin 170496 -> 143872 bytes 9 files changed, 25 insertions(+), 25 deletions(-) diff --git a/libgambatte/libgambatte.vcxproj b/libgambatte/libgambatte.vcxproj index 3d744a9a8c..4eecef7e04 100644 --- a/libgambatte/libgambatte.vcxproj +++ b/libgambatte/libgambatte.vcxproj @@ -22,7 +22,7 @@ {5D630682-7BDA-474D-B387-0EB420DDC199} Win32Proj libgambatte - 10.0.17763.0 + 8.1 diff --git a/libgambatte/src/gambatte.cpp b/libgambatte/src/gambatte.cpp index a1e3dfa65c..818c09c48d 100644 --- a/libgambatte/src/gambatte.cpp +++ b/libgambatte/src/gambatte.cpp @@ -75,7 +75,7 @@ void GB::blitTo(gambatte::uint_least32_t *videoBuf, int pitch) for (int i = 0; i < 144; i++) { - std::memcpy(dst, src, sizeof(gambatte::uint_least32_t) * 160); + std::memcpy(dst, src, sizeof gambatte::uint_least32_t * 160); src += 160; dst += pitch; } diff --git a/libgambatte/src/initstate.cpp b/libgambatte/src/initstate.cpp index 6f6e3a6e7c..d539354112 100644 --- a/libgambatte/src/initstate.cpp +++ b/libgambatte/src/initstate.cpp @@ -700,7 +700,7 @@ static void setInitialCgbWram(unsigned char *const wram) { std::memset(wram + 0x2000, 0, 0x1000); - for (std::size_t i = 0; i < sizeof(cgbWramDumpDiff) / sizeof(cgbWramDumpDiff[0]); ++i) + for (std::size_t i = 0; i < sizeof cgbWramDumpDiff / sizeof cgbWramDumpDiff[0]; ++i) wram[cgbWramDumpDiff[i].addr] = cgbWramDumpDiff[i].val; } @@ -972,7 +972,7 @@ static void setInitialDmgWram(unsigned char *const wram) { std::memcpy(wram + 0x1000, wram, 0x1000); - for (std::size_t i = 0; i < sizeof(dmgWramDumpDiff) / sizeof(dmgWramDumpDiff[0]); ++i) + for (std::size_t i = 0; i < sizeof dmgWramDumpDiff / sizeof dmgWramDumpDiff[0]; ++i) wram[dmgWramDumpDiff[i].addr] = dmgWramDumpDiff[i].val; } @@ -1007,7 +1007,7 @@ static void setInitialVram(unsigned char *const vram, const bool cgb) { std::memset(vram, 0, 0x4000); - for (std::size_t i = 0; i < sizeof(even_numbered_8010_to_81a0_dump) / sizeof(even_numbered_8010_to_81a0_dump[0]); ++i) { + for (std::size_t i = 0; i < sizeof even_numbered_8010_to_81a0_dump; ++i) { vram[0x0010 + i * 2] = even_numbered_8010_to_81a0_dump[i]; } @@ -1076,8 +1076,8 @@ static void setInitialCgbIoamhram(unsigned char *const ioamhram) { }; std::memset(ioamhram, 0x00, 0x0A0); - std::memcpy(ioamhram + 0x0A0, feaxDump, sizeof(feaxDump)); - std::memcpy(ioamhram + 0x100, ffxxDump, sizeof(ffxxDump)); + std::memcpy(ioamhram + 0x0A0, feaxDump, sizeof feaxDump); + std::memcpy(ioamhram + 0x100, ffxxDump, sizeof ffxxDump); } static void setInitialDmgIoamhram(unsigned char *const ioamhram) { @@ -1139,9 +1139,9 @@ static void setInitialDmgIoamhram(unsigned char *const ioamhram) { 0x59, 0xEA, 0x39, 0x01, 0x2E, 0x00, 0x69, 0x00 }; - std::memcpy(ioamhram , oamDump, sizeof(oamDump)); + std::memcpy(ioamhram , oamDump, sizeof oamDump); std::memset(ioamhram + 0x0A0, 0x00, 0x060); - std::memcpy(ioamhram + 0x100, ffxxDump, sizeof(ffxxDump)); + std::memcpy(ioamhram + 0x100, ffxxDump, sizeof ffxxDump); } } // anon namespace @@ -1223,7 +1223,7 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM state.ppu.bgpData.ptr[i + 1] = 0x7F; } - std::memcpy(state.ppu.objpData.ptr, cgbObjpDump, sizeof(cgbObjpDump)); + std::memcpy(state.ppu.objpData.ptr, cgbObjpDump, sizeof cgbObjpDump); if (!cgb) { state.ppu.bgpData.ptr[0] = state.mem.ioamhram.get()[0x147]; @@ -1235,9 +1235,9 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM state.ppu.oamReaderBuf.ptr[pos] = state.mem.ioamhram.ptr[(pos * 2 & ~3) | (pos & 1)]; std::fill_n(state.ppu.oamReaderSzbuf.ptr, 40, false); - std::memset(state.ppu.spAttribList, 0, sizeof(state.ppu.spAttribList)); - std::memset(state.ppu.spByte0List, 0, sizeof(state.ppu.spByte0List)); - std::memset(state.ppu.spByte1List, 0, sizeof(state.ppu.spByte1List)); + std::memset(state.ppu.spAttribList, 0, sizeof state.ppu.spAttribList); + std::memset(state.ppu.spByte0List, 0, sizeof state.ppu.spByte0List); + std::memset(state.ppu.spByte1List, 0, sizeof state.ppu.spByte1List); state.ppu.videoCycles = 0; state.ppu.enableDisplayM0Time = state.cpu.cycleCounter; state.ppu.winYPos = 0xFF; diff --git a/libgambatte/src/newstate.h b/libgambatte/src/newstate.h index 2457a83669..d741879cff 100644 --- a/libgambatte/src/newstate.h +++ b/libgambatte/src/newstate.h @@ -80,18 +80,18 @@ public: // first line is default value in converted enum; last line is default value in argument x -#define EBS(x,d) do { int _ttmp = (d); if (isReader) ns->Load(&_ttmp, sizeof(_ttmp), #x); if (0) +#define EBS(x,d) do { int _ttmp = (d); if (isReader) ns->Load(&_ttmp, sizeof _ttmp, #x); if (0) #define EVS(x,v,n) else if (!isReader && (x) == (v)) _ttmp = (n); else if (isReader && _ttmp == (n)) (x) = (v) -#define EES(x,d) else if (isReader) (x) = (d); if (!isReader) ns->Save(&_ttmp, sizeof(_ttmp), #x); } while (0) +#define EES(x,d) else if (isReader) (x) = (d); if (!isReader) ns->Save(&_ttmp, sizeof _ttmp, #x); } while (0) #define RSS(x,b) do { if (isReader)\ -{ ptrdiff_t _ttmp; ns->Load(&_ttmp, sizeof(_ttmp), #x); (x) = (_ttmp == (ptrdiff_t)0xdeadbeef ? 0 : (b) + _ttmp); }\ +{ ptrdiff_t _ttmp; ns->Load(&_ttmp, sizeof _ttmp, #x); (x) = (_ttmp == (ptrdiff_t)0xdeadbeef ? 0 : (b) + _ttmp); }\ else\ -{ ptrdiff_t _ttmp = (x) == 0 ? 0xdeadbeef : (x) - (b); ns->Save(&_ttmp, sizeof(_ttmp), #x); } } while (0) +{ ptrdiff_t _ttmp = (x) == 0 ? 0xdeadbeef : (x) - (b); ns->Save(&_ttmp, sizeof _ttmp, #x); } } while (0) #define PSS(x,s) do { if (isReader) ns->Load((x), (s), #x); else ns->Save((x), (s), #x); } while (0) -#define NSS(x) do { if (isReader) ns->Load(&(x), sizeof(x), #x); else ns->Save(&(x), sizeof(x), #x); } while (0) +#define NSS(x) do { if (isReader) ns->Load(&(x), sizeof x, #x); else ns->Save(&(x), sizeof x, #x); } while (0) #define SSS(x) do { ns->EnterSection(#x); (x).SyncState(ns); ns->ExitSection(#x); } while (0) diff --git a/libgambatte/src/sound.cpp b/libgambatte/src/sound.cpp index 01685afc74..508ebaf827 100644 --- a/libgambatte/src/sound.cpp +++ b/libgambatte/src/sound.cpp @@ -85,7 +85,7 @@ void PSG::loadState(const SaveState &state) { void PSG::accumulate_channels(const unsigned long cycles) { uint_least32_t *const buf = buffer + bufferPos; - std::memset(buf, 0, cycles * sizeof(uint_least32_t)); + std::memset(buf, 0, cycles * sizeof *buf); ch1.update(buf, soVol, cycles); ch2.update(buf, soVol, cycles); ch3.update(buf, soVol, cycles); diff --git a/libgambatte/src/video.cpp b/libgambatte/src/video.cpp index bb9a206932..c2b82a6495 100644 --- a/libgambatte/src/video.cpp +++ b/libgambatte/src/video.cpp @@ -57,7 +57,7 @@ LCD::LCD(const unsigned char *const oamram, const unsigned char *const vram, con std::memset( bgpData, 0, sizeof bgpData); std::memset(objpData, 0, sizeof objpData); - for (std::size_t i = 0; i < sizeof(dmgColorsRgb32) / sizeof(dmgColorsRgb32[0]); ++i) + for (std::size_t i = 0; i < sizeof dmgColorsRgb32 / sizeof dmgColorsRgb32[0]; ++i) setDmgPaletteColor(i, (3 - (i & 3)) * 85 * 0x010101); reset(oamram, vram, false); diff --git a/libgambatte/src/video/ppu.cpp b/libgambatte/src/video/ppu.cpp index fde43224fb..70d7db9e54 100644 --- a/libgambatte/src/video/ppu.cpp +++ b/libgambatte/src/video/ppu.cpp @@ -149,7 +149,7 @@ namespace M2 { }; static void f0(PPUPriv &p) { - std::memset(&p.spLut, 0, sizeof(p.spLut)); + std::memset(&p.spLut, 0, sizeof p.spLut); p.reg0 = 0; p.nextSprite = 0; p.nextCallPtr = &f1_; @@ -1638,7 +1638,7 @@ void PPU::loadState(const SaveState &ss, const unsigned char *const oamram) { p_.nextCallPtr = m3loopState; p_.cycles = -1; } else if (vcycs < 143 * 456L + static_cast(m3StartLineCycle(p_.cgb)) + MAX_M3START_CYCLES) { - const struct CycleState lineCycleStates[] = { + const CycleState lineCycleStates[] = { { &M3Start::f0_, m3StartLineCycle(p_.cgb) }, { &M3Start::f1_, m3StartLineCycle(p_.cgb) + MAX_M3START_CYCLES }, { &M2::LyNon0::f0_, weMasterCheckPriorToLyIncLineCycle(p_.cgb) }, @@ -1647,7 +1647,7 @@ void PPU::loadState(const SaveState &ss, const unsigned char *const oamram) { }; const std::size_t pos = - upperBound(lineCycleStates, lineCycles); + upperBound(lineCycleStates, lineCycles); p_.cycles = lineCycles - lineCycleStates[pos].cycle; p_.nextCallPtr = lineCycleStates[pos].state; diff --git a/libgambatte/src/video/sprite_mapper.cpp b/libgambatte/src/video/sprite_mapper.cpp index cd2e28b1b2..208b80e8e7 100644 --- a/libgambatte/src/video/sprite_mapper.cpp +++ b/libgambatte/src/video/sprite_mapper.cpp @@ -107,7 +107,7 @@ void SpriteMapper::OamReader::change(const unsigned long cc) { void SpriteMapper::OamReader::setStatePtrs(SaveState &state) { state.ppu.oamReaderBuf.set(buf, sizeof buf); - state.ppu.oamReaderSzbuf.set(szbuf, sizeof(szbuf) / sizeof(bool)); + state.ppu.oamReaderSzbuf.set(szbuf, sizeof szbuf / sizeof *szbuf); } void SpriteMapper::OamReader::loadState(const SaveState &ss, const unsigned char *const oamram) { @@ -129,7 +129,7 @@ SYNCFUNC(SpriteMapper::OamReader) } void SpriteMapper::OamReader::enableDisplay(const unsigned long cc) { - std::memset(buf, 0x00, sizeof(buf)); + std::memset(buf, 0x00, sizeof buf); std::fill(szbuf, szbuf + 40, false); lu = cc + (80 << lyCounter.isDoubleSpeed()); lastChange = 80; diff --git a/output/dll/libgambatte.dll b/output/dll/libgambatte.dll index 03e8ecd3795aa1a79f1a3e1a4ead98f60f0141ea..980a355abd9a40816cef4cc19e6ec16b03e12ac9 100644 GIT binary patch literal 143872 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·ZN;S1k8IqSc&T z&jQ4AxTQ0&Y9e@+eTuCo>;FblqKRJ`r4`dtzBhV!6iK2Zu!>$i?U7vNlSr-ywt}n# z-1 zF=UdGwv%b-y?GsGZf2T>CIP}V<|Fw1*WO3ck!=#U z(>HH^@Avp|taH}cXMe1{_F8MNz4qGw{LhG0-GpZSo-$I;d zT-8_?UyJKUxC#)_YB8=$xF&WZHaM=;xFqCbGN-sK*3>kY_?K>*kdbM3P0W%UlP2Gi zeXHxX+jH*t!kts}*Dlu=NBsNJ)Z8z-?z%hgp09j$+Sl%NIj8^0*Z(yC8#ChRV>bme z@B8Ncc$npyJ$v>8+~1Mnl_2J=qgsBQCIaNh<^NulcJUoJoxINZgm5}xJ3ufaoKA+; z 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 j#h9-|fA+HzY!-U53jId$JYU84@e@3Swg literal 170496 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 z Date: Sat, 25 May 2019 18:58:49 +0200 Subject: [PATCH 04/38] return more informative load result --- libgambatte/include/gambatte.h | 5 +- libgambatte/include/loadres.h | 23 ++++++ libgambatte/src/cpu.h | 2 +- libgambatte/src/gambatte.cpp | 10 +-- libgambatte/src/mem/cartridge.cpp | 124 ++++++++++++++---------------- libgambatte/src/mem/cartridge.h | 3 +- libgambatte/src/memory.cpp | 6 +- libgambatte/src/memory.h | 2 +- output/dll/libgambatte.dll | Bin 143872 -> 142336 bytes 9 files changed, 95 insertions(+), 80 deletions(-) create mode 100644 libgambatte/include/loadres.h diff --git a/libgambatte/include/gambatte.h b/libgambatte/include/gambatte.h index 94bab7befb..a3553bc139 100644 --- a/libgambatte/include/gambatte.h +++ b/libgambatte/include/gambatte.h @@ -20,6 +20,7 @@ #define GAMBATTE_H #include "gbint.h" +#include "loadres.h" #include #include #include @@ -48,7 +49,7 @@ class GB { public: GB(); ~GB(); - + enum LoadFlag { FORCE_DMG = 1, /**< Treat the ROM as not having CGB support regardless of what its header advertises. */ GBA_CGB = 2, /**< Use GBA intial CPU register values when in CGB mode. */ @@ -61,7 +62,7 @@ public: * @param flags ORed combination of LoadFlags. * @return 0 on success, negative value on failure. */ - int load(const char *romfiledata, unsigned romfilelength, std::uint32_t now, unsigned flags, unsigned div); + LoadRes load(const char *romfiledata, unsigned romfilelength, std::uint32_t now, unsigned flags, unsigned div); int loadGBCBios(const char* biosfiledata); int loadDMGBios(const char* biosfiledata); diff --git a/libgambatte/include/loadres.h b/libgambatte/include/loadres.h new file mode 100644 index 0000000000..6ee5e370b5 --- /dev/null +++ b/libgambatte/include/loadres.h @@ -0,0 +1,23 @@ +#ifndef GAMBATTE_LOADRES_H +#define GAMBATTE_LOADRES_H + +#include + +namespace gambatte { + +enum LoadRes { + LOADRES_BAD_FILE_OR_UNKNOWN_MBC = -0x7FFF, + LOADRES_IO_ERROR, + LOADRES_UNSUPPORTED_MBC_HUC3 = -0x1FE, + LOADRES_UNSUPPORTED_MBC_TAMA5, + LOADRES_UNSUPPORTED_MBC_POCKET_CAMERA, + LOADRES_UNSUPPORTED_MBC_MBC7 = -0x122, + LOADRES_UNSUPPORTED_MBC_MBC6 = -0x120, + LOADRES_UNSUPPORTED_MBC_MBC4 = -0x117, + LOADRES_UNSUPPORTED_MBC_MMM01 = -0x10D, + LOADRES_OK = 0 +}; + +} + +#endif diff --git a/libgambatte/src/cpu.h b/libgambatte/src/cpu.h index 427847e95d..8d3d7c99a7 100644 --- a/libgambatte/src/cpu.h +++ b/libgambatte/src/cpu.h @@ -104,7 +104,7 @@ public: memory.setLinkCallback(callback); } - int load(const char *romfiledata, unsigned romfilelength, bool forceDmg, bool multicartCompat) { + LoadRes load(const char *romfiledata, unsigned romfilelength, bool forceDmg, bool multicartCompat) { return memory.loadROM(romfiledata, romfilelength, forceDmg, multicartCompat); } diff --git a/libgambatte/src/gambatte.cpp b/libgambatte/src/gambatte.cpp index 818c09c48d..0c05ed4e1a 100644 --- a/libgambatte/src/gambatte.cpp +++ b/libgambatte/src/gambatte.cpp @@ -140,13 +140,13 @@ void GB::setLinkCallback(void(*callback)()) { p_->cpu.setLinkCallback(callback); } -int GB::load(const char *romfiledata, unsigned romfilelength, const std::uint32_t now, const unsigned flags, const unsigned div) { +LoadRes GB::load(const char *romfiledata, unsigned romfilelength, const std::uint32_t now, unsigned const flags, const unsigned div) { //if (p_->cpu.loaded()) // p_->cpu.saveSavedata(); - const int failed = p_->cpu.load(romfiledata, romfilelength, flags & FORCE_DMG, flags & MULTICART_COMPAT); + LoadRes const loadres = p_->cpu.load(romfiledata, romfilelength, flags & FORCE_DMG, flags & MULTICART_COMPAT); - if (!failed) { + if (loadres == LOADRES_OK) { SaveState state; p_->cpu.setStatePtrs(state); p_->loadflags = flags; @@ -155,7 +155,7 @@ int GB::load(const char *romfiledata, unsigned romfilelength, const std::uint32_ //p_->cpu.loadSavedata(); } - return failed; + return loadres; } int GB::loadGBCBios(const char* biosfiledata) { @@ -223,7 +223,7 @@ const std::string GB::romTitle() const { if (p_->cpu.loaded()) { char title[0x11]; std::memcpy(title, p_->cpu.romTitle(), 0x10); - title[(title[0xF] & 0x80) ? 0xF : 0x10] = '\0'; + title[title[0xF] & 0x80 ? 0xF : 0x10] = '\0'; return std::string(title); } diff --git a/libgambatte/src/mem/cartridge.cpp b/libgambatte/src/mem/cartridge.cpp index e062fb0c96..63e48171f3 100644 --- a/libgambatte/src/mem/cartridge.cpp +++ b/libgambatte/src/mem/cartridge.cpp @@ -525,7 +525,23 @@ static unsigned pow2ceil(unsigned n) { return n; } -int Cartridge::loadROM(const char *romfiledata, unsigned romfilelength, const bool forceDmg, const bool multicartCompat) { +static bool isMbc2(unsigned char h147) { return h147 == 5 || h147 == 6; } + +static unsigned numRambanksFromH14x(unsigned char h147, unsigned char h149) { + switch (h149) { + case 0x00: return isMbc2(h147) ? 1 : 0; + case 0x01: + case 0x02: return 1; + } + + return 4; +} + +static bool presumedMulti64Mbc1(unsigned char const header[], unsigned const rombanks) { + return header[0x147] == 1 && header[0x149] == 0 && rombanks == 64; +} + +LoadRes Cartridge::loadROM(const char *romfiledata, unsigned romfilelength, bool const forceDmg, bool const multicartCompat) { //const std::auto_ptr rom(newFileInstance(romfile)); //if (rom->fail()) @@ -542,39 +558,39 @@ int Cartridge::loadROM(const char *romfiledata, unsigned romfilelength, const bo if (romfilelength >= sizeof header) std::memcpy(header, romfiledata, sizeof header); else - return -1; + return LOADRES_IO_ERROR; switch (header[0x0147]) { - case 0x00: std::puts("Plain ROM loaded."); type = PLAIN; break; - case 0x01: std::puts("MBC1 ROM loaded."); type = MBC1; break; - case 0x02: std::puts("MBC1 ROM+RAM loaded."); type = MBC1; break; - case 0x03: std::puts("MBC1 ROM+RAM+BATTERY loaded."); type = MBC1; break; - case 0x05: std::puts("MBC2 ROM loaded."); type = MBC2; break; - case 0x06: std::puts("MBC2 ROM+BATTERY loaded."); type = MBC2; break; - case 0x08: std::puts("Plain ROM with additional RAM loaded."); type = PLAIN; break; - case 0x09: std::puts("Plain ROM with additional RAM and Battery loaded."); type = PLAIN; break; - case 0x0B: std::puts("MM01 ROM not supported."); return -1; - case 0x0C: std::puts("MM01 ROM not supported."); return -1; - case 0x0D: std::puts("MM01 ROM not supported."); return -1; - case 0x0F: std::puts("MBC3 ROM+TIMER+BATTERY loaded."); type = MBC3; break; - case 0x10: std::puts("MBC3 ROM+TIMER+RAM+BATTERY loaded."); type = MBC3; break; - case 0x11: std::puts("MBC3 ROM loaded."); type = MBC3; break; - case 0x12: std::puts("MBC3 ROM+RAM loaded."); type = MBC3; break; - case 0x13: std::puts("MBC3 ROM+RAM+BATTERY loaded."); type = MBC3; break; - case 0x15: std::puts("MBC4 ROM not supported."); return -1; - case 0x16: std::puts("MBC4 ROM not supported."); return -1; - case 0x17: std::puts("MBC4 ROM not supported."); return -1; - case 0x19: std::puts("MBC5 ROM loaded."); type = MBC5; break; - case 0x1A: std::puts("MBC5 ROM+RAM loaded."); type = MBC5; break; - case 0x1B: std::puts("MBC5 ROM+RAM+BATTERY loaded."); type = MBC5; break; - case 0x1C: std::puts("MBC5+RUMBLE ROM not supported."); type = MBC5; break; - case 0x1D: std::puts("MBC5+RUMBLE+RAM ROM not suported."); type = MBC5; break; - case 0x1E: std::puts("MBC5+RUMBLE+RAM+BATTERY ROM not supported."); type = MBC5; break; - case 0xFC: std::puts("Pocket Camera ROM not supported."); return -1; - case 0xFD: std::puts("Bandai TAMA5 ROM not supported."); return -1; - case 0xFE: std::puts("HuC3 ROM not supported."); return -1; - case 0xFF: std::puts("HuC1 ROM+RAM+BATTERY loaded."); type = HUC1; break; - default: std::puts("Wrong data-format, corrupt or unsupported ROM."); return -1; + case 0x00: type = PLAIN; break; + case 0x01: + case 0x02: + case 0x03: type = MBC1; break; + case 0x05: + case 0x06: type = MBC2; break; + case 0x08: + case 0x09: type = PLAIN; break; + case 0x0B: + case 0x0C: + case 0x0D: return LOADRES_UNSUPPORTED_MBC_MMM01; + case 0x0F: + case 0x10: + case 0x11: + case 0x12: + case 0x13: type = MBC3; break; + case 0x15: + case 0x16: + case 0x17: return LOADRES_UNSUPPORTED_MBC_MBC4; + case 0x19: + case 0x1A: + case 0x1B: + case 0x1C: + case 0x1D: + case 0x1E: type = MBC5; break; + case 0xFC: return LOADRES_UNSUPPORTED_MBC_POCKET_CAMERA; + case 0xFD: return LOADRES_UNSUPPORTED_MBC_TAMA5; + case 0xFE: return LOADRES_UNSUPPORTED_MBC_HUC3; + case 0xFF: type = HUC1; break; + default: return LOADRES_BAD_FILE_OR_UNKNOWN_MBC; } /*switch (header[0x0148]) { @@ -591,40 +607,15 @@ int Cartridge::loadROM(const char *romfiledata, unsigned romfilelength, const bo case 0x53: rombanks = 80; break; case 0x54: rombanks = 96; break; default: return -1; - } + }*/ - std::printf("rombanks: %u\n", rombanks);*/ - - switch (header[0x0149]) { - case 0x00: /*std::puts("No RAM");*/ rambanks = type == MBC2; break; - case 0x01: /*std::puts("2kB RAM");*/ /*rambankrom=1; break;*/ - case 0x02: /*std::puts("8kB RAM");*/ - rambanks = 1; - break; - case 0x03: /*std::puts("32kB RAM");*/ - rambanks = 4; - break; - case 0x04: /*std::puts("128kB RAM");*/ - rambanks = 16; - break; - case 0x05: /*std::puts("undocumented kB RAM");*/ - rambanks = 16; - break; - default: /*std::puts("Wrong data-format, corrupt or unsupported ROM loaded.");*/ - rambanks = 16; - break; - } - - cgb = !forceDmg; - std::printf("cgb: %d\n", cgb); - } - - std::printf("rambanks: %u\n", rambanks); - - const std::size_t filesize = romfilelength; //rom->size(); - rombanks = std::max(pow2ceil(filesize / 0x4000), 2u); - std::printf("rombanks: %u\n", static_cast(filesize / 0x4000)); + rambanks = numRambanksFromH14x(header[0x147], header[0x149]); + cgb = !forceDmg; + } + std::size_t const filesize = romfilelength; //rom->size(); + rombanks = std::max(pow2ceil(filesize / 0x4000), 2u); + mbc.reset(); memptrs.reset(rombanks, rambanks, cgb ? 8 : 2); rtc.set(false, 0); @@ -641,8 +632,7 @@ int Cartridge::loadROM(const char *romfiledata, unsigned romfilelength, const bo switch (type) { case PLAIN: mbc.reset(new Mbc0(memptrs)); break; case MBC1: - if (!rambanks && rombanks == 64 && multicartCompat) { - std::puts("Multi-ROM \"MBC1\" presumed"); + if (multicartCompat && presumedMulti64Mbc1(memptrs.romdata(), rombanks)) { mbc.reset(new Mbc1Multi64(memptrs)); } else mbc.reset(new Mbc1(memptrs)); @@ -654,7 +644,7 @@ int Cartridge::loadROM(const char *romfiledata, unsigned romfilelength, const bo case HUC1: mbc.reset(new HuC1(memptrs)); break; } - return 0; + return LOADRES_OK; } static bool hasBattery(const unsigned char headerByte0x147) { diff --git a/libgambatte/src/mem/cartridge.h b/libgambatte/src/mem/cartridge.h index 511c4438ae..934d44df7e 100644 --- a/libgambatte/src/mem/cartridge.h +++ b/libgambatte/src/mem/cartridge.h @@ -19,6 +19,7 @@ #ifndef CARTRIDGE_H #define CARTRIDGE_H +#include "loadres.h" #include "memptrs.h" #include "rtc.h" #include "savestate.h" @@ -98,7 +99,7 @@ public: bool getMemoryArea(int which, unsigned char **data, int *length) const; - int loadROM(const char *romfiledata, unsigned romfilelength, bool forceDmg, bool multicartCompat); + LoadRes loadROM(const char *romfiledata, unsigned romfilelength, bool forceDmg, bool multicartCompat); const char * romTitle() const { return reinterpret_cast(memptrs.romdata() + 0x134); } void setRTCCallback(std::uint32_t (*callback)()) { diff --git a/libgambatte/src/memory.cpp b/libgambatte/src/memory.cpp index 0b92460ca5..657a647fcf 100644 --- a/libgambatte/src/memory.cpp +++ b/libgambatte/src/memory.cpp @@ -1056,14 +1056,14 @@ void Memory::nontrivial_write(const unsigned P, const unsigned data, const unsig ioamhram[P - 0xFE00] = data; } -int Memory::loadROM(const char *romfiledata, unsigned romfilelength, const bool forceDmg, const bool multicartCompat) { - if (const int fail = cart.loadROM(romfiledata, romfilelength, forceDmg, multicartCompat)) +LoadRes Memory::loadROM(const char *romfiledata, unsigned romfilelength, const bool forceDmg, const bool multicartCompat) { + if (LoadRes const fail = cart.loadROM(romfiledata, romfilelength, forceDmg, multicartCompat)) return fail; sound.init(cart.isCgb()); display.reset(ioamhram, cart.vramdata(), cart.isCgb()); - return 0; + return LOADRES_OK; } unsigned Memory::fillSoundBuffer(const unsigned long cycleCounter) { diff --git a/libgambatte/src/memory.h b/libgambatte/src/memory.h index 3ea2eec6ac..fcd7981730 100644 --- a/libgambatte/src/memory.h +++ b/libgambatte/src/memory.h @@ -296,7 +296,7 @@ public: unsigned long event(unsigned long cycleCounter); unsigned long resetCounters(unsigned long cycleCounter); - int loadROM(const char *romfiledata, unsigned romfilelength, bool forceDmg, bool multicartCompat); + LoadRes loadROM(const char *romfiledata, unsigned romfilelength, bool forceDmg, bool multicartCompat); void setInputGetter(unsigned (*getInput)()) { this->getInput = getInput; diff --git a/output/dll/libgambatte.dll b/output/dll/libgambatte.dll index 980a355abd9a40816cef4cc19e6ec16b03e12ac9..4fac65dc3a39e0105e69397bffed25ce7456143c 100644 GIT binary patch delta 25769 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�K?u#B1denB!Y3UM6z%&e>2LSmBB9tOU!T; z05aE&9Kzoli8~5=6Zt2d=Zw##oJ%>F8fQu{C7O~<@n*ZZz+7a0!m>C`NL!Xxm=etG!9PRy$31NLQ~rtZUFsF|IIfGhQ)rDU(w2 zQvPEaV7}9Q!2E?-Z&_n`&eCq-(iCaRG*wzcT4GvKTCy`ulV(UWr6pODts1MrYO-3b z4(m+oV%+O}$d+JFv?tke|$YQlC$4O#LjCH(hUqwb(3oSRSw(v3zG4l$M>gGVRf{x6}TeHrzVNdXII#^?mDA z>$SESwhgu#+vm1EcAI^f{So^+_DE-XTzXFWwDkGu%hK;j-<)2NUYWixy(+yLXPLDA zFT(Q)?TgyCv>#}{*8W@DQ#VAHth4DR>*nhUbsKcMbd|anbmw%*`YHNb^tbC*>(}ac z>G$eu^>67v(zoh=(f2mQ8?=T};{(PgjIS9#!_XK~rlj1Ga!<;mDW9Y``=s8Jx;6Fw z)SA>|snxb~HcrjBIQXqx?Q7bT+E29?wZEd#fx0A}S$C7}7Tq%48l6Y?knVu4PIp{) zR@b8YLC5R+>l5_`{Z{=$`lt1;>QCxF)wlZ_9&eazxXrNAu-;H^c*0O?_?zKv!)Jyz z18?kaOfcT!G!`4T7#}h|W3;8_rp`@Wk-9#0TdFs;D)r^mcT)e6dLi}a)ZV5+rctI; zQ;w;?RAGA7^t$PksoC^{Db9SoIo&+fyvV%5yve-F{Fu4M{D%4O=4K4Xbr_H-mW7so zSiiIWZ0%)>vnAWiwwr9X*p}JW*>>0-$3Pshowhl@vR$(EuwQ4l+i$kdwclkgwR`N3 z+h4H1Y5&0fPx}>nuk@kmH>8hCpPqgj4N3x*5Ev4hK36|ize2xWzfJGeSLt8Yzpej3 z|E2y%Ju?h2j54Gc))?G|3d6I8*A1r(7Y$bpQO4m$ozZT***MpDm$B5i9i4v8*x)pt zHntf5ZRAt*cwy$JtW0sGTurG=eLHoc=}FT+P1WX8=3mVHEQyvBOO9o>Wtrt<+AG#6 zwna9<-eG?*ozpOBg8NGQhT%-=6XvJQkS>F9w0ln1{@u#q%YT|VFKgO0oK~S#YE@db zHd-rcn^P{OC{mrVsmZBUsD5_p9jR-e`Ug{=Og)tPR_X_-U!;DQdNs9=X_#q@$zr~uwBEGS^tkC6)2pTvrhk~eH-(us<}7ob`401X^G@^Q=4Z{XLEvWd1@jeiFUwHN zSc}Cn!IEzgEcaM8;r;llqW%cqv_;RjS{acS2((`;$e(r!^wW~(K`k_BDlS{#-6mv}d*F;0OMrjZGa2t9se=k!h9LYaVZzZn?#Bwj|sY zcERSex7#PDFHPr4@dkj=BU+~WTsKZ{g9V>4{M!(gVoKr48Fv9?Tkz7=>yE;$oYVR6 z2K=lG)2s9Y^uzR{^%}idKVE;cJ`Wbs$8?QJYf3X^n|7Gq^7fABuXo;PS)Nvr_F~$} zwC1!+X-aFH^#<#B>kRn2d#yXHd##78jn+?KwVbWLZ4_+wM%ygg9kzRHWwwWGPuX6F z&3}UIu3ZyphdtqhXfePQyCGPD7=k#_*Qm6T|lgg>jH^tTEGA zU|epz&*(KCFdi{}XgrUp^yBo- zdHQGcHTu89S)A8j*7q^w7^WH)8CJkylo~c0?lE`Gb=mg9JMY`AFesZ80ILK{E2#Go-6 zAhgw(0kLz8xez?hIJ*F{FEB2K^vjHekiW#Z7VdDPaVvUJZrp9$W30eLyw6x=tTxsd z4;kx?hm8%!qsHULlg1|F8RJ>wIb$BX^J*s4V0^2tQYjl^riZZ`rTL$Rq3nsNA+j)XZ3CR zOPIJhgVK;-NHioFtcF~}9ITTT8`i=)_h3S+G#oP28=Bw{&l)Zm+Oc#}!#)$SfJ&Bz zInRmLS036D|8UYU9+!r#bHG$7aVZHYi7D2UjFc>_wj3#oQ-qX~l(mu#)uc3}G^LzL zIg52^OG6R90$UYC-Cp)S}dlsk>A6q*kO>rXIKWEVQCb!m!bbweFmT LPh?^fU-7>HhoMe| delta 27187 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 G?EeBj{w87o From 567756d6cc841408bf3363fc064dcbfc7703058d Mon Sep 17 00:00:00 2001 From: MrWint Date: Sat, 25 May 2019 19:18:15 +0200 Subject: [PATCH 05/38] formatting/readability --- libgambatte/Makefile | 2 +- libgambatte/include/gambatte.h | 16 +- libgambatte/src/common/array.h | 4 +- libgambatte/src/cpu.cpp | 278 +++++------ libgambatte/src/cpu.h | 30 +- libgambatte/src/file/stdfile.h | 2 +- libgambatte/src/gambatte.cpp | 22 +- libgambatte/src/initstate.cpp | 102 ++-- libgambatte/src/insertion_sort.h | 12 +- libgambatte/src/interrupter.cpp | 2 +- libgambatte/src/interruptrequester.cpp | 18 +- libgambatte/src/interruptrequester.h | 24 +- libgambatte/src/mem/cartridge.cpp | 20 +- libgambatte/src/mem/cartridge.h | 12 +- libgambatte/src/mem/memptrs.cpp | 6 +- libgambatte/src/mem/memptrs.h | 8 +- libgambatte/src/mem/rtc.cpp | 14 +- libgambatte/src/mem/rtc.h | 22 +- libgambatte/src/memory.cpp | 48 +- libgambatte/src/memory.h | 30 +- libgambatte/src/minkeeper.h | 40 +- libgambatte/src/newstate.h | 2 +- libgambatte/src/savestate.h | 28 +- libgambatte/src/sound.cpp | 20 +- libgambatte/src/sound.h | 12 +- libgambatte/src/sound/channel1.cpp | 50 +- libgambatte/src/sound/channel1.h | 24 +- libgambatte/src/sound/channel2.cpp | 32 +- libgambatte/src/sound/channel2.h | 18 +- libgambatte/src/sound/channel3.cpp | 42 +- libgambatte/src/sound/channel3.h | 28 +- libgambatte/src/sound/channel4.cpp | 68 +-- libgambatte/src/sound/channel4.h | 26 +- libgambatte/src/sound/duty_unit.cpp | 14 +- libgambatte/src/sound/duty_unit.h | 2 +- libgambatte/src/sound/envelope_unit.cpp | 28 +- libgambatte/src/sound/envelope_unit.h | 4 +- libgambatte/src/sound/length_counter.cpp | 12 +- libgambatte/src/sound/master_disabler.h | 2 +- libgambatte/src/sound/sound_unit.h | 2 +- libgambatte/src/tima.cpp | 28 +- libgambatte/src/tima.h | 14 +- libgambatte/src/video.cpp | 146 +++--- libgambatte/src/video.h | 44 +- libgambatte/src/video/ly_counter.cpp | 12 +- libgambatte/src/video/ly_counter.h | 8 +- libgambatte/src/video/lyc_irq.cpp | 12 +- libgambatte/src/video/lyc_irq.h | 8 +- libgambatte/src/video/next_m0_time.h | 2 +- libgambatte/src/video/ppu.cpp | 562 +++++++++++------------ libgambatte/src/video/ppu.h | 8 +- libgambatte/src/video/sprite_mapper.cpp | 6 +- libgambatte/src/video/sprite_mapper.h | 2 +- output/dll/libgambatte.dll | Bin 142336 -> 142336 bytes 54 files changed, 989 insertions(+), 989 deletions(-) diff --git a/libgambatte/Makefile b/libgambatte/Makefile index 33e7712716..5ae56f6a29 100644 --- a/libgambatte/Makefile +++ b/libgambatte/Makefile @@ -61,6 +61,6 @@ $(TARGET) : $(OBJS) clean: $(RM) $(OBJS) $(RM) $(TARGET) - + install: $(CP) $(TARGET) $(DEST_$(ARCH)) diff --git a/libgambatte/include/gambatte.h b/libgambatte/include/gambatte.h index a3553bc139..c90620eb01 100644 --- a/libgambatte/include/gambatte.h +++ b/libgambatte/include/gambatte.h @@ -55,7 +55,7 @@ public: GBA_CGB = 2, /**< Use GBA intial CPU register values when in CGB mode. */ MULTICART_COMPAT = 4, /**< Use heuristics to detect and support some multicart MBCs disguised as MBC1. */ }; - + /** Load ROM image. * * @param romfile Path to rom image file. Typically a .gbc, .gb, or .zip-file (if zip-support is compiled in). @@ -63,7 +63,7 @@ public: * @return 0 on success, negative value on failure. */ LoadRes load(const char *romfiledata, unsigned romfilelength, std::uint32_t now, unsigned flags, unsigned div); - + int loadGBCBios(const char* biosfiledata); int loadDMGBios(const char* biosfiledata); @@ -96,17 +96,17 @@ public: * Equivalent to reloading a ROM image, or turning a Game Boy Color off and on again. */ void reset(std::uint32_t now, unsigned div); - + /** @param palNum 0 <= palNum < 3. One of BG_PALETTE, SP1_PALETTE and SP2_PALETTE. * @param colorNum 0 <= colorNum < 4 */ void setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned rgb32); - + void setCgbPalette(unsigned *lut); /** Sets the callback used for getting input state. */ void setInputGetter(unsigned (*getInput)()); - + void setReadCallback(MemoryCallback); void setWriteCallback(MemoryCallback); void setExecCallback(MemoryCallback); @@ -118,7 +118,7 @@ public: /** Returns true if the currently loaded ROM image is treated as having CGB support. */ bool isCgb() const; - + /** Returns true if a ROM image is loaded. */ bool isLoaded() const; @@ -126,10 +126,10 @@ public: void loadSavedata(const char *data); int saveSavedataLength(); void saveSavedata(char *dest); - + // 0 = vram, 1 = rom, 2 = wram, 3 = cartram, 4 = oam, 5 = hram bool getMemoryArea(int which, unsigned char **data, int *length); - + /** ROM header title of currently loaded ROM image. */ const std::string romTitle() const; diff --git a/libgambatte/src/common/array.h b/libgambatte/src/common/array.h index c650390d4e..cc45e8584a 100644 --- a/libgambatte/src/common/array.h +++ b/libgambatte/src/common/array.h @@ -26,7 +26,7 @@ template class Array : Uncopyable { T *a; std::size_t sz; - + public: explicit Array(const std::size_t size = 0) : a(size ? new T[size] : 0), sz(size) {} ~Array() { delete []a; } @@ -39,7 +39,7 @@ public: template class ScopedArray : Uncopyable { T *a_; - + public: explicit ScopedArray(T *a = 0) : a_(a) {} ~ScopedArray() { delete []a_; } diff --git a/libgambatte/src/cpu.cpp b/libgambatte/src/cpu.cpp index 8ea6714cb3..f884e897a6 100644 --- a/libgambatte/src/cpu.cpp +++ b/libgambatte/src/cpu.cpp @@ -47,12 +47,12 @@ CPU::CPU() long CPU::runFor(const unsigned long cycles) { memory.setBasetime(cycleCounter_); process(cycles/* << memory.isDoubleSpeed()*/); - + const long csb = memory.cyclesSinceBlit(cycleCounter_); - + if (cycleCounter_ & 0x80000000) cycleCounter_ = memory.resetCounters(cycleCounter_); - + return csb; } @@ -63,7 +63,7 @@ long CPU::runFor(const unsigned long cycles) { static void calcHF(const unsigned HF1, unsigned& HF2) { unsigned arg1 = HF1 & 0xF; unsigned arg2 = (HF2 & 0xF) + (HF2 >> 8 & 1); - + if (HF2 & 0x800) { arg1 = arg2; arg2 = 1; @@ -73,7 +73,7 @@ static void calcHF(const unsigned HF1, unsigned& HF2) { arg1 -= arg2; else arg1 = (arg1 + arg2) << 5; - + HF2 |= arg1 & 0x200; } @@ -93,7 +93,7 @@ void CPU::setStatePtrs(SaveState &state) { void CPU::loadState(const SaveState &state) { memory.loadState(state/*, cycleCounter_*/); - + cycleCounter_ = state.cpu.cycleCounter; PC = state.cpu.PC & 0xFFFF; SP = state.cpu.SP & 0xFFFF; @@ -497,10 +497,10 @@ void CPU::process(const unsigned long cycles) { //unsigned char A = A_; unsigned long cycleCounter = cycleCounter_; - + while (memory.isActive()) { //unsigned short PC = PC_; - + if (memory.halted()) { if (cycleCounter < memory.nextEventTime()) { const unsigned long cycles = memory.nextEventTime() - cycleCounter; @@ -508,7 +508,7 @@ void CPU::process(const unsigned long cycles) { } } else while (cycleCounter < memory.nextEventTime()) { unsigned char opcode = 0x00; - + int FullPC = PC; if (PC >= 0x4000 && PC <= 0x7FFF) @@ -552,7 +552,7 @@ void CPU::process(const unsigned long cycles) { skip = false; } } - + switch (opcode) { //nop (4 cycles): //Do nothing for 4 cycles: @@ -591,12 +591,12 @@ void CPU::process(const unsigned long cycles) { case 0x08: { unsigned l, h; - + PC_READ(l); PC_READ(h); - + const unsigned addr = h << 8 | l; - + WRITE(addr, SP & 0xFF); WRITE((addr + 1) & 0xFFFF, SP >> 8); } @@ -669,7 +669,7 @@ void CPU::process(const unsigned long cycles) { CF = A << 1; A = (CF | oldcf) & 0xFF; } - + HF2 = 0; ZF = 1; break; @@ -704,7 +704,7 @@ void CPU::process(const unsigned long cycles) { CF = A << 8; A = (A | oldcf) >> 1; } - + HF2 = 0; ZF = 1; break; @@ -728,9 +728,9 @@ void CPU::process(const unsigned long cycles) { case 0x22: { unsigned addr = HL(); - + WRITE(addr, A); - + addr = (addr + 1) & 0xFFFF; L = addr; H = addr >> 8; @@ -756,38 +756,38 @@ void CPU::process(const unsigned long cycles) { case 0x27: /*{ unsigned correction = ((A > 0x99) || (CF & 0x100)) ? 0x60 : 0x00; - + calcHF(HF1, HF2); - + if ((A & 0x0F) > 0x09 || (HF2 & 0x200)) correction |= 0x06; - + HF1 = A; HF2 = (HF2 & 0x400) | correction; CF = (correction & 0x40) << 2; A = (HF2 & 0x400) ? A - correction : (A + correction); ZF = A; }*/ - + calcHF(HF1, HF2); - + { unsigned correction = (CF & 0x100) ? 0x60 : 0x00; - + if (HF2 & 0x200) correction |= 0x06; - + if (!(HF2 &= 0x400)) { if ((A & 0x0F) > 0x09) correction |= 0x06; - + if (A > 0x99) correction |= 0x60; - + A += correction; } else A -= correction; - + CF = correction << 2 & 0x100; ZF = A; A &= 0xFF; @@ -815,9 +815,9 @@ void CPU::process(const unsigned long cycles) { case 0x2A: { unsigned addr = HL(); - + READ(A, addr); - + addr = (addr + 1) & 0xFFFF; L = addr; H = addr >> 8; @@ -859,10 +859,10 @@ void CPU::process(const unsigned long cycles) { case 0x31: { unsigned l, h; - + PC_READ(l); PC_READ(h); - + SP = h << 8 | l; } break; @@ -872,9 +872,9 @@ void CPU::process(const unsigned long cycles) { case 0x32: { unsigned addr = HL(); - + WRITE(addr, A); - + addr = (addr - 1) & 0xFFFF; L = addr; H = addr >> 8; @@ -888,10 +888,10 @@ void CPU::process(const unsigned long cycles) { //inc (hl) (12 cycles): //Increment value at address in hl, check flags except CF: - case 0x34: + case 0x34: { const unsigned addr = HL(); - + READ(HF2, addr); ZF = HF2 + 1; WRITE(addr, ZF & 0xFF); @@ -904,7 +904,7 @@ void CPU::process(const unsigned long cycles) { case 0x35: { const unsigned addr = HL(); - + READ(HF2, addr); ZF = HF2 - 1; WRITE(addr, ZF & 0xFF); @@ -917,7 +917,7 @@ void CPU::process(const unsigned long cycles) { case 0x36: { unsigned tmp; - + PC_READ(tmp); WRITE(HL(), tmp); } @@ -958,10 +958,10 @@ void CPU::process(const unsigned long cycles) { case 0x3A: { unsigned addr = HL(); - + A = memory.read(addr, cycleCounter); cycleCounter += 4; - + addr = (addr - 1) & 0xFFFF; L = addr; H = addr >> 8; @@ -1221,7 +1221,7 @@ void CPU::process(const unsigned long cycles) { unsigned data; READ(data, HL()); - + add_a_u8(data); } break; @@ -1249,9 +1249,9 @@ void CPU::process(const unsigned long cycles) { case 0x8E: { unsigned data; - + READ(data, HL()); - + adc_a_u8(data); } break; @@ -1279,9 +1279,9 @@ void CPU::process(const unsigned long cycles) { case 0x96: { unsigned data; - + READ(data, HL()); - + sub_a_u8(data); } break; @@ -1311,9 +1311,9 @@ void CPU::process(const unsigned long cycles) { case 0x9E: { unsigned data; - + READ(data, HL()); - + sbc_a_u8(data); } break; @@ -1341,9 +1341,9 @@ void CPU::process(const unsigned long cycles) { case 0xA6: { unsigned data; - + READ(data, HL()); - + and_a_u8(data); } break; @@ -1374,9 +1374,9 @@ void CPU::process(const unsigned long cycles) { case 0xAE: { unsigned data; - + READ(data, HL()); - + xor_a_u8(data); } break; @@ -1405,9 +1405,9 @@ void CPU::process(const unsigned long cycles) { case 0xB6: { unsigned data; - + READ(data, HL()); - + or_a_u8(data); } break; @@ -1437,9 +1437,9 @@ void CPU::process(const unsigned long cycles) { case 0xBE: { unsigned data; - + READ(data, HL()); - + cp_a_u8(data); } break; @@ -1453,7 +1453,7 @@ void CPU::process(const unsigned long cycles) { //Pop two bytes from the stack and jump to that address, if ZF is unset: case 0xC0: cycleCounter += 4; - + if (ZF & 0xFF) { ret(); } @@ -1495,9 +1495,9 @@ void CPU::process(const unsigned long cycles) { case 0xC6: { unsigned data; - + PC_READ(data); - + add_a_u8(data); } break; @@ -1509,11 +1509,11 @@ void CPU::process(const unsigned long cycles) { //Pop two bytes from the stack and jump to that address, if ZF is set: case 0xC8: cycleCounter += 4; - + if (!(ZF & 0xFF)) { ret(); } - + break; //ret (16 cycles): @@ -1537,7 +1537,7 @@ void CPU::process(const unsigned long cycles) { //CB OPCODES (Shifts, rotates and bits): case 0xCB: PC_READ(opcode); - + switch (opcode) { case 0x00: rlc_r(B); @@ -1562,14 +1562,14 @@ void CPU::process(const unsigned long cycles) { case 0x06: { const unsigned addr = HL(); - + READ(CF, addr); CF <<= 1; - + ZF = CF | (CF >> 8); WRITE(addr, ZF & 0xFF); - + HF2 = 0; } break; @@ -1599,13 +1599,13 @@ void CPU::process(const unsigned long cycles) { case 0x0E: { const unsigned addr = HL(); - + READ(ZF, addr); - + CF = ZF << 8; - + WRITE(addr, (ZF | CF) >> 1 & 0xFF); - + HF2 = 0; } break; @@ -1636,14 +1636,14 @@ void CPU::process(const unsigned long cycles) { { const unsigned addr = HL(); const unsigned oldcf = CF >> 8 & 1; - + READ(CF, addr); CF <<= 1; - + ZF = CF | oldcf; - + WRITE(addr, ZF & 0xFF); - + HF2 = 0; } break; @@ -1673,15 +1673,15 @@ void CPU::process(const unsigned long cycles) { case 0x1E: { const unsigned addr = HL(); - + READ(ZF, addr); - + const unsigned oldcf = CF & 0x100; CF = ZF << 8; ZF = (ZF | oldcf) >> 1; - + WRITE(addr, ZF); - + HF2 = 0; } break; @@ -1711,14 +1711,14 @@ void CPU::process(const unsigned long cycles) { case 0x26: { const unsigned addr = HL(); - + READ(CF, addr); CF <<= 1; - + ZF = CF; - + WRITE(addr, ZF & 0xFF); - + HF2 = 0; } break; @@ -1748,13 +1748,13 @@ void CPU::process(const unsigned long cycles) { case 0x2E: { const unsigned addr = HL(); - + READ(CF, addr); - + ZF = CF >> 1; - + WRITE(addr, ZF | (CF & 0x80)); - + CF <<= 8; HF2 = 0; } @@ -1785,11 +1785,11 @@ void CPU::process(const unsigned long cycles) { case 0x36: { const unsigned addr = HL(); - + READ(ZF, addr); - + WRITE(addr, (ZF << 4 | ZF >> 4) & 0xFF); - + CF = HF2 = 0; } break; @@ -1819,13 +1819,13 @@ void CPU::process(const unsigned long cycles) { case 0x3E: { const unsigned addr = HL(); - + READ(CF, addr); - + ZF = CF >> 1; - + WRITE(addr, ZF); - + CF <<= 8; HF2 = 0; } @@ -1854,9 +1854,9 @@ void CPU::process(const unsigned long cycles) { case 0x46: { unsigned data; - + READ(data, HL()); - + bit0_u8(data); } break; @@ -1884,9 +1884,9 @@ void CPU::process(const unsigned long cycles) { case 0x4E: { unsigned data; - + READ(data, HL()); - + bit1_u8(data); } break; @@ -1914,9 +1914,9 @@ void CPU::process(const unsigned long cycles) { case 0x56: { unsigned data; - + READ(data, HL()); - + bit2_u8(data); } break; @@ -1944,9 +1944,9 @@ void CPU::process(const unsigned long cycles) { case 0x5E: { unsigned data; - + READ(data, HL()); - + bit3_u8(data); } break; @@ -1974,9 +1974,9 @@ void CPU::process(const unsigned long cycles) { case 0x66: { unsigned data; - + READ(data, HL()); - + bit4_u8(data); } break; @@ -2004,9 +2004,9 @@ void CPU::process(const unsigned long cycles) { case 0x6E: { unsigned data; - + READ(data, HL()); - + bit5_u8(data); } break; @@ -2034,9 +2034,9 @@ void CPU::process(const unsigned long cycles) { case 0x76: { unsigned data; - + READ(data, HL()); - + bit6_u8(data); } break; @@ -2064,9 +2064,9 @@ void CPU::process(const unsigned long cycles) { case 0x7E: { unsigned data; - + READ(data, HL()); - + bit7_u8(data); } break; @@ -2479,9 +2479,9 @@ void CPU::process(const unsigned long cycles) { case 0xCE: { unsigned data; - + PC_READ(data); - + adc_a_u8(data); } break; @@ -2493,11 +2493,11 @@ void CPU::process(const unsigned long cycles) { //Pop two bytes from the stack and jump to that address, if CF is unset: case 0xD0: cycleCounter += 4; - + if (!(CF & 0x100)) { ret(); } - + break; case 0xD1: @@ -2537,9 +2537,9 @@ void CPU::process(const unsigned long cycles) { case 0xD6: { unsigned data; - + PC_READ(data); - + sub_a_u8(data); } break; @@ -2551,11 +2551,11 @@ void CPU::process(const unsigned long cycles) { //Pop two bytes from the stack and jump to that address, if CF is set: case 0xD8: cycleCounter += 4; - + if (CF & 0x100) { ret(); } - + break; //reti (16 cycles): @@ -2563,11 +2563,11 @@ void CPU::process(const unsigned long cycles) { case 0xD9: { unsigned l, h; - + pop_rr(h, l); - + memory.ei(cycleCounter); - + PC_MOD(h << 8 | l); } break; @@ -2607,9 +2607,9 @@ void CPU::process(const unsigned long cycles) { case 0xDE: { unsigned data; - + PC_READ(data); - + sbc_a_u8(data); } break; @@ -2622,9 +2622,9 @@ void CPU::process(const unsigned long cycles) { case 0xE0: { unsigned tmp; - + PC_READ(tmp); - + FF_WRITE(0xFF00 | tmp, A); } break; @@ -2652,9 +2652,9 @@ void CPU::process(const unsigned long cycles) { case 0xE6: { unsigned data; - + PC_READ(data); - + and_a_u8(data); } break; @@ -2690,10 +2690,10 @@ void CPU::process(const unsigned long cycles) { case 0xEA: { unsigned l, h; - + PC_READ(l); PC_READ(h); - + WRITE(h << 8 | l, A); } break; @@ -2713,9 +2713,9 @@ void CPU::process(const unsigned long cycles) { case 0xEE: { unsigned data; - + PC_READ(data); - + xor_a_u8(data); } break; @@ -2728,9 +2728,9 @@ void CPU::process(const unsigned long cycles) { case 0xF0: { unsigned tmp; - + PC_READ(tmp); - + FF_READ(A, 0xFF00 | tmp); } break; @@ -2738,9 +2738,9 @@ void CPU::process(const unsigned long cycles) { case 0xF1: /*pop_rr(A, F); Cycles(12); break;*/ { unsigned F; - + pop_rr(A, F); - + FROM_F(F); } break; @@ -2762,10 +2762,10 @@ void CPU::process(const unsigned long cycles) { break; case 0xF5: /*push_rr(A, F); Cycles(16); break;*/ calcHF(HF1, HF2); - + { unsigned F = F(); - + push_rr(A, F); } break; @@ -2775,7 +2775,7 @@ void CPU::process(const unsigned long cycles) { unsigned data; PC_READ(data); - + or_a_u8(data); } break; @@ -2816,10 +2816,10 @@ void CPU::process(const unsigned long cycles) { case 0xFA: { unsigned l, h; - + PC_READ(l); PC_READ(h); - + READ(A, h << 8 | l); } break; @@ -2843,7 +2843,7 @@ void CPU::process(const unsigned long cycles) { unsigned data; PC_READ(data); - + cp_a_u8(data); } break; @@ -2853,11 +2853,11 @@ void CPU::process(const unsigned long cycles) { // default: break; } } - + //PC_ = PC; cycleCounter = memory.event(cycleCounter); } - + //A_ = A; cycleCounter_ = cycleCounter; } diff --git a/libgambatte/src/cpu.h b/libgambatte/src/cpu.h index 8d3d7c99a7..d7ffcd7b17 100644 --- a/libgambatte/src/cpu.h +++ b/libgambatte/src/cpu.h @@ -26,48 +26,48 @@ namespace gambatte { class CPU { Memory memory; - + unsigned long cycleCounter_; unsigned short PC; unsigned short SP; - + unsigned HF1, HF2, ZF, CF; unsigned char A, B, C, D, E, /*F,*/ H, L; bool skip; - + int *interruptAddresses; int numInterruptAddresses; int hitInterruptAddress; void process(unsigned long cycles); - + void (*tracecallback)(void *); public: - + CPU(); // void halt(); // unsigned interrupt(unsigned address, unsigned cycleCounter); - + long runFor(unsigned long cycles); void setStatePtrs(SaveState &state); void loadState(const SaveState &state); void setLayers(unsigned mask) { memory.setLayers(mask); } - + void loadSavedata(const char *data) { memory.loadSavedata(data); } int saveSavedataLength() {return memory.saveSavedataLength(); } void saveSavedata(char *dest) { memory.saveSavedata(dest); } - + bool getMemoryArea(int which, unsigned char **data, int *length) { return memory.getMemoryArea(which, data, length); } void setVideoBuffer(uint_least32_t *const videoBuf, const int pitch) { memory.setVideoBuffer(videoBuf, pitch); } - + void setInputGetter(unsigned (*getInput)()) { memory.setInputGetter(getInput); } @@ -103,19 +103,19 @@ public: void setLinkCallback(void(*callback)()) { memory.setLinkCallback(callback); } - + LoadRes load(const char *romfiledata, unsigned romfilelength, bool forceDmg, bool multicartCompat) { return memory.loadROM(romfiledata, romfilelength, forceDmg, multicartCompat); } - + bool loaded() const { return memory.loaded(); } const char * romTitle() const { return memory.romTitle(); } - + void setSoundBuffer(uint_least32_t *const buf) { memory.setSoundBuffer(buf); } unsigned fillSoundBuffer() { return memory.fillSoundBuffer(cycleCounter_); } - + bool isCgb() const { return memory.isCgb(); } - + void setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned rgb32) { memory.setDmgPaletteColor(palNum, colorNum, rgb32); } @@ -123,7 +123,7 @@ public: void setCgbPalette(unsigned *lut) { memory.setCgbPalette(lut); } - + unsigned char* cgbBiosBuffer() { return memory.cgbBiosBuffer(); } unsigned char* dmgBiosBuffer() { return memory.dmgBiosBuffer(); } bool gbIsCgb() { return memory.gbIsCgb(); } diff --git a/libgambatte/src/file/stdfile.h b/libgambatte/src/file/stdfile.h index 625e765c57..72947b11a7 100644 --- a/libgambatte/src/file/stdfile.h +++ b/libgambatte/src/file/stdfile.h @@ -41,7 +41,7 @@ public: stream.seekg(0, std::ios::beg); } } - + virtual void rewind() { stream.seekg(0, std::ios::beg); } virtual std::size_t size() const { return fsize; }; virtual void read(char *buffer, std::size_t amount) { stream.read(buffer, amount); } diff --git a/libgambatte/src/gambatte.cpp b/libgambatte/src/gambatte.cpp index 0c05ed4e1a..e8580829c5 100644 --- a/libgambatte/src/gambatte.cpp +++ b/libgambatte/src/gambatte.cpp @@ -30,7 +30,7 @@ struct GB::Priv { unsigned layersMask; uint_least32_t vbuff[160*144]; - + Priv() : loadflags(0), layersMask(LAYER_MASK_BG | LAYER_MASK_OBJ) { } @@ -39,13 +39,13 @@ struct GB::Priv { { } }; - + GB::GB() : p_(new Priv) {} GB::~GB() { //if (p_->cpu.loaded()) // p_->cpu.saveSavedata(); - + delete p_; } @@ -54,12 +54,12 @@ long GB::runFor(gambatte::uint_least32_t *const soundBuf, unsigned &samples) { samples = 0; return -1; } - + p_->cpu.setVideoBuffer(p_->vbuff, 160); p_->cpu.setSoundBuffer(soundBuf); const long cyclesSinceBlit = p_->cpu.runFor(samples * 2); samples = p_->cpu.fillSoundBuffer(); - + return cyclesSinceBlit < 0 ? cyclesSinceBlit : static_cast(samples) - (cyclesSinceBlit >> 1); } @@ -83,7 +83,7 @@ void GB::blitTo(gambatte::uint_least32_t *videoBuf, int pitch) void GB::reset(const std::uint32_t now, const unsigned div) { if (p_->cpu.loaded()) { - + int length = p_->cpu.saveSavedataLength(); char *s; if (length > 0) @@ -91,7 +91,7 @@ void GB::reset(const std::uint32_t now, const unsigned div) { s = (char *) std::malloc(length); p_->cpu.saveSavedata(s); } - + SaveState state; p_->cpu.setStatePtrs(state); setInitState(state, !(p_->loadflags & FORCE_DMG), p_->loadflags & GBA_CGB, now, div); @@ -143,9 +143,9 @@ void GB::setLinkCallback(void(*callback)()) { LoadRes GB::load(const char *romfiledata, unsigned romfilelength, const std::uint32_t now, unsigned const flags, const unsigned div) { //if (p_->cpu.loaded()) // p_->cpu.saveSavedata(); - + LoadRes const loadres = p_->cpu.load(romfiledata, romfilelength, flags & FORCE_DMG, flags & MULTICART_COMPAT); - + if (loadres == LOADRES_OK) { SaveState state; p_->cpu.setStatePtrs(state); @@ -154,7 +154,7 @@ LoadRes GB::load(const char *romfiledata, unsigned romfilelength, const std::uin p_->cpu.loadState(state); //p_->cpu.loadSavedata(); } - + return loadres; } @@ -226,7 +226,7 @@ const std::string GB::romTitle() const { title[title[0xF] & 0x80 ? 0xF : 0x10] = '\0'; return std::string(title); } - + return std::string(); } diff --git a/libgambatte/src/initstate.cpp b/libgambatte/src/initstate.cpp index d539354112..7134577e2d 100644 --- a/libgambatte/src/initstate.cpp +++ b/libgambatte/src/initstate.cpp @@ -677,29 +677,29 @@ static void setInitialCgbWram(unsigned char *const wram) { { 0x7FCD, 0xBF }, { 0x7FCE, 0x7F }, { 0x7FCF, 0xFB }, { 0x7FDB, 0xF7 }, { 0x7FDF, 0x7F }, { 0x7FE8, 0xDF }, { 0x7FEC, 0xFB }, { 0x7FF2, 0xF7 } }; - + for (unsigned addr = 0x0000; addr < 0x0800; addr += 0x10) { std::memset(wram + addr + 0x00, 0xFF, 0x08); std::memset(wram + addr + 0x08, 0x00, 0x08); } - + for (unsigned addr = 0x0800; addr < 0x1000; addr += 0x10) { std::memset(wram + addr + 0x00, 0x00, 0x08); std::memset(wram + addr + 0x08, 0xFF, 0x08); } - + for (unsigned addr = 0x0E00; addr < 0x1000; addr += 0x10) { wram[addr + 0x02] = 0xFF; wram[addr + 0x0A] = 0x00; } - + for (unsigned addr = 0x1000; addr < 0x8000; addr += 0x1000) { if (0x2000 != addr) std::memcpy(wram + addr, wram, 0x1000); } - + std::memset(wram + 0x2000, 0, 0x1000); - + for (std::size_t i = 0; i < sizeof cgbWramDumpDiff / sizeof cgbWramDumpDiff[0]; ++i) wram[cgbWramDumpDiff[i].addr] = cgbWramDumpDiff[i].val; } @@ -959,19 +959,19 @@ static void setInitialDmgWram(unsigned char *const wram) { { 0x1FA2, 0x40 }, { 0x1FB6, 0x80 }, { 0x1FC6, 0x10 }, { 0x1FCC, 0x20 }, { 0x1FD2, 0x20 }, { 0x1FD8, 0x04 }, { 0x1FDC, 0x10 }, { 0x1FDE, 0x04 } }; - + for (unsigned addr = 0x0000; addr < 0x0800; addr += 0x200) { std::memset(wram + addr , 0x00, 0x100); std::memset(wram + addr + 0x100, 0xFF, 0x100); } - + for (unsigned addr = 0x0800; addr < 0x1000; addr += 0x200) { std::memset(wram + addr , 0xFF, 0x100); std::memset(wram + addr + 0x100, 0x00, 0x100); } - + std::memcpy(wram + 0x1000, wram, 0x1000); - + for (std::size_t i = 0; i < sizeof dmgWramDumpDiff / sizeof dmgWramDumpDiff[0]; ++i) wram[dmgWramDumpDiff[i].addr] = dmgWramDumpDiff[i].val; } @@ -1004,21 +1004,21 @@ static void setInitialVram(unsigned char *const vram, const bool cgb) { 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0xFC, 0xFC, 0x3C, 0x42, 0xB9, 0xA5, 0xB9, 0xA5, 0x42, 0x3C }; - + std::memset(vram, 0, 0x4000); - + for (std::size_t i = 0; i < sizeof even_numbered_8010_to_81a0_dump; ++i) { vram[0x0010 + i * 2] = even_numbered_8010_to_81a0_dump[i]; } - + if (!cgb) { unsigned i = 1; - + for (unsigned addr = 0x1904; addr < 0x1910; ++addr) vram[addr] = i++; - + vram[0x1910] = 0x19; - + for (unsigned addr = 0x1924; addr < 0x1930; ++addr) vram[addr] = i++; } @@ -1039,7 +1039,7 @@ static void setInitialCgbIoamhram(unsigned char *const ioamhram) { 0x24, 0x13, 0xFD, 0x3A, 0x10, 0x10, 0xAD, 0x45, 0x24, 0x13, 0xFD, 0x3A, 0x10, 0x10, 0xAD, 0x45 }; - + static const unsigned char ffxxDump[0x100] = { 0xCF, 0x00, 0x7C, 0xFF, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, @@ -1074,7 +1074,7 @@ static void setInitialCgbIoamhram(unsigned char *const ioamhram) { 0x98, 0xD1, 0x71, 0x02, 0x4D, 0x01, 0xC1, 0xFF, 0x0D, 0x00, 0xD3, 0x05, 0xF9, 0x00, 0x0B, 0x00 }; - + std::memset(ioamhram, 0x00, 0x0A0); std::memcpy(ioamhram + 0x0A0, feaxDump, sizeof feaxDump); std::memcpy(ioamhram + 0x100, ffxxDump, sizeof ffxxDump); @@ -1103,7 +1103,7 @@ static void setInitialDmgIoamhram(unsigned char *const ioamhram) { 0x24, 0x40, 0x42, 0x05, 0x0E, 0x04, 0x20, 0xA6, 0x5E, 0xC1, 0x97, 0x7E, 0x44, 0x05, 0x01, 0xA9 }; - + static const unsigned char ffxxDump[0x100] = { 0xCF, 0x00, 0x7E, 0xFF, 0xD3, 0x00, 0x00, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, @@ -1138,7 +1138,7 @@ static void setInitialDmgIoamhram(unsigned char *const ioamhram) { 0xBC, 0x7F, 0x7E, 0xD0, 0xC7, 0xC3, 0xBD, 0xCF, 0x59, 0xEA, 0x39, 0x01, 0x2E, 0x00, 0x69, 0x00 }; - + std::memcpy(ioamhram , oamDump, sizeof oamDump); std::memset(ioamhram + 0x0A0, 0x00, 0x060); std::memcpy(ioamhram + 0x100, ffxxDump, sizeof ffxxDump); @@ -1148,24 +1148,24 @@ static void setInitialDmgIoamhram(unsigned char *const ioamhram) { void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbMode, const std::uint32_t now, const unsigned div) { static const unsigned char cgbObjpDump[0x40] = { - 0x00, 0x00, 0xF2, 0xAB, - 0x61, 0xC2, 0xD9, 0xBA, - 0x88, 0x6E, 0xDD, 0x63, - 0x28, 0x27, 0xFB, 0x9F, - 0x35, 0x42, 0xD6, 0xD4, - 0x50, 0x48, 0x57, 0x5E, - 0x23, 0x3E, 0x3D, 0xCA, - 0x71, 0x21, 0x37, 0xC0, - 0xC6, 0xB3, 0xFB, 0xF9, - 0x08, 0x00, 0x8D, 0x29, - 0xA3, 0x20, 0xDB, 0x87, - 0x62, 0x05, 0x5D, 0xD4, - 0x0E, 0x08, 0xFE, 0xAF, - 0x20, 0x02, 0xD7, 0xFF, - 0x07, 0x6A, 0x55, 0xEC, + 0x00, 0x00, 0xF2, 0xAB, + 0x61, 0xC2, 0xD9, 0xBA, + 0x88, 0x6E, 0xDD, 0x63, + 0x28, 0x27, 0xFB, 0x9F, + 0x35, 0x42, 0xD6, 0xD4, + 0x50, 0x48, 0x57, 0x5E, + 0x23, 0x3E, 0x3D, 0xCA, + 0x71, 0x21, 0x37, 0xC0, + 0xC6, 0xB3, 0xFB, 0xF9, + 0x08, 0x00, 0x8D, 0x29, + 0xA3, 0x20, 0xDB, 0x87, + 0x62, 0x05, 0x5D, 0xD4, + 0x0E, 0x08, 0xFE, 0xAF, + 0x20, 0x02, 0xD7, 0xFF, + 0x07, 0x6A, 0x55, 0xEC, 0x83, 0x40, 0x0B, 0x77 }; - + state.cpu.cycleCounter = 8; state.cpu.PC = 0; state.cpu.SP = 0; @@ -1183,7 +1183,7 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM state.mem.agbMode = gbaCgbMode; std::memset(state.mem.sram.ptr, 0xFF, state.mem.sram.getSz()); - + setInitialVram(state.mem.vram.ptr, cgb); if (cgb) { @@ -1193,11 +1193,11 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM setInitialDmgWram(state.mem.wram.ptr); setInitialDmgIoamhram(state.mem.ioamhram.ptr); } - + state.mem.ioamhram.ptr[0x104] = 0; state.mem.ioamhram.ptr[0x140] = 0; state.mem.ioamhram.ptr[0x144] = 0x00; - + state.mem.divLastUpdate = 0 - div; state.mem.timaLastUpdate = 0; state.mem.tmatime = DISABLED_TIME; @@ -1217,23 +1217,23 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM state.mem.hdmaTransfer = false; state.mem.gbIsCgb = cgb; - + for (unsigned i = 0x00; i < 0x40; i += 0x02) { state.ppu.bgpData.ptr[i ] = 0xFF; state.ppu.bgpData.ptr[i + 1] = 0x7F; } - + std::memcpy(state.ppu.objpData.ptr, cgbObjpDump, sizeof cgbObjpDump); - + if (!cgb) { state.ppu.bgpData.ptr[0] = state.mem.ioamhram.get()[0x147]; state.ppu.objpData.ptr[0] = state.mem.ioamhram.get()[0x148]; state.ppu.objpData.ptr[1] = state.mem.ioamhram.get()[0x149]; } - + for (unsigned pos = 0; pos < 80; ++pos) state.ppu.oamReaderBuf.ptr[pos] = state.mem.ioamhram.ptr[(pos * 2 & ~3) | (pos & 1)]; - + std::fill_n(state.ppu.oamReaderSzbuf.ptr, 40, false); std::memset(state.ppu.spAttribList, 0, sizeof state.ppu.spAttribList); std::memset(state.ppu.spByte0List, 0, sizeof state.ppu.spByte0List); @@ -1263,9 +1263,9 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM state.ppu.pendingLcdstatIrq = false; state.ppu.isCgb = cgb; - + state.spu.cycleCounter = 0; // spu.cycleCounter >> 12 & 7 represents the frame sequencer position. - + state.spu.ch1.sweep.counter = SoundUnit::COUNTER_DISABLED; state.spu.ch1.sweep.shadow = 0; state.spu.ch1.sweep.nr0 = 0; @@ -1279,7 +1279,7 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM state.spu.ch1.lcounter.lengthCounter = 0; state.spu.ch1.nr4 = 0; state.spu.ch1.master = false; - + state.spu.ch2.duty.nextPosUpdate = SoundUnit::COUNTER_DISABLED; state.spu.ch2.duty.nr3 = 0; state.spu.ch2.duty.pos = 0; @@ -1289,10 +1289,10 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM state.spu.ch2.lcounter.lengthCounter = 0; state.spu.ch2.nr4 = 0; state.spu.ch2.master = false; - + for (unsigned i = 0; i < 0x10; ++i) state.spu.ch3.waveRam.ptr[i] = state.mem.ioamhram.get()[0x130 + i]; - + state.spu.ch3.lcounter.counter = SoundUnit::COUNTER_DISABLED; state.spu.ch3.lcounter.lengthCounter = 0x100; state.spu.ch3.waveCounter = SoundUnit::COUNTER_DISABLED; @@ -1302,7 +1302,7 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM state.spu.ch3.wavePos = 0; state.spu.ch3.sampleBuf = 0; state.spu.ch3.master = false; - + state.spu.ch4.lfsr.counter = state.spu.cycleCounter + 4; state.spu.ch4.lfsr.reg = 0xFF; state.spu.ch4.env.counter = SoundUnit::COUNTER_DISABLED; @@ -1311,7 +1311,7 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM state.spu.ch4.lcounter.lengthCounter = 0; state.spu.ch4.nr4 = 0; state.spu.ch4.master = false; - + state.rtc.baseTime = now; state.rtc.haltTime = state.rtc.baseTime; state.rtc.dataDh = 0; diff --git a/libgambatte/src/insertion_sort.h b/libgambatte/src/insertion_sort.h index f6a3c991a4..cfcd3fbd25 100644 --- a/libgambatte/src/insertion_sort.h +++ b/libgambatte/src/insertion_sort.h @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2007 by Sindre Aamås * + * Copyright (C) 2007 by Sindre Aam�s * * aamas@stud.ntnu.no * * * * This program is free software; you can redistribute it and/or modify * @@ -26,19 +26,19 @@ template void insertionSort(T *const start, T *const end, Less less) { if (start >= end) return; - + T *a = start; - + while (++a < end) { const T e = *a; - + T *b = a; - + while (b != start && less(e, *(b - 1))) { *b = *(b - 1); b = b - 1; } - + *b = e; } } diff --git a/libgambatte/src/interrupter.cpp b/libgambatte/src/interrupter.cpp index 9eac990516..4223c11a8b 100644 --- a/libgambatte/src/interrupter.cpp +++ b/libgambatte/src/interrupter.cpp @@ -35,7 +35,7 @@ unsigned long Interrupter::interrupt(const unsigned address, unsigned long cycle memory.write(SP, PC & 0xFF, cycleCounter); PC = address; cycleCounter += 8; - + return cycleCounter; } diff --git a/libgambatte/src/interruptrequester.cpp b/libgambatte/src/interruptrequester.cpp index c656ad2368..8e57790d7a 100644 --- a/libgambatte/src/interruptrequester.cpp +++ b/libgambatte/src/interruptrequester.cpp @@ -28,13 +28,13 @@ void InterruptRequester::loadState(const SaveState &state) { ifreg_ = state.mem.ioamhram.get()[0x10F]; iereg_ = state.mem.ioamhram.get()[0x1FF] & 0x1F; intFlags.set(state.mem.IME, state.mem.halted); - + eventTimes.setValue(intFlags.imeOrHalted() && pendingIrqs() ? minIntTime : static_cast(DISABLED_TIME)); } void InterruptRequester::resetCc(const unsigned long oldCc, const unsigned long newCc) { minIntTime = minIntTime < oldCc ? 0 : minIntTime - (oldCc - newCc); - + if (eventTimes.value(INTERRUPTS) != DISABLED_TIME) eventTimes.setValue(minIntTime); } @@ -42,35 +42,35 @@ void InterruptRequester::resetCc(const unsigned long oldCc, const unsigned long void InterruptRequester::ei(const unsigned long cc) { intFlags.setIme(); minIntTime = cc + 1; - + if (pendingIrqs()) eventTimes.setValue(minIntTime); } void InterruptRequester::di() { intFlags.unsetIme(); - + if (!intFlags.imeOrHalted()) eventTimes.setValue(DISABLED_TIME); } void InterruptRequester::halt() { intFlags.setHalted(); - + if (pendingIrqs()) eventTimes.setValue(minIntTime); } void InterruptRequester::unhalt() { intFlags.unsetHalted(); - + if (!intFlags.imeOrHalted()) eventTimes.setValue(DISABLED_TIME); } void InterruptRequester::flagIrq(const unsigned bit) { ifreg_ |= bit; - + if (intFlags.imeOrHalted() && pendingIrqs()) eventTimes.setValue(minIntTime); } @@ -82,14 +82,14 @@ void InterruptRequester::ackIrq(const unsigned bit) { void InterruptRequester::setIereg(const unsigned iereg) { iereg_ = iereg & 0x1F; - + if (intFlags.imeOrHalted()) eventTimes.setValue(pendingIrqs() ? minIntTime : static_cast(DISABLED_TIME)); } void InterruptRequester::setIfreg(const unsigned ifreg) { ifreg_ = ifreg; - + if (intFlags.imeOrHalted()) eventTimes.setValue(pendingIrqs() ? minIntTime : static_cast(DISABLED_TIME)); } diff --git a/libgambatte/src/interruptrequester.h b/libgambatte/src/interruptrequester.h index 8d22e35afb..5e5cb7d10c 100644 --- a/libgambatte/src/interruptrequester.h +++ b/libgambatte/src/interruptrequester.h @@ -32,41 +32,41 @@ class InterruptRequester { unsigned long minIntTime; unsigned ifreg_; unsigned iereg_; - + class IntFlags { friend class InterruptRequester; unsigned char flags_; enum { IME_MASK = 1, HALTED_MASK = 2 }; - + public: IntFlags() : flags_(0) {} - + bool ime() const { return flags_ & IME_MASK; } bool halted() const { return flags_ & HALTED_MASK; } bool imeOrHalted() const { return flags_; } - + void setIme() { flags_ |= IME_MASK; } void unsetIme() { flags_ &= ~IME_MASK; } - + void setHalted() { flags_ |= HALTED_MASK; } void unsetHalted() { flags_ &= ~HALTED_MASK; } - + void set(const bool ime, const bool halted) { flags_ = halted * HALTED_MASK + ime * IME_MASK; } } intFlags; - + public: InterruptRequester(); - + void loadState(const SaveState &); - + void resetCc(unsigned long oldCc, unsigned long newCc); - + unsigned ifreg() const { return ifreg_; } unsigned iereg() const { return iereg_; } unsigned pendingIrqs() const { return ifreg_ & iereg_; } bool ime() const { return intFlags.ime(); } bool halted() const { return intFlags.halted(); } - + void ei(unsigned long cc); void di(); void halt(); @@ -75,7 +75,7 @@ public: void ackIrq(unsigned bit); void setIereg(unsigned iereg); void setIfreg(unsigned ifreg); - + MemEventId minEventId() const { return static_cast(eventTimes.min()); } unsigned long minEventTime() const { return eventTimes.minValue(); } template void setEventTime(unsigned long value) { eventTimes.setValue(value); } diff --git a/libgambatte/src/mem/cartridge.cpp b/libgambatte/src/mem/cartridge.cpp index 63e48171f3..5d3dea9e4d 100644 --- a/libgambatte/src/mem/cartridge.cpp +++ b/libgambatte/src/mem/cartridge.cpp @@ -76,11 +76,11 @@ public: }; static inline unsigned rambanks(const MemPtrs &memptrs) { - return static_cast(memptrs.rambankdataend() - memptrs.rambankdata()) / 0x2000; + return std::size_t(memptrs.rambankdataend() - memptrs.rambankdata()) / 0x2000; } static inline unsigned rombanks(const MemPtrs &memptrs) { - return static_cast(memptrs.romdataend() - memptrs.romdata() ) / 0x4000; + return std::size_t(memptrs.romdataend() - memptrs.romdata() ) / 0x4000; } class Mbc1 : public DefaultMbc { @@ -219,7 +219,7 @@ public: memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, 0); setRombank(); } - + virtual bool isAddressWithinAreaRombankCanBeMappedTo(unsigned addr, unsigned bank) const { return (addr < 0x4000) == ((bank & 0xF) == 0); } @@ -287,10 +287,10 @@ class Mbc3 : public DefaultMbc { static unsigned adjustedRombank(unsigned bank) { return bank & 0x7F ? bank : bank | 1; } void setRambank() const { unsigned flags = enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0; - + if (rtc) { rtc->set(enableRam, rambank); - + if (rtc->getActive()) flags |= MemPtrs::RTC_EN; } @@ -546,7 +546,7 @@ LoadRes Cartridge::loadROM(const char *romfiledata, unsigned romfilelength, bool //if (rom->fail()) // return -1; - + unsigned rambanks = 1; unsigned rombanks = 2; bool cgb = false; @@ -609,8 +609,8 @@ LoadRes Cartridge::loadROM(const char *romfiledata, unsigned romfilelength, bool default: return -1; }*/ - rambanks = numRambanksFromH14x(header[0x147], header[0x149]); - + rambanks = numRambanksFromH14x(header[0x147], header[0x149]); + cgb = !forceDmg; } std::size_t const filesize = romfilelength; //rom->size(); @@ -625,10 +625,10 @@ LoadRes Cartridge::loadROM(const char *romfiledata, unsigned romfilelength, bool std::memcpy(memptrs.romdata(), romfiledata, (filesize / 0x4000) * 0x4000ul); std::memset(memptrs.romdata() + (filesize / 0x4000) * 0x4000ul, 0xFF, (rombanks - filesize / 0x4000) * 0x4000ul); enforce8bit(memptrs.romdata(), rombanks * 0x4000ul); - + //if (rom->fail()) // return -1; - + switch (type) { case PLAIN: mbc.reset(new Mbc0(memptrs)); break; case MBC1: diff --git a/libgambatte/src/mem/cartridge.h b/libgambatte/src/mem/cartridge.h index 934d44df7e..a71fc5a0f8 100644 --- a/libgambatte/src/mem/cartridge.h +++ b/libgambatte/src/mem/cartridge.h @@ -63,13 +63,13 @@ class Cartridge { MemPtrs memptrs; Rtc rtc; std::auto_ptr mbc; - + public: void setStatePtrs(SaveState &); void loadState(const SaveState &); - + bool loaded() const { return mbc.get(); } - + const unsigned char * rmem(unsigned area) const { return memptrs.rmem(area); } unsigned char * wmem(unsigned area) const { return memptrs.wmem(area); } unsigned char * vramdata() const { return memptrs.vramdata(); } @@ -85,14 +85,14 @@ public: void setVrambank(unsigned bank) { memptrs.setVrambank(bank); } void setWrambank(unsigned bank) { memptrs.setWrambank(bank); } void setOamDmaSrc(OamDmaSrc oamDmaSrc) { memptrs.setOamDmaSrc(oamDmaSrc); } - + void mbcWrite(unsigned addr, unsigned data) { mbc->romWrite(addr, data); } bool isCgb() const { return gambatte::isCgb(memptrs); } - + void rtcWrite(unsigned data) { rtc.write(data); } unsigned char rtcRead() const { return *rtc.getActive(); } - + void loadSavedata(const char *data); int saveSavedataLength(); void saveSavedata(char *dest); diff --git a/libgambatte/src/mem/memptrs.cpp b/libgambatte/src/mem/memptrs.cpp index bd166e86cf..7b6c2c2ee6 100644 --- a/libgambatte/src/mem/memptrs.cpp +++ b/libgambatte/src/mem/memptrs.cpp @@ -45,7 +45,7 @@ void MemPtrs::reset(const unsigned rombanks, const unsigned rambanks, const unsi wramdataend_ = wramdata_[0] + wrambanks * 0x1000ul; std::memset(rdisabledRamw(), 0xFF, 0x2000); - + oamDmaSrc_ = OAM_DMA_SRC_OFF; rmem_[0x3] = rmem_[0x2] = rmem_[0x1] = rmem_[0x0] = romdata_[0]; rmem_[0xC] = wmem_[0xC] = wramdata_[0] - 0xC000; @@ -87,7 +87,7 @@ void MemPtrs::setRambank(const unsigned flags, const unsigned rambank) { } void MemPtrs::setWrambank(const unsigned bank) { - wramdata_[1] = wramdata_[0] + ((bank & 0x07) ? (bank & 0x07) : 1) * 0x1000; + wramdata_[1] = wramdata_[0] + (bank & 0x07 ? bank & 0x07 : 1) * 0x1000; rmem_[0xD] = wmem_[0xD] = wramdata_[1] - 0xD000; disconnectOamDmaAreas(); } @@ -100,7 +100,7 @@ void MemPtrs::setOamDmaSrc(const OamDmaSrc oamDmaSrc) { rmem_[0xC] = wmem_[0xC] = wramdata_[0] - 0xC000; rmem_[0xD] = wmem_[0xD] = wramdata_[1] - 0xD000; rmem_[0xE] = wmem_[0xE] = wramdata_[0] - 0xE000; - + oamDmaSrc_ = oamDmaSrc; disconnectOamDmaAreas(); } diff --git a/libgambatte/src/mem/memptrs.h b/libgambatte/src/mem/memptrs.h index 68fd2b0006..3d2c6ca3ca 100644 --- a/libgambatte/src/mem/memptrs.h +++ b/libgambatte/src/mem/memptrs.h @@ -29,7 +29,7 @@ enum OamDmaSrc { OAM_DMA_SRC_ROM, OAM_DMA_SRC_SRAM, OAM_DMA_SRC_VRAM, class MemPtrs { const unsigned char *rmem_[0x10]; unsigned char *wmem_[0x10]; - + unsigned char *romdata_[2]; unsigned char *wramdata_[2]; unsigned char *vrambankptr_; @@ -38,7 +38,7 @@ class MemPtrs { unsigned char *memchunk_; unsigned char *rambankdata_; unsigned char *wramdataend_; - + OamDmaSrc oamDmaSrc_; unsigned curRomBank_; @@ -54,11 +54,11 @@ class MemPtrs { unsigned char * wdisabledRam() const { return wramdataend_ + 0x2000; } public: enum RamFlag { READ_EN = 1, WRITE_EN = 2, RTC_EN = 4 }; - + MemPtrs(); ~MemPtrs(); void reset(unsigned rombanks, unsigned rambanks, unsigned wrambanks); - + const unsigned char * rmem(unsigned area) const { return rmem_[area]; } unsigned char * wmem(unsigned area) const { return wmem_[area]; } unsigned char * vramdata() const { return rambankdata_ - 0x4000; } diff --git a/libgambatte/src/mem/rtc.cpp b/libgambatte/src/mem/rtc.cpp index ae97d0876c..57b2964548 100644 --- a/libgambatte/src/mem/rtc.cpp +++ b/libgambatte/src/mem/rtc.cpp @@ -41,24 +41,24 @@ Rtc::Rtc() void Rtc::doLatch() { std::uint32_t tmp = ((dataDh & 0x40) ? haltTime : timeCB()) - baseTime; - + while (tmp > 0x1FF * 86400) { baseTime += 0x1FF * 86400; tmp -= 0x1FF * 86400; dataDh |= 0x80; } - + dataDl = (tmp / 86400) & 0xFF; dataDh &= 0xFE; dataDh |= ((tmp / 86400) & 0x100) >> 8; tmp %= 86400; - + dataH = tmp / 3600; tmp %= 3600; - + dataM = tmp / 60; tmp %= 60; - + dataS = tmp; } @@ -99,7 +99,7 @@ void Rtc::loadState(const SaveState &state) { dataM = state.rtc.dataM; dataS = state.rtc.dataS; lastLatchData = state.rtc.lastLatchData; - + doSwapActive(); } @@ -108,7 +108,7 @@ void Rtc::setDh(const unsigned new_dh) { const std::uint32_t old_highdays = ((unixtime - baseTime) / 86400) & 0x100; baseTime += old_highdays * 86400; baseTime -= ((new_dh & 0x1) << 8) * 86400; - + if ((dataDh ^ new_dh) & 0x40) { if (new_dh & 0x40) haltTime = timeCB(); diff --git a/libgambatte/src/mem/rtc.h b/libgambatte/src/mem/rtc.h index cdbd7e66d0..67eedb863b 100644 --- a/libgambatte/src/mem/rtc.h +++ b/libgambatte/src/mem/rtc.h @@ -41,7 +41,7 @@ private: bool enabled; bool lastLatchData; std::uint32_t (*timeCB)(); - + void doLatch(); void doSwapActive(); void setDh(unsigned new_dh); @@ -49,37 +49,37 @@ private: void setH(unsigned new_hours); void setM(unsigned new_minutes); void setS(unsigned new_seconds); - + public: Rtc(); - + const unsigned char* getActive() const { return activeData; } std::uint32_t getBaseTime() const { return baseTime; } - + void setBaseTime(const std::uint32_t baseTime) { this->baseTime = baseTime; // doLatch(); } - + void latch(const unsigned data) { if (!lastLatchData && data == 1) doLatch(); - + lastLatchData = data; } - + void loadState(const SaveState &state); - + void set(const bool enabled, unsigned bank) { bank &= 0xF; bank -= 8; - + this->enabled = enabled; this->index = bank; - + doSwapActive(); } - + void write(const unsigned data) { // if (activeSet) (this->*activeSet)(data); diff --git a/libgambatte/src/memory.cpp b/libgambatte/src/memory.cpp index 657a647fcf..a9fa6a031c 100644 --- a/libgambatte/src/memory.cpp +++ b/libgambatte/src/memory.cpp @@ -98,7 +98,7 @@ void Memory::loadState(const SaveState &state) { intreq.setEventTime((ioamhram[0x140] & 0x80) ? display.nextMode1IrqTime() : state.cpu.cycleCounter); blanklcd = false; - + if (!isCgb()) std::memset(cart.vramdata() + 0x2000, 0, 0x2000); } @@ -106,7 +106,7 @@ void Memory::loadState(const SaveState &state) { void Memory::setEndtime(const unsigned long cycleCounter, const unsigned long inc) { if (intreq.eventTime(BLIT) <= cycleCounter) intreq.setEventTime(intreq.eventTime(BLIT) + (70224 << isDoubleSpeed())); - + intreq.setEventTime(cycleCounter + (inc << isDoubleSpeed())); } @@ -165,7 +165,7 @@ unsigned long Memory::event(unsigned long cycleCounter) { while (cycleCounter >= intreq.minEventTime() && intreq.eventTime(END) != DISABLED_TIME) cycleCounter = event(cycleCounter); - + intreq.setEventTime(DISABLED_TIME); break; @@ -173,17 +173,17 @@ unsigned long Memory::event(unsigned long cycleCounter) { { const bool lcden = ioamhram[0x140] >> 7 & 1; unsigned long blitTime = intreq.eventTime(BLIT); - + if (lcden | blanklcd) { display.updateScreen(blanklcd, cycleCounter); intreq.setEventTime(DISABLED_TIME); intreq.setEventTime(DISABLED_TIME); - + while (cycleCounter >= intreq.minEventTime()) cycleCounter = event(cycleCounter); } else blitTime += 70224 << isDoubleSpeed(); - + blanklcd = lcden ^ 1; intreq.setEventTime(blitTime); } @@ -202,7 +202,7 @@ unsigned long Memory::event(unsigned long cycleCounter) { unsigned dmaDest = dmaDestination; unsigned dmaLength = ((ioamhram[0x155] & 0x7F) + 0x1) * 0x10; unsigned length = hdmaReqFlagged(intreq) ? 0x10 : dmaLength; - + ackDmaReq(&intreq); if ((static_cast(dmaDest) + length) & 0x10000) { @@ -255,7 +255,7 @@ unsigned long Memory::event(unsigned long cycleCounter) { if ((ioamhram[0x155] & 0x80) && display.hdmaIsEnabled()) { if (lastOamDmaUpdate != DISABLED_TIME) updateOamDma(cycleCounter); - + display.disableHdma(cycleCounter); } } @@ -275,11 +275,11 @@ unsigned long Memory::event(unsigned long cycleCounter) { if (halted()) { if (gbIsCgb_ || (!gbIsCgb_ && cycleCounter <= halttime + 4)) cycleCounter += 4; - + intreq.unhalt(); intreq.setEventTime(DISABLED_TIME); } - + if (ime()) { unsigned address; @@ -297,7 +297,7 @@ unsigned long Memory::event(unsigned long cycleCounter) { cycleCounter += 4; display.update(cycleCounter); const unsigned n = pendingIrqs & -pendingIrqs; - + if (n == 0) { address = 0; } @@ -306,11 +306,11 @@ unsigned long Memory::event(unsigned long cycleCounter) { address = lut[n-1]; } else address = 0x50 + n; - + intreq.ackIrq(n); PC = address; } - + break; } @@ -319,15 +319,15 @@ unsigned long Memory::event(unsigned long cycleCounter) { unsigned long Memory::stop(unsigned long cycleCounter) { cycleCounter += 4; - + if (ioamhram[0x14D] & isCgb()) { sound.generate_samples(cycleCounter, isDoubleSpeed()); - + display.speedChange(cycleCounter); ioamhram[0x14D] ^= 0x81; intreq.setEventTime((ioamhram[0x140] & 0x80) ? display.nextMode1IrqTime() : cycleCounter + (70224 << isDoubleSpeed())); - + if (intreq.eventTime(END) > cycleCounter) { intreq.setEventTime(cycleCounter + (isDoubleSpeed() ? (intreq.eventTime(END) - cycleCounter) << 1 : (intreq.eventTime(END) - cycleCounter) >> 1)); @@ -381,7 +381,7 @@ unsigned long Memory::resetCounters(unsigned long cycleCounter) { decEventCycles(UNHALT, dec); cycleCounter -= dec; - + intreq.resetCc(oldCC, cycleCounter); tima.resetCc(oldCC, cycleCounter, TimaInterruptRequester(intreq)); display.resetCc(oldCC, cycleCounter); @@ -453,7 +453,7 @@ const unsigned char * Memory::oamDmaSrcPtr() const { case OAM_DMA_SRC_INVALID: case OAM_DMA_SRC_OFF: break; } - + return ioamhram[0x146] == 0xFF && !isCgb() ? oamDmaSrcZero() : cart.rdisabledRam(); } @@ -536,7 +536,7 @@ unsigned Memory::nontrivial_ff_read(const unsigned P, const unsigned long cycleC static bool isInOamDmaConflictArea(const OamDmaSrc oamDmaSrc, const unsigned addr, const bool cgb) { struct Area { unsigned short areaUpper, exceptAreaLower, exceptAreaWidth, pad; }; - + static const Area cgbAreas[] = { { 0xC000, 0x8000, 0x2000, 0 }, { 0xC000, 0x8000, 0x2000, 0 }, @@ -545,7 +545,7 @@ static bool isInOamDmaConflictArea(const OamDmaSrc oamDmaSrc, const unsigned add { 0xC000, 0x8000, 0x2000, 0 }, { 0x0000, 0x0000, 0x0000, 0 } }; - + static const Area dmgAreas[] = { { 0xFE00, 0x8000, 0x2000, 0 }, { 0xFE00, 0x8000, 0x2000, 0 }, @@ -554,7 +554,7 @@ static bool isInOamDmaConflictArea(const OamDmaSrc oamDmaSrc, const unsigned add { 0xFE00, 0x8000, 0x2000, 0 }, { 0x0000, 0x0000, 0x0000, 0 } }; - + const Area *const a = cgb ? cgbAreas : dmgAreas; return addr < a[oamDmaSrc].areaUpper && addr - a[oamDmaSrc].exceptAreaLower >= a[oamDmaSrc].exceptAreaWidth; @@ -564,7 +564,7 @@ unsigned Memory::nontrivial_read(const unsigned P, const unsigned long cycleCoun if (P < 0xFF80) { if (lastOamDmaUpdate != DISABLED_TIME) { updateOamDma(cycleCounter); - + if (isInOamDmaConflictArea(cart.oamDmaSrc(), P, isCgb()) && oamDmaPos < 0xA0) return ioamhram[oamDmaPos]; } @@ -830,7 +830,7 @@ void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned if ((ioamhram[0x140] ^ data) & 0x80) { const unsigned lyc = display.getStat(ioamhram[0x145], cycleCounter) & 4; const bool hdmaEnabled = display.hdmaIsEnabled(); - + display.lcdcChange(data, cycleCounter); ioamhram[0x144] = 0; ioamhram[0x141] &= 0xF8; @@ -1022,7 +1022,7 @@ void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned void Memory::nontrivial_write(const unsigned P, const unsigned data, const unsigned long cycleCounter) { if (lastOamDmaUpdate != DISABLED_TIME) { updateOamDma(cycleCounter); - + if (isInOamDmaConflictArea(cart.oamDmaSrc(), P, isCgb()) && oamDmaPos < 0xA0) { ioamhram[oamDmaPos] = data; return; diff --git a/libgambatte/src/memory.h b/libgambatte/src/memory.h index fcd7981730..5529fa71e9 100644 --- a/libgambatte/src/memory.h +++ b/libgambatte/src/memory.h @@ -57,13 +57,13 @@ class Memory { unsigned (*getInput)(); unsigned long divLastUpdate; unsigned long lastOamDmaUpdate; - + InterruptRequester intreq; Tima tima; LCD display; PSG sound; Interrupter interrupter; - + unsigned short dmaSource; unsigned short dmaDestination; unsigned char oamDmaPos; @@ -80,24 +80,24 @@ class Memory { void startOamDma(unsigned long cycleCounter); void endOamDma(unsigned long cycleCounter); const unsigned char * oamDmaSrcPtr() const; - + unsigned nontrivial_ff_read(unsigned P, unsigned long cycleCounter); unsigned nontrivial_read(unsigned P, unsigned long cycleCounter); void nontrivial_ff_write(unsigned P, unsigned data, unsigned long cycleCounter); void nontrivial_write(unsigned P, unsigned data, unsigned long cycleCounter); - + unsigned nontrivial_peek(unsigned P); unsigned nontrivial_ff_peek(unsigned P); void updateSerial(unsigned long cc); void updateTimaIrq(unsigned long cc); void updateIrqs(unsigned long cc); - + bool isDoubleSpeed() const { return display.isDoubleSpeed(); } public: explicit Memory(const Interrupter &interrupter, unsigned short &sp, unsigned short &pc); - + bool loaded() const { return cart.loaded(); } unsigned curRomBank() const { return cart.curRomBank(); } const char * romTitle() const { return cart.romTitle(); } @@ -124,9 +124,9 @@ public: unsigned long nextEventTime() const { return intreq.minEventTime(); } void setLayers(unsigned mask) { display.setLayers(mask); } - + bool isActive() const { return intreq.eventTime(END) != DISABLED_TIME; } - + long cyclesSinceBlit(const unsigned long cc) const { return cc < intreq.eventTime(BLIT) ? -1 : static_cast((cc - intreq.eventTime(BLIT)) >> isDoubleSpeed()); } @@ -155,7 +155,7 @@ public: CDMapResult ret = { eCDLog_AddrType_ROM, P }; return ret; } - else if(P<0x8000) + else if(P<0x8000) { unsigned bank = cart.rmem(P>>12) - cart.rmem(0); unsigned addr = P+bank; @@ -187,7 +187,7 @@ public: return ret; } else if(P<0xFF80) {} - else + else { ////this is just for debugging, really, it's pretty useless //CDMapResult ret = { eCDLog_AddrType_HRAM, (P-0xFF80) }; @@ -262,7 +262,7 @@ public: } else nontrivial_write(P, data, cycleCounter); } - + void write(const unsigned P, const unsigned data, const unsigned long cycleCounter) { if (cart.wmem(P >> 12)) { cart.wmem(P >> 12)[P] = data; @@ -277,7 +277,7 @@ public: cdCallback(map.addr,map.type,eCDLog_Flags_Data); } } - + void ff_write(const unsigned P, const unsigned data, const unsigned long cycleCounter) { if (P - 0xFF80u < 0x7Fu) { ioamhram[P - 0xFE00] = data; @@ -329,14 +329,14 @@ public: void setBasetime(unsigned long cc) { basetime = cc; } void setEndtime(unsigned long cc, unsigned long inc); - + void setSoundBuffer(uint_least32_t *const buf) { sound.setBuffer(buf); } unsigned fillSoundBuffer(unsigned long cc); - + void setVideoBuffer(uint_least32_t *const videoBuf, const int pitch) { display.setVideoBuffer(videoBuf, pitch); } - + void setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned long rgb32); void setCgbPalette(unsigned *lut); diff --git a/libgambatte/src/minkeeper.h b/libgambatte/src/minkeeper.h index ad2d6f79d2..5816a0fad7 100644 --- a/libgambatte/src/minkeeper.h +++ b/libgambatte/src/minkeeper.h @@ -42,26 +42,26 @@ class MinKeeper { enum { LEVELS = MinKeeperUtil::CeiledLog2::R }; template struct Num { enum { R = MinKeeperUtil::RoundedDiv2n::R }; }; template struct Sum { enum { R = MinKeeperUtil::Sum::R }; }; - + template struct UpdateValue { enum { P = Sum::R + id }; enum { C0 = Sum::R + id * 2 }; - + static void updateValue(MinKeeper &m) { // GCC 4.3 generates better code with the ternary operator on i386. m.a[P] = (id * 2 + 1 == Num::R || m.values[m.a[C0]] < m.values[m.a[C0 + 1]]) ? m.a[C0] : m.a[C0 + 1]; UpdateValue::updateValue(m); } }; - + template struct UpdateValue { static void updateValue(MinKeeper &m) { m.minValue_ = m.values[m.a[0]]; } }; - + class UpdateValueLut { template struct FillLut { static void fillLut(UpdateValueLut & l) { @@ -69,42 +69,42 @@ class MinKeeper { FillLut::fillLut(l); } }; - + template struct FillLut<-1,dummy> { static void fillLut(UpdateValueLut &) {} }; - + void (*lut_[Num::R])(MinKeeper&); - + public: UpdateValueLut() { FillLut::R-1,0>::fillLut(*this); } void call(int id, MinKeeper &mk) const { lut_[id](mk); } }; - + static UpdateValueLut updateValueLut; unsigned long values[ids]; unsigned long minValue_; int a[Sum::R]; - + template static void updateValue(MinKeeper &m); - + public: explicit MinKeeper(unsigned long initValue = 0xFFFFFFFF); - + int min() const { return a[0]; } unsigned long minValue() const { return minValue_; } - + template void setValue(const unsigned long cnt) { values[id] = cnt; updateValue(*this); } - + void setValue(const int id, const unsigned long cnt) { values[id] = cnt; updateValueLut.call(id >> 1, *this); } - + unsigned long value(const int id) const { return values[id]; } // not sure if i understood everything in minkeeper correctly, so something might be missing here? @@ -122,28 +122,28 @@ template typename MinKeeper::UpdateValueLut MinKeeper::update template MinKeeper::MinKeeper(const unsigned long initValue) { std::fill(values, values + ids, initValue); - + for (int i = 0; i < Num::R; ++i) { a[Sum::R + i] = (i * 2 + 1 == ids || values[i * 2] < values[i * 2 + 1]) ? i * 2 : i * 2 + 1; } - + int n = Num::R; int off = Sum::R; - + while (off) { const int pn = (n + 1) >> 1; const int poff = off - pn; - + for (int i = 0; i < pn; ++i) { a[poff + i] = (i * 2 + 1 == n || values[a[off + i * 2]] < values[a[off + i * 2 + 1]]) ? a[off + i * 2] : a[off + i * 2 + 1]; } - + off = poff; n = pn; } - + minValue_ = values[a[0]]; } diff --git a/libgambatte/src/newstate.h b/libgambatte/src/newstate.h index d741879cff..576da30bfb 100644 --- a/libgambatte/src/newstate.h +++ b/libgambatte/src/newstate.h @@ -65,7 +65,7 @@ public: virtual void ExitSection(const char *name); }; -// defines and explicitly instantiates +// defines and explicitly instantiates #define SYNCFUNC(x)\ template void x::SyncState(NewState *ns);\ template void x::SyncState(NewState *ns);\ diff --git a/libgambatte/src/savestate.h b/libgambatte/src/savestate.h index 014efbb4c8..598426edd8 100644 --- a/libgambatte/src/savestate.h +++ b/libgambatte/src/savestate.h @@ -30,13 +30,13 @@ struct SaveState { class Ptr { T *ptr; unsigned long sz; - + public: Ptr() : ptr(0), sz(0) {} const T* get() const { return ptr; } unsigned long getSz() const { return sz; } void set(T *ptr, const unsigned long sz) { this->ptr = ptr; this->sz = sz; } - + friend class SaverList; friend void setInitState(SaveState &, bool, bool, std::uint32_t, unsigned); }; @@ -55,7 +55,7 @@ struct SaveState { unsigned char L; bool skip; } cpu; - + struct Mem { Ptr vram; Ptr sram; @@ -83,14 +83,14 @@ struct SaveState { bool agbMode; bool gbIsCgb; } mem; - + struct PPU { Ptr bgpData; Ptr objpData; //SpriteMapper::OamReader Ptr oamReaderBuf; Ptr oamReaderSzbuf; - + unsigned long videoCycles; unsigned long enableDisplayM0Time; unsigned short lastM0Time; @@ -119,24 +119,24 @@ struct SaveState { bool pendingLcdstatIrq; bool isCgb; } ppu; - + struct SPU { struct Duty { unsigned long nextPosUpdate; unsigned char nr3; unsigned char pos; }; - + struct Env { unsigned long counter; unsigned char volume; }; - + struct LCounter { unsigned long counter; unsigned short lengthCounter; }; - + struct { struct { unsigned long counter; @@ -150,7 +150,7 @@ struct SaveState { unsigned char nr4; bool master; } ch1; - + struct { Duty duty; Env env; @@ -158,7 +158,7 @@ struct SaveState { unsigned char nr4; bool master; } ch2; - + struct { Ptr waveRam; LCounter lcounter; @@ -170,7 +170,7 @@ struct SaveState { unsigned char sampleBuf; bool master; } ch3; - + struct { struct { unsigned long counter; @@ -181,10 +181,10 @@ struct SaveState { unsigned char nr4; bool master; } ch4; - + unsigned long cycleCounter; } spu; - + struct RTC { unsigned long baseTime; unsigned long haltTime; diff --git a/libgambatte/src/sound.cpp b/libgambatte/src/sound.cpp index 508ebaf827..68449b158e 100644 --- a/libgambatte/src/sound.cpp +++ b/libgambatte/src/sound.cpp @@ -75,7 +75,7 @@ void PSG::loadState(const SaveState &state) { ch2.loadState(state); ch3.loadState(state); ch4.loadState(state); - + lastUpdate = state.cpu.cycleCounter; set_so_volume(state.mem.ioamhram.get()[0x124]); map_so(state.mem.ioamhram.get()[0x125]); @@ -84,7 +84,7 @@ void PSG::loadState(const SaveState &state) { void PSG::accumulate_channels(const unsigned long cycles) { uint_least32_t *const buf = buffer + bufferPos; - + std::memset(buf, 0, cycles * sizeof *buf); ch1.update(buf, soVol, cycles); ch2.update(buf, soVol, cycles); @@ -98,7 +98,7 @@ void PSG::generate_samples(const unsigned long cycleCounter, const unsigned doub if (cycles) accumulate_channels(cycles); - + bufferPos += cycles; } @@ -111,10 +111,10 @@ unsigned PSG::fillBuffer() { uint_least32_t sum = rsum; uint_least32_t *b = buffer; unsigned n = bufferPos; - + if (unsigned n2 = n >> 3) { n -= n2 << 3; - + do { sum += b[0]; b[0] = sum ^ 0x8000; @@ -132,18 +132,18 @@ unsigned PSG::fillBuffer() { b[6] = sum ^ 0x8000; sum += b[7]; b[7] = sum ^ 0x8000; - + b += 8; } while (--n2); } - + while (n--) { sum += *b; *b++ = sum ^ 0x8000; // xor away the initial rsum value of 0x8000 (which prevents borrows from the high word) from the low word } - + rsum = sum; - + return bufferPos; } @@ -161,7 +161,7 @@ void PSG::set_so_volume(const unsigned nr50) { void PSG::map_so(const unsigned nr51) { const unsigned long tmp = nr51 * so1Mul + (nr51 >> 4) * so2Mul; - + ch1.setSo((tmp & 0x00010001) * 0xFFFF); ch2.setSo((tmp >> 1 & 0x00010001) * 0xFFFF); ch3.setSo((tmp >> 2 & 0x00010001) * 0xFFFF); diff --git a/libgambatte/src/sound.h b/libgambatte/src/sound.h index 7d430d613d..367a10f1a8 100644 --- a/libgambatte/src/sound.h +++ b/libgambatte/src/sound.h @@ -32,16 +32,16 @@ class PSG { Channel2 ch2; Channel3 ch3; Channel4 ch4; - + uint_least32_t *buffer; - + unsigned long lastUpdate; unsigned long soVol; - + uint_least32_t rsum; - + unsigned bufferPos; - + bool enabled; void accumulate_channels(unsigned long cycles); @@ -57,7 +57,7 @@ public: void resetCounter(unsigned long newCc, unsigned long oldCc, unsigned doubleSpeed); unsigned fillBuffer(); void setBuffer(uint_least32_t *const buf) { buffer = buf; bufferPos = 0; } - + bool isEnabled() const { return enabled; } void setEnabled(bool value) { enabled = value; } diff --git a/libgambatte/src/sound/channel1.cpp b/libgambatte/src/sound/channel1.cpp index eae98c966c..4a9f0a7578 100644 --- a/libgambatte/src/sound/channel1.cpp +++ b/libgambatte/src/sound/channel1.cpp @@ -33,31 +33,31 @@ Channel1::SweepUnit::SweepUnit(MasterDisabler &disabler, DutyUnit &dutyUnit) : unsigned Channel1::SweepUnit::calcFreq() { unsigned freq = shadow >> (nr0 & 0x07); - + if (nr0 & 0x08) { freq = shadow - freq; negging = true; } else freq = shadow + freq; - + if (freq & 2048) disableMaster(); - + return freq; } void Channel1::SweepUnit::event() { const unsigned long period = nr0 >> 4 & 0x07; - + if (period) { const unsigned freq = calcFreq(); - + if (!(freq & 2048) && (nr0 & 0x07)) { shadow = freq; dutyUnit.setFreq(freq, counter); calcFreq(); } - + counter += period << 14; } else counter += 8ul << 14; @@ -66,22 +66,22 @@ void Channel1::SweepUnit::event() { void Channel1::SweepUnit::nr0Change(const unsigned newNr0) { if (negging && !(newNr0 & 0x08)) disableMaster(); - + nr0 = newNr0; } void Channel1::SweepUnit::nr4Init(const unsigned long cc) { negging = false; shadow = dutyUnit.getFreq(); - + const unsigned period = nr0 >> 4 & 0x07; const unsigned shift = nr0 & 0x07; - + if (period | shift) counter = ((cc >> 14) + (period ? period : 8)) << 14; else counter = COUNTER_DISABLED; - + if (shift) calcFreq(); } @@ -139,7 +139,7 @@ void Channel1::setNr0(const unsigned data) { void Channel1::setNr1(const unsigned data) { lengthCounter.nr1Change(data, nr4, cycleCounter); dutyUnit.nr1Change(data, cycleCounter); - + setEvent(); } @@ -148,7 +148,7 @@ void Channel1::setNr2(const unsigned data) { disableMaster(); else staticOutputTest(cycleCounter); - + setEvent(); } @@ -159,18 +159,18 @@ void Channel1::setNr3(const unsigned data) { void Channel1::setNr4(const unsigned data) { lengthCounter.nr4Change(nr4, data, cycleCounter); - + nr4 = data; - + dutyUnit.nr4Change(data, cycleCounter); - + if (data & 0x80) { //init-bit nr4 &= 0x7F; master = !envelopeUnit.nr4Init(cycleCounter); sweepUnit.nr4Init(cycleCounter); staticOutputTest(cycleCounter); } - + setEvent(); } @@ -187,7 +187,7 @@ void Channel1::reset() { dutyUnit.reset(); envelopeUnit.reset(); sweepUnit.reset(); - + setEvent(); } @@ -200,7 +200,7 @@ void Channel1::loadState(const SaveState &state) { dutyUnit.loadState(state.spu.ch1.duty, state.mem.ioamhram.get()[0x111], state.spu.ch1.nr4, state.spu.cycleCounter); envelopeUnit.loadState(state.spu.ch1.env, state.mem.ioamhram.get()[0x112], state.spu.cycleCounter); lengthCounter.loadState(state.spu.ch1.lcounter, state.spu.cycleCounter); - + cycleCounter = state.spu.cycleCounter; nr4 = state.spu.ch1.nr4; master = state.spu.ch1.master; @@ -210,42 +210,42 @@ void Channel1::update(uint_least32_t *buf, const unsigned long soBaseVol, unsign const unsigned long outBase = envelopeUnit.dacIsOn() ? soBaseVol & soMask : 0; const unsigned long outLow = outBase * (0 - 15ul); const unsigned long endCycles = cycleCounter + cycles; - + for (;;) { const unsigned long outHigh = master ? outBase * (envelopeUnit.getVolume() * 2 - 15ul) : outLow; const unsigned long nextMajorEvent = nextEventUnit->getCounter() < endCycles ? nextEventUnit->getCounter() : endCycles; unsigned long out = dutyUnit.isHighState() ? outHigh : outLow; - + while (dutyUnit.getCounter() <= nextMajorEvent) { *buf = out - prevOut; prevOut = out; buf += dutyUnit.getCounter() - cycleCounter; cycleCounter = dutyUnit.getCounter(); - + dutyUnit.event(); out = dutyUnit.isHighState() ? outHigh : outLow; } - + if (cycleCounter < nextMajorEvent) { *buf = out - prevOut; prevOut = out; buf += nextMajorEvent - cycleCounter; cycleCounter = nextMajorEvent; } - + if (nextEventUnit->getCounter() == nextMajorEvent) { nextEventUnit->event(); setEvent(); } else break; } - + if (cycleCounter & SoundUnit::COUNTER_MAX) { dutyUnit.resetCounters(cycleCounter); lengthCounter.resetCounters(cycleCounter); envelopeUnit.resetCounters(cycleCounter); sweepUnit.resetCounters(cycleCounter); - + cycleCounter -= SoundUnit::COUNTER_MAX; } } diff --git a/libgambatte/src/sound/channel1.h b/libgambatte/src/sound/channel1.h index 5cd7c23ed2..113629bb8a 100644 --- a/libgambatte/src/sound/channel1.h +++ b/libgambatte/src/sound/channel1.h @@ -38,9 +38,9 @@ class Channel1 { unsigned short shadow; unsigned char nr0; bool negging; - + unsigned calcFreq(); - + public: SweepUnit(MasterDisabler &disabler, DutyUnit &dutyUnit); void event(); @@ -51,27 +51,27 @@ class Channel1 { templatevoid SyncState(NewState *ns); }; - + friend class StaticOutputTester; - + StaticOutputTester staticOutputTest; DutyMasterDisabler disableMaster; LengthCounter lengthCounter; DutyUnit dutyUnit; EnvelopeUnit envelopeUnit; SweepUnit sweepUnit; - + SoundUnit *nextEventUnit; - + unsigned long cycleCounter; unsigned long soMask; unsigned long prevOut; - + unsigned char nr4; bool master; - + void setEvent(); - + public: Channel1(); void setNr0(unsigned data); @@ -79,12 +79,12 @@ public: void setNr2(unsigned data); void setNr3(unsigned data); void setNr4(unsigned data); - + void setSo(unsigned long soMask); bool isActive() const { return master; } - + void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles); - + void reset(); void init(bool cgb); void loadState(const SaveState &state); diff --git a/libgambatte/src/sound/channel2.cpp b/libgambatte/src/sound/channel2.cpp index b3135e2299..13bcc23b4a 100644 --- a/libgambatte/src/sound/channel2.cpp +++ b/libgambatte/src/sound/channel2.cpp @@ -46,7 +46,7 @@ void Channel2::setEvent() { void Channel2::setNr1(const unsigned data) { lengthCounter.nr1Change(data, nr4, cycleCounter); dutyUnit.nr1Change(data, cycleCounter); - + setEvent(); } @@ -55,7 +55,7 @@ void Channel2::setNr2(const unsigned data) { disableMaster(); else staticOutputTest(cycleCounter); - + setEvent(); } @@ -66,17 +66,17 @@ void Channel2::setNr3(const unsigned data) { void Channel2::setNr4(const unsigned data) { lengthCounter.nr4Change(nr4, data, cycleCounter); - + nr4 = data; - + if (data & 0x80) { //init-bit nr4 &= 0x7F; master = !envelopeUnit.nr4Init(cycleCounter); staticOutputTest(cycleCounter); } - + dutyUnit.nr4Change(data, cycleCounter); - + setEvent(); } @@ -88,11 +88,11 @@ void Channel2::setSo(const unsigned long soMask) { void Channel2::reset() { cycleCounter = 0x1000 | (cycleCounter & 0xFFF); // cycleCounter >> 12 & 7 represents the frame sequencer position. - + // lengthCounter.reset(); dutyUnit.reset(); envelopeUnit.reset(); - + setEvent(); } @@ -104,7 +104,7 @@ void Channel2::loadState(const SaveState &state) { dutyUnit.loadState(state.spu.ch2.duty, state.mem.ioamhram.get()[0x116], state.spu.ch2.nr4,state.spu.cycleCounter); envelopeUnit.loadState(state.spu.ch2.env, state.mem.ioamhram.get()[0x117], state.spu.cycleCounter); lengthCounter.loadState(state.spu.ch2.lcounter, state.spu.cycleCounter); - + cycleCounter = state.spu.cycleCounter; nr4 = state.spu.ch2.nr4; master = state.spu.ch2.master; @@ -114,41 +114,41 @@ void Channel2::update(uint_least32_t *buf, const unsigned long soBaseVol, unsign const unsigned long outBase = envelopeUnit.dacIsOn() ? soBaseVol & soMask : 0; const unsigned long outLow = outBase * (0 - 15ul); const unsigned long endCycles = cycleCounter + cycles; - + for (;;) { const unsigned long outHigh = master ? outBase * (envelopeUnit.getVolume() * 2 - 15ul) : outLow; const unsigned long nextMajorEvent = nextEventUnit->getCounter() < endCycles ? nextEventUnit->getCounter() : endCycles; unsigned long out = dutyUnit.isHighState() ? outHigh : outLow; - + while (dutyUnit.getCounter() <= nextMajorEvent) { *buf += out - prevOut; prevOut = out; buf += dutyUnit.getCounter() - cycleCounter; cycleCounter = dutyUnit.getCounter(); - + dutyUnit.event(); out = dutyUnit.isHighState() ? outHigh : outLow; } - + if (cycleCounter < nextMajorEvent) { *buf += out - prevOut; prevOut = out; buf += nextMajorEvent - cycleCounter; cycleCounter = nextMajorEvent; } - + if (nextEventUnit->getCounter() == nextMajorEvent) { nextEventUnit->event(); setEvent(); } else break; } - + if (cycleCounter & SoundUnit::COUNTER_MAX) { dutyUnit.resetCounters(cycleCounter); lengthCounter.resetCounters(cycleCounter); envelopeUnit.resetCounters(cycleCounter); - + cycleCounter -= SoundUnit::COUNTER_MAX; } } diff --git a/libgambatte/src/sound/channel2.h b/libgambatte/src/sound/channel2.h index 9cc2ef28b6..611d8cd51c 100644 --- a/libgambatte/src/sound/channel2.h +++ b/libgambatte/src/sound/channel2.h @@ -32,37 +32,37 @@ struct SaveState; class Channel2 { friend class StaticOutputTester; - + StaticOutputTester staticOutputTest; DutyMasterDisabler disableMaster; LengthCounter lengthCounter; DutyUnit dutyUnit; EnvelopeUnit envelopeUnit; - + SoundUnit *nextEventUnit; - + unsigned long cycleCounter; unsigned long soMask; unsigned long prevOut; - + unsigned char nr4; bool master; - + void setEvent(); - + public: Channel2(); void setNr1(unsigned data); void setNr2(unsigned data); void setNr3(unsigned data); void setNr4(unsigned data); - + void setSo(unsigned long soMask); // void deactivate() { disableMaster(); setEvent(); } bool isActive() const { return master; } - + void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles); - + void reset(); void init(bool cgb); void loadState(const SaveState &state); diff --git a/libgambatte/src/sound/channel3.cpp b/libgambatte/src/sound/channel3.cpp index aeab458d6b..d91be4416b 100644 --- a/libgambatte/src/sound/channel3.cpp +++ b/libgambatte/src/sound/channel3.cpp @@ -47,33 +47,33 @@ Channel3::Channel3() : void Channel3::setNr0(const unsigned data) { nr0 = data & 0x80; - + if (!(data & 0x80)) disableMaster(); } void Channel3::setNr2(const unsigned data) { rShift = (data >> 5 & 3U) - 1; - + if (rShift > 3) rShift = 4; } void Channel3::setNr4(const unsigned data) { lengthCounter.nr4Change(nr4, data, cycleCounter); - + nr4 = data & 0x7F; - + if (data & nr0/* & 0x80*/) { if (!cgb && waveCounter == cycleCounter + 1) { const unsigned pos = ((wavePos + 1) & 0x1F) >> 1; - + if (pos < 4) waveRam[0] = waveRam[pos]; else std::memcpy(waveRam, waveRam + (pos & ~3), 4); } - + master = true; wavePos = 0; lastReadTime = waveCounter = cycleCounter + toPeriod(nr3, data) + 3; @@ -102,7 +102,7 @@ void Channel3::setStatePtrs(SaveState &state) { void Channel3::loadState(const SaveState &state) { lengthCounter.loadState(state.spu.ch3.lcounter, state.spu.cycleCounter); - + cycleCounter = state.spu.cycleCounter; waveCounter = std::max(state.spu.ch3.waveCounter, state.spu.cycleCounter); lastReadTime = state.spu.ch3.lastReadTime; @@ -111,7 +111,7 @@ void Channel3::loadState(const SaveState &state) { wavePos = state.spu.ch3.wavePos & 0x1F; sampleBuf = state.spu.ch3.sampleBuf; master = state.spu.ch3.master; - + nr0 = state.mem.ioamhram.get()[0x11A] & 0x80; setNr2(state.mem.ioamhram.get()[0x11C]); } @@ -133,20 +133,20 @@ void Channel3::updateWaveCounter(const unsigned long cc) { void Channel3::update(uint_least32_t *buf, const unsigned long soBaseVol, unsigned long cycles) { const unsigned long outBase = (nr0/* & 0x80*/) ? soBaseVol & soMask : 0; - + if (outBase && rShift != 4) { const unsigned long endCycles = cycleCounter + cycles; - + for (;;) { const unsigned long nextMajorEvent = lengthCounter.getCounter() < endCycles ? lengthCounter.getCounter() : endCycles; unsigned long out = outBase * (master ? ((sampleBuf >> (~wavePos << 2 & 4) & 0xF) >> rShift) * 2 - 15ul : 0 - 15ul); - + while (waveCounter <= nextMajorEvent) { *buf += out - prevOut; prevOut = out; buf += waveCounter - cycleCounter; cycleCounter = waveCounter; - + lastReadTime = waveCounter; waveCounter += toPeriod(nr3, nr4); ++wavePos; @@ -154,14 +154,14 @@ void Channel3::update(uint_least32_t *buf, const unsigned long soBaseVol, unsign sampleBuf = waveRam[wavePos >> 1]; out = outBase * (/*master ? */((sampleBuf >> (~wavePos << 2 & 4) & 0xF) >> rShift) * 2 - 15ul/* : 0 - 15ul*/); } - + if (cycleCounter < nextMajorEvent) { *buf += out - prevOut; prevOut = out; buf += nextMajorEvent - cycleCounter; cycleCounter = nextMajorEvent; } - + if (lengthCounter.getCounter() == nextMajorEvent) { lengthCounter.event(); } else @@ -171,23 +171,23 @@ void Channel3::update(uint_least32_t *buf, const unsigned long soBaseVol, unsign const unsigned long out = outBase * (0 - 15ul); *buf += out - prevOut; prevOut = out; - + cycleCounter += cycles; - + while (lengthCounter.getCounter() <= cycleCounter) { updateWaveCounter(lengthCounter.getCounter()); lengthCounter.event(); } - + updateWaveCounter(cycleCounter); } - + if (cycleCounter & SoundUnit::COUNTER_MAX) { lengthCounter.resetCounters(cycleCounter); - + if (waveCounter != SoundUnit::COUNTER_DISABLED) waveCounter -= SoundUnit::COUNTER_MAX; - + lastReadTime -= SoundUnit::COUNTER_MAX; cycleCounter -= SoundUnit::COUNTER_MAX; } @@ -196,7 +196,7 @@ void Channel3::update(uint_least32_t *buf, const unsigned long soBaseVol, unsign SYNCFUNC(Channel3) { NSS(waveRam); - + SSS(lengthCounter); NSS(cycleCounter); diff --git a/libgambatte/src/sound/channel3.h b/libgambatte/src/sound/channel3.h index fe81789899..a6c5253c44 100644 --- a/libgambatte/src/sound/channel3.h +++ b/libgambatte/src/sound/channel3.h @@ -31,35 +31,35 @@ struct SaveState; class Channel3 { class Ch3MasterDisabler : public MasterDisabler { unsigned long &waveCounter; - + public: Ch3MasterDisabler(bool &m, unsigned long &wC) : MasterDisabler(m), waveCounter(wC) {} void operator()() { MasterDisabler::operator()(); waveCounter = SoundUnit::COUNTER_DISABLED; } }; - + unsigned char waveRam[0x10]; - + Ch3MasterDisabler disableMaster; LengthCounter lengthCounter; - + unsigned long cycleCounter; unsigned long soMask; unsigned long prevOut; unsigned long waveCounter; unsigned long lastReadTime; - + unsigned char nr0; unsigned char nr3; unsigned char nr4; unsigned char wavePos; unsigned char rShift; unsigned char sampleBuf; - + bool master; bool cgb; - + void updateWaveCounter(unsigned long cc); - + public: Channel3(); bool isActive() const { return master; } @@ -74,26 +74,26 @@ public: void setNr4(unsigned data); void setSo(unsigned long soMask); void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles); - + unsigned waveRamRead(unsigned index) const { if (master) { if (!cgb && cycleCounter != lastReadTime) return 0xFF; - + index = wavePos >> 1; } - + return waveRam[index]; } - + void waveRamWrite(unsigned index, unsigned data) { if (master) { if (!cgb && cycleCounter != lastReadTime) return; - + index = wavePos >> 1; } - + waveRam[index] = data; } diff --git a/libgambatte/src/sound/channel4.cpp b/libgambatte/src/sound/channel4.cpp index 31c0590646..cc5a29799f 100644 --- a/libgambatte/src/sound/channel4.cpp +++ b/libgambatte/src/sound/channel4.cpp @@ -23,12 +23,12 @@ static unsigned long toPeriod(const unsigned nr3) { unsigned s = (nr3 >> 4) + 3; unsigned r = nr3 & 7; - + if (!r) { r = 1; --s; } - + return r << s; } @@ -46,13 +46,13 @@ void Channel4::Lfsr::updateBackupCounter(const unsigned long cc) { const unsigned long period = toPeriod(nr3); backupCounter = cc - (cc - backupCounter) % period + period; }*/ - + if (backupCounter <= cc) { const unsigned long period = toPeriod(nr3); unsigned long periods = (cc - backupCounter) / period + 1; - + backupCounter += periods * period; - + if (master && nr3 < 0xE0) { if (nr3 & 8) { while (periods > 6) { @@ -60,7 +60,7 @@ void Channel4::Lfsr::updateBackupCounter(const unsigned long cc) { reg = (reg >> 6 & ~0x7E) | xored | xored << 8; periods -= 6; } - + const unsigned xored = ((reg ^ reg >> 1) << (7 - periods)) & 0x7F; reg = (reg >> periods & ~(0x80 - (0x80 >> periods))) | xored | xored << 8; } else { @@ -68,7 +68,7 @@ void Channel4::Lfsr::updateBackupCounter(const unsigned long cc) { reg = reg ^ reg >> 1; periods -= 15; } - + reg = reg >> periods | (((reg ^ reg >> 1) << (15 - periods)) & 0x7FFF); } } @@ -95,27 +95,27 @@ inline void Channel4::Lfsr::event() { if (nr3 < 0xE0) { const unsigned shifted = reg >> 1; const unsigned xored = (reg ^ shifted) & 1; - + reg = shifted | xored << 14; - + if (nr3 & 8) reg = (reg & ~0x40) | xored << 6; } - + counter += toPeriod(nr3); backupCounter = counter; - - + + /*if (nr3 < 0xE0) { const unsigned periods = nextStateDistance[reg & 0x3F]; const unsigned xored = ((reg ^ reg >> 1) << (7 - periods)) & 0x7F; - + reg = reg >> periods | xored << 8; - + if (nr3 & 8) reg = reg & ~(0x80 - (0x80 >> periods)) | xored; } - + const unsigned long period = toPeriod(nr3); backupCounter = counter + period; counter += period * nextStateDistance[reg & 0x3F];*/ @@ -124,7 +124,7 @@ inline void Channel4::Lfsr::event() { void Channel4::Lfsr::nr3Change(const unsigned newNr3, const unsigned long cc) { updateBackupCounter(cc); nr3 = newNr3; - + // if (counter != COUNTER_DISABLED) // counter = backupCounter + toPeriod(nr3) * (nextStateDistance[reg & 0x3F] - 1); } @@ -191,7 +191,7 @@ void Channel4::setEvent() { void Channel4::setNr1(const unsigned data) { lengthCounter.nr1Change(data, nr4, cycleCounter); - + setEvent(); } @@ -200,26 +200,26 @@ void Channel4::setNr2(const unsigned data) { disableMaster(); else staticOutputTest(cycleCounter); - + setEvent(); } void Channel4::setNr4(const unsigned data) { lengthCounter.nr4Change(nr4, data, cycleCounter); - + nr4 = data; - + if (data & 0x80) { //init-bit nr4 &= 0x7F; - + master = !envelopeUnit.nr4Init(cycleCounter); - + if (master) lfsr.nr4Init(cycleCounter); - + staticOutputTest(cycleCounter); } - + setEvent(); } @@ -235,7 +235,7 @@ void Channel4::reset() { // lengthCounter.reset(); lfsr.reset(cycleCounter); envelopeUnit.reset(); - + setEvent(); } @@ -247,7 +247,7 @@ void Channel4::loadState(const SaveState &state) { lfsr.loadState(state); envelopeUnit.loadState(state.spu.ch4.env, state.mem.ioamhram.get()[0x121], state.spu.cycleCounter); lengthCounter.loadState(state.spu.ch4.lcounter, state.spu.cycleCounter); - + cycleCounter = state.spu.cycleCounter; nr4 = state.spu.ch4.nr4; master = state.spu.ch4.master; @@ -257,41 +257,41 @@ void Channel4::update(uint_least32_t *buf, const unsigned long soBaseVol, unsign const unsigned long outBase = envelopeUnit.dacIsOn() ? soBaseVol & soMask : 0; const unsigned long outLow = outBase * (0 - 15ul); const unsigned long endCycles = cycleCounter + cycles; - + for (;;) { const unsigned long outHigh = /*master ? */outBase * (envelopeUnit.getVolume() * 2 - 15ul)/* : outLow*/; const unsigned long nextMajorEvent = nextEventUnit->getCounter() < endCycles ? nextEventUnit->getCounter() : endCycles; unsigned long out = lfsr.isHighState() ? outHigh : outLow; - + while (lfsr.getCounter() <= nextMajorEvent) { *buf += out - prevOut; prevOut = out; buf += lfsr.getCounter() - cycleCounter; cycleCounter = lfsr.getCounter(); - + lfsr.event(); out = lfsr.isHighState() ? outHigh : outLow; } - + if (cycleCounter < nextMajorEvent) { *buf += out - prevOut; prevOut = out; buf += nextMajorEvent - cycleCounter; cycleCounter = nextMajorEvent; } - + if (nextEventUnit->getCounter() == nextMajorEvent) { nextEventUnit->event(); setEvent(); } else break; } - + if (cycleCounter & SoundUnit::COUNTER_MAX) { lengthCounter.resetCounters(cycleCounter); lfsr.resetCounters(cycleCounter); envelopeUnit.resetCounters(cycleCounter); - + cycleCounter -= SoundUnit::COUNTER_MAX; } } @@ -311,7 +311,7 @@ SYNCFUNC(Channel4) NSS(cycleCounter); NSS(soMask); NSS(prevOut); - + NSS(nr4); NSS(master); } diff --git a/libgambatte/src/sound/channel4.h b/libgambatte/src/sound/channel4.h index 785c98f2f6..4490404fa2 100644 --- a/libgambatte/src/sound/channel4.h +++ b/libgambatte/src/sound/channel4.h @@ -36,9 +36,9 @@ class Channel4 { unsigned short reg; unsigned char nr3; bool master; - + void updateBackupCounter(unsigned long cc); - + public: Lfsr(); void event(); @@ -54,45 +54,45 @@ class Channel4 { templatevoid SyncState(NewState *ns); }; - + class Ch4MasterDisabler : public MasterDisabler { Lfsr &lfsr; public: Ch4MasterDisabler(bool &m, Lfsr &lfsr) : MasterDisabler(m), lfsr(lfsr) {} void operator()() { MasterDisabler::operator()(); lfsr.disableMaster(); } }; - + friend class StaticOutputTester; - + StaticOutputTester staticOutputTest; Ch4MasterDisabler disableMaster; LengthCounter lengthCounter; EnvelopeUnit envelopeUnit; Lfsr lfsr; - + SoundUnit *nextEventUnit; - + unsigned long cycleCounter; unsigned long soMask; unsigned long prevOut; - + unsigned char nr4; bool master; - + void setEvent(); - + public: Channel4(); void setNr1(unsigned data); void setNr2(unsigned data); void setNr3(unsigned data) { lfsr.nr3Change(data, cycleCounter); /*setEvent();*/ } void setNr4(unsigned data); - + void setSo(unsigned long soMask); bool isActive() const { return master; } - + void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles); - + void reset(); void init(bool cgb); void loadState(const SaveState &state); diff --git a/libgambatte/src/sound/duty_unit.cpp b/libgambatte/src/sound/duty_unit.cpp index 02ba2a8a04..4d2a0ddde6 100644 --- a/libgambatte/src/sound/duty_unit.cpp +++ b/libgambatte/src/sound/duty_unit.cpp @@ -21,7 +21,7 @@ static inline bool toOutState(const unsigned duty, const unsigned pos) { static const unsigned char duties[4] = { 0x80, 0x81, 0xE1, 0x7E }; - + return duties[duty] >> pos & 1; } @@ -52,7 +52,7 @@ void DutyUnit::setCounter() { 0, 3, 2, 1, 0, 3, 2, 1, 0, 5, 4, 3, 2, 1, 0, 1 }; - + if (enableEvents && nextPosUpdate != COUNTER_DISABLED) counter = nextPosUpdate + period * nextStateDistance[(duty * 8) | pos]; else @@ -67,13 +67,13 @@ void DutyUnit::setFreq(const unsigned newFreq, const unsigned long cc) { void DutyUnit::event() { unsigned inc = period << duty; - + if (duty == 3) inc -= period * 2; - + if (!(high ^= true)) inc = period * 8 - inc; - + counter += inc; } @@ -89,7 +89,7 @@ void DutyUnit::nr3Change(const unsigned newNr3, const unsigned long cc) { void DutyUnit::nr4Change(const unsigned newNr4, const unsigned long cc) { setFreq((newNr4 << 8 & 0x700) | (getFreq() & 0xFF), cc); - + if (newNr4 & 0x80) { nextPosUpdate = (cc & ~1) + period; setCounter(); @@ -124,7 +124,7 @@ void DutyUnit::loadState(const SaveState::SPU::Duty &dstate, const unsigned nr1, void DutyUnit::resetCounters(const unsigned long oldCc) { if (nextPosUpdate == COUNTER_DISABLED) return; - + updatePos(oldCc); nextPosUpdate -= COUNTER_MAX; SoundUnit::resetCounters(oldCc); diff --git a/libgambatte/src/sound/duty_unit.h b/libgambatte/src/sound/duty_unit.h index 0a5f3e9412..a45294133e 100644 --- a/libgambatte/src/sound/duty_unit.h +++ b/libgambatte/src/sound/duty_unit.h @@ -50,7 +50,7 @@ public: void resetCounters(unsigned long oldCc); void killCounter(); void reviveCounter(unsigned long cc); - + //intended for use by SweepUnit only. unsigned getFreq() const { return 2048 - (period >> 1); } void setFreq(unsigned newFreq, unsigned long cc); diff --git a/libgambatte/src/sound/envelope_unit.cpp b/libgambatte/src/sound/envelope_unit.cpp index 7c81607fa0..917c73bc13 100644 --- a/libgambatte/src/sound/envelope_unit.cpp +++ b/libgambatte/src/sound/envelope_unit.cpp @@ -25,21 +25,21 @@ EnvelopeUnit::VolOnOffEvent EnvelopeUnit::nullEvent; void EnvelopeUnit::event() { const unsigned long period = nr2 & 7; - + if (period) { unsigned newVol = volume; - + if (nr2 & 8) ++newVol; else --newVol; - + if (newVol < 0x10U) { volume = newVol; - + if (volume < 2) volOnOffEvent(counter); - + counter += period << 15; } else counter = COUNTER_DISABLED; @@ -52,32 +52,32 @@ bool EnvelopeUnit::nr2Change(const unsigned newNr2) { ++volume; else if (!(nr2 & 8)) volume += 2; - + if ((nr2 ^ newNr2) & 8) volume = 0x10 - volume; - + volume &= 0xF; - + nr2 = newNr2; - + return !(newNr2 & 0xF8); } bool EnvelopeUnit::nr4Init(const unsigned long cc) { { unsigned long period = nr2 & 7; - + if (!period) period = 8; - + if (!(cc & 0x7000)) ++period; - + counter = cc - ((cc - 0x1000) & 0x7FFF) + period * 0x8000; } - + volume = nr2 >> 4; - + return !(nr2 & 0xF8); } diff --git a/libgambatte/src/sound/envelope_unit.h b/libgambatte/src/sound/envelope_unit.h index 4a0587d141..69f74d1997 100644 --- a/libgambatte/src/sound/envelope_unit.h +++ b/libgambatte/src/sound/envelope_unit.h @@ -31,13 +31,13 @@ public: virtual ~VolOnOffEvent() {} virtual void operator()(unsigned long /*cc*/) {} }; - + private: static VolOnOffEvent nullEvent; VolOnOffEvent &volOnOffEvent; unsigned char nr2; unsigned char volume; - + public: explicit EnvelopeUnit(VolOnOffEvent &volOnOffEvent = nullEvent); void event(); diff --git a/libgambatte/src/sound/length_counter.cpp b/libgambatte/src/sound/length_counter.cpp index e1e9f00862..7822fb934f 100644 --- a/libgambatte/src/sound/length_counter.cpp +++ b/libgambatte/src/sound/length_counter.cpp @@ -44,23 +44,23 @@ void LengthCounter::nr1Change(const unsigned newNr1, const unsigned nr4, const u void LengthCounter::nr4Change(const unsigned oldNr4, const unsigned newNr4, const unsigned long cycleCounter) { if (counter != COUNTER_DISABLED) lengthCounter = (counter >> 13) - (cycleCounter >> 13); - + { unsigned dec = 0; - + if (newNr4 & 0x40) { dec = ~cycleCounter >> 12 & 1; - + if (!(oldNr4 & 0x40) && lengthCounter) { if (!(lengthCounter -= dec)) disableMaster(); } } - + if ((newNr4 & 0x80) && !lengthCounter) lengthCounter = lengthMask + 1 - dec; } - + if ((newNr4 & 0x40) && lengthCounter) counter = ((cycleCounter >> 13) + lengthCounter) << 13; else @@ -69,7 +69,7 @@ void LengthCounter::nr4Change(const unsigned oldNr4, const unsigned newNr4, cons /*void LengthCounter::reset() { counter = COUNTER_DISABLED; - + if (cgb) lengthCounter = lengthMask + 1; }*/ diff --git a/libgambatte/src/sound/master_disabler.h b/libgambatte/src/sound/master_disabler.h index e9f75382e6..efbc37f114 100644 --- a/libgambatte/src/sound/master_disabler.h +++ b/libgambatte/src/sound/master_disabler.h @@ -22,7 +22,7 @@ namespace gambatte { class MasterDisabler { bool &master; - + public: MasterDisabler(bool &m) : master(m) {} virtual ~MasterDisabler() {} diff --git a/libgambatte/src/sound/sound_unit.h b/libgambatte/src/sound/sound_unit.h index b1ca691e8a..d2a00582ad 100644 --- a/libgambatte/src/sound/sound_unit.h +++ b/libgambatte/src/sound/sound_unit.h @@ -26,7 +26,7 @@ protected: unsigned long counter; public: enum { COUNTER_MAX = 0x80000000u, COUNTER_DISABLED = 0xFFFFFFFFu }; - + SoundUnit() : counter(COUNTER_DISABLED) {} virtual ~SoundUnit() {} virtual void event() = 0; diff --git a/libgambatte/src/tima.cpp b/libgambatte/src/tima.cpp index 979ffded65..276385e782 100644 --- a/libgambatte/src/tima.cpp +++ b/libgambatte/src/tima.cpp @@ -34,11 +34,11 @@ tac_(0) void Tima::loadState(const SaveState &state, const TimaInterruptRequester timaIrq) { lastUpdate_ = state.mem.timaLastUpdate; tmatime_ = state.mem.tmatime; - + tima_ = state.mem.ioamhram.get()[0x105]; tma_ = state.mem.ioamhram.get()[0x106]; tac_ = state.mem.ioamhram.get()[0x107]; - + timaIrq.setNextIrqEventTime((tac_ & 4) ? (tmatime_ != DISABLED_TIME && tmatime_ > state.cpu.cycleCounter @@ -51,14 +51,14 @@ void Tima::loadState(const SaveState &state, const TimaInterruptRequester timaIr void Tima::resetCc(const unsigned long oldCc, const unsigned long newCc, const TimaInterruptRequester timaIrq) { const unsigned long dec = oldCc - newCc; - + if (tac_ & 0x04) { updateIrq(oldCc, timaIrq); updateTima(oldCc); - + lastUpdate_ -= dec; timaIrq.setNextIrqEventTime(timaIrq.nextIrqEventTime() - dec); - + if (tmatime_ != DISABLED_TIME) tmatime_ -= dec; } @@ -106,7 +106,7 @@ void Tima::setTima(const unsigned data, const unsigned long cycleCounter, const timaIrq.setNextIrqEventTime(lastUpdate_ + ((256u - data) << timaClock[tac_ & 3]) + 3); } - + tima_ = data; } @@ -115,14 +115,14 @@ void Tima::setTma(const unsigned data, const unsigned long cycleCounter, const T updateIrq(cycleCounter, timaIrq); updateTima(cycleCounter); } - + tma_ = data; } void Tima::setTac(const unsigned data, const unsigned long cycleCounter, const TimaInterruptRequester timaIrq, bool gbIsCgb) { if (tac_ ^ data) { unsigned long nextIrqEventTime = timaIrq.nextIrqEventTime(); - + if (tac_ & 0x04) { updateIrq(cycleCounter, timaIrq); updateTima(cycleCounter); @@ -130,10 +130,10 @@ void Tima::setTac(const unsigned data, const unsigned long cycleCounter, const T lastUpdate_ -= (1u << (timaClock[tac_ & 3] - 1)) + 3; tmatime_ -= (1u << (timaClock[tac_ & 3] - 1)) + 3; nextIrqEventTime -= (1u << (timaClock[tac_ & 3] - 1)) + 3; - + if (cycleCounter >= nextIrqEventTime) timaIrq.flagIrq(); - + updateTima(cycleCounter); tmatime_ = DISABLED_TIME; @@ -143,18 +143,18 @@ void Tima::setTac(const unsigned data, const unsigned long cycleCounter, const T if (data & 4) { lastUpdate_ = (cycleCounter >> timaClock[data & 3]) << timaClock[data & 3]; unsigned long diff = cycleCounter - basetime_; - + if (gbIsCgb) { if (((diff >> (timaClock[tac_ & 3] - 1)) & 1) == 1 && ((diff >> (timaClock[data & 3] - 1)) & 1) == 0) tima_++; - } + } lastUpdate_ = basetime_ + ((diff >> timaClock[data & 3]) << timaClock[data & 3]); nextIrqEventTime = lastUpdate_ + ((256u - tima_) << timaClock[data & 3]) + 3; } - + timaIrq.setNextIrqEventTime(nextIrqEventTime); } - + tac_ = data; } diff --git a/libgambatte/src/tima.h b/libgambatte/src/tima.h index a66f987837..df866a903e 100644 --- a/libgambatte/src/tima.h +++ b/libgambatte/src/tima.h @@ -25,7 +25,7 @@ namespace gambatte { class TimaInterruptRequester { InterruptRequester &intreq; - + public: explicit TimaInterruptRequester(InterruptRequester &intreq) : intreq(intreq) {} void flagIrq() const { intreq.flagIrq(4); } @@ -37,29 +37,29 @@ class Tima { unsigned long basetime_; unsigned long lastUpdate_; unsigned long tmatime_; - + unsigned char tima_; unsigned char tma_; unsigned char tac_; - + void updateIrq(const unsigned long cc, const TimaInterruptRequester timaIrq) { while (cc >= timaIrq.nextIrqEventTime()) doIrqEvent(timaIrq); } - + void updateTima(unsigned long cc); - + public: Tima(); void loadState(const SaveState &, TimaInterruptRequester timaIrq); void resetCc(unsigned long oldCc, unsigned long newCc, TimaInterruptRequester timaIrq); - + void setTima(unsigned tima, unsigned long cc, TimaInterruptRequester timaIrq); void setTma(unsigned tma, unsigned long cc, TimaInterruptRequester timaIrq); void setTac(unsigned tac, unsigned long cc, TimaInterruptRequester timaIrq, bool gbIsCgb); void resTac(unsigned long cc, TimaInterruptRequester timaIrq); unsigned tima(unsigned long cc); - + void doIrqEvent(TimaInterruptRequester timaIrq); templatevoid SyncState(NewState *ns); diff --git a/libgambatte/src/video.cpp b/libgambatte/src/video.cpp index c2b82a6495..a30bf98c01 100644 --- a/libgambatte/src/video.cpp +++ b/libgambatte/src/video.cpp @@ -23,7 +23,7 @@ namespace gambatte { -void LCD::setDmgPalette(unsigned long *const palette, const unsigned long *const dmgColors, const unsigned data) { +void LCD::setDmgPalette(unsigned long palette[], const unsigned long dmgColors[], unsigned data) { palette[0] = dmgColors[data & 3]; palette[1] = dmgColors[data >> 2 & 3]; palette[2] = dmgColors[data >> 4 & 3]; @@ -77,18 +77,18 @@ void LCD::setCgb(bool cgb) { static unsigned long mode2IrqSchedule(const unsigned statReg, const LyCounter &lyCounter, const unsigned long cycleCounter) { if (!(statReg & 0x20)) return DISABLED_TIME; - + unsigned next = lyCounter.time() - cycleCounter; - + if (lyCounter.ly() >= 143 || (lyCounter.ly() == 142 && next <= 4) || (statReg & 0x08)) { next += (153u - lyCounter.ly()) * lyCounter.lineTime(); } else { if (next <= 4) next += lyCounter.lineTime(); - + next -= 4; } - + return cycleCounter + next; } @@ -96,12 +96,12 @@ static inline unsigned long m0IrqTimeFromXpos166Time(const unsigned long xpos166 return xpos166Time + cgb - ds; } -static inline unsigned long hdmaTimeFromM0Time(const unsigned long m0Time, const bool ds) { +static inline unsigned long hdmaTimeFromM0Time(unsigned long m0Time, bool ds) { return m0Time + 1 - ds; } -static unsigned long nextHdmaTime(const unsigned long lastM0Time, - const unsigned long nextM0Time, const unsigned long cycleCounter, const bool ds) { +static unsigned long nextHdmaTime(unsigned long lastM0Time, + unsigned long nextM0Time, unsigned long cycleCounter, bool ds) { return cycleCounter < hdmaTimeFromM0Time(lastM0Time, ds) ? hdmaTimeFromM0Time(lastM0Time, ds) : hdmaTimeFromM0Time(nextM0Time, ds); @@ -125,7 +125,7 @@ void LCD::loadState(const SaveState &state, const unsigned char *const oamram) { if (ppu.lcdc() & 0x80) { nextM0Time_.predictNextM0Time(ppu); lycIrq.reschedule(ppu.lyCounter(), ppu.now()); - + eventTimes_.setm(state.ppu.pendingLcdstatIrq ? ppu.now() + 1 : static_cast(DISABLED_TIME)); eventTimes_.setm(state.ppu.oldWy != state.mem.ioamhram.get()[0x14A] @@ -140,8 +140,8 @@ void LCD::loadState(const SaveState &state, const unsigned char *const oamram) { ? nextHdmaTime(ppu.lastM0Time(), nextM0Time_.predictedNextM0Time(), ppu.now(), isDoubleSpeed()) : static_cast(DISABLED_TIME)); } else for (int i = 0; i < NUM_MEM_EVENTS; ++i) - eventTimes_.set(static_cast(i), DISABLED_TIME); - + eventTimes_.set(MemEvent(i), DISABLED_TIME); + refreshPalettes(); } @@ -200,7 +200,7 @@ static void clear(T *buf, const unsigned long color, const int dpitch) { void LCD::updateScreen(const bool blanklcd, const unsigned long cycleCounter) { update(cycleCounter); - + if (blanklcd && ppu.frameBuf().fb()) { const unsigned long color = ppu.cgb() ? gbcToRgb32(0xFFFF) : dmgColorsRgb32[0]; clear(ppu.frameBuf().fb(), color, ppu.frameBuf().pitch()); @@ -210,18 +210,18 @@ void LCD::updateScreen(const bool blanklcd, const unsigned long cycleCounter) { void LCD::resetCc(const unsigned long oldCc, const unsigned long newCc) { update(oldCc); ppu.resetCc(oldCc, newCc); - + if (ppu.lcdc() & 0x80) { const unsigned long dec = oldCc - newCc; - + nextM0Time_.invalidatePredictedNextM0Time(); lycIrq.reschedule(ppu.lyCounter(), newCc); - + for (int i = 0; i < NUM_MEM_EVENTS; ++i) { - if (eventTimes_(static_cast(i)) != DISABLED_TIME) - eventTimes_.set(static_cast(i), eventTimes_(static_cast(i)) - dec); + if (eventTimes_(MemEvent(i)) != DISABLED_TIME) + eventTimes_.set(MemEvent(i), eventTimes_(MemEvent(i)) - dec); } - + eventTimes_.set(ppu.lyCounter().time()); } } @@ -229,20 +229,20 @@ void LCD::resetCc(const unsigned long oldCc, const unsigned long newCc) { void LCD::speedChange(const unsigned long cycleCounter) { update(cycleCounter); ppu.speedChange(cycleCounter); - + if (ppu.lcdc() & 0x80) { nextM0Time_.predictNextM0Time(ppu); lycIrq.reschedule(ppu.lyCounter(), cycleCounter); - + eventTimes_.set(ppu.lyCounter().time()); eventTimes_.setm(SpriteMapper::schedule(ppu.lyCounter(), cycleCounter)); eventTimes_.setm(lycIrq.time()); eventTimes_.setm(ppu.lyCounter().nextFrameCycle(144 * 456, cycleCounter)); eventTimes_.setm(mode2IrqSchedule(statReg, ppu.lyCounter(), cycleCounter)); - + if (eventTimes_(MODE0_IRQ) != DISABLED_TIME && eventTimes_(MODE0_IRQ) - cycleCounter > 1) eventTimes_.setm(m0IrqTimeFromXpos166Time(ppu.predictedNextXposTime(166), ppu.cgb(), isDoubleSpeed())); - + if (hdmaIsEnabled() && eventTimes_(HDMA_REQ) - cycleCounter > 1) { eventTimes_.setm(nextHdmaTime(ppu.lastM0Time(), nextM0Time_.predictedNextM0Time(), cycleCounter, isDoubleSpeed())); @@ -261,7 +261,7 @@ unsigned long LCD::m0TimeOfCurrentLine(const unsigned long cc) { update(cc); nextM0Time_.predictNextM0Time(ppu); } - + return gambatte::m0TimeOfCurrentLine(ppu.lyCounter().time(), ppu.lastM0Time(), nextM0Time_.predictedNextM0Time()); } @@ -269,7 +269,7 @@ static bool isHdmaPeriod(const LyCounter &lyCounter, const unsigned long m0TimeOfCurrentLy, const unsigned long cycleCounter) { const unsigned timeToNextLy = lyCounter.time() - cycleCounter; - + return /*(ppu.lcdc & 0x80) && */lyCounter.ly() < 144 && timeToNextLy > 4 && cycleCounter >= hdmaTimeFromM0Time(m0TimeOfCurrentLy, lyCounter.isDoubleSpeed()); } @@ -280,27 +280,27 @@ void LCD::enableHdma(const unsigned long cycleCounter) { nextM0Time_.predictNextM0Time(ppu); } else if (cycleCounter >= eventTimes_.nextEventTime()) update(cycleCounter); - + if (isHdmaPeriod(ppu.lyCounter(), gambatte::m0TimeOfCurrentLine(ppu.lyCounter().time(), ppu.lastM0Time(), nextM0Time_.predictedNextM0Time()), cycleCounter)) { eventTimes_.flagHdmaReq(); } - + eventTimes_.setm(nextHdmaTime(ppu.lastM0Time(), nextM0Time_.predictedNextM0Time(), cycleCounter, isDoubleSpeed())); } void LCD::disableHdma(const unsigned long cycleCounter) { if (cycleCounter >= eventTimes_.nextEventTime()) update(cycleCounter); - + eventTimes_.setm(DISABLED_TIME); } bool LCD::vramAccessible(const unsigned long cycleCounter) { if (cycleCounter >= eventTimes_.nextEventTime()) update(cycleCounter); - + return !(ppu.lcdc() & 0x80) || ppu.lyCounter().ly() >= 144 || ppu.lyCounter().lineCycles(cycleCounter) < 80U || cycleCounter + isDoubleSpeed() - ppu.cgb() + 2 >= m0TimeOfCurrentLine(cycleCounter); @@ -309,7 +309,7 @@ bool LCD::vramAccessible(const unsigned long cycleCounter) { bool LCD::cgbpAccessible(const unsigned long cycleCounter) { if (cycleCounter >= eventTimes_.nextEventTime()) update(cycleCounter); - + return !(ppu.lcdc() & 0x80) || ppu.lyCounter().ly() >= 144 || ppu.lyCounter().lineCycles(cycleCounter) < 80U + isDoubleSpeed() || cycleCounter >= m0TimeOfCurrentLine(cycleCounter) + 3 - isDoubleSpeed(); @@ -319,17 +319,17 @@ void LCD::doCgbColorChange(unsigned char *const pdata, unsigned long *const palette, unsigned index, const unsigned data) { pdata[index] = data; index >>= 1; - palette[index] = gbcToRgb32(pdata[index << 1] | pdata[(index << 1) + 1] << 8); + palette[index] = gbcToRgb32(pdata[index * 2] | pdata[(index * 2) + 1] << 8); } -void LCD::doCgbBgColorChange(unsigned index, const unsigned data, const unsigned long cycleCounter) { +void LCD::doCgbBgColorChange(unsigned index, unsigned data, unsigned long cycleCounter) { if (cgbpAccessible(cycleCounter)) { update(cycleCounter); doCgbColorChange(bgpData, ppu.bgPalette(), index, data); } } -void LCD::doCgbSpColorChange(unsigned index, const unsigned data, const unsigned long cycleCounter) { +void LCD::doCgbSpColorChange(unsigned index, unsigned data, unsigned long cycleCounter) { if (cgbpAccessible(cycleCounter)) { update(cycleCounter); doCgbColorChange(objpData, ppu.spPalette(), index, data); @@ -339,7 +339,7 @@ void LCD::doCgbSpColorChange(unsigned index, const unsigned data, const unsigned bool LCD::oamReadable(const unsigned long cycleCounter) { if (!(ppu.lcdc() & 0x80) || ppu.inactivePeriodAfterDisplayEnable(cycleCounter)) return true; - + if (cycleCounter >= eventTimes_.nextEventTime()) update(cycleCounter); @@ -352,7 +352,7 @@ bool LCD::oamReadable(const unsigned long cycleCounter) { bool LCD::oamWritable(const unsigned long cycleCounter) { if (!(ppu.lcdc() & 0x80) || ppu.inactivePeriodAfterDisplayEnable(cycleCounter)) return true; - + if (cycleCounter >= eventTimes_.nextEventTime()) update(cycleCounter); @@ -387,7 +387,7 @@ void LCD::wyChange(const unsigned newValue, const unsigned long cycleCounter) { update(cycleCounter + 1); ppu.setWy(newValue); // mode3CyclesChange(); // should be safe to wait until after wy2 delay, because no mode3 events are close to when wy1 is read. - + // wy2 is a delayed version of wy. really just slowness of ly == wy comparison. if (ppu.cgb() && (ppu.lcdc() & 0x80)) { eventTimes_.setm(cycleCounter + 5); @@ -420,7 +420,7 @@ void LCD::oamChange(const unsigned long cycleCounter) { void LCD::oamChange(const unsigned char *const oamram, const unsigned long cycleCounter) { update(cycleCounter); ppu.oamChange(oamram, cycleCounter); - + if (ppu.lcdc() & 0x80) eventTimes_.setm(SpriteMapper::schedule(ppu.lyCounter(), cycleCounter)); } @@ -428,53 +428,53 @@ void LCD::oamChange(const unsigned char *const oamram, const unsigned long cycle void LCD::lcdcChange(const unsigned data, const unsigned long cycleCounter) { const unsigned oldLcdc = ppu.lcdc(); update(cycleCounter); - + if ((oldLcdc ^ data) & 0x80) { ppu.setLcdc(data, cycleCounter); - + if (data & 0x80) { lycIrq.lcdReset(); m0Irq_.lcdReset(statReg, lycIrq.lycReg()); - + if (lycIrq.lycReg() == 0 && (statReg & 0x40)) eventTimes_.flagIrq(2); nextM0Time_.predictNextM0Time(ppu); lycIrq.reschedule(ppu.lyCounter(), cycleCounter); - + eventTimes_.set(ppu.lyCounter().time()); eventTimes_.setm(SpriteMapper::schedule(ppu.lyCounter(), cycleCounter)); eventTimes_.setm(lycIrq.time()); eventTimes_.setm(ppu.lyCounter().nextFrameCycle(144 * 456, cycleCounter)); eventTimes_.setm(mode2IrqSchedule(statReg, ppu.lyCounter(), cycleCounter)); - + if (statReg & 0x08) eventTimes_.setm(m0IrqTimeFromXpos166Time(ppu.predictedNextXposTime(166), ppu.cgb(), isDoubleSpeed())); - + if (hdmaIsEnabled()) { eventTimes_.setm(nextHdmaTime(ppu.lastM0Time(), nextM0Time_.predictedNextM0Time(), cycleCounter, isDoubleSpeed())); } } else for (int i = 0; i < NUM_MEM_EVENTS; ++i) - eventTimes_.set(static_cast(i), DISABLED_TIME); + eventTimes_.set(MemEvent(i), DISABLED_TIME); } else if (data & 0x80) { if (ppu.cgb()) { ppu.setLcdc((oldLcdc & ~0x14) | (data & 0x14), cycleCounter); - + if ((oldLcdc ^ data) & 0x04) eventTimes_.setm(SpriteMapper::schedule(ppu.lyCounter(), cycleCounter)); - + update(cycleCounter + isDoubleSpeed() + 1); ppu.setLcdc(data, cycleCounter + isDoubleSpeed() + 1); - + if ((oldLcdc ^ data) & 0x20) mode3CyclesChange(); } else { ppu.setLcdc(data, cycleCounter); - + if ((oldLcdc ^ data) & 0x04) eventTimes_.setm(SpriteMapper::schedule(ppu.lyCounter(), cycleCounter)); - + if ((oldLcdc ^ data) & 0x22) mode3CyclesChange(); } @@ -512,7 +512,7 @@ void LCD::lcdstatChange(unsigned const data, unsigned long const cycleCounter) { unsigned const old = statReg; statReg = data; lycIrq.statRegChange(data, ppu.lyCounter(), cycleCounter); - + if (ppu.lcdc() & 0x80) { int const timeToNextLy = ppu.lyCounter().time() - cycleCounter; LyCnt const lycCmp = getLycCmpLy(ppu.lyCounter(), cycleCounter); @@ -565,12 +565,12 @@ void LCD::lcdstatChange(unsigned const data, unsigned long const cycleCounter) { eventTimes_.setm(mode2IrqSchedule(data, ppu.lyCounter(), cycleCounter)); eventTimes_.setm(lycIrq.time()); } - + m2IrqStatReg_ = eventTimes_(MODE2_IRQ) - cycleCounter > (ppu.cgb() - isDoubleSpeed()) * 4U ? data : (m2IrqStatReg_ & 0x10) | (statReg & ~0x10); m1IrqStatReg_ = eventTimes_(MODE1_IRQ) - cycleCounter > (ppu.cgb() - isDoubleSpeed()) * 4U ? data : (m1IrqStatReg_ & 0x08) | (statReg & ~0x08); - + m0Irq_.statRegChange(data, eventTimes_(MODE0_IRQ), cycleCounter, ppu.cgb()); } @@ -583,16 +583,16 @@ void LCD::lycRegChange(unsigned const data, unsigned long const cycleCounter) { if (cycleCounter >= eventTimes_.nextEventTime()) update(cycleCounter); - m0Irq_.lycRegChange(data, eventTimes_(MODE0_IRQ), cycleCounter, isDoubleSpeed(), ppu.cgb()); + m0Irq_.lycRegChange(data, eventTimes_(MODE0_IRQ), cycleCounter, isDoubleSpeed(), ppu.cgb()); lycIrq.lycRegChange(data, ppu.lyCounter(), cycleCounter); - + if (!(ppu.lcdc() & 0x80)) return; - + eventTimes_.setm(lycIrq.time()); int const timeToNextLy = ppu.lyCounter().time() - cycleCounter; - + if ((statReg & 0x40) && data < 154 && (ppu.lyCounter().ly() < 144 ? !(statReg & 0x08) || cycleCounter < m0TimeOfCurrentLine(cycleCounter) || timeToNextLy <= 4 << ppu.cgb() @@ -649,22 +649,22 @@ inline void LCD::doMode2IrqEvent() { const unsigned ly = eventTimes_(LY_COUNT) - eventTimes_(MODE2_IRQ) < 8 ? (ppu.lyCounter().ly() == 153 ? 0 : ppu.lyCounter().ly() + 1) : ppu.lyCounter().ly(); - + if ((ly != 0 || !(m2IrqStatReg_ & 0x10)) && (!(m2IrqStatReg_ & 0x40) || (lycIrq.lycReg() != 0 ? ly != (lycIrq.lycReg() + 1U) : ly > 1))) { eventTimes_.flagIrq(2); } - + m2IrqStatReg_ = statReg; - + if (!(statReg & 0x08)) { unsigned long nextTime = eventTimes_(MODE2_IRQ) + ppu.lyCounter().lineTime(); - + if (ly == 0) { nextTime -= 4; } else if (ly == 143) nextTime += ppu.lyCounter().lineTime() * 10 + 4; - + eventTimes_.setm(nextTime); } else eventTimes_.setm(eventTimes_(MODE2_IRQ) + (70224 << isDoubleSpeed())); @@ -679,7 +679,7 @@ inline void LCD::event() { m1IrqStatReg_ = statReg; eventTimes_.setm(eventTimes_(MODE1_IRQ) + (70224 << isDoubleSpeed())); break; - + case LYC_IRQ: { unsigned char ifreg = 0; lycIrq.doEvent(&ifreg, ppu.lyCounter()); @@ -687,48 +687,48 @@ inline void LCD::event() { eventTimes_.setm(lycIrq.time()); break; } - + case SPRITE_MAP: eventTimes_.setm(ppu.doSpriteMapEvent(eventTimes_(SPRITE_MAP))); mode3CyclesChange(); break; - + case HDMA_REQ: eventTimes_.flagHdmaReq(); nextM0Time_.predictNextM0Time(ppu); eventTimes_.setm(hdmaTimeFromM0Time(nextM0Time_.predictedNextM0Time(), isDoubleSpeed())); break; - + case MODE2_IRQ: doMode2IrqEvent(); break; - + case MODE0_IRQ: { unsigned char ifreg = 0; m0Irq_.doEvent(&ifreg, ppu.lyCounter().ly(), statReg, lycIrq.lycReg()); eventTimes_.flagIrq(ifreg); } - - eventTimes_.setm((statReg & 0x08) + + eventTimes_.setm(statReg & 0x08 ? m0IrqTimeFromXpos166Time(ppu.predictedNextXposTime(166), ppu.cgb(), isDoubleSpeed()) : static_cast(DISABLED_TIME)); break; - + case ONESHOT_LCDSTATIRQ: eventTimes_.flagIrq(2); eventTimes_.setm(DISABLED_TIME); break; - + case ONESHOT_UPDATEWY2: ppu.updateWy2(); mode3CyclesChange(); eventTimes_.setm(DISABLED_TIME); break; } - + break; - + case LY_COUNT: ppu.doLyCountEvent(); eventTimes_.set(ppu.lyCounter().time()); @@ -741,12 +741,12 @@ inline void LCD::event() { void LCD::update(const unsigned long cycleCounter) { if (!(ppu.lcdc() & 0x80)) return; - + while (cycleCounter >= eventTimes_.nextEventTime()) { ppu.update(eventTimes_.nextEventTime()); event(); } - + ppu.update(cycleCounter); } diff --git a/libgambatte/src/video.h b/libgambatte/src/video.h index a42bd5608f..7b5628b938 100644 --- a/libgambatte/src/video.h +++ b/libgambatte/src/video.h @@ -31,7 +31,7 @@ namespace gambatte { class VideoInterruptRequester { InterruptRequester * intreq; - + public: explicit VideoInterruptRequester(InterruptRequester * intreq) : intreq(intreq) {} void flagHdmaReq() const { gambatte::flagHdmaReq(intreq); } @@ -42,40 +42,40 @@ public: class M0Irq { unsigned char statReg_; unsigned char lycReg_; - + public: M0Irq() : statReg_(0), lycReg_(0) {} - + void lcdReset(const unsigned statReg, const unsigned lycReg) { statReg_ = statReg; lycReg_ = lycReg; } - + void statRegChange(const unsigned statReg, const unsigned long nextM0IrqTime, const unsigned long cc, const bool cgb) { if (nextM0IrqTime - cc > cgb * 2U) statReg_ = statReg; } - + void lycRegChange(const unsigned lycReg, const unsigned long nextM0IrqTime, const unsigned long cc, const bool ds, const bool cgb) { if (nextM0IrqTime - cc > cgb * 5 + 1U - ds) lycReg_ = lycReg; } - + void doEvent(unsigned char *const ifreg, const unsigned ly, const unsigned statReg, const unsigned lycReg) { if (((statReg_ | statReg) & 0x08) && (!(statReg_ & 0x40) || ly != lycReg_)) *ifreg |= 2; - + statReg_ = statReg; lycReg_ = lycReg; } - + void loadState(const SaveState &state) { lycReg_ = state.ppu.m0lyc; statReg_ = state.mem.ioamhram.get()[0x141]; } - + unsigned statReg() const { return statReg_; } template @@ -90,33 +90,33 @@ class LCD { enum Event { MEM_EVENT, LY_COUNT }; enum { NUM_EVENTS = LY_COUNT + 1 }; enum MemEvent { ONESHOT_LCDSTATIRQ, ONESHOT_UPDATEWY2, MODE1_IRQ, LYC_IRQ, SPRITE_MAP, HDMA_REQ, MODE2_IRQ, MODE0_IRQ }; enum { NUM_MEM_EVENTS = MODE0_IRQ + 1 }; - + class EventTimes { MinKeeper eventMin_; MinKeeper memEventMin_; VideoInterruptRequester memEventRequester_; - + void setMemEvent() { const unsigned long nmet = nextMemEventTime(); eventMin_.setValue(nmet); memEventRequester_.setNextEventTime(nmet); } - + public: explicit EventTimes(const VideoInterruptRequester memEventRequester) : memEventRequester_(memEventRequester) {} - + Event nextEvent() const { return static_cast(eventMin_.min()); } unsigned long nextEventTime() const { return eventMin_.minValue(); } unsigned long operator()(const Event e) const { return eventMin_.value(e); } template void set(const unsigned long time) { eventMin_.setValue(time); } void set(const Event e, const unsigned long time) { eventMin_.setValue(e, time); } - + MemEvent nextMemEvent() const { return static_cast(memEventMin_.min()); } unsigned long nextMemEventTime() const { return memEventMin_.minValue(); } unsigned long operator()(const MemEvent e) const { return memEventMin_.value(e); } template void setm(const unsigned long time) { memEventMin_.setValue(time); setMemEvent(); } void set(const MemEvent e, const unsigned long time) { memEventMin_.setValue(e, time); setMemEvent(); } - + void flagIrq(const unsigned bit) { memEventRequester_.flagIrq(bit); } void flagHdmaReq() { memEventRequester_.flagHdmaReq(); } @@ -128,7 +128,7 @@ class LCD { //SSS(memEventRequester_); // not needed } }; - + PPU ppu; unsigned long dmgColorsRgb32[3 * 4]; unsigned long cgbColorsRgb32[32768]; @@ -144,7 +144,7 @@ class LCD { unsigned char m2IrqStatReg_; unsigned char m1IrqStatReg_; - static void setDmgPalette(unsigned long *palette, const unsigned long *dmgColors, unsigned data); + static void setDmgPalette(unsigned long palette[], const unsigned long dmgColors[], unsigned data); void setDmgPaletteColor(unsigned index, unsigned long rgb32); unsigned long gbcToRgb32(const unsigned bgr15); @@ -158,7 +158,7 @@ class LCD { unsigned long m0TimeOfCurrentLine(unsigned long cc); bool cgbpAccessible(unsigned long cycleCounter); - + void mode3CyclesChange(); void doCgbBgColorChange(unsigned index, unsigned data, unsigned long cycleCounter); void doCgbSpColorChange(unsigned index, unsigned data, unsigned long cycleCounter); @@ -242,7 +242,7 @@ public: update(cycleCounter); lyReg = ppu.lyCounter().ly(); - + if (lyReg == 153) { if (isDoubleSpeed()) { if (ppu.lyCounter().time() - cycleCounter <= 456 * 2 - 8) @@ -261,13 +261,13 @@ public: void lcdcChange(unsigned data, unsigned long cycleCounter); void lcdstatChange(unsigned data, unsigned long cycleCounter); void lycRegChange(unsigned data, unsigned long cycleCounter); - + void enableHdma(unsigned long cycleCounter); void disableHdma(unsigned long cycleCounter); bool hdmaIsEnabled() const { return eventTimes_(HDMA_REQ) != DISABLED_TIME; } - + void update(unsigned long cycleCounter); - + bool isCgb() const { return ppu.cgb(); } bool isDoubleSpeed() const { return ppu.lyCounter().isDoubleSpeed(); } diff --git a/libgambatte/src/video/ly_counter.cpp b/libgambatte/src/video/ly_counter.cpp index 4b978dfeee..d589bada73 100644 --- a/libgambatte/src/video/ly_counter.cpp +++ b/libgambatte/src/video/ly_counter.cpp @@ -30,28 +30,28 @@ LyCounter::LyCounter() void LyCounter::doEvent() { ++ly_; - + if (ly_ == 154) ly_ = 0; - + time_ = time_ + lineTime_; } unsigned long LyCounter::nextLineCycle(const unsigned lineCycle, const unsigned long cycleCounter) const { unsigned long tmp = time_ + (lineCycle << ds); - + if (tmp - cycleCounter > lineTime_) tmp -= lineTime_; - + return tmp; } unsigned long LyCounter::nextFrameCycle(const unsigned long frameCycle, const unsigned long cycleCounter) const { unsigned long tmp = time_ + (((153U - ly()) * 456U + frameCycle) << ds); - + if (tmp - cycleCounter > 70224U << ds) tmp -= 70224U << ds; - + return tmp; } diff --git a/libgambatte/src/video/ly_counter.h b/libgambatte/src/video/ly_counter.h index 9aca9877b1..a493ce55a5 100644 --- a/libgambatte/src/video/ly_counter.h +++ b/libgambatte/src/video/ly_counter.h @@ -30,20 +30,20 @@ class LyCounter { unsigned short lineTime_; unsigned char ly_; bool ds; - + public: LyCounter(); void doEvent(); bool isDoubleSpeed() const { return ds; } - + unsigned long frameCycles(const unsigned long cc) const { return ly_ * 456ul + lineCycles(cc); } - + unsigned lineCycles(const unsigned long cc) const { return 456u - ((time_ - cc) >> isDoubleSpeed()); } - + unsigned lineTime() const { return lineTime_; } unsigned ly() const { return ly_; } unsigned long nextLineCycle(unsigned lineCycle, unsigned long cycleCounter) const; diff --git a/libgambatte/src/video/lyc_irq.cpp b/libgambatte/src/video/lyc_irq.cpp index 024ef35233..6b95ac6e57 100644 --- a/libgambatte/src/video/lyc_irq.cpp +++ b/libgambatte/src/video/lyc_irq.cpp @@ -45,20 +45,20 @@ void LycIrq::regChange(const unsigned statReg, const unsigned lycReg, const LyCo statRegSrc_ = statReg; lycRegSrc_ = lycReg; time_ = std::min(time_, timeSrc); - + if (cgb_) { if (time_ - cc > 8 || (timeSrc != time_ && time_ - cc > 4U - lyCounter.isDoubleSpeed() * 4U)) lycReg_ = lycReg; - + if (time_ - cc > 4U - lyCounter.isDoubleSpeed() * 4U) statReg_ = statReg; } else { if (time_ - cc > 4 || timeSrc != time_) lycReg_ = lycReg; - + if (time_ - cc > 4 || lycReg_ != 0) statReg_ = statReg; - + statReg_ = (statReg_ & 0x40) | (statReg & ~0x40); } } @@ -66,13 +66,13 @@ void LycIrq::regChange(const unsigned statReg, const unsigned lycReg, const LyCo void LycIrq::doEvent(unsigned char *const ifreg, const LyCounter &lyCounter) { if ((statReg_ | statRegSrc_) & 0x40) { const unsigned cmpLy = lyCounter.time() - time_ < lyCounter.lineTime() ? 0 : lyCounter.ly(); - + if (lycReg_ == cmpLy && (lycReg_ - 1U < 144U - 1U ? !(statReg_ & 0x20) : !(statReg_ & 0x10))) { *ifreg |= 2; } } - + lycReg_ = lycRegSrc_; statReg_ = statRegSrc_; time_ = schedule(statReg_, lycReg_, lyCounter, time_); diff --git a/libgambatte/src/video/lyc_irq.h b/libgambatte/src/video/lyc_irq.h index 510039d996..497c745d46 100644 --- a/libgambatte/src/video/lyc_irq.h +++ b/libgambatte/src/video/lyc_irq.h @@ -33,9 +33,9 @@ class LycIrq { unsigned char lycReg_; unsigned char statReg_; bool cgb_; - + void regChange(unsigned statReg, unsigned lycReg, const LyCounter &lyCounter, unsigned long cc); - + public: LycIrq(); void doEvent(unsigned char *ifreg, const LyCounter &lyCounter); @@ -45,11 +45,11 @@ public: void setCgb(const bool cgb) { cgb_ = cgb; } void lcdReset(); void reschedule(const LyCounter & lyCounter, unsigned long cc); - + void statRegChange(unsigned statReg, const LyCounter &lyCounter, unsigned long cc) { regChange(statReg, lycRegSrc_, lyCounter, cc); } - + void lycRegChange(unsigned lycReg, const LyCounter &lyCounter, unsigned long cc) { regChange(statRegSrc_, lycReg, lyCounter, cc); } diff --git a/libgambatte/src/video/next_m0_time.h b/libgambatte/src/video/next_m0_time.h index 0a91c4d330..5937b0734a 100644 --- a/libgambatte/src/video/next_m0_time.h +++ b/libgambatte/src/video/next_m0_time.h @@ -7,7 +7,7 @@ namespace gambatte { class NextM0Time { unsigned predictedNextM0Time_; - + public: NextM0Time() : predictedNextM0Time_(0) {} void predictNextM0Time(const class PPU &v); diff --git a/libgambatte/src/video/ppu.cpp b/libgambatte/src/video/ppu.cpp index 70d7db9e54..d5c5e9607a 100644 --- a/libgambatte/src/video/ppu.cpp +++ b/libgambatte/src/video/ppu.cpp @@ -78,7 +78,7 @@ namespace M3Loop { DECLARE_FUNC(4, 0x84); DECLARE_FUNC(5, 0x85); } - + namespace LoadSprites { DECLARE_FUNC(0, 0x88); DECLARE_FUNC(1, 0x89); @@ -87,7 +87,7 @@ namespace M3Loop { DECLARE_FUNC(4, 0x8C); DECLARE_FUNC(5, 0x8D); } - + namespace StartWindowDraw { DECLARE_FUNC(0, 0x90); DECLARE_FUNC(1, 0x91); @@ -129,25 +129,25 @@ namespace M2 { nextCall(m3StartLineCycle(p.cgb), M3Start::f0_, p); } } - + namespace LyNon0 { static void f0(PPUPriv &p) { p.weMaster |= (p.lcdc & 0x20) && p.lyCounter.ly() == p.wy; nextCall(weMasterCheckAfterLyIncLineCycle(p.cgb) - weMasterCheckPriorToLyIncLineCycle(p.cgb), f1_, p); } - + static void f1(PPUPriv &p) { p.weMaster |= (p.lcdc & 0x20) && p.lyCounter.ly() + 1 == p.wy; nextCall(456 - weMasterCheckAfterLyIncLineCycle(p.cgb) + m3StartLineCycle(p.cgb), M3Start::f0_, p); } } - + /*struct SpriteLess { bool operator()(const Sprite lhs, const Sprite rhs) const { return lhs.spx < rhs.spx; } }; - + static void f0(PPUPriv &p) { std::memset(&p.spLut, 0, sizeof p.spLut); p.reg0 = 0; @@ -155,37 +155,37 @@ namespace M2 { p.nextCallPtr = &f1_; f1(p); } - + static void f1(PPUPriv &p) { int cycles = p.cycles; unsigned oampos = p.reg0; unsigned nextSprite = p.nextSprite; const unsigned nly = (p.lyCounter.ly() + 1 == 154 ? 0 : p.lyCounter.ly() + 1) + ((p.lyCounter.time()-(p.now-p.cycles)) <= 4); const bool ls = p.spriteMapper.largeSpritesSource(); - + do { const unsigned spy = p.spriteMapper.oamram()[oampos ]; const unsigned spx = p.spriteMapper.oamram()[oampos+1]; const unsigned ydiff = spy - nly; - + if (ls ? ydiff < 16u : ydiff - 8u < 8u) { p.spriteList[nextSprite].spx = spx; p.spriteList[nextSprite].line = 15u - ydiff; p.spriteList[nextSprite].oampos = oampos; - + if (++nextSprite == 10) { cycles -= (0xA0 - 4 - oampos) >> 1; oampos = 0xA0 - 4; } } - + oampos += 4; } while ((cycles-=2) >= 0 && oampos != 0xA0); - + p.reg0 = oampos; p.nextSprite = nextSprite; p.cycles = cycles; - + if (oampos == 0xA0) { insertionSort(p.spriteList, p.spriteList + nextSprite, SpriteLess()); p.spriteList[nextSprite].spx = 0xFF; @@ -198,23 +198,23 @@ namespace M2 { namespace M3Start { static void f0(PPUPriv &p) { p.xpos = 0; - + if (p.winDrawState & p.lcdc >> 5 & WIN_DRAW_START) { p.winDrawState = WIN_DRAW_STARTED; p.wscx = 8 + (p.scx & 7); ++p.winYPos; } else p.winDrawState = 0; - + p.nextCallPtr = &f1_; f1(p); } - + static void f1(PPUPriv &p) { while (p.xpos < MAX_M3START_CYCLES) { if ((p.xpos & 7) == (p.scx & 7)) break; - + switch (p.xpos & 7) { case 0: if ((p.winDrawState & WIN_DRAW_STARTED) && (p.layersMask & LAYER_MASK_WINDOW)) { @@ -229,31 +229,31 @@ namespace M3Start { case 2: { const unsigned yoffset = ((p.winDrawState & WIN_DRAW_STARTED) && (p.layersMask & LAYER_MASK_WINDOW)) ? p.winYPos : p.scy + p.lyCounter.ly(); - + p.reg0 = p.vram[0x1000 + (p.nattrib << 10 & 0x2000) - ((p.reg1 * 32 | p.lcdc << 8) & 0x1000) + p.reg1 * 16 + ((-(p.nattrib >> 6 & 1) ^ yoffset) & 7) * 2]; } - + break; case 4: { const unsigned yoffset = ((p.winDrawState & WIN_DRAW_STARTED) && (p.layersMask & LAYER_MASK_WINDOW)) ? p.winYPos : p.scy + p.lyCounter.ly(); const unsigned r1 = p.vram[0x1000 + (p.nattrib << 10 & 0x2000) - ((p.reg1 * 32 | p.lcdc << 8) & 0x1000) + p.reg1 * 16 + ((-(p.nattrib >> 6 & 1) ^ yoffset) & 7) * 2 + 1]; - + p.ntileword = (expand_lut + (p.nattrib << 3 & 0x100))[p.reg0] + (expand_lut + (p.nattrib << 3 & 0x100))[r1 ] * 2; } - + break; } - + ++p.xpos; - + if (--p.cycles < 0) return; } - + { const unsigned ly = p.lyCounter.ly(); const unsigned numSprites = p.spriteMapper.numSprites(ly); @@ -269,14 +269,14 @@ namespace M3Start { p.spriteList[i].oampos = pos * 2; p.spwordList[i] = 0; } - + p.spriteList[numSprites].spx = 0xFF; p.nextSprite = 0; } - + p.xpos = 0; p.endx = 8 - (p.scx & 7); - + static const PPUState *const flut[8] = { &M3Loop::Tile::f0_, &M3Loop::Tile::f1_, @@ -287,7 +287,7 @@ namespace M3Start { &M3Loop::Tile::f5_, &M3Loop::Tile::f5_ }; - + nextCall(1-p.cgb, *flut[p.scx & 7], p); } } @@ -298,66 +298,66 @@ namespace M3Loop { const unsigned tileIndexSign = ~p.lcdc << 3 & 0x80; const unsigned char *const tileDataLine = p.vram + tileIndexSign * 32 + tileline * 2; int xpos = p.xpos; - + do { int nextSprite = p.nextSprite; - - if (static_cast(p.spriteList[nextSprite].spx) < xpos + 8) { + + if (int(p.spriteList[nextSprite].spx) < xpos + 8) { int cycles = p.cycles - 8; - + if (p.lcdc & 2) { - cycles -= std::max(11 - (static_cast(p.spriteList[nextSprite].spx) - xpos), 6); - - for (unsigned i = nextSprite + 1; static_cast(p.spriteList[i].spx) < xpos + 8; ++i) + cycles -= std::max(11 - (int(p.spriteList[nextSprite].spx) - xpos), 6); + + for (unsigned i = nextSprite + 1; int(p.spriteList[i].spx) < xpos + 8; ++i) cycles -= 6; - + if (cycles < 0) break; - + p.cycles = cycles; - + do { unsigned reg0, reg1 = p.spriteMapper.oamram()[p.spriteList[nextSprite].oampos + 2] * 16; const unsigned attrib = p.spriteMapper.oamram()[p.spriteList[nextSprite].oampos + 3]; - + { const unsigned spline = (attrib & 0x40 ? p.spriteList[nextSprite].line ^ 15 : p.spriteList[nextSprite].line) * 2; reg0 = p.vram[(p.lcdc & 4 ? (reg1 & ~16) | spline : reg1 | (spline & ~16)) ]; reg1 = p.vram[(p.lcdc & 4 ? (reg1 & ~16) | spline : reg1 | (spline & ~16)) + 1]; } - + p.spwordList[nextSprite] = expand_lut[reg0 + (attrib << 3 & 0x100)] + expand_lut[reg1 + (attrib << 3 & 0x100)] * 2; p.spriteList[nextSprite].attrib = attrib; ++nextSprite; - } while (static_cast(p.spriteList[nextSprite].spx) < xpos + 8); + } while (int(p.spriteList[nextSprite].spx) < xpos + 8); } else { if (cycles < 0) break; - + p.cycles = cycles; - + do { ++nextSprite; - } while (static_cast(p.spriteList[nextSprite].spx) < xpos + 8); + } while (int(p.spriteList[nextSprite].spx) < xpos + 8); } - + p.nextSprite = nextSprite; - } else if (nextSprite-1 < 0 || static_cast(p.spriteList[nextSprite-1].spx) <= xpos - 8) { + } else if (nextSprite-1 < 0 || int(p.spriteList[nextSprite-1].spx) <= xpos - 8) { if (!(p.cycles & ~7)) break; - - int n = (( xend + 7 < static_cast(p.spriteList[nextSprite].spx) - ? xend + 7 : static_cast(p.spriteList[nextSprite].spx)) - xpos) & ~7; + + int n = (( xend + 7 < int(p.spriteList[nextSprite].spx) + ? xend + 7 : int(p.spriteList[nextSprite].spx)) - xpos) & ~7; n = (p.cycles & ~7) < n ? p.cycles & ~7 : n; p.cycles -= n; - + unsigned ntileword = p.ntileword; uint_least32_t * dst = dbufline + xpos - 8; uint_least32_t *const dstend = dst + n; xpos += n; - + if (!(p.lcdc & 1)) { do { *dst++ = p.bgPalette[0]; } while (dst != dstend); tileMapXpos += n >> 3; @@ -375,28 +375,28 @@ namespace M3Loop { dst[6] = p.bgPalette[(ntileword & 0x3000) >> 12]; dst[7] = p.bgPalette[ ntileword >> 14]; dst += 8; - + unsigned const tno = tileMapLine[tileMapXpos & 0x1F]; tileMapXpos = (tileMapXpos & 0x1F) + 1; ntileword = expand_lut[(tileDataLine + tno * 16 - (tno & tileIndexSign) * 32)[0]] + expand_lut[(tileDataLine + tno * 16 - (tno & tileIndexSign) * 32)[1]] * 2; } while (dst != dstend); - + p.ntileword = ntileword; continue; } else { int cycles = p.cycles - 8; - + if (cycles < 0) break; - + p.cycles = cycles; } - + { uint_least32_t *const dst = dbufline + (xpos - 8); const unsigned tileword = -(p.lcdc & 1U) & p.ntileword; - + dst[0] = p.bgPalette[ tileword & 0x0003 ]; dst[1] = p.bgPalette[(tileword & 0x000C) >> 2]; dst[2] = p.bgPalette[(tileword & 0x0030) >> 4]; @@ -405,21 +405,21 @@ namespace M3Loop { dst[5] = p.bgPalette[(tileword & 0x0C00) >> 10]; dst[6] = p.bgPalette[(tileword & 0x3000) >> 12]; dst[7] = p.bgPalette[ tileword >> 14]; - + int i = nextSprite - 1; - + if(p.layersMask & LAYER_MASK_OBJ) { if (!(p.lcdc & 2)) { do { - const int pos = static_cast(p.spriteList[i].spx) - xpos; + const int pos = int(p.spriteList[i].spx) - xpos; p.spwordList[i] >>= pos * 2 >= 0 ? 16 - pos * 2 : 16 + pos * 2; --i; - } while (i >= 0 && static_cast(p.spriteList[i].spx) > xpos - 8); + } while (i >= 0 && int(p.spriteList[i].spx) > xpos - 8); } else { do { int n; - int pos = static_cast(p.spriteList[i].spx) - xpos; + int pos = int(p.spriteList[i].spx) - xpos; if (pos < 0) { n = pos + 8; @@ -471,24 +471,24 @@ namespace M3Loop { p.spwordList[i] = spword; --i; - } while (i >= 0 && static_cast(p.spriteList[i].spx) > xpos - 8); + } while (i >= 0 && int(p.spriteList[i].spx) > xpos - 8); } } } - + { unsigned const tno = tileMapLine[tileMapXpos & 0x1F]; tileMapXpos = (tileMapXpos & 0x1F) + 1; p.ntileword = expand_lut[(tileDataLine + tno * 16 - (tno & tileIndexSign) * 32)[0]] + expand_lut[(tileDataLine + tno * 16 - (tno & tileIndexSign) * 32)[1]] * 2; } - + xpos = xpos + 8; } while (xpos < xend); - + p.xpos = xpos; } - + static void doFullTilesUnrolledCgb(PPUPriv &p, const int xend, uint_least32_t *const dbufline, const unsigned char *const tileMapLine, const unsigned tileline, unsigned tileMapXpos) { int xpos = p.xpos; @@ -501,26 +501,26 @@ namespace M3Loop { dbufline[i] = p.bgPalette[0]; //guessing? return; } - + do { int nextSprite = p.nextSprite; - - if (static_cast(p.spriteList[nextSprite].spx) < xpos + 8) { + + if (int(p.spriteList[nextSprite].spx) < xpos + 8) { int cycles = p.cycles - 8; - cycles -= std::max(11 - (static_cast(p.spriteList[nextSprite].spx) - xpos), 6); - - for (unsigned i = nextSprite + 1; static_cast(p.spriteList[i].spx) < xpos + 8; ++i) + cycles -= std::max(11 - (int(p.spriteList[nextSprite].spx) - xpos), 6); + + for (unsigned i = nextSprite + 1; int(p.spriteList[i].spx) < xpos + 8; ++i) cycles -= 6; - + if (cycles < 0) break; - + p.cycles = cycles; - + do { unsigned reg0, reg1 = p.spriteMapper.oamram()[p.spriteList[nextSprite].oampos + 2] * 16; const unsigned attrib = p.spriteMapper.oamram()[p.spriteList[nextSprite].oampos + 3]; - + { const unsigned spline = (attrib & 0x40 ? p.spriteList[nextSprite].line ^ 15 : p.spriteList[nextSprite].line) * 2; @@ -529,29 +529,29 @@ namespace M3Loop { reg1 = vram[(attrib << 10 & 0x2000) + (p.lcdc & 4 ? (reg1 & ~16) | spline : reg1 | (spline & ~16)) + 1]; } - + p.spwordList[nextSprite] = expand_lut[reg0 + (attrib << 3 & 0x100)] + expand_lut[reg1 + (attrib << 3 & 0x100)] * 2; p.spriteList[nextSprite].attrib = attrib; ++nextSprite; - } while (static_cast(p.spriteList[nextSprite].spx) < xpos + 8); - + } while (int(p.spriteList[nextSprite].spx) < xpos + 8); + p.nextSprite = nextSprite; - } else if (nextSprite-1 < 0 || static_cast(p.spriteList[nextSprite-1].spx) <= xpos - 8) { + } else if (nextSprite-1 < 0 || int(p.spriteList[nextSprite-1].spx) <= xpos - 8) { if (!(p.cycles & ~7)) break; - - int n = (( xend + 7 < static_cast(p.spriteList[nextSprite].spx) - ? xend + 7 : static_cast(p.spriteList[nextSprite].spx)) - xpos) & ~7; + + int n = (( xend + 7 < int(p.spriteList[nextSprite].spx) + ? xend + 7 : int(p.spriteList[nextSprite].spx)) - xpos) & ~7; n = (p.cycles & ~7) < n ? p.cycles & ~7 : n; p.cycles -= n; - + unsigned ntileword = p.ntileword; unsigned nattrib = p.nattrib; uint_least32_t * dst = dbufline + xpos - 8; uint_least32_t *const dstend = dst + n; xpos += n; - + do { const unsigned long *const bgPalette = p.bgPalette + (nattrib & 7) * 4; dst[0] = bgPalette[ ntileword & 0x0003 ]; @@ -563,36 +563,36 @@ namespace M3Loop { dst[6] = bgPalette[(ntileword & 0x3000) >> 12]; dst[7] = bgPalette[ ntileword >> 14]; dst += 8; - + unsigned const tno = tileMapLine[ tileMapXpos & 0x1F ]; nattrib = tileMapLine[(tileMapXpos & 0x1F) + 0x2000]; tileMapXpos = (tileMapXpos & 0x1F) + 1; - + unsigned const tdo = tdoffset & ~(tno << 5); unsigned char const *const td = vram + tno * 16 + (nattrib & 0x40 ? tdo ^ 14 : tdo) + (nattrib << 10 & 0x2000); unsigned short const *const explut = expand_lut + (nattrib << 3 & 0x100); ntileword = explut[td[0]] + explut[td[1]] * 2; } while (dst != dstend); - + p.ntileword = ntileword; p.nattrib = nattrib; continue; } else { int cycles = p.cycles - 8; - + if (cycles < 0) break; - + p.cycles = cycles; } - + { uint_least32_t *const dst = dbufline + (xpos - 8); const unsigned tileword = p.ntileword; const unsigned attrib = p.nattrib; const unsigned long *const bgPalette = p.bgPalette + (attrib & 7) * 4; - + dst[0] = bgPalette[ tileword & 0x0003 ]; dst[1] = bgPalette[(tileword & 0x000C) >> 2]; dst[2] = bgPalette[(tileword & 0x0030) >> 4]; @@ -601,24 +601,24 @@ namespace M3Loop { dst[5] = bgPalette[(tileword & 0x0C00) >> 10]; dst[6] = bgPalette[(tileword & 0x3000) >> 12]; dst[7] = bgPalette[ tileword >> 14]; - + int i = nextSprite - 1; if(p.layersMask & LAYER_MASK_OBJ) { if (!(p.lcdc & 2)) { do { - const int pos = static_cast(p.spriteList[i].spx) - xpos; + const int pos = int(p.spriteList[i].spx) - xpos; p.spwordList[i] >>= pos * 2 >= 0 ? 16 - pos * 2 : 16 + pos * 2; --i; - } while (i >= 0 && static_cast(p.spriteList[i].spx) > xpos - 8); + } while (i >= 0 && int(p.spriteList[i].spx) > xpos - 8); } else { unsigned char idtab[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; const unsigned bgenmask = p.lcdc << 7 & 0x80; do { int n; - int pos = static_cast(p.spriteList[i].spx) - xpos; + int pos = int(p.spriteList[i].spx) - xpos; if (pos < 0) { n = pos + 8; @@ -698,7 +698,7 @@ namespace M3Loop { p.spwordList[i] = spword; --i; - } while (i >= 0 && static_cast(p.spriteList[i].spx) > xpos - 8); + } while (i >= 0 && int(p.spriteList[i].spx) > xpos - 8); } } } @@ -707,7 +707,7 @@ namespace M3Loop { unsigned const tno = tileMapLine[ tileMapXpos & 0x1F ]; unsigned const nattrib = tileMapLine[(tileMapXpos & 0x1F) + 0x2000]; tileMapXpos = (tileMapXpos & 0x1F) + 1; - + unsigned const tdo = tdoffset & ~(tno << 5); unsigned char const *const td = vram + tno * 16 + (nattrib & 0x40 ? tdo ^ 14 : tdo) + (nattrib << 10 & 0x2000); @@ -715,25 +715,25 @@ namespace M3Loop { p.ntileword = explut[td[0]] + explut[td[1]] * 2; p.nattrib = nattrib; } - + xpos = xpos + 8; } while (xpos < xend); - + p.xpos = xpos; } - + static void doFullTilesUnrolled(PPUPriv &p) { int xpos = p.xpos; - const int xend = static_cast(p.wx) < xpos || p.wx >= 168 ? 161 : static_cast(p.wx) - 7; - + const int xend = int(p.wx) < xpos || p.wx >= 168 ? 161 : int(p.wx) - 7; + if (xpos >= xend) return; - + uint_least32_t *const dbufline = p.framebuf.fbline(); const unsigned char *tileMapLine; unsigned tileline; unsigned tileMapXpos; - + if ((p.winDrawState & WIN_DRAW_STARTED) && (p.layersMask & LAYER_MASK_WINDOW)) { tileMapLine = p.vram + (p.lcdc << 4 & 0x400) + (p.winYPos & 0xF8) * 4 + 0x1800; tileMapXpos = (xpos + p.wscx) >> 3; @@ -743,75 +743,75 @@ namespace M3Loop { tileMapXpos = (p.scx + xpos + 1 - p.cgb) >> 3; tileline = (p.scy + p.lyCounter.ly()) & 7; } - + if (xpos < 8) { uint_least32_t prebuf[16]; - + if (p.cgb) { doFullTilesUnrolledCgb(p, xend < 8 ? xend : 8, prebuf + (8 - xpos), tileMapLine, tileline, tileMapXpos); } else doFullTilesUnrolledDmg(p, xend < 8 ? xend : 8, prebuf + (8 - xpos), tileMapLine, tileline, tileMapXpos); - + const int newxpos = p.xpos; - + if (newxpos > 8) { std::memcpy(dbufline, prebuf + (8 - xpos), (newxpos - 8) * sizeof *dbufline); } else if (newxpos < 8) return; - + if (newxpos >= xend) return; - + tileMapXpos += (newxpos - xpos) >> 3; } - + if (p.cgb) { doFullTilesUnrolledCgb(p, xend, dbufline, tileMapLine, tileline, tileMapXpos); } else doFullTilesUnrolledDmg(p, xend, dbufline, tileMapLine, tileline, tileMapXpos); } - + static void plotPixel(PPUPriv &p) { //ZING! const int xpos = p.xpos; const unsigned tileword = p.tileword; uint_least32_t *const fbline = p.framebuf.fbline(); - - if (static_cast(p.wx) == xpos && (p.weMaster || (p.wy2 == p.lyCounter.ly() && (p.lcdc & 0x20))) && xpos < 167) { + + if (int(p.wx) == xpos && (p.weMaster || (p.wy2 == p.lyCounter.ly() && (p.lcdc & 0x20))) && xpos < 167) { if (p.winDrawState == 0 && (p.lcdc & 0x20)) { p.winDrawState = WIN_DRAW_START | WIN_DRAW_STARTED; ++p.winYPos; } else if (!p.cgb && (p.winDrawState == 0 || xpos == 166)) p.winDrawState |= WIN_DRAW_START; } - + const unsigned twdata = tileword & ((p.lcdc & 1) | p.cgb) * 3; unsigned long pixel = p.bgPalette[twdata + (p.attrib & 7) * 4]; - int i = static_cast(p.nextSprite) - 1; + int i = int(p.nextSprite) - 1; if(!(p.layersMask & LAYER_MASK_BG)) { pixel = p.bgPalette[0]; //guessing? clobber the tile that was read } - - if (i >= 0 && static_cast(p.spriteList[i].spx) > xpos - 8) { + + if (i >= 0 && int(p.spriteList[i].spx) > xpos - 8) { unsigned spdata = 0; unsigned attrib = 0; - + if (p.cgb) { unsigned minId = 0xFF; - + do { if ((p.spwordList[i] & 3) && p.spriteList[i].oampos < minId) { spdata = p.spwordList[i] & 3; attrib = p.spriteList[i].attrib; minId = p.spriteList[i].oampos; } - + p.spwordList[i] >>= 2; --i; - } while (i >= 0 && static_cast(p.spriteList[i].spx) > xpos - 8); - + } while (i >= 0 && int(p.spriteList[i].spx) > xpos - 8); + if(p.layersMask & LAYER_MASK_OBJ) { if (spdata && (p.lcdc & 2) && (!((attrib | p.attrib) & 0x80) || !twdata || !(p.lcdc & 1))) @@ -823,11 +823,11 @@ namespace M3Loop { spdata = p.spwordList[i] & 3; attrib = p.spriteList[i].attrib; } - + p.spwordList[i] >>= 2; --i; - } while (i >= 0 && static_cast(p.spriteList[i].spx) > xpos - 8); - + } while (i >= 0 && int(p.spriteList[i].spx) > xpos - 8); + if(p.layersMask & LAYER_MASK_OBJ) { if (spdata && (p.lcdc & 2) && (!(attrib & 0x80) || !twdata)) @@ -835,27 +835,27 @@ namespace M3Loop { } } } - + if (xpos - 8 >= 0) fbline[xpos - 8] = pixel; - + p.xpos = xpos + 1; p.tileword = tileword >> 2; } - + static void plotPixelIfNoSprite(PPUPriv &p) { if (p.spriteList[p.nextSprite].spx == p.xpos) { if (!((p.lcdc & 2) | p.cgb)) { do { ++p.nextSprite; } while (p.spriteList[p.nextSprite].spx == p.xpos); - + plotPixel(p); } } else plotPixel(p); } - + static unsigned long nextM2Time(const PPUPriv &p) { unsigned long nextm2 = p.lyCounter.isDoubleSpeed() ? p.lyCounter.time() + (weMasterCheckPriorToLyIncLineCycle(true ) + M2_DS_OFFSET) * 2 - 456 * 2 @@ -863,62 +863,62 @@ namespace M3Loop { if (p.lyCounter.ly() == 143) nextm2 += (456 * 10 + 456 - weMasterCheckPriorToLyIncLineCycle(p.cgb)) << p.lyCounter.isDoubleSpeed(); - + return nextm2; } - + static void xpos168(PPUPriv &p) { p.lastM0Time = p.now - (p.cycles << p.lyCounter.isDoubleSpeed()); - + const unsigned long nextm2 = nextM2Time(p); - + p.cycles = p.now >= nextm2 - ? (static_cast(p.now - nextm2) >> p.lyCounter.isDoubleSpeed()) - : -(static_cast(nextm2 - p.now) >> p.lyCounter.isDoubleSpeed()); - + ? (long(p.now - nextm2) >> p.lyCounter.isDoubleSpeed()) + : -(long(nextm2 - p.now) >> p.lyCounter.isDoubleSpeed()); + nextCall(0, p.lyCounter.ly() == 143 ? M2::Ly0::f0_ : M2::LyNon0::f0_, p); } - + static bool handleWinDrawStartReq(const PPUPriv &p, const int xpos, unsigned char &winDrawState) { const bool startWinDraw = (xpos < 167 || p.cgb) && (winDrawState &= WIN_DRAW_STARTED); - + if (!(p.lcdc & 0x20)) winDrawState &= ~WIN_DRAW_STARTED; - + return startWinDraw; } - + static bool handleWinDrawStartReq(PPUPriv &p) { return handleWinDrawStartReq(p, p.xpos, p.winDrawState); } - + namespace StartWindowDraw { static void inc(const PPUState &nextf, PPUPriv &p) { if (!(p.lcdc & 0x20) && p.cgb) { plotPixelIfNoSprite(p); - + if (p.xpos == p.endx) { if (p.xpos < 168) { nextCall(1,Tile::f0_,p); } else xpos168(p); - + return; } } - + nextCall(1,nextf,p); } - + static void f0(PPUPriv &p) { if (p.xpos == p.endx) { p.tileword = p.ntileword; p.attrib = p.nattrib; p.endx = p.xpos < 160 ? p.xpos + 8 : 168; } - + p.wscx = 8 - p.xpos; - + if ((p.winDrawState & WIN_DRAW_STARTED) && (p.layersMask & LAYER_MASK_WINDOW)) { p.reg1 = p.vram[(p.lcdc << 4 & 0x400) + (p.winYPos & 0xF8) * 4 + 0x1800]; p.nattrib = p.vram[(p.lcdc << 4 & 0x400) + (p.winYPos & 0xF8) * 4 + 0x3800]; @@ -926,40 +926,40 @@ namespace M3Loop { p.reg1 = p.vram[(p.lcdc << 7 & 0x400) + ((p.scy + p.lyCounter.ly()) & 0xF8) * 4 + 0x1800]; p.nattrib = p.vram[(p.lcdc << 7 & 0x400) + ((p.scy + p.lyCounter.ly()) & 0xF8) * 4 + 0x3800]; } - + inc(f1_,p); } - + static void f1(PPUPriv &p) { inc(f2_,p); } - + static void f2(PPUPriv &p) { const unsigned yoffset = ((p.winDrawState & WIN_DRAW_STARTED) && (p.layersMask & LAYER_MASK_WINDOW)) ? p.winYPos : p.scy + p.lyCounter.ly(); - + p.reg0 = p.vram[0x1000 + (p.nattrib << 10 & 0x2000) - ((p.reg1 * 32 | p.lcdc << 8) & 0x1000) + p.reg1 * 16 + ((-(p.nattrib >> 6 & 1) ^ yoffset) & 7) * 2]; inc(f3_,p); } - + static void f3(PPUPriv &p) { inc(f4_,p); } - + static void f4(PPUPriv &p) { const unsigned yoffset = ((p.winDrawState & WIN_DRAW_STARTED) && (p.layersMask & LAYER_MASK_WINDOW)) ? p.winYPos : p.scy + p.lyCounter.ly(); const unsigned r1 = p.vram[0x1000 + (p.nattrib << 10 & 0x2000) - ((p.reg1 * 32 | p.lcdc << 8) & 0x1000) + p.reg1 * 16 + ((-(p.nattrib >> 6 & 1) ^ yoffset) & 7) * 2 + 1]; - + p.ntileword = (expand_lut + (p.nattrib << 3 & 0x100))[p.reg0] + (expand_lut + (p.nattrib << 3 & 0x100))[r1 ] * 2; - + inc(f5_,p); } - + static void f5(PPUPriv &p) { inc(Tile::f0_,p); } @@ -968,7 +968,7 @@ namespace M3Loop { namespace LoadSprites { static void inc(const PPUState &nextf, PPUPriv &p) { plotPixelIfNoSprite(p); - + if (p.xpos == p.endx) { if (p.xpos < 168) { nextCall(1,Tile::f0_,p); @@ -977,68 +977,68 @@ namespace M3Loop { } else nextCall(1,nextf,p); } - + static void f0(PPUPriv &p) { p.reg1 = p.spriteMapper.oamram()[p.spriteList[p.currentSprite].oampos + 2]; nextCall(1,f1_,p); } - + static void f1(PPUPriv &p) { if ((p.winDrawState & WIN_DRAW_START) && handleWinDrawStartReq(p)) return StartWindowDraw::f0(p); - + p.spriteList[p.currentSprite].attrib = p.spriteMapper.oamram()[p.spriteList[p.currentSprite].oampos + 3]; inc(f2_,p); } - + static void f2(PPUPriv &p) { if ((p.winDrawState & WIN_DRAW_START) && handleWinDrawStartReq(p)) return StartWindowDraw::f0(p); - + const unsigned spline = ((p.spriteList[p.currentSprite].attrib & 0x40) ? p.spriteList[p.currentSprite].line ^ 15 : p.spriteList[p.currentSprite].line) * 2; p.reg0 = p.vram[(p.spriteList[p.currentSprite].attrib << 10 & p.cgb * 0x2000) + ((p.lcdc & 4) ? (p.reg1 * 16 & ~16) | spline : p.reg1 * 16 | (spline & ~16))]; inc(f3_,p); } - + static void f3(PPUPriv &p) { if ((p.winDrawState & WIN_DRAW_START) && handleWinDrawStartReq(p)) return StartWindowDraw::f0(p); - + inc(f4_,p); } - + static void f4(PPUPriv &p) { if ((p.winDrawState & WIN_DRAW_START) && handleWinDrawStartReq(p)) return StartWindowDraw::f0(p); - + const unsigned spline = ((p.spriteList[p.currentSprite].attrib & 0x40) ? p.spriteList[p.currentSprite].line ^ 15 : p.spriteList[p.currentSprite].line) * 2; p.reg1 = p.vram[(p.spriteList[p.currentSprite].attrib << 10 & p.cgb * 0x2000) + ((p.lcdc & 4) ? (p.reg1 * 16 & ~16) | spline : p.reg1 * 16 | (spline & ~16)) + 1]; inc(f5_,p); } - + static void f5(PPUPriv &p) { if ((p.winDrawState & WIN_DRAW_START) && handleWinDrawStartReq(p)) return StartWindowDraw::f0(p); - + plotPixelIfNoSprite(p); - + unsigned entry = p.currentSprite; - + if (entry == p.nextSprite) { ++p.nextSprite; } else { entry = p.nextSprite - 1; p.spriteList[entry] = p.spriteList[p.currentSprite]; } - + p.spwordList[entry] = expand_lut[p.reg0 + (p.spriteList[entry].attrib << 3 & 0x100)] + expand_lut[p.reg1 + (p.spriteList[entry].attrib << 3 & 0x100)] * 2; p.spriteList[entry].spx = p.xpos; - + if (p.xpos == p.endx) { if (p.xpos < 168) { nextCall(1,Tile::f0_,p); @@ -1050,32 +1050,32 @@ namespace M3Loop { } } }; - + namespace Tile { static void inc(const PPUState &nextf, PPUPriv &p) { plotPixelIfNoSprite(p); - + if (p.xpos == 168) { xpos168(p); } else nextCall(1,nextf,p); } - + static void f0(PPUPriv &p) { if ((p.winDrawState & WIN_DRAW_START) && handleWinDrawStartReq(p)) return StartWindowDraw::f0(p); - + doFullTilesUnrolled(p); - + if (p.xpos == 168) { ++p.cycles; return xpos168(p); } - + p.tileword = p.ntileword; p.attrib = p.nattrib; p.endx = p.xpos < 160 ? p.xpos + 8 : 168; - + if ((p.winDrawState & WIN_DRAW_STARTED) && (p.layersMask & LAYER_MASK_WINDOW) ) { p.reg1 = p.vram[(p.lcdc << 4 & 0x400) + (p.winYPos & 0xF8) * 4 + ((p.xpos + p.wscx) >> 3 & 0x1F) + 0x1800]; @@ -1087,84 +1087,84 @@ namespace M3Loop { p.nattrib = p.vram[((p.lcdc << 7 | (p.scx + p.xpos + 1 - p.cgb) >> 3) & 0x41F) + ((p.scy + p.lyCounter.ly()) & 0xF8) * 4 + 0x3800]; } - + inc(f1_,p); } - + static void f1(PPUPriv &p) { if ((p.winDrawState & WIN_DRAW_START) && handleWinDrawStartReq(p)) return StartWindowDraw::f0(p); - + inc(f2_,p); } - + static void f2(PPUPriv &p) { if ((p.winDrawState & WIN_DRAW_START) && handleWinDrawStartReq(p)) return StartWindowDraw::f0(p); - + const unsigned yoffset = ((p.winDrawState & WIN_DRAW_STARTED) && (p.layersMask & LAYER_MASK_WINDOW)) ? p.winYPos : p.scy + p.lyCounter.ly(); - + p.reg0 = p.vram[0x1000 + (p.nattrib << 10 & 0x2000) - ((p.reg1 * 32 | p.lcdc << 8) & 0x1000) + p.reg1 * 16 + ((-(p.nattrib >> 6 & 1) ^ yoffset) & 7) * 2]; inc(f3_,p); } - + static void f3(PPUPriv &p) { if ((p.winDrawState & WIN_DRAW_START) && handleWinDrawStartReq(p)) return StartWindowDraw::f0(p); - + inc(f4_,p); } - + static void f4(PPUPriv &p) { if ((p.winDrawState & WIN_DRAW_START) && handleWinDrawStartReq(p)) return StartWindowDraw::f0(p); - + const unsigned yoffset = ((p.winDrawState & WIN_DRAW_STARTED) && (p.layersMask & LAYER_MASK_WINDOW)) ? p.winYPos : p.scy + p.lyCounter.ly(); const unsigned r1 = p.vram[0x1000 + (p.nattrib << 10 & 0x2000) - ((p.reg1 * 32 | p.lcdc << 8) & 0x1000) + p.reg1 * 16 + ((-(p.nattrib >> 6 & 1) ^ yoffset) & 7) * 2 + 1]; - + p.ntileword = (expand_lut + (p.nattrib << 3 & 0x100))[p.reg0] + (expand_lut + (p.nattrib << 3 & 0x100))[r1 ] * 2; - + plotPixelIfNoSprite(p); - + if (p.xpos == 168) { xpos168(p); } else nextCall(1,f5_,p); } - + static void f5(PPUPriv &p) { int endx = p.endx; p.nextCallPtr = &f5_; - + do { if ((p.winDrawState & WIN_DRAW_START) && handleWinDrawStartReq(p)) return StartWindowDraw::f0(p); - + if (p.spriteList[p.nextSprite].spx == p.xpos) { if ((p.lcdc & 2) | p.cgb) { p.currentSprite = p.nextSprite; return LoadSprites::f0(p); } - + do { ++p.nextSprite; } while (p.spriteList[p.nextSprite].spx == p.xpos); } - + plotPixel(p); - + if (p.xpos == endx) { if (endx < 168) { nextCall(1,f0_,p); } else xpos168(p); - + return; } } while (--p.cycles >= 0); @@ -1177,7 +1177,7 @@ namespace M2 { namespace Ly0 { static unsigned predictCyclesUntilXpos_f0(const PPUPriv &p, unsigned winDrawState, int targetxpos, unsigned cycles); } - + namespace LyNon0 { static unsigned predictCyclesUntilXpos_f0(const PPUPriv &p, unsigned winDrawState, int targetxpos, unsigned cycles); } @@ -1187,19 +1187,19 @@ namespace M3Loop { static unsigned predictCyclesUntilXposNextLine(const PPUPriv &p, unsigned winDrawState, const int targetx) { if (p.wx == 166 && !p.cgb && p.xpos < 167 && (p.weMaster || (p.wy2 == p.lyCounter.ly() && (p.lcdc & 0x20)))) winDrawState = WIN_DRAW_START | (WIN_DRAW_STARTED & p.lcdc >> 4); - + const unsigned cycles = (nextM2Time(p) - p.now) >> p.lyCounter.isDoubleSpeed(); return p.lyCounter.ly() == 143 ? M2::Ly0::predictCyclesUntilXpos_f0(p, winDrawState, targetx, cycles) : M2::LyNon0::predictCyclesUntilXpos_f0(p, winDrawState, targetx, cycles); } - + namespace StartWindowDraw { static unsigned predictCyclesUntilXpos_fn(const PPUPriv &p, int xpos, int endx, unsigned ly, unsigned nextSprite, bool weMaster, unsigned winDrawState, int fno, int targetx, unsigned cycles); } - + namespace Tile { static const unsigned char* addSpriteCycles(const unsigned char *nextSprite, const unsigned char *spriteEnd, const unsigned char *const spxOf, const unsigned maxSpx, @@ -1223,7 +1223,7 @@ namespace M3Loop { return nextSprite; } - + static unsigned predictCyclesUntilXpos_fn(const PPUPriv &p, const int xpos, const int endx, const unsigned ly, const unsigned nextSprite, const bool weMaster, unsigned char winDrawState, const int fno, const int targetx, unsigned cycles) { @@ -1231,16 +1231,16 @@ namespace M3Loop { return StartWindowDraw::predictCyclesUntilXpos_fn(p, xpos, endx, ly, nextSprite, weMaster, WIN_DRAW_STARTED & p.lcdc >> 4, 0, targetx, cycles); } - + if (xpos > targetx) return predictCyclesUntilXposNextLine(p, winDrawState, targetx); - + enum { NO_TILE_NUMBER = 1 }; // low bit set, so it will never be equal to an actual tile number. - + int nwx = 0xFF; cycles += targetx - xpos; - if (p.wx - static_cast(xpos) < targetx - static_cast(xpos) && (p.lcdc & 0x20) + if (p.wx - unsigned(xpos) < targetx - unsigned(xpos) && (p.lcdc & 0x20) && (weMaster || p.wy2 == ly) && !(winDrawState & WIN_DRAW_STARTED) && (p.cgb || p.wx != 166)) { nwx = p.wx; cycles += 6; @@ -1250,10 +1250,10 @@ namespace M3Loop { const unsigned char *sprite = p.spriteMapper.sprites(ly); const unsigned char *const spriteEnd = sprite + p.spriteMapper.numSprites(ly); sprite += nextSprite; - + if (sprite < spriteEnd) { const int spx = p.spriteMapper.posbuf()[*sprite + 1]; - unsigned firstTileXpos = static_cast(endx) & 7; // ok even if endx is capped at 168, because fno will be used. + unsigned firstTileXpos = unsigned(endx) & 7; // ok even if endx is capped at 168, because fno will be used. unsigned prevSpriteTileNo = (xpos - firstTileXpos) & ~7; // this tile. all sprites on this tile will now add 6 cycles. // except this one @@ -1273,16 +1273,16 @@ namespace M3Loop { targetx, firstTileXpos, prevSpriteTileNo, &cycles); } } - + return cycles; } - + static unsigned predictCyclesUntilXpos_fn(const PPUPriv &p, - const int endx, const int fno, const int targetx, const unsigned cycles) { + int endx, int fno, int targetx, unsigned cycles) { return predictCyclesUntilXpos_fn(p, p.xpos, endx, p.lyCounter.ly(), p.nextSprite, p.weMaster, p.winDrawState, fno, targetx, cycles); } - + static unsigned predictCyclesUntilXpos_f0(const PPUPriv &p, int targetx, unsigned cycles) { return predictCyclesUntilXpos_fn(p, p.xpos < 160 ? p.xpos + 8 : 168, 0, targetx, cycles); } @@ -1302,14 +1302,14 @@ namespace M3Loop { return predictCyclesUntilXpos_fn(p, p.endx, 5, targetx, cycles); } } - + namespace StartWindowDraw { static unsigned predictCyclesUntilXpos_fn(const PPUPriv &p, int xpos, const int endx, const unsigned ly, const unsigned nextSprite, const bool weMaster, const unsigned winDrawState, const int fno, const int targetx, unsigned cycles) { if (xpos > targetx) return predictCyclesUntilXposNextLine(p, winDrawState, targetx); - + unsigned cinc = 6 - fno; if (!(p.lcdc & 0x20) && p.cgb) { @@ -1322,23 +1322,23 @@ namespace M3Loop { xpos += xinc; } } - + cycles += cinc; if (xpos <= targetx) { return Tile::predictCyclesUntilXpos_fn(p, xpos, xpos < 160 ? xpos + 8 : 168, ly, nextSprite, weMaster, winDrawState, 0, targetx, cycles); } - + return cycles - 1; } - - static unsigned predictCyclesUntilXpos_fn(const PPUPriv &p, const int endx, - const int fno, const int targetx, const unsigned cycles) { + + static unsigned predictCyclesUntilXpos_fn(const PPUPriv &p, int endx, + int fno, int targetx, unsigned cycles) { return predictCyclesUntilXpos_fn(p, p.xpos, endx, p.lyCounter.ly(), p.nextSprite, p.weMaster, p.winDrawState, fno, targetx, cycles); } - + static unsigned predictCyclesUntilXpos_f0(const PPUPriv &p, const int targetx, const unsigned cycles) { return predictCyclesUntilXpos_fn(p, p.xpos == p.endx ? (p.xpos < 160 ? p.xpos + 8 : 168) : p.endx, 0, targetx, cycles); } @@ -1362,16 +1362,16 @@ namespace M3Loop { namespace LoadSprites { static unsigned predictCyclesUntilXpos_fn(const PPUPriv &p, const int fno, const int targetx, unsigned cycles) { unsigned nextSprite = p.nextSprite; - + if ((p.lcdc & 2) | p.cgb) { cycles += 6 - fno; nextSprite += 1; } - + return Tile::predictCyclesUntilXpos_fn(p, p.xpos, p.endx, p.lyCounter.ly(), nextSprite, p.weMaster, p.winDrawState, 5, targetx, cycles); } - + static unsigned predictCyclesUntilXpos_f0(const PPUPriv &p, int targetx, unsigned cycles) { return predictCyclesUntilXpos_fn(p, 0, targetx, cycles); } @@ -1394,24 +1394,24 @@ namespace M3Loop { } namespace M3Start { - static unsigned predictCyclesUntilXpos_f1(const PPUPriv &p, const unsigned xpos, const unsigned ly, - const bool weMaster, const unsigned winDrawState, const int targetx, unsigned cycles) { - cycles += std::min((static_cast(p.scx) - static_cast(xpos)) & 7, MAX_M3START_CYCLES - xpos) + 1 - p.cgb; + static unsigned predictCyclesUntilXpos_f1(const PPUPriv &p, unsigned xpos, unsigned ly, + bool weMaster, unsigned winDrawState, int targetx, unsigned cycles) { + cycles += std::min((unsigned(p.scx) - unsigned(xpos)) & 7, MAX_M3START_CYCLES - xpos) + 1 - p.cgb; return M3Loop::Tile::predictCyclesUntilXpos_fn(p, 0, 8 - (p.scx & 7), ly, 0, weMaster, winDrawState, std::min(p.scx & 7, 5), targetx, cycles); } - - static unsigned predictCyclesUntilXpos_f0(const PPUPriv &p, const unsigned ly, - const bool weMaster, unsigned winDrawState, const int targetx, const unsigned cycles) { + + static unsigned predictCyclesUntilXpos_f0(const PPUPriv &p, unsigned ly, + bool weMaster, unsigned winDrawState, int targetx, unsigned cycles) { winDrawState = (winDrawState & p.lcdc >> 5 & WIN_DRAW_START) ? WIN_DRAW_STARTED : 0; return predictCyclesUntilXpos_f1(p, 0, ly, weMaster, winDrawState, targetx, cycles); } - - static unsigned predictCyclesUntilXpos_f0(const PPUPriv &p, const int targetx, const unsigned cycles) { + + static unsigned predictCyclesUntilXpos_f0(const PPUPriv &p, int targetx, unsigned cycles) { const unsigned ly = p.lyCounter.ly() + (p.lyCounter.time() - p.now < 16); return predictCyclesUntilXpos_f0(p, ly, p.weMaster, p.winDrawState, targetx, cycles); } - + static unsigned predictCyclesUntilXpos_f1(const PPUPriv &p, const int targetx, const unsigned cycles) { return predictCyclesUntilXpos_f1(p, p.xpos, p.lyCounter.ly(), p.weMaster, p.winDrawState, targetx, cycles); } @@ -1420,44 +1420,44 @@ namespace M3Start { namespace M2 { namespace Ly0 { static unsigned predictCyclesUntilXpos_f0(const PPUPriv &p, - const unsigned winDrawState, const int targetx, const unsigned cycles) { - const bool weMaster = (p.lcdc & 0x20) && 0 == p.wy; - const unsigned ly = 0; - + unsigned winDrawState, int targetx, unsigned cycles) { + bool weMaster = (p.lcdc & 0x20) && 0 == p.wy; + unsigned ly = 0; + return M3Start::predictCyclesUntilXpos_f0(p, ly, weMaster, winDrawState, targetx, cycles + m3StartLineCycle(p.cgb)); } - - static unsigned predictCyclesUntilXpos_f0(const PPUPriv &p, const int targetx, const unsigned cycles) { + + static unsigned predictCyclesUntilXpos_f0(const PPUPriv &p, int targetx, unsigned cycles) { return predictCyclesUntilXpos_f0(p, p.winDrawState, targetx, cycles); } } - + namespace LyNon0 { static unsigned predictCyclesUntilXpos_f1(const PPUPriv &p, bool weMaster, - const unsigned winDrawState, const int targetx, const unsigned cycles) { - const unsigned ly = p.lyCounter.ly() + 1; - + unsigned winDrawState, int targetx, unsigned cycles) { + unsigned ly = p.lyCounter.ly() + 1; + weMaster |= (p.lcdc & 0x20) && ly == p.wy; - + return M3Start::predictCyclesUntilXpos_f0(p, ly, weMaster, winDrawState, targetx, cycles + 456 - weMasterCheckAfterLyIncLineCycle(p.cgb) + m3StartLineCycle(p.cgb)); } - - static unsigned predictCyclesUntilXpos_f1(const PPUPriv &p, const int targetx, const unsigned cycles) { + + static unsigned predictCyclesUntilXpos_f1(const PPUPriv &p, int targetx, unsigned cycles) { return predictCyclesUntilXpos_f1(p, p.weMaster, p.winDrawState, targetx, cycles); } - + static unsigned predictCyclesUntilXpos_f0(const PPUPriv &p, - const unsigned winDrawState, const int targetx, const unsigned cycles) { - const bool weMaster = p.weMaster || ((p.lcdc & 0x20) && p.lyCounter.ly() == p.wy); - + unsigned winDrawState, int targetx, unsigned cycles) { + bool weMaster = p.weMaster || ((p.lcdc & 0x20) && p.lyCounter.ly() == p.wy); + return predictCyclesUntilXpos_f1(p, weMaster, winDrawState, targetx, cycles + weMasterCheckAfterLyIncLineCycle(p.cgb) - weMasterCheckPriorToLyIncLineCycle(p.cgb)); } - - static unsigned predictCyclesUntilXpos_f0(const PPUPriv &p, const int targetx, const unsigned cycles) { + + static unsigned predictCyclesUntilXpos_f0(const PPUPriv &p, int targetx, unsigned cycles) { return predictCyclesUntilXpos_f0(p, p.winDrawState, targetx, cycles); } } @@ -1508,7 +1508,7 @@ struct BSearch { static std::size_t upperBound(const T a[], const K e) { if (e < a[start + len / 2]) return BSearch::upperBound(a, e); - + return BSearch::upperBound(a, e); } }; @@ -1539,14 +1539,14 @@ static const PPUState * decodeM3LoopState(const unsigned state) { case M3Loop::Tile::ID3: return &M3Loop::Tile::f3_; case M3Loop::Tile::ID4: return &M3Loop::Tile::f4_; case M3Loop::Tile::ID5: return &M3Loop::Tile::f5_; - + case M3Loop::LoadSprites::ID0: return &M3Loop::LoadSprites::f0_; case M3Loop::LoadSprites::ID1: return &M3Loop::LoadSprites::f1_; case M3Loop::LoadSprites::ID2: return &M3Loop::LoadSprites::f2_; case M3Loop::LoadSprites::ID3: return &M3Loop::LoadSprites::f3_; case M3Loop::LoadSprites::ID4: return &M3Loop::LoadSprites::f4_; case M3Loop::LoadSprites::ID5: return &M3Loop::LoadSprites::f5_; - + case M3Loop::StartWindowDraw::ID0: return &M3Loop::StartWindowDraw::f0_; case M3Loop::StartWindowDraw::ID1: return &M3Loop::StartWindowDraw::f1_; case M3Loop::StartWindowDraw::ID2: return &M3Loop::StartWindowDraw::f2_; @@ -1560,10 +1560,10 @@ static const PPUState * decodeM3LoopState(const unsigned state) { static long cyclesUntilM0Upperbound(const PPUPriv &p) { long cycles = 168 - p.xpos + 6; - + for (unsigned i = p.nextSprite; i < 10 && p.spriteList[i].spx < 168; ++i) cycles += 11; - + return cycles; } @@ -1584,13 +1584,13 @@ static void loadSpriteList(PPUPriv &p, const SaveState &ss) { p.spriteList[i].attrib = ss.ppu.spAttribList[i] & 0xFF; p.spwordList[i] = (ss.ppu.spByte1List[i] * 0x100 + ss.ppu.spByte0List[i]) & 0xFFFF; } - + p.spriteList[numSprites].spx = 0xFF; p.nextSprite = std::min(ss.ppu.nextSprite, numSprites); - + while (p.spriteList[p.nextSprite].spx < ss.ppu.xpos) ++p.nextSprite; - + p.currentSprite = std::min(p.nextSprite, ss.ppu.currentSprite); } } @@ -1605,7 +1605,7 @@ void PPU::loadState(const SaveState &ss, const unsigned char *const oamram) { ? videoCycles - ds * M2_DS_OFFSET + 70224 : videoCycles - ds * M2_DS_OFFSET; const long lineCycles = static_cast(vcycs) % 456; - + p_.now = ss.cpu.cycleCounter; p_.lcdc = ss.mem.ioamhram.get()[0x140]; p_.lyCounter.setDoubleSpeed(ds); @@ -1632,12 +1632,12 @@ void PPU::loadState(const SaveState &ss, const unsigned char *const oamram) { p_.lastM0Time = p_.now - ss.ppu.lastM0Time; p_.cgb = ss.ppu.isCgb; loadSpriteList(p_, ss); - + if (m3loopState && videoCycles < 144 * 456L && p_.xpos < 168 - && lineCycles + cyclesUntilM0Upperbound(p_) < static_cast(weMasterCheckPriorToLyIncLineCycle(p_.cgb))) { + && lineCycles + cyclesUntilM0Upperbound(p_) < long(weMasterCheckPriorToLyIncLineCycle(p_.cgb))) { p_.nextCallPtr = m3loopState; p_.cycles = -1; - } else if (vcycs < 143 * 456L + static_cast(m3StartLineCycle(p_.cgb)) + MAX_M3START_CYCLES) { + } else if (vcycs < 143 * 456L + long(m3StartLineCycle(p_.cgb)) + MAX_M3START_CYCLES) { const CycleState lineCycleStates[] = { { &M3Start::f0_, m3StartLineCycle(p_.cgb) }, { &M3Start::f1_, m3StartLineCycle(p_.cgb) + MAX_M3START_CYCLES }, @@ -1645,13 +1645,13 @@ void PPU::loadState(const SaveState &ss, const unsigned char *const oamram) { { &M2::LyNon0::f1_, weMasterCheckAfterLyIncLineCycle(p_.cgb) }, { &M3Start::f0_, m3StartLineCycle(p_.cgb) + 456 } }; - + const std::size_t pos = upperBound(lineCycleStates, lineCycles); - + p_.cycles = lineCycles - lineCycleStates[pos].cycle; p_.nextCallPtr = lineCycleStates[pos].state; - + if (&M3Start::f1_ == lineCycleStates[pos].state) { p_.xpos = lineCycles - m3StartLineCycle(p_.cgb) + 1; p_.cycles = -1; @@ -1671,7 +1671,7 @@ void PPU::reset(const unsigned char *const oamram, const unsigned char *const vr void PPU::resetCc(const unsigned long oldCc, const unsigned long newCc) { const unsigned long dec = oldCc - newCc; const unsigned long videoCycles = p_.lcdc & 0x80 ? p_.lyCounter.frameCycles(p_.now) : 0; - + p_.now -= dec; p_.lastM0Time = p_.lastM0Time ? p_.lastM0Time - dec : p_.lastM0Time; p_.lyCounter.reset(videoCycles, p_.now); @@ -1680,12 +1680,12 @@ void PPU::resetCc(const unsigned long oldCc, const unsigned long newCc) { void PPU::speedChange(const unsigned long cycleCounter) { const unsigned long videoCycles = p_.lcdc & 0x80 ? p_.lyCounter.frameCycles(p_.now) : 0; - + p_.spriteMapper.preSpeedChange(cycleCounter); p_.lyCounter.setDoubleSpeed(!p_.lyCounter.isDoubleSpeed()); p_.lyCounter.reset(videoCycles, p_.now); p_.spriteMapper.postSpeedChange(cycleCounter); - + if (&M2::Ly0::f0_ == p_.nextCallPtr || &M2::LyNon0::f0_ == p_.nextCallPtr) { if (p_.lyCounter.isDoubleSpeed()) { p_.cycles -= M2_DS_OFFSET; @@ -1707,7 +1707,7 @@ void PPU::setLcdc(const unsigned lcdc, const unsigned long cc) { p_.weMaster = (lcdc & 0x20) && 0 == p_.wy; p_.winDrawState = 0; p_.nextCallPtr = &M3Start::f0_; - p_.cycles = -static_cast(m3StartLineCycle(p_.cgb) + M2_DS_OFFSET * p_.lyCounter.isDoubleSpeed()); + p_.cycles = -int(m3StartLineCycle(p_.cgb) + M2_DS_OFFSET * p_.lyCounter.isDoubleSpeed()); } else if ((p_.lcdc ^ lcdc) & 0x20) { if (!(lcdc & 0x20)) { if (p_.winDrawState == WIN_DRAW_STARTED || p_.xpos == 168) @@ -1717,23 +1717,23 @@ void PPU::setLcdc(const unsigned lcdc, const unsigned long cc) { ++p_.winYPos; } } - + if ((p_.lcdc ^ lcdc) & 0x04) { if (p_.lcdc & lcdc & 0x80) p_.spriteMapper.oamChange(cc); - + p_.spriteMapper.setLargeSpritesSource(lcdc & 0x04); } - + p_.lcdc = lcdc; } void PPU::update(const unsigned long cc) { const int cycles = (cc - p_.now) >> p_.lyCounter.isDoubleSpeed(); - + p_.now += cycles << p_.lyCounter.isDoubleSpeed(); p_.cycles += cycles; - + if (p_.cycles >= 0) { p_.framebuf.setFbline(p_.lyCounter.ly()); p_.nextCallPtr->f(p_); diff --git a/libgambatte/src/video/ppu.h b/libgambatte/src/video/ppu.h index a32176d589..9ef73f74d4 100644 --- a/libgambatte/src/video/ppu.h +++ b/libgambatte/src/video/ppu.h @@ -33,9 +33,9 @@ class PPUFrameBuf { uint_least32_t *buf_; uint_least32_t *fbline_; int pitch_; - + static uint_least32_t * nullfbline() { static uint_least32_t nullfbline_[160]; return nullfbline_; } - + public: PPUFrameBuf() : buf_(0), fbline_(nullfbline()), pitch_(0) {} uint_least32_t * fb() const { return buf_; } @@ -93,7 +93,7 @@ struct PPUPriv { bool cgb; bool weMaster; - + PPUPriv(NextM0Time &nextM0Time, const unsigned char *oamram, const unsigned char *vram); }; @@ -104,7 +104,7 @@ public: : p_(nextM0Time, oamram, vram) { } - + unsigned long * bgPalette() { return p_.bgPalette; } bool cgb() const { return p_.cgb; } void doLyCountEvent() { p_.lyCounter.doEvent(); } diff --git a/libgambatte/src/video/sprite_mapper.cpp b/libgambatte/src/video/sprite_mapper.cpp index 208b80e8e7..489af046d2 100644 --- a/libgambatte/src/video/sprite_mapper.cpp +++ b/libgambatte/src/video/sprite_mapper.cpp @@ -83,10 +83,10 @@ void SpriteMapper::OamReader::update(const unsigned long cc) { if (!(pos & 1)) { if (pos == 80) pos = 0; - + if (cgb_) szbuf[pos >> 1] = largeSpritesSrc; - + buf[pos ] = oamram[pos * 2 ]; buf[pos + 1] = oamram[pos * 2 + 1]; } else @@ -194,7 +194,7 @@ SYNCFUNC(SpriteMapper) { NSS(spritemap); NSS(num); - + SSS(nextM0Time_); SSS(oamReader); } diff --git a/libgambatte/src/video/sprite_mapper.h b/libgambatte/src/video/sprite_mapper.h index b6171f842d..254b433cfd 100644 --- a/libgambatte/src/video/sprite_mapper.h +++ b/libgambatte/src/video/sprite_mapper.h @@ -78,7 +78,7 @@ public: private: mutable unsigned char spritemap[144*10]; mutable unsigned char num[144]; - + NextM0Time &nextM0Time_; OamReader oamReader; diff --git a/output/dll/libgambatte.dll b/output/dll/libgambatte.dll index 4fac65dc3a39e0105e69397bffed25ce7456143c..5c29f24fa2a36f6061d21dd0b8062d3379613fce 100644 GIT binary patch delta 2339 zcmY*Z2~-v56`ni1rw>FK5D%y^fLmOXSz(4*o=4@>s3Y}06rG)5078n=p7QX74!6>5#z9xA=hB`1C7eDi%1ai%`Oc#({vOY6A77m#;*+mff_fgnxEsk7MY3oT48E&MBzAKU$y(|{UyQB5l zIV{%nkLRUH?`6LtMpUXVj$SOdu``H9BkbaCzP1ypRhg22cI750bYOx!vc5%vaH(x z%L2%*jn-^Cr1pUZ%jyf;Ln8F|aqMS?mHoo7DkEnIybFV@t3zNpz$ZrXFqj;X$j+tK($P=|;4<3Bz*4YVv&X`58#y7vI9dw>jaU|rK&sWj z!W|OwjH?1HfPRJ;3xmkVGmTaO1fx0@UWM(}-dKnV4qi74?X6pk_WDj*m&U_lAkR;- zl!;IdFvdEb0Hf@XYu%dy(@7|`lxeUz6t{aR6>jx_3hS%4As1k<@kTmK7;u&+2+xlT zPWb-W(cN9vNTuOnOXZs9`3_sv>2NX(l8lr>_$Vnq?olQ0Ac$yJZ}-tSB*eD~UEs3e zm8cn?;yIJpo$uhz&+5+a@~yv-1JL3k3VvaeIHaj?G$ci!iE z`fLviP8h$>hHxXl0_MO|<7@?5UuI0HgmgG)9jJu80kG9dT?3WmjEZ$Y)g!hbb|VfU zjv_eyIZh&PLHq^rH^g1UL&VGYf06gV+z>=0A_g(al4{_s5V&F0Ho#N}(%*U7J;QTv zLHk_m<`(GLgY4R8-P-{(0wB{EzYEV)g0*iKyhR!ydXNr%_)=5G6!gIZ%lnpeitsScXlJmm`mM1La+f1^f@JM7{>7 zGUVP;90++Satif@$X6mKR$y)&P&G)$eTX{pYUGJU*cb<0g}kj8H-@?g`8L#vx?rGI z<4rUMqU)@}eK6ccK0Ih`_re*i&%=ZGE`El!q5~c|;$MEWNqlS);z$HZ90C6wLA;Cw zBMUU$7+;2N5})L+h@9*ygBt$=V_NhE2IqhO*j z(!X5rF9Umgu^->iert)HB%`EGtT2l$WmmE5*=_7T_6U2NJly>`}f_T9lj0BPCGnuZ~nDb+S4~eOLWJtyQSb@hQtYGK+)P0=Q4^R#qr zxwcAc(7alsc181P4>iI`Ib)oxbAmI;xyZTP>2Yp#n$D9Y&Q@oK6SuGph;jIV3ZNtC z(X>RT(CKs$T}SVro9J8gQ@SrRf>D|2OeRyvtY$VbyO_hwpP38HP38duEX78#quDri z0y~|Z$ELG+YzgZ@KlZcVu$S3;ET98}xp?kPZZ5Z!E9BO2o4GHzZ@E^kq>}@_H{YKh z$;6(JwOKu{-cW~W zW3~C(JDN+Y)PASkM{ED+eC#B=K=}Wrq4Y#Lna-vQ=wPNFGl-ePWHT=2W9Bn-tdVJE zt~1Bj8*DpEa-rPo+}tA0&F$b0a9?uYao4#>K89y`ji16N@-zA0@EN>^*ZEESUi71p zzs%p^JNZX^7f%Wfp^p$Q3=pD);ldb!6+}T3CJIx8nZkVGJz=F#DOh7(BQMzMY1{t* DmzMrh delta 2370 zcmY*Z3se+G7VTHez@P}Nl10$~BBD{${*Kd3cMqbRND^02QCtO$CJ~9KEW>I51wk=7 zfW&rf4XF5!pXlg_O=99F7=K5NG4Y4)5#6ZxNzkmTY!2@1n;3Vqbw#|Rv{tj2A_%ob>t~t z3==(JlTL=f22b<*P9SlzHZI2sw>_0Ph0rZ@0H&+Pk}Jzi0Th*0f;6SY67PwcZ3WUJ zPD`J|;hwHJMI_Ru1?IBwP_yUuB(K@D3AtAIhgO|CvrGRWK)Lclv#11$%ErROhj@bj zYzD~DLi2h;}^d-BRM6!^mP?U7dkp;o(l$_urgyQkN8fOgN4i!01% zd-%1!_z4oPrNMH~>T3)@gLdkAFOWSCu5ayug{^3Fz#h+qHZFifZ?E?UH^3R)02mCl z#$ABfek4ve(#?<=3}+0ZD{Kr2Yy1nA{i|VRzcb9x=k$gdFw$u14RZl5>ZAKWa@a!W z%elWdH(%e^2Tl=~V$6?)RuhSuZOj?~1pu4%dvP!eL?eA595s==EWN4%DxZ@?%5dkuLctm%wzdnps{_(Q$%<-3pzFj^lx9fpN_1&VUr&S-3T z-0Jm~9iu1XS-2z=uB4!{BV+xJ+ApWLlG*3`v9eyxM%VDP<%r*usgL0BqzXIr^h_i^Rh}(#t5Yx05c!r!p9*F3I z=z)kqyo?V-Zbdi{BM_;GKN*3QFg67C7@4*3CUltg-oL$Ru7?@-`Wv3L5a3VZJ~h7B z1ZjRyqj%YcCobP8+6H6EsvS@ZhxD~OUtPuQwlM070@3dZ!Q9wM%{(H4)ya@K|n9XK@Wz(VIHv~3;2r!w7zVDDJ7U_S_cxPp|C@Wza<`Ff zCL~gNh+8b-_Hqr}G44EfoonG9bH8wYd=UQ%AHnzK8D8QO`8WBoduqCgc3YCI z%J#M1sXG~1oE)EHueI0P&)A#oPwW=Cr#x6rl+)xaxlpc>H^}?t6LORMqf9AXl~_em zl9dU{4CMo5sj^1dt{hS>D0h@!lwdVl<V{XKg{vAxE=+y1%z zwEdd>vE5(pAxF#ob-3XQ@^;Tk&fL Date: Sat, 25 May 2019 19:29:59 +0200 Subject: [PATCH 06/38] ppu: less magical lcdc constants --- libgambatte/src/video.h | 2 +- libgambatte/src/video/lcddef.h | 21 + libgambatte/src/video/ppu.cpp | 2484 ++++++++++++++++---------------- libgambatte/src/video/ppu.h | 1 + output/dll/libgambatte.dll | Bin 142336 -> 141824 bytes 5 files changed, 1279 insertions(+), 1229 deletions(-) create mode 100644 libgambatte/src/video/lcddef.h diff --git a/libgambatte/src/video.h b/libgambatte/src/video.h index 7b5628b938..ebfc14b7df 100644 --- a/libgambatte/src/video.h +++ b/libgambatte/src/video.h @@ -64,7 +64,7 @@ public: } void doEvent(unsigned char *const ifreg, const unsigned ly, const unsigned statReg, const unsigned lycReg) { - if (((statReg_ | statReg) & 0x08) && (!(statReg_ & 0x40) || ly != lycReg_)) + if (((statReg_ | statReg) & lcdstat_m0irqen) && (!(statReg_ & lcdstat_lycirqen) || ly != lycReg_)) *ifreg |= 2; statReg_ = statReg; diff --git a/libgambatte/src/video/lcddef.h b/libgambatte/src/video/lcddef.h new file mode 100644 index 0000000000..5fbf3756cb --- /dev/null +++ b/libgambatte/src/video/lcddef.h @@ -0,0 +1,21 @@ +#ifndef LCDDEF_H +#define LCDDEF_H + +namespace gambatte { + +enum { lcdc_bgen = 0x01, + lcdc_objen = 0x02, + lcdc_obj2x = 0x04, + lcdc_tdsel = 0x10, + lcdc_we = 0x20, + lcdc_en = 0x80 }; + +enum { lcdstat_lycflag = 0x04, + lcdstat_m0irqen = 0x08, + lcdstat_m1irqen = 0x10, + lcdstat_m2irqen = 0x20, + lcdstat_lycirqen = 0x40 }; + +} + +#endif diff --git a/libgambatte/src/video/ppu.cpp b/libgambatte/src/video/ppu.cpp index d5c5e9607a..a016cea84f 100644 --- a/libgambatte/src/video/ppu.cpp +++ b/libgambatte/src/video/ppu.cpp @@ -1,21 +1,21 @@ -/*************************************************************************** - * Copyright (C) 2010 by Sindre AamÃ¥s * - * aamas@stud.ntnu.no * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License version 2 as * - * published by the Free Software Foundation. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License version 2 for more details. * - * * - * You should have received a copy of the GNU General Public License * - * version 2 along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ +// +// Copyright (C) 2010 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + #include "ppu.h" #include "savestate.h" #include @@ -26,11 +26,13 @@ namespace { using namespace gambatte; -#define PREP(u8) (((u8) << 7 & 0x80) | ((u8) << 5 & 0x40) | ((u8) << 3 & 0x20) | ((u8) << 1 & 0x10) | \ - ((u8) >> 1 & 0x08) | ((u8) >> 3 & 0x04) | ((u8) >> 5 & 0x02) | ((u8) >> 7 & 0x01)) +#define PREP(u8) (((u8) << 7 & 0x80) | ((u8) << 5 & 0x40) | ((u8) << 3 & 0x20) | ((u8) << 1 & 0x10) \ + | ((u8) >> 1 & 0x08) | ((u8) >> 3 & 0x04) | ((u8) >> 5 & 0x02) | ((u8) >> 7 & 0x01)) -#define EXPAND(u8) ((PREP(u8) << 7 & 0x4000) | (PREP(u8) << 6 & 0x1000) | (PREP(u8) << 5 & 0x0400) | (PREP(u8) << 4 & 0x0100) | \ - (PREP(u8) << 3 & 0x0040) | (PREP(u8) << 2 & 0x0010) | (PREP(u8) << 1 & 0x0004) | (PREP(u8) & 0x0001)) +#define EXPAND(u8) ((PREP(u8) << 7 & 0x4000) | (PREP(u8) << 6 & 0x1000) \ + | (PREP(u8) << 5 & 0x0400) | (PREP(u8) << 4 & 0x0100) \ + | (PREP(u8) << 3 & 0x0040) | (PREP(u8) << 2 & 0x0010) \ + | (PREP(u8) << 1 & 0x0004) | (PREP(u8) & 0x0001)) #define EXPAND_ROW(n) EXPAND((n)|0x0), EXPAND((n)|0x1), EXPAND((n)|0x2), EXPAND((n)|0x3), \ EXPAND((n)|0x4), EXPAND((n)|0x5), EXPAND((n)|0x6), EXPAND((n)|0x7), \ @@ -42,7 +44,7 @@ using namespace gambatte; EXPAND_ROW(0x80), EXPAND_ROW(0x90), EXPAND_ROW(0xA0), EXPAND_ROW(0xB0), \ EXPAND_ROW(0xC0), EXPAND_ROW(0xD0), EXPAND_ROW(0xE0), EXPAND_ROW(0xF0) -static const unsigned short expand_lut[0x200] = { +static unsigned short const expand_lut[0x200] = { EXPAND_TABLE, #undef PREP @@ -59,59 +61,58 @@ static const unsigned short expand_lut[0x200] = { #define DECLARE_FUNC(n, id) \ enum { ID##n = id }; \ static void f##n (PPUPriv &); \ - static unsigned predictCyclesUntilXpos_f##n (const PPUPriv &, int targetxpos, unsigned cycles); \ - static const PPUState f##n##_ = { f##n, predictCyclesUntilXpos_f##n, ID##n } - -namespace M2 { - namespace Ly0 { DECLARE_FUNC(0, 0); } - namespace LyNon0 { DECLARE_FUNC(0, 0); DECLARE_FUNC(1, 0); } -} + static unsigned predictCyclesUntilXpos_f##n (PPUPriv const &, int targetxpos, unsigned cycles); \ + static PPUState const f##n##_ = { f##n, predictCyclesUntilXpos_f##n, ID##n } +namespace M2_Ly0 { DECLARE_FUNC(0, 0); } +namespace M2_LyNon0 { DECLARE_FUNC(0, 0); DECLARE_FUNC(1, 0); } namespace M3Start { DECLARE_FUNC(0, 0); DECLARE_FUNC(1, 0); } - namespace M3Loop { - namespace Tile { - DECLARE_FUNC(0, 0x80); - DECLARE_FUNC(1, 0x81); - DECLARE_FUNC(2, 0x82); - DECLARE_FUNC(3, 0x83); - DECLARE_FUNC(4, 0x84); - DECLARE_FUNC(5, 0x85); - } - - namespace LoadSprites { - DECLARE_FUNC(0, 0x88); - DECLARE_FUNC(1, 0x89); - DECLARE_FUNC(2, 0x8A); - DECLARE_FUNC(3, 0x8B); - DECLARE_FUNC(4, 0x8C); - DECLARE_FUNC(5, 0x8D); - } - - namespace StartWindowDraw { - DECLARE_FUNC(0, 0x90); - DECLARE_FUNC(1, 0x91); - DECLARE_FUNC(2, 0x92); - DECLARE_FUNC(3, 0x93); - DECLARE_FUNC(4, 0x94); - DECLARE_FUNC(5, 0x95); - } +namespace Tile { + DECLARE_FUNC(0, 0x80); + DECLARE_FUNC(1, 0x81); + DECLARE_FUNC(2, 0x82); + DECLARE_FUNC(3, 0x83); + DECLARE_FUNC(4, 0x84); + DECLARE_FUNC(5, 0x85); } +namespace LoadSprites { + DECLARE_FUNC(0, 0x88); + DECLARE_FUNC(1, 0x89); + DECLARE_FUNC(2, 0x8A); + DECLARE_FUNC(3, 0x8B); + DECLARE_FUNC(4, 0x8C); + DECLARE_FUNC(5, 0x8D); +} +namespace StartWindowDraw { + DECLARE_FUNC(0, 0x90); + DECLARE_FUNC(1, 0x91); + DECLARE_FUNC(2, 0x92); + DECLARE_FUNC(3, 0x93); + DECLARE_FUNC(4, 0x94); + DECLARE_FUNC(5, 0x95); +} +} // namespace M3Loop #undef DECLARE_FUNC -enum { WIN_DRAW_START = 1, WIN_DRAW_STARTED = 2 }; +enum { win_draw_start = 1, win_draw_started = 2 }; +enum { m2_ds_offset = 3 }; +enum { max_m3start_cycles = 80 }; +enum { attr_yflip = 0x40, attr_bgpriority = 0x80 }; -enum { M2_DS_OFFSET = 3 }; -enum { MAX_M3START_CYCLES = 80 }; +static inline int lcdcEn( PPUPriv const &p) { return p.lcdc & lcdc_en; } +static inline int lcdcWinEn(PPUPriv const &p) { return p.lcdc & lcdc_we; } +static inline int lcdcObj2x(PPUPriv const &p) { return p.lcdc & lcdc_obj2x; } +static inline int lcdcObjEn(PPUPriv const &p) { return p.lcdc & lcdc_objen; } +static inline int lcdcBgEn( PPUPriv const &p) { return p.lcdc & lcdc_bgen; } -static inline int weMasterCheckPriorToLyIncLineCycle(const bool cgb) { return 450 - cgb; } -static inline int weMasterCheckAfterLyIncLineCycle(const bool cgb) { return 454 - cgb; } -static inline int m3StartLineCycle(const bool /*cgb*/) { return 83; } - -static inline void nextCall(const int cycles, const PPUState &state, PPUPriv &p) { - const int c = p.cycles - cycles; +static inline int weMasterCheckPriorToLyIncLineCycle(bool cgb) { return 450 - cgb; } +static inline int weMasterCheckAfterLyIncLineCycle(bool cgb) { return 454 - cgb; } +static inline int m3StartLineCycle(bool /*cgb*/) { return 83; } +static inline void nextCall(int const cycles, PPUState const &state, PPUPriv &p) { + int const c = p.cycles - cycles; if (c >= 0) { p.cycles = c; return state.f(p); @@ -121,29 +122,32 @@ static inline void nextCall(const int cycles, const PPUState &state, PPUPriv &p) p.nextCallPtr = &state; } +namespace M2_Ly0 { + static void f0(PPUPriv &p) { + p.weMaster = lcdcWinEn(p) && 0 == p.wy; + p.winYPos = 0xFF; + nextCall(m3StartLineCycle(p.cgb), M3Start::f0_, p); + } +} + +namespace M2_LyNon0 { + static void f0(PPUPriv &p) { + p.weMaster |= lcdcWinEn(p) && p.lyCounter.ly() == p.wy; + nextCall( weMasterCheckAfterLyIncLineCycle(p.cgb) + - weMasterCheckPriorToLyIncLineCycle(p.cgb), f1_, p); + } + + static void f1(PPUPriv &p) { + p.weMaster |= lcdcWinEn(p) && p.lyCounter.ly() + 1 == p.wy; + nextCall(456 - weMasterCheckAfterLyIncLineCycle(p.cgb) + m3StartLineCycle(p.cgb), + M3Start::f0_, p); + } +} + +/* namespace M2 { - namespace Ly0 { - static void f0(PPUPriv &p) { - p.weMaster = (p.lcdc & 0x20) && 0 == p.wy; - p.winYPos = 0xFF; - nextCall(m3StartLineCycle(p.cgb), M3Start::f0_, p); - } - } - - namespace LyNon0 { - static void f0(PPUPriv &p) { - p.weMaster |= (p.lcdc & 0x20) && p.lyCounter.ly() == p.wy; - nextCall(weMasterCheckAfterLyIncLineCycle(p.cgb) - weMasterCheckPriorToLyIncLineCycle(p.cgb), f1_, p); - } - - static void f1(PPUPriv &p) { - p.weMaster |= (p.lcdc & 0x20) && p.lyCounter.ly() + 1 == p.wy; - nextCall(456 - weMasterCheckAfterLyIncLineCycle(p.cgb) + m3StartLineCycle(p.cgb), M3Start::f0_, p); - } - } - - /*struct SpriteLess { - bool operator()(const Sprite lhs, const Sprite rhs) const { + struct SpriteLess { + bool operator()(Sprite lhs, Sprite rhs) const { return lhs.spx < rhs.spx; } }; @@ -160,13 +164,14 @@ namespace M2 { int cycles = p.cycles; unsigned oampos = p.reg0; unsigned nextSprite = p.nextSprite; - const unsigned nly = (p.lyCounter.ly() + 1 == 154 ? 0 : p.lyCounter.ly() + 1) + ((p.lyCounter.time()-(p.now-p.cycles)) <= 4); - const bool ls = p.spriteMapper.largeSpritesSource(); + unsigned const nly = (p.lyCounter.ly() + 1 == 154 ? 0 : p.lyCounter.ly() + 1) + + ((p.lyCounter.time()-(p.now-p.cycles)) <= 4); + bool const ls = p.spriteMapper.largeSpritesSource(); do { - const unsigned spy = p.spriteMapper.oamram()[oampos ]; - const unsigned spx = p.spriteMapper.oamram()[oampos+1]; - const unsigned ydiff = spy - nly; + unsigned const spy = p.spriteMapper.oamram()[oampos ]; + unsigned const spx = p.spriteMapper.oamram()[oampos+1]; + unsigned const ydiff = spy - nly; if (ls ? ydiff < 16u : ydiff - 8u < 8u) { p.spriteList[nextSprite].spx = spx; @@ -192,15 +197,38 @@ namespace M2 { p.nextSprite = 0; nextCall(0, M3Start::f0_, p); } - }*/ + } +} +*/ + +static int loadTileDataByte0(PPUPriv const &p) { + unsigned const yoffset = p.winDrawState & win_draw_started + ? p.winYPos + : p.scy + p.lyCounter.ly(); + + return p.vram[0x1000 + (p.nattrib << 10 & 0x2000) + - ((p.reg1 * 32 | p.lcdc << 8) & 0x1000) + + p.reg1 * 16 + + ((-(p.nattrib >> 6 & 1) ^ yoffset) & 7) * 2]; +} + +static int loadTileDataByte1(PPUPriv const &p) { + unsigned const yoffset = p.winDrawState & win_draw_started + ? p.winYPos + : p.scy + p.lyCounter.ly(); + + return p.vram[0x1000 + (p.nattrib << 10 & 0x2000) + - ((p.reg1 * 32 | p.lcdc << 8) & 0x1000) + + p.reg1 * 16 + + ((-(p.nattrib >> 6 & 1) ^ yoffset) & 7) * 2 + 1]; } namespace M3Start { static void f0(PPUPriv &p) { p.xpos = 0; - if (p.winDrawState & p.lcdc >> 5 & WIN_DRAW_START) { - p.winDrawState = WIN_DRAW_STARTED; + if ((p.winDrawState & win_draw_start) && lcdcWinEn(p)) { + p.winDrawState = win_draw_started; p.wscx = 8 + (p.scx & 7); ++p.winYPos; } else @@ -211,38 +239,33 @@ namespace M3Start { } static void f1(PPUPriv &p) { - while (p.xpos < MAX_M3START_CYCLES) { + while (p.xpos < max_m3start_cycles) { if ((p.xpos & 7) == (p.scx & 7)) break; switch (p.xpos & 7) { case 0: - if ((p.winDrawState & WIN_DRAW_STARTED) && (p.layersMask & LAYER_MASK_WINDOW)) { - p.reg1 = p.vram[(p.lcdc << 4 & 0x400) + (p.winYPos & 0xF8) * 4 + (p.wscx >> 3 & 0x1F) + 0x1800]; - p.nattrib = p.vram[(p.lcdc << 4 & 0x400) + (p.winYPos & 0xF8) * 4 + (p.wscx >> 3 & 0x1F) + 0x3800]; + if (p.winDrawState & win_draw_started) { + p.reg1 = p.vram[(p.lcdc << 4 & 0x400) + (p.winYPos & 0xF8) * 4 + + (p.wscx >> 3 & 0x1F) + 0x1800]; + p.nattrib = p.vram[(p.lcdc << 4 & 0x400) + (p.winYPos & 0xF8) * 4 + + (p.wscx >> 3 & 0x1F) + 0x3800]; } else { - p.reg1 = p.vram[((p.lcdc << 7 | p.scx >> 3) & 0x41F) + ((p.scy + p.lyCounter.ly()) & 0xF8) * 4 + 0x1800]; - p.nattrib = p.vram[((p.lcdc << 7 | p.scx >> 3) & 0x41F) + ((p.scy + p.lyCounter.ly()) & 0xF8) * 4 + 0x3800]; + p.reg1 = p.vram[((p.lcdc << 7 | p.scx >> 3) & 0x41F) + + ((p.scy + p.lyCounter.ly()) & 0xF8) * 4 + 0x1800]; + p.nattrib = p.vram[((p.lcdc << 7 | p.scx >> 3) & 0x41F) + + ((p.scy + p.lyCounter.ly()) & 0xF8) * 4 + 0x3800]; } break; case 2: - { - const unsigned yoffset = ((p.winDrawState & WIN_DRAW_STARTED) && (p.layersMask & LAYER_MASK_WINDOW)) ? p.winYPos : p.scy + p.lyCounter.ly(); - - p.reg0 = p.vram[0x1000 + (p.nattrib << 10 & 0x2000) - ((p.reg1 * 32 | p.lcdc << 8) & 0x1000) - + p.reg1 * 16 + ((-(p.nattrib >> 6 & 1) ^ yoffset) & 7) * 2]; - } - + p.reg0 = loadTileDataByte0(p); break; case 4: { - const unsigned yoffset = ((p.winDrawState & WIN_DRAW_STARTED) && (p.layersMask & LAYER_MASK_WINDOW)) ? p.winYPos : p.scy + p.lyCounter.ly(); - const unsigned r1 = p.vram[0x1000 + (p.nattrib << 10 & 0x2000) - ((p.reg1 * 32 | p.lcdc << 8) & 0x1000) - + p.reg1 * 16 + ((-(p.nattrib >> 6 & 1) ^ yoffset) & 7) * 2 + 1]; - - p.ntileword = (expand_lut + (p.nattrib << 3 & 0x100))[p.reg0] + - (expand_lut + (p.nattrib << 3 & 0x100))[r1 ] * 2; + int const r1 = loadTileDataByte1(p); + p.ntileword = (expand_lut + (p.nattrib << 3 & 0x100))[p.reg0] + + (expand_lut + (p.nattrib << 3 & 0x100))[r1 ] * 2; } break; @@ -255,14 +278,14 @@ namespace M3Start { } { - const unsigned ly = p.lyCounter.ly(); - const unsigned numSprites = p.spriteMapper.numSprites(ly); - const unsigned char *const sprites = p.spriteMapper.sprites(ly); + unsigned const ly = p.lyCounter.ly(); + unsigned const numSprites = p.spriteMapper.numSprites(ly); + unsigned char const *const sprites = p.spriteMapper.sprites(ly); for (unsigned i = 0; i < numSprites; ++i) { - const unsigned pos = sprites[i]; - const unsigned spy = p.spriteMapper.posbuf()[pos ]; - const unsigned spx = p.spriteMapper.posbuf()[pos+1]; + unsigned pos = sprites[i]; + unsigned spy = p.spriteMapper.posbuf()[pos ]; + unsigned spx = p.spriteMapper.posbuf()[pos+1]; p.spriteList[i].spx = spx; p.spriteList[i].line = ly + 16u - spy; @@ -277,7 +300,7 @@ namespace M3Start { p.xpos = 0; p.endx = 8 - (p.scx & 7); - static const PPUState *const flut[8] = { + static PPUState const *const flut[8] = { &M3Loop::Tile::f0_, &M3Loop::Tile::f1_, &M3Loop::Tile::f2_, @@ -293,220 +316,20 @@ namespace M3Start { } namespace M3Loop { - static void doFullTilesUnrolledDmg(PPUPriv &p, const int xend, uint_least32_t *const dbufline, - const unsigned char *const tileMapLine, const unsigned tileline, unsigned tileMapXpos) { - const unsigned tileIndexSign = ~p.lcdc << 3 & 0x80; - const unsigned char *const tileDataLine = p.vram + tileIndexSign * 32 + tileline * 2; - int xpos = p.xpos; - do { - int nextSprite = p.nextSprite; +static void doFullTilesUnrolledDmg(PPUPriv &p, int const xend, uint_least32_t *const dbufline, + unsigned char const *const tileMapLine, unsigned const tileline, unsigned tileMapXpos) { + unsigned const tileIndexSign = ~p.lcdc << 3 & 0x80; + unsigned char const *const tileDataLine = p.vram + tileIndexSign * 32 + tileline * 2; + int xpos = p.xpos; - if (int(p.spriteList[nextSprite].spx) < xpos + 8) { - int cycles = p.cycles - 8; + do { + int nextSprite = p.nextSprite; - if (p.lcdc & 2) { - cycles -= std::max(11 - (int(p.spriteList[nextSprite].spx) - xpos), 6); + if (int(p.spriteList[nextSprite].spx) < xpos + 8) { + int cycles = p.cycles - 8; - for (unsigned i = nextSprite + 1; int(p.spriteList[i].spx) < xpos + 8; ++i) - cycles -= 6; - - if (cycles < 0) - break; - - p.cycles = cycles; - - do { - unsigned reg0, reg1 = p.spriteMapper.oamram()[p.spriteList[nextSprite].oampos + 2] * 16; - const unsigned attrib = p.spriteMapper.oamram()[p.spriteList[nextSprite].oampos + 3]; - - { - const unsigned spline = (attrib & 0x40 ? - p.spriteList[nextSprite].line ^ 15 : p.spriteList[nextSprite].line) * 2; - reg0 = p.vram[(p.lcdc & 4 ? (reg1 & ~16) | spline : reg1 | (spline & ~16)) ]; - reg1 = p.vram[(p.lcdc & 4 ? (reg1 & ~16) | spline : reg1 | (spline & ~16)) + 1]; - } - - p.spwordList[nextSprite] = expand_lut[reg0 + (attrib << 3 & 0x100)] - + expand_lut[reg1 + (attrib << 3 & 0x100)] * 2; - p.spriteList[nextSprite].attrib = attrib; - ++nextSprite; - } while (int(p.spriteList[nextSprite].spx) < xpos + 8); - } else { - if (cycles < 0) - break; - - p.cycles = cycles; - - do { - ++nextSprite; - } while (int(p.spriteList[nextSprite].spx) < xpos + 8); - } - - p.nextSprite = nextSprite; - } else if (nextSprite-1 < 0 || int(p.spriteList[nextSprite-1].spx) <= xpos - 8) { - if (!(p.cycles & ~7)) - break; - - int n = (( xend + 7 < int(p.spriteList[nextSprite].spx) - ? xend + 7 : int(p.spriteList[nextSprite].spx)) - xpos) & ~7; - n = (p.cycles & ~7) < n ? p.cycles & ~7 : n; - p.cycles -= n; - - unsigned ntileword = p.ntileword; - uint_least32_t * dst = dbufline + xpos - 8; - uint_least32_t *const dstend = dst + n; - xpos += n; - - if (!(p.lcdc & 1)) { - do { *dst++ = p.bgPalette[0]; } while (dst != dstend); - tileMapXpos += n >> 3; - - unsigned const tno = tileMapLine[(tileMapXpos - 1) & 0x1F]; - ntileword = expand_lut[(tileDataLine + tno * 16 - (tno & tileIndexSign) * 32)[0]] - + expand_lut[(tileDataLine + tno * 16 - (tno & tileIndexSign) * 32)[1]] * 2; - } else do { - dst[0] = p.bgPalette[ ntileword & 0x0003 ]; - dst[1] = p.bgPalette[(ntileword & 0x000C) >> 2]; - dst[2] = p.bgPalette[(ntileword & 0x0030) >> 4]; - dst[3] = p.bgPalette[(ntileword & 0x00C0) >> 6]; - dst[4] = p.bgPalette[(ntileword & 0x0300) >> 8]; - dst[5] = p.bgPalette[(ntileword & 0x0C00) >> 10]; - dst[6] = p.bgPalette[(ntileword & 0x3000) >> 12]; - dst[7] = p.bgPalette[ ntileword >> 14]; - dst += 8; - - unsigned const tno = tileMapLine[tileMapXpos & 0x1F]; - tileMapXpos = (tileMapXpos & 0x1F) + 1; - ntileword = expand_lut[(tileDataLine + tno * 16 - (tno & tileIndexSign) * 32)[0]] - + expand_lut[(tileDataLine + tno * 16 - (tno & tileIndexSign) * 32)[1]] * 2; - } while (dst != dstend); - - p.ntileword = ntileword; - continue; - } else { - int cycles = p.cycles - 8; - - if (cycles < 0) - break; - - p.cycles = cycles; - } - - { - uint_least32_t *const dst = dbufline + (xpos - 8); - const unsigned tileword = -(p.lcdc & 1U) & p.ntileword; - - dst[0] = p.bgPalette[ tileword & 0x0003 ]; - dst[1] = p.bgPalette[(tileword & 0x000C) >> 2]; - dst[2] = p.bgPalette[(tileword & 0x0030) >> 4]; - dst[3] = p.bgPalette[(tileword & 0x00C0) >> 6]; - dst[4] = p.bgPalette[(tileword & 0x0300) >> 8]; - dst[5] = p.bgPalette[(tileword & 0x0C00) >> 10]; - dst[6] = p.bgPalette[(tileword & 0x3000) >> 12]; - dst[7] = p.bgPalette[ tileword >> 14]; - - int i = nextSprite - 1; - - if(p.layersMask & LAYER_MASK_OBJ) - { - if (!(p.lcdc & 2)) { - do { - const int pos = int(p.spriteList[i].spx) - xpos; - p.spwordList[i] >>= pos * 2 >= 0 ? 16 - pos * 2 : 16 + pos * 2; - --i; - } while (i >= 0 && int(p.spriteList[i].spx) > xpos - 8); - } else { - do { - int n; - int pos = int(p.spriteList[i].spx) - xpos; - - if (pos < 0) { - n = pos + 8; - pos = 0; - } else - n = 8 - pos; - - const unsigned attrib = p.spriteList[i].attrib; - unsigned spword = p.spwordList[i]; - const unsigned long *const spPalette = p.spPalette + (attrib >> 2 & 4); - uint_least32_t *d = dst + pos; - - if (!(attrib & 0x80)) { - switch (n) { - case 8: if (spword >> 14 ) { d[7] = spPalette[spword >> 14 ]; } - case 7: if (spword >> 12 & 3) { d[6] = spPalette[spword >> 12 & 3]; } - case 6: if (spword >> 10 & 3) { d[5] = spPalette[spword >> 10 & 3]; } - case 5: if (spword >> 8 & 3) { d[4] = spPalette[spword >> 8 & 3]; } - case 4: if (spword >> 6 & 3) { d[3] = spPalette[spword >> 6 & 3]; } - case 3: if (spword >> 4 & 3) { d[2] = spPalette[spword >> 4 & 3]; } - case 2: if (spword >> 2 & 3) { d[1] = spPalette[spword >> 2 & 3]; } - case 1: if (spword & 3) { d[0] = spPalette[spword & 3]; } - } - - spword >>= n * 2; - - /*do { - if (spword & 3) - dst[pos] = spPalette[spword & 3]; - - spword >>= 2; - ++pos; - } while (--n);*/ - } else { - unsigned tw = tileword >> pos * 2; - d += n; - n = -n; - - do { - if (spword & 3) { - d[n] = tw & 3 ? p.bgPalette[tw & 3] - : spPalette[spword & 3]; - } - - spword >>= 2; - tw >>= 2; - } while (++n); - } - - p.spwordList[i] = spword; - --i; - } while (i >= 0 && int(p.spriteList[i].spx) > xpos - 8); - } - } - } - - { - unsigned const tno = tileMapLine[tileMapXpos & 0x1F]; - tileMapXpos = (tileMapXpos & 0x1F) + 1; - p.ntileword = expand_lut[(tileDataLine + tno * 16 - (tno & tileIndexSign) * 32)[0]] - + expand_lut[(tileDataLine + tno * 16 - (tno & tileIndexSign) * 32)[1]] * 2; - } - - xpos = xpos + 8; - } while (xpos < xend); - - p.xpos = xpos; - } - - static void doFullTilesUnrolledCgb(PPUPriv &p, const int xend, uint_least32_t *const dbufline, - const unsigned char *const tileMapLine, const unsigned tileline, unsigned tileMapXpos) { - int xpos = p.xpos; - const unsigned char *const vram = p.vram; - const unsigned tdoffset = tileline * 2 + (~p.lcdc & 0x10) * 0x100; - - if(!(p.layersMask & LAYER_MASK_BG)) - { - for(int x=xpos,i=0;x> 2]; - dst[2] = bgPalette[(ntileword & 0x0030) >> 4]; - dst[3] = bgPalette[(ntileword & 0x00C0) >> 6]; - dst[4] = bgPalette[(ntileword & 0x0300) >> 8]; - dst[5] = bgPalette[(ntileword & 0x0C00) >> 10]; - dst[6] = bgPalette[(ntileword & 0x3000) >> 12]; - dst[7] = bgPalette[ ntileword >> 14]; - dst += 8; - - unsigned const tno = tileMapLine[ tileMapXpos & 0x1F ]; - nattrib = tileMapLine[(tileMapXpos & 0x1F) + 0x2000]; - tileMapXpos = (tileMapXpos & 0x1F) + 1; - - unsigned const tdo = tdoffset & ~(tno << 5); - unsigned char const *const td = vram + tno * 16 - + (nattrib & 0x40 ? tdo ^ 14 : tdo) + (nattrib << 10 & 0x2000); - unsigned short const *const explut = expand_lut + (nattrib << 3 & 0x100); - ntileword = explut[td[0]] + explut[td[1]] * 2; - } while (dst != dstend); - - p.ntileword = ntileword; - p.nattrib = nattrib; - continue; } else { - int cycles = p.cycles - 8; - if (cycles < 0) break; p.cycles = cycles; + + do { + ++nextSprite; + } while (int(p.spriteList[nextSprite].spx) < xpos + 8); } - { - uint_least32_t *const dst = dbufline + (xpos - 8); - const unsigned tileword = p.ntileword; - const unsigned attrib = p.nattrib; - const unsigned long *const bgPalette = p.bgPalette + (attrib & 7) * 4; + p.nextSprite = nextSprite; + } else if (nextSprite-1 < 0 || int(p.spriteList[nextSprite-1].spx) <= xpos - 8) { + if (!(p.cycles & ~7)) + break; - dst[0] = bgPalette[ tileword & 0x0003 ]; - dst[1] = bgPalette[(tileword & 0x000C) >> 2]; - dst[2] = bgPalette[(tileword & 0x0030) >> 4]; - dst[3] = bgPalette[(tileword & 0x00C0) >> 6]; - dst[4] = bgPalette[(tileword & 0x0300) >> 8]; - dst[5] = bgPalette[(tileword & 0x0C00) >> 10]; - dst[6] = bgPalette[(tileword & 0x3000) >> 12]; - dst[7] = bgPalette[ tileword >> 14]; + int n = (( xend + 7 < int(p.spriteList[nextSprite].spx) + ? xend + 7 : int(p.spriteList[nextSprite].spx)) - xpos) & ~7; + n = (p.cycles & ~7) < n ? p.cycles & ~7 : n; + p.cycles -= n; - int i = nextSprite - 1; + unsigned ntileword = p.ntileword; + uint_least32_t * dst = dbufline + xpos - 8; + uint_least32_t *const dstend = dst + n; + xpos += n; - if(p.layersMask & LAYER_MASK_OBJ) - { - if (!(p.lcdc & 2)) { - do { - const int pos = int(p.spriteList[i].spx) - xpos; - p.spwordList[i] >>= pos * 2 >= 0 ? 16 - pos * 2 : 16 + pos * 2; - --i; - } while (i >= 0 && int(p.spriteList[i].spx) > xpos - 8); - } else { - unsigned char idtab[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; - const unsigned bgenmask = p.lcdc << 7 & 0x80; + if (!lcdcBgEn(p)) { + do { *dst++ = p.bgPalette[0]; } while (dst != dstend); + tileMapXpos += n >> 3; - do { - int n; - int pos = int(p.spriteList[i].spx) - xpos; + unsigned const tno = tileMapLine[(tileMapXpos - 1) & 0x1F]; + ntileword = expand_lut[(tileDataLine + tno * 16 - (tno & tileIndexSign) * 32)[0]] + + expand_lut[(tileDataLine + tno * 16 - (tno & tileIndexSign) * 32)[1]] * 2; + } else do { + dst[0] = p.bgPalette[ ntileword & 0x0003 ]; + dst[1] = p.bgPalette[(ntileword & 0x000C) >> 2]; + dst[2] = p.bgPalette[(ntileword & 0x0030) >> 4]; + dst[3] = p.bgPalette[(ntileword & 0x00C0) >> 6]; + dst[4] = p.bgPalette[(ntileword & 0x0300) >> 8]; + dst[5] = p.bgPalette[(ntileword & 0x0C00) >> 10]; + dst[6] = p.bgPalette[(ntileword & 0x3000) >> 12]; + dst[7] = p.bgPalette[ ntileword >> 14]; + dst += 8; - if (pos < 0) { - n = pos + 8; - pos = 0; - } else - n = 8 - pos; + unsigned const tno = tileMapLine[tileMapXpos & 0x1F]; + tileMapXpos = (tileMapXpos & 0x1F) + 1; + ntileword = expand_lut[(tileDataLine + tno * 16 - (tno & tileIndexSign) * 32)[0]] + + expand_lut[(tileDataLine + tno * 16 - (tno & tileIndexSign) * 32)[1]] * 2; + } while (dst != dstend); - const unsigned char id = p.spriteList[i].oampos; - const unsigned sattrib = p.spriteList[i].attrib; - unsigned spword = p.spwordList[i]; - const unsigned long *const spPalette = p.spPalette + (sattrib & 7) * 4; + p.ntileword = ntileword; + continue; + } else { + int cycles = p.cycles - 8; + if (cycles < 0) + break; - if (!((attrib | sattrib) & bgenmask)) { - unsigned char *const idt = idtab + pos; - uint_least32_t *const d = dst + pos; + p.cycles = cycles; + } - switch (n) { - case 8: if ((spword >> 14 ) && id < idt[7]) { - idt[7] = id; - d[7] = spPalette[spword >> 14 ]; - } - case 7: if ((spword >> 12 & 3) && id < idt[6]) { - idt[6] = id; - d[6] = spPalette[spword >> 12 & 3]; - } - case 6: if ((spword >> 10 & 3) && id < idt[5]) { - idt[5] = id; - d[5] = spPalette[spword >> 10 & 3]; - } - case 5: if ((spword >> 8 & 3) && id < idt[4]) { - idt[4] = id; - d[4] = spPalette[spword >> 8 & 3]; - } - case 4: if ((spword >> 6 & 3) && id < idt[3]) { - idt[3] = id; - d[3] = spPalette[spword >> 6 & 3]; - } - case 3: if ((spword >> 4 & 3) && id < idt[2]) { - idt[2] = id; - d[2] = spPalette[spword >> 4 & 3]; - } - case 2: if ((spword >> 2 & 3) && id < idt[1]) { - idt[1] = id; - d[1] = spPalette[spword >> 2 & 3]; - } - case 1: if ((spword & 3) && id < idt[0]) { - idt[0] = id; - d[0] = spPalette[spword & 3]; - } - } + { + uint_least32_t *const dst = dbufline + (xpos - 8); + unsigned const tileword = -(p.lcdc & 1U) & p.ntileword; - spword >>= n * 2; + dst[0] = p.bgPalette[ tileword & 0x0003 ]; + dst[1] = p.bgPalette[(tileword & 0x000C) >> 2]; + dst[2] = p.bgPalette[(tileword & 0x0030) >> 4]; + dst[3] = p.bgPalette[(tileword & 0x00C0) >> 6]; + dst[4] = p.bgPalette[(tileword & 0x0300) >> 8]; + dst[5] = p.bgPalette[(tileword & 0x0C00) >> 10]; + dst[6] = p.bgPalette[(tileword & 0x3000) >> 12]; + dst[7] = p.bgPalette[ tileword >> 14]; - /*do { - if ((spword & 3) && id < idtab[pos]) { - idtab[pos] = id; + int i = nextSprite - 1; + + if (!lcdcObjEn(p)) { + do { + int pos = int(p.spriteList[i].spx) - xpos; + p.spwordList[i] >>= pos * 2 >= 0 ? 16 - pos * 2 : 16 + pos * 2; + --i; + } while (i >= 0 && int(p.spriteList[i].spx) > xpos - 8); + } else { + do { + int n; + int pos = int(p.spriteList[i].spx) - xpos; + if (pos < 0) { + n = pos + 8; + pos = 0; + } else + n = 8 - pos; + + unsigned const attrib = p.spriteList[i].attrib; + unsigned spword = p.spwordList[i]; + unsigned long const *const spPalette = p.spPalette + (attrib >> 2 & 4); + uint_least32_t *d = dst + pos; + + if (!(attrib & attr_bgpriority)) { + switch (n) { + case 8: if (spword >> 14 ) { d[7] = spPalette[spword >> 14 ]; } // fallthrough + case 7: if (spword >> 12 & 3) { d[6] = spPalette[spword >> 12 & 3]; } // fallthrough + case 6: if (spword >> 10 & 3) { d[5] = spPalette[spword >> 10 & 3]; } // fallthrough + case 5: if (spword >> 8 & 3) { d[4] = spPalette[spword >> 8 & 3]; } // fallthrough + case 4: if (spword >> 6 & 3) { d[3] = spPalette[spword >> 6 & 3]; } // fallthrough + case 3: if (spword >> 4 & 3) { d[2] = spPalette[spword >> 4 & 3]; } // fallthrough + case 2: if (spword >> 2 & 3) { d[1] = spPalette[spword >> 2 & 3]; } // fallthrough + case 1: if (spword & 3) { d[0] = spPalette[spword & 3]; } + } + + spword >>= n * 2; + + /*do { + if (spword & 3) dst[pos] = spPalette[spword & 3]; - } - spword >>= 2; - ++pos; - } while (--n);*/ - } else { - unsigned tw = tileword >> pos * 2; + spword >>= 2; + ++pos; + } while (--n);*/ + } else { + unsigned tw = tileword >> pos * 2; + d += n; + n = -n; - do { - if ((spword & 3) && id < idtab[pos]) { - idtab[pos] = id; - dst[pos] = tw & 3 ? bgPalette[tw & 3] : spPalette[spword & 3]; - } - - spword >>= 2; - tw >>= 2; - ++pos; - } while (--n); + do { + if (spword & 3) { + d[n] = tw & 3 + ? p.bgPalette[ tw & 3] + : spPalette[spword & 3]; } - p.spwordList[i] = spword; - --i; - } while (i >= 0 && int(p.spriteList[i].spx) > xpos - 8); + spword >>= 2; + tw >>= 2; + } while (++n); } - } - } - { - unsigned const tno = tileMapLine[ tileMapXpos & 0x1F ]; - unsigned const nattrib = tileMapLine[(tileMapXpos & 0x1F) + 0x2000]; + p.spwordList[i] = spword; + --i; + } while (i >= 0 && int(p.spriteList[i].spx) > xpos - 8); + } + } + + unsigned const tno = tileMapLine[tileMapXpos & 0x1F]; + tileMapXpos = (tileMapXpos & 0x1F) + 1; + p.ntileword = expand_lut[(tileDataLine + tno * 16 - (tno & tileIndexSign) * 32)[0]] + + expand_lut[(tileDataLine + tno * 16 - (tno & tileIndexSign) * 32)[1]] * 2; + + xpos = xpos + 8; + } while (xpos < xend); + + p.xpos = xpos; +} + +static void doFullTilesUnrolledCgb(PPUPriv &p, int const xend, uint_least32_t *const dbufline, + unsigned char const *const tileMapLine, unsigned const tileline, unsigned tileMapXpos) { + int xpos = p.xpos; + unsigned char const *const vram = p.vram; + unsigned const tdoffset = tileline * 2 + (~p.lcdc & 0x10) * 0x100; + + do { + int nextSprite = p.nextSprite; + + if (int(p.spriteList[nextSprite].spx) < xpos + 8) { + int cycles = p.cycles - 8; + cycles -= std::max(11 - (int(p.spriteList[nextSprite].spx) - xpos), 6); + + for (unsigned i = nextSprite + 1; int(p.spriteList[i].spx) < xpos + 8; ++i) + cycles -= 6; + + if (cycles < 0) + break; + + p.cycles = cycles; + + do { + unsigned char const *const oam = p.spriteMapper.oamram(); + unsigned reg0, reg1 = oam[p.spriteList[nextSprite].oampos + 2] * 16; + unsigned const attrib = oam[p.spriteList[nextSprite].oampos + 3]; + unsigned const spline = ( attrib & attr_yflip + ? p.spriteList[nextSprite].line ^ 15 + : p.spriteList[nextSprite].line ) * 2; + + reg0 = vram[(attrib << 10 & 0x2000) + + (lcdcObj2x(p) ? (reg1 & ~16) | spline : reg1 | (spline & ~16)) ]; + reg1 = vram[(attrib << 10 & 0x2000) + + (lcdcObj2x(p) ? (reg1 & ~16) | spline : reg1 | (spline & ~16)) + 1]; + + p.spwordList[nextSprite] = expand_lut[reg0 + (attrib << 3 & 0x100)] + + expand_lut[reg1 + (attrib << 3 & 0x100)] * 2; + p.spriteList[nextSprite].attrib = attrib; + ++nextSprite; + } while (int(p.spriteList[nextSprite].spx) < xpos + 8); + + p.nextSprite = nextSprite; + } else if (nextSprite-1 < 0 || int(p.spriteList[nextSprite-1].spx) <= xpos - 8) { + if (!(p.cycles & ~7)) + break; + + int n = (( xend + 7 < int(p.spriteList[nextSprite].spx) + ? xend + 7 : int(p.spriteList[nextSprite].spx)) - xpos) & ~7; + n = (p.cycles & ~7) < n ? p.cycles & ~7 : n; + p.cycles -= n; + + unsigned ntileword = p.ntileword; + unsigned nattrib = p.nattrib; + uint_least32_t * dst = dbufline + xpos - 8; + uint_least32_t *const dstend = dst + n; + xpos += n; + + do { + unsigned long const *const bgPalette = p.bgPalette + (nattrib & 7) * 4; + dst[0] = bgPalette[ ntileword & 0x0003 ]; + dst[1] = bgPalette[(ntileword & 0x000C) >> 2]; + dst[2] = bgPalette[(ntileword & 0x0030) >> 4]; + dst[3] = bgPalette[(ntileword & 0x00C0) >> 6]; + dst[4] = bgPalette[(ntileword & 0x0300) >> 8]; + dst[5] = bgPalette[(ntileword & 0x0C00) >> 10]; + dst[6] = bgPalette[(ntileword & 0x3000) >> 12]; + dst[7] = bgPalette[ ntileword >> 14]; + dst += 8; + + unsigned const tno = tileMapLine[ tileMapXpos & 0x1F ]; + nattrib = tileMapLine[(tileMapXpos & 0x1F) + 0x2000]; tileMapXpos = (tileMapXpos & 0x1F) + 1; unsigned const tdo = tdoffset & ~(tno << 5); unsigned char const *const td = vram + tno * 16 - + (nattrib & 0x40 ? tdo ^ 14 : tdo) + (nattrib << 10 & 0x2000); + + (nattrib & attr_yflip ? tdo ^ 14 : tdo) + + (nattrib << 10 & 0x2000); unsigned short const *const explut = expand_lut + (nattrib << 3 & 0x100); - p.ntileword = explut[td[0]] + explut[td[1]] * 2; - p.nattrib = nattrib; + ntileword = explut[td[0]] + explut[td[1]] * 2; + } while (dst != dstend); + + p.ntileword = ntileword; + p.nattrib = nattrib; + continue; + } else { + int cycles = p.cycles - 8; + + if (cycles < 0) + break; + + p.cycles = cycles; + } + + { + uint_least32_t *const dst = dbufline + (xpos - 8); + unsigned const tileword = p.ntileword; + unsigned const attrib = p.nattrib; + unsigned long const *const bgPalette = p.bgPalette + (attrib & 7) * 4; + + dst[0] = bgPalette[ tileword & 0x0003 ]; + dst[1] = bgPalette[(tileword & 0x000C) >> 2]; + dst[2] = bgPalette[(tileword & 0x0030) >> 4]; + dst[3] = bgPalette[(tileword & 0x00C0) >> 6]; + dst[4] = bgPalette[(tileword & 0x0300) >> 8]; + dst[5] = bgPalette[(tileword & 0x0C00) >> 10]; + dst[6] = bgPalette[(tileword & 0x3000) >> 12]; + dst[7] = bgPalette[ tileword >> 14]; + + int i = nextSprite - 1; + + if (!lcdcObjEn(p)) { + do { + int pos = int(p.spriteList[i].spx) - xpos; + p.spwordList[i] >>= pos * 2 >= 0 ? 16 - pos * 2 : 16 + pos * 2; + --i; + } while (i >= 0 && int(p.spriteList[i].spx) > xpos - 8); + } else { + unsigned char idtab[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + unsigned const bgprioritymask = p.lcdc << 7; + + do { + int n; + int pos = int(p.spriteList[i].spx) - xpos; + + if (pos < 0) { + n = pos + 8; + pos = 0; + } else + n = 8 - pos; + + unsigned char const id = p.spriteList[i].oampos; + unsigned const sattrib = p.spriteList[i].attrib; + unsigned spword = p.spwordList[i]; + unsigned long const *const spPalette = p.spPalette + (sattrib & 7) * 4; + + if (!((attrib | sattrib) & bgprioritymask)) { + unsigned char *const idt = idtab + pos; + uint_least32_t *const d = dst + pos; + + switch (n) { + case 8: if ((spword >> 14 ) && id < idt[7]) { + idt[7] = id; + d[7] = spPalette[spword >> 14 ]; + } // fallthrough + case 7: if ((spword >> 12 & 3) && id < idt[6]) { + idt[6] = id; + d[6] = spPalette[spword >> 12 & 3]; + } // fallthrough + case 6: if ((spword >> 10 & 3) && id < idt[5]) { + idt[5] = id; + d[5] = spPalette[spword >> 10 & 3]; + } // fallthrough + case 5: if ((spword >> 8 & 3) && id < idt[4]) { + idt[4] = id; + d[4] = spPalette[spword >> 8 & 3]; + } // fallthrough + case 4: if ((spword >> 6 & 3) && id < idt[3]) { + idt[3] = id; + d[3] = spPalette[spword >> 6 & 3]; + } // fallthrough + case 3: if ((spword >> 4 & 3) && id < idt[2]) { + idt[2] = id; + d[2] = spPalette[spword >> 4 & 3]; + } // fallthrough + case 2: if ((spword >> 2 & 3) && id < idt[1]) { + idt[1] = id; + d[1] = spPalette[spword >> 2 & 3]; + } // fallthrough + case 1: if ((spword & 3) && id < idt[0]) { + idt[0] = id; + d[0] = spPalette[spword & 3]; + } + } + + spword >>= n * 2; + + /*do { + if ((spword & 3) && id < idtab[pos]) { + idtab[pos] = id; + dst[pos] = spPalette[spword & 3]; + } + + spword >>= 2; + ++pos; + } while (--n);*/ + } else { + unsigned tw = tileword >> pos * 2; + + do { + if ((spword & 3) && id < idtab[pos]) { + idtab[pos] = id; + dst[pos] = tw & 3 + ? bgPalette[ tw & 3] + : spPalette[spword & 3]; + } + + spword >>= 2; + tw >>= 2; + ++pos; + } while (--n); + } + + p.spwordList[i] = spword; + --i; + } while (i >= 0 && int(p.spriteList[i].spx) > xpos - 8); } + } - xpos = xpos + 8; - } while (xpos < xend); + { + unsigned const tno = tileMapLine[ tileMapXpos & 0x1F ]; + unsigned const nattrib = tileMapLine[(tileMapXpos & 0x1F) + 0x2000]; + tileMapXpos = (tileMapXpos & 0x1F) + 1; - p.xpos = xpos; + unsigned const tdo = tdoffset & ~(tno << 5); + unsigned char const *const td = vram + tno * 16 + + (nattrib & attr_yflip ? tdo ^ 14 : tdo) + + (nattrib << 10 & 0x2000); + unsigned short const *const explut = expand_lut + (nattrib << 3 & 0x100); + p.ntileword = explut[td[0]] + explut[td[1]] * 2; + p.nattrib = nattrib; + } + + xpos = xpos + 8; + } while (xpos < xend); + + p.xpos = xpos; +} + +static void doFullTilesUnrolled(PPUPriv &p) { + int xpos = p.xpos; + int const xend = static_cast(p.wx) < xpos || p.wx >= 168 + ? 161 + : static_cast(p.wx) - 7; + if (xpos >= xend) + return; + + uint_least32_t *const dbufline = p.framebuf.fbline(); + unsigned char const *tileMapLine; + unsigned tileline; + unsigned tileMapXpos; + + if (p.winDrawState & win_draw_started) { + tileMapLine = p.vram + (p.lcdc << 4 & 0x400) + + (p.winYPos & 0xF8) * 4 + 0x1800; + tileMapXpos = (xpos + p.wscx) >> 3; + tileline = p.winYPos & 7; + } else { + tileMapLine = p.vram + (p.lcdc << 7 & 0x400) + + ((p.scy + p.lyCounter.ly()) & 0xF8) * 4 + 0x1800; + tileMapXpos = (p.scx + xpos + 1 - p.cgb) >> 3; + tileline = (p.scy + p.lyCounter.ly()) & 7; } - static void doFullTilesUnrolled(PPUPriv &p) { - int xpos = p.xpos; - const int xend = int(p.wx) < xpos || p.wx >= 168 ? 161 : int(p.wx) - 7; - - if (xpos >= xend) - return; - - uint_least32_t *const dbufline = p.framebuf.fbline(); - const unsigned char *tileMapLine; - unsigned tileline; - unsigned tileMapXpos; - - if ((p.winDrawState & WIN_DRAW_STARTED) && (p.layersMask & LAYER_MASK_WINDOW)) { - tileMapLine = p.vram + (p.lcdc << 4 & 0x400) + (p.winYPos & 0xF8) * 4 + 0x1800; - tileMapXpos = (xpos + p.wscx) >> 3; - tileline = p.winYPos & 7; - } else { - tileMapLine = p.vram + (p.lcdc << 7 & 0x400) + ((p.scy + p.lyCounter.ly()) & 0xF8) * 4 + 0x1800; - tileMapXpos = (p.scx + xpos + 1 - p.cgb) >> 3; - tileline = (p.scy + p.lyCounter.ly()) & 7; - } - - if (xpos < 8) { - uint_least32_t prebuf[16]; - - if (p.cgb) { - doFullTilesUnrolledCgb(p, xend < 8 ? xend : 8, prebuf + (8 - xpos), tileMapLine, tileline, tileMapXpos); - } else - doFullTilesUnrolledDmg(p, xend < 8 ? xend : 8, prebuf + (8 - xpos), tileMapLine, tileline, tileMapXpos); - - const int newxpos = p.xpos; - - if (newxpos > 8) { - std::memcpy(dbufline, prebuf + (8 - xpos), (newxpos - 8) * sizeof *dbufline); - } else if (newxpos < 8) - return; - - if (newxpos >= xend) - return; - - tileMapXpos += (newxpos - xpos) >> 3; - } + if (xpos < 8) { + uint_least32_t prebuf[16]; if (p.cgb) { - doFullTilesUnrolledCgb(p, xend, dbufline, tileMapLine, tileline, tileMapXpos); - } else - doFullTilesUnrolledDmg(p, xend, dbufline, tileMapLine, tileline, tileMapXpos); + doFullTilesUnrolledCgb(p, xend < 8 ? xend : 8, prebuf + (8 - xpos), + tileMapLine, tileline, tileMapXpos); + } else { + doFullTilesUnrolledDmg(p, xend < 8 ? xend : 8, prebuf + (8 - xpos), + tileMapLine, tileline, tileMapXpos); + } + + int const newxpos = p.xpos; + + if (newxpos > 8) { + std::memcpy(dbufline, prebuf + (8 - xpos), (newxpos - 8) * sizeof *dbufline); + } else if (newxpos < 8) + return; + + if (newxpos >= xend) + return; + + tileMapXpos += (newxpos - xpos) >> 3; } - static void plotPixel(PPUPriv &p) { - //ZING! - const int xpos = p.xpos; - const unsigned tileword = p.tileword; - uint_least32_t *const fbline = p.framebuf.fbline(); + if (p.cgb) { + doFullTilesUnrolledCgb(p, xend, dbufline, tileMapLine, tileline, tileMapXpos); + } else + doFullTilesUnrolledDmg(p, xend, dbufline, tileMapLine, tileline, tileMapXpos); +} - if (int(p.wx) == xpos && (p.weMaster || (p.wy2 == p.lyCounter.ly() && (p.lcdc & 0x20))) && xpos < 167) { - if (p.winDrawState == 0 && (p.lcdc & 0x20)) { - p.winDrawState = WIN_DRAW_START | WIN_DRAW_STARTED; - ++p.winYPos; - } else if (!p.cgb && (p.winDrawState == 0 || xpos == 166)) - p.winDrawState |= WIN_DRAW_START; - } +static void plotPixel(PPUPriv &p) { + int const xpos = p.xpos; + unsigned const tileword = p.tileword; + uint_least32_t *const fbline = p.framebuf.fbline(); - const unsigned twdata = tileword & ((p.lcdc & 1) | p.cgb) * 3; - unsigned long pixel = p.bgPalette[twdata + (p.attrib & 7) * 4]; - int i = int(p.nextSprite) - 1; + if (static_cast(p.wx) == xpos + && (p.weMaster || (p.wy2 == p.lyCounter.ly() && lcdcWinEn(p))) + && xpos < 167) { + if (p.winDrawState == 0 && lcdcWinEn(p)) { + p.winDrawState = win_draw_start | win_draw_started; + ++p.winYPos; + } else if (!p.cgb && (p.winDrawState == 0 || xpos == 166)) + p.winDrawState |= win_draw_start; + } - if(!(p.layersMask & LAYER_MASK_BG)) - { - pixel = p.bgPalette[0]; //guessing? clobber the tile that was read - } + unsigned const twdata = tileword & ((p.lcdc & 1) | p.cgb) * 3; + unsigned long pixel = p.bgPalette[twdata + (p.attrib & 7) * 4]; + int i = static_cast(p.nextSprite) - 1; - if (i >= 0 && int(p.spriteList[i].spx) > xpos - 8) { - unsigned spdata = 0; - unsigned attrib = 0; + if (i >= 0 && int(p.spriteList[i].spx) > xpos - 8) { + unsigned spdata = 0; + unsigned attrib = 0; - if (p.cgb) { - unsigned minId = 0xFF; + if (p.cgb) { + unsigned minId = 0xFF; - do { - if ((p.spwordList[i] & 3) && p.spriteList[i].oampos < minId) { - spdata = p.spwordList[i] & 3; - attrib = p.spriteList[i].attrib; - minId = p.spriteList[i].oampos; - } - - p.spwordList[i] >>= 2; - --i; - } while (i >= 0 && int(p.spriteList[i].spx) > xpos - 8); - - if(p.layersMask & LAYER_MASK_OBJ) - { - if (spdata && (p.lcdc & 2) && (!((attrib | p.attrib) & 0x80) || !twdata || !(p.lcdc & 1))) - pixel = p.spPalette[(attrib & 7) * 4 + spdata]; + do { + if ((p.spwordList[i] & 3) && p.spriteList[i].oampos < minId) { + spdata = p.spwordList[i] & 3; + attrib = p.spriteList[i].attrib; + minId = p.spriteList[i].oampos; } - } else { - do { - if (p.spwordList[i] & 3) { - spdata = p.spwordList[i] & 3; - attrib = p.spriteList[i].attrib; - } - p.spwordList[i] >>= 2; - --i; - } while (i >= 0 && int(p.spriteList[i].spx) > xpos - 8); + p.spwordList[i] >>= 2; + --i; + } while (i >= 0 && int(p.spriteList[i].spx) > xpos - 8); - if(p.layersMask & LAYER_MASK_OBJ) - { - if (spdata && (p.lcdc & 2) && (!(attrib & 0x80) || !twdata)) - pixel = p.spPalette[(attrib >> 2 & 4) + spdata]; + if (spdata && lcdcObjEn(p) + && (!((attrib | p.attrib) & attr_bgpriority) || !twdata || !lcdcBgEn(p))) { + pixel = p.spPalette[(attrib & 7) * 4 + spdata]; + } + } else { + do { + if (p.spwordList[i] & 3) { + spdata = p.spwordList[i] & 3; + attrib = p.spriteList[i].attrib; } - } + + p.spwordList[i] >>= 2; + --i; + } while (i >= 0 && int(p.spriteList[i].spx) > xpos - 8); + + if (spdata && lcdcObjEn(p) && (!(attrib & attr_bgpriority) || !twdata)) + pixel = p.spPalette[(attrib >> 2 & 4) + spdata]; } - - if (xpos - 8 >= 0) - fbline[xpos - 8] = pixel; - - p.xpos = xpos + 1; - p.tileword = tileword >> 2; } - static void plotPixelIfNoSprite(PPUPriv &p) { - if (p.spriteList[p.nextSprite].spx == p.xpos) { - if (!((p.lcdc & 2) | p.cgb)) { - do { - ++p.nextSprite; - } while (p.spriteList[p.nextSprite].spx == p.xpos); + if (xpos - 8 >= 0) + fbline[xpos - 8] = pixel; - plotPixel(p); - } - } else - plotPixel(p); - } + p.xpos = xpos + 1; + p.tileword = tileword >> 2; +} - static unsigned long nextM2Time(const PPUPriv &p) { - unsigned long nextm2 = p.lyCounter.isDoubleSpeed() - ? p.lyCounter.time() + (weMasterCheckPriorToLyIncLineCycle(true ) + M2_DS_OFFSET) * 2 - 456 * 2 - : p.lyCounter.time() + weMasterCheckPriorToLyIncLineCycle(p.cgb) - 456 ; - - if (p.lyCounter.ly() == 143) - nextm2 += (456 * 10 + 456 - weMasterCheckPriorToLyIncLineCycle(p.cgb)) << p.lyCounter.isDoubleSpeed(); - - return nextm2; - } - - static void xpos168(PPUPriv &p) { - p.lastM0Time = p.now - (p.cycles << p.lyCounter.isDoubleSpeed()); - - const unsigned long nextm2 = nextM2Time(p); - - p.cycles = p.now >= nextm2 - ? (long(p.now - nextm2) >> p.lyCounter.isDoubleSpeed()) - : -(long(nextm2 - p.now) >> p.lyCounter.isDoubleSpeed()); - - nextCall(0, p.lyCounter.ly() == 143 ? M2::Ly0::f0_ : M2::LyNon0::f0_, p); - } - - static bool handleWinDrawStartReq(const PPUPriv &p, const int xpos, unsigned char &winDrawState) { - const bool startWinDraw = (xpos < 167 || p.cgb) && (winDrawState &= WIN_DRAW_STARTED); - - if (!(p.lcdc & 0x20)) - winDrawState &= ~WIN_DRAW_STARTED; - - return startWinDraw; - } - - static bool handleWinDrawStartReq(PPUPriv &p) { - return handleWinDrawStartReq(p, p.xpos, p.winDrawState); - } - - namespace StartWindowDraw { - static void inc(const PPUState &nextf, PPUPriv &p) { - if (!(p.lcdc & 0x20) && p.cgb) { - plotPixelIfNoSprite(p); - - if (p.xpos == p.endx) { - if (p.xpos < 168) { - nextCall(1,Tile::f0_,p); - } else - xpos168(p); - - return; - } - } - - nextCall(1,nextf,p); - } - - static void f0(PPUPriv &p) { - if (p.xpos == p.endx) { - p.tileword = p.ntileword; - p.attrib = p.nattrib; - p.endx = p.xpos < 160 ? p.xpos + 8 : 168; - } - - p.wscx = 8 - p.xpos; - - if ((p.winDrawState & WIN_DRAW_STARTED) && (p.layersMask & LAYER_MASK_WINDOW)) { - p.reg1 = p.vram[(p.lcdc << 4 & 0x400) + (p.winYPos & 0xF8) * 4 + 0x1800]; - p.nattrib = p.vram[(p.lcdc << 4 & 0x400) + (p.winYPos & 0xF8) * 4 + 0x3800]; - } else { - p.reg1 = p.vram[(p.lcdc << 7 & 0x400) + ((p.scy + p.lyCounter.ly()) & 0xF8) * 4 + 0x1800]; - p.nattrib = p.vram[(p.lcdc << 7 & 0x400) + ((p.scy + p.lyCounter.ly()) & 0xF8) * 4 + 0x3800]; - } - - inc(f1_,p); - } - - static void f1(PPUPriv &p) { - inc(f2_,p); - } - - static void f2(PPUPriv &p) { - const unsigned yoffset = ((p.winDrawState & WIN_DRAW_STARTED) && (p.layersMask & LAYER_MASK_WINDOW)) ? p.winYPos : p.scy + p.lyCounter.ly(); - - p.reg0 = p.vram[0x1000 + (p.nattrib << 10 & 0x2000) - - ((p.reg1 * 32 | p.lcdc << 8) & 0x1000) - + p.reg1 * 16 + ((-(p.nattrib >> 6 & 1) ^ yoffset) & 7) * 2]; - - inc(f3_,p); - } - - static void f3(PPUPriv &p) { - inc(f4_,p); - } - - static void f4(PPUPriv &p) { - const unsigned yoffset = ((p.winDrawState & WIN_DRAW_STARTED) && (p.layersMask & LAYER_MASK_WINDOW)) ? p.winYPos : p.scy + p.lyCounter.ly(); - const unsigned r1 = p.vram[0x1000 + (p.nattrib << 10 & 0x2000) - - ((p.reg1 * 32 | p.lcdc << 8) & 0x1000) - + p.reg1 * 16 + ((-(p.nattrib >> 6 & 1) ^ yoffset) & 7) * 2 + 1]; - - p.ntileword = (expand_lut + (p.nattrib << 3 & 0x100))[p.reg0] + - (expand_lut + (p.nattrib << 3 & 0x100))[r1 ] * 2; - - inc(f5_,p); - } - - static void f5(PPUPriv &p) { - inc(Tile::f0_,p); - } - }; - - namespace LoadSprites { - static void inc(const PPUState &nextf, PPUPriv &p) { - plotPixelIfNoSprite(p); - - if (p.xpos == p.endx) { - if (p.xpos < 168) { - nextCall(1,Tile::f0_,p); - } else - xpos168(p); - } else - nextCall(1,nextf,p); - } - - static void f0(PPUPriv &p) { - p.reg1 = p.spriteMapper.oamram()[p.spriteList[p.currentSprite].oampos + 2]; - nextCall(1,f1_,p); - } - - static void f1(PPUPriv &p) { - if ((p.winDrawState & WIN_DRAW_START) && handleWinDrawStartReq(p)) - return StartWindowDraw::f0(p); - - p.spriteList[p.currentSprite].attrib = p.spriteMapper.oamram()[p.spriteList[p.currentSprite].oampos + 3]; - inc(f2_,p); - } - - static void f2(PPUPriv &p) { - if ((p.winDrawState & WIN_DRAW_START) && handleWinDrawStartReq(p)) - return StartWindowDraw::f0(p); - - const unsigned spline = ((p.spriteList[p.currentSprite].attrib & 0x40) ? - p.spriteList[p.currentSprite].line ^ 15 : p.spriteList[p.currentSprite].line) * 2; - p.reg0 = p.vram[(p.spriteList[p.currentSprite].attrib << 10 & p.cgb * 0x2000) + - ((p.lcdc & 4) ? (p.reg1 * 16 & ~16) | spline : p.reg1 * 16 | (spline & ~16))]; - inc(f3_,p); - } - - static void f3(PPUPriv &p) { - if ((p.winDrawState & WIN_DRAW_START) && handleWinDrawStartReq(p)) - return StartWindowDraw::f0(p); - - inc(f4_,p); - } - - static void f4(PPUPriv &p) { - if ((p.winDrawState & WIN_DRAW_START) && handleWinDrawStartReq(p)) - return StartWindowDraw::f0(p); - - const unsigned spline = ((p.spriteList[p.currentSprite].attrib & 0x40) ? - p.spriteList[p.currentSprite].line ^ 15 : p.spriteList[p.currentSprite].line) * 2; - p.reg1 = p.vram[(p.spriteList[p.currentSprite].attrib << 10 & p.cgb * 0x2000) + - ((p.lcdc & 4) ? (p.reg1 * 16 & ~16) | spline : p.reg1 * 16 | (spline & ~16)) + 1]; - inc(f5_,p); - } - - static void f5(PPUPriv &p) { - if ((p.winDrawState & WIN_DRAW_START) && handleWinDrawStartReq(p)) - return StartWindowDraw::f0(p); - - plotPixelIfNoSprite(p); - - unsigned entry = p.currentSprite; - - if (entry == p.nextSprite) { +static void plotPixelIfNoSprite(PPUPriv &p) { + if (p.spriteList[p.nextSprite].spx == p.xpos) { + if (!(lcdcObjEn(p) | p.cgb)) { + do { ++p.nextSprite; - } else { - entry = p.nextSprite - 1; - p.spriteList[entry] = p.spriteList[p.currentSprite]; - } + } while (p.spriteList[p.nextSprite].spx == p.xpos); - p.spwordList[entry] = expand_lut[p.reg0 + (p.spriteList[entry].attrib << 3 & 0x100)] + - expand_lut[p.reg1 + (p.spriteList[entry].attrib << 3 & 0x100)] * 2; - p.spriteList[entry].spx = p.xpos; + plotPixel(p); + } + } else + plotPixel(p); +} + +static unsigned long nextM2Time(PPUPriv const &p) { + unsigned long nextm2 = p.lyCounter.isDoubleSpeed() + ? p.lyCounter.time() + (weMasterCheckPriorToLyIncLineCycle(true ) + m2_ds_offset) * 2 - 456 * 2 + : p.lyCounter.time() + weMasterCheckPriorToLyIncLineCycle(p.cgb) - 456 ; + if (p.lyCounter.ly() == 143) + nextm2 += (456 * 10 + 456 - weMasterCheckPriorToLyIncLineCycle(p.cgb)) << p.lyCounter.isDoubleSpeed(); + + return nextm2; +} + +static void xpos168(PPUPriv &p) { + p.lastM0Time = p.now - (p.cycles << p.lyCounter.isDoubleSpeed()); + + unsigned long const nextm2 = nextM2Time(p); + + p.cycles = p.now >= nextm2 + ? long((p.now - nextm2) >> p.lyCounter.isDoubleSpeed()) + : -long((nextm2 - p.now) >> p.lyCounter.isDoubleSpeed()); + + nextCall(0, p.lyCounter.ly() == 143 ? M2_Ly0::f0_ : M2_LyNon0::f0_, p); +} + +static bool handleWinDrawStartReq(PPUPriv const &p, int const xpos, unsigned char &winDrawState) { + bool const startWinDraw = (xpos < 167 || p.cgb) + && (winDrawState &= win_draw_started); + if (!lcdcWinEn(p)) + winDrawState &= ~win_draw_started; + + return startWinDraw; +} + +static bool handleWinDrawStartReq(PPUPriv &p) { + return handleWinDrawStartReq(p, p.xpos, p.winDrawState); +} + +namespace StartWindowDraw { + static void inc(PPUState const &nextf, PPUPriv &p) { + if (!lcdcWinEn(p) && p.cgb) { + plotPixelIfNoSprite(p); if (p.xpos == p.endx) { if (p.xpos < 168) { - nextCall(1,Tile::f0_,p); + nextCall(1, Tile::f0_, p); } else xpos168(p); - } else { - p.nextCallPtr = &Tile::f5_; - nextCall(1,Tile::f5_,p); + + return; } } - }; - namespace Tile { - static void inc(const PPUState &nextf, PPUPriv &p) { - plotPixelIfNoSprite(p); - - if (p.xpos == 168) { - xpos168(p); - } else - nextCall(1,nextf,p); - } - - static void f0(PPUPriv &p) { - if ((p.winDrawState & WIN_DRAW_START) && handleWinDrawStartReq(p)) - return StartWindowDraw::f0(p); - - doFullTilesUnrolled(p); - - if (p.xpos == 168) { - ++p.cycles; - return xpos168(p); - } + nextCall(1, nextf, p); + } + static void f0(PPUPriv &p) { + if (p.xpos == p.endx) { p.tileword = p.ntileword; p.attrib = p.nattrib; p.endx = p.xpos < 160 ? p.xpos + 8 : 168; + } - if ((p.winDrawState & WIN_DRAW_STARTED) && (p.layersMask & LAYER_MASK_WINDOW) ) { - p.reg1 = p.vram[(p.lcdc << 4 & 0x400) + (p.winYPos & 0xF8) * 4 - + ((p.xpos + p.wscx) >> 3 & 0x1F) + 0x1800]; - p.nattrib = p.vram[(p.lcdc << 4 & 0x400) + (p.winYPos & 0xF8) * 4 - + ((p.xpos + p.wscx) >> 3 & 0x1F) + 0x3800]; - } else { - p.reg1 = p.vram[((p.lcdc << 7 | (p.scx + p.xpos + 1 - p.cgb) >> 3) & 0x41F) - + ((p.scy + p.lyCounter.ly()) & 0xF8) * 4 + 0x1800]; - p.nattrib = p.vram[((p.lcdc << 7 | (p.scx + p.xpos + 1 - p.cgb) >> 3) & 0x41F) - + ((p.scy + p.lyCounter.ly()) & 0xF8) * 4 + 0x3800]; + p.wscx = 8 - p.xpos; + + if (p.winDrawState & win_draw_started) { + p.reg1 = p.vram[(p.lcdc << 4 & 0x400) + + (p.winYPos & 0xF8) * 4 + 0x1800]; + p.nattrib = p.vram[(p.lcdc << 4 & 0x400) + + (p.winYPos & 0xF8) * 4 + 0x3800]; + } else { + p.reg1 = p.vram[(p.lcdc << 7 & 0x400) + + ((p.scy + p.lyCounter.ly()) & 0xF8) * 4 + 0x1800]; + p.nattrib = p.vram[(p.lcdc << 7 & 0x400) + + ((p.scy + p.lyCounter.ly()) & 0xF8) * 4 + 0x3800]; + } + + inc(f1_, p); + } + + static void f1(PPUPriv &p) { + inc(f2_, p); + } + + static void f2(PPUPriv &p) { + p.reg0 = loadTileDataByte0(p); + inc(f3_, p); + } + + static void f3(PPUPriv &p) { + inc(f4_, p); + } + + static void f4(PPUPriv &p) { + int const r1 = loadTileDataByte1(p); + + p.ntileword = (expand_lut + (p.nattrib << 3 & 0x100))[p.reg0] + + (expand_lut + (p.nattrib << 3 & 0x100))[r1 ] * 2; + + inc(f5_, p); + } + + static void f5(PPUPriv &p) { + inc(Tile::f0_, p); + } +} + +namespace LoadSprites { + static void inc(PPUState const &nextf, PPUPriv &p) { + plotPixelIfNoSprite(p); + + if (p.xpos == p.endx) { + if (p.xpos < 168) { + nextCall(1, Tile::f0_, p); + } else + xpos168(p); + } else + nextCall(1, nextf, p); + } + + static void f0(PPUPriv &p) { + p.reg1 = p.spriteMapper.oamram()[p.spriteList[p.currentSprite].oampos + 2]; + nextCall(1, f1_, p); + } + + static void f1(PPUPriv &p) { + if ((p.winDrawState & win_draw_start) && handleWinDrawStartReq(p)) + return StartWindowDraw::f0(p); + + p.spriteList[p.currentSprite].attrib = + p.spriteMapper.oamram()[p.spriteList[p.currentSprite].oampos + 3]; + inc(f2_, p); + } + + static void f2(PPUPriv &p) { + if ((p.winDrawState & win_draw_start) && handleWinDrawStartReq(p)) + return StartWindowDraw::f0(p); + + unsigned const spline = + ( p.spriteList[p.currentSprite].attrib & attr_yflip + ? p.spriteList[p.currentSprite].line ^ 15 + : p.spriteList[p.currentSprite].line ) * 2; + p.reg0 = p.vram[(p.spriteList[p.currentSprite].attrib << 10 & p.cgb * 0x2000) + + (lcdcObj2x(p) ? (p.reg1 * 16 & ~16) | spline : p.reg1 * 16 | (spline & ~16))]; + inc(f3_, p); + } + + static void f3(PPUPriv &p) { + if ((p.winDrawState & win_draw_start) && handleWinDrawStartReq(p)) + return StartWindowDraw::f0(p); + + inc(f4_, p); + } + + static void f4(PPUPriv &p) { + if ((p.winDrawState & win_draw_start) && handleWinDrawStartReq(p)) + return StartWindowDraw::f0(p); + + unsigned const spline = + ( p.spriteList[p.currentSprite].attrib & attr_yflip + ? p.spriteList[p.currentSprite].line ^ 15 + : p.spriteList[p.currentSprite].line ) * 2; + p.reg1 = p.vram[(p.spriteList[p.currentSprite].attrib << 10 & p.cgb * 0x2000) + + (lcdcObj2x(p) ? (p.reg1 * 16 & ~16) | spline : p.reg1 * 16 | (spline & ~16)) + 1]; + inc(f5_, p); + } + + static void f5(PPUPriv &p) { + if ((p.winDrawState & win_draw_start) && handleWinDrawStartReq(p)) + return StartWindowDraw::f0(p); + + plotPixelIfNoSprite(p); + + unsigned entry = p.currentSprite; + + if (entry == p.nextSprite) { + ++p.nextSprite; + } else { + entry = p.nextSprite - 1; + p.spriteList[entry] = p.spriteList[p.currentSprite]; + } + + p.spwordList[entry] = expand_lut[p.reg0 + (p.spriteList[entry].attrib << 3 & 0x100)] + + expand_lut[p.reg1 + (p.spriteList[entry].attrib << 3 & 0x100)] * 2; + p.spriteList[entry].spx = p.xpos; + + if (p.xpos == p.endx) { + if (p.xpos < 168) { + nextCall(1, Tile::f0_, p); + } else + xpos168(p); + } else { + p.nextCallPtr = &Tile::f5_; + nextCall(1, Tile::f5_, p); + } + } +} + +namespace Tile { + static void inc(PPUState const &nextf, PPUPriv &p) { + plotPixelIfNoSprite(p); + + if (p.xpos == 168) { + xpos168(p); + } else + nextCall(1, nextf, p); + } + + static void f0(PPUPriv &p) { + if ((p.winDrawState & win_draw_start) && handleWinDrawStartReq(p)) + return StartWindowDraw::f0(p); + + doFullTilesUnrolled(p); + + if (p.xpos == 168) { + ++p.cycles; + return xpos168(p); + } + + p.tileword = p.ntileword; + p.attrib = p.nattrib; + p.endx = p.xpos < 160 ? p.xpos + 8 : 168; + + if (p.winDrawState & win_draw_started) { + p.reg1 = p.vram[(p.lcdc << 4 & 0x400) + + (p.winYPos & 0xF8) * 4 + + ((p.xpos + p.wscx) >> 3 & 0x1F) + 0x1800]; + p.nattrib = p.vram[(p.lcdc << 4 & 0x400) + + (p.winYPos & 0xF8) * 4 + + ((p.xpos + p.wscx) >> 3 & 0x1F) + 0x3800]; + } else { + p.reg1 = p.vram[((p.lcdc << 7 | (p.scx + p.xpos + 1 - p.cgb) >> 3) & 0x41F) + + ((p.scy + p.lyCounter.ly()) & 0xF8) * 4 + 0x1800]; + p.nattrib = p.vram[((p.lcdc << 7 | (p.scx + p.xpos + 1 - p.cgb) >> 3) & 0x41F) + + ((p.scy + p.lyCounter.ly()) & 0xF8) * 4 + 0x3800]; + } + + inc(f1_, p); + } + + static void f1(PPUPriv &p) { + if ((p.winDrawState & win_draw_start) && handleWinDrawStartReq(p)) + return StartWindowDraw::f0(p); + + inc(f2_, p); + } + + static void f2(PPUPriv &p) { + if ((p.winDrawState & win_draw_start) && handleWinDrawStartReq(p)) + return StartWindowDraw::f0(p); + + p.reg0 = loadTileDataByte0(p); + inc(f3_, p); + } + + static void f3(PPUPriv &p) { + if ((p.winDrawState & win_draw_start) && handleWinDrawStartReq(p)) + return StartWindowDraw::f0(p); + + inc(f4_, p); + } + + static void f4(PPUPriv &p) { + if ((p.winDrawState & win_draw_start) && handleWinDrawStartReq(p)) + return StartWindowDraw::f0(p); + + int const r1 = loadTileDataByte1(p); + + p.ntileword = (expand_lut + (p.nattrib << 3 & 0x100))[p.reg0] + + (expand_lut + (p.nattrib << 3 & 0x100))[r1 ] * 2; + + plotPixelIfNoSprite(p); + + if (p.xpos == 168) { + xpos168(p); + } else + nextCall(1, f5_, p); + } + + static void f5(PPUPriv &p) { + int endx = p.endx; + p.nextCallPtr = &f5_; + + do { + if ((p.winDrawState & win_draw_start) && handleWinDrawStartReq(p)) + return StartWindowDraw::f0(p); + + if (p.spriteList[p.nextSprite].spx == p.xpos) { + if (lcdcObjEn(p) | p.cgb) { + p.currentSprite = p.nextSprite; + return LoadSprites::f0(p); + } + + do { + ++p.nextSprite; + } while (p.spriteList[p.nextSprite].spx == p.xpos); } - inc(f1_,p); - } + plotPixel(p); - static void f1(PPUPriv &p) { - if ((p.winDrawState & WIN_DRAW_START) && handleWinDrawStartReq(p)) - return StartWindowDraw::f0(p); + if (p.xpos == endx) { + if (endx < 168) { + nextCall(1, f0_, p); + } else + xpos168(p); - inc(f2_,p); - } - - static void f2(PPUPriv &p) { - if ((p.winDrawState & WIN_DRAW_START) && handleWinDrawStartReq(p)) - return StartWindowDraw::f0(p); - - const unsigned yoffset = ((p.winDrawState & WIN_DRAW_STARTED) && (p.layersMask & LAYER_MASK_WINDOW)) ? p.winYPos : p.scy + p.lyCounter.ly(); - - p.reg0 = p.vram[0x1000 + (p.nattrib << 10 & 0x2000) - - ((p.reg1 * 32 | p.lcdc << 8) & 0x1000) - + p.reg1 * 16 + ((-(p.nattrib >> 6 & 1) ^ yoffset) & 7) * 2]; - - inc(f3_,p); - } - - static void f3(PPUPriv &p) { - if ((p.winDrawState & WIN_DRAW_START) && handleWinDrawStartReq(p)) - return StartWindowDraw::f0(p); - - inc(f4_,p); - } - - static void f4(PPUPriv &p) { - if ((p.winDrawState & WIN_DRAW_START) && handleWinDrawStartReq(p)) - return StartWindowDraw::f0(p); - - const unsigned yoffset = ((p.winDrawState & WIN_DRAW_STARTED) && (p.layersMask & LAYER_MASK_WINDOW)) ? p.winYPos : p.scy + p.lyCounter.ly(); - const unsigned r1 = p.vram[0x1000 + (p.nattrib << 10 & 0x2000) - - ((p.reg1 * 32 | p.lcdc << 8) & 0x1000) - + p.reg1 * 16 + ((-(p.nattrib >> 6 & 1) ^ yoffset) & 7) * 2 + 1]; - - p.ntileword = (expand_lut + (p.nattrib << 3 & 0x100))[p.reg0] + - (expand_lut + (p.nattrib << 3 & 0x100))[r1 ] * 2; - - plotPixelIfNoSprite(p); - - if (p.xpos == 168) { - xpos168(p); - } else - nextCall(1,f5_,p); - } - - static void f5(PPUPriv &p) { - int endx = p.endx; - p.nextCallPtr = &f5_; - - do { - if ((p.winDrawState & WIN_DRAW_START) && handleWinDrawStartReq(p)) - return StartWindowDraw::f0(p); - - if (p.spriteList[p.nextSprite].spx == p.xpos) { - if ((p.lcdc & 2) | p.cgb) { - p.currentSprite = p.nextSprite; - return LoadSprites::f0(p); - } - - do { - ++p.nextSprite; - } while (p.spriteList[p.nextSprite].spx == p.xpos); - } - - plotPixel(p); - - if (p.xpos == endx) { - if (endx < 168) { - nextCall(1,f0_,p); - } else - xpos168(p); - - return; - } - } while (--p.cycles >= 0); - } - }; -}; - - -namespace M2 { - namespace Ly0 { - static unsigned predictCyclesUntilXpos_f0(const PPUPriv &p, unsigned winDrawState, int targetxpos, unsigned cycles); + return; + } + } while (--p.cycles >= 0); } +} - namespace LyNon0 { - static unsigned predictCyclesUntilXpos_f0(const PPUPriv &p, unsigned winDrawState, int targetxpos, unsigned cycles); - } +} // namespace M3Loop + +namespace M2_Ly0 { + static unsigned predictCyclesUntilXpos_f0(PPUPriv const &p, unsigned winDrawState, + int targetxpos, unsigned cycles); +} + +namespace M2_LyNon0 { + static unsigned predictCyclesUntilXpos_f0(PPUPriv const &p, unsigned winDrawState, + int targetxpos, unsigned cycles); } namespace M3Loop { - static unsigned predictCyclesUntilXposNextLine(const PPUPriv &p, unsigned winDrawState, const int targetx) { - if (p.wx == 166 && !p.cgb && p.xpos < 167 && (p.weMaster || (p.wy2 == p.lyCounter.ly() && (p.lcdc & 0x20)))) - winDrawState = WIN_DRAW_START | (WIN_DRAW_STARTED & p.lcdc >> 4); - const unsigned cycles = (nextM2Time(p) - p.now) >> p.lyCounter.isDoubleSpeed(); - - return p.lyCounter.ly() == 143 - ? M2::Ly0::predictCyclesUntilXpos_f0(p, winDrawState, targetx, cycles) - : M2::LyNon0::predictCyclesUntilXpos_f0(p, winDrawState, targetx, cycles); +static unsigned predictCyclesUntilXposNextLine( + PPUPriv const &p, unsigned winDrawState, int const targetx) { + if (p.wx == 166 && !p.cgb && p.xpos < 167 + && (p.weMaster || (p.wy2 == p.lyCounter.ly() && lcdcWinEn(p)))) { + winDrawState = win_draw_start | (lcdcWinEn(p) ? win_draw_started : 0); } - namespace StartWindowDraw { - static unsigned predictCyclesUntilXpos_fn(const PPUPriv &p, int xpos, int endx, unsigned ly, - unsigned nextSprite, bool weMaster, unsigned winDrawState, int fno, int targetx, unsigned cycles); - } + unsigned const cycles = (nextM2Time(p) - p.now) >> p.lyCounter.isDoubleSpeed(); - namespace Tile { - static const unsigned char* addSpriteCycles(const unsigned char *nextSprite, - const unsigned char *spriteEnd, const unsigned char *const spxOf, const unsigned maxSpx, - const unsigned firstTileXpos, unsigned prevSpriteTileNo, unsigned *const cyclesAccumulator) { - unsigned sum = 0; + return p.lyCounter.ly() == 143 + ? M2_Ly0::predictCyclesUntilXpos_f0(p, winDrawState, targetx, cycles) + : M2_LyNon0::predictCyclesUntilXpos_f0(p, winDrawState, targetx, cycles); +} - while (nextSprite < spriteEnd && spxOf[*nextSprite] <= maxSpx) { - unsigned cycles = 6; - const unsigned distanceFromTileStart = (spxOf[*nextSprite] - firstTileXpos) & 7; - const unsigned tileNo = (spxOf[*nextSprite] - firstTileXpos) & ~7; +namespace StartWindowDraw { + static unsigned predictCyclesUntilXpos_fn(PPUPriv const &p, int xpos, + int endx, unsigned ly, unsigned nextSprite, bool weMaster, + unsigned winDrawState, int fno, int targetx, unsigned cycles); +} - if (distanceFromTileStart < 5 && tileNo != prevSpriteTileNo) - cycles = 11 - distanceFromTileStart; +namespace Tile { + static unsigned char const * addSpriteCycles(unsigned char const *nextSprite, + unsigned char const *spriteEnd, unsigned char const *const spxOf, + unsigned const maxSpx, unsigned const firstTileXpos, + unsigned prevSpriteTileNo, unsigned *const cyclesAccumulator) { + unsigned sum = 0; - prevSpriteTileNo = tileNo; - sum += cycles; - ++nextSprite; - } + while (nextSprite < spriteEnd && spxOf[*nextSprite] <= maxSpx) { + unsigned cycles = 6; + unsigned const distanceFromTileStart = (spxOf[*nextSprite] - firstTileXpos) & 7; + unsigned const tileNo = (spxOf[*nextSprite] - firstTileXpos) & ~7; - *cyclesAccumulator += sum; + if (distanceFromTileStart < 5 && tileNo != prevSpriteTileNo) + cycles = 11 - distanceFromTileStart; - return nextSprite; + prevSpriteTileNo = tileNo; + sum += cycles; + ++nextSprite; } - static unsigned predictCyclesUntilXpos_fn(const PPUPriv &p, const int xpos, - const int endx, const unsigned ly, const unsigned nextSprite, - const bool weMaster, unsigned char winDrawState, const int fno, const int targetx, unsigned cycles) { - if ((winDrawState & WIN_DRAW_START) && handleWinDrawStartReq(p, xpos, winDrawState)) { - return StartWindowDraw::predictCyclesUntilXpos_fn(p, xpos, endx, - ly, nextSprite, weMaster, WIN_DRAW_STARTED & p.lcdc >> 4, 0, targetx, cycles); - } + *cyclesAccumulator += sum; - if (xpos > targetx) - return predictCyclesUntilXposNextLine(p, winDrawState, targetx); + return nextSprite; + } - enum { NO_TILE_NUMBER = 1 }; // low bit set, so it will never be equal to an actual tile number. + static unsigned predictCyclesUntilXpos_fn(PPUPriv const &p, int const xpos, + int const endx, unsigned const ly, unsigned const nextSprite, + bool const weMaster, unsigned char winDrawState, int const fno, + int const targetx, unsigned cycles) { + if ((winDrawState & win_draw_start) + && handleWinDrawStartReq(p, xpos, winDrawState)) { + return StartWindowDraw::predictCyclesUntilXpos_fn(p, xpos, endx, + ly, nextSprite, weMaster, winDrawState, 0, targetx, cycles); + } - int nwx = 0xFF; - cycles += targetx - xpos; + if (xpos > targetx) + return predictCyclesUntilXposNextLine(p, winDrawState, targetx); - if (p.wx - unsigned(xpos) < targetx - unsigned(xpos) && (p.lcdc & 0x20) - && (weMaster || p.wy2 == ly) && !(winDrawState & WIN_DRAW_STARTED) && (p.cgb || p.wx != 166)) { - nwx = p.wx; - cycles += 6; - } + enum { NO_TILE_NUMBER = 1 }; // low bit set, so it will never be equal to an actual tile number. - if ((p.lcdc & 2) | p.cgb) { - const unsigned char *sprite = p.spriteMapper.sprites(ly); - const unsigned char *const spriteEnd = sprite + p.spriteMapper.numSprites(ly); - sprite += nextSprite; + int nwx = 0xFF; + cycles += targetx - xpos; - if (sprite < spriteEnd) { - const int spx = p.spriteMapper.posbuf()[*sprite + 1]; - unsigned firstTileXpos = unsigned(endx) & 7; // ok even if endx is capped at 168, because fno will be used. - unsigned prevSpriteTileNo = (xpos - firstTileXpos) & ~7; // this tile. all sprites on this tile will now add 6 cycles. + if (p.wx - unsigned(xpos) < targetx - unsigned(xpos) + && lcdcWinEn(p) && (weMaster || p.wy2 == ly) + && !(winDrawState & win_draw_started) + && (p.cgb || p.wx != 166)) { + nwx = p.wx; + cycles += 6; + } - // except this one - if (fno + spx - xpos < 5 && spx <= nwx) { - cycles += 11 - (fno + spx - xpos); - sprite += 1; - } + if (lcdcObjEn(p) | p.cgb) { + unsigned char const *sprite = p.spriteMapper.sprites(ly); + unsigned char const *const spriteEnd = sprite + p.spriteMapper.numSprites(ly); + sprite += nextSprite; - if (nwx < targetx) { - sprite = addSpriteCycles(sprite, spriteEnd, p.spriteMapper.posbuf() + 1, - nwx, firstTileXpos, prevSpriteTileNo, &cycles); - firstTileXpos = nwx + 1; - prevSpriteTileNo = NO_TILE_NUMBER; - } - - addSpriteCycles(sprite, spriteEnd, p.spriteMapper.posbuf() + 1, - targetx, firstTileXpos, prevSpriteTileNo, &cycles); + if (sprite < spriteEnd) { + int const spx = p.spriteMapper.posbuf()[*sprite + 1]; + unsigned firstTileXpos = endx & 7u; // ok even if endx is capped at 168, + // because fno will be used. + unsigned prevSpriteTileNo = (xpos - firstTileXpos) & ~7; // this tile. all sprites on this + // tile will now add 6 cycles. + // except this one + if (fno + spx - xpos < 5 && spx <= nwx) { + cycles += 11 - (fno + spx - xpos); + sprite += 1; } - } - return cycles; - } - - static unsigned predictCyclesUntilXpos_fn(const PPUPriv &p, - int endx, int fno, int targetx, unsigned cycles) { - return predictCyclesUntilXpos_fn(p, p.xpos, endx, p.lyCounter.ly(), - p.nextSprite, p.weMaster, p.winDrawState, fno, targetx, cycles); - } - - static unsigned predictCyclesUntilXpos_f0(const PPUPriv &p, int targetx, unsigned cycles) { - return predictCyclesUntilXpos_fn(p, p.xpos < 160 ? p.xpos + 8 : 168, 0, targetx, cycles); - } - static unsigned predictCyclesUntilXpos_f1(const PPUPriv &p, int targetx, unsigned cycles) { - return predictCyclesUntilXpos_fn(p, p.endx, 1, targetx, cycles); - } - static unsigned predictCyclesUntilXpos_f2(const PPUPriv &p, int targetx, unsigned cycles) { - return predictCyclesUntilXpos_fn(p, p.endx, 2, targetx, cycles); - } - static unsigned predictCyclesUntilXpos_f3(const PPUPriv &p, int targetx, unsigned cycles) { - return predictCyclesUntilXpos_fn(p, p.endx, 3, targetx, cycles); - } - static unsigned predictCyclesUntilXpos_f4(const PPUPriv &p, int targetx, unsigned cycles) { - return predictCyclesUntilXpos_fn(p, p.endx, 4, targetx, cycles); - } - static unsigned predictCyclesUntilXpos_f5(const PPUPriv &p, int targetx, unsigned cycles) { - return predictCyclesUntilXpos_fn(p, p.endx, 5, targetx, cycles); - } - } - - namespace StartWindowDraw { - static unsigned predictCyclesUntilXpos_fn(const PPUPriv &p, int xpos, - const int endx, const unsigned ly, const unsigned nextSprite, const bool weMaster, - const unsigned winDrawState, const int fno, const int targetx, unsigned cycles) { - if (xpos > targetx) - return predictCyclesUntilXposNextLine(p, winDrawState, targetx); - - unsigned cinc = 6 - fno; - - if (!(p.lcdc & 0x20) && p.cgb) { - unsigned xinc = std::min(cinc, std::min(endx, targetx + 1) - xpos); - - if (((p.lcdc & 2) | p.cgb) && p.spriteList[nextSprite].spx < xpos + xinc) { - xpos = p.spriteList[nextSprite].spx; - } else { - cinc = xinc; - xpos += xinc; + if (nwx < targetx) { + sprite = addSpriteCycles(sprite, spriteEnd, p.spriteMapper.posbuf() + 1, + nwx, firstTileXpos, prevSpriteTileNo, &cycles); + firstTileXpos = nwx + 1; + prevSpriteTileNo = NO_TILE_NUMBER; } + + addSpriteCycles(sprite, spriteEnd, p.spriteMapper.posbuf() + 1, + targetx, firstTileXpos, prevSpriteTileNo, &cycles); } - - cycles += cinc; - - if (xpos <= targetx) { - return Tile::predictCyclesUntilXpos_fn(p, xpos, xpos < 160 ? xpos + 8 : 168, - ly, nextSprite, weMaster, winDrawState, 0, targetx, cycles); - } - - return cycles - 1; } - static unsigned predictCyclesUntilXpos_fn(const PPUPriv &p, int endx, - int fno, int targetx, unsigned cycles) { - return predictCyclesUntilXpos_fn(p, p.xpos, endx, - p.lyCounter.ly(), p.nextSprite, p.weMaster, p.winDrawState, fno, targetx, cycles); - } - - static unsigned predictCyclesUntilXpos_f0(const PPUPriv &p, const int targetx, const unsigned cycles) { - return predictCyclesUntilXpos_fn(p, p.xpos == p.endx ? (p.xpos < 160 ? p.xpos + 8 : 168) : p.endx, 0, targetx, cycles); - } - static unsigned predictCyclesUntilXpos_f1(const PPUPriv &p, int targetx, unsigned cycles) { - return predictCyclesUntilXpos_fn(p, p.endx, 1, targetx, cycles); - } - static unsigned predictCyclesUntilXpos_f2(const PPUPriv &p, int targetx, unsigned cycles) { - return predictCyclesUntilXpos_fn(p, p.endx, 2, targetx, cycles); - } - static unsigned predictCyclesUntilXpos_f3(const PPUPriv &p, int targetx, unsigned cycles) { - return predictCyclesUntilXpos_fn(p, p.endx, 3, targetx, cycles); - } - static unsigned predictCyclesUntilXpos_f4(const PPUPriv &p, int targetx, unsigned cycles) { - return predictCyclesUntilXpos_fn(p, p.endx, 4, targetx, cycles); - } - static unsigned predictCyclesUntilXpos_f5(const PPUPriv &p, int targetx, unsigned cycles) { - return predictCyclesUntilXpos_fn(p, p.endx, 5, targetx, cycles); - } + return cycles; } - namespace LoadSprites { - static unsigned predictCyclesUntilXpos_fn(const PPUPriv &p, const int fno, const int targetx, unsigned cycles) { - unsigned nextSprite = p.nextSprite; + static unsigned predictCyclesUntilXpos_fn(PPUPriv const &p, + int endx, int fno, int targetx, unsigned cycles) { + return predictCyclesUntilXpos_fn(p, p.xpos, endx, p.lyCounter.ly(), + p.nextSprite, p.weMaster, p.winDrawState, fno, targetx, cycles); + } - if ((p.lcdc & 2) | p.cgb) { - cycles += 6 - fno; - nextSprite += 1; - } - - return Tile::predictCyclesUntilXpos_fn(p, p.xpos, p.endx, p.lyCounter.ly(), - nextSprite, p.weMaster, p.winDrawState, 5, targetx, cycles); - } - - static unsigned predictCyclesUntilXpos_f0(const PPUPriv &p, int targetx, unsigned cycles) { - return predictCyclesUntilXpos_fn(p, 0, targetx, cycles); - } - static unsigned predictCyclesUntilXpos_f1(const PPUPriv &p, int targetx, unsigned cycles) { - return predictCyclesUntilXpos_fn(p, 1, targetx, cycles); - } - static unsigned predictCyclesUntilXpos_f2(const PPUPriv &p, int targetx, unsigned cycles) { - return predictCyclesUntilXpos_fn(p, 2, targetx, cycles); - } - static unsigned predictCyclesUntilXpos_f3(const PPUPriv &p, int targetx, unsigned cycles) { - return predictCyclesUntilXpos_fn(p, 3, targetx, cycles); - } - static unsigned predictCyclesUntilXpos_f4(const PPUPriv &p, int targetx, unsigned cycles) { - return predictCyclesUntilXpos_fn(p, 4, targetx, cycles); - } - static unsigned predictCyclesUntilXpos_f5(const PPUPriv &p, int targetx, unsigned cycles) { - return predictCyclesUntilXpos_fn(p, 5, targetx, cycles); - } + static unsigned predictCyclesUntilXpos_f0(PPUPriv const &p, int targetx, unsigned cycles) { + return predictCyclesUntilXpos_fn(p, p.xpos < 160 ? p.xpos + 8 : 168, 0, targetx, cycles); + } + static unsigned predictCyclesUntilXpos_f1(PPUPriv const &p, int targetx, unsigned cycles) { + return predictCyclesUntilXpos_fn(p, p.endx, 1, targetx, cycles); + } + static unsigned predictCyclesUntilXpos_f2(PPUPriv const &p, int targetx, unsigned cycles) { + return predictCyclesUntilXpos_fn(p, p.endx, 2, targetx, cycles); + } + static unsigned predictCyclesUntilXpos_f3(PPUPriv const &p, int targetx, unsigned cycles) { + return predictCyclesUntilXpos_fn(p, p.endx, 3, targetx, cycles); + } + static unsigned predictCyclesUntilXpos_f4(PPUPriv const &p, int targetx, unsigned cycles) { + return predictCyclesUntilXpos_fn(p, p.endx, 4, targetx, cycles); + } + static unsigned predictCyclesUntilXpos_f5(PPUPriv const &p, int targetx, unsigned cycles) { + return predictCyclesUntilXpos_fn(p, p.endx, 5, targetx, cycles); } } -namespace M3Start { - static unsigned predictCyclesUntilXpos_f1(const PPUPriv &p, unsigned xpos, unsigned ly, - bool weMaster, unsigned winDrawState, int targetx, unsigned cycles) { - cycles += std::min((unsigned(p.scx) - unsigned(xpos)) & 7, MAX_M3START_CYCLES - xpos) + 1 - p.cgb; - return M3Loop::Tile::predictCyclesUntilXpos_fn(p, 0, 8 - (p.scx & 7), ly, 0, - weMaster, winDrawState, std::min(p.scx & 7, 5), targetx, cycles); +namespace StartWindowDraw { + static unsigned predictCyclesUntilXpos_fn(PPUPriv const &p, int xpos, + int const endx, unsigned const ly, unsigned const nextSprite, bool const weMaster, + unsigned const winDrawState, int const fno, int const targetx, unsigned cycles) { + if (xpos > targetx) + return predictCyclesUntilXposNextLine(p, winDrawState, targetx); + + unsigned cinc = 6 - fno; + + if (!lcdcWinEn(p) && p.cgb) { + unsigned xinc = std::min(cinc, std::min(endx, targetx + 1) - xpos); + + if ((lcdcObjEn(p) | p.cgb) && p.spriteList[nextSprite].spx < xpos + xinc) { + xpos = p.spriteList[nextSprite].spx; + } else { + cinc = xinc; + xpos += xinc; + } + } + + cycles += cinc; + + if (xpos <= targetx) { + return Tile::predictCyclesUntilXpos_fn(p, xpos, xpos < 160 ? xpos + 8 : 168, + ly, nextSprite, weMaster, winDrawState, 0, targetx, cycles); + } + + return cycles - 1; } - static unsigned predictCyclesUntilXpos_f0(const PPUPriv &p, unsigned ly, + static unsigned predictCyclesUntilXpos_fn(PPUPriv const &p, + int endx, int fno, int targetx, unsigned cycles) { + return predictCyclesUntilXpos_fn(p, p.xpos, endx, p.lyCounter.ly(), + p.nextSprite, p.weMaster, p.winDrawState, fno, targetx, cycles); + } + + static unsigned predictCyclesUntilXpos_f0(PPUPriv const &p, int targetx, unsigned cycles) { + int endx = p.xpos == p.endx + ? (p.xpos < 160 ? p.xpos + 8 : 168) + : p.endx; + return predictCyclesUntilXpos_fn(p, endx, 0, targetx, cycles); + } + static unsigned predictCyclesUntilXpos_f1(PPUPriv const &p, int targetx, unsigned cycles) { + return predictCyclesUntilXpos_fn(p, p.endx, 1, targetx, cycles); + } + static unsigned predictCyclesUntilXpos_f2(PPUPriv const &p, int targetx, unsigned cycles) { + return predictCyclesUntilXpos_fn(p, p.endx, 2, targetx, cycles); + } + static unsigned predictCyclesUntilXpos_f3(PPUPriv const &p, int targetx, unsigned cycles) { + return predictCyclesUntilXpos_fn(p, p.endx, 3, targetx, cycles); + } + static unsigned predictCyclesUntilXpos_f4(PPUPriv const &p, int targetx, unsigned cycles) { + return predictCyclesUntilXpos_fn(p, p.endx, 4, targetx, cycles); + } + static unsigned predictCyclesUntilXpos_f5(PPUPriv const &p, int targetx, unsigned cycles) { + return predictCyclesUntilXpos_fn(p, p.endx, 5, targetx, cycles); + } +} + +namespace LoadSprites { + static unsigned predictCyclesUntilXpos_fn(PPUPriv const &p, + int const fno, int const targetx, unsigned cycles) { + unsigned nextSprite = p.nextSprite; + if (lcdcObjEn(p) | p.cgb) { + cycles += 6 - fno; + nextSprite += 1; + } + + return Tile::predictCyclesUntilXpos_fn(p, p.xpos, p.endx, p.lyCounter.ly(), + nextSprite, p.weMaster, p.winDrawState, 5, targetx, cycles); + } + + static unsigned predictCyclesUntilXpos_f0(PPUPriv const &p, int targetx, unsigned cycles) { + return predictCyclesUntilXpos_fn(p, 0, targetx, cycles); + } + static unsigned predictCyclesUntilXpos_f1(PPUPriv const &p, int targetx, unsigned cycles) { + return predictCyclesUntilXpos_fn(p, 1, targetx, cycles); + } + static unsigned predictCyclesUntilXpos_f2(PPUPriv const &p, int targetx, unsigned cycles) { + return predictCyclesUntilXpos_fn(p, 2, targetx, cycles); + } + static unsigned predictCyclesUntilXpos_f3(PPUPriv const &p, int targetx, unsigned cycles) { + return predictCyclesUntilXpos_fn(p, 3, targetx, cycles); + } + static unsigned predictCyclesUntilXpos_f4(PPUPriv const &p, int targetx, unsigned cycles) { + return predictCyclesUntilXpos_fn(p, 4, targetx, cycles); + } + static unsigned predictCyclesUntilXpos_f5(PPUPriv const &p, int targetx, unsigned cycles) { + return predictCyclesUntilXpos_fn(p, 5, targetx, cycles); + } +} + +} // namespace M3Loop + +namespace M3Start { + static unsigned predictCyclesUntilXpos_f1(PPUPriv const &p, unsigned xpos, unsigned ly, bool weMaster, unsigned winDrawState, int targetx, unsigned cycles) { - winDrawState = (winDrawState & p.lcdc >> 5 & WIN_DRAW_START) ? WIN_DRAW_STARTED : 0; + cycles += std::min(unsigned(p.scx - xpos) & 7, max_m3start_cycles - xpos) + 1 - p.cgb; + return M3Loop::Tile::predictCyclesUntilXpos_fn(p, 0, 8 - (p.scx & 7), ly, 0, + weMaster, winDrawState, std::min(p.scx & 7, 5), targetx, cycles); + } + + static unsigned predictCyclesUntilXpos_f0(PPUPriv const &p, unsigned ly, + bool weMaster, unsigned winDrawState, int targetx, unsigned cycles) { + winDrawState = (winDrawState & win_draw_start) && lcdcWinEn(p) ? win_draw_started : 0; return predictCyclesUntilXpos_f1(p, 0, ly, weMaster, winDrawState, targetx, cycles); } - static unsigned predictCyclesUntilXpos_f0(const PPUPriv &p, int targetx, unsigned cycles) { - const unsigned ly = p.lyCounter.ly() + (p.lyCounter.time() - p.now < 16); + static unsigned predictCyclesUntilXpos_f0(PPUPriv const &p, int targetx, unsigned cycles) { + unsigned ly = p.lyCounter.ly() + (p.lyCounter.time() - p.now < 16); return predictCyclesUntilXpos_f0(p, ly, p.weMaster, p.winDrawState, targetx, cycles); } - static unsigned predictCyclesUntilXpos_f1(const PPUPriv &p, const int targetx, const unsigned cycles) { - return predictCyclesUntilXpos_f1(p, p.xpos, p.lyCounter.ly(), p.weMaster, p.winDrawState, targetx, cycles); + static unsigned predictCyclesUntilXpos_f1(PPUPriv const &p, int targetx, unsigned cycles) { + return predictCyclesUntilXpos_f1(p, p.xpos, p.lyCounter.ly(), p.weMaster, + p.winDrawState, targetx, cycles); } } -namespace M2 { - namespace Ly0 { - static unsigned predictCyclesUntilXpos_f0(const PPUPriv &p, - unsigned winDrawState, int targetx, unsigned cycles) { - bool weMaster = (p.lcdc & 0x20) && 0 == p.wy; - unsigned ly = 0; +namespace M2_Ly0 { + static unsigned predictCyclesUntilXpos_f0(PPUPriv const &p, + unsigned winDrawState, int targetx, unsigned cycles) { + bool weMaster = lcdcWinEn(p) && 0 == p.wy; + unsigned ly = 0; - return M3Start::predictCyclesUntilXpos_f0(p, ly, weMaster, - winDrawState, targetx, cycles + m3StartLineCycle(p.cgb)); + return M3Start::predictCyclesUntilXpos_f0(p, ly, weMaster, + winDrawState, targetx, cycles + m3StartLineCycle(p.cgb)); - } - - static unsigned predictCyclesUntilXpos_f0(const PPUPriv &p, int targetx, unsigned cycles) { - return predictCyclesUntilXpos_f0(p, p.winDrawState, targetx, cycles); - } } - namespace LyNon0 { - static unsigned predictCyclesUntilXpos_f1(const PPUPriv &p, bool weMaster, - unsigned winDrawState, int targetx, unsigned cycles) { - unsigned ly = p.lyCounter.ly() + 1; + static unsigned predictCyclesUntilXpos_f0(PPUPriv const &p, int targetx, unsigned cycles) { + return predictCyclesUntilXpos_f0(p, p.winDrawState, targetx, cycles); + } +} - weMaster |= (p.lcdc & 0x20) && ly == p.wy; +namespace M2_LyNon0 { + static unsigned predictCyclesUntilXpos_f1(PPUPriv const &p, bool weMaster, + unsigned winDrawState, int targetx, unsigned cycles) { + unsigned ly = p.lyCounter.ly() + 1; + weMaster |= lcdcWinEn(p) && ly == p.wy; - return M3Start::predictCyclesUntilXpos_f0(p, ly, weMaster, winDrawState, targetx, - cycles + 456 - weMasterCheckAfterLyIncLineCycle(p.cgb) + m3StartLineCycle(p.cgb)); - } + return M3Start::predictCyclesUntilXpos_f0(p, ly, weMaster, winDrawState, targetx, + cycles + 456 - weMasterCheckAfterLyIncLineCycle(p.cgb) + m3StartLineCycle(p.cgb)); + } - static unsigned predictCyclesUntilXpos_f1(const PPUPriv &p, int targetx, unsigned cycles) { - return predictCyclesUntilXpos_f1(p, p.weMaster, p.winDrawState, targetx, cycles); - } + static unsigned predictCyclesUntilXpos_f1(PPUPriv const &p, int targetx, unsigned cycles) { + return predictCyclesUntilXpos_f1(p, p.weMaster, p.winDrawState, targetx, cycles); + } - static unsigned predictCyclesUntilXpos_f0(const PPUPriv &p, - unsigned winDrawState, int targetx, unsigned cycles) { - bool weMaster = p.weMaster || ((p.lcdc & 0x20) && p.lyCounter.ly() == p.wy); + static unsigned predictCyclesUntilXpos_f0(PPUPriv const &p, + unsigned winDrawState, int targetx, unsigned cycles) { + bool weMaster = p.weMaster || (lcdcWinEn(p) && p.lyCounter.ly() == p.wy); - return predictCyclesUntilXpos_f1(p, weMaster, winDrawState, targetx, - cycles + weMasterCheckAfterLyIncLineCycle(p.cgb) - weMasterCheckPriorToLyIncLineCycle(p.cgb)); - } + return predictCyclesUntilXpos_f1(p, weMaster, winDrawState, targetx, + cycles + weMasterCheckAfterLyIncLineCycle(p.cgb) + - weMasterCheckPriorToLyIncLineCycle(p.cgb)); + } - static unsigned predictCyclesUntilXpos_f0(const PPUPriv &p, int targetx, unsigned cycles) { - return predictCyclesUntilXpos_f0(p, p.winDrawState, targetx, cycles); - } + static unsigned predictCyclesUntilXpos_f0(PPUPriv const &p, int targetx, unsigned cycles) { + return predictCyclesUntilXpos_f0(p, p.winDrawState, targetx, cycles); } } @@ -1467,45 +1485,55 @@ namespace M2 { namespace gambatte { -PPUPriv::PPUPriv(NextM0Time &nextM0Time, const unsigned char *const oamram, const unsigned char *const vram) : - nextSprite(0), - currentSprite(0xFF), - layersMask(LAYER_MASK_BG|LAYER_MASK_OBJ), - vram(vram), - nextCallPtr(&M2::Ly0::f0_), - now(0), - lastM0Time(0), - cycles(-4396), - tileword(0), - ntileword(0), - spriteMapper(nextM0Time, lyCounter, oamram), - lcdc(0), - scy(0), - scx(0), - wy(0), - wy2(0), - wx(0), - winDrawState(0), - wscx(0), - winYPos(0), - reg0(0), - reg1(0), - attrib(0), - nattrib(0), - xpos(0), - endx(0), - cgb(false), - weMaster(false) +PPUPriv::PPUPriv(NextM0Time &nextM0Time, unsigned char const *const oamram, unsigned char const *const vram) +: nextSprite(0) +, currentSprite(0xFF) +, vram(vram) +, nextCallPtr(&M2_Ly0::f0_) +, now(0) +, lastM0Time(0) +, cycles(-4396) +, tileword(0) +, ntileword(0) +, spriteMapper(nextM0Time, lyCounter, oamram) +, lcdc(0) +, scy(0) +, scx(0) +, wy(0) +, wy2(0) +, wx(0) +, winDrawState(0) +, wscx(0) +, winYPos(0) +, reg0(0) +, reg1(0) +, attrib(0) +, nattrib(0) +, xpos(0) +, endx(0) +, cgb(false) +, weMaster(false) { std::memset(spriteList, 0, sizeof spriteList); std::memset(spwordList, 0, sizeof spwordList); } +static void saveSpriteList(PPUPriv const &p, SaveState &ss) { + for (unsigned i = 0; i < 10; ++i) { + ss.ppu.spAttribList[i] = p.spriteList[i].attrib; + ss.ppu.spByte0List[i] = p.spwordList[i] & 0xFF; + ss.ppu.spByte1List[i] = p.spwordList[i] >> 8; + } + + ss.ppu.nextSprite = p.nextSprite; + ss.ppu.currentSprite = p.currentSprite; +} + namespace { template struct BSearch { - static std::size_t upperBound(const T a[], const K e) { + static std::size_t upperBound(T const a[], K e) { if (e < a[start + len / 2]) return BSearch::upperBound(a, e); @@ -1515,23 +1543,23 @@ struct BSearch { template struct BSearch { - static std::size_t upperBound(const T[], const K) { + static std::size_t upperBound(T const [], K ) { return start; } }; template -std::size_t upperBound(const T a[], const K e) { +std::size_t upperBound(T const a[], K e) { return BSearch::upperBound(a, e); } struct CycleState { - const PPUState *state; + PPUState const *state; long cycle; operator long() const { return cycle; } }; -static const PPUState * decodeM3LoopState(const unsigned state) { +static PPUState const * decodeM3LoopState(unsigned state) { switch (state) { case M3Loop::Tile::ID0: return &M3Loop::Tile::f0_; case M3Loop::Tile::ID1: return &M3Loop::Tile::f1_; @@ -1558,25 +1586,24 @@ static const PPUState * decodeM3LoopState(const unsigned state) { return 0; } -static long cyclesUntilM0Upperbound(const PPUPriv &p) { +static long cyclesUntilM0Upperbound(PPUPriv const &p) { long cycles = 168 - p.xpos + 6; - for (unsigned i = p.nextSprite; i < 10 && p.spriteList[i].spx < 168; ++i) cycles += 11; return cycles; } -static void loadSpriteList(PPUPriv &p, const SaveState &ss) { +static void loadSpriteList(PPUPriv &p, SaveState const &ss) { if (ss.ppu.videoCycles < 144 * 456UL && ss.ppu.xpos < 168) { - const unsigned ly = ss.ppu.videoCycles / 456; - const unsigned numSprites = p.spriteMapper.numSprites(ly); - const unsigned char *const sprites = p.spriteMapper.sprites(ly); + unsigned const ly = ss.ppu.videoCycles / 456; + unsigned const numSprites = p.spriteMapper.numSprites(ly); + unsigned char const *const sprites = p.spriteMapper.sprites(ly); for (unsigned i = 0; i < numSprites; ++i) { - const unsigned pos = sprites[i]; - const unsigned spy = p.spriteMapper.posbuf()[pos ]; - const unsigned spx = p.spriteMapper.posbuf()[pos+1]; + unsigned pos = sprites[i]; + unsigned spy = p.spriteMapper.posbuf()[pos ]; + unsigned spx = p.spriteMapper.posbuf()[pos+1]; p.spriteList[i].spx = spx; p.spriteList[i].line = ly + 16u - spy; @@ -1597,14 +1624,14 @@ static void loadSpriteList(PPUPriv &p, const SaveState &ss) { } -void PPU::loadState(const SaveState &ss, const unsigned char *const oamram) { - const PPUState *const m3loopState = decodeM3LoopState(ss.ppu.state); - const long videoCycles = std::min(ss.ppu.videoCycles, 70223UL); - const bool ds = p_.cgb & ss.mem.ioamhram.get()[0x14D] >> 7; - const long vcycs = videoCycles - ds * M2_DS_OFFSET < 0 - ? videoCycles - ds * M2_DS_OFFSET + 70224 - : videoCycles - ds * M2_DS_OFFSET; - const long lineCycles = static_cast(vcycs) % 456; +void PPU::loadState(SaveState const &ss, unsigned char const *const oamram) { + PPUState const *const m3loopState = decodeM3LoopState(ss.ppu.state); + long const videoCycles = std::min(ss.ppu.videoCycles, 70223UL); + bool const ds = p_.cgb & ss.mem.ioamhram.get()[0x14D] >> 7; + long const vcycs = videoCycles - ds * m2_ds_offset < 0 + ? videoCycles - ds * m2_ds_offset + 70224 + : videoCycles - ds * m2_ds_offset; + long const lineCycles = static_cast(vcycs) % 456; p_.now = ss.cpu.cycleCounter; p_.lcdc = ss.mem.ioamhram.get()[0x140]; @@ -1628,25 +1655,25 @@ void PPU::loadState(const SaveState &ss, const unsigned char *const oamram) { p_.nattrib = ss.ppu.nattrib & 0xFF; p_.wscx = ss.ppu.wscx; p_.weMaster = ss.ppu.weMaster; - p_.winDrawState = ss.ppu.winDrawState & (WIN_DRAW_START | WIN_DRAW_STARTED); + p_.winDrawState = ss.ppu.winDrawState & (win_draw_start | win_draw_started); p_.lastM0Time = p_.now - ss.ppu.lastM0Time; p_.cgb = ss.ppu.isCgb; loadSpriteList(p_, ss); if (m3loopState && videoCycles < 144 * 456L && p_.xpos < 168 - && lineCycles + cyclesUntilM0Upperbound(p_) < long(weMasterCheckPriorToLyIncLineCycle(p_.cgb))) { + && lineCycles + cyclesUntilM0Upperbound(p_) < weMasterCheckPriorToLyIncLineCycle(p_.cgb)) { p_.nextCallPtr = m3loopState; p_.cycles = -1; - } else if (vcycs < 143 * 456L + long(m3StartLineCycle(p_.cgb)) + MAX_M3START_CYCLES) { - const CycleState lineCycleStates[] = { - { &M3Start::f0_, m3StartLineCycle(p_.cgb) }, - { &M3Start::f1_, m3StartLineCycle(p_.cgb) + MAX_M3START_CYCLES }, - { &M2::LyNon0::f0_, weMasterCheckPriorToLyIncLineCycle(p_.cgb) }, - { &M2::LyNon0::f1_, weMasterCheckAfterLyIncLineCycle(p_.cgb) }, - { &M3Start::f0_, m3StartLineCycle(p_.cgb) + 456 } + } else if (vcycs < 143 * 456L + static_cast(m3StartLineCycle(p_.cgb)) + max_m3start_cycles) { + CycleState const lineCycleStates[] = { + { &M3Start::f0_, m3StartLineCycle(p_.cgb) }, + { &M3Start::f1_, m3StartLineCycle(p_.cgb) + max_m3start_cycles }, + { &M2_LyNon0::f0_, weMasterCheckPriorToLyIncLineCycle(p_.cgb) }, + { &M2_LyNon0::f1_, weMasterCheckAfterLyIncLineCycle(p_.cgb) }, + { &M3Start::f0_, m3StartLineCycle(p_.cgb) + 456 } }; - const std::size_t pos = + std::size_t const pos = upperBound(lineCycleStates, lineCycles); p_.cycles = lineCycles - lineCycleStates[pos].cycle; @@ -1658,19 +1685,19 @@ void PPU::loadState(const SaveState &ss, const unsigned char *const oamram) { } } else { p_.cycles = vcycs - 70224; - p_.nextCallPtr = &M2::Ly0::f0_; + p_.nextCallPtr = &M2_Ly0::f0_; } } -void PPU::reset(const unsigned char *const oamram, const unsigned char *const vram, const bool cgb) { +void PPU::reset(unsigned char const *oamram, unsigned char const *vram, bool cgb) { p_.vram = vram; p_.cgb = cgb; p_.spriteMapper.reset(oamram, cgb); } -void PPU::resetCc(const unsigned long oldCc, const unsigned long newCc) { - const unsigned long dec = oldCc - newCc; - const unsigned long videoCycles = p_.lcdc & 0x80 ? p_.lyCounter.frameCycles(p_.now) : 0; +void PPU::resetCc(unsigned long const oldCc, unsigned long const newCc) { + unsigned long const dec = oldCc - newCc; + unsigned long const videoCycles = lcdcEn(p_) ? p_.lyCounter.frameCycles(p_.now) : 0; p_.now -= dec; p_.lastM0Time = p_.lastM0Time ? p_.lastM0Time - dec : p_.lastM0Time; @@ -1678,58 +1705,59 @@ void PPU::resetCc(const unsigned long oldCc, const unsigned long newCc) { p_.spriteMapper.resetCycleCounter(oldCc, newCc); } -void PPU::speedChange(const unsigned long cycleCounter) { - const unsigned long videoCycles = p_.lcdc & 0x80 ? p_.lyCounter.frameCycles(p_.now) : 0; +void PPU::speedChange(unsigned long const cycleCounter) { + unsigned long const videoCycles = lcdcEn(p_) ? p_.lyCounter.frameCycles(p_.now) : 0; p_.spriteMapper.preSpeedChange(cycleCounter); p_.lyCounter.setDoubleSpeed(!p_.lyCounter.isDoubleSpeed()); p_.lyCounter.reset(videoCycles, p_.now); p_.spriteMapper.postSpeedChange(cycleCounter); - if (&M2::Ly0::f0_ == p_.nextCallPtr || &M2::LyNon0::f0_ == p_.nextCallPtr) { + if (&M2_Ly0::f0_ == p_.nextCallPtr || &M2_LyNon0::f0_ == p_.nextCallPtr) { if (p_.lyCounter.isDoubleSpeed()) { - p_.cycles -= M2_DS_OFFSET; + p_.cycles -= m2_ds_offset; } else - p_.cycles += M2_DS_OFFSET; + p_.cycles += m2_ds_offset; } } -unsigned long PPU::predictedNextXposTime(const unsigned xpos) const { - return p_.now + (p_.nextCallPtr->predictCyclesUntilXpos_f(p_, xpos, -p_.cycles) << p_.lyCounter.isDoubleSpeed()); +unsigned long PPU::predictedNextXposTime(unsigned xpos) const { + return p_.now + + (p_.nextCallPtr->predictCyclesUntilXpos_f(p_, xpos, -p_.cycles) << p_.lyCounter.isDoubleSpeed()); } -void PPU::setLcdc(const unsigned lcdc, const unsigned long cc) { - if ((p_.lcdc ^ lcdc) & lcdc & 0x80) { +void PPU::setLcdc(unsigned const lcdc, unsigned long const cc) { + if ((p_.lcdc ^ lcdc) & lcdc & lcdc_en) { p_.now = cc; p_.lastM0Time = 0; p_.lyCounter.reset(0, p_.now); p_.spriteMapper.enableDisplay(cc); - p_.weMaster = (lcdc & 0x20) && 0 == p_.wy; + p_.weMaster = (lcdc & lcdc_we) && 0 == p_.wy; p_.winDrawState = 0; p_.nextCallPtr = &M3Start::f0_; - p_.cycles = -int(m3StartLineCycle(p_.cgb) + M2_DS_OFFSET * p_.lyCounter.isDoubleSpeed()); - } else if ((p_.lcdc ^ lcdc) & 0x20) { - if (!(lcdc & 0x20)) { - if (p_.winDrawState == WIN_DRAW_STARTED || p_.xpos == 168) - p_.winDrawState &= ~WIN_DRAW_STARTED; - } else if (p_.winDrawState == WIN_DRAW_START) { - p_.winDrawState |= WIN_DRAW_STARTED; + p_.cycles = -int(m3StartLineCycle(p_.cgb) + m2_ds_offset * p_.lyCounter.isDoubleSpeed()); + } else if ((p_.lcdc ^ lcdc) & lcdc_we) { + if (!(lcdc & lcdc_we)) { + if (p_.winDrawState == win_draw_started || p_.xpos == 168) + p_.winDrawState &= ~win_draw_started; + } else if (p_.winDrawState == win_draw_start) { + p_.winDrawState |= win_draw_started; ++p_.winYPos; } } - if ((p_.lcdc ^ lcdc) & 0x04) { - if (p_.lcdc & lcdc & 0x80) + if ((p_.lcdc ^ lcdc) & lcdc_obj2x) { + if (p_.lcdc & lcdc & lcdc_en) p_.spriteMapper.oamChange(cc); - p_.spriteMapper.setLargeSpritesSource(lcdc & 0x04); + p_.spriteMapper.setLargeSpritesSource(lcdc & lcdc_obj2x); } p_.lcdc = lcdc; } -void PPU::update(const unsigned long cc) { - const int cycles = (cc - p_.now) >> p_.lyCounter.isDoubleSpeed(); +void PPU::update(unsigned long const cc) { + int const cycles = (cc - p_.now) >> p_.lyCounter.isDoubleSpeed(); p_.now += cycles << p_.lyCounter.isDoubleSpeed(); p_.cycles += cycles; @@ -1750,9 +1778,9 @@ SYNCFUNC(PPU) NSS(p_.currentSprite); EBS(p_.nextCallPtr, 0); - EVS(p_.nextCallPtr, &M2::Ly0::f0_, 1); - EVS(p_.nextCallPtr, &M2::LyNon0::f0_, 2); - EVS(p_.nextCallPtr, &M2::LyNon0::f1_, 3); + EVS(p_.nextCallPtr, &M2_Ly0::f0_, 1); + EVS(p_.nextCallPtr, &M2_LyNon0::f0_, 2); + EVS(p_.nextCallPtr, &M2_LyNon0::f1_, 3); EVS(p_.nextCallPtr, &M3Start::f0_, 4); EVS(p_.nextCallPtr, &M3Start::f1_, 5); EVS(p_.nextCallPtr, &M3Loop::Tile::f0_, 6); diff --git a/libgambatte/src/video/ppu.h b/libgambatte/src/video/ppu.h index 9ef73f74d4..c3d7f37da5 100644 --- a/libgambatte/src/video/ppu.h +++ b/libgambatte/src/video/ppu.h @@ -19,6 +19,7 @@ #ifndef PPU_H #define PPU_H +#include "lcddef.h" #include "video/ly_counter.h" #include "video/sprite_mapper.h" #include "gbint.h" diff --git a/output/dll/libgambatte.dll b/output/dll/libgambatte.dll index 5c29f24fa2a36f6061d21dd0b8062d3379613fce..a871f98e63ae8c618c519c765adff6e10cab0465 100644 GIT binary patch delta 9215 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 delta 9822 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 SmU@l4P+g*Sj~Pw3i2n!ds3=hY From 1dc9d0e123881500072406380f8e7cf3446d9a2e Mon Sep 17 00:00:00 2001 From: MrWint Date: Sat, 25 May 2019 19:35:42 +0200 Subject: [PATCH 07/38] allow mapping rom bank0 in more cases --- libgambatte/src/mem/cartridge.cpp | 17 ++++++++++++----- output/dll/libgambatte.dll | Bin 141824 -> 141824 bytes 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/libgambatte/src/mem/cartridge.cpp b/libgambatte/src/mem/cartridge.cpp index 5d3dea9e4d..e1f570fdcc 100644 --- a/libgambatte/src/mem/cartridge.cpp +++ b/libgambatte/src/mem/cartridge.cpp @@ -92,7 +92,7 @@ class Mbc1 : public DefaultMbc { static unsigned adjustedRombank(unsigned bank) { return bank & 0x1F ? bank : bank | 1; } void setRambank() const { memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, rambank & (rambanks(memptrs) - 1)); } - void setRombank() const { memptrs.setRombank(adjustedRombank(rombank & (rombanks(memptrs) - 1))); } + void setRombank() const { memptrs.setRombank(adjustedRombank(rombank) & (rombanks(memptrs) - 1)); } public: explicit Mbc1(MemPtrs &memptrs) @@ -172,7 +172,7 @@ class Mbc1Multi64 : public Mbc { memptrs.setRombank(adjustedRombank(rb)); } else { memptrs.setRombank0(0); - memptrs.setRombank(adjustedRombank(rombank & (rombanks(memptrs) - 1))); + memptrs.setRombank(adjustedRombank(rombank) & (rombanks(memptrs) - 1)); } } @@ -193,7 +193,9 @@ public: break; case 1: rombank = (rombank & 0x60) | (data & 0x1F); - memptrs.setRombank(adjustedRombank(rombank0Mode ? toMulti64Rombank(rombank) : rombank & (rombanks(memptrs) - 1))); + memptrs.setRombank(rombank0Mode + ? adjustedRombank(toMulti64Rombank(rombank)) + : adjustedRombank(rombank) & (rombanks(memptrs) - 1)); break; case 2: rombank = (data << 5 & 0x60) | (rombank & 0x1F); @@ -433,8 +435,13 @@ class Mbc5 : public DefaultMbc { bool enableRam; static unsigned adjustedRombank(const unsigned bank) { return bank; } - void setRambank() const { memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, rambank & (rambanks(memptrs) - 1)); } - void setRombank() const { memptrs.setRombank(adjustedRombank(rombank & (rombanks(memptrs) - 1))); } + + void setRambank() const { + memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, + rambank & (rambanks(memptrs) - 1)); + } + + void setRombank() const { memptrs.setRombank(adjustedRombank(rombank) & (rombanks(memptrs) - 1)); } public: explicit Mbc5(MemPtrs &memptrs) diff --git a/output/dll/libgambatte.dll b/output/dll/libgambatte.dll index a871f98e63ae8c618c519c765adff6e10cab0465..4b53f609938abdf7c1054faf54c797ffd518d0dc 100644 GIT binary patch delta 467 zcmZp;!qISrV}k%A)9jkfLW~Bdm;yN_mt0a|l%IU?iWYl(+{T)}F?9R0lz8%QbLW`6>8!q3j3@&`cRfq@vF>xtCm0(Z z%JaK?c@fI9d0+w079P|NkNGaOF{9 z>~Q0H;Q|xkmuG0Z#UQW=hz)Kt2>3S_p5I=0p3zv2v0(esw~XgGtY+7|i~(B5zy!n| zK)geoiDAcoMvxE#OpXW026~!-K?0--gh6uC*L`D@0!G;OQ{NbwI2qZupZ(9+FU#1p KJyw=!F%JMMPN*yZ delta 579 zcmZp;!qISrV}k%AlXUfFAx48!I*uG+-GwaO(-tr=FlbMk10)+?F*GnR9DK#+rF^0L zRQI9g6O0WH<-5=EyG%~JuEw@PoPpuYna#6Ki!thUm$UG1bLSvhx5{M3v-5?5yKja! zRC6(Od$W|p@NaYG2%Eg|tbTp8C<8-xHB0xg?sE_;`CUGAw}V{NxqS_gJot={`B>cR zxb8-f7%PJVP@=H`$n0e7bY$st0x*EMPBheUF?1d5@Z$*Yt^ Date: Sat, 25 May 2019 20:27:44 +0200 Subject: [PATCH 08/38] Update sound --- libgambatte/src/initstate.cpp | 32 +- libgambatte/src/memory.cpp | 92 ++--- libgambatte/src/savestate.h | 1 + libgambatte/src/sound.cpp | 199 ++++++----- libgambatte/src/sound.h | 92 +++-- libgambatte/src/sound/channel1.cpp | 327 ++++++++--------- libgambatte/src/sound/channel1.h | 132 +++---- libgambatte/src/sound/channel2.cpp | 215 ++++++----- libgambatte/src/sound/channel2.h | 86 +++-- libgambatte/src/sound/channel3.cpp | 281 +++++++-------- libgambatte/src/sound/channel3.h | 126 +++---- libgambatte/src/sound/channel4.cpp | 352 ++++++++----------- libgambatte/src/sound/channel4.h | 155 ++++---- libgambatte/src/sound/duty_unit.cpp | 200 ++++++----- libgambatte/src/sound/duty_unit.h | 91 ++--- libgambatte/src/sound/envelope_unit.cpp | 138 ++++---- libgambatte/src/sound/envelope_unit.h | 58 +-- libgambatte/src/sound/length_counter.cpp | 104 +++--- libgambatte/src/sound/length_counter.h | 53 ++- libgambatte/src/sound/master_disabler.h | 47 +-- libgambatte/src/sound/sound_unit.h | 54 +-- libgambatte/src/sound/static_output_tester.h | 52 +-- output/dll/libgambatte.dll | Bin 141824 -> 142336 bytes 23 files changed, 1424 insertions(+), 1463 deletions(-) diff --git a/libgambatte/src/initstate.cpp b/libgambatte/src/initstate.cpp index 7134577e2d..ccd984d745 100644 --- a/libgambatte/src/initstate.cpp +++ b/libgambatte/src/initstate.cpp @@ -1266,37 +1266,37 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM state.spu.cycleCounter = 0; // spu.cycleCounter >> 12 & 7 represents the frame sequencer position. - state.spu.ch1.sweep.counter = SoundUnit::COUNTER_DISABLED; + state.spu.ch1.sweep.counter = SoundUnit::counter_disabled; state.spu.ch1.sweep.shadow = 0; state.spu.ch1.sweep.nr0 = 0; state.spu.ch1.sweep.negging = false; - state.spu.ch1.duty.nextPosUpdate = (state.spu.cycleCounter & ~1ul) + 37 * 2; - state.spu.ch1.duty.nr3 = 0; + state.spu.ch1.duty.nextPosUpdate = SoundUnit::counter_disabled; state.spu.ch1.duty.pos = 0; - state.spu.ch1.env.counter = SoundUnit::COUNTER_DISABLED; + state.spu.ch1.duty.high = false; + state.spu.ch1.duty.nr3 = 0; + state.spu.ch1.env.counter = SoundUnit::counter_disabled; state.spu.ch1.env.volume = 0; - state.spu.ch1.lcounter.counter = SoundUnit::COUNTER_DISABLED; + state.spu.ch1.lcounter.counter = SoundUnit::counter_disabled; state.spu.ch1.lcounter.lengthCounter = 0; state.spu.ch1.nr4 = 0; state.spu.ch1.master = false; - state.spu.ch2.duty.nextPosUpdate = SoundUnit::COUNTER_DISABLED; + state.spu.ch2.duty.nextPosUpdate = SoundUnit::counter_disabled; state.spu.ch2.duty.nr3 = 0; state.spu.ch2.duty.pos = 0; - state.spu.ch2.env.counter = SoundUnit::COUNTER_DISABLED; + state.spu.ch2.duty.high = false; + state.spu.ch2.env.counter = SoundUnit::counter_disabled; state.spu.ch2.env.volume = 0; - state.spu.ch2.lcounter.counter = SoundUnit::COUNTER_DISABLED; + state.spu.ch2.lcounter.counter = SoundUnit::counter_disabled; state.spu.ch2.lcounter.lengthCounter = 0; state.spu.ch2.nr4 = 0; state.spu.ch2.master = false; - for (unsigned i = 0; i < 0x10; ++i) - state.spu.ch3.waveRam.ptr[i] = state.mem.ioamhram.get()[0x130 + i]; - - state.spu.ch3.lcounter.counter = SoundUnit::COUNTER_DISABLED; + std::memcpy(state.spu.ch3.waveRam.ptr, state.mem.ioamhram.get() + 0x130, 0x10); + state.spu.ch3.lcounter.counter = SoundUnit::counter_disabled; state.spu.ch3.lcounter.lengthCounter = 0x100; - state.spu.ch3.waveCounter = SoundUnit::COUNTER_DISABLED; - state.spu.ch3.lastReadTime = SoundUnit::COUNTER_DISABLED; + state.spu.ch3.waveCounter = SoundUnit::counter_disabled; + state.spu.ch3.lastReadTime = SoundUnit::counter_disabled; state.spu.ch3.nr3 = 0; state.spu.ch3.nr4 = 0; state.spu.ch3.wavePos = 0; @@ -1305,9 +1305,9 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM state.spu.ch4.lfsr.counter = state.spu.cycleCounter + 4; state.spu.ch4.lfsr.reg = 0xFF; - state.spu.ch4.env.counter = SoundUnit::COUNTER_DISABLED; + state.spu.ch4.env.counter = SoundUnit::counter_disabled; state.spu.ch4.env.volume = 0; - state.spu.ch4.lcounter.counter = SoundUnit::COUNTER_DISABLED; + state.spu.ch4.lcounter.counter = SoundUnit::counter_disabled; state.spu.ch4.lcounter.lengthCounter = 0; state.spu.ch4.nr4 = 0; state.spu.ch4.master = false; diff --git a/libgambatte/src/memory.cpp b/libgambatte/src/memory.cpp index a9fa6a031c..328e6cf083 100644 --- a/libgambatte/src/memory.cpp +++ b/libgambatte/src/memory.cpp @@ -321,7 +321,7 @@ unsigned long Memory::stop(unsigned long cycleCounter) { cycleCounter += 4; if (ioamhram[0x14D] & isCgb()) { - sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.generateSamples(cycleCounter, isDoubleSpeed()); display.speedChange(cycleCounter); ioamhram[0x14D] ^= 0x81; @@ -496,7 +496,7 @@ unsigned Memory::nontrivial_ff_read(const unsigned P, const unsigned long cycleC break; case 0x26: if (ioamhram[0x126] & 0x80) { - sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.generateSamples(cycleCounter, isDoubleSpeed()); ioamhram[0x126] = 0xF0 | sound.getStatus(); } else ioamhram[0x126] = 0x70; @@ -518,7 +518,7 @@ unsigned Memory::nontrivial_ff_read(const unsigned P, const unsigned long cycleC case 0x3D: case 0x3E: case 0x3F: - sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.generateSamples(cycleCounter, isDoubleSpeed()); return sound.waveRamRead(P & 0xF); case 0x41: return ioamhram[0x141] | display.getStat(ioamhram[0x145], cycleCounter); @@ -670,8 +670,8 @@ void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned return; case 0x10: if (!sound.isEnabled()) return; - sound.generate_samples(cycleCounter, isDoubleSpeed()); - sound.set_nr10(data); + sound.generateSamples(cycleCounter, isDoubleSpeed()); + sound.setNr10(data); data |= 0x80; break; case 0x11: @@ -682,24 +682,24 @@ void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned data &= 0x3F; } - sound.generate_samples(cycleCounter, isDoubleSpeed()); - sound.set_nr11(data); + sound.generateSamples(cycleCounter, isDoubleSpeed()); + sound.setNr11(data); data |= 0x3F; break; case 0x12: if (!sound.isEnabled()) return; - sound.generate_samples(cycleCounter, isDoubleSpeed()); - sound.set_nr12(data); + sound.generateSamples(cycleCounter, isDoubleSpeed()); + sound.setNr12(data); break; case 0x13: if (!sound.isEnabled()) return; - sound.generate_samples(cycleCounter, isDoubleSpeed()); - sound.set_nr13(data); + sound.generateSamples(cycleCounter, isDoubleSpeed()); + sound.setNr13(data); return; case 0x14: if (!sound.isEnabled()) return; - sound.generate_samples(cycleCounter, isDoubleSpeed()); - sound.set_nr14(data); + sound.generateSamples(cycleCounter, isDoubleSpeed()); + sound.setNr14(data); data |= 0xBF; break; case 0x16: @@ -710,88 +710,88 @@ void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned data &= 0x3F; } - sound.generate_samples(cycleCounter, isDoubleSpeed()); - sound.set_nr21(data); + sound.generateSamples(cycleCounter, isDoubleSpeed()); + sound.setNr21(data); data |= 0x3F; break; case 0x17: if (!sound.isEnabled()) return; - sound.generate_samples(cycleCounter, isDoubleSpeed()); - sound.set_nr22(data); + sound.generateSamples(cycleCounter, isDoubleSpeed()); + sound.setNr22(data); break; case 0x18: if (!sound.isEnabled()) return; - sound.generate_samples(cycleCounter, isDoubleSpeed()); - sound.set_nr23(data); + sound.generateSamples(cycleCounter, isDoubleSpeed()); + sound.setNr23(data); return; case 0x19: if (!sound.isEnabled()) return; - sound.generate_samples(cycleCounter, isDoubleSpeed()); - sound.set_nr24(data); + sound.generateSamples(cycleCounter, isDoubleSpeed()); + sound.setNr24(data); data |= 0xBF; break; case 0x1A: if (!sound.isEnabled()) return; - sound.generate_samples(cycleCounter, isDoubleSpeed()); - sound.set_nr30(data); + sound.generateSamples(cycleCounter, isDoubleSpeed()); + sound.setNr30(data); data |= 0x7F; break; case 0x1B: if (!sound.isEnabled() && isCgb()) return; - sound.generate_samples(cycleCounter, isDoubleSpeed()); - sound.set_nr31(data); + sound.generateSamples(cycleCounter, isDoubleSpeed()); + sound.setNr31(data); return; case 0x1C: if (!sound.isEnabled()) return; - sound.generate_samples(cycleCounter, isDoubleSpeed()); - sound.set_nr32(data); + sound.generateSamples(cycleCounter, isDoubleSpeed()); + sound.setNr32(data); data |= 0x9F; break; case 0x1D: if (!sound.isEnabled()) return; - sound.generate_samples(cycleCounter, isDoubleSpeed()); - sound.set_nr33(data); + sound.generateSamples(cycleCounter, isDoubleSpeed()); + sound.setNr33(data); return; case 0x1E: if (!sound.isEnabled()) return; - sound.generate_samples(cycleCounter, isDoubleSpeed()); - sound.set_nr34(data); + sound.generateSamples(cycleCounter, isDoubleSpeed()); + sound.setNr34(data); data |= 0xBF; break; case 0x20: if (!sound.isEnabled() && isCgb()) return; - sound.generate_samples(cycleCounter, isDoubleSpeed()); - sound.set_nr41(data); + sound.generateSamples(cycleCounter, isDoubleSpeed()); + sound.setNr41(data); return; case 0x21: if (!sound.isEnabled()) return; - sound.generate_samples(cycleCounter, isDoubleSpeed()); - sound.set_nr42(data); + sound.generateSamples(cycleCounter, isDoubleSpeed()); + sound.setNr42(data); break; case 0x22: if (!sound.isEnabled()) return; - sound.generate_samples(cycleCounter, isDoubleSpeed()); - sound.set_nr43(data); + sound.generateSamples(cycleCounter, isDoubleSpeed()); + sound.setNr43(data); break; case 0x23: if (!sound.isEnabled()) return; - sound.generate_samples(cycleCounter, isDoubleSpeed()); - sound.set_nr44(data); + sound.generateSamples(cycleCounter, isDoubleSpeed()); + sound.setNr44(data); data |= 0xBF; break; case 0x24: if (!sound.isEnabled()) return; - sound.generate_samples(cycleCounter, isDoubleSpeed()); - sound.set_so_volume(data); + sound.generateSamples(cycleCounter, isDoubleSpeed()); + sound.setSoVolume(data); break; case 0x25: if (!sound.isEnabled()) return; - sound.generate_samples(cycleCounter, isDoubleSpeed()); - sound.map_so(data); + sound.generateSamples(cycleCounter, isDoubleSpeed()); + sound.mapSo(data); break; case 0x26: if ((ioamhram[0x126] ^ data) & 0x80) { - sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.generateSamples(cycleCounter, isDoubleSpeed()); if (!(data & 0x80)) { for (unsigned i = 0xFF10; i < 0xFF26; ++i) @@ -822,7 +822,7 @@ void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned case 0x3D: case 0x3E: case 0x3F: - sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.generateSamples(cycleCounter, isDoubleSpeed()); sound.waveRamWrite(P & 0xF, data); break; case 0x40: @@ -1067,7 +1067,7 @@ LoadRes Memory::loadROM(const char *romfiledata, unsigned romfilelength, const b } unsigned Memory::fillSoundBuffer(const unsigned long cycleCounter) { - sound.generate_samples(cycleCounter, isDoubleSpeed()); + sound.generateSamples(cycleCounter, isDoubleSpeed()); return sound.fillBuffer(); } diff --git a/libgambatte/src/savestate.h b/libgambatte/src/savestate.h index 598426edd8..13707b1e53 100644 --- a/libgambatte/src/savestate.h +++ b/libgambatte/src/savestate.h @@ -125,6 +125,7 @@ struct SaveState { unsigned long nextPosUpdate; unsigned char nr3; unsigned char pos; + unsigned char /*bool*/ high; }; struct Env { diff --git a/libgambatte/src/sound.cpp b/libgambatte/src/sound.cpp index 68449b158e..69ebe39f18 100644 --- a/libgambatte/src/sound.cpp +++ b/libgambatte/src/sound.cpp @@ -1,33 +1,33 @@ -/*************************************************************************** - * Copyright (C) 2007 by Sindre AamÃ¥s * - * aamas@stud.ntnu.no * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License version 2 as * - * published by the Free Software Foundation. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License version 2 for more details. * - * * - * You should have received a copy of the GNU General Public License * - * version 2 along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ +// +// Copyright (C) 2007 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + #include "sound.h" #include "savestate.h" -#include #include +#include /* Frame Sequencer Step Length Ctr Vol Env Sweep - - - - - - - - - - - - - - - - - - - - - 0 Clock - Clock -S 1 - Clock - +S0 0 Clock - Clock +S1 1 - Clock - 2 Clock - - 3 - - - 4 Clock - Clock @@ -37,82 +37,82 @@ S 1 - Clock - - - - - - - - - - - - - - - - - - - - - Rate 256 Hz 64 Hz 128 Hz -S) start step on sound power on. +S0) start step on sound power on. +S1) step gets immediately incremented at power on if closer to previous edge than next. +Clock) clock timer on transition to step. + */ namespace gambatte { PSG::PSG() -: buffer(0), - lastUpdate(0), - soVol(0), - rsum(0x8000), // initialize to 0x8000 to prevent borrows from high word, xor away later - bufferPos(0), - enabled(false) +: buffer_(0) +, bufferPos_(0) +, lastUpdate_(0) +, soVol_(0) +, rsum_(0x8000) // initialize to 0x8000 to prevent borrows from high word, xor away later +, enabled_(false) { } -void PSG::init(const bool cgb) { - ch1.init(cgb); - ch2.init(cgb); - ch3.init(cgb); - ch4.init(cgb); +void PSG::init(bool cgb) { + ch1_.init(cgb); + ch3_.init(cgb); } void PSG::reset() { - ch1.reset(); - ch2.reset(); - ch3.reset(); - ch4.reset(); + ch1_.reset(); + ch2_.reset(); + ch3_.reset(); + ch4_.reset(); } void PSG::setStatePtrs(SaveState &state) { - ch3.setStatePtrs(state); + ch3_.setStatePtrs(state); } -void PSG::loadState(const SaveState &state) { - ch1.loadState(state); - ch2.loadState(state); - ch3.loadState(state); - ch4.loadState(state); +void PSG::loadState(SaveState const &state) { + ch1_.loadState(state); + ch2_.loadState(state); + ch3_.loadState(state); + ch4_.loadState(state); - lastUpdate = state.cpu.cycleCounter; - set_so_volume(state.mem.ioamhram.get()[0x124]); - map_so(state.mem.ioamhram.get()[0x125]); - enabled = state.mem.ioamhram.get()[0x126] >> 7 & 1; + lastUpdate_ = state.cpu.cycleCounter; + setSoVolume(state.mem.ioamhram.get()[0x124]); + mapSo(state.mem.ioamhram.get()[0x125]); + enabled_ = state.mem.ioamhram.get()[0x126] >> 7 & 1; } -void PSG::accumulate_channels(const unsigned long cycles) { - uint_least32_t *const buf = buffer + bufferPos; - +void PSG::accumulateChannels(unsigned long const cycles) { + uint_least32_t *const buf = buffer_ + bufferPos_; std::memset(buf, 0, cycles * sizeof *buf); - ch1.update(buf, soVol, cycles); - ch2.update(buf, soVol, cycles); - ch3.update(buf, soVol, cycles); - ch4.update(buf, soVol, cycles); + ch1_.update(buf, soVol_, cycles); + ch2_.update(buf, soVol_, cycles); + ch3_.update(buf, soVol_, cycles); + ch4_.update(buf, soVol_, cycles); } -void PSG::generate_samples(const unsigned long cycleCounter, const unsigned doubleSpeed) { - const unsigned long cycles = (cycleCounter - lastUpdate) >> (1 + doubleSpeed); - lastUpdate += cycles << (1 + doubleSpeed); +void PSG::generateSamples(unsigned long const cycleCounter, bool const doubleSpeed) { + unsigned long const cycles = (cycleCounter - lastUpdate_) >> (1 + doubleSpeed); + lastUpdate_ += cycles << (1 + doubleSpeed); if (cycles) - accumulate_channels(cycles); + accumulateChannels(cycles); - bufferPos += cycles; + bufferPos_ += cycles; } -void PSG::resetCounter(const unsigned long newCc, const unsigned long oldCc, const unsigned doubleSpeed) { - generate_samples(oldCc, doubleSpeed); - lastUpdate = newCc - (oldCc - lastUpdate); +void PSG::resetCounter(unsigned long newCc, unsigned long oldCc, bool doubleSpeed) { + generateSamples(oldCc, doubleSpeed); + lastUpdate_ = newCc - (oldCc - lastUpdate_); } -unsigned PSG::fillBuffer() { - uint_least32_t sum = rsum; - uint_least32_t *b = buffer; - unsigned n = bufferPos; +std::size_t PSG::fillBuffer() { + uint_least32_t sum = rsum_; + uint_least32_t *b = buffer_; + std::size_t n = bufferPos_; - if (unsigned n2 = n >> 3) { + if (std::size_t n2 = n >> 3) { n -= n2 << 3; do { @@ -139,50 +139,59 @@ unsigned PSG::fillBuffer() { while (n--) { sum += *b; - *b++ = sum ^ 0x8000; // xor away the initial rsum value of 0x8000 (which prevents borrows from the high word) from the low word + // xor away the initial rsum value of 0x8000 (which prevents + // borrows from the high word) from the low word + *b++ = sum ^ 0x8000; } - rsum = sum; + rsum_ = sum; - return bufferPos; + return bufferPos_; } -#ifdef WORDS_BIGENDIAN -static const unsigned long so1Mul = 0x00000001; -static const unsigned long so2Mul = 0x00010000; -#else -static const unsigned long so1Mul = 0x00010000; -static const unsigned long so2Mul = 0x00000001; -#endif - -void PSG::set_so_volume(const unsigned nr50) { - soVol = (((nr50 & 0x7) + 1) * so1Mul + ((nr50 >> 4 & 0x7) + 1) * so2Mul) * 64; +static bool isBigEndianSampleOrder() { + union { + uint_least32_t ul32; + unsigned char uc[sizeof(uint_least32_t)]; + } u; + u.ul32 = -0x10000; + return u.uc[0]; } -void PSG::map_so(const unsigned nr51) { - const unsigned long tmp = nr51 * so1Mul + (nr51 >> 4) * so2Mul; +static unsigned long so1Mul() { return isBigEndianSampleOrder() ? 0x00000001 : 0x00010000; } +static unsigned long so2Mul() { return isBigEndianSampleOrder() ? 0x00010000 : 0x00000001; } - ch1.setSo((tmp & 0x00010001) * 0xFFFF); - ch2.setSo((tmp >> 1 & 0x00010001) * 0xFFFF); - ch3.setSo((tmp >> 2 & 0x00010001) * 0xFFFF); - ch4.setSo((tmp >> 3 & 0x00010001) * 0xFFFF); +void PSG::setSoVolume(unsigned nr50) { + soVol_ = ((nr50 & 0x7) + 1) * so1Mul() * 64 + + ((nr50 >> 4 & 0x7) + 1) * so2Mul() * 64; +} + +void PSG::mapSo(unsigned nr51) { + unsigned long so = nr51 * so1Mul() + (nr51 >> 4) * so2Mul(); + ch1_.setSo((so & 0x00010001) * 0xFFFF); + ch2_.setSo((so >> 1 & 0x00010001) * 0xFFFF); + ch3_.setSo((so >> 2 & 0x00010001) * 0xFFFF); + ch4_.setSo((so >> 3 & 0x00010001) * 0xFFFF); } unsigned PSG::getStatus() const { - return ch1.isActive() | ch2.isActive() << 1 | ch3.isActive() << 2 | ch4.isActive() << 3; + return ch1_.isActive() + | ch2_.isActive() << 1 + | ch3_.isActive() << 2 + | ch4_.isActive() << 3; } // the buffer and position are not saved, as they're set and flushed on each runfor() call SYNCFUNC(PSG) { - SSS(ch1); - SSS(ch2); - SSS(ch3); - SSS(ch4); - NSS(lastUpdate); - NSS(soVol); - NSS(rsum); - NSS(enabled); + SSS(ch1_); + SSS(ch2_); + SSS(ch3_); + SSS(ch4_); + NSS(lastUpdate_); + NSS(soVol_); + NSS(rsum_); + NSS(enabled_); } } diff --git a/libgambatte/src/sound.h b/libgambatte/src/sound.h index 367a10f1a8..ff3c7e0fb2 100644 --- a/libgambatte/src/sound.h +++ b/libgambatte/src/sound.h @@ -28,67 +28,65 @@ namespace gambatte { class PSG { - Channel1 ch1; - Channel2 ch2; - Channel3 ch3; - Channel4 ch4; - - uint_least32_t *buffer; - - unsigned long lastUpdate; - unsigned long soVol; - - uint_least32_t rsum; - - unsigned bufferPos; - - bool enabled; - - void accumulate_channels(unsigned long cycles); - public: PSG(); void init(bool cgb); void reset(); void setStatePtrs(SaveState &state); - void loadState(const SaveState &state); + void loadState(SaveState const &state); - void generate_samples(unsigned long cycleCounter, unsigned doubleSpeed); - void resetCounter(unsigned long newCc, unsigned long oldCc, unsigned doubleSpeed); - unsigned fillBuffer(); - void setBuffer(uint_least32_t *const buf) { buffer = buf; bufferPos = 0; } + void generateSamples(unsigned long cycleCounter, bool doubleSpeed); + void resetCounter(unsigned long newCc, unsigned long oldCc, bool doubleSpeed); + std::size_t fillBuffer(); + void setBuffer(uint_least32_t *buf) { buffer_ = buf; bufferPos_ = 0; } - bool isEnabled() const { return enabled; } - void setEnabled(bool value) { enabled = value; } + bool isEnabled() const { return enabled_; } + void setEnabled(bool value) { enabled_ = value; } - void set_nr10(unsigned data) { ch1.setNr0(data); } - void set_nr11(unsigned data) { ch1.setNr1(data); } - void set_nr12(unsigned data) { ch1.setNr2(data); } - void set_nr13(unsigned data) { ch1.setNr3(data); } - void set_nr14(unsigned data) { ch1.setNr4(data); } + void setNr10(unsigned data) { ch1_.setNr0(data); } + void setNr11(unsigned data) { ch1_.setNr1(data); } + void setNr12(unsigned data) { ch1_.setNr2(data); } + void setNr13(unsigned data) { ch1_.setNr3(data); } + void setNr14(unsigned data) { ch1_.setNr4(data); } - void set_nr21(unsigned data) { ch2.setNr1(data); } - void set_nr22(unsigned data) { ch2.setNr2(data); } - void set_nr23(unsigned data) { ch2.setNr3(data); } - void set_nr24(unsigned data) { ch2.setNr4(data); } + void setNr21(unsigned data) { ch2_.setNr1(data); } + void setNr22(unsigned data) { ch2_.setNr2(data); } + void setNr23(unsigned data) { ch2_.setNr3(data); } + void setNr24(unsigned data) { ch2_.setNr4(data); } - void set_nr30(unsigned data) { ch3.setNr0(data); } - void set_nr31(unsigned data) { ch3.setNr1(data); } - void set_nr32(unsigned data) { ch3.setNr2(data); } - void set_nr33(unsigned data) { ch3.setNr3(data); } - void set_nr34(unsigned data) { ch3.setNr4(data); } - unsigned waveRamRead(unsigned index) const { return ch3.waveRamRead(index); } - void waveRamWrite(unsigned index, unsigned data) { ch3.waveRamWrite(index, data); } + void setNr30(unsigned data) { ch3_.setNr0(data); } + void setNr31(unsigned data) { ch3_.setNr1(data); } + void setNr32(unsigned data) { ch3_.setNr2(data); } + void setNr33(unsigned data) { ch3_.setNr3(data); } + void setNr34(unsigned data) { ch3_.setNr4(data); } + unsigned waveRamRead(unsigned index) const { return ch3_.waveRamRead(index); } + void waveRamWrite(unsigned index, unsigned data) { ch3_.waveRamWrite(index, data); } - void set_nr41(unsigned data) { ch4.setNr1(data); } - void set_nr42(unsigned data) { ch4.setNr2(data); } - void set_nr43(unsigned data) { ch4.setNr3(data); } - void set_nr44(unsigned data) { ch4.setNr4(data); } + void setNr41(unsigned data) { ch4_.setNr1(data); } + void setNr42(unsigned data) { ch4_.setNr2(data); } + void setNr43(unsigned data) { ch4_.setNr3(data); } + void setNr44(unsigned data) { ch4_.setNr4(data); } - void set_so_volume(unsigned nr50); - void map_so(unsigned nr51); + void setSoVolume(unsigned nr50); + void mapSo(unsigned nr51); unsigned getStatus() const; +private: + Channel1 ch1_; + Channel2 ch2_; + Channel3 ch3_; + Channel4 ch4_; + uint_least32_t *buffer_; + std::size_t bufferPos_; + unsigned long lastUpdate_; + unsigned long soVol_; + uint_least32_t rsum_; + bool enabled_; + + void accumulateChannels(unsigned long cycles); + + +public: templatevoid SyncState(NewState *ns); }; diff --git a/libgambatte/src/sound/channel1.cpp b/libgambatte/src/sound/channel1.cpp index 4a9f0a7578..9e94ab2a20 100644 --- a/libgambatte/src/sound/channel1.cpp +++ b/libgambatte/src/sound/channel1.cpp @@ -1,275 +1,276 @@ -/*************************************************************************** - * Copyright (C) 2007 by Sindre Aam�s * - * aamas@stud.ntnu.no * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License version 2 as * - * published by the Free Software Foundation. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License version 2 for more details. * - * * - * You should have received a copy of the GNU General Public License * - * version 2 along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ +// +// Copyright (C) 2007 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + #include "channel1.h" #include "../savestate.h" #include - namespace gambatte { -Channel1::SweepUnit::SweepUnit(MasterDisabler &disabler, DutyUnit &dutyUnit) : - disableMaster(disabler), - dutyUnit(dutyUnit), - shadow(0), - nr0(0), - negging(false) -{} +Channel1::SweepUnit::SweepUnit(MasterDisabler &disabler, DutyUnit &dutyUnit) +: disableMaster_(disabler) +, dutyUnit_(dutyUnit) +, shadow_(0) +, nr0_(0) +, negging_(false) +, cgb_(false) +{ +} unsigned Channel1::SweepUnit::calcFreq() { - unsigned freq = shadow >> (nr0 & 0x07); + unsigned freq = shadow_ >> (nr0_ & 0x07); - if (nr0 & 0x08) { - freq = shadow - freq; - negging = true; + if (nr0_ & 0x08) { + freq = shadow_ - freq; + negging_ = true; } else - freq = shadow + freq; + freq = shadow_ + freq; if (freq & 2048) - disableMaster(); + disableMaster_(); return freq; } void Channel1::SweepUnit::event() { - const unsigned long period = nr0 >> 4 & 0x07; + unsigned long const period = nr0_ >> 4 & 0x07; if (period) { - const unsigned freq = calcFreq(); + unsigned const freq = calcFreq(); - if (!(freq & 2048) && (nr0 & 0x07)) { - shadow = freq; - dutyUnit.setFreq(freq, counter); + if (!(freq & 2048) && (nr0_ & 0x07)) { + shadow_ = freq; + dutyUnit_.setFreq(freq, counter_); calcFreq(); } - counter += period << 14; + counter_ += period << 14; } else - counter += 8ul << 14; + counter_ += 8ul << 14; } -void Channel1::SweepUnit::nr0Change(const unsigned newNr0) { - if (negging && !(newNr0 & 0x08)) - disableMaster(); +void Channel1::SweepUnit::nr0Change(unsigned newNr0) { + if (negging_ && !(newNr0 & 0x08)) + disableMaster_(); - nr0 = newNr0; + nr0_ = newNr0; } -void Channel1::SweepUnit::nr4Init(const unsigned long cc) { - negging = false; - shadow = dutyUnit.getFreq(); +void Channel1::SweepUnit::nr4Init(unsigned long const cc) { + negging_ = false; + shadow_ = dutyUnit_.freq(); - const unsigned period = nr0 >> 4 & 0x07; - const unsigned shift = nr0 & 0x07; + unsigned const period = nr0_ >> 4 & 0x07; + unsigned const shift = nr0_ & 0x07; if (period | shift) - counter = ((cc >> 14) + (period ? period : 8)) << 14; + counter_ = ((((cc + 2 + cgb_ * 2) >> 14) + (period ? period : 8)) << 14) + 2; else - counter = COUNTER_DISABLED; + counter_ = counter_disabled; if (shift) calcFreq(); } void Channel1::SweepUnit::reset() { - counter = COUNTER_DISABLED; + counter_ = counter_disabled; } -void Channel1::SweepUnit::loadState(const SaveState &state) { - counter = std::max(state.spu.ch1.sweep.counter, state.spu.cycleCounter); - shadow = state.spu.ch1.sweep.shadow; - nr0 = state.spu.ch1.sweep.nr0; - negging = state.spu.ch1.sweep.negging; +void Channel1::SweepUnit::loadState(SaveState const &state) { + counter_ = std::max(state.spu.ch1.sweep.counter, state.spu.cycleCounter); + shadow_ = state.spu.ch1.sweep.shadow; + nr0_ = state.spu.ch1.sweep.nr0; + negging_ = state.spu.ch1.sweep.negging; } template void Channel1::SweepUnit::SyncState(NewState *ns) { - NSS(counter); - NSS(shadow); - NSS(nr0); - NSS(negging); + NSS(counter_); + NSS(shadow_); + NSS(nr0_); + NSS(negging_); + NSS(cgb_); } -Channel1::Channel1() : - staticOutputTest(*this, dutyUnit), - disableMaster(master, dutyUnit), - lengthCounter(disableMaster, 0x3F), - envelopeUnit(staticOutputTest), - sweepUnit(disableMaster, dutyUnit), - cycleCounter(0), - soMask(0), - prevOut(0), - nr4(0), - master(false) +Channel1::Channel1() +: staticOutputTest_(*this, dutyUnit_) +, disableMaster_(master_, dutyUnit_) +, lengthCounter_(disableMaster_, 0x3F) +, envelopeUnit_(staticOutputTest_) +, sweepUnit_(disableMaster_, dutyUnit_) +, nextEventUnit_(0) +, cycleCounter_(0) +, soMask_(0) +, prevOut_(0) +, nr4_(0) +, master_(false) { setEvent(); } void Channel1::setEvent() { -// nextEventUnit = &dutyUnit; -// if (sweepUnit.getCounter() < nextEventUnit->getCounter()) - nextEventUnit = &sweepUnit; - if (envelopeUnit.getCounter() < nextEventUnit->getCounter()) - nextEventUnit = &envelopeUnit; - if (lengthCounter.getCounter() < nextEventUnit->getCounter()) - nextEventUnit = &lengthCounter; + nextEventUnit_ = &sweepUnit_; + if (envelopeUnit_.counter() < nextEventUnit_->counter()) + nextEventUnit_ = &envelopeUnit_; + if (lengthCounter_.counter() < nextEventUnit_->counter()) + nextEventUnit_ = &lengthCounter_; } -void Channel1::setNr0(const unsigned data) { - sweepUnit.nr0Change(data); +void Channel1::setNr0(unsigned data) { + sweepUnit_.nr0Change(data); setEvent(); } -void Channel1::setNr1(const unsigned data) { - lengthCounter.nr1Change(data, nr4, cycleCounter); - dutyUnit.nr1Change(data, cycleCounter); - +void Channel1::setNr1(unsigned data) { + lengthCounter_.nr1Change(data, nr4_, cycleCounter_); + dutyUnit_.nr1Change(data, cycleCounter_); setEvent(); } -void Channel1::setNr2(const unsigned data) { - if (envelopeUnit.nr2Change(data)) - disableMaster(); +void Channel1::setNr2(unsigned data) { + if (envelopeUnit_.nr2Change(data)) + disableMaster_(); else - staticOutputTest(cycleCounter); + staticOutputTest_(cycleCounter_); setEvent(); } -void Channel1::setNr3(const unsigned data) { - dutyUnit.nr3Change(data, cycleCounter); +void Channel1::setNr3(unsigned data) { + dutyUnit_.nr3Change(data, cycleCounter_); setEvent(); } -void Channel1::setNr4(const unsigned data) { - lengthCounter.nr4Change(nr4, data, cycleCounter); +void Channel1::setNr4(unsigned const data) { + lengthCounter_.nr4Change(nr4_, data, cycleCounter_); + nr4_ = data; + dutyUnit_.nr4Change(data, cycleCounter_, master_); - nr4 = data; - - dutyUnit.nr4Change(data, cycleCounter); - - if (data & 0x80) { //init-bit - nr4 &= 0x7F; - master = !envelopeUnit.nr4Init(cycleCounter); - sweepUnit.nr4Init(cycleCounter); - staticOutputTest(cycleCounter); + if (data & 0x80) { // init-bit + nr4_ &= 0x7F; + master_ = !envelopeUnit_.nr4Init(cycleCounter_); + sweepUnit_.nr4Init(cycleCounter_); + staticOutputTest_(cycleCounter_); } setEvent(); } -void Channel1::setSo(const unsigned long soMask) { - this->soMask = soMask; - staticOutputTest(cycleCounter); +void Channel1::setSo(unsigned long soMask) { + soMask_ = soMask; + staticOutputTest_(cycleCounter_); setEvent(); } void Channel1::reset() { - cycleCounter = 0x1000 | (cycleCounter & 0xFFF); // cycleCounter >> 12 & 7 represents the frame sequencer position. - -// lengthCounter.reset(); - dutyUnit.reset(); - envelopeUnit.reset(); - sweepUnit.reset(); + // cycleCounter >> 12 & 7 represents the frame sequencer position. + cycleCounter_ &= 0xFFF; + cycleCounter_ += ~(cycleCounter_ + 2) << 1 & 0x1000; + dutyUnit_.reset(); + envelopeUnit_.reset(); + sweepUnit_.reset(); setEvent(); } -void Channel1::init(const bool cgb) { - lengthCounter.init(cgb); +void Channel1::init(bool cgb) { + sweepUnit_.init(cgb); } -void Channel1::loadState(const SaveState &state) { - sweepUnit.loadState(state); - dutyUnit.loadState(state.spu.ch1.duty, state.mem.ioamhram.get()[0x111], state.spu.ch1.nr4, state.spu.cycleCounter); - envelopeUnit.loadState(state.spu.ch1.env, state.mem.ioamhram.get()[0x112], state.spu.cycleCounter); - lengthCounter.loadState(state.spu.ch1.lcounter, state.spu.cycleCounter); +void Channel1::loadState(SaveState const &state) { + sweepUnit_.loadState(state); + dutyUnit_.loadState(state.spu.ch1.duty, state.mem.ioamhram.get()[0x111], + state.spu.ch1.nr4, state.spu.cycleCounter); + envelopeUnit_.loadState(state.spu.ch1.env, state.mem.ioamhram.get()[0x112], + state.spu.cycleCounter); + lengthCounter_.loadState(state.spu.ch1.lcounter, state.spu.cycleCounter); - cycleCounter = state.spu.cycleCounter; - nr4 = state.spu.ch1.nr4; - master = state.spu.ch1.master; + cycleCounter_ = state.spu.cycleCounter; + nr4_ = state.spu.ch1.nr4; + master_ = state.spu.ch1.master; } -void Channel1::update(uint_least32_t *buf, const unsigned long soBaseVol, unsigned long cycles) { - const unsigned long outBase = envelopeUnit.dacIsOn() ? soBaseVol & soMask : 0; - const unsigned long outLow = outBase * (0 - 15ul); - const unsigned long endCycles = cycleCounter + cycles; +void Channel1::update(uint_least32_t *buf, unsigned long const soBaseVol, unsigned long cycles) { + unsigned long const outBase = envelopeUnit_.dacIsOn() ? soBaseVol & soMask_ : 0; + unsigned long const outLow = outBase * (0 - 15ul); + unsigned long const endCycles = cycleCounter_ + cycles; for (;;) { - const unsigned long outHigh = master ? outBase * (envelopeUnit.getVolume() * 2 - 15ul) : outLow; - const unsigned long nextMajorEvent = nextEventUnit->getCounter() < endCycles ? nextEventUnit->getCounter() : endCycles; - unsigned long out = dutyUnit.isHighState() ? outHigh : outLow; + unsigned long const outHigh = master_ + ? outBase * (envelopeUnit_.getVolume() * 2 - 15ul) + : outLow; + unsigned long const nextMajorEvent = std::min(nextEventUnit_->counter(), endCycles); + unsigned long out = dutyUnit_.isHighState() ? outHigh : outLow; - while (dutyUnit.getCounter() <= nextMajorEvent) { - *buf = out - prevOut; - prevOut = out; - buf += dutyUnit.getCounter() - cycleCounter; - cycleCounter = dutyUnit.getCounter(); + while (dutyUnit_.counter() <= nextMajorEvent) { + *buf = out - prevOut_; + prevOut_ = out; + buf += dutyUnit_.counter() - cycleCounter_; + cycleCounter_ = dutyUnit_.counter(); - dutyUnit.event(); - out = dutyUnit.isHighState() ? outHigh : outLow; + dutyUnit_.event(); + out = dutyUnit_.isHighState() ? outHigh : outLow; } - if (cycleCounter < nextMajorEvent) { - *buf = out - prevOut; - prevOut = out; - buf += nextMajorEvent - cycleCounter; - cycleCounter = nextMajorEvent; + if (cycleCounter_ < nextMajorEvent) { + *buf = out - prevOut_; + prevOut_ = out; + buf += nextMajorEvent - cycleCounter_; + cycleCounter_ = nextMajorEvent; } - if (nextEventUnit->getCounter() == nextMajorEvent) { - nextEventUnit->event(); + if (nextEventUnit_->counter() == nextMajorEvent) { + nextEventUnit_->event(); setEvent(); } else break; } - if (cycleCounter & SoundUnit::COUNTER_MAX) { - dutyUnit.resetCounters(cycleCounter); - lengthCounter.resetCounters(cycleCounter); - envelopeUnit.resetCounters(cycleCounter); - sweepUnit.resetCounters(cycleCounter); - - cycleCounter -= SoundUnit::COUNTER_MAX; + if (cycleCounter_ >= SoundUnit::counter_max) { + dutyUnit_.resetCounters(cycleCounter_); + lengthCounter_.resetCounters(cycleCounter_); + envelopeUnit_.resetCounters(cycleCounter_); + sweepUnit_.resetCounters(cycleCounter_); + cycleCounter_ -= SoundUnit::counter_max; } } SYNCFUNC(Channel1) { - SSS(lengthCounter); - SSS(dutyUnit); - SSS(envelopeUnit); - SSS(sweepUnit); + SSS(lengthCounter_); + SSS(dutyUnit_); + SSS(envelopeUnit_); + SSS(sweepUnit_); - EBS(nextEventUnit, 0); - EVS(nextEventUnit, &dutyUnit, 1); - EVS(nextEventUnit, &sweepUnit, 2); - EVS(nextEventUnit, &envelopeUnit, 3); - EVS(nextEventUnit, &lengthCounter, 4); - EES(nextEventUnit, NULL); + EBS(nextEventUnit_, 0); + EVS(nextEventUnit_, &dutyUnit_, 1); + EVS(nextEventUnit_, &sweepUnit_, 2); + EVS(nextEventUnit_, &envelopeUnit_, 3); + EVS(nextEventUnit_, &lengthCounter_, 4); + EES(nextEventUnit_, NULL); - NSS(cycleCounter); - NSS(soMask); - NSS(prevOut); + NSS(cycleCounter_); + NSS(soMask_); + NSS(prevOut_); - NSS(nr4); - NSS(master); + NSS(nr4_); + NSS(master_); } } diff --git a/libgambatte/src/sound/channel1.h b/libgambatte/src/sound/channel1.h index 113629bb8a..36c62e646b 100644 --- a/libgambatte/src/sound/channel1.h +++ b/libgambatte/src/sound/channel1.h @@ -1,29 +1,29 @@ -/*************************************************************************** - * Copyright (C) 2007 by Sindre AamÃ¥s * - * aamas@stud.ntnu.no * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License version 2 as * - * published by the Free Software Foundation. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License version 2 for more details. * - * * - * You should have received a copy of the GNU General Public License * - * version 2 along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ +// +// Copyright (C) 2007 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + #ifndef SOUND_CHANNEL1_H #define SOUND_CHANNEL1_H -#include "gbint.h" -#include "master_disabler.h" -#include "length_counter.h" #include "duty_unit.h" #include "envelope_unit.h" +#include "gbint.h" +#include "length_counter.h" +#include "master_disabler.h" #include "static_output_tester.h" #include "newstate.h" @@ -32,46 +32,6 @@ namespace gambatte { struct SaveState; class Channel1 { - class SweepUnit : public SoundUnit { - MasterDisabler &disableMaster; - DutyUnit &dutyUnit; - unsigned short shadow; - unsigned char nr0; - bool negging; - - unsigned calcFreq(); - - public: - SweepUnit(MasterDisabler &disabler, DutyUnit &dutyUnit); - void event(); - void nr0Change(unsigned newNr0); - void nr4Init(unsigned long cycleCounter); - void reset(); - void loadState(const SaveState &state); - - templatevoid SyncState(NewState *ns); - }; - - friend class StaticOutputTester; - - StaticOutputTester staticOutputTest; - DutyMasterDisabler disableMaster; - LengthCounter lengthCounter; - DutyUnit dutyUnit; - EnvelopeUnit envelopeUnit; - SweepUnit sweepUnit; - - SoundUnit *nextEventUnit; - - unsigned long cycleCounter; - unsigned long soMask; - unsigned long prevOut; - - unsigned char nr4; - bool master; - - void setEvent(); - public: Channel1(); void setNr0(unsigned data); @@ -79,16 +39,56 @@ public: void setNr2(unsigned data); void setNr3(unsigned data); void setNr4(unsigned data); - void setSo(unsigned long soMask); - bool isActive() const { return master; } - + bool isActive() const { return master_; } void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles); - void reset(); void init(bool cgb); - void loadState(const SaveState &state); + void loadState(SaveState const &state); +private: + class SweepUnit : public SoundUnit { + public: + SweepUnit(MasterDisabler &disabler, DutyUnit &dutyUnit); + virtual void event(); + void nr0Change(unsigned newNr0); + void nr4Init(unsigned long cycleCounter); + void reset(); + void init(bool cgb) { cgb_ = cgb; } + void loadState(SaveState const &state); + + private: + MasterDisabler &disableMaster_; + DutyUnit &dutyUnit_; + unsigned short shadow_; + unsigned char nr0_; + bool negging_; + bool cgb_; + + unsigned calcFreq(); + + public: + templatevoid SyncState(NewState *ns); + }; + + friend class StaticOutputTester; + + StaticOutputTester staticOutputTest_; + DutyMasterDisabler disableMaster_; + LengthCounter lengthCounter_; + DutyUnit dutyUnit_; + EnvelopeUnit envelopeUnit_; + SweepUnit sweepUnit_; + SoundUnit *nextEventUnit_; + unsigned long cycleCounter_; + unsigned long soMask_; + unsigned long prevOut_; + unsigned char nr4_; + bool master_; + + void setEvent(); + +public: templatevoid SyncState(NewState *ns); }; diff --git a/libgambatte/src/sound/channel2.cpp b/libgambatte/src/sound/channel2.cpp index 13bcc23b4a..6f32e4dd46 100644 --- a/libgambatte/src/sound/channel2.cpp +++ b/libgambatte/src/sound/channel2.cpp @@ -1,176 +1,171 @@ -/*************************************************************************** - * Copyright (C) 2007 by Sindre AamÃ¥s * - * aamas@stud.ntnu.no * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License version 2 as * - * published by the Free Software Foundation. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License version 2 for more details. * - * * - * You should have received a copy of the GNU General Public License * - * version 2 along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ +// +// Copyright (C) 2007 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + #include "channel2.h" #include "../savestate.h" +#include namespace gambatte { -Channel2::Channel2() : - staticOutputTest(*this, dutyUnit), - disableMaster(master, dutyUnit), - lengthCounter(disableMaster, 0x3F), - envelopeUnit(staticOutputTest), - cycleCounter(0), - soMask(0), - prevOut(0), - nr4(0), - master(false) +Channel2::Channel2() +: staticOutputTest_(*this, dutyUnit_) +, disableMaster_(master_, dutyUnit_) +, lengthCounter_(disableMaster_, 0x3F) +, envelopeUnit_(staticOutputTest_) +, cycleCounter_(0) +, soMask_(0) +, prevOut_(0) +, nr4_(0) +, master_(false) { setEvent(); } void Channel2::setEvent() { -// nextEventUnit = &dutyUnit; -// if (envelopeUnit.getCounter() < nextEventUnit->getCounter()) - nextEventUnit = &envelopeUnit; - if (lengthCounter.getCounter() < nextEventUnit->getCounter()) - nextEventUnit = &lengthCounter; + nextEventUnit = &envelopeUnit_; + if (lengthCounter_.counter() < nextEventUnit->counter()) + nextEventUnit = &lengthCounter_; } -void Channel2::setNr1(const unsigned data) { - lengthCounter.nr1Change(data, nr4, cycleCounter); - dutyUnit.nr1Change(data, cycleCounter); - +void Channel2::setNr1(unsigned data) { + lengthCounter_.nr1Change(data, nr4_, cycleCounter_); + dutyUnit_.nr1Change(data, cycleCounter_); setEvent(); } -void Channel2::setNr2(const unsigned data) { - if (envelopeUnit.nr2Change(data)) - disableMaster(); +void Channel2::setNr2(unsigned data) { + if (envelopeUnit_.nr2Change(data)) + disableMaster_(); else - staticOutputTest(cycleCounter); + staticOutputTest_(cycleCounter_); setEvent(); } -void Channel2::setNr3(const unsigned data) { - dutyUnit.nr3Change(data, cycleCounter); +void Channel2::setNr3(unsigned data) { + dutyUnit_.nr3Change(data, cycleCounter_); setEvent(); } -void Channel2::setNr4(const unsigned data) { - lengthCounter.nr4Change(nr4, data, cycleCounter); +void Channel2::setNr4(unsigned const data) { + lengthCounter_.nr4Change(nr4_, data, cycleCounter_); + nr4_ = data; + dutyUnit_.nr4Change(data, cycleCounter_, master_); - nr4 = data; - - if (data & 0x80) { //init-bit - nr4 &= 0x7F; - master = !envelopeUnit.nr4Init(cycleCounter); - staticOutputTest(cycleCounter); + if (data & 0x80) { // init-bit + nr4_ &= 0x7F; + master_ = !envelopeUnit_.nr4Init(cycleCounter_); + staticOutputTest_(cycleCounter_); } - dutyUnit.nr4Change(data, cycleCounter); - setEvent(); } -void Channel2::setSo(const unsigned long soMask) { - this->soMask = soMask; - staticOutputTest(cycleCounter); +void Channel2::setSo(unsigned long soMask) { + soMask_ = soMask; + staticOutputTest_(cycleCounter_); setEvent(); } void Channel2::reset() { - cycleCounter = 0x1000 | (cycleCounter & 0xFFF); // cycleCounter >> 12 & 7 represents the frame sequencer position. - -// lengthCounter.reset(); - dutyUnit.reset(); - envelopeUnit.reset(); + // cycleCounter >> 12 & 7 represents the frame sequencer position. + cycleCounter_ &= 0xFFF; + cycleCounter_ += ~(cycleCounter_ + 2) << 1 & 0x1000; + dutyUnit_.reset(); + envelopeUnit_.reset(); setEvent(); } -void Channel2::init(const bool cgb) { - lengthCounter.init(cgb); +void Channel2::loadState(SaveState const &state) { + dutyUnit_.loadState(state.spu.ch2.duty, state.mem.ioamhram.get()[0x116], + state.spu.ch2.nr4, state.spu.cycleCounter); + envelopeUnit_.loadState(state.spu.ch2.env, state.mem.ioamhram.get()[0x117], + state.spu.cycleCounter); + lengthCounter_.loadState(state.spu.ch2.lcounter, state.spu.cycleCounter); + + cycleCounter_ = state.spu.cycleCounter; + nr4_ = state.spu.ch2.nr4; + master_ = state.spu.ch2.master; } -void Channel2::loadState(const SaveState &state) { - dutyUnit.loadState(state.spu.ch2.duty, state.mem.ioamhram.get()[0x116], state.spu.ch2.nr4,state.spu.cycleCounter); - envelopeUnit.loadState(state.spu.ch2.env, state.mem.ioamhram.get()[0x117], state.spu.cycleCounter); - lengthCounter.loadState(state.spu.ch2.lcounter, state.spu.cycleCounter); - - cycleCounter = state.spu.cycleCounter; - nr4 = state.spu.ch2.nr4; - master = state.spu.ch2.master; -} - -void Channel2::update(uint_least32_t *buf, const unsigned long soBaseVol, unsigned long cycles) { - const unsigned long outBase = envelopeUnit.dacIsOn() ? soBaseVol & soMask : 0; - const unsigned long outLow = outBase * (0 - 15ul); - const unsigned long endCycles = cycleCounter + cycles; +void Channel2::update(uint_least32_t *buf, unsigned long const soBaseVol, unsigned long cycles) { + unsigned long const outBase = envelopeUnit_.dacIsOn() ? soBaseVol & soMask_ : 0; + unsigned long const outLow = outBase * (0 - 15ul); + unsigned long const endCycles = cycleCounter_ + cycles; for (;;) { - const unsigned long outHigh = master ? outBase * (envelopeUnit.getVolume() * 2 - 15ul) : outLow; - const unsigned long nextMajorEvent = nextEventUnit->getCounter() < endCycles ? nextEventUnit->getCounter() : endCycles; - unsigned long out = dutyUnit.isHighState() ? outHigh : outLow; + unsigned long const outHigh = master_ + ? outBase * (envelopeUnit_.getVolume() * 2 - 15ul) + : outLow; + unsigned long const nextMajorEvent = std::min(nextEventUnit->counter(), endCycles); + unsigned long out = dutyUnit_.isHighState() ? outHigh : outLow; - while (dutyUnit.getCounter() <= nextMajorEvent) { - *buf += out - prevOut; - prevOut = out; - buf += dutyUnit.getCounter() - cycleCounter; - cycleCounter = dutyUnit.getCounter(); + while (dutyUnit_.counter() <= nextMajorEvent) { + *buf += out - prevOut_; + prevOut_ = out; + buf += dutyUnit_.counter() - cycleCounter_; + cycleCounter_ = dutyUnit_.counter(); - dutyUnit.event(); - out = dutyUnit.isHighState() ? outHigh : outLow; + dutyUnit_.event(); + out = dutyUnit_.isHighState() ? outHigh : outLow; } - if (cycleCounter < nextMajorEvent) { - *buf += out - prevOut; - prevOut = out; - buf += nextMajorEvent - cycleCounter; - cycleCounter = nextMajorEvent; + if (cycleCounter_ < nextMajorEvent) { + *buf += out - prevOut_; + prevOut_ = out; + buf += nextMajorEvent - cycleCounter_; + cycleCounter_ = nextMajorEvent; } - if (nextEventUnit->getCounter() == nextMajorEvent) { + if (nextEventUnit->counter() == nextMajorEvent) { nextEventUnit->event(); setEvent(); } else break; } - if (cycleCounter & SoundUnit::COUNTER_MAX) { - dutyUnit.resetCounters(cycleCounter); - lengthCounter.resetCounters(cycleCounter); - envelopeUnit.resetCounters(cycleCounter); - - cycleCounter -= SoundUnit::COUNTER_MAX; + if (cycleCounter_ >= SoundUnit::counter_max) { + dutyUnit_.resetCounters(cycleCounter_); + lengthCounter_.resetCounters(cycleCounter_); + envelopeUnit_.resetCounters(cycleCounter_); + cycleCounter_ -= SoundUnit::counter_max; } } SYNCFUNC(Channel2) { - SSS(lengthCounter); - SSS(dutyUnit); - SSS(envelopeUnit); + SSS(lengthCounter_); + SSS(dutyUnit_); + SSS(envelopeUnit_); EBS(nextEventUnit, 0); - EVS(nextEventUnit, &dutyUnit, 1); - EVS(nextEventUnit, &envelopeUnit, 2); - EVS(nextEventUnit, &lengthCounter, 3); + EVS(nextEventUnit, &dutyUnit_, 1); + EVS(nextEventUnit, &envelopeUnit_, 2); + EVS(nextEventUnit, &lengthCounter_, 3); EES(nextEventUnit, NULL); - NSS(cycleCounter); - NSS(soMask); - NSS(prevOut); + NSS(cycleCounter_); + NSS(soMask_); + NSS(prevOut_); - NSS(nr4); - NSS(master); + NSS(nr4_); + NSS(master_); } } diff --git a/libgambatte/src/sound/channel2.h b/libgambatte/src/sound/channel2.h index 611d8cd51c..8934cc7991 100644 --- a/libgambatte/src/sound/channel2.h +++ b/libgambatte/src/sound/channel2.h @@ -1,28 +1,28 @@ -/*************************************************************************** - * Copyright (C) 2007 by Sindre AamÃ¥s * - * aamas@stud.ntnu.no * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License version 2 as * - * published by the Free Software Foundation. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License version 2 for more details. * - * * - * You should have received a copy of the GNU General Public License * - * version 2 along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ +// +// Copyright (C) 2007 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + #ifndef SOUND_CHANNEL2_H #define SOUND_CHANNEL2_H -#include "gbint.h" -#include "length_counter.h" #include "duty_unit.h" #include "envelope_unit.h" +#include "gbint.h" +#include "length_counter.h" #include "static_output_tester.h" #include "newstate.h" @@ -31,42 +31,36 @@ namespace gambatte { struct SaveState; class Channel2 { - friend class StaticOutputTester; - - StaticOutputTester staticOutputTest; - DutyMasterDisabler disableMaster; - LengthCounter lengthCounter; - DutyUnit dutyUnit; - EnvelopeUnit envelopeUnit; - - SoundUnit *nextEventUnit; - - unsigned long cycleCounter; - unsigned long soMask; - unsigned long prevOut; - - unsigned char nr4; - bool master; - - void setEvent(); - public: Channel2(); void setNr1(unsigned data); void setNr2(unsigned data); void setNr3(unsigned data); void setNr4(unsigned data); - void setSo(unsigned long soMask); - // void deactivate() { disableMaster(); setEvent(); } - bool isActive() const { return master; } - + bool isActive() const { return master_; } void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles); - void reset(); - void init(bool cgb); - void loadState(const SaveState &state); + void loadState(SaveState const &state); +private: + friend class StaticOutputTester; + + StaticOutputTester staticOutputTest_; + DutyMasterDisabler disableMaster_; + LengthCounter lengthCounter_; + DutyUnit dutyUnit_; + EnvelopeUnit envelopeUnit_; + SoundUnit *nextEventUnit; + unsigned long cycleCounter_; + unsigned long soMask_; + unsigned long prevOut_; + unsigned char nr4_; + bool master_; + + void setEvent(); + +public: templatevoid SyncState(NewState *ns); }; diff --git a/libgambatte/src/sound/channel3.cpp b/libgambatte/src/sound/channel3.cpp index d91be4416b..06fe6dc2fd 100644 --- a/libgambatte/src/sound/channel3.cpp +++ b/libgambatte/src/sound/channel3.cpp @@ -1,25 +1,25 @@ -/*************************************************************************** - * Copyright (C) 2007 by Sindre AamÃ¥s * - * aamas@stud.ntnu.no * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License version 2 as * - * published by the Free Software Foundation. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License version 2 for more details. * - * * - * You should have received a copy of the GNU General Public License * - * version 2 along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ +// +// Copyright (C) 2007 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + #include "channel3.h" #include "../savestate.h" -#include #include +#include static inline unsigned toPeriod(const unsigned nr3, const unsigned nr4) { return 0x800 - ((nr4 << 8 & 0x700) | nr3); @@ -27,193 +27,196 @@ static inline unsigned toPeriod(const unsigned nr3, const unsigned nr4) { namespace gambatte { -Channel3::Channel3() : - disableMaster(master, waveCounter), - lengthCounter(disableMaster, 0xFF), - cycleCounter(0), - soMask(0), - prevOut(0), - waveCounter(SoundUnit::COUNTER_DISABLED), - lastReadTime(0), - nr0(0), - nr3(0), - nr4(0), - wavePos(0), - rShift(4), - sampleBuf(0), - master(false), - cgb(false) -{} +Channel3::Channel3() +: disableMaster_(master_, waveCounter_) +, lengthCounter_(disableMaster_, 0xFF) +, cycleCounter_(0) +, soMask_(0) +, prevOut_(0) +, waveCounter_(SoundUnit::counter_disabled) +, lastReadTime_(0) +, nr0_(0) +, nr3_(0) +, nr4_(0) +, wavePos_(0) +, rshift_(4) +, sampleBuf_(0) +, master_(false) +, cgb_(false) +{ +} -void Channel3::setNr0(const unsigned data) { - nr0 = data & 0x80; +void Channel3::setNr0(unsigned data) { + nr0_ = data & 0x80; if (!(data & 0x80)) - disableMaster(); + disableMaster_(); } -void Channel3::setNr2(const unsigned data) { - rShift = (data >> 5 & 3U) - 1; - - if (rShift > 3) - rShift = 4; +void Channel3::setNr2(unsigned data) { + rshift_ = (data >> 5 & 3U) - 1; + if (rshift_ > 3) + rshift_ = 4; } -void Channel3::setNr4(const unsigned data) { - lengthCounter.nr4Change(nr4, data, cycleCounter); +void Channel3::setNr4(unsigned const data) { + lengthCounter_.nr4Change(nr4_, data, cycleCounter_); + nr4_ = data & 0x7F; - nr4 = data & 0x7F; - - if (data & nr0/* & 0x80*/) { - if (!cgb && waveCounter == cycleCounter + 1) { - const unsigned pos = ((wavePos + 1) & 0x1F) >> 1; + if (data & nr0_/* & 0x80*/) { + if (!cgb_ && waveCounter_ == cycleCounter_ + 1) { + unsigned const pos = ((wavePos_ + 1) & 0x1F) >> 1; if (pos < 4) - waveRam[0] = waveRam[pos]; + waveRam_[0] = waveRam_[pos]; else - std::memcpy(waveRam, waveRam + (pos & ~3), 4); + std::memcpy(waveRam_, waveRam_ + (pos & ~3), 4); } - master = true; - wavePos = 0; - lastReadTime = waveCounter = cycleCounter + toPeriod(nr3, data) + 3; + master_ = true; + wavePos_ = 0; + lastReadTime_ = waveCounter_ = cycleCounter_ + toPeriod(nr3_, data) + 3; } } -void Channel3::setSo(const unsigned long soMask) { - this->soMask = soMask; +void Channel3::setSo(unsigned long soMask) { + soMask_ = soMask; } void Channel3::reset() { - cycleCounter = 0x1000 | (cycleCounter & 0xFFF); // cycleCounter >> 12 & 7 represents the frame sequencer position. + // cycleCounter >> 12 & 7 represents the frame sequencer position. + cycleCounter_ &= 0xFFF; + cycleCounter_ += ~(cycleCounter_ + 2) << 1 & 0x1000; -// lengthCounter.reset(); - sampleBuf = 0; + sampleBuf_ = 0; } -void Channel3::init(const bool cgb) { - this->cgb = cgb; - lengthCounter.init(cgb); +void Channel3::init(bool cgb) { + cgb_ = cgb; } void Channel3::setStatePtrs(SaveState &state) { - state.spu.ch3.waveRam.set(waveRam, sizeof waveRam); + state.spu.ch3.waveRam.set(waveRam_, sizeof waveRam_); } -void Channel3::loadState(const SaveState &state) { - lengthCounter.loadState(state.spu.ch3.lcounter, state.spu.cycleCounter); +void Channel3::loadState(SaveState const &state) { + lengthCounter_.loadState(state.spu.ch3.lcounter, state.spu.cycleCounter); - cycleCounter = state.spu.cycleCounter; - waveCounter = std::max(state.spu.ch3.waveCounter, state.spu.cycleCounter); - lastReadTime = state.spu.ch3.lastReadTime; - nr3 = state.spu.ch3.nr3; - nr4 = state.spu.ch3.nr4; - wavePos = state.spu.ch3.wavePos & 0x1F; - sampleBuf = state.spu.ch3.sampleBuf; - master = state.spu.ch3.master; + cycleCounter_ = state.spu.cycleCounter; + waveCounter_ = std::max(state.spu.ch3.waveCounter, state.spu.cycleCounter); + lastReadTime_ = state.spu.ch3.lastReadTime; + nr3_ = state.spu.ch3.nr3; + nr4_ = state.spu.ch3.nr4; + wavePos_ = state.spu.ch3.wavePos & 0x1F; + sampleBuf_ = state.spu.ch3.sampleBuf; + master_ = state.spu.ch3.master; - nr0 = state.mem.ioamhram.get()[0x11A] & 0x80; + nr0_ = state.mem.ioamhram.get()[0x11A] & 0x80; setNr2(state.mem.ioamhram.get()[0x11C]); } -void Channel3::updateWaveCounter(const unsigned long cc) { - if (cc >= waveCounter) { - const unsigned period = toPeriod(nr3, nr4); - const unsigned long periods = (cc - waveCounter) / period; +void Channel3::updateWaveCounter(unsigned long const cc) { + if (cc >= waveCounter_) { + unsigned const period = toPeriod(nr3_, nr4_); + unsigned long const periods = (cc - waveCounter_) / period; - lastReadTime = waveCounter + periods * period; - waveCounter = lastReadTime + period; + lastReadTime_ = waveCounter_ + periods * period; + waveCounter_ = lastReadTime_ + period; - wavePos += periods + 1; - wavePos &= 0x1F; + wavePos_ += periods + 1; + wavePos_ &= 0x1F; - sampleBuf = waveRam[wavePos >> 1]; + sampleBuf_ = waveRam_[wavePos_ >> 1]; } } -void Channel3::update(uint_least32_t *buf, const unsigned long soBaseVol, unsigned long cycles) { - const unsigned long outBase = (nr0/* & 0x80*/) ? soBaseVol & soMask : 0; +void Channel3::update(uint_least32_t *buf, unsigned long const soBaseVol, unsigned long cycles) { + unsigned long const outBase = nr0_/* & 0x80*/ ? soBaseVol & soMask_ : 0; - if (outBase && rShift != 4) { - const unsigned long endCycles = cycleCounter + cycles; + if (outBase && rshift_ != 4) { + unsigned long const endCycles = cycleCounter_ + cycles; for (;;) { - const unsigned long nextMajorEvent = lengthCounter.getCounter() < endCycles ? lengthCounter.getCounter() : endCycles; - unsigned long out = outBase * (master ? ((sampleBuf >> (~wavePos << 2 & 4) & 0xF) >> rShift) * 2 - 15ul : 0 - 15ul); + unsigned long const nextMajorEvent = + std::min(lengthCounter_.counter(), endCycles); + unsigned long out = master_ + ? ((sampleBuf_ >> (~wavePos_ << 2 & 4) & 0xF) >> rshift_) * 2 - 15ul + : 0 - 15ul; + out *= outBase; - while (waveCounter <= nextMajorEvent) { - *buf += out - prevOut; - prevOut = out; - buf += waveCounter - cycleCounter; - cycleCounter = waveCounter; + while (waveCounter_ <= nextMajorEvent) { + *buf += out - prevOut_; + prevOut_ = out; + buf += waveCounter_ - cycleCounter_; + cycleCounter_ = waveCounter_; - lastReadTime = waveCounter; - waveCounter += toPeriod(nr3, nr4); - ++wavePos; - wavePos &= 0x1F; - sampleBuf = waveRam[wavePos >> 1]; - out = outBase * (/*master ? */((sampleBuf >> (~wavePos << 2 & 4) & 0xF) >> rShift) * 2 - 15ul/* : 0 - 15ul*/); + lastReadTime_ = waveCounter_; + waveCounter_ += toPeriod(nr3_, nr4_); + ++wavePos_; + wavePos_ &= 0x1F; + sampleBuf_ = waveRam_[wavePos_ >> 1]; + out = ((sampleBuf_ >> (~wavePos_ << 2 & 4) & 0xF) >> rshift_) * 2 - 15ul; + out *= outBase; } - if (cycleCounter < nextMajorEvent) { - *buf += out - prevOut; - prevOut = out; - buf += nextMajorEvent - cycleCounter; - cycleCounter = nextMajorEvent; + if (cycleCounter_ < nextMajorEvent) { + *buf += out - prevOut_; + prevOut_ = out; + buf += nextMajorEvent - cycleCounter_; + cycleCounter_ = nextMajorEvent; } - if (lengthCounter.getCounter() == nextMajorEvent) { - lengthCounter.event(); + if (lengthCounter_.counter() == nextMajorEvent) { + lengthCounter_.event(); } else break; } } else { - const unsigned long out = outBase * (0 - 15ul); - *buf += out - prevOut; - prevOut = out; + unsigned long const out = outBase * (0 - 15ul); + *buf += out - prevOut_; + prevOut_ = out; + cycleCounter_ += cycles; - cycleCounter += cycles; - - while (lengthCounter.getCounter() <= cycleCounter) { - updateWaveCounter(lengthCounter.getCounter()); - lengthCounter.event(); + while (lengthCounter_.counter() <= cycleCounter_) { + updateWaveCounter(lengthCounter_.counter()); + lengthCounter_.event(); } - updateWaveCounter(cycleCounter); + updateWaveCounter(cycleCounter_); } - if (cycleCounter & SoundUnit::COUNTER_MAX) { - lengthCounter.resetCounters(cycleCounter); + if (cycleCounter_ >= SoundUnit::counter_max) { + lengthCounter_.resetCounters(cycleCounter_); - if (waveCounter != SoundUnit::COUNTER_DISABLED) - waveCounter -= SoundUnit::COUNTER_MAX; + if (waveCounter_ != SoundUnit::counter_disabled) + waveCounter_ -= SoundUnit::counter_max; - lastReadTime -= SoundUnit::COUNTER_MAX; - cycleCounter -= SoundUnit::COUNTER_MAX; + lastReadTime_ -= SoundUnit::counter_max; + cycleCounter_ -= SoundUnit::counter_max; } } SYNCFUNC(Channel3) { - NSS(waveRam); + NSS(waveRam_); - SSS(lengthCounter); + SSS(lengthCounter_); - NSS(cycleCounter); - NSS(soMask); - NSS(prevOut); - NSS(waveCounter); - NSS(lastReadTime); + NSS(cycleCounter_); + NSS(soMask_); + NSS(prevOut_); + NSS(waveCounter_); + NSS(lastReadTime_); - NSS(nr0); - NSS(nr3); - NSS(nr4); - NSS(wavePos); - NSS(rShift); - NSS(sampleBuf); + NSS(nr0_); + NSS(nr3_); + NSS(nr4_); + NSS(wavePos_); + NSS(rshift_); + NSS(sampleBuf_); - NSS(master); - NSS(cgb); + NSS(master_); + NSS(cgb_); } } diff --git a/libgambatte/src/sound/channel3.h b/libgambatte/src/sound/channel3.h index a6c5253c44..fe2e43fd1b 100644 --- a/libgambatte/src/sound/channel3.h +++ b/libgambatte/src/sound/channel3.h @@ -1,27 +1,27 @@ -/*************************************************************************** - * Copyright (C) 2007 by Sindre AamÃ¥s * - * aamas@stud.ntnu.no * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License version 2 as * - * published by the Free Software Foundation. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License version 2 for more details. * - * * - * You should have received a copy of the GNU General Public License * - * version 2 along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ +// +// Copyright (C) 2007 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + #ifndef SOUND_CHANNEL3_H #define SOUND_CHANNEL3_H #include "gbint.h" -#include "master_disabler.h" #include "length_counter.h" +#include "master_disabler.h" #include "newstate.h" namespace gambatte { @@ -29,74 +29,78 @@ namespace gambatte { struct SaveState; class Channel3 { - class Ch3MasterDisabler : public MasterDisabler { - unsigned long &waveCounter; - - public: - Ch3MasterDisabler(bool &m, unsigned long &wC) : MasterDisabler(m), waveCounter(wC) {} - void operator()() { MasterDisabler::operator()(); waveCounter = SoundUnit::COUNTER_DISABLED; } - }; - - unsigned char waveRam[0x10]; - - Ch3MasterDisabler disableMaster; - LengthCounter lengthCounter; - - unsigned long cycleCounter; - unsigned long soMask; - unsigned long prevOut; - unsigned long waveCounter; - unsigned long lastReadTime; - - unsigned char nr0; - unsigned char nr3; - unsigned char nr4; - unsigned char wavePos; - unsigned char rShift; - unsigned char sampleBuf; - - bool master; - bool cgb; - - void updateWaveCounter(unsigned long cc); - public: Channel3(); - bool isActive() const { return master; } + bool isActive() const { return master_; } void reset(); void init(bool cgb); void setStatePtrs(SaveState &state); void loadState(const SaveState &state); void setNr0(unsigned data); - void setNr1(unsigned data) { lengthCounter.nr1Change(data, nr4, cycleCounter); } + void setNr1(unsigned data) { lengthCounter_.nr1Change(data, nr4_, cycleCounter_); } void setNr2(unsigned data); - void setNr3(unsigned data) { nr3 = data; } + void setNr3(unsigned data) { nr3_ = data; } void setNr4(unsigned data); void setSo(unsigned long soMask); void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles); unsigned waveRamRead(unsigned index) const { - if (master) { - if (!cgb && cycleCounter != lastReadTime) + if (master_) { + if (!cgb_ && cycleCounter_ != lastReadTime_) return 0xFF; - index = wavePos >> 1; + index = wavePos_ >> 1; } - return waveRam[index]; + return waveRam_[index]; } void waveRamWrite(unsigned index, unsigned data) { - if (master) { - if (!cgb && cycleCounter != lastReadTime) + if (master_) { + if (!cgb_ && cycleCounter_ != lastReadTime_) return; - index = wavePos >> 1; + index = wavePos_ >> 1; } - waveRam[index] = data; + waveRam_[index] = data; } +private: + class Ch3MasterDisabler : public MasterDisabler { + public: + Ch3MasterDisabler(bool &m, unsigned long &wC) : MasterDisabler(m), waveCounter_(wC) {} + + virtual void operator()() { + MasterDisabler::operator()(); + waveCounter_ = SoundUnit::counter_disabled; + } + + private: + unsigned long &waveCounter_; + }; + + unsigned char waveRam_[0x10]; + Ch3MasterDisabler disableMaster_; + LengthCounter lengthCounter_; + unsigned long cycleCounter_; + unsigned long soMask_; + unsigned long prevOut_; + unsigned long waveCounter_; + unsigned long lastReadTime_; + unsigned char nr0_; + unsigned char nr3_; + unsigned char nr4_; + unsigned char wavePos_; + unsigned char rshift_; + unsigned char sampleBuf_; + bool master_; + bool cgb_; + + void updateWaveCounter(unsigned long cc); + + +public: templatevoid SyncState(NewState *ns); }; diff --git a/libgambatte/src/sound/channel4.cpp b/libgambatte/src/sound/channel4.cpp index cc5a29799f..9528ceb943 100644 --- a/libgambatte/src/sound/channel4.cpp +++ b/libgambatte/src/sound/channel4.cpp @@ -1,26 +1,26 @@ -/*************************************************************************** - * Copyright (C) 2007 by Sindre AamÃ¥s * - * aamas@stud.ntnu.no * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License version 2 as * - * published by the Free Software Foundation. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License version 2 for more details. * - * * - * You should have received a copy of the GNU General Public License * - * version 2 along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ +// +// Copyright (C) 2007 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + #include "channel4.h" #include "../savestate.h" #include -static unsigned long toPeriod(const unsigned nr3) { +static unsigned long toPeriod(unsigned const nr3) { unsigned s = (nr3 >> 4) + 3; unsigned r = nr3 & 7; @@ -34,286 +34,242 @@ static unsigned long toPeriod(const unsigned nr3) { namespace gambatte { -Channel4::Lfsr::Lfsr() : -backupCounter(COUNTER_DISABLED), -reg(0x7FFF), -nr3(0), -master(false) -{} +Channel4::Lfsr::Lfsr() +: backupCounter_(counter_disabled) +, reg_(0x7FFF) +, nr3_(0) +, master_(false) +{ +} -void Channel4::Lfsr::updateBackupCounter(const unsigned long cc) { - /*if (backupCounter <= cc) { - const unsigned long period = toPeriod(nr3); - backupCounter = cc - (cc - backupCounter) % period + period; - }*/ +void Channel4::Lfsr::updateBackupCounter(unsigned long const cc) { + if (backupCounter_ <= cc) { + unsigned long const period = toPeriod(nr3_); + unsigned long periods = (cc - backupCounter_) / period + 1; + backupCounter_ += periods * period; - if (backupCounter <= cc) { - const unsigned long period = toPeriod(nr3); - unsigned long periods = (cc - backupCounter) / period + 1; - - backupCounter += periods * period; - - if (master && nr3 < 0xE0) { - if (nr3 & 8) { + if (master_ && nr3_ < 0xE0) { + if (nr3_ & 8) { while (periods > 6) { - const unsigned xored = (reg << 1 ^ reg) & 0x7E; - reg = (reg >> 6 & ~0x7E) | xored | xored << 8; + unsigned const xored = (reg_ << 1 ^ reg_) & 0x7E; + reg_ = (reg_ >> 6 & ~0x7E) | xored | xored << 8; periods -= 6; } - const unsigned xored = ((reg ^ reg >> 1) << (7 - periods)) & 0x7F; - reg = (reg >> periods & ~(0x80 - (0x80 >> periods))) | xored | xored << 8; + unsigned const xored = ((reg_ ^ reg_ >> 1) << (7 - periods)) & 0x7F; + reg_ = (reg_ >> periods & ~(0x80 - (0x80 >> periods))) | xored | xored << 8; } else { while (periods > 15) { - reg = reg ^ reg >> 1; + reg_ = reg_ ^ reg_ >> 1; periods -= 15; } - reg = reg >> periods | (((reg ^ reg >> 1) << (15 - periods)) & 0x7FFF); + reg_ = reg_ >> periods | (((reg_ ^ reg_ >> 1) << (15 - periods)) & 0x7FFF); } } } } -void Channel4::Lfsr::reviveCounter(const unsigned long cc) { +void Channel4::Lfsr::reviveCounter(unsigned long cc) { updateBackupCounter(cc); - counter = backupCounter; + counter_ = backupCounter_; } -/*static const unsigned char nextStateDistance[0x40] = { - 6, 1, 1, 2, 2, 1, 1, 3, - 3, 1, 1, 2, 2, 1, 1, 4, - 4, 1, 1, 2, 2, 1, 1, 3, - 3, 1, 1, 2, 2, 1, 1, 5, - 5, 1, 1, 2, 2, 1, 1, 3, - 3, 1, 1, 2, 2, 1, 1, 4, - 4, 1, 1, 2, 2, 1, 1, 3, - 3, 1, 1, 2, 2, 1, 1, 6, -};*/ - inline void Channel4::Lfsr::event() { - if (nr3 < 0xE0) { - const unsigned shifted = reg >> 1; - const unsigned xored = (reg ^ shifted) & 1; + if (nr3_ < 0xE0) { + unsigned const shifted = reg_ >> 1; + unsigned const xored = (reg_ ^ shifted) & 1; + reg_ = shifted | xored << 14; - reg = shifted | xored << 14; - - if (nr3 & 8) - reg = (reg & ~0x40) | xored << 6; + if (nr3_ & 8) + reg_ = (reg_ & ~0x40) | xored << 6; } - counter += toPeriod(nr3); - backupCounter = counter; - - - /*if (nr3 < 0xE0) { - const unsigned periods = nextStateDistance[reg & 0x3F]; - const unsigned xored = ((reg ^ reg >> 1) << (7 - periods)) & 0x7F; - - reg = reg >> periods | xored << 8; - - if (nr3 & 8) - reg = reg & ~(0x80 - (0x80 >> periods)) | xored; - } - - const unsigned long period = toPeriod(nr3); - backupCounter = counter + period; - counter += period * nextStateDistance[reg & 0x3F];*/ + counter_ += toPeriod(nr3_); + backupCounter_ = counter_; } -void Channel4::Lfsr::nr3Change(const unsigned newNr3, const unsigned long cc) { +void Channel4::Lfsr::nr3Change(unsigned newNr3, unsigned long cc) { updateBackupCounter(cc); - nr3 = newNr3; - -// if (counter != COUNTER_DISABLED) -// counter = backupCounter + toPeriod(nr3) * (nextStateDistance[reg & 0x3F] - 1); + nr3_ = newNr3; } void Channel4::Lfsr::nr4Init(unsigned long cc) { disableMaster(); updateBackupCounter(cc); - master = true; - backupCounter += 4; - counter = backupCounter; -// counter = backupCounter + toPeriod(nr3) * (nextStateDistance[reg & 0x3F] - 1); + master_ = true; + backupCounter_ += 4; + counter_ = backupCounter_; } -void Channel4::Lfsr::reset(const unsigned long cc) { - nr3 = 0; +void Channel4::Lfsr::reset(unsigned long cc) { + nr3_ = 0; disableMaster(); - backupCounter = cc + toPeriod(nr3); + backupCounter_ = cc + toPeriod(nr3_); } -void Channel4::Lfsr::resetCounters(const unsigned long oldCc) { +void Channel4::Lfsr::resetCounters(unsigned long oldCc) { updateBackupCounter(oldCc); - backupCounter -= COUNTER_MAX; + backupCounter_ -= counter_max; SoundUnit::resetCounters(oldCc); } -void Channel4::Lfsr::loadState(const SaveState &state) { - counter = backupCounter = std::max(state.spu.ch4.lfsr.counter, state.spu.cycleCounter); - reg = state.spu.ch4.lfsr.reg; - master = state.spu.ch4.master; - nr3 = state.mem.ioamhram.get()[0x122]; +void Channel4::Lfsr::loadState(SaveState const &state) { + counter_ = backupCounter_ = std::max(state.spu.ch4.lfsr.counter, state.spu.cycleCounter); + reg_ = state.spu.ch4.lfsr.reg; + master_ = state.spu.ch4.master; + nr3_ = state.mem.ioamhram.get()[0x122]; } template void Channel4::Lfsr::SyncState(NewState *ns) { - NSS(counter); - NSS(backupCounter); - NSS(reg); - NSS(nr3); - NSS(master); + NSS(counter_); + NSS(backupCounter_); + NSS(reg_); + NSS(nr3_); + NSS(master_); } -Channel4::Channel4() : - staticOutputTest(*this, lfsr), - disableMaster(master, lfsr), - lengthCounter(disableMaster, 0x3F), - envelopeUnit(staticOutputTest), - cycleCounter(0), - soMask(0), - prevOut(0), - nr4(0), - master(false) +Channel4::Channel4() +: staticOutputTest_(*this, lfsr_) +, disableMaster_(master_, lfsr_) +, lengthCounter_(disableMaster_, 0x3F) +, envelopeUnit_(staticOutputTest_) +, nextEventUnit_(0) +, cycleCounter_(0) +, soMask_(0) +, prevOut_(0) +, nr4_(0) +, master_(false) { setEvent(); } void Channel4::setEvent() { -// nextEventUnit = &lfsr; -// if (envelopeUnit.getCounter() < nextEventUnit->getCounter()) - nextEventUnit = &envelopeUnit; - if (lengthCounter.getCounter() < nextEventUnit->getCounter()) - nextEventUnit = &lengthCounter; + nextEventUnit_ = &envelopeUnit_; + if (lengthCounter_.counter() < nextEventUnit_->counter()) + nextEventUnit_ = &lengthCounter_; } -void Channel4::setNr1(const unsigned data) { - lengthCounter.nr1Change(data, nr4, cycleCounter); - +void Channel4::setNr1(unsigned data) { + lengthCounter_.nr1Change(data, nr4_, cycleCounter_); setEvent(); } -void Channel4::setNr2(const unsigned data) { - if (envelopeUnit.nr2Change(data)) - disableMaster(); +void Channel4::setNr2(unsigned data) { + if (envelopeUnit_.nr2Change(data)) + disableMaster_(); else - staticOutputTest(cycleCounter); + staticOutputTest_(cycleCounter_); setEvent(); } -void Channel4::setNr4(const unsigned data) { - lengthCounter.nr4Change(nr4, data, cycleCounter); +void Channel4::setNr4(unsigned const data) { + lengthCounter_.nr4Change(nr4_, data, cycleCounter_); + nr4_ = data; - nr4 = data; + if (data & 0x80) { // init-bit + nr4_ &= 0x7F; + master_ = !envelopeUnit_.nr4Init(cycleCounter_); - if (data & 0x80) { //init-bit - nr4 &= 0x7F; + if (master_) + lfsr_.nr4Init(cycleCounter_); - master = !envelopeUnit.nr4Init(cycleCounter); - - if (master) - lfsr.nr4Init(cycleCounter); - - staticOutputTest(cycleCounter); + staticOutputTest_(cycleCounter_); } setEvent(); } -void Channel4::setSo(const unsigned long soMask) { - this->soMask = soMask; - staticOutputTest(cycleCounter); +void Channel4::setSo(unsigned long soMask) { + soMask_ = soMask; + staticOutputTest_(cycleCounter_); setEvent(); } void Channel4::reset() { - cycleCounter = 0x1000 | (cycleCounter & 0xFFF); // cycleCounter >> 12 & 7 represents the frame sequencer position. - -// lengthCounter.reset(); - lfsr.reset(cycleCounter); - envelopeUnit.reset(); + // cycleCounter >> 12 & 7 represents the frame sequencer position. + cycleCounter_ &= 0xFFF; + cycleCounter_ += ~(cycleCounter_ + 2) << 1 & 0x1000; + lfsr_.reset(cycleCounter_); + envelopeUnit_.reset(); setEvent(); } -void Channel4::init(const bool cgb) { - lengthCounter.init(cgb); +void Channel4::loadState(SaveState const &state) { + lfsr_.loadState(state); + envelopeUnit_.loadState(state.spu.ch4.env, state.mem.ioamhram.get()[0x121], + state.spu.cycleCounter); + lengthCounter_.loadState(state.spu.ch4.lcounter, state.spu.cycleCounter); + + cycleCounter_ = state.spu.cycleCounter; + nr4_ = state.spu.ch4.nr4; + master_ = state.spu.ch4.master; } -void Channel4::loadState(const SaveState &state) { - lfsr.loadState(state); - envelopeUnit.loadState(state.spu.ch4.env, state.mem.ioamhram.get()[0x121], state.spu.cycleCounter); - lengthCounter.loadState(state.spu.ch4.lcounter, state.spu.cycleCounter); - - cycleCounter = state.spu.cycleCounter; - nr4 = state.spu.ch4.nr4; - master = state.spu.ch4.master; -} - -void Channel4::update(uint_least32_t *buf, const unsigned long soBaseVol, unsigned long cycles) { - const unsigned long outBase = envelopeUnit.dacIsOn() ? soBaseVol & soMask : 0; - const unsigned long outLow = outBase * (0 - 15ul); - const unsigned long endCycles = cycleCounter + cycles; +void Channel4::update(uint_least32_t *buf, unsigned long const soBaseVol, unsigned long cycles) { + unsigned long const outBase = envelopeUnit_.dacIsOn() ? soBaseVol & soMask_ : 0; + unsigned long const outLow = outBase * (0 - 15ul); + unsigned long const endCycles = cycleCounter_ + cycles; for (;;) { - const unsigned long outHigh = /*master ? */outBase * (envelopeUnit.getVolume() * 2 - 15ul)/* : outLow*/; - const unsigned long nextMajorEvent = nextEventUnit->getCounter() < endCycles ? nextEventUnit->getCounter() : endCycles; - unsigned long out = lfsr.isHighState() ? outHigh : outLow; + unsigned long const outHigh = outBase * (envelopeUnit_.getVolume() * 2 - 15ul); + unsigned long const nextMajorEvent = std::min(nextEventUnit_->counter(), endCycles); + unsigned long out = lfsr_.isHighState() ? outHigh : outLow; - while (lfsr.getCounter() <= nextMajorEvent) { - *buf += out - prevOut; - prevOut = out; - buf += lfsr.getCounter() - cycleCounter; - cycleCounter = lfsr.getCounter(); + while (lfsr_.counter() <= nextMajorEvent) { + *buf += out - prevOut_; + prevOut_ = out; + buf += lfsr_.counter() - cycleCounter_; + cycleCounter_ = lfsr_.counter(); - lfsr.event(); - out = lfsr.isHighState() ? outHigh : outLow; + lfsr_.event(); + out = lfsr_.isHighState() ? outHigh : outLow; } - if (cycleCounter < nextMajorEvent) { - *buf += out - prevOut; - prevOut = out; - buf += nextMajorEvent - cycleCounter; - cycleCounter = nextMajorEvent; + if (cycleCounter_ < nextMajorEvent) { + *buf += out - prevOut_; + prevOut_ = out; + buf += nextMajorEvent - cycleCounter_; + cycleCounter_ = nextMajorEvent; } - if (nextEventUnit->getCounter() == nextMajorEvent) { - nextEventUnit->event(); + if (nextEventUnit_->counter() == nextMajorEvent) { + nextEventUnit_->event(); setEvent(); } else break; } - if (cycleCounter & SoundUnit::COUNTER_MAX) { - lengthCounter.resetCounters(cycleCounter); - lfsr.resetCounters(cycleCounter); - envelopeUnit.resetCounters(cycleCounter); - - cycleCounter -= SoundUnit::COUNTER_MAX; + if (cycleCounter_ >= SoundUnit::counter_max) { + lengthCounter_.resetCounters(cycleCounter_); + lfsr_.resetCounters(cycleCounter_); + envelopeUnit_.resetCounters(cycleCounter_); + cycleCounter_ -= SoundUnit::counter_max; } } SYNCFUNC(Channel4) { - SSS(lengthCounter); - SSS(envelopeUnit); - SSS(lfsr); + SSS(lengthCounter_); + SSS(envelopeUnit_); + SSS(lfsr_); - EBS(nextEventUnit, 0); - EVS(nextEventUnit, &lfsr, 1); - EVS(nextEventUnit, &envelopeUnit, 2); - EVS(nextEventUnit, &lengthCounter, 3); - EES(nextEventUnit, NULL); + EBS(nextEventUnit_, 0); + EVS(nextEventUnit_, &lfsr_, 1); + EVS(nextEventUnit_, &envelopeUnit_, 2); + EVS(nextEventUnit_, &lengthCounter_, 3); + EES(nextEventUnit_, NULL); - NSS(cycleCounter); - NSS(soMask); - NSS(prevOut); + NSS(cycleCounter_); + NSS(soMask_); + NSS(prevOut_); - NSS(nr4); - NSS(master); + NSS(nr4_); + NSS(master_); } } diff --git a/libgambatte/src/sound/channel4.h b/libgambatte/src/sound/channel4.h index 4490404fa2..0ad159181f 100644 --- a/libgambatte/src/sound/channel4.h +++ b/libgambatte/src/sound/channel4.h @@ -1,28 +1,28 @@ -/*************************************************************************** - * Copyright (C) 2007 by Sindre AamÃ¥s * - * aamas@stud.ntnu.no * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License version 2 as * - * published by the Free Software Foundation. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License version 2 for more details. * - * * - * You should have received a copy of the GNU General Public License * - * version 2 along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ +// +// Copyright (C) 2007 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + #ifndef SOUND_CHANNEL4_H #define SOUND_CHANNEL4_H -#include "gbint.h" -#include "master_disabler.h" -#include "length_counter.h" #include "envelope_unit.h" +#include "gbint.h" +#include "length_counter.h" +#include "master_disabler.h" #include "static_output_tester.h" #include "newstate.h" @@ -31,72 +31,71 @@ namespace gambatte { struct SaveState; class Channel4 { - class Lfsr : public SoundUnit { - unsigned long backupCounter; - unsigned short reg; - unsigned char nr3; - bool master; - - void updateBackupCounter(unsigned long cc); - - public: - Lfsr(); - void event(); - bool isHighState() const { return ~reg & 1; } - void nr3Change(unsigned newNr3, unsigned long cc); - void nr4Init(unsigned long cc); - void reset(unsigned long cc); - void loadState(const SaveState &state); - void resetCounters(unsigned long oldCc); - void disableMaster() { killCounter(); master = false; reg = 0x7FFF; } - void killCounter() { counter = COUNTER_DISABLED; } - void reviveCounter(unsigned long cc); - - templatevoid SyncState(NewState *ns); - }; - - class Ch4MasterDisabler : public MasterDisabler { - Lfsr &lfsr; - public: - Ch4MasterDisabler(bool &m, Lfsr &lfsr) : MasterDisabler(m), lfsr(lfsr) {} - void operator()() { MasterDisabler::operator()(); lfsr.disableMaster(); } - }; - - friend class StaticOutputTester; - - StaticOutputTester staticOutputTest; - Ch4MasterDisabler disableMaster; - LengthCounter lengthCounter; - EnvelopeUnit envelopeUnit; - Lfsr lfsr; - - SoundUnit *nextEventUnit; - - unsigned long cycleCounter; - unsigned long soMask; - unsigned long prevOut; - - unsigned char nr4; - bool master; - - void setEvent(); - public: Channel4(); void setNr1(unsigned data); void setNr2(unsigned data); - void setNr3(unsigned data) { lfsr.nr3Change(data, cycleCounter); /*setEvent();*/ } + void setNr3(unsigned data) { lfsr_.nr3Change(data, cycleCounter_); } void setNr4(unsigned data); - void setSo(unsigned long soMask); - bool isActive() const { return master; } - + bool isActive() const { return master_; } void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles); - void reset(); - void init(bool cgb); - void loadState(const SaveState &state); + void loadState(SaveState const &state); +private: + class Lfsr : public SoundUnit { + public: + Lfsr(); + virtual void event(); + virtual void resetCounters(unsigned long oldCc); + bool isHighState() const { return ~reg_ & 1; } + void nr3Change(unsigned newNr3, unsigned long cc); + void nr4Init(unsigned long cc); + void reset(unsigned long cc); + void loadState(SaveState const &state); + void disableMaster() { killCounter(); master_ = false; reg_ = 0x7FFF; } + void killCounter() { counter_ = counter_disabled; } + void reviveCounter(unsigned long cc); + + private: + unsigned long backupCounter_; + unsigned short reg_; + unsigned char nr3_; + bool master_; + + void updateBackupCounter(unsigned long cc); + + public: + templatevoid SyncState(NewState *ns); + }; + + class Ch4MasterDisabler : public MasterDisabler { + public: + Ch4MasterDisabler(bool &m, Lfsr &lfsr) : MasterDisabler(m), lfsr_(lfsr) {} + virtual void operator()() { MasterDisabler::operator()(); lfsr_.disableMaster(); } + + private: + Lfsr &lfsr_; + }; + + friend class StaticOutputTester; + + StaticOutputTester staticOutputTest_; + Ch4MasterDisabler disableMaster_; + LengthCounter lengthCounter_; + EnvelopeUnit envelopeUnit_; + Lfsr lfsr_; + SoundUnit *nextEventUnit_; + unsigned long cycleCounter_; + unsigned long soMask_; + unsigned long prevOut_; + unsigned char nr4_; + bool master_; + + void setEvent(); + +public: templatevoid SyncState(NewState *ns); }; diff --git a/libgambatte/src/sound/duty_unit.cpp b/libgambatte/src/sound/duty_unit.cpp index 4d2a0ddde6..5f9f484742 100644 --- a/libgambatte/src/sound/duty_unit.cpp +++ b/libgambatte/src/sound/duty_unit.cpp @@ -1,156 +1,162 @@ -/*************************************************************************** - * Copyright (C) 2007 by Sindre AamÃ¥s * - * aamas@stud.ntnu.no * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License version 2 as * - * published by the Free Software Foundation. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License version 2 for more details. * - * * - * You should have received a copy of the GNU General Public License * - * version 2 along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ +// +// Copyright (C) 2007 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + #include "duty_unit.h" #include -static inline bool toOutState(const unsigned duty, const unsigned pos) { - static const unsigned char duties[4] = { 0x80, 0x81, 0xE1, 0x7E }; - - return duties[duty] >> pos & 1; +static inline bool toOutState(unsigned duty, unsigned pos) { + return 0x7EE18180 >> (duty * 8 + pos) & 1; } -static inline unsigned toPeriod(const unsigned freq) { - return (2048 - freq) << 1; +static inline unsigned toPeriod(unsigned freq) { + return (2048 - freq) * 2; } namespace gambatte { -void DutyUnit::updatePos(const unsigned long cc) { - if (cc >= nextPosUpdate) { - const unsigned long inc = (cc - nextPosUpdate) / period + 1; - nextPosUpdate += period * inc; - pos += inc; - pos &= 7; +DutyUnit::DutyUnit() +: nextPosUpdate_(counter_disabled) +, period_(4096) +, pos_(0) +, duty_(0) +, inc_(0) +, high_(false) +, enableEvents_(true) +{ +} + +void DutyUnit::updatePos(unsigned long const cc) { + if (cc >= nextPosUpdate_) { + unsigned long const inc = (cc - nextPosUpdate_) / period_ + 1; + nextPosUpdate_ += period_ * inc; + pos_ += inc; + pos_ &= 7; + high_ = toOutState(duty_, pos_); } } -void DutyUnit::setDuty(const unsigned nr1) { - duty = nr1 >> 6; - high = toOutState(duty, pos); -} - void DutyUnit::setCounter() { - static const unsigned char nextStateDistance[4 * 8] = { - 6, 5, 4, 3, 2, 1, 0, 0, - 0, 5, 4, 3, 2, 1, 0, 1, - 0, 3, 2, 1, 0, 3, 2, 1, - 0, 5, 4, 3, 2, 1, 0, 1 + static unsigned char const nextStateDistance[4 * 8] = { + 7, 6, 5, 4, 3, 2, 1, 1, + 1, 6, 5, 4, 3, 2, 1, 2, + 1, 4, 3, 2, 1, 4, 3, 2, + 1, 6, 5, 4, 3, 2, 1, 2 }; - if (enableEvents && nextPosUpdate != COUNTER_DISABLED) - counter = nextPosUpdate + period * nextStateDistance[(duty * 8) | pos]; - else - counter = COUNTER_DISABLED; + if (enableEvents_ && nextPosUpdate_ != counter_disabled) { + unsigned const npos = (pos_ + 1) & 7; + counter_ = nextPosUpdate_; + inc_ = nextStateDistance[duty_ * 8 + npos]; + if (toOutState(duty_, npos) == high_) { + counter_ += period_ * inc_; + inc_ = nextStateDistance[duty_ * 8 + ((npos + inc_) & 7)]; + } + } else + counter_ = counter_disabled; } -void DutyUnit::setFreq(const unsigned newFreq, const unsigned long cc) { +void DutyUnit::setFreq(unsigned newFreq, unsigned long cc) { updatePos(cc); - period = toPeriod(newFreq); + period_ = toPeriod(newFreq); setCounter(); } void DutyUnit::event() { - unsigned inc = period << duty; + static unsigned char const inc[] = { + 1, 7, + 2, 6, + 4, 4, + 6, 2, + }; - if (duty == 3) - inc -= period * 2; - - if (!(high ^= true)) - inc = period * 8 - inc; - - counter += inc; + high_ ^= true; + counter_ += inc_ * period_; + inc_ = inc[duty_ * 2 + high_]; } -void DutyUnit::nr1Change(const unsigned newNr1, const unsigned long cc) { +void DutyUnit::nr1Change(unsigned newNr1, unsigned long cc) { updatePos(cc); - setDuty(newNr1); + duty_ = newNr1 >> 6; setCounter(); } -void DutyUnit::nr3Change(const unsigned newNr3, const unsigned long cc) { - setFreq((getFreq() & 0x700) | newNr3, cc); +void DutyUnit::nr3Change(unsigned newNr3, unsigned long cc) { + setFreq((freq() & 0x700) | newNr3, cc); } -void DutyUnit::nr4Change(const unsigned newNr4, const unsigned long cc) { - setFreq((newNr4 << 8 & 0x700) | (getFreq() & 0xFF), cc); +void DutyUnit::nr4Change(unsigned const newNr4, unsigned long const cc, + bool const master) { + setFreq((newNr4 << 8 & 0x700) | (freq() & 0xFF), cc); if (newNr4 & 0x80) { - nextPosUpdate = (cc & ~1) + period; + nextPosUpdate_ = (cc & ~1ul) + period_ + 4 - (master << 1); setCounter(); } } -DutyUnit::DutyUnit() : -nextPosUpdate(COUNTER_DISABLED), -period(4096), -pos(0), -duty(0), -high(false), -enableEvents(true) -{} - void DutyUnit::reset() { - pos = 0; - high = toOutState(duty, pos); - nextPosUpdate = COUNTER_DISABLED; + pos_ = 0; + high_ = false; + nextPosUpdate_ = counter_disabled; setCounter(); } -void DutyUnit::loadState(const SaveState::SPU::Duty &dstate, const unsigned nr1, const unsigned nr4, const unsigned long cc) { - nextPosUpdate = std::max(dstate.nextPosUpdate, cc); - pos = dstate.pos & 7; - setDuty(nr1); - period = toPeriod((nr4 << 8 & 0x700) | dstate.nr3); - enableEvents = true; +void DutyUnit::loadState(SaveState::SPU::Duty const &dstate, + unsigned const nr1, unsigned const nr4, unsigned long const cc) { + nextPosUpdate_ = std::max(dstate.nextPosUpdate, cc); + pos_ = dstate.pos & 7; + high_ = dstate.high; + duty_ = nr1 >> 6; + period_ = toPeriod((nr4 << 8 & 0x700) | dstate.nr3); + enableEvents_ = true; setCounter(); } -void DutyUnit::resetCounters(const unsigned long oldCc) { - if (nextPosUpdate == COUNTER_DISABLED) +void DutyUnit::resetCounters(unsigned long const oldCc) { + if (nextPosUpdate_ == counter_disabled) return; updatePos(oldCc); - nextPosUpdate -= COUNTER_MAX; - SoundUnit::resetCounters(oldCc); -} - -void DutyUnit::killCounter() { - enableEvents = false; + nextPosUpdate_ -= counter_max; setCounter(); } -void DutyUnit::reviveCounter(const unsigned long cc) { +void DutyUnit::killCounter() { + enableEvents_ = false; + setCounter(); +} + +void DutyUnit::reviveCounter(unsigned long const cc) { updatePos(cc); - high = toOutState(duty, pos); - enableEvents = true; + enableEvents_ = true; setCounter(); } SYNCFUNC(DutyUnit) { - NSS(counter); - NSS(nextPosUpdate); - NSS(period); - NSS(pos); - NSS(duty); - NSS(high); - NSS(enableEvents); + NSS(counter_); + NSS(nextPosUpdate_); + NSS(period_); + NSS(pos_); + NSS(duty_); + NSS(inc_); + NSS(high_); + NSS(enableEvents_); } } diff --git a/libgambatte/src/sound/duty_unit.h b/libgambatte/src/sound/duty_unit.h index a45294133e..0f96140c9e 100644 --- a/libgambatte/src/sound/duty_unit.h +++ b/libgambatte/src/sound/duty_unit.h @@ -1,21 +1,21 @@ -/*************************************************************************** - * Copyright (C) 2007 by Sindre Aam�s * - * aamas@stud.ntnu.no * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License version 2 as * - * published by the Free Software Foundation. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License version 2 for more details. * - * * - * You should have received a copy of the GNU General Public License * - * version 2 along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ +// +// Copyright (C) 2007 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + #ifndef DUTY_UNIT_H #define DUTY_UNIT_H @@ -27,42 +27,47 @@ namespace gambatte { class DutyUnit : public SoundUnit { - unsigned long nextPosUpdate; - unsigned short period; - unsigned char pos; - unsigned char duty; - bool high; - bool enableEvents; +public: + DutyUnit(); + virtual void event(); + virtual void resetCounters(unsigned long oldCc); + bool isHighState() const { return high_; } + void nr1Change(unsigned newNr1, unsigned long cc); + void nr3Change(unsigned newNr3, unsigned long cc); + void nr4Change(unsigned newNr4, unsigned long cc, bool master); + void reset(); + void loadState(SaveState::SPU::Duty const &dstate, unsigned nr1, unsigned nr4, unsigned long cc); + void killCounter(); + void reviveCounter(unsigned long cc); + + //intended for use by SweepUnit only. + unsigned freq() const { return 2048 - (period_ >> 1); } + void setFreq(unsigned newFreq, unsigned long cc); + +private: + unsigned long nextPosUpdate_; + unsigned short period_; + unsigned char pos_; + unsigned char duty_; + unsigned char inc_; + bool high_; + bool enableEvents_; void setCounter(); void setDuty(unsigned nr1); void updatePos(unsigned long cc); public: - DutyUnit(); - void event(); - bool isHighState() const { return high; } - void nr1Change(unsigned newNr1, unsigned long cc); - void nr3Change(unsigned newNr3, unsigned long cc); - void nr4Change(unsigned newNr4, unsigned long cc); - void reset(); - void loadState(const SaveState::SPU::Duty &dstate, unsigned nr1, unsigned nr4, unsigned long cc); - void resetCounters(unsigned long oldCc); - void killCounter(); - void reviveCounter(unsigned long cc); - - //intended for use by SweepUnit only. - unsigned getFreq() const { return 2048 - (period >> 1); } - void setFreq(unsigned newFreq, unsigned long cc); - templatevoid SyncState(NewState *ns); }; class DutyMasterDisabler : public MasterDisabler { - DutyUnit &dutyUnit; public: - DutyMasterDisabler(bool &m, DutyUnit &dutyUnit) : MasterDisabler(m), dutyUnit(dutyUnit) {} - void operator()() { MasterDisabler::operator()(); dutyUnit.killCounter(); } + DutyMasterDisabler(bool &m, DutyUnit &dutyUnit) : MasterDisabler(m), dutyUnit_(dutyUnit) {} + virtual void operator()() { MasterDisabler::operator()(); dutyUnit_.killCounter(); } + +private: + DutyUnit &dutyUnit_; }; } diff --git a/libgambatte/src/sound/envelope_unit.cpp b/libgambatte/src/sound/envelope_unit.cpp index 917c73bc13..7e9a81bb81 100644 --- a/libgambatte/src/sound/envelope_unit.cpp +++ b/libgambatte/src/sound/envelope_unit.cpp @@ -1,108 +1,98 @@ -/*************************************************************************** - * Copyright (C) 2007 by Sindre AamÃ¥s * - * aamas@stud.ntnu.no * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License version 2 as * - * published by the Free Software Foundation. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License version 2 for more details. * - * * - * You should have received a copy of the GNU General Public License * - * version 2 along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ +// +// Copyright (C) 2007 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + #include "envelope_unit.h" #include namespace gambatte { -EnvelopeUnit::VolOnOffEvent EnvelopeUnit::nullEvent; +EnvelopeUnit::VolOnOffEvent EnvelopeUnit::nullEvent_; + +EnvelopeUnit::EnvelopeUnit(VolOnOffEvent &volOnOffEvent) +: volOnOffEvent_(volOnOffEvent) +, nr2_(0) +, volume_(0) +{ +} + +void EnvelopeUnit::reset() { + counter_ = counter_disabled; +} + +void EnvelopeUnit::loadState(SaveState::SPU::Env const &estate, unsigned nr2, unsigned long cc) { + counter_ = std::max(estate.counter, cc); + volume_ = estate.volume; + nr2_ = nr2; +} void EnvelopeUnit::event() { - const unsigned long period = nr2 & 7; + unsigned long const period = nr2_ & 7; if (period) { - unsigned newVol = volume; - - if (nr2 & 8) + unsigned newVol = volume_; + if (nr2_ & 8) ++newVol; else --newVol; if (newVol < 0x10U) { - volume = newVol; + volume_ = newVol; + if (volume_ < 2) + volOnOffEvent_(counter_); - if (volume < 2) - volOnOffEvent(counter); - - counter += period << 15; + counter_ += period << 15; } else - counter = COUNTER_DISABLED; + counter_ = counter_disabled; } else - counter += 8ul << 15; + counter_ += 8ul << 15; } -bool EnvelopeUnit::nr2Change(const unsigned newNr2) { - if (!(nr2 & 7) && counter != COUNTER_DISABLED) - ++volume; - else if (!(nr2 & 8)) - volume += 2; +bool EnvelopeUnit::nr2Change(unsigned const newNr2) { + if (!(nr2_ & 7) && counter_ != counter_disabled) + ++volume_; + else if (!(nr2_ & 8)) + volume_ += 2; - if ((nr2 ^ newNr2) & 8) - volume = 0x10 - volume; - - volume &= 0xF; - - nr2 = newNr2; + if ((nr2_ ^ newNr2) & 8) + volume_ = 0x10 - volume_; + volume_ &= 0xF; + nr2_ = newNr2; return !(newNr2 & 0xF8); } -bool EnvelopeUnit::nr4Init(const unsigned long cc) { - { - unsigned long period = nr2 & 7; +bool EnvelopeUnit::nr4Init(unsigned long const cc) { + unsigned long period = nr2_ & 7 ? nr2_ & 7 : 8; - if (!period) - period = 8; + if (((cc + 2) & 0x7000) == 0x0000) + ++period; - if (!(cc & 0x7000)) - ++period; + counter_ = cc - ((cc - 0x1000) & 0x7FFF) + period * 0x8000; - counter = cc - ((cc - 0x1000) & 0x7FFF) + period * 0x8000; - } - - volume = nr2 >> 4; - - return !(nr2 & 0xF8); -} - -EnvelopeUnit::EnvelopeUnit(VolOnOffEvent &volOnOffEvent) -: volOnOffEvent(volOnOffEvent), - nr2(0), - volume(0) -{ -} - -void EnvelopeUnit::reset() { - counter = COUNTER_DISABLED; -} - -void EnvelopeUnit::loadState(const SaveState::SPU::Env &estate, const unsigned nr2, const unsigned long cc) { - counter = std::max(estate.counter, cc); - volume = estate.volume; - this->nr2 = nr2; + volume_ = nr2_ >> 4; + return !(nr2_ & 0xF8); } SYNCFUNC(EnvelopeUnit) { - NSS(counter); - NSS(nr2); - NSS(volume); + NSS(counter_); + NSS(nr2_); + NSS(volume_); } } diff --git a/libgambatte/src/sound/envelope_unit.h b/libgambatte/src/sound/envelope_unit.h index 69f74d1997..4705dfc6f5 100644 --- a/libgambatte/src/sound/envelope_unit.h +++ b/libgambatte/src/sound/envelope_unit.h @@ -1,21 +1,21 @@ -/*************************************************************************** - * Copyright (C) 2007 by Sindre AamÃ¥s * - * aamas@stud.ntnu.no * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License version 2 as * - * published by the Free Software Foundation. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License version 2 for more details. * - * * - * You should have received a copy of the GNU General Public License * - * version 2 along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ +// +// Copyright (C) 2007 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + #ifndef ENVELOPE_UNIT_H #define ENVELOPE_UNIT_H @@ -32,22 +32,22 @@ public: virtual void operator()(unsigned long /*cc*/) {} }; -private: - static VolOnOffEvent nullEvent; - VolOnOffEvent &volOnOffEvent; - unsigned char nr2; - unsigned char volume; - -public: - explicit EnvelopeUnit(VolOnOffEvent &volOnOffEvent = nullEvent); + explicit EnvelopeUnit(VolOnOffEvent &volOnOffEvent = nullEvent_); void event(); - bool dacIsOn() const { return nr2 & 0xF8; } - unsigned getVolume() const { return volume; } + bool dacIsOn() const { return nr2_ & 0xF8; } + unsigned getVolume() const { return volume_; } bool nr2Change(unsigned newNr2); bool nr4Init(unsigned long cycleCounter); void reset(); - void loadState(const SaveState::SPU::Env &estate, unsigned nr2, unsigned long cc); + void loadState(SaveState::SPU::Env const &estate, unsigned nr2, unsigned long cc); +private: + static VolOnOffEvent nullEvent_; + VolOnOffEvent &volOnOffEvent_; + unsigned char nr2_; + unsigned char volume_; + +public: templatevoid SyncState(NewState *ns); }; diff --git a/libgambatte/src/sound/length_counter.cpp b/libgambatte/src/sound/length_counter.cpp index 7822fb934f..cdfd4c18bb 100644 --- a/libgambatte/src/sound/length_counter.cpp +++ b/libgambatte/src/sound/length_counter.cpp @@ -1,93 +1,83 @@ -/*************************************************************************** - * Copyright (C) 2007 by Sindre AamÃ¥s * - * aamas@stud.ntnu.no * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License version 2 as * - * published by the Free Software Foundation. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License version 2 for more details. * - * * - * You should have received a copy of the GNU General Public License * - * version 2 along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * -***************************************************************************/ +// +// Copyright (C) 2007 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + #include "length_counter.h" #include "master_disabler.h" #include namespace gambatte { -LengthCounter::LengthCounter(MasterDisabler &disabler, const unsigned mask) : - disableMaster(disabler), - lengthMask(mask) +LengthCounter::LengthCounter(MasterDisabler &disabler, unsigned const mask) +: disableMaster_(disabler) +, lengthCounter_(0) +, lengthMask_(mask) { - init(false); nr1Change(0, 0, 0); } void LengthCounter::event() { - counter = COUNTER_DISABLED; - lengthCounter = 0; - disableMaster(); + counter_ = counter_disabled; + lengthCounter_ = 0; + disableMaster_(); } -void LengthCounter::nr1Change(const unsigned newNr1, const unsigned nr4, const unsigned long cycleCounter) { - lengthCounter = (~newNr1 & lengthMask) + 1; - counter = (nr4 & 0x40) ?( (cycleCounter >> 13) + lengthCounter) << 13 : static_cast(COUNTER_DISABLED); +void LengthCounter::nr1Change(unsigned const newNr1, unsigned const nr4, unsigned long const cc) { + lengthCounter_ = (~newNr1 & lengthMask_) + 1; + counter_ = nr4 & 0x40 + ? ((cc >> 13) + lengthCounter_) << 13 + : static_cast(counter_disabled); } -void LengthCounter::nr4Change(const unsigned oldNr4, const unsigned newNr4, const unsigned long cycleCounter) { - if (counter != COUNTER_DISABLED) - lengthCounter = (counter >> 13) - (cycleCounter >> 13); +void LengthCounter::nr4Change(unsigned const oldNr4, unsigned const newNr4, unsigned long const cc) { + if (counter_ != counter_disabled) + lengthCounter_ = (counter_ >> 13) - (cc >> 13); { unsigned dec = 0; if (newNr4 & 0x40) { - dec = ~cycleCounter >> 12 & 1; + dec = ~cc >> 12 & 1; - if (!(oldNr4 & 0x40) && lengthCounter) { - if (!(lengthCounter -= dec)) - disableMaster(); + if (!(oldNr4 & 0x40) && lengthCounter_) { + if (!(lengthCounter_ -= dec)) + disableMaster_(); } } - if ((newNr4 & 0x80) && !lengthCounter) - lengthCounter = lengthMask + 1 - dec; + if ((newNr4 & 0x80) && !lengthCounter_) + lengthCounter_ = lengthMask_ + 1 - dec; } - if ((newNr4 & 0x40) && lengthCounter) - counter = ((cycleCounter >> 13) + lengthCounter) << 13; + if ((newNr4 & 0x40) && lengthCounter_) + counter_ = ((cc >> 13) + lengthCounter_) << 13; else - counter = COUNTER_DISABLED; + counter_ = counter_disabled; } -/*void LengthCounter::reset() { - counter = COUNTER_DISABLED; - - if (cgb) - lengthCounter = lengthMask + 1; -}*/ - -void LengthCounter::init(const bool cgb) { - this->cgb = cgb; -} - -void LengthCounter::loadState(const SaveState::SPU::LCounter &lstate, const unsigned long cc) { - counter = std::max(lstate.counter, cc); - lengthCounter = lstate.lengthCounter; +void LengthCounter::loadState(SaveState::SPU::LCounter const &lstate, unsigned long const cc) { + counter_ = std::max(lstate.counter, cc); + lengthCounter_ = lstate.lengthCounter; } SYNCFUNC(LengthCounter) { - NSS(counter); - NSS(lengthCounter); - NSS(cgb); + NSS(counter_); + NSS(lengthCounter_); } } diff --git a/libgambatte/src/sound/length_counter.h b/libgambatte/src/sound/length_counter.h index 7250ac0a10..31727945f0 100644 --- a/libgambatte/src/sound/length_counter.h +++ b/libgambatte/src/sound/length_counter.h @@ -1,21 +1,21 @@ -/*************************************************************************** - * Copyright (C) 2007 by Sindre AamÃ¥s * - * aamas@stud.ntnu.no * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License version 2 as * - * published by the Free Software Foundation. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License version 2 for more details. * - * * - * You should have received a copy of the GNU General Public License * - * version 2 along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * -***************************************************************************/ +// +// Copyright (C) 2007 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + #ifndef LENGTH_COUNTER_H #define LENGTH_COUNTER_H @@ -28,20 +28,19 @@ namespace gambatte { class MasterDisabler; class LengthCounter : public SoundUnit { - MasterDisabler &disableMaster; - unsigned short lengthCounter; - const unsigned char lengthMask; - bool cgb; - public: LengthCounter(MasterDisabler &disabler, unsigned lengthMask); - void event(); + virtual void event(); void nr1Change(unsigned newNr1, unsigned nr4, unsigned long cc); void nr4Change(unsigned oldNr4, unsigned newNr4, unsigned long cc); -// void reset(); - void init(bool cgb); - void loadState(const SaveState::SPU::LCounter &lstate, unsigned long cc); + void loadState(SaveState::SPU::LCounter const &lstate, unsigned long cc); +private: + MasterDisabler &disableMaster_; + unsigned short lengthCounter_; + unsigned char const lengthMask_; + +public: templatevoid SyncState(NewState *ns); }; diff --git a/libgambatte/src/sound/master_disabler.h b/libgambatte/src/sound/master_disabler.h index efbc37f114..a9c16adec1 100644 --- a/libgambatte/src/sound/master_disabler.h +++ b/libgambatte/src/sound/master_disabler.h @@ -1,33 +1,36 @@ -/*************************************************************************** - * Copyright (C) 2007 by Sindre AamÃ¥s * - * aamas@stud.ntnu.no * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License version 2 as * - * published by the Free Software Foundation. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License version 2 for more details. * - * * - * You should have received a copy of the GNU General Public License * - * version 2 along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * -***************************************************************************/ +// +// Copyright (C) 2007 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + #ifndef MASTER_DISABLER_H #define MASTER_DISABLER_H namespace gambatte { -class MasterDisabler { - bool &master; +class MasterDisabler { public: - MasterDisabler(bool &m) : master(m) {} + explicit MasterDisabler(bool &master) : master_(master) {} virtual ~MasterDisabler() {} - virtual void operator()() { master = false; } + virtual void operator()() { master_ = false; } + +private: + bool &master_; }; + } #endif diff --git a/libgambatte/src/sound/sound_unit.h b/libgambatte/src/sound/sound_unit.h index d2a00582ad..c990939c9c 100644 --- a/libgambatte/src/sound/sound_unit.h +++ b/libgambatte/src/sound/sound_unit.h @@ -1,37 +1,43 @@ -/*************************************************************************** - * Copyright (C) 2007 by Sindre AamÃ¥s * - * aamas@stud.ntnu.no * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License version 2 as * - * published by the Free Software Foundation. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License version 2 for more details. * - * * - * You should have received a copy of the GNU General Public License * - * version 2 along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ +// +// Copyright (C) 2007 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + #ifndef SOUND_UNIT_H #define SOUND_UNIT_H namespace gambatte { class SoundUnit { -protected: - unsigned long counter; public: - enum { COUNTER_MAX = 0x80000000u, COUNTER_DISABLED = 0xFFFFFFFFu }; + enum { counter_max = 0x80000000u, counter_disabled = 0xFFFFFFFFu }; - SoundUnit() : counter(COUNTER_DISABLED) {} virtual ~SoundUnit() {} virtual void event() = 0; - unsigned long getCounter() const { return counter; } - virtual void resetCounters(unsigned long /*oldCc*/) { if (counter != COUNTER_DISABLED) counter -= COUNTER_MAX; } + + virtual void resetCounters(unsigned long /*oldCc*/) { + if (counter_ != counter_disabled) + counter_ -= counter_max; + } + + unsigned long counter() const { return counter_; } + +protected: + SoundUnit() : counter_(counter_disabled) {} + unsigned long counter_; }; } diff --git a/libgambatte/src/sound/static_output_tester.h b/libgambatte/src/sound/static_output_tester.h index ac785e564d..0d7edbbf99 100644 --- a/libgambatte/src/sound/static_output_tester.h +++ b/libgambatte/src/sound/static_output_tester.h @@ -1,21 +1,21 @@ -/*************************************************************************** - * Copyright (C) 2007 by Sindre AamÃ¥s * - * aamas@stud.ntnu.no * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License version 2 as * - * published by the Free Software Foundation. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License version 2 for more details. * - * * - * You should have received a copy of the GNU General Public License * - * version 2 along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ +// +// Copyright (C) 2007 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + #ifndef STATIC_OUTPUT_TESTER_H #define STATIC_OUTPUT_TESTER_H @@ -25,19 +25,21 @@ namespace gambatte { template class StaticOutputTester : public EnvelopeUnit::VolOnOffEvent { - const Channel &ch; - Unit &unit; public: - StaticOutputTester(const Channel &ch, Unit &unit) : ch(ch), unit(unit) {} + StaticOutputTester(Channel const &ch, Unit &unit) : ch_(ch), unit_(unit) {} void operator()(unsigned long cc); + +private: + Channel const &ch_; + Unit &unit_; }; template -void StaticOutputTester::operator()(const unsigned long cc) { - if (ch.soMask && ch.master && ch.envelopeUnit.getVolume()) - unit.reviveCounter(cc); +void StaticOutputTester::operator()(unsigned long cc) { + if (ch_.soMask_ && ch_.master_ && ch_.envelopeUnit_.getVolume()) + unit_.reviveCounter(cc); else - unit.killCounter(); + unit_.killCounter(); } } diff --git a/output/dll/libgambatte.dll b/output/dll/libgambatte.dll index 4b53f609938abdf7c1054faf54c797ffd518d0dc..56a6f6d8790ec5cdcecd43843028526e4913d4a9 100644 GIT binary patch delta 43876 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%0AlH+rWcqCL1uD5i??%3oAbb zSrGGs>zEL|G8hNi&@!FHjDKLTd6qrDRQcp@2#d(|i;dU&YovEyB$NI|2**QJ4BIxA}tWDt2 z*<%@QC66DIixtciyre`(ObQ2L3iu8!2Zt^t;aWq^MXsoaK z>AZ7%%}+;6@-;slG1b>{bi|#3vUI2SPC2G1zhPka8`T zc{=6yMe$f+9^i8G$S{7nd6BHiyZyo{$K$LA`NnpHr!A!Znq{;VcBg+1ZvXccd|3Q= zTm$~`bObJSh1EE-jh-K{2g5as@GiljAl1)kqrIWAa3WU!DYc7I<7$&W$mJD<|2j=o z#Y;Vv$sd}w|Ln7esHPbGZG+Kmz$EPOjJvV3hn=Mp&e28>ed}+gn%NL##SYN_of9Z0 z4v%ijJUxhG@R*Adpg#GKd!8?yu$;wsiTORf*pFKf#&Frn`&KdK#5IU$T4U-c2%Z)|LF+X%M8o!~sB0)Nfhr12ppF6)sHDII zYAG;YHM{!<;6O#a!)>LlJ3C=u%iTG>S$p=7yP!Ag%XYY3z1axX!hNzg>n1-q$^Bz* zc7vQfsWLj2-N@Ke?h$?1tsOHL;ob$`c?^72dZ7D-J}iOlsQjo8b2MXhmDA(cV~ovq zf7+idkbjw1dF#!r${;5dxEHKqT`Kz~vgab@F%v8Qk->gp5l>9Up5+?aNB2}#9v;DZ z$=$0bFmum|%zVRmX5Nhuk;BZ>5k5xvI>+5Si!Fz=k}Nj18S}VL-NxG8=|O$aJ_Rm+ z&^0;Bd8(fb-{lXw`ewNpj2nXqyG4osUA&;9TN-A>Y1|HTRnpxwOTN2&9Bc2McsuLY zI-jSB8!q$RWw*28E#?c9`{+2<+3Knsi+9B>%ZMezd-&pF4zoBo!Y`w10H0V6b9uNN z1LG2{!I#-!*$|K!w?vv*UJ6Du_p!I?_k3FD8h8tjM&VsD69tu0NEIdNuWJCyO(P8s zaSi<1iga$-d`ce@B)@DH;W7wk_TQnohmbk$i(^@TtEd^gg?_^GP!?u$B>&pA1y6<| zA=M?2A@v1hlNAP4V0x-T2wE8C-aL*agjC%Z<5=cjR2}BGJ-&$`jcGYmb*9 zsQLtk*B^Q;ZajestU!SYtU+M@`zq9luU&_ne_0oQE&BB+6SY`5^$vEX1og_#c_Y(iLykZY-YW(KPallv{M%)N)*AJ*^QWK=z7 z4h7|<3z^x1a2rB@gh+%74>I!y2yZ`F`RyDm1X#Gc?_4&r^X^2E%kw+_L$3Xhc_u!O zFeB;xyFHsL?Q_|eUFD1J%6mP$NHaiuJqg;y>rWierFjazlH_gS@j)=GB6Jx}8X zIBlUCaS-&6Cz<&a!WRe{!Y>F{5Eic$p#`31giZ*(5aJM$5xnVXfU^9%7?48ZiLr=DR~=mb1B7#lR;rwi1@r5uY~Lz zOCb<_=JoTIvy?1sSw>X;kNA`j&gOoyoVByAq9mUkOG_!q+hKaQ!G%zL8=MzYz+3!9 zj-lbi+kK1TW*aO!s51SNkLH%Wgz!nrZeg)j1z=iOh$F1Uzg=XftJ0Po!1#062NG^~ zFm4dxs)BKJ-P%$cj2l9@lfgLaP(q##Mh+ufT`(?%a2JAcsf23?#-$O?5M)&QaKc3d z;w(`-=CWJUk>=gUw`UMxOdz4lPM=-0^bf|3AY5`VZY1GSgK?t>H_DImZlU{DEyvV_ z_02G0#gy_>%Nh6I*RoFc2Q^IekR4s2EmI&vnx;U8v`vB38>hD-f#f@=8LcZbRW^fJ zbI62ZlN!Q8TBcwT(liA!q-_dhNaG}7!L9SE#%ZN9E&49jRn1e+e9iW$?{)om4U~lN z<~gsQD;w?8`^6?3T@VUrqP!TTqK*2hP9RqhrN6}rWJr_Mb^bqSvy=Ip3$2dN&;wg7 z7myv;Yz3x;#ougod7>|%?K{2Qx*uB4@F6N+>)<)7d z4OAdQTBtyVG*N*JX`=!u8mTqd;&y7DdSj(7l}Pml>f)`{SB;_VR4@%`r~(<%QUy|P zs+9-aOp)cy7WW;`uzA)oxQOj+J&JzYXV7oARQeq_gnrYK@msK}eLjD*p2Q#B;`yUl zfBxvxk3VjX#be`|W>fJ{vZNQs@~7d&96ViF#uukie8m}j5u>KrhJn~gz$Js{k$AKD z#Yu?PG#i88mIkS+rrB7YjQe@brs~i0a(M3CD~(tNPS-lmb-(m1 zo5C#a%g?fdY@xeqBa3JA-9K$)1K9)a8=hl*%nR|!FwZ-9U+2yhRYfMtGWWFSSa*;< z_8g13c^ML&S-1?-;3~rJ<(FT6dFJ6}4*-kF#Ekb2&THn0!WejLp3#!g#a;UxdnwX_ z#4HcYt<9mDP|`RX=geixl%L z{{!EV%=*SY=dxr^BVKY*o+k+w<@bL)F;LXrXSRFI7IsIwEySo37-6eqMPrd^Xqvi@f4h_X97p_j_zWa%1}#oD&|j z5bSvs<-YUa?k#{1?%wLY^A*<1bTS2z+`N4f^xY$Maj$%Z^=V=Hnxlg~sR?6ls z?%A*6)@z--!}>z3EPdM>ABDg(8lgYJ7=&pEOAt;Y>_s>NnpJqxUoE}vh`*b9$z$jC7az zB!CQr3rLLr8jSHu!i74KvFa@D{R6%L#YxG0yyOt`sYpAC=LN)mIA_7Ed*;uXZ54!b7R)WQjC`mN@yaaJu6dV~L18^16wjcy4gA#7g>B^&-`Qceh=1iQ$3oa4tE2pKnE$jM&*(4WZp+WCq0(}ZMacNPi}-ukp-m%*ddM*d z^ic$(Ec#1t8XZL9zKld~Nauv4;T2tJ1ITA1ZMHAXedJe`hY`Tt>k?bg?HlyBAme{n z8A}%tzpUdj+@~(Fj?IW9-~HYt*5k%h>T~ozqCOoEC-ky-*-SK3dCpE7LXuxXavgA6 z-I)!nU9)I_FL6p%=YEJVlp%&EW|affCGkg8BG%?;y~*f70`UZU5@hpfbV-4DBXFzS zXB$`-F#i*e>`;ompojs-9B?!scT=1*AhizZsh|@y?is(a?gJ^`UgT*i9~s7gJb~KG zhAf-=&EHs;#N-e~%aJq{?7ZYuzYU-%_px_hfyh2YGs%bZ(^EdW0y7F}v)$}B*0H?} zAff4xl1@UKAw21(Z6QfEB<(~9EQnsvzx@uWs{%A5Rvyv-IIpp&T4eW|msq=TR68V* z{)lv8h^Ut!h9~MjLlR_jpgL6Wc<`78mW;D(RgNzFGEn`Z+zb9-9d14!AcDBmfixO4 zI!E483K1tfbN}=^i)tT|x*HtI6WxEi#5#1dA+4XDzL852oObvW$RvSN1IWCBsE$X4 zupS!_Cps!exqH@Sc;F=Way(kbqc1fgY;`|>8Qy*#8S>r5ms!V_4FFFf#PF0pu^{q_ zqNgzl!&U%}OtsYA=1(2#Ulu@inUi(S4t*N}{L(33r*+;y^Sln3l|e<#bGn=gGGGws*it-|E)a>*O+ zo9G$!H+ptX{ONC)9&Q(tY+{X*{YHAu#uqrKsrS``2m0Ia6_4m{?)72vGuA?+mmoZa z@G3$jLM?&^p%KB{U6y(vBp_rV3fVUuwLC8l~j^M8Rhe6&PHa%ZoyHPm2as$-&XF$o{TqCMn=gwGS=lYJII*^>?J(kS)Ob?HE`bG_Xlqr zI&WCZw3M{B)2h>I(k2dHoc?HfNxG8$RQksBSJK}|{~-O7^na!QoG#fUiQJLJo8r60 zAB;Z||55zs@&AfHAOCCopYbgcA`_w$dL(2gOilPRp+#cb#BPbV43o+wsUPwjNc?2j z)Rbu{`6;tgEGhF-7N!)YEWIP;fpixHyr2Gky0itaeu41Ygqev_k|D{M6p_?wTzsH*rx+Ekeq$f;C zn49oe!kUB^6Dkr8CVY}mpU{}lF7XCyVoG9u;`~Hg;;O`#5_cpXN&GnR+r%r0?UMQ> zr6o;FnwzvN>B*!QlHN+HN;;YJb<)pCQgYkm*yKUUqmy%!?@fLr^cs#m3k$$bK08We;d9z9Yw9f z*ow#A_^S9rDBp>M9}-$6rY5#cDok1mHEl_zK?4TeHt3x}RfAd&jv8D!^q)iHh7}BZ ze%Q~$hNUb>*_%?8aww%XzEds z)-UbO;dR5m8SWW=emEq;&S&trIsVc3Z{j`hpbPOA;~U~H$4d!@1Y<(igdqvz5@shj z5_TkQ`l2o3wJE<i!xHQAQDHTh(6LvqZ(F#{J4 zv~Cz!HPAEAJZQ+E{6Wf~U4zaJiWoe2@I8ZF@SSsm8wa-=k~-whArB5I8?tH0fg#@y z=`=KJ=tD!F8~WbRUxxM^Hg4D>!(JYCYS^E{ZcLe+Qk?Qe%BLyKQWH|Ar#e&LPCc92 zI&Em$oU|v?{+_0#wI7~2eBtoT!~ee~&Oa!s>yF~PbZsD&XcQwO4A2qcT2^!4z4zUF z-@WhM2L@+Q(SUA7sA#~F4k$!oZ9@=Tr)qQvsl-r!M50bh!X~1@4yhPz5{!))DYX$t z6EPY!sZB`0A5&>uYrkOr=*-US&d%)4+q37K&w0DE+oSL5fV;@8b5FQ^ArRK<*#xgH z8jVBAD1a=mQXwirC8!jYqow%Qcmw`DejUF9-Z+o@@m>57NhXM7k~~sGo+VABpNydi zbP6TZrg^lO{)%p(+vuD0F#Q|7NUzhGY$03D*0C`Aka>9`M?8nG;s^M3ULZoHqFQVf ztpbh!K0+OKbT&+%JY`$ZDXr2Yd{$s|ld{)M)LA55f zk9D&PY=C+AXg--g#%(^27x8C#EwAG{_w@*2bW&ku zC+T#pbha+g%k*kpuU`YZzOPT|FZ2!lBR9@f?mQ5?!d(L$^_u&(+v%Q$0N)-qbc)v# z+5^`yC;?4FF3Ll{K&{B3Idn0tpwH76K*libpvUMx=w*77dd&%D8pNvFd=a$RXTE2C z4zaoi?nz`*nPPL<61I#z$LiTG*2a#q)9ey^n5T1xKf#N6JwMN5#eT6@cFIrW6?sdJ zQjdhx6nHI5q2FFm+tphTk{%UsCh52JQQhTw;CH6+aDY#O=Ls3dQmq^EdkJ@Bcs;`> zxSR20?aB3e!f-nq=Ya2nI3E{6qmO(q6s1so^`c3PvN7#n=h3*$o9Q#QCGC+wS zHY7ZYEF^w0O|*#Dt>e~hYoZLwr)7oQC{tC7O0v(}nNGwR(xcraKr7E$uO|@>q|vx; zgi3HHo(T`XOh(c1Pzjy}*fnt3W^@`|Mz3+n=r@K8FMbe@$B8%%Psbc*;8}P+UIZdn z;3Q6XCZEHf`UMWRR%slqP;0tqSv^hpN1G1;QPs+FxF z`B&K|2c=KNtAKLVeD$=dQZK4q>Hw7GS@pHLtH#=sZD!B13+yucc_@}0_M7%0yT`t4 z|J#1hNpi3=!3HqeRA=a1U94B?b^2xfM}1JA)ED(l z{eYX`KIVoVcNeyq7%The*(To z8F9vB0~?t}uJM$y+<4Ad2V~o6v;f=QH%@|T14a~{hyyqibXtn5@$c}fct3s@D(q8y z9siglj1<^hQV=3#WHot->?UnMxHIHya*xE(sZ;~qN@*?KLR;vcA%0)cJ9MnM5uo0~ z&+^OsUpz*P5fem$_^Fs8rVA5feO$~Ic_LpFf#)m1^&3Q;Xb`W8X3;K=iC%F*%mHhU zks0zgvPtfRKKWRNK9k=_uX;qKE3AZ?t%4B26F{aR^{~Cg?gW_!08O#ZI42pJyuxX6 z-f-HTkDSk)=^*U)n!9;0f40Cb>2N)5_p3Yyw!mVv1MNfY0IY98F`p4@Bmwc0@l-qm z=iz7YVf+C^^u_{w2S<}6l23}sO0tI30jstG@5AIIIZe)yZ$J+pok)KM>|)UGGiWxQ zM}zd|w21zameZBAimsvSX&v24o9LhDQTh?x_GnsEo!7JNKw8x1P@+tcsnRb45&_!< zlxhZqBVQKEcG)41$PeT(*(JNdg16+5j7X32-Je^k1`4T3?NQAttXfr@YFD{BsPlCp zY^4%i3VW&Ct#qs0KKGJ4=-zT8F8s~?NPOZ@JW4?VWuyGi2+OKq7S*E$7)D_j52s)W zo`Xia&+mBPcfddfHFCgCrAC!eW2`mS1I?R^Jw~$;Hd>8#quV$IwmJu4i~!+%5XAsS z5X3Az+okS!epqVt3c*@5V)1L(RSJm1L7L(r-RgIMw=-x zxcsJV=9sx=5lo6zW|diEHkgfOvl$MV9p(|37+vNi^D1C7VtQCS^Roc#und;Vf~=fX zvQ2C|(76?$(ayTqIo1c@7-A8?X*2*e1&B?#0H|j1LSD`*f!;N|@rRjvjQ8@Z{2K2E qyvBotgDM{yWIMcF9SUm9_DAK!l+p&;2S`sj__9B0g%`f{^8XKNws#u< delta 43534 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 -xu^r9Cs4E!B%OX}bEi$HM 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 From a6aeef840a665a79ad046a1ae54fcd5489fa6155 Mon Sep 17 00:00:00 2001 From: MrWint Date: Sat, 25 May 2019 21:01:50 +0200 Subject: [PATCH 09/38] Update PPU --- libgambatte/src/counterdef.h | 2 +- libgambatte/src/initstate.cpp | 8 +- libgambatte/src/interruptrequester.cpp | 12 +- libgambatte/src/interruptrequester.h | 2 +- libgambatte/src/memory.cpp | 62 ++++---- libgambatte/src/memory.h | 2 +- libgambatte/src/tima.cpp | 18 +-- libgambatte/src/video.cpp | 32 ++-- libgambatte/src/video.h | 50 +----- libgambatte/src/video/ly_counter.cpp | 68 ++++---- libgambatte/src/video/ly_counter.h | 56 +++---- libgambatte/src/video/lyc_irq.cpp | 86 +++++----- libgambatte/src/video/lyc_irq.h | 75 ++++----- libgambatte/src/video/m0_irq.h | 68 ++++++++ libgambatte/src/video/next_m0_time.cpp | 8 +- libgambatte/src/video/next_m0_time.h | 8 +- libgambatte/src/video/ppu.h | 106 +++++++------ libgambatte/src/video/sprite_mapper.cpp | 199 +++++++++++++----------- libgambatte/src/video/sprite_mapper.h | 189 +++++++++++----------- output/dll/libgambatte.dll | Bin 142336 -> 142336 bytes 20 files changed, 552 insertions(+), 499 deletions(-) create mode 100644 libgambatte/src/video/m0_irq.h diff --git a/libgambatte/src/counterdef.h b/libgambatte/src/counterdef.h index 9d19f44815..66d078ad1f 100644 --- a/libgambatte/src/counterdef.h +++ b/libgambatte/src/counterdef.h @@ -2,7 +2,7 @@ #define COUNTERDEF_H namespace gambatte { -enum { DISABLED_TIME = 0xFFFFFFFFul }; +enum { disabled_time = 0xFFFFFFFFul }; } #endif diff --git a/libgambatte/src/initstate.cpp b/libgambatte/src/initstate.cpp index ccd984d745..362f6701a0 100644 --- a/libgambatte/src/initstate.cpp +++ b/libgambatte/src/initstate.cpp @@ -1200,10 +1200,10 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM state.mem.divLastUpdate = 0 - div; state.mem.timaLastUpdate = 0; - state.mem.tmatime = DISABLED_TIME; - state.mem.nextSerialtime = DISABLED_TIME; - state.mem.lastOamDmaUpdate = DISABLED_TIME; - state.mem.unhaltTime = DISABLED_TIME; + state.mem.tmatime = disabled_time; + state.mem.nextSerialtime = disabled_time; + state.mem.lastOamDmaUpdate = disabled_time; + state.mem.unhaltTime = disabled_time; state.mem.minIntTime = 0; state.mem.rombank = 1; state.mem.dmaSource = 0; diff --git a/libgambatte/src/interruptrequester.cpp b/libgambatte/src/interruptrequester.cpp index 8e57790d7a..8001ce6ca9 100644 --- a/libgambatte/src/interruptrequester.cpp +++ b/libgambatte/src/interruptrequester.cpp @@ -29,13 +29,13 @@ void InterruptRequester::loadState(const SaveState &state) { iereg_ = state.mem.ioamhram.get()[0x1FF] & 0x1F; intFlags.set(state.mem.IME, state.mem.halted); - eventTimes.setValue(intFlags.imeOrHalted() && pendingIrqs() ? minIntTime : static_cast(DISABLED_TIME)); + eventTimes.setValue(intFlags.imeOrHalted() && pendingIrqs() ? minIntTime : static_cast(disabled_time)); } void InterruptRequester::resetCc(const unsigned long oldCc, const unsigned long newCc) { minIntTime = minIntTime < oldCc ? 0 : minIntTime - (oldCc - newCc); - if (eventTimes.value(INTERRUPTS) != DISABLED_TIME) + if (eventTimes.value(INTERRUPTS) != disabled_time) eventTimes.setValue(minIntTime); } @@ -51,7 +51,7 @@ void InterruptRequester::di() { intFlags.unsetIme(); if (!intFlags.imeOrHalted()) - eventTimes.setValue(DISABLED_TIME); + eventTimes.setValue(disabled_time); } void InterruptRequester::halt() { @@ -65,7 +65,7 @@ void InterruptRequester::unhalt() { intFlags.unsetHalted(); if (!intFlags.imeOrHalted()) - eventTimes.setValue(DISABLED_TIME); + eventTimes.setValue(disabled_time); } void InterruptRequester::flagIrq(const unsigned bit) { @@ -84,14 +84,14 @@ void InterruptRequester::setIereg(const unsigned iereg) { iereg_ = iereg & 0x1F; if (intFlags.imeOrHalted()) - eventTimes.setValue(pendingIrqs() ? minIntTime : static_cast(DISABLED_TIME)); + eventTimes.setValue(pendingIrqs() ? minIntTime : static_cast(disabled_time)); } void InterruptRequester::setIfreg(const unsigned ifreg) { ifreg_ = ifreg; if (intFlags.imeOrHalted()) - eventTimes.setValue(pendingIrqs() ? minIntTime : static_cast(DISABLED_TIME)); + eventTimes.setValue(pendingIrqs() ? minIntTime : static_cast(disabled_time)); } SYNCFUNC(InterruptRequester) diff --git a/libgambatte/src/interruptrequester.h b/libgambatte/src/interruptrequester.h index 5e5cb7d10c..5d0c49f860 100644 --- a/libgambatte/src/interruptrequester.h +++ b/libgambatte/src/interruptrequester.h @@ -87,7 +87,7 @@ public: inline void flagHdmaReq(InterruptRequester *const intreq) { intreq->setEventTime(0); } inline void flagGdmaReq(InterruptRequester *const intreq) { intreq->setEventTime(1); } -inline void ackDmaReq(InterruptRequester *const intreq) { intreq->setEventTime(DISABLED_TIME); } +inline void ackDmaReq(InterruptRequester *const intreq) { intreq->setEventTime(disabled_time); } inline bool hdmaReqFlagged(const InterruptRequester &intreq) { return intreq.eventTime(DMA) == 0; } inline bool gdmaReqFlagged(const InterruptRequester &intreq) { return intreq.eventTime(DMA) == 1; } diff --git a/libgambatte/src/memory.cpp b/libgambatte/src/memory.cpp index 328e6cf083..f244cf00d0 100644 --- a/libgambatte/src/memory.cpp +++ b/libgambatte/src/memory.cpp @@ -32,7 +32,7 @@ Memory::Memory(const Interrupter &interrupter_in, unsigned short &sp, unsigned s linkCallback(0), getInput(0), divLastUpdate(0), - lastOamDmaUpdate(DISABLED_TIME), + lastOamDmaUpdate(disabled_time), display(ioamhram, 0, VideoInterruptRequester(&intreq)), interrupter(interrupter_in), dmaSource(0), @@ -80,7 +80,7 @@ void Memory::loadState(const SaveState &state) { dmaSource = state.mem.dmaSource; dmaDestination = state.mem.dmaDestination; oamDmaPos = state.mem.oamDmaPos; - serialCnt = intreq.eventTime(SERIAL) != DISABLED_TIME + serialCnt = intreq.eventTime(SERIAL) != disabled_time ? serialCntFrom(intreq.eventTime(SERIAL) - state.cpu.cycleCounter, ioamhram[0x102] & isCgb() * 2) : 8; @@ -88,7 +88,7 @@ void Memory::loadState(const SaveState &state) { cart.setOamDmaSrc(OAM_DMA_SRC_OFF); cart.setWrambank(isCgb() && (ioamhram[0x170] & 0x07) ? ioamhram[0x170] & 0x07 : 1); - if (lastOamDmaUpdate != DISABLED_TIME) { + if (lastOamDmaUpdate != disabled_time) { oamDmaInitSetup(); const unsigned oamEventPos = oamDmaPos < 0xA0 ? 0xA0 : 0x100; @@ -112,11 +112,11 @@ void Memory::setEndtime(const unsigned long cycleCounter, const unsigned long in void Memory::updateSerial(const unsigned long cc) { if (!LINKCABLE) { - if (intreq.eventTime(SERIAL) != DISABLED_TIME) { + if (intreq.eventTime(SERIAL) != disabled_time) { if (intreq.eventTime(SERIAL) <= cc) { ioamhram[0x101] = (((ioamhram[0x101] + 1) << serialCnt) - 1) & 0xFF; ioamhram[0x102] &= 0x7F; - intreq.setEventTime(DISABLED_TIME); + intreq.setEventTime(disabled_time); intreq.flagIrq(8); } else { const int targetCnt = serialCntFrom(intreq.eventTime(SERIAL) - cc, ioamhram[0x102] & isCgb() * 2); @@ -126,10 +126,10 @@ void Memory::updateSerial(const unsigned long cc) { } } else { - if (intreq.eventTime(SERIAL) != DISABLED_TIME) { + if (intreq.eventTime(SERIAL) != disabled_time) { if (intreq.eventTime(SERIAL) <= cc) { linkClockTrigger = true; - intreq.setEventTime(DISABLED_TIME); + intreq.setEventTime(disabled_time); if (linkCallback) linkCallback(); } @@ -149,7 +149,7 @@ void Memory::updateIrqs(const unsigned long cc) { } unsigned long Memory::event(unsigned long cycleCounter) { - if (lastOamDmaUpdate != DISABLED_TIME) + if (lastOamDmaUpdate != disabled_time) updateOamDma(cycleCounter); switch (intreq.minEventId()) { @@ -158,15 +158,15 @@ unsigned long Memory::event(unsigned long cycleCounter) { PC = (PC + 1) & 0xFFFF; cycleCounter += 4; intreq.unhalt(); - intreq.setEventTime(DISABLED_TIME); + intreq.setEventTime(disabled_time); break; case END: - intreq.setEventTime(DISABLED_TIME - 1); + intreq.setEventTime(disabled_time - 1); - while (cycleCounter >= intreq.minEventTime() && intreq.eventTime(END) != DISABLED_TIME) + while (cycleCounter >= intreq.minEventTime() && intreq.eventTime(END) != disabled_time) cycleCounter = event(cycleCounter); - intreq.setEventTime(DISABLED_TIME); + intreq.setEventTime(disabled_time); break; case BLIT: @@ -176,8 +176,8 @@ unsigned long Memory::event(unsigned long cycleCounter) { if (lcden | blanklcd) { display.updateScreen(blanklcd, cycleCounter); - intreq.setEventTime(DISABLED_TIME); - intreq.setEventTime(DISABLED_TIME); + intreq.setEventTime(disabled_time); + intreq.setEventTime(disabled_time); while (cycleCounter >= intreq.minEventTime()) cycleCounter = event(cycleCounter); @@ -192,8 +192,8 @@ unsigned long Memory::event(unsigned long cycleCounter) { updateSerial(cycleCounter); break; case OAM: - intreq.setEventTime(lastOamDmaUpdate == DISABLED_TIME ? - static_cast(DISABLED_TIME) : intreq.eventTime(OAM) + 0xA0 * 4); + intreq.setEventTime(lastOamDmaUpdate == disabled_time ? + static_cast(disabled_time) : intreq.eventTime(OAM) + 0xA0 * 4); break; case DMA: { @@ -217,7 +217,7 @@ unsigned long Memory::event(unsigned long cycleCounter) { { unsigned long lOamDmaUpdate = lastOamDmaUpdate; - lastOamDmaUpdate = DISABLED_TIME; + lastOamDmaUpdate = disabled_time; while (length--) { const unsigned src = dmaSrc++ & 0xFFFF; @@ -236,7 +236,7 @@ unsigned long Memory::event(unsigned long cycleCounter) { ioamhram[src & 0xFF] = data; } else if (oamDmaPos == 0xA0) { endOamDma(lOamDmaUpdate - 1); - lOamDmaUpdate = DISABLED_TIME; + lOamDmaUpdate = disabled_time; } } @@ -253,7 +253,7 @@ unsigned long Memory::event(unsigned long cycleCounter) { ioamhram[0x155] = ((dmaLength / 0x10 - 0x1) & 0xFF) | (ioamhram[0x155] & 0x80); if ((ioamhram[0x155] & 0x80) && display.hdmaIsEnabled()) { - if (lastOamDmaUpdate != DISABLED_TIME) + if (lastOamDmaUpdate != disabled_time) updateOamDma(cycleCounter); display.disableHdma(cycleCounter); @@ -269,7 +269,7 @@ unsigned long Memory::event(unsigned long cycleCounter) { break; case INTERRUPTS: if (stopped) { - intreq.setEventTime(DISABLED_TIME); + intreq.setEventTime(disabled_time); break; } if (halted()) { @@ -277,7 +277,7 @@ unsigned long Memory::event(unsigned long cycleCounter) { cycleCounter += 4; intreq.unhalt(); - intreq.setEventTime(DISABLED_TIME); + intreq.setEventTime(disabled_time); } if (ime()) { @@ -347,17 +347,17 @@ unsigned long Memory::stop(unsigned long cycleCounter) { } static void decCycles(unsigned long &counter, const unsigned long dec) { - if (counter != DISABLED_TIME) + if (counter != disabled_time) counter -= dec; } void Memory::decEventCycles(const MemEventId eventId, const unsigned long dec) { - if (intreq.eventTime(eventId) != DISABLED_TIME) + if (intreq.eventTime(eventId) != disabled_time) intreq.setEventTime(eventId, intreq.eventTime(eventId) - dec); } unsigned long Memory::resetCounters(unsigned long cycleCounter) { - if (lastOamDmaUpdate != DISABLED_TIME) + if (lastOamDmaUpdate != disabled_time) updateOamDma(cycleCounter); updateIrqs(cycleCounter); @@ -424,7 +424,7 @@ void Memory::updateOamDma(const unsigned long cycleCounter) { ioamhram[oamDmaPos] = oamDmaSrc ? oamDmaSrc[oamDmaPos] : cart.rtcRead(); } else if (oamDmaPos == 0xA0) { endOamDma(lastOamDmaUpdate - 1); - lastOamDmaUpdate = DISABLED_TIME; + lastOamDmaUpdate = disabled_time; break; } } @@ -468,7 +468,7 @@ void Memory::endOamDma(const unsigned long cycleCounter) { } unsigned Memory::nontrivial_ff_read(const unsigned P, const unsigned long cycleCounter) { - if (lastOamDmaUpdate != DISABLED_TIME) + if (lastOamDmaUpdate != disabled_time) updateOamDma(cycleCounter); switch (P & 0x7F) { @@ -562,7 +562,7 @@ static bool isInOamDmaConflictArea(const OamDmaSrc oamDmaSrc, const unsigned add unsigned Memory::nontrivial_read(const unsigned P, const unsigned long cycleCounter) { if (P < 0xFF80) { - if (lastOamDmaUpdate != DISABLED_TIME) { + if (lastOamDmaUpdate != disabled_time) { updateOamDma(cycleCounter); if (isInOamDmaConflictArea(cart.oamDmaSrc(), P, isCgb()) && oamDmaPos < 0xA0) @@ -626,7 +626,7 @@ unsigned Memory::nontrivial_ff_peek(const unsigned P) { } void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned long cycleCounter) { - if (lastOamDmaUpdate != DISABLED_TIME) + if (lastOamDmaUpdate != disabled_time) updateOamDma(cycleCounter); switch (P & 0xFF) { @@ -645,7 +645,7 @@ void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned serialCnt = 8; intreq.setEventTime((data & 0x81) == 0x81 ? (data & isCgb() * 2 ? (cycleCounter & ~0x7ul) + 0x10 * 8 : (cycleCounter & ~0xFFul) + 0x200 * 8) - : static_cast(DISABLED_TIME)); + : static_cast(disabled_time)); data |= 0x7E - isCgb() * 2; break; @@ -865,7 +865,7 @@ void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned display.lycRegChange(data, cycleCounter); break; case 0x46: - if (lastOamDmaUpdate != DISABLED_TIME) + if (lastOamDmaUpdate != disabled_time) endOamDma(cycleCounter); lastOamDmaUpdate = cycleCounter; @@ -1020,7 +1020,7 @@ void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned } void Memory::nontrivial_write(const unsigned P, const unsigned data, const unsigned long cycleCounter) { - if (lastOamDmaUpdate != DISABLED_TIME) { + if (lastOamDmaUpdate != disabled_time) { updateOamDma(cycleCounter); if (isInOamDmaConflictArea(cart.oamDmaSrc(), P, isCgb()) && oamDmaPos < 0xA0) { diff --git a/libgambatte/src/memory.h b/libgambatte/src/memory.h index 5529fa71e9..4c3b3a2f7b 100644 --- a/libgambatte/src/memory.h +++ b/libgambatte/src/memory.h @@ -125,7 +125,7 @@ public: void setLayers(unsigned mask) { display.setLayers(mask); } - bool isActive() const { return intreq.eventTime(END) != DISABLED_TIME; } + bool isActive() const { return intreq.eventTime(END) != disabled_time; } long cyclesSinceBlit(const unsigned long cc) const { return cc < intreq.eventTime(BLIT) ? -1 : static_cast((cc - intreq.eventTime(BLIT)) >> isDoubleSpeed()); diff --git a/libgambatte/src/tima.cpp b/libgambatte/src/tima.cpp index 276385e782..b45fdfc987 100644 --- a/libgambatte/src/tima.cpp +++ b/libgambatte/src/tima.cpp @@ -25,7 +25,7 @@ namespace gambatte { Tima::Tima() : lastUpdate_(0), -tmatime_(DISABLED_TIME), +tmatime_(disabled_time), tima_(0), tma_(0), tac_(0) @@ -41,11 +41,11 @@ void Tima::loadState(const SaveState &state, const TimaInterruptRequester timaIr timaIrq.setNextIrqEventTime((tac_ & 4) ? - (tmatime_ != DISABLED_TIME && tmatime_ > state.cpu.cycleCounter + (tmatime_ != disabled_time && tmatime_ > state.cpu.cycleCounter ? tmatime_ : lastUpdate_ + ((256u - tima_) << timaClock[tac_ & 3]) + 3) : - static_cast(DISABLED_TIME) + static_cast(disabled_time) ); } @@ -59,7 +59,7 @@ void Tima::resetCc(const unsigned long oldCc, const unsigned long newCc, const T lastUpdate_ -= dec; timaIrq.setNextIrqEventTime(timaIrq.nextIrqEventTime() - dec); - if (tmatime_ != DISABLED_TIME) + if (tmatime_ != disabled_time) tmatime_ -= dec; } } @@ -71,7 +71,7 @@ void Tima::updateTima(const unsigned long cycleCounter) { if (cycleCounter >= tmatime_) { if (cycleCounter >= tmatime_ + 4) - tmatime_ = DISABLED_TIME; + tmatime_ = disabled_time; tima_ = tma_; } @@ -87,7 +87,7 @@ void Tima::updateTima(const unsigned long cycleCounter) { if (cycleCounter >= tmatime_) { if (cycleCounter >= tmatime_ + 4) - tmatime_ = DISABLED_TIME; + tmatime_ = disabled_time; tmp = tma_; } @@ -102,7 +102,7 @@ void Tima::setTima(const unsigned data, const unsigned long cycleCounter, const updateTima(cycleCounter); if (tmatime_ - cycleCounter < 4) - tmatime_ = DISABLED_TIME; + tmatime_ = disabled_time; timaIrq.setNextIrqEventTime(lastUpdate_ + ((256u - data) << timaClock[tac_ & 3]) + 3); } @@ -136,8 +136,8 @@ void Tima::setTac(const unsigned data, const unsigned long cycleCounter, const T updateTima(cycleCounter); - tmatime_ = DISABLED_TIME; - nextIrqEventTime = DISABLED_TIME; + tmatime_ = disabled_time; + nextIrqEventTime = disabled_time; } if (data & 4) { diff --git a/libgambatte/src/video.cpp b/libgambatte/src/video.cpp index a30bf98c01..555b5504ba 100644 --- a/libgambatte/src/video.cpp +++ b/libgambatte/src/video.cpp @@ -76,7 +76,7 @@ void LCD::setCgb(bool cgb) { static unsigned long mode2IrqSchedule(const unsigned statReg, const LyCounter &lyCounter, const unsigned long cycleCounter) { if (!(statReg & 0x20)) - return DISABLED_TIME; + return disabled_time; unsigned next = lyCounter.time() - cycleCounter; @@ -127,20 +127,20 @@ void LCD::loadState(const SaveState &state, const unsigned char *const oamram) { lycIrq.reschedule(ppu.lyCounter(), ppu.now()); eventTimes_.setm(state.ppu.pendingLcdstatIrq - ? ppu.now() + 1 : static_cast(DISABLED_TIME)); + ? ppu.now() + 1 : static_cast(disabled_time)); eventTimes_.setm(state.ppu.oldWy != state.mem.ioamhram.get()[0x14A] - ? ppu.now() + 1 : static_cast(DISABLED_TIME)); + ? ppu.now() + 1 : static_cast(disabled_time)); eventTimes_.set(ppu.lyCounter().time()); eventTimes_.setm(SpriteMapper::schedule(ppu.lyCounter(), ppu.now())); eventTimes_.setm(lycIrq.time()); eventTimes_.setm(ppu.lyCounter().nextFrameCycle(144 * 456, ppu.now())); eventTimes_.setm(mode2IrqSchedule(statReg, ppu.lyCounter(), ppu.now())); - eventTimes_.setm((statReg & 0x08) ? ppu.now() + state.ppu.nextM0Irq : static_cast(DISABLED_TIME)); + eventTimes_.setm((statReg & 0x08) ? ppu.now() + state.ppu.nextM0Irq : static_cast(disabled_time)); eventTimes_.setm(state.mem.hdmaTransfer ? nextHdmaTime(ppu.lastM0Time(), nextM0Time_.predictedNextM0Time(), ppu.now(), isDoubleSpeed()) - : static_cast(DISABLED_TIME)); + : static_cast(disabled_time)); } else for (int i = 0; i < NUM_MEM_EVENTS; ++i) - eventTimes_.set(MemEvent(i), DISABLED_TIME); + eventTimes_.set(MemEvent(i), disabled_time); refreshPalettes(); } @@ -218,7 +218,7 @@ void LCD::resetCc(const unsigned long oldCc, const unsigned long newCc) { lycIrq.reschedule(ppu.lyCounter(), newCc); for (int i = 0; i < NUM_MEM_EVENTS; ++i) { - if (eventTimes_(MemEvent(i)) != DISABLED_TIME) + if (eventTimes_(MemEvent(i)) != disabled_time) eventTimes_.set(MemEvent(i), eventTimes_(MemEvent(i)) - dec); } @@ -240,7 +240,7 @@ void LCD::speedChange(const unsigned long cycleCounter) { eventTimes_.setm(ppu.lyCounter().nextFrameCycle(144 * 456, cycleCounter)); eventTimes_.setm(mode2IrqSchedule(statReg, ppu.lyCounter(), cycleCounter)); - if (eventTimes_(MODE0_IRQ) != DISABLED_TIME && eventTimes_(MODE0_IRQ) - cycleCounter > 1) + if (eventTimes_(MODE0_IRQ) != disabled_time && eventTimes_(MODE0_IRQ) - cycleCounter > 1) eventTimes_.setm(m0IrqTimeFromXpos166Time(ppu.predictedNextXposTime(166), ppu.cgb(), isDoubleSpeed())); if (hdmaIsEnabled() && eventTimes_(HDMA_REQ) - cycleCounter > 1) { @@ -294,7 +294,7 @@ void LCD::disableHdma(const unsigned long cycleCounter) { if (cycleCounter >= eventTimes_.nextEventTime()) update(cycleCounter); - eventTimes_.setm(DISABLED_TIME); + eventTimes_.setm(disabled_time); } bool LCD::vramAccessible(const unsigned long cycleCounter) { @@ -365,12 +365,12 @@ bool LCD::oamWritable(const unsigned long cycleCounter) { void LCD::mode3CyclesChange() { nextM0Time_.invalidatePredictedNextM0Time(); - if (eventTimes_(MODE0_IRQ) != DISABLED_TIME + if (eventTimes_(MODE0_IRQ) != disabled_time && eventTimes_(MODE0_IRQ) > m0IrqTimeFromXpos166Time(ppu.now(), ppu.cgb(), isDoubleSpeed())) { eventTimes_.setm(m0IrqTimeFromXpos166Time(ppu.predictedNextXposTime(166), ppu.cgb(), isDoubleSpeed())); } - if (eventTimes_(HDMA_REQ) != DISABLED_TIME + if (eventTimes_(HDMA_REQ) != disabled_time && eventTimes_(HDMA_REQ) > hdmaTimeFromM0Time(ppu.lastM0Time(), isDoubleSpeed())) { nextM0Time_.predictNextM0Time(ppu); eventTimes_.setm(hdmaTimeFromM0Time(nextM0Time_.predictedNextM0Time(), isDoubleSpeed())); @@ -456,7 +456,7 @@ void LCD::lcdcChange(const unsigned data, const unsigned long cycleCounter) { nextM0Time_.predictedNextM0Time(), cycleCounter, isDoubleSpeed())); } } else for (int i = 0; i < NUM_MEM_EVENTS; ++i) - eventTimes_.set(MemEvent(i), DISABLED_TIME); + eventTimes_.set(MemEvent(i), disabled_time); } else if (data & 0x80) { if (ppu.cgb()) { ppu.setLcdc((oldLcdc & ~0x14) | (data & 0x14), cycleCounter); @@ -557,7 +557,7 @@ void LCD::lcdstatChange(unsigned const data, unsigned long const cycleCounter) { } } - if ((data & 0x08) && eventTimes_(MODE0_IRQ) == DISABLED_TIME) { + if ((data & 0x08) && eventTimes_(MODE0_IRQ) == disabled_time) { update(cycleCounter); eventTimes_.setm(m0IrqTimeFromXpos166Time(ppu.predictedNextXposTime(166), ppu.cgb(), isDoubleSpeed())); } @@ -712,18 +712,18 @@ inline void LCD::event() { eventTimes_.setm(statReg & 0x08 ? m0IrqTimeFromXpos166Time(ppu.predictedNextXposTime(166), ppu.cgb(), isDoubleSpeed()) - : static_cast(DISABLED_TIME)); + : static_cast(disabled_time)); break; case ONESHOT_LCDSTATIRQ: eventTimes_.flagIrq(2); - eventTimes_.setm(DISABLED_TIME); + eventTimes_.setm(disabled_time); break; case ONESHOT_UPDATEWY2: ppu.updateWy2(); mode3CyclesChange(); - eventTimes_.setm(DISABLED_TIME); + eventTimes_.setm(disabled_time); break; } diff --git a/libgambatte/src/video.h b/libgambatte/src/video.h index ebfc14b7df..2bf3f577c2 100644 --- a/libgambatte/src/video.h +++ b/libgambatte/src/video.h @@ -21,6 +21,7 @@ #include "video/ppu.h" #include "video/lyc_irq.h" +#include "video/m0_irq.h" #include "video/next_m0_time.h" #include "interruptrequester.h" #include "minkeeper.h" @@ -39,53 +40,6 @@ public: void setNextEventTime(const unsigned long time) const { intreq->setEventTime

+WVvQQ^f*hm@J@iYVNXy89`O$uVh<8f;|ko}Q3T?(W$E zOC2lrc7ec|?yA*&u+6FXh{fho9=ZD2mcSWqe$T)7&GU$T-wtT+=)M0JI(V8Q_Rq#D zKn=E8-N$tJLK0tf5*CnFhu()2vhYxU|7_f%G{&~L2y2_``8VYDp(%h`hvOlNY(9+j z$Z__tmWD+{I;o<)W69)`5%lN1$l{Z2=pXu$xyPa)&QWr#JwQ8h^7ANQ9smBkFO4%u zCt2u@+K~{8%2Ci8b=%O114zlS)*F6132=eboSFooWZY>Jw(Cz1g7c*2bS#7r44A6`mI6DZMlb~}qkpB4`hi%*QLm-%>pEp1d`RsfT2wHYQi==pA3j{iDU1$Oj zNPhm}O=wD5T^xb!oQplNJ#;Y#T9fpzI?%cRlJ?~|2yk5faw9+l@;ZV)Df?;=wts%L z0*;ZQuLpo1dGPgEXhQmY^8kE__VQS4S6m(kjY;k0Mc8Ivndb94?nXaPe<#VjXr!6O z#CUZlFjrthD{25B@$ z-9J=M<4#lecVScc1kGaQoREtNLs7$&dTqgi%_t*06fI0dD$5mYc!0hCq7L=5wEwqz z7GLZt@Bgob7Y6WigL*iy((@lQDm`8)Xv`TpQi_(Q!;h_xDK9 z-@ZrirA~Pq?$46qD&;PEL{!&XN6tn%H%npfr7|e$&|SYoDiY6Hj&jK=N>i_E=(N*h z^Yujdf;7FMhE=534W54Ao21`}p@W-~gEvCG*PN;~w;wVv>Aa zg>CEVvW|M@#xe`=VBy72nXTuKQz|c-$sOwXd5jO`%EWc>RsR)wci=M8gQ(OAR=1NG zm2Y>WC`cQWQTYxMR9c^`^hYsiJ#h|I{T!BtNlKw%lH&SF-a8)S1Jvg+uT!q(|2P$z zc@X`wt7BeRxm0h(ZA7U|xRtN=HXdzk`kRfg7dA3p_QbX9A5A1Nu8DuMaFs-U7i($# z&jWm2Pow^FJ9V-ZV@{i-u>Upy(>mTKnknul< z%DuCd^gjsvBPjkJ#h4K9B8DIwo$&}$0N_c}7zj@f50Cc>ics(=VlU!L#3O{43zZ*{ z-ZvljOnX2vyAb0ZQp`p~Fq()*oWVz_7*&55YB7ms24JBWiJ6KC8;@ND9O0^tpbzij zOTWjCIp22$nyDsRe^@Ze_9Mk?LYzZ9L=3V%!osEct8lIJQPB*4%xsH&I$;AEr>p_^ z-IogsE~fgwIFqNWwDK$ArDU)FD0ED|6#&hCrWjjnZG*1aHaHS5uDn80cw75Qk)e=$ ze>=YIPZX1h=!Bz-8t8M_D^Pg#v}5?4x+W6=7+bgCO*(K3DO!JkHwRR zl`qDi%;A1l3hqOdL7B$=j=4iUj5H`Sx!)DD4;e!hIJ|!w23^q1LpJLfo#tJsn0VMW z>zN1A%J7GwS$kxKxpn9(iA#?6UkOhAiY&YyktEwiu}K3?B&0Sj3XPz=Zb$Caym)C5UEbVBQ?yTptUMgCW_ubOhvk zgipe?BFu=62v5YfiNI7MNTO3e1}Z&Zy7S36nBftg0!$eq50Q-MhfpD!B7T|TT=*W$ zZ3RV6j|ITJHfR|z0f=x!2ZS17L^vAoHuh%3FvK{-2Z&U}3wb)y3lU2YMTm8XEz6vF zRv69Avjw!U$^P{guv^9f zb8|5;iX;W;j3xCnlYkjqP)}FQ0Vcr)%yLBsX$I*+q$6hoQ(?sui?kW}Rr#2~NK;5h zB8@*hsYs+#kY0wgDjRk4fLVYv9(5)LX-hV~q+|e7or9AjT`?c&Tr34h-^|36MLHAd z8042AJs)ZFJe&-jwjo`Rj(nu^kuLccPK>njAA<_{8Aw}^u10YTF;-tdH-&h-^=+UU0vnC$<+Ip;^n^62~;t_WdnMY&NDL^G5( zPK<~_Bp{9k(o6>GRJ~^J_ynG`Zi<0IRE1Mab}-Fc2*xR!p*qqG@~Z>v#;4HS3)weA z?ZcnKh+yR23JGcJ=%{&Ib_@7Hjh+YT=e+e>leR{l*`653EF%)G(|4XKP z;1;2}@;fs5KQeoZXD}Q*ooUY?-zRzpP}MsDFHxZIilYX`^@y8W7M&bX@;Jsh;QOTQ z6<@PK_Hde>(0U_KT~a5`N{ycUA=-71O2EFJH;Xdwa;~T8reINWw9B*!V^haQPoJf9 z_^x>V6g_F+&zUo4!npU5?}>lWDHlPPX{qC*-|5y3Ny_JMyy5tpnxcGP=Ec|RVQHZ7 zCuIHD_+)i?ePA@#xN*Eh>yuura zJms^wfhQ=u8v9aDdXcA$Rb*-FAUl5EHuA^BYvV;il?yPjp6{!Cu`6HW^H64w@uGvh zwgU6@R{L--Is(e=bChnoeWe%OQuPTiBM^#lNfI($>NM-{rg6tIN0ZP-EKPb2mdL z`$MXIas(Y^`CM~N(_Y(EYtt5Lr|OsJpXhbcTir+Nn5II(v#0IFXaN z7_JA`i|fOgxp;0Um(1mGtGS&P?lMR5?Rhgli!b3%@YQ^X5F;cC1wy58Q}9u%)I-%7 z>N52Ob&Wbw(@T@0DbiGFTpC6zY7?|M+6wJ8?Q0?{juaP(JH#8JkB-;%*NxZZ>o)5S z>b}uE)-}}|^po_<^@sHT)`v>5(o|`+bW-|7YAyGbXUmpS`3w0Exr1SlA=6N9xMJ`! zvc^%yT;nd|_eMXH&NR`q)Kq2qQCS@YR3WylRDD%*Ra;e8RiSJrb|^cJox;v#bJ>sB zZEOX*ldWX;u{-!<{8#)P{uka$Xd%2Q$UU{M&^{471>aW!I)X&t7HQ}1Bnr@oq+D+Qev|nm#w1MIPF*VTPAqck6C=4tXY>onz>y_(aS>zYTJ8jY7WQ2Pc(>>ceeZJBnD z_7q0#k@gR5m}u!NnnW{(Z@Op^^Tjn{g?LCjD_#}vi+_rYt__AWM)xn>a@{7~9^GkO zCk$pk{b+rXK2vYguhehV@6(^rHTUed@F78|wS&U)A245KViH zfDs(18LyeDS)f_2xuSWX`Ca3y4cB(j_S25iPSIv)^R+9qmaW?T+H=~g+I!ksZ4)t4 z?@b9EDq_Zt}4Dz`Gu8=F@ zin$Ulicb-<#X_-6+$Ww9uZs`FXX5L+7P@x05j}K+bQ5(`(H)y^jc&VcFXq5C-6P!} zx+eMveOJAte^)<3|AF43C;BS=CH-yvGkp^&Lh33Rq`uM^DFqL9uCz)jmkvm$q_3sB zQjKJ3BnQiFWv%>f`ppQGjWawl0O3$iG`Eq`J-Z1~AUrGWeUU_a~4C-TXB3ZIHc zeJ-EDXY+PG(IQS3?}>MG-g>Qmh`vx?sxQ|c*H4h{Nu%YKcqC<`XzF8HW7=z?GU`Kn ziDkKs9PkTpcpD*Bh*RgNsRE#C(aCRE;C#3sE|TknC8rzLn~UQ{auc}^xtUxBm&+~V zR^ixkZnSo~HdAZWF4tPV)dq`#D2lQeBlZw`iG6VQ>96fsqtCCgd&K;2F)smBA=GXxkr7-t);##qyQ zQ?cp1>4vGsWWlVd0807t_fXkYB^bEdsv1?Dsw*q9-PrzE-zTxXxg;)?vvKRV16*UC z;d#D0|1Lk2|Bx@^Kjjbe=lHAqP5v(bGhf5E6rzOof+Y0Eh`cY%!HU07$QPCgMZy|k zqp$^Q|88NQa9B7YoD(hySN(;%coop4bD!ayf%L(%0bs0I`MLI{*Lx delta 7407 zcmc&&d0bRg`@hd+SA>f!;({Q!XL^^Jxik0PnE?~CT$;iKH^j6=+)%;Lq#48o7*OHi8ah3dS%_P$R>2q%)28kHotJRJ4`nfyXbJW$@ozE6G$URbaW{)2X^pvUO(uC$< zAiqrzp^LNY#Iew!BksNk^$X{E`cPDg4M~gpf>%5;nzEmCr|ov`o74fI6S}I+8Y1CIo4KvQG_3p<*d26=Ql+<6J$(3qV6QOpO77 z9GN-_+gH;k15Z|`hhqC_dZ!Q`S3%+N7tT}LR9wOmp8S#?jpERZmB2Z-Wq5nxl6THf zX#Y*9XMY$*Q|_!RCuB7Su#zSpMM1f<>qp%I(w&p78)$f!G@I26Mmm#cjlyGe9?bFf zfK}w!+!nCUd3|mLz+UIV+)z*0L^c(C0_(}(!ieTeFmCa_Ba(*NuMI!oNhK7{&uxM! z5?SqBQ0NcfA}iDhnS#*zXiE%8Pmx&gJ)m^ZicB^oK zYM`)bs4^eb1$if^K?yQIgyuD!aQD(3USB=}Qpl#|?_mD_vAi2p zkj^Wn!V*%lLcuh-xZ-_)#iaYHb-3N@tA+t_zE<)OT4qyJ;Es87agBuw)OF*5CFC6T zw74&2lXprDkV6Ve!@)*2lpcgxBy~*-jP#r}M&ty28cNQu*#skrZS7J>Ce7DXK_)r6 zZVHSb#`R^vNw{R!ux2zmZiQ`vTc=99lJ*_r?rTyXjLHLbjF-!?tnxG;C*;C-@G=inF>&I89(nFu7kI+7cOc zu3B`4o&&`!o|4kX@g4K|apjy09m0ac{=# z&7b!fx_Ig?TknhR&d#iN(rrgrQ&f01(|&a(hboG2rtf%2V{(k$sf7ZvdS_3tlj}RT zL6LLWuC5R~t-fk?A6V#8*NDaDY#zC=yCOKFp5OC1zgZq}?AZn#oNw-ZM2AjR#euW2 z3Qz+J?e*7m$Q;tY<_OFtt@n?CNo4N+w*qtUh%%VM`Af03x&Qc%#7{YV*}tMsXZ1C&4}sv zSZL-fJ01!UO7@)?0AVELH)!vPK!wDPgg)w=TD~t z0Gg6tzIp|MNb560v7K?IC${^~)`Tj)W$p&$(OBn26^`V!Q18yU>U{IX@5E+22j^^~2NX=NafC*=I~N z(}DVx@+xTC0jQh{FF9u;J+3`cX^&IBM%d0AWK`VVwz2`m+o}aGD zz3zW(M!skhQ*U$sWt&Ma+XyxGQx^9S59>>_W0HaGS@u%Z^mDo>~Bn)t6GV=?*hTK{(UDJC1y1+AAprB7n7Lgh8k&LP+90^XzW(>X5mP@HEJ z4NM+8Eu{0_xq{$nYLHL7#ekL#-xYgDJHAJD5E-4{aAH-n=#f z=5ij>89vpji`(sT9(y3In%{1o^Vkz<)%11~p=KA_%zwPCIVqjn$dwulI&c_+A^B9WP*75FYSPbJG zzpsYtknH-t6z`Yc0zA~?ss1T8hkFg=`Td1wt`18bGuFa!*z93&=&sVSib@CAkoovBTV*4`STOUx{WXvHoBT@f-vx=TGGsJ%F(F|+B;&( zppzrB4D!7h2Mbrglwh#APp z#XcH)Z-n}^-sUK&01-AizO4X*R3m{_E(vvH7S z1?K)doE+(@Y@~D5+e}ecY?yXPXCobp{8FT6A#KUR$#BtyNEgjSKGFq9m(IY6kyd|8 zv&gq0ZAbb(@?(+CLptq4+zitBNGFegK*m) zKXA>s2RRpb#|?6f--N zW=@CTl%c4OG=u#6jKlO0nt37nO1NXlLl_#0+$$}CIUa@@ZE%Ot81@*)(TC6>NHvH8 zW+jSLN6Bp+^dp$$*8zQYLZ6)-Gaf-?C>mM1G)#HES%Z_RF0QSQAk1Unv;T9{gy@Cv z#a}$WbVGVPlldb*1Ytp$<-cV3~Xu2tc;?ESS>y*(W(?_jjOe_!Ybkj^`=)?(@{JdQHJCdeC5giaBqAy}B!iI1loQUIys|b%MI*ba#$aF*WLL?)y5Dr(o553xBRPxgb zhD)d1YSpU2zlmPpO~gW^Ag|yBUM?1;WaKS>Mz*e3MqOV@izoj3g zFVL6kKhvMn-_~SU`78M^xszfYpkynR z%6E#FkuwfA<{3XV{%CAsGML^oEi~1b?wMMf`FLovmi~u-o`A_;2`Y{3G5=Xd%2JC_-N$Nth(qgoVNyp-R{<+z}Mb2+cUn z2bvsBfo6^76RYNP%{Q7Gn#Y>P+6ZkoZJf4Pw@J5K_qDE87c9Olju&m>O7Wlw`Z(NK zwf?C7y8ekCSHS>v6}wg#*h`u|+Tq%X+K;sP+BMor?Jn&x?IrCUZLQWz7p&`m5qm>7 zSXZXosXK~MyQBL{*GhaaCDo^`5d6$``?u}a)8o)9mHH^sk2MjwsgjMaarFV=6; z@6;dDcfnxxGb9;O4cUf5!*auB!ydzNLu09x^r~c%-jb4~0u1lRQjK(6`boMcJ&^+B zD4CN@@+3J=ULjY?pUJ&V!%eBC4^4K{Qd7BUm+6@4lIdrw>2Fh@Im*nLyPFftqcPC? z%*V`^&3Dazs>u`u)E4ZTu#qgszQzt<$FS2`JG+YgnBC8Q&9>qyHG4ECG?z6uHNROqE5u6iGx00&iP&48rEhI$iM!}7C&;7a40*2Xkax>x7tmFmBxHiq?uP2%K{aSou2)I9mbUli*)M^Q{)*k6$_O5t_l?F3Wa}hs2iG%x1fM4 z;uc%ErQAxcl#Aih#M$Cvu}s_}9v3f(x5UR{BYg{fdpw99`T_d)^pkNph58cxR{bu_ zf{Xe)`oHu6hDbv!iv2Y990Nr%u`D?I16a!0v~%*q;BFTXB-tQ=4tn5ZBxg}mn#i%#+F_mDN zP&S}i;Udnm9JihWeh!*P3-LmNCRalh0TqI8qjM~9zFY_w#dX1w6UV*DC2+&I_qd7N z2b_(|;}&r%(6*9G(oNN6>+HH>-S@gsQ6uU_t0KmVJ;YvOA3Xj3Vj^CNWUPiMc=6K2 zbTLz$i5GCTm@6*R?={>pcwxq~QV%In8ZTL;MbZYTS~@0uk5$uCZYFn@jj}}^B~Qbg zUnZB!JLMztck)g7u^ga8D>~&(B}tj0%u*I9<;ouAuyRqkVO4%te2uM)ESA(nV+xj2 zo3Y5a#<=ZVeEn-)*n^_n8IeUt|$lhRoWvz8= zV+>q3t~)mfYjZY+YAv^mJIP((?s9){jris~$M@ui;`wFsh5RzUoZro#;D5k^O$nhw zXTdD=7sd)72}CFp_6lc&8^Uiwkfyyx)>t%Wbbsoai_utYG#J|<7}@tRu2aP67}I&; ze6d(uX%*Lrn?KUXLa770s* z5@Eeifz^M9utzu`92QOrXN3#Gb$l1lnx-C_FpZ1pK<5G!xt4XLr$b;k-lG0;qMRu& SmAA{)u6CX2i-7NK=KlaQT}qt* From 7b518d0cf75a2f17fb27c9e826db058292e15501 Mon Sep 17 00:00:00 2001 From: MrWint Date: Sun, 26 May 2019 00:13:34 +0200 Subject: [PATCH 17/38] Update InterruptRequester --- libgambatte/src/interruptrequester.cpp | 118 +++++++++++++---------- libgambatte/src/interruptrequester.h | 125 +++++++++++++------------ libgambatte/src/mem/cartridge.cpp | 2 + output/dll/libgambatte.dll | Bin 142336 -> 142336 bytes 4 files changed, 134 insertions(+), 111 deletions(-) diff --git a/libgambatte/src/interruptrequester.cpp b/libgambatte/src/interruptrequester.cpp index 79dc60836b..af37ed584e 100644 --- a/libgambatte/src/interruptrequester.cpp +++ b/libgambatte/src/interruptrequester.cpp @@ -1,106 +1,120 @@ -/*************************************************************************** - * Copyright (C) 2010 by Sindre Aamås * - * aamas@stud.ntnu.no * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License version 2 as * - * published by the Free Software Foundation. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License version 2 for more details. * - * * - * You should have received a copy of the GNU General Public License * - * version 2 along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ +// +// Copyright (C) 2010 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + #include "interruptrequester.h" #include "savestate.h" namespace gambatte { -InterruptRequester::InterruptRequester() : minIntTime(0), ifreg_(0), iereg_(0) {} +InterruptRequester::InterruptRequester() +: eventTimes_(disabled_time) +, minIntTime_(0) +, ifreg_(0) +, iereg_(0) +{ +} -void InterruptRequester::loadState(const SaveState &state) { - minIntTime = state.mem.minIntTime; +void InterruptRequester::loadState(SaveState const &state) { + minIntTime_ = state.mem.minIntTime; ifreg_ = state.mem.ioamhram.get()[0x10F]; iereg_ = state.mem.ioamhram.get()[0x1FF] & 0x1F; - intFlags.set(state.mem.IME, state.mem.halted); + intFlags_.set(state.mem.IME, state.mem.halted); - eventTimes.setValue(intFlags.imeOrHalted() && pendingIrqs() ? minIntTime : static_cast(disabled_time)); + eventTimes_.setValue(intFlags_.imeOrHalted() && pendingIrqs() + ? minIntTime_ + : static_cast(disabled_time)); } -void InterruptRequester::resetCc(const unsigned long oldCc, const unsigned long newCc) { - minIntTime = minIntTime < oldCc ? 0 : minIntTime - (oldCc - newCc); +void InterruptRequester::resetCc(unsigned long oldCc, unsigned long newCc) { + minIntTime_ = minIntTime_ < oldCc ? 0 : minIntTime_ - (oldCc - newCc); - if (eventTimes.value(intevent_interrupts) != disabled_time) - eventTimes.setValue(minIntTime); + if (eventTimes_.value(intevent_interrupts) != disabled_time) + eventTimes_.setValue(minIntTime_); } -void InterruptRequester::ei(const unsigned long cc) { - intFlags.setIme(); - minIntTime = cc + 1; +void InterruptRequester::ei(unsigned long cc) { + intFlags_.setIme(); + minIntTime_ = cc + 1; if (pendingIrqs()) - eventTimes.setValue(minIntTime); + eventTimes_.setValue(minIntTime_); } void InterruptRequester::di() { - intFlags.unsetIme(); + intFlags_.unsetIme(); - if (!intFlags.imeOrHalted()) - eventTimes.setValue(disabled_time); + if (!intFlags_.imeOrHalted()) + eventTimes_.setValue(disabled_time); } void InterruptRequester::halt() { - intFlags.setHalted(); + intFlags_.setHalted(); if (pendingIrqs()) - eventTimes.setValue(minIntTime); + eventTimes_.setValue(minIntTime_); } void InterruptRequester::unhalt() { - intFlags.unsetHalted(); + intFlags_.unsetHalted(); - if (!intFlags.imeOrHalted()) - eventTimes.setValue(disabled_time); + if (!intFlags_.imeOrHalted()) + eventTimes_.setValue(disabled_time); } -void InterruptRequester::flagIrq(const unsigned bit) { +void InterruptRequester::flagIrq(unsigned bit) { ifreg_ |= bit; - if (intFlags.imeOrHalted() && pendingIrqs()) - eventTimes.setValue(minIntTime); + if (intFlags_.imeOrHalted() && pendingIrqs()) + eventTimes_.setValue(minIntTime_); } -void InterruptRequester::ackIrq(const unsigned bit) { +void InterruptRequester::ackIrq(unsigned bit) { ifreg_ ^= bit; di(); } -void InterruptRequester::setIereg(const unsigned iereg) { +void InterruptRequester::setIereg(unsigned iereg) { iereg_ = iereg & 0x1F; - if (intFlags.imeOrHalted()) - eventTimes.setValue(pendingIrqs() ? minIntTime : static_cast(disabled_time)); + if (intFlags_.imeOrHalted()) { + eventTimes_.setValue(pendingIrqs() + ? minIntTime_ + : static_cast(disabled_time)); + } } -void InterruptRequester::setIfreg(const unsigned ifreg) { +void InterruptRequester::setIfreg(unsigned ifreg) { ifreg_ = ifreg; - if (intFlags.imeOrHalted()) - eventTimes.setValue(pendingIrqs() ? minIntTime : static_cast(disabled_time)); + if (intFlags_.imeOrHalted()) { + eventTimes_.setValue(pendingIrqs() + ? minIntTime_ + : static_cast(disabled_time)); + } } SYNCFUNC(InterruptRequester) { - SSS(eventTimes); - NSS(minIntTime); + SSS(eventTimes_); + NSS(minIntTime_); NSS(ifreg_); NSS(iereg_); - NSS(intFlags.flags_); + NSS(intFlags_.flags_); } } diff --git a/libgambatte/src/interruptrequester.h b/libgambatte/src/interruptrequester.h index b3a43e6849..cb54265078 100644 --- a/libgambatte/src/interruptrequester.h +++ b/libgambatte/src/interruptrequester.h @@ -1,21 +1,21 @@ -/*************************************************************************** - * Copyright (C) 2010 by Sindre Aamås * - * aamas@stud.ntnu.no * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License version 2 as * - * published by the Free Software Foundation. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License version 2 for more details. * - * * - * You should have received a copy of the GNU General Public License * - * version 2 along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ +// +// Copyright (C) 2010 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + #ifndef INTERRUPT_REQUESTER_H #define INTERRUPT_REQUESTER_H @@ -24,49 +24,29 @@ #include "newstate.h" namespace gambatte { + struct SaveState; -enum IntEventId { intevent_unhalt, intevent_end, intevent_blit, intevent_serial, intevent_oam, intevent_dma, intevent_tima, intevent_video, intevent_interrupts }; + +enum IntEventId { intevent_unhalt, + intevent_end, + intevent_blit, + intevent_serial, + intevent_oam, + intevent_dma, + intevent_tima, + intevent_video, + intevent_interrupts, intevent_last = intevent_interrupts }; class InterruptRequester { - MinKeeper eventTimes; - unsigned long minIntTime; - unsigned ifreg_; - unsigned iereg_; - - class IntFlags { - friend class InterruptRequester; - unsigned char flags_; - enum { IME_MASK = 1, HALTED_MASK = 2 }; - - public: - IntFlags() : flags_(0) {} - - bool ime() const { return flags_ & IME_MASK; } - bool halted() const { return flags_ & HALTED_MASK; } - bool imeOrHalted() const { return flags_; } - - void setIme() { flags_ |= IME_MASK; } - void unsetIme() { flags_ &= ~IME_MASK; } - - void setHalted() { flags_ |= HALTED_MASK; } - void unsetHalted() { flags_ &= ~HALTED_MASK; } - - void set(const bool ime, const bool halted) { flags_ = halted * HALTED_MASK + ime * IME_MASK; } - } intFlags; - public: InterruptRequester(); - - void loadState(const SaveState &); - + void loadState(SaveState const &); void resetCc(unsigned long oldCc, unsigned long newCc); - unsigned ifreg() const { return ifreg_; } unsigned iereg() const { return iereg_; } unsigned pendingIrqs() const { return ifreg_ & iereg_; } - bool ime() const { return intFlags.ime(); } - bool halted() const { return intFlags.halted(); } - + bool ime() const { return intFlags_.ime(); } + bool halted() const { return intFlags_.halted(); } void ei(unsigned long cc); void di(); void halt(); @@ -76,20 +56,47 @@ public: void setIereg(unsigned iereg); void setIfreg(unsigned ifreg); - IntEventId minEventId() const { return static_cast(eventTimes.min()); } - unsigned long minEventTime() const { return eventTimes.minValue(); } - template void setEventTime(unsigned long value) { eventTimes.setValue(value); } - void setEventTime(const IntEventId id, unsigned long value) { eventTimes.setValue(id, value); } - unsigned long eventTime(IntEventId id) const { return eventTimes.value(id); } + IntEventId minEventId() const { return static_cast(eventTimes_.min()); } + unsigned long minEventTime() const { return eventTimes_.minValue(); } + template void setEventTime(unsigned long value) { eventTimes_.setValue(value); } + void setEventTime(IntEventId id, unsigned long value) { eventTimes_.setValue(id, value); } + unsigned long eventTime(IntEventId id) const { return eventTimes_.value(id); } +private: + class IntFlags { + friend class InterruptRequester; + public: + IntFlags() : flags_(0) {} + bool ime() const { return flags_ & flag_ime; } + bool halted() const { return flags_ & flag_halted; } + bool imeOrHalted() const { return flags_; } + void setIme() { flags_ |= flag_ime; } + void unsetIme() { flags_ &= ~flag_ime; } + void setHalted() { flags_ |= flag_halted; } + void unsetHalted() { flags_ &= ~flag_halted; } + void set(bool ime, bool halted) { flags_ = halted * flag_halted + ime * flag_ime; } + + private: + unsigned char flags_; + enum { flag_ime = 1, flag_halted = 2 }; + }; + + MinKeeper eventTimes_; + unsigned long minIntTime_; + unsigned ifreg_; + unsigned iereg_; + IntFlags intFlags_; + + +public: templatevoid SyncState(NewState *ns); }; inline void flagHdmaReq(InterruptRequester &intreq) { intreq.setEventTime(0); } inline void flagGdmaReq(InterruptRequester &intreq) { intreq.setEventTime(1); } inline void ackDmaReq(InterruptRequester &intreq) { intreq.setEventTime(disabled_time); } -inline bool hdmaReqFlagged(const InterruptRequester &intreq) { return intreq.eventTime(intevent_dma) == 0; } -inline bool gdmaReqFlagged(const InterruptRequester &intreq) { return intreq.eventTime(intevent_dma) == 1; } +inline bool hdmaReqFlagged(InterruptRequester const &intreq) { return intreq.eventTime(intevent_dma) == 0; } +inline bool gdmaReqFlagged(InterruptRequester const &intreq) { return intreq.eventTime(intevent_dma) == 1; } } diff --git a/libgambatte/src/mem/cartridge.cpp b/libgambatte/src/mem/cartridge.cpp index e1f570fdcc..911036f94b 100644 --- a/libgambatte/src/mem/cartridge.cpp +++ b/libgambatte/src/mem/cartridge.cpp @@ -593,6 +593,8 @@ LoadRes Cartridge::loadROM(const char *romfiledata, unsigned romfilelength, bool case 0x1C: case 0x1D: case 0x1E: type = MBC5; break; + case 0x20: return LOADRES_UNSUPPORTED_MBC_MBC6; + case 0x22: return LOADRES_UNSUPPORTED_MBC_MBC7; case 0xFC: return LOADRES_UNSUPPORTED_MBC_POCKET_CAMERA; case 0xFD: return LOADRES_UNSUPPORTED_MBC_TAMA5; case 0xFE: return LOADRES_UNSUPPORTED_MBC_HUC3; diff --git a/output/dll/libgambatte.dll b/output/dll/libgambatte.dll index 47b5225b7dd7135e261623fdadb257bfd0fa8772..11be420021c5186d5f4c848544cbcc838dfb4fa9 100644 GIT binary patch delta 15525 zcmb_@30zcF`~N+c0S4JdK|w_YCB?Pb@9cxhqUAD%Ya(D-UJVr!ihQv_L4<}$^k`#7 zMlQLbSwjt#7AkL=8e*xLOHpoPZj|`Gni~J-+&e41z5m|N=f~%BW}fps&w0){&vwo| zcPKt1EIuRLBSL+D*mo_Ha{aYXbV4YKh@{>_6jjcH|K5$IXfeviyU=uc=VsiA-hlG$ z+v%w&xTX$?ob_*Ep`bUtXA@2mJd2*hm4dS<8P5*Pi2dgqh+{>*XG=|L; zOmWUwYyT#220~-8I5-sr+g}Q{qPU7P6jj=g-_Nq521cE1agf0Ww@x-?IJ@9JArpIb z{|kl;lXK$+Top2b-uWHA7-FQU5S-Yl0wv+yo!*DftkBH~(IK?Rf+|v5EyD#Bscn|w z!iv;;mf>O6u{SDGA3FbW3-=3K@c-kwe$#Fb8-zqAD_M!qGWKD%?CUJ27~c%vjS}qD z5l;so2LHRWk^Xl%R&_DZj&gfpm)IxhN`rlMW>y6K@D2NKa|a@NVFB)yJ1TBK0k`5o zmJ+gJi`~88Db5eH_NBSg5dGmIe12Xm(&4-F7`k+@1F?Q)8il(TNTd3f;r9jA1A~3MEjC`4#y>N6vHR^Bw zpdcs^^|9A4*3se4tuTP4ts6}_FD$X6C7lp`!i>XT?Sqo-Lth<%&`^7xc?(V3p20E8 z($P43_OfRYinbpt2@0Sy-o{7Ex}#Y8<+2@!p104wsytdizq}Q1t=vO@v>8vc#`W0x z1}HpLn4LAYQ`>;`*U)h#;C35Kw)D1^T1cZR^{OTSoG5=j}(-5=Ub_#deIgq0Mf&5B)g z$Fii=GDBzqzZ=_Ta{f|@FRfWXSJ?2RwHh?TUbVIqA)&piEgzwN__cNGW6GbTsP+P( zwd_36v1@`C*JJIQwe;l`_}ZJ9^wv2zeSH>6$M3Cw3MJzE>mR32?!bfIdJ)Crx8Ksy z7vI3=-ntT2Dgf_axedhB*Ws_L7ts&b;1O?cM6vkt+nMxqY#+Jd4(geK?00+zTm6jI+!Lv!seFV-llH&VhJyO=Dagbm!OUX*Th4 zoTG8+yL$SwV%+(ouK3Km@6q#T;^K`q`cw|?zUh5>b0I#mX(9bVHr8+68ZFF*Wjius zXqZJDd}cVyNc%9{e~U&`nFXq~uH-x;ekMiX3A^HP)t0_UgY8@Ppb$Lky=mxi{P}yE zqh5oQ*6!HL$yvk65#oZaCfNWF4vRfZnjN{M**Y%PK9S zuL68;YaJxCUE4D0i%;Xw?FDr1(|FnTr$S<%h8+1$cr1-kZ4_?Z9v$bLMn=5{0zrC2 zELnt;NH;nEcnQbvI77Eh#lP>Upyy1*%Xe;yYMesYb2z>+@afE$gSEA)KCgIi5R*%q z9ntW~PqH!}udc127qYn5uH1-cCwo<5n+z(B!L1tuv3*x;M1oJ#a?;cl-`bTxUzmhD z?{1>4^YBl*=c8WO^!@|--3&Z-PhtF&*C9+>1iTMVu1HCYx2)I`;`!z8eBKoz>Asna7djcN#YNU972NoX?Tz&3 zt8j8%AxgoY)Rocus_^iSUM7`~hS0ND5vJbOG}`2R0CDr$iZ4KxOYTyY5acE<;p9C2 zao4DN;Bb$*pNx662-FkODXQX|9KdB`Tp^dNu^-t0+IFgb&#>0PvDFD7trg0?|^PXc>pbH zHwi5PmZNNZAV+6djyt>dgNb>uFpf@gwOd#2g?Do4)zk|6Etyhy9g$omdF<2e;>><3n06-oA{-M zakLPc9VCOXuo;3FYns4v)C5Ztf<@p2y_jiHE)0IWEJ+!{fw3%1X(m&TGxBld^Jx z&4Rveg8*M8!Ey;^_P9)06#?@J=+Yn2H@{fU`>G&?v5BzwxwKpOiq!-Y`(tYemP{~@ z9agL**cgB84T24v>JQooILjYfN3c9x`DNl?hFgD^F8C1S_0=KVCP-X7{^5oW{SV>x zl;A(a8xM8}I3DZ}aXi=|(wy^d9Xte z@}O@Nsv%|ZC>Mv`Fyz4wama(NKsf@jm6I#HT21irKl`& zBtORgO|sm8gL=t+VeyhMign^Ijt+Hq4dc2VO{JS-u;Cbsg6vz5bw$X8_kBI7_xf%W z1;=}rB#@Hv+KjNt@bodoa-VI(;-=o{As*k<9XYZ2Y)>S>FPwM`?`TS+_jbjD6DGUjt1kOo5vzvkQ0wT(G!V4#BzFX7yR3a zL3C?p-20oS={244;%`#vo1O9gZ+d&U+rJr2PfWl|P7S2r6ybtzXVRxd_A}pZLMQnn>@7z<-=dfL`&Z$z+YEd%|R=pO%EVR41D3v^C;&r%iD5zjk^i{Y*F> z^W8Q2yHL!W$w6K5x--w9F8INj74&PJaOw98Lt;9C$Bh$2I`GnO40K*6Y&c8A=bR$9QDayb0K12-I{jvu1r z;L;US1z#o$Dd&Cxe)|XGAdw&E7El4lS;KQae<)dqs4tT{QO+s&;g4|xjwJdD&UUk( z;n_KE*tnxP-W7hlac$UydJb`oNzUO5DKB`5Q+*32nYxfkiU??z`&3o?k^-bL|C=< z7fN&r%ChT}s1b_d1|`ae;z}Ld7r1Ll!z0dPU+&xs{BtkwpLOuH2OX3)9E|Kk?S#ti zoA#%FejL$xx3T5o^4OcVC{gfjN;C}0lTaXo%x!0j>+DIFCZU)$zfvM=>cc~+Bog4^ zd~!UT+y<@1Yc4ChZTnAc7jVnviRzzPDG@1^z;e$;4+EMMg1KojbCR-1zOsBjg$={= zuJr5{7X&9F!4yjqoPWm(t@e5tnib;>SGvLD(??hOp;;JRjm9^xM4>#O2B0FYuD=?E z%JBrKui@%esMp|kp{|fa+u0GTmg_`kj=Kyq`t1p4N6R(1L8(04t>J*k- zi^g|>3gf6=*P`G_M{;cdq$1KX59(sP6zV#zz7Oi7@Cm3-x0lG_>9J_ z*Sn!sj_P@%8zeV^r2!R&XWSSBw_)Y*IRx9`Q-^T0)-p*j#8M=ierHi~y{bV1;ek{ishR3xI4vyuJIIcC3qcb^L z%+c{2rw;!CoTE4-k!zjE(X%)@hoh4?&Ro3v*TKPs95RG!E#>H3p!)}Lq?lu@##z4& z4&KHga*nZ%qw{h6Z5N}8V?2f8IQZ1zCGa^0*TQEeKG$Z5c3G8}#B0={cLzTohLFfQyz8Oif?^tpD$qge z@Mx%r-IXY)v;Ys+(gi9t?n-B<#PgLykdtBO8%{Pe>R{Dg3W>ggavI7-D6N$J^Sf=x zT*|rD1WT~xI7D`uK=zfM3|bI!C>3@Gin^YxC*yK;NUPL!v7QXg)yckjJzh^n=jvpr z>mogwKz$(4ktv5Oz@3AMzg4-$A)~}t(}Zj_2bdwDgHr<%#%^jn9{@Db!}abZ_XR)`9b6BWHGE1IUvfV%GUY%8Joq2?T`L!S z9aajq2gG-&JmRxf?ok7nd{@ge`7W17eAmk(U0o|CTI7*#tar^k0*m%&;VD6O7Jn`6 z-pKHb-&5dPbK~zubQ}NtyWJ@6M2oEBbroho#S~#hp5PFr1Ej(vtVoZ4z9Ku(nmy)F zdTcFRRD7A<-*_lJt{wgkjSz5ObUr@$ATh2QS}R@=K7VL9JUCX26v`S7jpP{u;&Jpt zy?81a4cKVgLYMN_Oo(VVFA3ahb z^c_~VU!s4D`zQ|8(CAagJ{s*s$nIDyKyY~;VQUXW1JG96fFSfZdf7H52-TsbwyST*HlKt9$?=i|^0Ns`bh;`O9MOLDO8?Ix-VZW?ZC?%wj1pfH$y5 zX0t+nAcu_>`5SlAWM@+v4nOj_8&=;EytbGXi%&d4&!LIVnMuDu`coEk)v3* zX5AxtRqGkity?16ty>W1)h*xGN4fmg4m@7%y5)P-+tfY)A|9AP!7lxJhUABV6_9LQ zZyO$tCNgd{yCr$`>_)tbb|Xa74I&h6YY9iaBHrojb?ksh+u%+p;s4aLONspd&9nQu z_5atNZTmU`8BmHXEb`C3HnpR#Wp?zn8K8lgk!bM$v9BF~H@}Wt~Q_ImE$4lZkO)vn3|t^ApKtY4QF^!kU)!=WJ%X z9gVt~xwC~C8X)tB7Y#cL)J+f&Q`i&z$J7<2ScWfWY=W) zv!t*!{y-|b%^yf(FZpGjv~*9X!7$n6cF87*LUFd7C={CFt0XwO{}(OsJGHNx0O9zF zo_d?T3gS6^HN+#nD&i4e9r1{-l6b^dOFZJMCLRGjjq+%LhkO;qBfgsA5nomD2^=$Rix`W%1UO>kX3u0%hFR=Cjnqf=0ZuNndQn|kE? z`YMt~d^O2SGkeYmf2~Suwu;|)(|y&+OZ3$zkED5G)|GYAeKpFD>arQwFOc8LCdB}RFC>AlS9_rvRP5*j7njJ)pUU`yzJNdAz{8D@ zoRz%ED2$CIfa|OV`7eTv_c=`g5XmO_10q)J4|HZ#{y-NtIh3}okB1$#!z5t#Akudh zJmNbI9`T(AkN8f6M|@|(Bfe81NFaGQNp=_$(5r!T zXRBij+}E;lsLw^7{)jE&0PnQ<4t(c$Z)X$fTuOKap3VZc(%o4PIR@U41+weh2oD7L z$p?;L*9_eH1XqdfWUCpA`c43aXOdCsxlz0`N`kt}AUWP8xCN}3Nw8$UVOKmuFq3V~ zI5Y&zx_{h%H7mFg4Bf%5JlMgo0RDZ;y5IKkrnO~^N4$ZdYD_ zuVG!&{(F|?ySq)BLe$LZ!q2um;%i(U@wG0GaOVAeJnA>`&*lXVqRzjzFDIg-fq9U( zu;ZQyoexGSj#E?7rvY^C3yxKp$P&=|28%>rLaBkW7|KK_!=MPET%7IDpjWp%B)Yc%iOxdV3uOb8Vkpl*Np&orjf#5Hr8Y-E8IlA|p0o;yWWF?B~DhelI?P(fGn&KVPR-%uPSx(yER+qp2 zDlw6^6b|$7PbAnFe{3qjGX1ekg0cQs4#Dz$Fmf_1g*r)o!B)F@7GYQy0ZXcZn>l!N zNp%$nlcKObif83qL2sqFeGd2M*NHiM-ZR)1|<*jQR&eJfZX-8+|j%SWg~M6 zX@PSb&)gv-d9XuF@?eLcin013H^xhzZaQg z5H2p~CRx)!VXpO-6~oUud}EVahATKdHsitNgwq?UJlG*tU32C$#NP_mN!-x}!i{Jl z%{N@R0n&Wql}CbEZ@jw3AtrL8l-L3~`=k_n496nPf4%>=Ny zL-g@&9m0=1!GNJ7sQh;BEA;G0JBjg<>UkEuEaA>Y446!Kt)Fyui# z4jmKTL^hfh%yInk7J34O*3bT<9bWy;xMrU1Fusj~g6QvdI?^{G_}_vjZ5!W1b5Oh^ zY%6M`aSOfYjUA|(p8uh3!w#fGM%$?!$Y#!&iA3Vp5!_T!R0Gh>E;=V0&iX5PT(lU8 z_EaL#TEg=R66FBB2I#y(Xty8{*@pj%<0c|ex~T?}|L&=#PR{{e#mT>*3h(0hP>4d^}DB4DcGS4rhSLzQE3E$T0z zPud+D_n;bGcs4a}&iv8y^C!-k_guDZ{7uw7G<(?dxie=kf*+n!js-W-BNTDcjzr$# zSbQ5T3mDoE2(qD=dQ)7fiKaz4L9j?DHBcI$G)B>)7T}4u4ZMTyB-C_-(fm%yNg!z;Xk?IW@mPQ zAAiVO#Zgd1@HP9*AYs|cj~pgy+kd!%Qilr1WrR+zQS^UNw{FLRaYqSC8a6;{2kI;gs# z3Q_k_GwN~b7u9d8zf#{-CuqiMUeRpPoX|Yd4Af52F4gYReyiJ1%RH{VGFpkvu^@MqVxd zLVi^Ko&2Kwh8!t6EBYxEigd*^#S4lhij|6Y6?c`vOixC}Ol1}^A27$57Um(7sG6yI z7Zw()9;8lFXR2W)A~T}M&ykBjQ94DfVxOW(@q^+Q1-x!k_D~L1>Xc)YQC&Ka&7?ioUiJ&c2mI^$SlmT`fx*tp8L z(P%d|8Xp*`ctq8~!Xm^y#Y4pg@sr{i;sxTRVl3Vwc8I?cpA+8{w~ISV%>5*}l1K6| zMK8rrg<6rOn4(}6g^E(eD#b>HU9n$rN^w~+P^nQqsmxT)Q@*O)pxmYWLfNc5ue_#w zpbTevgEkn@Mi!IDEMqWJ!<=BQGmn@^(8L(kTdJ>B^VKhax2)D~($;DJsXeW|stwik z)yZ{fx~aOkW?hl)b=?j9S;GS3G9x8M6#TrBDi?PJgWQ+>tZY*s&>qoJ$%rCho-I44 zqu{xy7U+|bGm`U?7Re>>>>H9+$s%b3bCS8l++)I2ajGGl5uR4fRlTGtQLRyJQGKM^ z50kl|x~2L<)k)n0%%M|{RhzTa3)E({Mg5j~r}{JXarH&DNRz0MYes9bGz&ClFwEPW zVUB6O*IWnVL}+_x`)ifjQ4qAxX$!O~v~Oy6YWIPGTC_K`55YvebW&ZqZj

5|(hh z)4|si@m9&>(qw6({FLH`;z#B)^&#~fO@SuH48n4?dD?t!p|%KYU8*hD+O+MuF8bj{ zDi3iERdM!Q@4 zg6@Q_QeSSUHrz6FGE&8eii1fcN|s96Bom~kp!bWiwX#j}GjcOk1*(ONHb62#GF385 zk|!yWluK4g)=M^l2|kj1E;%G=mV7VyS#ljZKa%WL9#o!Co>N{{4r88UN||z|lBr_W zFzc9Vu>EFc8&eArQODFnR5UP+%wY(=W-!bd=9(&8qti^%Gx~J>RLGIV`ZfA(kQp2G&H5JouX@@LWk@i{4C#jHh6RQagUzth@P*-9 z!&Sorh?{;!#`vT$+h|^7tTujZY%*Rn-Xr#^L6jV{5iaf{){37HPZP7^m&Ik`wc^cU zyZC_kgt$d~Tl_#AEQylzkqnkFl97_}lIfr~vt+eot7Na_1Ze6vNhfJ<=^&{=`h@gp z=^W`&=^N6WV7?R53({-S`_fQZFWKX=WSL3!oEb9QO4(-FC$c8lMcF-BxV*2Nk*CRL z%3qROdWZU)y0eDSq-myUo&%>V(^P9d(0r-+TJx>Od|7i_GetW~ zYth30b!ms{GIeuc8(OK`rrV|aR7dHBpy1*9@%owiSM*=$&*|41zA@Y|^f3-KjxuH# zuNqs4-u57h9IE8vQR4Apv$$NmO8ky^oA?v)dda7fM#%-quaXGqbl7@Iq^qTGNw-Nq zm)?^iSr=LF(Xv4@u}mo&AsZ!oLAFR%2>JdU(1$~IMAj@jEjus!S>}}8lC{Zxmr?Q{ zd6>M5JXYRYo+uwASITwrr{o3l)$)z<_vALw0J_q)VE~;=3zfF>v2^e0ahiCD9#ORmvw)@}o>?19Xc cVi;u@V=x&e8m1aD4LOdIBzj4}DskBV11cEsAOHXW delta 15766 zcmcJ04_s4K|NlLg0aFpC{8vO&R8-9UgWYX+8>oO4>J(B+R5Y_Ip<#krk2(|-aZJ&p zjgm~uKlrbxX+s|^OrP-a4_awoGc9`3oQg$Bg{krVoO`#it$Kd-dVPPqJlp5I&*yy3 zIiG*$+;eBiC2`3mah(M9pMUsfF6I1drs#g%P;dyf6H(M^0o?UAmZO(Y3Eq#U&{KBc zuJjgEV%tsUpg`k6Byn&xSS*UA^S0vz(bMQYt`uEHt@v60tf*ouqPPnse$*sK)XRt} zJrR^In(XNQvh5T9sR%v9(!exiw=E2`pqL?_Q&d?)(ExM#Rv2}X*-i!@);h_U<@gZy z?ULQ+(4{|S$TB)6zK*N9OrRgXgum%xpdW~FeAfyTkKgM0HhfO&w!NRE3oS9DinLa9 zs;DBZ&73N(NNYEz23f|oRHQv{^!^?X2zvhi#by4^W(yjMB-C2660v#ggXxO1(;UtC zPWQLaFwf&es1kpVf;ywjq zVj7G21r0Tqk``bDVm~6zeC06%*A4kl7N9NtH_tp zaTbRa*PzdA?-U34qjR?Umsq-cbuA2FZi}EP$I@jsw6rUt=a=B_%le{L+wf&05jtxt zH0_{i)zdg~MF#rLHhsm@2%WGUFAeac{kGuKtD@0K+x1m@5Z&i}+xpdEB6@Hw-c?yg zKeGcrX^H7I*$N6z6HlKu#e9A0haxJ=vUYWMigNTlWLszHhUlL4xMpn{I*tFcR!3Xx zxZk=6^c7~-wb6Mm;76(==&09i=~ds+G1cpdDi%Xu_3Mf7x$u@{9g&pfm}avX@jp;B z0#j+HMJ>2%UfOD&DmH`PjcqeJUbWzB8=t4u)%b}`8uZXswW$oDdRw@)2%+P6P4%mh z37HhtQ7pEsxH?77uU&*C2@^Qwi)4=I=zB&g({?ng#T}O?c#MZ=jR-`fIs#w~e+@TkfLXKd+#u zkoU@0!R#&T$cn%fvPw^ijE*aK!d7*!k!7xhEQyaXPYp7sNz9q?VMfR2c>C59F|$g4 z*K8EzIPT%HZ94k7<+#VeaC~XoPC9rdE_uUB7tg@aZ@x`uzlcx0IiH@8hjrU`h1E`n zW!rNjX_!R}d}cY4N&6#s&<>3x5gM~B>v+#-m_bo^!u~j1wWB|}jBPvW&>=i)=ac9h zet+lo(4kX_oFlxPjl7(CT)fMuXoOTJ;IP<}q}g6Tnl0m^jE-9`5*?&ZwTw$Jb}Tfm zo|c>C*trn5?>c~*@&4Lex^xQew!4@Pnu1sCp4g@SDaetpcaNem+82sjcZbES2BgE@ z4gx`Xa1>dDgGe_z-dcp?_FSSjK8b(bQ$crm60h9*X6URO!k)+TWx}V!FCT07F@2-m zIEX1A&Gs<(6eh_Q;|=>N=zeo>pZx{Fev>;@qM8gUj>N5R_+#7tsNiFhI-3TPrf~e- z{(kh*Nw~*bO|)(v{`#$X=qxtAeV)u1?Yh3egZ#u04A9^drsKa@s1{m{Gg!1cCQ4i876 zI~VbZcjP3Coy-lg&N0?F(Qqz^w>67kjx zq(?{@s0cEenP2Hst8jM1NxEt!jy*OTMqYVre8?q;osMFOWo>%@Y1I^NlCS$712*|A*d!NTlR6|Y>GXJRG5+Ovp^1X* z)?pNuD&}c1G}}oAWa?aAvVkeo5QfupaK*qhXAgq9 z*2VI)7~+4iSj6#u8;Hpr<9Ap`||`GngKGAECB zxS__s5pEMje-Lj1=o4@P=o4`Q=o4}iMNbi-UwX%!!0Hop0_YQU0_YQV0J`I@hb!)k zWcGaE^*g!nuLqt}mMiiEDW0Ln&%i781jIA=1jHlyOdU?S|0VnutaO^}FNfc@k1M$qMxNaQmP(|G!6FcCU>C_xLGaAo4t?wHY zO;OXxk^GGC_k_6x4(g@H#3f6EDDE2m;Ph};*C4*@=`^}93hU2sXs2!0nQ#P8o=49< z5vz%$C^+6bC4rQzHB*Bo!PCcN^F6K=OPgX*J09N@jjmzS<=%+G&wV@)?`cY>b0hGN zO)`2&1Ri*968ab~Ij2K+@!@l$>3Lxr&L`8!VVF6;n>-qv9}8mfDl}JF`G7q;NLFxgI;l;k;xi9>kT7K`Ain%RGnm!BjaDV`ZFUsfp2~` zgQkOV=KtKJ7X@L}r95;HS6`Y*PG^_O>7m_l+2`}S*t>$qjT42~@$ygfw4@u>Unb)7 zE)(%Y3)k@G%fo2NL45MEH21teaKrP*(VE{nIu2#Su5x$Bnz7%6ymzd$J#^7CrR^aL zvP;{$hm@}c#*p_)9<)%Bj`)=h_-agR1$OG@S@-}Nwgu|uF@c6-2X*rxp7KR}e>lET zrH*jjE%**8A0kLexWh{cg0K3UIPZ&IVi7doo>(t}N+fP+?u{=tN8*83*3vZ%_~4a6 z^otGn>XmG|_%v2r<-lMySCi-}M{tlGGP(Fpy0>$6qdx|lOD9K{^ki3 zsr-?WSkfLerV;UQ$1o`l4xno`;j3aQL?aP#&J~Z5ST= zZ73=LYA{;N*I$Er8GZ-qReb#p)N3%hF&NeIb={3nbO29)dIMj#K>ZBf2K7sP{VLS2 z;UA#h%GZZGLeT@BdIBghPIZLgr9g$@^^UI`4+b2Ai;#L;VOo2KAGC{UOvZ z;2yV!!fiv3+hN#vI|45M2=x!3+IeaxP-1)(c%rZaJ`-?g%TSn~9zMt5IW0qFIbz6A zX=2M*u{j`LG=W!igr$@`wsIV_kz;%lJPiT1k6=akL`wq5ywegB*=Za&rFmE z3K*^<+1!&@v&l&I{s0(*%fsXE3}@Bohl@vRL!oypT@cv`}9Uc$ct=QSP)<6B#KI-92-@N^W9=&bR5sg;AySH0!Q;mJl|^M=~+CT%hL%wCx?T77#3K>BZ+)#2~QXB zbR|zqdCqG5K5%a55hdSR$J0eTeT1hOp7SK0_v5g@3p~Q|t<5~Wgr{2wT?+owHkjtx zalgBdfN6~I8HG*onSkGdPX=Fw&oMZt?GcoNwQc$^r(uazI_+v{8#a3%LJ~`Acw?$4 z&JRf{&~Zy@7*x*F&Ppg$N?etmP)TuBdO)QCdPAQ^*mhtmPbGUXb-XGU&^b^RLMeq( zg>11uwINdp?_Cqjf#!1%-0334zjb8L;x3J8um!?4o}we;@^x|$xGmL@q4_#&CDiRW z9T}ak^V_~eMJa66X?O!djmbW25%sgJLwICapye%SR;3duV)Ffh~RoQd$hwWA~^@{-#!$Q!Pxfk zkO*FD?{6adxGgKaM;8ay6J6XMSbEP7XrhPP(WUo9KocF@?yhJ!Ocq~y&p%}JhZS&z zan5tCeEj*W6l@QO=TZg4W3Bu{6EJzMR$%g6u7G&1S3ts@D<)bLkO;1G%>n|8c57jx zX!nL7FQIC8Yu>l3Zu9pBW`xZ z^mxnk%*KqUTDWrgC}WVJF(ak}{u)P$_%FH;EAPk0R6}dUi{jahsqhF~F-ojxXdERl z_{HF&`#Nbh8D!DoiiIM$*sT~R1_!AaBeqP75=OI3np$BLLC5+OaI{9hTKx6>-lz~i zyiXGMzy~aBEHfV%V552C!L$7zkoip!Rb&a%ZFNp}RGiRzR8%b<_ON#(KdC33lX6eE z!8Y|FgW$&g<=?K+bRXN2j;%C0W$#0yBXI9;zwC$LH9)L&x<49>?ACIBGy;`d?f&Qh zT5l}~K>F|%z4#mIV&R6GTRp8P%b~Y_5P-^Q^oup43rd!(gS$}kqvDTJds2?OJt@li zaVQG4Zta4COiSQ$2K*?HTsTHrM#XRra$5uYfe;m=;yJ=Wu4f29T%6R&Efk?Ed0K2) zkN~^4qlgUC2@5RM0t*qI;wyX%XDV}EojHell&karh%BMMAgi`hmeE@l*9J}bv}IH- zoI?4CA2p48KrnZJkDAVjz%m{{9w(7_AY`;!>*20w96Dg_(hc=fo*)7`by3Kj@dk>x zOWs2ibIsOi-B7Hc)+TcRQEP;!TAPG??4eg42{1Rg6-#KBV*ima$$ZW+G7J>k2?z1C zOR*rnQ?X9*rcq-!mu6ifcB5@~rDm6;PCdJjPDQ&AqUksZ3bUqkM|~u3_vm!+(2)Oc z4!)5L^Ywp!AOr%tOdcpcexy2m&^5axv8LtW5HoTSOT0}oiI@m>U}7jCS&`J1 z9_O7gEa?fqPiY3Q2L>tUa!bK-=}I3{q%#~x#&hR~be3D}4W#&_p!8I3wKE`` zLPz?TGkp|SW%6bj&DD4VY20>iAf0RWN<--x?zn?trh8tV5oN&x*rdtkrDF3BoTtLz zcrR#d1j+BeQ<<=rd+H1jz5`KOtf^C50;i|C1jJKc0^+GK0rAwBfOx7*Ks24xBERlN#)+KBr8LY3Y6z&39wfo^MV>EY-0I*r}qdA?;l95iH z&iK(X+^*spO&B1^Jb8>IgHiljd!9dQTkl7sM_?Nq+RGy+IhT;_4g@EV<91>v@Fe6U zQ!mu_&$S5ZhP8XD5s<&AN1?B$A_c@#lY%sp`}FYVsAFG zLk7WNIoYlngtqsh%O={drl4O$v7(7cG6TxGu}IQq9Fk;>N0O~jB8*5fY=ZsAG}Ppe zu33*gjzUe>V5uYo;Vs%?F}x~lAxlqjzoUdFMT%?p#>T*p5jas0@JR(e^0q7t>Lh&O z#cn(4!$F;YvCF?p3yI*lzvocZSFv9G5-&^DoRQi#GKI; zbyFsa>n<<}@1Me~9hoR13SK3_n^WkVkKFu-D+{vDAB*~%U|L4PP)Qg>ZUzyzk?-MJ zZ@NFX)f)%^5k#h&BhWdB8xXtqAo7y}qKgFR;aZNAr?bd=`SM&cg9aiF9M;7wFz^O| z7tO9ExTKfQAlMkMDU{D7SdR7DSd<86>@n`Yni1RvhW4?e0Qwjbz`t%u*PBeil-4)L zp+1R!!HhyTV#k(osQ(|>G1|wDLI>WEACE@^{=kp|ho>d^)p?mxKs;?JAfCn)5Kn6Y z;%m-a!CZf2PAB)D+mj!_*PsF@Skykoh^_>pO#8qbbl8uMDX@P%1)2R~Yv#ax6BHwq zp-@CnzU1I?2ny!xYo;T+AI&YWk9ryv`}OJ_sGtbtMhWgHYKl%7z2vR|Bymc-J^ zYJ1j7Bnz12zX3^lKGegNIiB3TzdraQg^M-tBc>BDnm8h!H}+<2`8r{3J>^w!6HA zG#`N8{3y#mQUV880QG#MYlL(oR|LSo`~=rJR}r0ZO9+>H16)-`uoCO?^^g=QNt63p zVAU$pqQ=n@)mmEj(y(@7#?;S>61=gI1e1DW zqX@=$W1|U{(usAx@#mit;lo;mQ4j>-YZyi6dj%mR9k78<6bhgahGg9l#Lon9B=Pyq zpcFtMD%~0akiVd2?Qt8?bY#jTEpSc~n0-Q00DWRo0DXc|0DYnoK%cPuT~xwz1zF@D z#HF+Ip9UuMBf|e$WRgMnxXe$mq=Uko>s?g>KT+|FO@0~9;B?ze09O*u&QKLVpICLy zS;!E7DOio5e3CkNEu?#fD?dQGXS@nXAlDhM&hdmKAOx&+h7~TM@~p^zP-l$tJxhIp zQ~*`ht)#>2(81Ihm_nORU<#m5WD1~9XbPZDYzm-Ha0;LhohEOMmy)=2Mybokosntb z!_;j?-vAYa`b4Mz`h=(eI%CxSMm2hwrn5P_W;1#WbrV1PTL;`tOucz-wtZay|wz8GUcPxCdEHl9@=-ycYf|2K*HQ zU9wJ~C3$ejuM+5nmyjgSf+U*=56}{zHv%2^B9hdZ;iVPOM!*{?;Q#?N1#}$H>j@9g zg+OlrT3QCYYmkJvD7@{Gq^vH4F}P-3p*9;B5`i&CB7q0Q7R8Qvlxz z^a`Mj%V02=v<2v@rGNun3G~)~z`#JS23iVu3D9PsTLDi2x&r9JC9oKv*8rWDCjqAQ z!Yb=v#d+)PFYQBvM0D8!`x|v=D+@n;ojYgVqw|Wg=gfVEvW~xlq5~h9T`=R>1=H;d z?x2S#xa|HL6S2Oz|kMHFaS&w zZ*6KrgL?8k`XNaTw2>}(1FZo+p?O_f6F@5b&zqEC)@47T*l^O%I9wR@QlucK&ic+z z=-IB%EPO^-_5ypv&uAyDasB5e^1g8l6bbwmR6>&)MY-DGwGjoE?o6&>Hf{z5%oyXGmDGj=guQ?30od~ z3)o`Hc+kFwreCJJF9Wsr_s7w&l~A0upFbVh6^=|4d6~8BfOVcf9pQg`FOqy_t?;M& zqI1^W@I_C5%eu#(?iIkkjU-dvw%_unS45*C>)gKdU{iu>gvz4YsG6@?r@5<9YnNz0 z*A8RzSqIy~wz6$(I~$~v=)!bSx)@!YE?yU=kJ88JFY z7*(EXwW?0_g(`rNF}X}R^Dc9h5vvvI@#?45mFnH<57hso{!ZOfqtndMY|x!Qg{>om;VM)h^XPRiCQbRiVrTrhqA7YM2uYQm4Yw>eZ%G>TlH#)RY8KCt+4G zpr|WJnaNX==Or&oUX#2nd4KZZJ(_LMfFe> z!whE(j1jcAfH5(Z%oe7OImVo2E;Bco-x!IyKWH;W{SWmf^)B@x^=Wk?=yIH9nkHYf z+@!H+Ue)Z?9MPQ7bkoLYhimoP@!C9XC8+mpZG-lV_8aZ@+6UTTHjb6CdiHs?g5AvS zXFp({Gb}Z%F>E&MHPjpaZMbZ>ZFoS`8;7V9@YP)!D;*(Ckxr1#lrE5#N%uJ<)Gs|9+y)8Q?`&1Sq-=B0O>1@)~q?<`UCj}-)B_|{+ zL4^~OpH7~iyfS%H^5@C7lJ6%6srslMQH@hgRXwjNQB|s5QSDV7RW+$Dt8SR3&0Sj0#+i=D+5v#VGu`%e@5344wGnHB4L=@NB% zU5$PXS%nl)G4RzFJeiUGOO`9&t=yp6u6codiKS8yMPfcgY1UEj@Kg(Q8F+Q2yh^@N zUM;VYZ~Uh%{@)9wy$=CR;L}Sovtl}K&{Yj*6!E7 zuRW#xO#7|2UE75XX9uz>_EB~UJC`-FRcsBrk3GtsW&gwe!om#m5S0R7S<-FNK-pyZ zSMpZ*1?9@*8dLH*<~hwG2&sYEMi3SQc8+Hg*hE&!Dp`ijVBgk#sQbY{6(T+Z9+d?u zI7NY?P*J2_A$+P6S z@@M4pK($vzZ)-h&*N% z!$H^;GDXZ{W(kB}8MB&sMSWD$qVdvCIr`d(z^RKYG*+cBV+0WUV>^(ME*I$>cdrUV?_kwPPZi8;O?uhQB?hD-w-7h*Z z^2v~BmxG?(kRO&e$uG;llm9C3s)$y|6zPg7V7%oD zi{cfqu5;bDr`Q{I*rbt z%h65M3xpuF?qhcNN$mDlfN(TrjRHUijj)PpHSo|<|%3w?xzc+kkR%9kMHuToYjHz=!>Ta??Cdm#6}r#zzkPq`aWKto#;s z0y=46(#WKVNt2SSqQP_}otR{;8cY|^?Tq!e!F0@&C{2teP7|+5&?IW48l{HOuo|N# zTa%-y(;U#$Lk@1xoY0&E$86QKY1%apG!*z{p|(i7Si4bMt*y~+)o$0;YWHdDw3&J% UzyIdy^YpXyoPALOz0~i20ls(kC;$Ke From a177b81b4b7b8dba37688ccd9c6c000c45cf046e Mon Sep 17 00:00:00 2001 From: MrWint Date: Sun, 26 May 2019 00:18:07 +0200 Subject: [PATCH 18/38] Update MinKeeper --- libgambatte/src/minkeeper.h | 187 +++++++++++++++++++----------------- output/dll/libgambatte.dll | Bin 142336 -> 142336 bytes 2 files changed, 99 insertions(+), 88 deletions(-) diff --git a/libgambatte/src/minkeeper.h b/libgambatte/src/minkeeper.h index 5816a0fad7..322d746d7c 100644 --- a/libgambatte/src/minkeeper.h +++ b/libgambatte/src/minkeeper.h @@ -1,157 +1,168 @@ -/*************************************************************************** - * Copyright (C) 2009 by Sindre Aamås * - * aamas@stud.ntnu.no * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License version 2 as * - * published by the Free Software Foundation. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License version 2 for more details. * - * * - * You should have received a copy of the GNU General Public License * - * version 2 along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * -***************************************************************************/ +// +// Copyright (C) 2009 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + #ifndef MINKEEPER_H #define MINKEEPER_H #include #include "newstate.h" -namespace MinKeeperUtil { -template struct CeiledLog2 { enum { R = 1 + CeiledLog2<(n + 1) / 2>::R }; }; -template<> struct CeiledLog2<1> { enum { R = 0 }; }; +namespace min_keeper_detail { -template struct RoundedDiv2n { enum { R = RoundedDiv2n<(v + 1) / 2, n - 1>::R }; }; -template struct RoundedDiv2n { enum { R = v }; }; +template struct CeiledLog2 { enum { r = 1 + CeiledLog2<(n + 1) / 2>::r }; }; +template<> struct CeiledLog2<1> { enum { r = 0 }; }; + +template struct CeiledDiv2n { enum { r = CeiledDiv2n<(v + 1) / 2, n - 1>::r }; }; +template struct CeiledDiv2n { enum { r = v }; }; +// alternatively: template struct CeiledDiv2n { enum { r = (v + (1 << n) - 1) >> n }; }; + +template class T, int n> struct Sum { enum { r = T::r + Sum::r }; }; +template class T> struct Sum { enum { r = 0 }; }; -template class T, int n> struct Sum { enum { R = T::R + Sum::R }; }; -template class T> struct Sum { enum { R = 0 }; }; } // Keeps track of minimum value identified by id as values change. -// Higher ids prioritized (as min value) if values are equal. Can easily be reversed by swapping < for <=. +// Higher ids prioritized (as min value) if values are equal. (Can be inverted by swapping < for <=). // Higher ids can be faster to change when the number of ids isn't a power of 2. -// Thus the ones that change more frequently should have higher ids if priority allows it. +// Thus, the ones that change more frequently should have higher ids if priority allows for it. template class MinKeeper { - enum { LEVELS = MinKeeperUtil::CeiledLog2::R }; - template struct Num { enum { R = MinKeeperUtil::RoundedDiv2n::R }; }; - template struct Sum { enum { R = MinKeeperUtil::Sum::R }; }; +public: + explicit MinKeeper(unsigned long initValue); + int min() const { return a_[0]; } + unsigned long minValue() const { return minValue_; } - template + template + void setValue(unsigned long cnt) { + values_[id] = cnt; + updateValue(*this); + } + + void setValue(int id, unsigned long cnt) { + values_[id] = cnt; + updateValueLut.call(id >> 1, *this); + } + + unsigned long value(int id) const { return values_[id]; } + +private: + enum { height = min_keeper_detail::CeiledLog2::r }; + template struct Num { enum { r = min_keeper_detail::CeiledDiv2n::r }; }; + template struct Sum { enum { r = min_keeper_detail::Sum::r }; }; + + template struct UpdateValue { - enum { P = Sum::R + id }; - enum { C0 = Sum::R + id * 2 }; - + enum { p = Sum::r + id + , c0 = Sum::r + id * 2 + , c1 = id * 2 + 1 < Num::r ? c0 + 1 : c0 }; static void updateValue(MinKeeper &m) { - // GCC 4.3 generates better code with the ternary operator on i386. - m.a[P] = (id * 2 + 1 == Num::R || m.values[m.a[C0]] < m.values[m.a[C0 + 1]]) ? m.a[C0] : m.a[C0 + 1]; - UpdateValue::updateValue(m); + m.a_[p] = m.values_[m.a_[c0]] < m.values_[m.a_[c1]] ? m.a_[c0] : m.a_[c1]; + UpdateValue::updateValue(m); } }; template - struct UpdateValue { + struct UpdateValue { static void updateValue(MinKeeper &m) { - m.minValue_ = m.values[m.a[0]]; + m.minValue_ = m.values_[m.a_[0]]; } }; class UpdateValueLut { - template struct FillLut { + public: + UpdateValueLut() { FillLut::r - 1, 0>::fillLut(*this); } + void call(int id, MinKeeper &mk) const { lut_[id](mk); } + + private: + template + struct FillLut { static void fillLut(UpdateValueLut & l) { l.lut_[id] = updateValue; - FillLut::fillLut(l); + FillLut::fillLut(l); } }; - template struct FillLut<-1,dummy> { + template + struct FillLut<-1, dummy> { static void fillLut(UpdateValueLut &) {} }; - void (*lut_[Num::R])(MinKeeper&); - - public: - UpdateValueLut() { FillLut::R-1,0>::fillLut(*this); } - void call(int id, MinKeeper &mk) const { lut_[id](mk); } + void (*lut_[Num::r])(MinKeeper &); }; static UpdateValueLut updateValueLut; - unsigned long values[ids]; + unsigned long values_[ids]; unsigned long minValue_; - int a[Sum::R]; + int a_[Sum::r]; template static void updateValue(MinKeeper &m); + public: - explicit MinKeeper(unsigned long initValue = 0xFFFFFFFF); - - int min() const { return a[0]; } - unsigned long minValue() const { return minValue_; } - - template - void setValue(const unsigned long cnt) { - values[id] = cnt; - updateValue(*this); - } - - void setValue(const int id, const unsigned long cnt) { - values[id] = cnt; - updateValueLut.call(id >> 1, *this); - } - - unsigned long value(const int id) const { return values[id]; } - // not sure if i understood everything in minkeeper correctly, so something might be missing here? template void SyncState(gambatte::NewState *ns) { - NSS(values); + NSS(values_); NSS(minValue_); - NSS(a); + NSS(a_); } }; template typename MinKeeper::UpdateValueLut MinKeeper::updateValueLut; template -MinKeeper::MinKeeper(const unsigned long initValue) { - std::fill(values, values + ids, initValue); +MinKeeper::MinKeeper(unsigned long const initValue) { + std::fill(values_, values_ + ids, initValue); - for (int i = 0; i < Num::R; ++i) { - a[Sum::R + i] = (i * 2 + 1 == ids || values[i * 2] < values[i * 2 + 1]) ? i * 2 : i * 2 + 1; + // todo: simplify/less template bloat. + + for (int i = 0; i < Num::r; ++i) { + int const c0 = i * 2; + int const c1 = c0 + 1 < ids ? c0 + 1 : c0; + a_[Sum::r + i] = values_[c0] < values_[c1] ? c0 : c1; } - int n = Num::R; - int off = Sum::R; - - while (off) { - const int pn = (n + 1) >> 1; - const int poff = off - pn; - + int n = Num::r; + int offset = Sum::r; + while (offset) { + int const pn = (n + 1) >> 1; + int const poff = offset - pn; for (int i = 0; i < pn; ++i) { - a[poff + i] = (i * 2 + 1 == n || - values[a[off + i * 2]] < values[a[off + i * 2 + 1]]) ? - a[off + i * 2] : a[off + i * 2 + 1]; + int const c0 = offset + i * 2; + int const c1 = i * 2 + 1 < n ? c0 + 1 : c0; + a_[poff + i] = values_[a_[c0]] < values_[a_[c1]] ? a_[c0] : a_[c1]; } - off = poff; - n = pn; + offset = poff; + n = pn; } - minValue_ = values[a[0]]; + minValue_ = values_[a_[0]]; } template template void MinKeeper::updateValue(MinKeeper &m) { - m.a[Sum::R + id] = (id * 2 + 1 == ids || m.values[id * 2] < m.values[id * 2 + 1]) ? id * 2 : id * 2 + 1; - UpdateValue::updateValue(m); + enum { c0 = id * 2 + , c1 = c0 + 1 < ids ? c0 + 1 : c0 }; + m.a_[Sum::r + id] = m.values_[c0] < m.values_[c1] ? c0 : c1; + UpdateValue::updateValue(m); } #endif diff --git a/output/dll/libgambatte.dll b/output/dll/libgambatte.dll index 11be420021c5186d5f4c848544cbcc838dfb4fa9..46598c121389db585c027ba268dd55a2e3a1b874 100644 GIT binary patch delta 29311 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 Zh2FZ;;2WC9nJ1VF&6C}8W7#DE{|`S+UvmHe delta 29793 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 Q6*tdQ5XssDmMbLx50XjoivR!s From 92204d527f2afd384299199867bad132d8097b30 Mon Sep 17 00:00:00 2001 From: MrWint Date: Sun, 26 May 2019 00:41:03 +0200 Subject: [PATCH 19/38] avoid bools in save state struct --- libgambatte/src/initstate.cpp | 32 ++++++------- libgambatte/src/savestate.h | 87 +++++++++++++++++----------------- output/dll/libgambatte.dll | Bin 142336 -> 142848 bytes 3 files changed, 60 insertions(+), 59 deletions(-) diff --git a/libgambatte/src/initstate.cpp b/libgambatte/src/initstate.cpp index 5b89a79044..c52a530773 100644 --- a/libgambatte/src/initstate.cpp +++ b/libgambatte/src/initstate.cpp @@ -25,8 +25,8 @@ namespace { -static void setInitialCgbWram(unsigned char *const wram) { - static const struct { unsigned short addr; unsigned char val; } cgbWramDumpDiff[] = { +static void setInitialCgbWram(unsigned char wram[]) { + static struct { unsigned short addr; unsigned char val; } const cgbWramDumpDiff[] = { { 0x0083, 0x7F }, { 0x008B, 0x10 }, { 0x00C0, 0x7F }, { 0x00E1, 0x7F }, { 0x00E2, 0x7F }, { 0x00EA, 0x10 }, { 0x010A, 0x40 }, { 0x0179, 0x01 }, { 0x01AF, 0x01 }, { 0x0201, 0xFB }, { 0x0254, 0xF7 }, { 0x0264, 0x7F }, @@ -704,8 +704,8 @@ static void setInitialCgbWram(unsigned char *const wram) { wram[cgbWramDumpDiff[i].addr] = cgbWramDumpDiff[i].val; } -static void setInitialDmgWram(unsigned char *const wram) { - static const struct { unsigned short addr; unsigned char val; } dmgWramDumpDiff[] = { +static void setInitialDmgWram(unsigned char wram[]) { + static struct { unsigned short addr; unsigned char val; } const dmgWramDumpDiff[] = { { 0x0000, 0x08 }, { 0x0004, 0x08 }, { 0x0008, 0x4D }, { 0x000A, 0x80 }, { 0x0010, 0x02 }, { 0x0018, 0x04 }, { 0x0020, 0x10 }, { 0x0028, 0x05 }, { 0x002C, 0x08 }, { 0x0038, 0x21 }, { 0x003A, 0x40 }, { 0x0060, 0x02 }, @@ -976,8 +976,8 @@ static void setInitialDmgWram(unsigned char *const wram) { wram[dmgWramDumpDiff[i].addr] = dmgWramDumpDiff[i].val; } -static void setInitialVram(unsigned char *const vram, const bool cgb) { - static const unsigned char even_numbered_8010_to_81a0_dump[] = { +static void setInitialVram(unsigned char vram[], bool const cgb) { + static unsigned char const even_numbered_8010_to_81a0_dump[] = { 0xF0, 0xF0, 0xFC, 0xFC, 0xFC, 0xFC, 0xF3, 0xF3, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0xF0, 0xF0, 0xF0, 0xF0, 0x00, 0x00, 0xF3, 0xF3, @@ -1024,8 +1024,8 @@ static void setInitialVram(unsigned char *const vram, const bool cgb) { } } -static void setInitialCgbIoamhram(unsigned char *const ioamhram) { - static const unsigned char feaxDump[0x60] = { +static void setInitialCgbIoamhram(unsigned char ioamhram[]) { + static unsigned char const feaxDump[0x60] = { 0x08, 0x01, 0xEF, 0xDE, 0x06, 0x4A, 0xCD, 0xBD, 0x08, 0x01, 0xEF, 0xDE, 0x06, 0x4A, 0xCD, 0xBD, 0x08, 0x01, 0xEF, 0xDE, 0x06, 0x4A, 0xCD, 0xBD, @@ -1040,7 +1040,7 @@ static void setInitialCgbIoamhram(unsigned char *const ioamhram) { 0x24, 0x13, 0xFD, 0x3A, 0x10, 0x10, 0xAD, 0x45 }; - static const unsigned char ffxxDump[0x100] = { + static unsigned char const ffxxDump[0x100] = { 0xCF, 0x00, 0x7C, 0xFF, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, 0x80, 0x3F, 0x00, 0xFF, 0xBF, 0xFF, 0x3F, 0x00, @@ -1080,8 +1080,8 @@ static void setInitialCgbIoamhram(unsigned char *const ioamhram) { std::memcpy(ioamhram + 0x100, ffxxDump, sizeof ffxxDump); } -static void setInitialDmgIoamhram(unsigned char *const ioamhram) { - static const unsigned char oamDump[0xA0] = { +static void setInitialDmgIoamhram(unsigned char ioamhram[]) { + static unsigned char const oamDump[0xA0] = { 0xBB, 0xD8, 0xC4, 0x04, 0xCD, 0xAC, 0xA1, 0xC7, 0x7D, 0x85, 0x15, 0xF0, 0xAD, 0x19, 0x11, 0x6A, 0xBA, 0xC7, 0x76, 0xF8, 0x5C, 0xA0, 0x67, 0x0A, @@ -1104,7 +1104,7 @@ static void setInitialDmgIoamhram(unsigned char *const ioamhram) { 0x5E, 0xC1, 0x97, 0x7E, 0x44, 0x05, 0x01, 0xA9 }; - static const unsigned char ffxxDump[0x100] = { + static unsigned char const ffxxDump[0x100] = { 0xCF, 0x00, 0x7E, 0xFF, 0xD3, 0x00, 0x00, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, 0x80, 0xBF, 0xF3, 0xFF, 0xBF, 0xFF, 0x3F, 0x00, @@ -1147,7 +1147,7 @@ static void setInitialDmgIoamhram(unsigned char *const ioamhram) { } // anon namespace void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbMode, const std::uint32_t now, const unsigned div) { - static const unsigned char cgbObjpDump[0x40] = { + static unsigned char const cgbObjpDump[0x40] = { 0x00, 0x00, 0xF2, 0xAB, 0x61, 0xC2, 0xD9, 0xBA, 0x88, 0x6E, 0xDD, 0x63, @@ -1182,7 +1182,7 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM state.mem.cgbSwitching = false; state.mem.agbMode = gbaCgbMode; - std::memset(state.mem.sram.ptr, 0xFF, state.mem.sram.getSz()); + std::memset(state.mem.sram.ptr, 0xFF, state.mem.sram.size()); setInitialVram(state.mem.vram.ptr, cgb); @@ -1263,8 +1263,8 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM state.ppu.pendingLcdstatIrq = false; state.ppu.isCgb = cgb; - - state.spu.cycleCounter = 0; // spu.cycleCounter >> 12 & 7 represents the frame sequencer position. + // spu.cycleCounter >> 12 & 7 represents the frame sequencer position. + state.spu.cycleCounter = state.cpu.cycleCounter >> 1; state.spu.ch1.sweep.counter = SoundUnit::counter_disabled; state.spu.ch1.sweep.shadow = 0; diff --git a/libgambatte/src/savestate.h b/libgambatte/src/savestate.h index 950244f1c0..5dd72a10cc 100644 --- a/libgambatte/src/savestate.h +++ b/libgambatte/src/savestate.h @@ -1,24 +1,25 @@ -/*************************************************************************** - * Copyright (C) 2008 by Sindre Aamås * - * aamas@stud.ntnu.no * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License version 2 as * - * published by the Free Software Foundation. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License version 2 for more details. * - * * - * You should have received a copy of the GNU General Public License * - * version 2 along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ +// +// Copyright (C) 2008 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + #ifndef SAVESTATE_H #define SAVESTATE_H +#include #include namespace gambatte { @@ -28,17 +29,17 @@ class SaverList; struct SaveState { template class Ptr { - T *ptr; - unsigned long sz; - public: - Ptr() : ptr(0), sz(0) {} - const T* get() const { return ptr; } - unsigned long getSz() const { return sz; } - void set(T *ptr, const unsigned long sz) { this->ptr = ptr; this->sz = sz; } + Ptr() : ptr(0), size_(0) {} + T const * get() const { return ptr; } + std::size_t size() const { return size_; } + void set(T *p, std::size_t size) { ptr = p; size_ = size; } friend class SaverList; friend void setInitState(SaveState &, bool, bool, std::uint32_t, unsigned); + private: + T *ptr; + std::size_t size_; }; struct CPU { @@ -73,15 +74,15 @@ struct SaveState { unsigned short dmaDestination; unsigned char rambank; unsigned char oamDmaPos; - bool IME; - bool halted; - bool enableRam; - bool rambankMode; - bool hdmaTransfer; - bool biosMode; - bool cgbSwitching; - bool agbMode; - bool gbIsCgb; + unsigned char /*bool*/ IME; + unsigned char /*bool*/ halted; + unsigned char /*bool*/ enableRam; + unsigned char /*bool*/ rambankMode; + unsigned char /*bool*/ hdmaTransfer; + unsigned char /*bool*/ biosMode; + unsigned char /*bool*/ cgbSwitching; + unsigned char /*bool*/ agbMode; + unsigned char /*bool*/ gbIsCgb; } mem; struct PPU { @@ -115,9 +116,9 @@ struct SaveState { unsigned char oldWy; unsigned char winDrawState; unsigned char wscx; - bool weMaster; - bool pendingLcdstatIrq; - bool isCgb; + unsigned char /*bool*/ weMaster; + unsigned char /*bool*/ pendingLcdstatIrq; + unsigned char /*bool*/ isCgb; } ppu; struct SPU { @@ -143,13 +144,13 @@ struct SaveState { unsigned long counter; unsigned short shadow; unsigned char nr0; - bool negging; + unsigned char /*bool*/ negging; } sweep; Duty duty; Env env; LCounter lcounter; unsigned char nr4; - bool master; + unsigned char /*bool*/ master; } ch1; struct { @@ -157,7 +158,7 @@ struct SaveState { Env env; LCounter lcounter; unsigned char nr4; - bool master; + unsigned char /*bool*/ master; } ch2; struct { @@ -169,7 +170,7 @@ struct SaveState { unsigned char nr4; unsigned char wavePos; unsigned char sampleBuf; - bool master; + unsigned char /*bool*/ master; } ch3; struct { @@ -180,7 +181,7 @@ struct SaveState { Env env; LCounter lcounter; unsigned char nr4; - bool master; + unsigned char /*bool*/ master; } ch4; unsigned long cycleCounter; @@ -194,7 +195,7 @@ struct SaveState { unsigned char dataH; unsigned char dataM; unsigned char dataS; - bool lastLatchData; + unsigned char /*bool*/ lastLatchData; } rtc; }; diff --git a/output/dll/libgambatte.dll b/output/dll/libgambatte.dll index 46598c121389db585c027ba268dd55a2e3a1b874..290ab90b78956e0656c817d35a5513595908d85f 100644 GIT binary patch delta 8372 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 He(nBW!67RA delta 8408 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 From 839c8abf54f197836819f7b73512a8ab2d4e9c4f Mon Sep 17 00:00:00 2001 From: MrWint Date: Sun, 26 May 2019 10:52:18 +0200 Subject: [PATCH 20/38] Build x64 version --- output/dll/libgambatte.dll | Bin 142848 -> 168448 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/output/dll/libgambatte.dll b/output/dll/libgambatte.dll index 290ab90b78956e0656c817d35a5513595908d85f..5176758d4b38917be743641deb308a04378ac8de 100644 GIT binary patch literal 168448 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 a+moLteWLb>m?txy3_e-?BnAG1?SBIwLAPE2 literal 142848 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 XORjf01+U!a{#p-0|NryfS_A(9^u4sB From a79febdef9a302cea49aa7885301c4c83481fea0 Mon Sep 17 00:00:00 2001 From: MrWint Date: Sun, 26 May 2019 12:06:40 +0200 Subject: [PATCH 21/38] Refactor cartridge code --- libgambatte/include/gambatte.h | 88 ++-- libgambatte/include/gbint.h | 36 +- libgambatte/src/mem/cartridge.cpp | 669 ++++++++++++++++-------------- libgambatte/src/mem/cartridge.h | 119 +++--- libgambatte/src/mem/memptrs.cpp | 107 ++--- libgambatte/src/mem/memptrs.h | 105 ++--- libgambatte/src/mem/rtc.cpp | 231 +++++------ libgambatte/src/mem/rtc.h | 112 +++-- libgambatte/src/memory.cpp | 22 +- output/dll/libgambatte.dll | Bin 168448 -> 168960 bytes 10 files changed, 761 insertions(+), 728 deletions(-) diff --git a/libgambatte/include/gambatte.h b/libgambatte/include/gambatte.h index ef4e4da646..0cccaafa9a 100644 --- a/libgambatte/include/gambatte.h +++ b/libgambatte/include/gambatte.h @@ -1,21 +1,21 @@ -/*************************************************************************** - * Copyright (C) 2007 by Sindre Aamås * - * aamas@stud.ntnu.no * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License version 2 as * - * published by the Free Software Foundation. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License version 2 for more details. * - * * - * You should have received a copy of the GNU General Public License * - * version 2 along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ +// +// Copyright (C) 2007 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + #ifndef GAMBATTE_H #define GAMBATTE_H @@ -23,11 +23,10 @@ #include "loadres.h" #include #include -#include -#include #include "newstate.h" namespace gambatte { + enum { BG_PALETTE = 0, SP1_PALETTE = 1, SP2_PALETTE = 2 }; typedef void (*MemoryCallback)(int32_t address, int64_t cycleOffset); @@ -57,9 +56,11 @@ public: MULTICART_COMPAT = 4, /**< Use heuristics to detect and support some multicart MBCs disguised as MBC1. */ }; - /** Load ROM image. + /** + * Load ROM image. * - * @param romfile Path to rom image file. Typically a .gbc, .gb, or .zip-file (if zip-support is compiled in). + * @param romfile Path to rom image file. Typically a .gbc, .gb, or .zip-file (if + * zip-support is compiled in). * @param flags ORed combination of LoadFlags. * @return 0 on success, negative value on failure. */ @@ -68,24 +69,33 @@ public: int loadGBCBios(const char* biosfiledata); int loadDMGBios(const char* biosfiledata); - /** Emulates until at least 'samples' stereo sound samples are produced in the supplied buffer, - * or until a video frame has been drawn. + /** + * Emulates until at least 'samples' audio samples are produced in the + * supplied audio buffer, or until a video frame has been drawn. * - * There are 35112 stereo sound samples in a video frame. - * May run for up to 2064 stereo samples too long. - * A stereo sample consists of two native endian 2s complement 16-bit PCM samples, - * with the left sample preceding the right one. Usually casting soundBuf to/from - * short* is OK and recommended. The reason for not using a short* in the interface - * is to avoid implementation-defined behaviour without compromising performance. + * There are 35112 audio (stereo) samples in a video frame. + * May run for up to 2064 audio samples too long. + * + * An audio sample consists of two native endian 2s complement 16-bit PCM samples, + * with the left sample preceding the right one. Usually casting audioBuf to + * int16_t* is OK. The reason for using an uint_least32_t* in the interface is to + * avoid implementation-defined behavior without compromising performance. + * libgambatte is strictly c++98, so fixed-width types are not an option (and even + * c99/c++11 cannot guarantee their availability). * * Returns early when a new video frame has finished drawing in the video buffer, * such that the caller may update the video output before the frame is overwritten. * The return value indicates whether a new video frame has been drawn, and the - * exact time (in number of samples) at which it was drawn. + * exact time (in number of samples) at which it was completed. * - * @param soundBuf buffer with space >= samples + 2064 - * @param samples in: number of stereo samples to produce, out: actual number of samples produced - * @return sample number at which the video frame was produced. -1 means no frame was produced. + * @param videoBuf 160x144 RGB32 (native endian) video frame buffer or 0 + * @param pitch distance in number of pixels (not bytes) from the start of one line + * to the next in videoBuf. + * @param audioBuf buffer with space >= samples + 2064 + * @param samples in: number of stereo samples to produce, + * out: actual number of samples produced + * @return sample offset in audioBuf at which the video frame was completed, or -1 + * if no new video frame was completed. */ std::ptrdiff_t runFor(gambatte::uint_least32_t *soundBuf, std::size_t &samples); @@ -93,12 +103,14 @@ public: void setLayers(unsigned mask); - /** Reset to initial state. + /** + * Reset to initial state. * Equivalent to reloading a ROM image, or turning a Game Boy Color off and on again. */ void reset(std::uint32_t now, unsigned div); - /** @param palNum 0 <= palNum < 3. One of BG_PALETTE, SP1_PALETTE and SP2_PALETTE. + /** + * @param palNum 0 <= palNum < 3. One of BG_PALETTE, SP1_PALETTE and SP2_PALETTE. * @param colorNum 0 <= colorNum < 4 */ void setDmgPaletteColor(int palNum, int colorNum, unsigned long rgb32); @@ -150,8 +162,8 @@ private: struct Priv; Priv *const p_; - GB(const GB &); - GB & operator=(const GB &); + GB(GB const &); + GB & operator=(GB const &); }; } diff --git a/libgambatte/include/gbint.h b/libgambatte/include/gbint.h index 8feb2aac25..272a9e8318 100644 --- a/libgambatte/include/gbint.h +++ b/libgambatte/include/gbint.h @@ -1,21 +1,21 @@ -/*************************************************************************** - * Copyright (C) 2007 by Sindre Aamås * - * aamas@stud.ntnu.no * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License version 2 as * - * published by the Free Software Foundation. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License version 2 for more details. * - * * - * You should have received a copy of the GNU General Public License * - * version 2 along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ +// +// Copyright (C) 2007 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + #ifndef GAMBATTE_INT_H #define GAMBATTE_INT_H diff --git a/libgambatte/src/mem/cartridge.cpp b/libgambatte/src/mem/cartridge.cpp index 911036f94b..18105d9d21 100644 --- a/libgambatte/src/mem/cartridge.cpp +++ b/libgambatte/src/mem/cartridge.cpp @@ -1,21 +1,21 @@ -/*************************************************************************** - * Copyright (C) 2007-2010 by Sindre Aamås * - * aamas@stud.ntnu.no * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License version 2 as * - * published by the Free Software Foundation. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License version 2 for more details. * - * * - * You should have received a copy of the GNU General Public License * - * version 2 along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ +// +// Copyright (C) 2007-2010 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + #include "cartridge.h" #include "../savestate.h" #include @@ -27,7 +27,7 @@ namespace gambatte { namespace { -static unsigned toMulti64Rombank(const unsigned rombank) { +static unsigned toMulti64Rombank(unsigned rombank) { return (rombank >> 1 & 0x30) | (rombank & 0xF); } @@ -43,83 +43,75 @@ public: }; class Mbc0 : public DefaultMbc { - MemPtrs &memptrs; - bool enableRam; - public: explicit Mbc0(MemPtrs &memptrs) - : memptrs(memptrs), - enableRam(false) + : memptrs_(memptrs) + , enableRam_(false) { } - virtual void romWrite(const unsigned P, const unsigned data) { - if (P < 0x2000) { - enableRam = (data & 0xF) == 0xA; - memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, 0); + virtual void romWrite(unsigned const p, unsigned const data) { + if (p < 0x2000) { + enableRam_ = (data & 0xF) == 0xA; + memptrs_.setRambank(enableRam_ ? MemPtrs::read_en | MemPtrs::write_en : 0, 0); } } virtual void saveState(SaveState::Mem &ss) const { - ss.enableRam = enableRam; + ss.enableRam = enableRam_; } - virtual void loadState(const SaveState::Mem &ss) { - enableRam = ss.enableRam; - memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, 0); + virtual void loadState(SaveState::Mem const &ss) { + enableRam_ = ss.enableRam; + memptrs_.setRambank(enableRam_ ? MemPtrs::read_en | MemPtrs::write_en : 0, 0); } +private: + MemPtrs &memptrs_; + bool enableRam_; + +public: virtual void SyncState(NewState *ns, bool isReader) { - NSS(enableRam); + NSS(enableRam_); } }; -static inline unsigned rambanks(const MemPtrs &memptrs) { +static inline unsigned rambanks(MemPtrs const &memptrs) { return std::size_t(memptrs.rambankdataend() - memptrs.rambankdata()) / 0x2000; } -static inline unsigned rombanks(const MemPtrs &memptrs) { +static inline unsigned rombanks(MemPtrs const &memptrs) { return std::size_t(memptrs.romdataend() - memptrs.romdata() ) / 0x4000; } class Mbc1 : public DefaultMbc { - MemPtrs &memptrs; - unsigned char rombank; - unsigned char rambank; - bool enableRam; - bool rambankMode; - - static unsigned adjustedRombank(unsigned bank) { return bank & 0x1F ? bank : bank | 1; } - void setRambank() const { memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, rambank & (rambanks(memptrs) - 1)); } - void setRombank() const { memptrs.setRombank(adjustedRombank(rombank) & (rombanks(memptrs) - 1)); } - public: explicit Mbc1(MemPtrs &memptrs) - : memptrs(memptrs), - rombank(1), - rambank(0), - enableRam(false), - rambankMode(false) + : memptrs_(memptrs) + , rombank_(1) + , rambank_(0) + , enableRam_(false) + , rambankMode_(false) { } - virtual void romWrite(const unsigned P, const unsigned data) { - switch (P >> 13 & 3) { + virtual void romWrite(unsigned const p, unsigned const data) { + switch (p >> 13 & 3) { case 0: - enableRam = (data & 0xF) == 0xA; + enableRam_ = (data & 0xF) == 0xA; setRambank(); break; case 1: - rombank = rambankMode ? data & 0x1F : (rombank & 0x60) | (data & 0x1F); + rombank_ = rambankMode_ ? data & 0x1F : (rombank_ & 0x60) | (data & 0x1F); setRombank(); break; case 2: - if (rambankMode) { - rambank = data & 3; + if (rambankMode_) { + rambank_ = data & 3; setRambank(); } else { - rombank = (data << 5 & 0x60) | (rombank & 0x1F); + rombank_ = (data << 5 & 0x60) | (rombank_ & 0x1F); setRombank(); } @@ -127,98 +119,98 @@ public: case 3: // Pretty sure this should take effect immediately, but I have a policy not to change old behavior // unless I have something (eg. a verified test or a game) that justifies it. - rambankMode = data & 1; + rambankMode_ = data & 1; break; } } virtual void saveState(SaveState::Mem &ss) const { - ss.rombank = rombank; - ss.rambank = rambank; - ss.enableRam = enableRam; - ss.rambankMode = rambankMode; + ss.rombank = rombank_; + ss.rambank = rambank_; + ss.enableRam = enableRam_; + ss.rambankMode = rambankMode_; } - virtual void loadState(const SaveState::Mem &ss) { - rombank = ss.rombank; - rambank = ss.rambank; - enableRam = ss.enableRam; - rambankMode = ss.rambankMode; + virtual void loadState(SaveState::Mem const &ss) { + rombank_ = ss.rombank; + rambank_ = ss.rambank; + enableRam_ = ss.enableRam; + rambankMode_ = ss.rambankMode; setRambank(); setRombank(); } +private: + MemPtrs &memptrs_; + unsigned char rombank_; + unsigned char rambank_; + bool enableRam_; + bool rambankMode_; + + static unsigned adjustedRombank(unsigned bank) { return bank & 0x1F ? bank : bank | 1; } + + void setRambank() const { + memptrs_.setRambank(enableRam_ ? MemPtrs::read_en | MemPtrs::write_en : 0, + rambank_ & (rambanks(memptrs_) - 1)); + } + + void setRombank() const { memptrs_.setRombank(adjustedRombank(rombank_) & (rombanks(memptrs_) - 1)); } + + +public: virtual void SyncState(NewState *ns, bool isReader) { - NSS(rombank); - NSS(rambank); - NSS(enableRam); - NSS(rambankMode); + NSS(rombank_); + NSS(rambank_); + NSS(enableRam_); + NSS(rambankMode_); } }; class Mbc1Multi64 : public Mbc { - MemPtrs &memptrs; - unsigned char rombank; - bool enableRam; - bool rombank0Mode; - - static unsigned adjustedRombank(unsigned bank) { return bank & 0x1F ? bank : bank | 1; } - - void setRombank() const { - if (rombank0Mode) { - const unsigned rb = toMulti64Rombank(rombank); - memptrs.setRombank0(rb & 0x30); - memptrs.setRombank(adjustedRombank(rb)); - } else { - memptrs.setRombank0(0); - memptrs.setRombank(adjustedRombank(rombank) & (rombanks(memptrs) - 1)); - } - } - public: explicit Mbc1Multi64(MemPtrs &memptrs) - : memptrs(memptrs), - rombank(1), - enableRam(false), - rombank0Mode(false) + : memptrs_(memptrs) + , rombank_(1) + , enableRam_(false) + , rombank0Mode_(false) { } - virtual void romWrite(const unsigned P, const unsigned data) { - switch (P >> 13 & 3) { + virtual void romWrite(unsigned const p, unsigned const data) { + switch (p >> 13 & 3) { case 0: - enableRam = (data & 0xF) == 0xA; - memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, 0); + enableRam_ = (data & 0xF) == 0xA; + memptrs_.setRambank(enableRam_ ? MemPtrs::read_en | MemPtrs::write_en : 0, 0); break; case 1: - rombank = (rombank & 0x60) | (data & 0x1F); - memptrs.setRombank(rombank0Mode - ? adjustedRombank(toMulti64Rombank(rombank)) - : adjustedRombank(rombank) & (rombanks(memptrs) - 1)); + rombank_ = (rombank_ & 0x60) | (data & 0x1F); + memptrs_.setRombank(rombank0Mode_ + ? adjustedRombank(toMulti64Rombank(rombank_)) + : adjustedRombank(rombank_) & (rombanks(memptrs_) - 1)); break; case 2: - rombank = (data << 5 & 0x60) | (rombank & 0x1F); + rombank_ = (data << 5 & 0x60) | (rombank_ & 0x1F); setRombank(); break; case 3: - rombank0Mode = data & 1; + rombank0Mode_ = data & 1; setRombank(); break; } } virtual void saveState(SaveState::Mem &ss) const { - ss.rombank = rombank; - ss.enableRam = enableRam; - ss.rambankMode = rombank0Mode; + ss.rombank = rombank_; + ss.enableRam = enableRam_; + ss.rambankMode = rombank0Mode_; } - virtual void loadState(const SaveState::Mem &ss) { - rombank = ss.rombank; - enableRam = ss.enableRam; - rombank0Mode = ss.rambankMode; - memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, 0); + virtual void loadState(SaveState::Mem const &ss) { + rombank_ = ss.rombank; + enableRam_ = ss.enableRam; + rombank0Mode_ = ss.rambankMode; + memptrs_.setRambank(enableRam_ ? MemPtrs::read_en | MemPtrs::write_en : 0, 0); setRombank(); } @@ -226,177 +218,188 @@ public: return (addr < 0x4000) == ((bank & 0xF) == 0); } +private: + MemPtrs &memptrs_; + unsigned char rombank_; + bool enableRam_; + bool rombank0Mode_; + + static unsigned adjustedRombank(unsigned bank) { return bank & 0x1F ? bank : bank | 1; } + + void setRombank() const { + if (rombank0Mode_) { + unsigned const rb = toMulti64Rombank(rombank_); + memptrs_.setRombank0(rb & 0x30); + memptrs_.setRombank(adjustedRombank(rb)); + } else { + memptrs_.setRombank0(0); + memptrs_.setRombank(adjustedRombank(rombank_) & (rombanks(memptrs_) - 1)); + } + } + +public: virtual void SyncState(NewState *ns, bool isReader) { - NSS(rombank); - NSS(enableRam); - NSS(rombank0Mode); + NSS(rombank_); + NSS(enableRam_); + NSS(rombank0Mode_); } }; class Mbc2 : public DefaultMbc { - MemPtrs &memptrs; - unsigned char rombank; - bool enableRam; - public: explicit Mbc2(MemPtrs &memptrs) - : memptrs(memptrs), - rombank(1), - enableRam(false) + : memptrs_(memptrs) + , rombank_(1) + , enableRam_(false) { } - virtual void romWrite(const unsigned P, const unsigned data) { - switch (P & 0x6100) { + virtual void romWrite(unsigned const p, unsigned const data) { + switch (p & 0x6100) { case 0x0000: - enableRam = (data & 0xF) == 0xA; - memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, 0); + enableRam_ = (data & 0xF) == 0xA; + memptrs_.setRambank(enableRam_ ? MemPtrs::read_en | MemPtrs::write_en : 0, 0); break; case 0x2100: - rombank = data & 0xF; - memptrs.setRombank(rombank & (rombanks(memptrs) - 1)); + rombank_ = data & 0xF; + memptrs_.setRombank(rombank_ & (rombanks(memptrs_) - 1)); break; } } virtual void saveState(SaveState::Mem &ss) const { - ss.rombank = rombank; - ss.enableRam = enableRam; + ss.rombank = rombank_; + ss.enableRam = enableRam_; } - virtual void loadState(const SaveState::Mem &ss) { - rombank = ss.rombank; - enableRam = ss.enableRam; - memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, 0); - memptrs.setRombank(rombank & (rombanks(memptrs) - 1)); + virtual void loadState(SaveState::Mem const &ss) { + rombank_ = ss.rombank; + enableRam_ = ss.enableRam; + memptrs_.setRambank(enableRam_ ? MemPtrs::read_en | MemPtrs::write_en : 0, 0); + memptrs_.setRombank(rombank_ & (rombanks(memptrs_) - 1)); } +private: + MemPtrs &memptrs_; + unsigned char rombank_; + bool enableRam_; + +public: virtual void SyncState(NewState *ns, bool isReader) { - NSS(rombank); - NSS(enableRam); + NSS(rombank_); + NSS(enableRam_); } }; class Mbc3 : public DefaultMbc { - MemPtrs &memptrs; - Rtc *const rtc; - unsigned char rombank; - unsigned char rambank; - bool enableRam; - - static unsigned adjustedRombank(unsigned bank) { return bank & 0x7F ? bank : bank | 1; } - void setRambank() const { - unsigned flags = enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0; - - if (rtc) { - rtc->set(enableRam, rambank); - - if (rtc->getActive()) - flags |= MemPtrs::RTC_EN; - } - - memptrs.setRambank(flags, rambank & (rambanks(memptrs) - 1)); - } - // we adjust the rombank before masking with size? this seems correct, as how would the mbc - // know that high rom address outputs were not connected - void setRombank() const { memptrs.setRombank(adjustedRombank(rombank) & (rombanks(memptrs) - 1)); } - public: Mbc3(MemPtrs &memptrs, Rtc *const rtc) - : memptrs(memptrs), - rtc(rtc), - rombank(1), - rambank(0), - enableRam(false) + : memptrs_(memptrs) + , rtc_(rtc) + , rombank_(1) + , rambank_(0) + , enableRam_(false) { } - virtual void romWrite(const unsigned P, const unsigned data) { - switch (P >> 13 & 3) { + virtual void romWrite(unsigned const p, unsigned const data) { + switch (p >> 13 & 3) { case 0: - enableRam = (data & 0xF) == 0xA; + enableRam_ = (data & 0xF) == 0xA; setRambank(); break; case 1: - rombank = data & 0x7F; + rombank_ = data & 0x7F; setRombank(); break; case 2: - rambank = data; + rambank_ = data; setRambank(); break; case 3: - if (rtc) - rtc->latch(data); + if (rtc_) + rtc_->latch(data); break; } } virtual void saveState(SaveState::Mem &ss) const { - ss.rombank = rombank; - ss.rambank = rambank; - ss.enableRam = enableRam; + ss.rombank = rombank_; + ss.rambank = rambank_; + ss.enableRam = enableRam_; } - virtual void loadState(const SaveState::Mem &ss) { - rombank = ss.rombank; - rambank = ss.rambank; - enableRam = ss.enableRam; + virtual void loadState(SaveState::Mem const &ss) { + rombank_ = ss.rombank; + rambank_ = ss.rambank; + enableRam_ = ss.enableRam; setRambank(); setRombank(); } +private: + MemPtrs &memptrs_; + Rtc *const rtc_; + unsigned char rombank_; + unsigned char rambank_; + bool enableRam_; + + void setRambank() const { + unsigned flags = enableRam_ ? MemPtrs::read_en | MemPtrs::write_en : 0; + + if (rtc_) { + rtc_->set(enableRam_, rambank_); + + if (rtc_->activeData()) + flags |= MemPtrs::rtc_en; + } + + memptrs_.setRambank(flags, rambank_ & (rambanks(memptrs_) - 1)); + } + + void setRombank() const { + memptrs_.setRombank(std::max(rombank_ & (rombanks(memptrs_) - 1), 1u)); + } + +public: virtual void SyncState(NewState *ns, bool isReader) { - NSS(rombank); - NSS(rambank); - NSS(enableRam); + NSS(rombank_); + NSS(rambank_); + NSS(enableRam_); } }; class HuC1 : public DefaultMbc { - MemPtrs &memptrs; - unsigned char rombank; - unsigned char rambank; - bool enableRam; - bool rambankMode; - - void setRambank() const { - memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : MemPtrs::READ_EN, - rambankMode ? rambank & (rambanks(memptrs) - 1) : 0); - } - - void setRombank() const { memptrs.setRombank((rambankMode ? rombank : rambank << 6 | rombank) & (rombanks(memptrs) - 1)); } - public: explicit HuC1(MemPtrs &memptrs) - : memptrs(memptrs), - rombank(1), - rambank(0), - enableRam(false), - rambankMode(false) + : memptrs_(memptrs) + , rombank_(1) + , rambank_(0) + , enableRam_(false) + , rambankMode_(false) { } - virtual void romWrite(const unsigned P, const unsigned data) { - switch (P >> 13 & 3) { + virtual void romWrite(unsigned const p, unsigned const data) { + switch (p >> 13 & 3) { case 0: - enableRam = (data & 0xF) == 0xA; + enableRam_ = (data & 0xF) == 0xA; setRambank(); break; case 1: - rombank = data & 0x3F; + rombank_ = data & 0x3F; setRombank(); break; case 2: - rambank = data & 3; - rambankMode ? setRambank() : setRombank(); + rambank_ = data & 3; + rambankMode_ ? setRambank() : setRombank(); break; case 3: - rambankMode = data & 1; + rambankMode_ = data & 1; setRambank(); setRombank(); break; @@ -404,67 +407,72 @@ public: } virtual void saveState(SaveState::Mem &ss) const { - ss.rombank = rombank; - ss.rambank = rambank; - ss.enableRam = enableRam; - ss.rambankMode = rambankMode; + ss.rombank = rombank_; + ss.rambank = rambank_; + ss.enableRam = enableRam_; + ss.rambankMode = rambankMode_; } - virtual void loadState(const SaveState::Mem &ss) { - rombank = ss.rombank; - rambank = ss.rambank; - enableRam = ss.enableRam; - rambankMode = ss.rambankMode; + virtual void loadState(SaveState::Mem const &ss) { + rombank_ = ss.rombank; + rambank_ = ss.rambank; + enableRam_ = ss.enableRam; + rambankMode_ = ss.rambankMode; setRambank(); setRombank(); } +private: + MemPtrs &memptrs_; + unsigned char rombank_; + unsigned char rambank_; + bool enableRam_; + bool rambankMode_; + + void setRambank() const { + memptrs_.setRambank(enableRam_ ? MemPtrs::read_en | MemPtrs::write_en : MemPtrs::read_en, + rambankMode_ ? rambank_ & (rambanks(memptrs_) - 1) : 0); + } + + void setRombank() const { + memptrs_.setRombank((rambankMode_ ? rombank_ : rambank_ << 6 | rombank_) + & (rombanks(memptrs_) - 1)); + } + +public: virtual void SyncState(NewState *ns, bool isReader) { - NSS(rombank); - NSS(rambank); - NSS(enableRam); - NSS(rambankMode); + NSS(rombank_); + NSS(rambank_); + NSS(enableRam_); + NSS(rambankMode_); } }; class Mbc5 : public DefaultMbc { - MemPtrs &memptrs; - unsigned short rombank; - unsigned char rambank; - bool enableRam; - - static unsigned adjustedRombank(const unsigned bank) { return bank; } - - void setRambank() const { - memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, - rambank & (rambanks(memptrs) - 1)); - } - - void setRombank() const { memptrs.setRombank(adjustedRombank(rombank) & (rombanks(memptrs) - 1)); } - public: explicit Mbc5(MemPtrs &memptrs) - : memptrs(memptrs), - rombank(1), - rambank(0), - enableRam(false) + : memptrs_(memptrs) + , rombank_(1) + , rambank_(0) + , enableRam_(false) { } - virtual void romWrite(const unsigned P, const unsigned data) { - switch (P >> 13 & 3) { + virtual void romWrite(unsigned const p, unsigned const data) { + switch (p >> 13 & 3) { case 0: - enableRam = (data & 0xF) == 0xA; + enableRam_ = (data & 0xF) == 0xA; setRambank(); break; case 1: - rombank = P < 0x3000 ? (rombank & 0x100) | data - : (data << 8 & 0x100) | (rombank & 0xFF); + rombank_ = p < 0x3000 + ? (rombank_ & 0x100) | data + : (data << 8 & 0x100) | (rombank_ & 0xFF); setRombank(); break; case 2: - rambank = data & 0xF; + rambank_ = data & 0xF; setRambank(); break; case 3: @@ -473,28 +481,44 @@ public: } virtual void saveState(SaveState::Mem &ss) const { - ss.rombank = rombank; - ss.rambank = rambank; - ss.enableRam = enableRam; + ss.rombank = rombank_; + ss.rambank = rambank_; + ss.enableRam = enableRam_; } - virtual void loadState(const SaveState::Mem &ss) { - rombank = ss.rombank; - rambank = ss.rambank; - enableRam = ss.enableRam; + virtual void loadState(SaveState::Mem const &ss) { + rombank_ = ss.rombank; + rambank_ = ss.rambank; + enableRam_ = ss.enableRam; setRambank(); setRombank(); } +private: + MemPtrs &memptrs_; + unsigned short rombank_; + unsigned char rambank_; + bool enableRam_; + + static unsigned adjustedRombank(unsigned bank) { return bank; } + + void setRambank() const { + memptrs_.setRambank(enableRam_ ? MemPtrs::read_en | MemPtrs::write_en : 0, + rambank_ & (rambanks(memptrs_) - 1)); + } + + void setRombank() const { memptrs_.setRombank(adjustedRombank(rombank_) & (rombanks(memptrs_) - 1)); } + +public: virtual void SyncState(NewState *ns, bool isReader) { - NSS(rombank); - NSS(rambank); - NSS(enableRam); + NSS(rombank_); + NSS(rambank_); + NSS(enableRam_); } }; -static bool hasRtc(const unsigned headerByte0x147) { +static bool hasRtc(unsigned headerByte0x147) { switch (headerByte0x147) { case 0x0F: case 0x10: return true; @@ -505,19 +529,19 @@ static bool hasRtc(const unsigned headerByte0x147) { } void Cartridge::setStatePtrs(SaveState &state) { - state.mem.vram.set(memptrs.vramdata(), memptrs.vramdataend() - memptrs.vramdata()); - state.mem.sram.set(memptrs.rambankdata(), memptrs.rambankdataend() - memptrs.rambankdata()); - state.mem.wram.set(memptrs.wramdata(0), memptrs.wramdataend() - memptrs.wramdata(0)); + state.mem.vram.set(memptrs_.vramdata(), memptrs_.vramdataend() - memptrs_.vramdata()); + state.mem.sram.set(memptrs_.rambankdata(), memptrs_.rambankdataend() - memptrs_.rambankdata()); + state.mem.wram.set(memptrs_.wramdata(0), memptrs_.wramdataend() - memptrs_.wramdata(0)); } -void Cartridge::loadState(const SaveState &state) { - rtc.loadState(state); - mbc->loadState(state.mem); +void Cartridge::loadState(SaveState const &state) { + rtc_.loadState(state); + mbc_->loadState(state.mem); } -static void enforce8bit(unsigned char *data, unsigned long sz) { +static void enforce8bit(unsigned char *data, std::size_t size) { if (static_cast(0x100)) - while (sz--) + while (size--) *data++ &= 0xFF; } @@ -544,7 +568,7 @@ static unsigned numRambanksFromH14x(unsigned char h147, unsigned char h149) { return 4; } -static bool presumedMulti64Mbc1(unsigned char const header[], unsigned const rombanks) { +static bool presumedMulti64Mbc1(unsigned char const header[], unsigned rombanks) { return header[0x147] == 1 && header[0x149] == 0 && rombanks == 64; } @@ -554,28 +578,33 @@ LoadRes Cartridge::loadROM(const char *romfiledata, unsigned romfilelength, bool //if (rom->fail()) // return -1; + enum Cartridgetype { type_plain, + type_mbc1, + type_mbc2, + type_mbc3, + type_mbc5, + type_huc1 }; + Cartridgetype type = type_plain; unsigned rambanks = 1; unsigned rombanks = 2; bool cgb = false; - enum Cartridgetype { PLAIN, MBC1, MBC2, MBC3, MBC5, HUC1 } type = PLAIN; { unsigned char header[0x150]; - //rom->read(reinterpret_cast(header), sizeof header); if (romfilelength >= sizeof header) std::memcpy(header, romfiledata, sizeof header); else return LOADRES_IO_ERROR; switch (header[0x0147]) { - case 0x00: type = PLAIN; break; + case 0x00: type = type_plain; break; case 0x01: case 0x02: - case 0x03: type = MBC1; break; + case 0x03: type = type_mbc1; break; case 0x05: - case 0x06: type = MBC2; break; + case 0x06: type = type_mbc2; break; case 0x08: - case 0x09: type = PLAIN; break; + case 0x09: type = type_plain; break; case 0x0B: case 0x0C: case 0x0D: return LOADRES_UNSUPPORTED_MBC_MMM01; @@ -583,7 +612,7 @@ LoadRes Cartridge::loadROM(const char *romfiledata, unsigned romfilelength, bool case 0x10: case 0x11: case 0x12: - case 0x13: type = MBC3; break; + case 0x13: type = type_mbc3; break; case 0x15: case 0x16: case 0x17: return LOADRES_UNSUPPORTED_MBC_MBC4; @@ -592,14 +621,14 @@ LoadRes Cartridge::loadROM(const char *romfiledata, unsigned romfilelength, bool case 0x1B: case 0x1C: case 0x1D: - case 0x1E: type = MBC5; break; + case 0x1E: type = type_mbc5; break; case 0x20: return LOADRES_UNSUPPORTED_MBC_MBC6; case 0x22: return LOADRES_UNSUPPORTED_MBC_MBC7; case 0xFC: return LOADRES_UNSUPPORTED_MBC_POCKET_CAMERA; case 0xFD: return LOADRES_UNSUPPORTED_MBC_TAMA5; case 0xFE: return LOADRES_UNSUPPORTED_MBC_HUC3; - case 0xFF: type = HUC1; break; - default: return LOADRES_BAD_FILE_OR_UNKNOWN_MBC; + case 0xFF: type = type_huc1; break; + default: return LOADRES_BAD_FILE_OR_UNKNOWN_MBC; } /*switch (header[0x0148]) { @@ -619,44 +648,42 @@ LoadRes Cartridge::loadROM(const char *romfiledata, unsigned romfilelength, bool }*/ rambanks = numRambanksFromH14x(header[0x147], header[0x149]); - cgb = !forceDmg; } - std::size_t const filesize = romfilelength; //rom->size(); + std::size_t const filesize = romfilelength; rombanks = std::max(pow2ceil(filesize / 0x4000), 2u); - mbc.reset(); - memptrs.reset(rombanks, rambanks, cgb ? 8 : 2); - rtc.set(false, 0); + mbc_.reset(); + memptrs_.reset(rombanks, rambanks, cgb ? 8 : 2); + rtc_.set(false, 0); - //rom->rewind(); - //rom->read(reinterpret_cast(memptrs.romdata()), (filesize / 0x4000) * 0x4000ul); - std::memcpy(memptrs.romdata(), romfiledata, (filesize / 0x4000) * 0x4000ul); - std::memset(memptrs.romdata() + (filesize / 0x4000) * 0x4000ul, 0xFF, (rombanks - filesize / 0x4000) * 0x4000ul); - enforce8bit(memptrs.romdata(), rombanks * 0x4000ul); - - //if (rom->fail()) - // return -1; + std::memcpy(memptrs_.romdata(), romfiledata, (filesize / 0x4000) * 0x4000ul); + std::memset(memptrs_.romdata() + filesize / 0x4000 * 0x4000ul, + 0xFF, + (rombanks - filesize / 0x4000) * 0x4000ul); + enforce8bit(memptrs_.romdata(), rombanks * 0x4000ul); switch (type) { - case PLAIN: mbc.reset(new Mbc0(memptrs)); break; - case MBC1: - if (multicartCompat && presumedMulti64Mbc1(memptrs.romdata(), rombanks)) { - mbc.reset(new Mbc1Multi64(memptrs)); + case type_plain: mbc_.reset(new Mbc0(memptrs_)); break; + case type_mbc1: + if (multicartCompat && presumedMulti64Mbc1(memptrs_.romdata(), rombanks)) { + mbc_.reset(new Mbc1Multi64(memptrs_)); } else - mbc.reset(new Mbc1(memptrs)); + mbc_.reset(new Mbc1(memptrs_)); break; - case MBC2: mbc.reset(new Mbc2(memptrs)); break; - case MBC3: mbc.reset(new Mbc3(memptrs, hasRtc(memptrs.romdata()[0x147]) ? &rtc : 0)); break; - case MBC5: mbc.reset(new Mbc5(memptrs)); break; - case HUC1: mbc.reset(new HuC1(memptrs)); break; + case type_mbc2: mbc_.reset(new Mbc2(memptrs_)); break; + case type_mbc3: + mbc_.reset(new Mbc3(memptrs_, hasRtc(memptrs_.romdata()[0x147]) ? &rtc_ : 0)); + break; + case type_mbc5: mbc_.reset(new Mbc5(memptrs_)); break; + case type_huc1: mbc_.reset(new HuC1(memptrs_)); break; } return LOADRES_OK; } -static bool hasBattery(const unsigned char headerByte0x147) { +static bool hasBattery(unsigned char headerByte0x147) { switch (headerByte0x147) { case 0x03: case 0x06: @@ -672,40 +699,40 @@ static bool hasBattery(const unsigned char headerByte0x147) { } void Cartridge::loadSavedata(const char *data) { - if (hasBattery(memptrs.romdata()[0x147])) { - int length = memptrs.rambankdataend() - memptrs.rambankdata(); - std::memcpy(memptrs.rambankdata(), data, length); + if (hasBattery(memptrs_.romdata()[0x147])) { + int length = memptrs_.rambankdataend() - memptrs_.rambankdata(); + std::memcpy(memptrs_.rambankdata(), data, length); data += length; - enforce8bit(memptrs.rambankdata(), length); + enforce8bit(memptrs_.rambankdata(), length); } - if (hasRtc(memptrs.romdata()[0x147])) { + if (hasRtc(memptrs_.romdata()[0x147])) { unsigned long basetime; std::memcpy(&basetime, data, 4); - rtc.setBaseTime(basetime); + rtc_.setBaseTime(basetime); } } int Cartridge::saveSavedataLength() { int ret = 0; - if (hasBattery(memptrs.romdata()[0x147])) { - ret = memptrs.rambankdataend() - memptrs.rambankdata(); + if (hasBattery(memptrs_.romdata()[0x147])) { + ret = memptrs_.rambankdataend() - memptrs_.rambankdata(); } - if (hasRtc(memptrs.romdata()[0x147])) { + if (hasRtc(memptrs_.romdata()[0x147])) { ret += 4; } return ret; } void Cartridge::saveSavedata(char *dest) { - if (hasBattery(memptrs.romdata()[0x147])) { - int length = memptrs.rambankdataend() - memptrs.rambankdata(); - std::memcpy(dest, memptrs.rambankdata(), length); + if (hasBattery(memptrs_.romdata()[0x147])) { + int length = memptrs_.rambankdataend() - memptrs_.rambankdata(); + std::memcpy(dest, memptrs_.rambankdata(), length); dest += length; } - if (hasRtc(memptrs.romdata()[0x147])) { - const unsigned long basetime = rtc.getBaseTime(); + if (hasRtc(memptrs_.romdata()[0x147])) { + const unsigned long basetime = rtc_.getBaseTime(); std::memcpy(dest, &basetime, 4); } } @@ -717,20 +744,20 @@ bool Cartridge::getMemoryArea(int which, unsigned char **data, int *length) cons switch (which) { case 0: - *data = memptrs.vramdata(); - *length = memptrs.vramdataend() - memptrs.vramdata(); + *data = memptrs_.vramdata(); + *length = memptrs_.vramdataend() - memptrs_.vramdata(); return true; case 1: - *data = memptrs.romdata(); - *length = memptrs.romdataend() - memptrs.romdata(); + *data = memptrs_.romdata(); + *length = memptrs_.romdataend() - memptrs_.romdata(); return true; case 2: - *data = memptrs.wramdata(0); - *length = memptrs.wramdataend() - memptrs.wramdata(0); + *data = memptrs_.wramdata(0); + *length = memptrs_.wramdataend() - memptrs_.wramdata(0); return true; case 3: - *data = memptrs.rambankdata(); - *length = memptrs.rambankdataend() - memptrs.rambankdata(); + *data = memptrs_.rambankdata(); + *length = memptrs_.rambankdataend() - memptrs_.rambankdata(); return true; default: @@ -741,9 +768,9 @@ bool Cartridge::getMemoryArea(int which, unsigned char **data, int *length) cons SYNCFUNC(Cartridge) { - SSS(memptrs); - SSS(rtc); - TSS(mbc); + SSS(memptrs_); + SSS(rtc_); + TSS(mbc_); } } diff --git a/libgambatte/src/mem/cartridge.h b/libgambatte/src/mem/cartridge.h index dd7fd2e95c..245bf537aa 100644 --- a/libgambatte/src/mem/cartridge.h +++ b/libgambatte/src/mem/cartridge.h @@ -1,21 +1,21 @@ -/*************************************************************************** - * Copyright (C) 2007-2010 by Sindre Aamås * - * aamas@stud.ntnu.no * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License version 2 as * - * published by the Free Software Foundation. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License version 2 for more details. * - * * - * You should have received a copy of the GNU General Public License * - * version 2 along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ +// +// Copyright (C) 2007-2010 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + #ifndef CARTRIDGE_H #define CARTRIDGE_H @@ -30,26 +30,12 @@ namespace gambatte { - //DOOM -//enum eAddressMappingType -//{ -// eAddressMappingType_ROM, -// eAddressMappingType_RAM -//}; -// -//struct AddressMapping -//{ -// int32_t address; -// eAddressMappingType type; -//}; - class Mbc { public: virtual ~Mbc() {} virtual void romWrite(unsigned P, unsigned data) = 0; - virtual void loadState(const SaveState::Mem &ss) = 0; + virtual void loadState(SaveState::Mem const &ss) = 0; virtual bool isAddressWithinAreaRombankCanBeMappedTo(unsigned address, unsigned rombank) const = 0; - //virtual void mapAddress(AddressMapping* mapping, unsigned address) const = 0; //DOOM templatevoid SyncState(NewState *ns) { @@ -60,52 +46,45 @@ public: }; class Cartridge { - MemPtrs memptrs; - Rtc rtc; - std::unique_ptr mbc; - public: void setStatePtrs(SaveState &); - void loadState(const SaveState &); - - bool loaded() const { return mbc.get(); } - - const unsigned char * rmem(unsigned area) const { return memptrs.rmem(area); } - unsigned char * wmem(unsigned area) const { return memptrs.wmem(area); } - unsigned char * vramdata() const { return memptrs.vramdata(); } - unsigned char * romdata(unsigned area) const { return memptrs.romdata(area); } - unsigned char * wramdata(unsigned area) const { return memptrs.wramdata(area); } - const unsigned char * rdisabledRam() const { return memptrs.rdisabledRam(); } - const unsigned char * rsrambankptr() const { return memptrs.rsrambankptr(); } - unsigned char * wsrambankptr() const { return memptrs.wsrambankptr(); } - unsigned char * vrambankptr() const { return memptrs.vrambankptr(); } - OamDmaSrc oamDmaSrc() const { return memptrs.oamDmaSrc(); } - unsigned curRomBank() const { return memptrs.curRomBank(); } - - void setVrambank(unsigned bank) { memptrs.setVrambank(bank); } - void setWrambank(unsigned bank) { memptrs.setWrambank(bank); } - void setOamDmaSrc(OamDmaSrc oamDmaSrc) { memptrs.setOamDmaSrc(oamDmaSrc); } - - void mbcWrite(unsigned addr, unsigned data) { mbc->romWrite(addr, data); } - - bool isCgb() const { return gambatte::isCgb(memptrs); } - - void rtcWrite(unsigned data) { rtc.write(data); } - unsigned char rtcRead() const { return *rtc.getActive(); } - - void loadSavedata(const char *data); + void loadState(SaveState const &); + bool loaded() const { return mbc_.get(); } + unsigned char const * rmem(unsigned area) const { return memptrs_.rmem(area); } + unsigned char * wmem(unsigned area) const { return memptrs_.wmem(area); } + unsigned char * vramdata() const { return memptrs_.vramdata(); } + unsigned char * romdata(unsigned area) const { return memptrs_.romdata(area); } + unsigned char * wramdata(unsigned area) const { return memptrs_.wramdata(area); } + unsigned char const * rdisabledRam() const { return memptrs_.rdisabledRam(); } + unsigned char const * rsrambankptr() const { return memptrs_.rsrambankptr(); } + unsigned char * wsrambankptr() const { return memptrs_.wsrambankptr(); } + unsigned char * vrambankptr() const { return memptrs_.vrambankptr(); } + OamDmaSrc oamDmaSrc() const { return memptrs_.oamDmaSrc(); } + void setVrambank(unsigned bank) { memptrs_.setVrambank(bank); } + void setWrambank(unsigned bank) { memptrs_.setWrambank(bank); } + void setOamDmaSrc(OamDmaSrc oamDmaSrc) { memptrs_.setOamDmaSrc(oamDmaSrc); } + unsigned curRomBank() const { return memptrs_.curRomBank(); } + void mbcWrite(unsigned addr, unsigned data) { mbc_->romWrite(addr, data); } + bool isCgb() const { return gambatte::isCgb(memptrs_); } + void rtcWrite(unsigned data) { rtc_.write(data); } + unsigned char rtcRead() const { return *rtc_.activeData(); } + void loadSavedata(char const *data); int saveSavedataLength(); void saveSavedata(char *dest); - bool getMemoryArea(int which, unsigned char **data, int *length) const; - - LoadRes loadROM(const char *romfiledata, unsigned romfilelength, bool forceDmg, bool multicartCompat); - const char * romTitle() const { return reinterpret_cast(memptrs.romdata() + 0x134); } + LoadRes loadROM(char const *romfiledata, unsigned romfilelength, bool forceDmg, bool multicartCompat); + char const * romTitle() const { return reinterpret_cast(memptrs_.romdata() + 0x134); } void setRTCCallback(std::uint32_t (*callback)()) { - rtc.setRTCCallback(callback); + rtc_.setRTCCallback(callback); } +private: + MemPtrs memptrs_; + Rtc rtc_; + std::unique_ptr mbc_; + +public: templatevoid SyncState(NewState *ns); }; diff --git a/libgambatte/src/mem/memptrs.cpp b/libgambatte/src/mem/memptrs.cpp index 7b6c2c2ee6..08a05bf40b 100644 --- a/libgambatte/src/mem/memptrs.cpp +++ b/libgambatte/src/mem/memptrs.cpp @@ -1,21 +1,21 @@ -/*************************************************************************** - * Copyright (C) 2007-2010 by Sindre Aamås * - * aamas@stud.ntnu.no * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License version 2 as * - * published by the Free Software Foundation. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License version 2 for more details. * - * * - * You should have received a copy of the GNU General Public License * - * version 2 along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ +// +// Copyright (C) 2007-2010 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + #include "memptrs.h" #include #include @@ -23,10 +23,19 @@ namespace gambatte { MemPtrs::MemPtrs() -: rmem_(), wmem_(), romdata_(), wramdata_(), vrambankptr_(0), rsrambankptr_(0), - wsrambankptr_(0), memchunk_(0), rambankdata_(0), wramdataend_(0), oamDmaSrc_(OAM_DMA_SRC_OFF), - curRomBank_(1), - memchunk_len(0) +: rmem_() +, wmem_() +, romdata_() +, wramdata_() +, vrambankptr_(0) +, rsrambankptr_(0) +, wsrambankptr_(0) +, memchunk_(0) +, rambankdata_(0) +, wramdataend_(0) +, oamDmaSrc_(oam_dma_src_off) +, curRomBank_(1) +, memchunk_len(0) { } @@ -34,7 +43,7 @@ MemPtrs::~MemPtrs() { delete []memchunk_; } -void MemPtrs::reset(const unsigned rombanks, const unsigned rambanks, const unsigned wrambanks) { +void MemPtrs::reset(unsigned const rombanks, unsigned const rambanks, unsigned const wrambanks) { delete []memchunk_; memchunk_len = 0x4000 + rombanks * 0x4000ul + 0x4000 + rambanks * 0x2000ul + wrambanks * 0x1000ul + 0x4000; memchunk_ = new unsigned char[memchunk_len]; @@ -46,7 +55,7 @@ void MemPtrs::reset(const unsigned rombanks, const unsigned rambanks, const unsi std::memset(rdisabledRamw(), 0xFF, 0x2000); - oamDmaSrc_ = OAM_DMA_SRC_OFF; + oamDmaSrc_ = oam_dma_src_off; rmem_[0x3] = rmem_[0x2] = rmem_[0x1] = rmem_[0x0] = romdata_[0]; rmem_[0xC] = wmem_[0xC] = wramdata_[0] - 0xC000; rmem_[0xE] = wmem_[0xE] = wramdata_[0] - 0xE000; @@ -60,39 +69,43 @@ void MemPtrs::reset(const unsigned rombanks, const unsigned rambanks, const unsi memchunk_savelen = wramdataend() - memchunk_ - memchunk_saveoffs; } -void MemPtrs::setRombank0(const unsigned bank) { +void MemPtrs::setRombank0(unsigned bank) { romdata_[0] = romdata() + bank * 0x4000ul; rmem_[0x3] = rmem_[0x2] = rmem_[0x1] = rmem_[0x0] = romdata_[0]; disconnectOamDmaAreas(); } -void MemPtrs::setRombank(const unsigned bank) { +void MemPtrs::setRombank(unsigned bank) { curRomBank_ = bank; romdata_[1] = romdata() + bank * 0x4000ul - 0x4000; rmem_[0x7] = rmem_[0x6] = rmem_[0x5] = rmem_[0x4] = romdata_[1]; disconnectOamDmaAreas(); } -void MemPtrs::setRambank(const unsigned flags, const unsigned rambank) { - unsigned char *const srambankptr = flags & RTC_EN - ? 0 - : (rambankdata() != rambankdataend() - ? rambankdata_ + rambank * 0x2000ul - 0xA000 : wdisabledRam() - 0xA000); +void MemPtrs::setRambank(unsigned const flags, unsigned const rambank) { + unsigned char *srambankptr = 0; + if (!(flags & rtc_en)) { + srambankptr = rambankdata() != rambankdataend() + ? rambankdata_ + rambank * 0x2000ul - 0xA000 + : wdisabledRam() - 0xA000; + } - rsrambankptr_ = (flags & READ_EN) && srambankptr != wdisabledRam() - 0xA000 ? srambankptr : rdisabledRamw() - 0xA000; - wsrambankptr_ = flags & WRITE_EN ? srambankptr : wdisabledRam() - 0xA000; + rsrambankptr_ = (flags & read_en) && srambankptr != wdisabledRam() - 0xA000 + ? srambankptr + : rdisabledRamw() - 0xA000; + wsrambankptr_ = flags & write_en ? srambankptr : wdisabledRam() - 0xA000; rmem_[0xB] = rmem_[0xA] = rsrambankptr_; wmem_[0xB] = wmem_[0xA] = wsrambankptr_; disconnectOamDmaAreas(); } -void MemPtrs::setWrambank(const unsigned bank) { +void MemPtrs::setWrambank(unsigned bank) { wramdata_[1] = wramdata_[0] + (bank & 0x07 ? bank & 0x07 : 1) * 0x1000; rmem_[0xD] = wmem_[0xD] = wramdata_[1] - 0xD000; disconnectOamDmaAreas(); } -void MemPtrs::setOamDmaSrc(const OamDmaSrc oamDmaSrc) { +void MemPtrs::setOamDmaSrc(OamDmaSrc oamDmaSrc) { rmem_[0x3] = rmem_[0x2] = rmem_[0x1] = rmem_[0x0] = romdata_[0]; rmem_[0x7] = rmem_[0x6] = rmem_[0x5] = rmem_[0x4] = romdata_[1]; rmem_[0xB] = rmem_[0xA] = rsrambankptr_; @@ -108,37 +121,37 @@ void MemPtrs::setOamDmaSrc(const OamDmaSrc oamDmaSrc) { void MemPtrs::disconnectOamDmaAreas() { if (isCgb(*this)) { switch (oamDmaSrc_) { - case OAM_DMA_SRC_ROM: // fall through - case OAM_DMA_SRC_SRAM: - case OAM_DMA_SRC_INVALID: + case oam_dma_src_rom: // fall through + case oam_dma_src_sram: + case oam_dma_src_invalid: std::fill(rmem_, rmem_ + 8, static_cast(0)); rmem_[0xB] = rmem_[0xA] = 0; wmem_[0xB] = wmem_[0xA] = 0; break; - case OAM_DMA_SRC_VRAM: + case oam_dma_src_vram: break; - case OAM_DMA_SRC_WRAM: + case oam_dma_src_wram: rmem_[0xE] = rmem_[0xD] = rmem_[0xC] = 0; wmem_[0xE] = wmem_[0xD] = wmem_[0xC] = 0; break; - case OAM_DMA_SRC_OFF: + case oam_dma_src_off: break; } } else { switch (oamDmaSrc_) { - case OAM_DMA_SRC_ROM: // fall through - case OAM_DMA_SRC_SRAM: - case OAM_DMA_SRC_WRAM: - case OAM_DMA_SRC_INVALID: + case oam_dma_src_rom: // fall through + case oam_dma_src_sram: + case oam_dma_src_wram: + case oam_dma_src_invalid: std::fill(rmem_, rmem_ + 8, static_cast(0)); rmem_[0xB] = rmem_[0xA] = 0; wmem_[0xB] = wmem_[0xA] = 0; rmem_[0xE] = rmem_[0xD] = rmem_[0xC] = 0; wmem_[0xE] = wmem_[0xD] = wmem_[0xC] = 0; break; - case OAM_DMA_SRC_VRAM: + case oam_dma_src_vram: break; - case OAM_DMA_SRC_OFF: + case oam_dma_src_off: break; } } diff --git a/libgambatte/src/mem/memptrs.h b/libgambatte/src/mem/memptrs.h index 3d2c6ca3ca..d41a265394 100644 --- a/libgambatte/src/mem/memptrs.h +++ b/libgambatte/src/mem/memptrs.h @@ -1,21 +1,21 @@ -/*************************************************************************** - * Copyright (C) 2007-2010 by Sindre Aamås * - * aamas@stud.ntnu.no * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License version 2 as * - * published by the Free Software Foundation. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License version 2 for more details. * - * * - * You should have received a copy of the GNU General Public License * - * version 2 along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ +// +// Copyright (C) 2007-2010 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + #ifndef MEMPTRS_H #define MEMPTRS_H @@ -23,43 +23,22 @@ namespace gambatte { -enum OamDmaSrc { OAM_DMA_SRC_ROM, OAM_DMA_SRC_SRAM, OAM_DMA_SRC_VRAM, - OAM_DMA_SRC_WRAM, OAM_DMA_SRC_INVALID, OAM_DMA_SRC_OFF }; +enum OamDmaSrc { oam_dma_src_rom, + oam_dma_src_sram, + oam_dma_src_vram, + oam_dma_src_wram, + oam_dma_src_invalid, + oam_dma_src_off, }; class MemPtrs { - const unsigned char *rmem_[0x10]; - unsigned char *wmem_[0x10]; - - unsigned char *romdata_[2]; - unsigned char *wramdata_[2]; - unsigned char *vrambankptr_; - unsigned char *rsrambankptr_; - unsigned char *wsrambankptr_; - unsigned char *memchunk_; - unsigned char *rambankdata_; - unsigned char *wramdataend_; - - OamDmaSrc oamDmaSrc_; - - unsigned curRomBank_; - - int memchunk_len; - int memchunk_saveoffs; - int memchunk_savelen; - - MemPtrs(const MemPtrs &); - MemPtrs & operator=(const MemPtrs &); - void disconnectOamDmaAreas(); - unsigned char * rdisabledRamw() const { return wramdataend_ ; } - unsigned char * wdisabledRam() const { return wramdataend_ + 0x2000; } public: - enum RamFlag { READ_EN = 1, WRITE_EN = 2, RTC_EN = 4 }; + enum RamFlag { read_en = 1, write_en = 2, rtc_en = 4 }; MemPtrs(); ~MemPtrs(); void reset(unsigned rombanks, unsigned rambanks, unsigned wrambanks); - const unsigned char * rmem(unsigned area) const { return rmem_[area]; } + unsigned char const * rmem(unsigned area) const { return rmem_[area]; } unsigned char * wmem(unsigned area) const { return wmem_[area]; } unsigned char * vramdata() const { return rambankdata_ - 0x4000; } unsigned char * vramdataend() const { return rambankdata_; } @@ -70,8 +49,8 @@ public: unsigned char * wramdataend() const { return wramdataend_; } unsigned char * rambankdata() const { return rambankdata_; } unsigned char * rambankdataend() const { return wramdata_[0]; } - const unsigned char * rdisabledRam() const { return rdisabledRamw(); } - const unsigned char * rsrambankptr() const { return rsrambankptr_; } + unsigned char const * rdisabledRam() const { return rdisabledRamw(); } + unsigned char const * rsrambankptr() const { return rsrambankptr_; } unsigned char * wsrambankptr() const { return wsrambankptr_; } unsigned char * vrambankptr() const { return vrambankptr_; } OamDmaSrc oamDmaSrc() const { return oamDmaSrc_; } @@ -84,10 +63,36 @@ public: void setWrambank(unsigned bank); void setOamDmaSrc(OamDmaSrc oamDmaSrc); +private: + unsigned char const *rmem_[0x10]; + unsigned char *wmem_[0x10]; + unsigned char *romdata_[2]; + unsigned char *wramdata_[2]; + unsigned char *vrambankptr_; + unsigned char *rsrambankptr_; + unsigned char *wsrambankptr_; + unsigned char *memchunk_; + unsigned char *rambankdata_; + unsigned char *wramdataend_; + OamDmaSrc oamDmaSrc_; + + unsigned curRomBank_; + + int memchunk_len; + int memchunk_saveoffs; + int memchunk_savelen; + + MemPtrs(MemPtrs const &); + MemPtrs & operator=(MemPtrs const &); + void disconnectOamDmaAreas(); + unsigned char * rdisabledRamw() const { return wramdataend_ ; } + unsigned char * wdisabledRam() const { return wramdataend_ + 0x2000; } + +public: templatevoid SyncState(NewState *ns); }; -inline bool isCgb(const MemPtrs &memptrs) { +inline bool isCgb(MemPtrs const &memptrs) { return memptrs.wramdataend() - memptrs.wramdata(0) == 0x8000; } diff --git a/libgambatte/src/mem/rtc.cpp b/libgambatte/src/mem/rtc.cpp index 57b2964548..5495201c4c 100644 --- a/libgambatte/src/mem/rtc.cpp +++ b/libgambatte/src/mem/rtc.cpp @@ -1,21 +1,21 @@ -/*************************************************************************** - * Copyright (C) 2007 by Sindre Aamås * - * aamas@stud.ntnu.no * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License version 2 as * - * published by the Free Software Foundation. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License version 2 for more details. * - * * - * You should have received a copy of the GNU General Public License * - * version 2 along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ +// +// Copyright (C) 2007 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + #include "rtc.h" #include "../savestate.h" #include @@ -23,155 +23,154 @@ namespace gambatte { Rtc::Rtc() -: activeData(NULL), - activeSet(NULL), - baseTime(0), - haltTime(0), - index(5), - dataDh(0), - dataDl(0), - dataH(0), - dataM(0), - dataS(0), - enabled(false), - lastLatchData(false), - timeCB(0) +: activeData_(0) +, activeSet_(0) +, baseTime_(0) +, haltTime_(0) +, index_(5) +, dataDh_(0) +, dataDl_(0) +, dataH_(0) +, dataM_(0) +, dataS_(0) +, enabled_(false) +, lastLatchData_(false) +, timeCB(0) { } void Rtc::doLatch() { - std::uint32_t tmp = ((dataDh & 0x40) ? haltTime : timeCB()) - baseTime; + std::uint32_t tmp = ((dataDh_ & 0x40) ? haltTime_ : timeCB()) - baseTime_; while (tmp > 0x1FF * 86400) { - baseTime += 0x1FF * 86400; + baseTime_ += 0x1FF * 86400; tmp -= 0x1FF * 86400; - dataDh |= 0x80; + dataDh_ |= 0x80; } - dataDl = (tmp / 86400) & 0xFF; - dataDh &= 0xFE; - dataDh |= ((tmp / 86400) & 0x100) >> 8; + dataDl_ = (tmp / 86400) & 0xFF; + dataDh_ &= 0xFE; + dataDh_ |= ((tmp / 86400) & 0x100) >> 8; tmp %= 86400; - dataH = tmp / 3600; + dataH_ = tmp / 3600; tmp %= 3600; - dataM = tmp / 60; + dataM_ = tmp / 60; tmp %= 60; - dataS = tmp; + dataS_ = tmp; } void Rtc::doSwapActive() { - if (!enabled || index > 4) { - activeData = NULL; - activeSet = NULL; - } else switch (index) { + if (!enabled_ || index_ > 4) { + activeData_ = 0; + activeSet_ = 0; + } else switch (index_) { case 0x00: - activeData = &dataS; - activeSet = &Rtc::setS; + activeData_ = &dataS_; + activeSet_ = &Rtc::setS; break; case 0x01: - activeData = &dataM; - activeSet = &Rtc::setM; + activeData_ = &dataM_; + activeSet_ = &Rtc::setM; break; case 0x02: - activeData = &dataH; - activeSet = &Rtc::setH; + activeData_ = &dataH_; + activeSet_ = &Rtc::setH; break; case 0x03: - activeData = &dataDl; - activeSet = &Rtc::setDl; + activeData_ = &dataDl_; + activeSet_ = &Rtc::setDl; break; case 0x04: - activeData = &dataDh; - activeSet = &Rtc::setDh; + activeData_ = &dataDh_; + activeSet_ = &Rtc::setDh; break; } } -void Rtc::loadState(const SaveState &state) { - baseTime = state.rtc.baseTime; - haltTime = state.rtc.haltTime; - dataDh = state.rtc.dataDh; - dataDl = state.rtc.dataDl; - dataH = state.rtc.dataH; - dataM = state.rtc.dataM; - dataS = state.rtc.dataS; - lastLatchData = state.rtc.lastLatchData; - +void Rtc::loadState(SaveState const &state) { + baseTime_ = state.rtc.baseTime; + haltTime_ = state.rtc.haltTime; + dataDh_ = state.rtc.dataDh; + dataDl_ = state.rtc.dataDl; + dataH_ = state.rtc.dataH; + dataM_ = state.rtc.dataM; + dataS_ = state.rtc.dataS; + lastLatchData_ = state.rtc.lastLatchData; doSwapActive(); } -void Rtc::setDh(const unsigned new_dh) { - const std::uint32_t unixtime = (dataDh & 0x40) ? haltTime : timeCB(); - const std::uint32_t old_highdays = ((unixtime - baseTime) / 86400) & 0x100; - baseTime += old_highdays * 86400; - baseTime -= ((new_dh & 0x1) << 8) * 86400; +void Rtc::setDh(unsigned const newDh) { + const std::uint32_t unixtime = (dataDh_ & 0x40) ? haltTime_ : timeCB(); + const std::uint32_t oldHighdays = ((unixtime - baseTime_) / 86400) & 0x100; + baseTime_ += oldHighdays * 86400; + baseTime_ -= ((newDh & 0x1) << 8) * 86400; - if ((dataDh ^ new_dh) & 0x40) { - if (new_dh & 0x40) - haltTime = timeCB(); + if ((dataDh_ ^ newDh) & 0x40) { + if (newDh & 0x40) + haltTime_ = timeCB(); else - baseTime += timeCB() - haltTime; + baseTime_ += timeCB() - haltTime_; } } -void Rtc::setDl(const unsigned new_lowdays) { - const std::uint32_t unixtime = (dataDh & 0x40) ? haltTime : timeCB(); - const std::uint32_t old_lowdays = ((unixtime - baseTime) / 86400) & 0xFF; - baseTime += old_lowdays * 86400; - baseTime -= new_lowdays * 86400; +void Rtc::setDl(unsigned const newLowdays) { + const std::uint32_t unixtime = (dataDh_ & 0x40) ? haltTime_ : timeCB(); + const std::uint32_t oldLowdays = ((unixtime - baseTime_) / 86400) & 0xFF; + baseTime_ += oldLowdays * 86400; + baseTime_ -= newLowdays * 86400; } -void Rtc::setH(const unsigned new_hours) { - const std::uint32_t unixtime = (dataDh & 0x40) ? haltTime : timeCB(); - const std::uint32_t old_hours = ((unixtime - baseTime) / 3600) % 24; - baseTime += old_hours * 3600; - baseTime -= new_hours * 3600; +void Rtc::setH(unsigned const newHours) { + const std::uint32_t unixtime = (dataDh_ & 0x40) ? haltTime_ : timeCB(); + const std::uint32_t oldHours = ((unixtime - baseTime_) / 3600) % 24; + baseTime_ += oldHours * 3600; + baseTime_ -= newHours * 3600; } -void Rtc::setM(const unsigned new_minutes) { - const std::uint32_t unixtime = (dataDh & 0x40) ? haltTime : timeCB(); - const std::uint32_t old_minutes = ((unixtime - baseTime) / 60) % 60; - baseTime += old_minutes * 60; - baseTime -= new_minutes * 60; +void Rtc::setM(unsigned const newMinutes) { + const std::uint32_t unixtime = (dataDh_ & 0x40) ? haltTime_ : timeCB(); + const std::uint32_t oldMinutes = ((unixtime - baseTime_) / 60) % 60; + baseTime_ += oldMinutes * 60; + baseTime_ -= newMinutes * 60; } -void Rtc::setS(const unsigned new_seconds) { - const std::uint32_t unixtime = (dataDh & 0x40) ? haltTime : timeCB(); - baseTime += (unixtime - baseTime) % 60; - baseTime -= new_seconds; +void Rtc::setS(unsigned const newSeconds) { + const std::uint32_t unixtime = (dataDh_ & 0x40) ? haltTime_ : timeCB(); + baseTime_ += (unixtime - baseTime_) % 60; + baseTime_ -= newSeconds; } SYNCFUNC(Rtc) { - EBS(activeData, 0); - EVS(activeData, &dataS, 1); - EVS(activeData, &dataM, 2); - EVS(activeData, &dataH, 3); - EVS(activeData, &dataDl, 4); - EVS(activeData, &dataDh, 5); - EES(activeData, NULL); + EBS(activeData_, 0); + EVS(activeData_, &dataS_, 1); + EVS(activeData_, &dataM_, 2); + EVS(activeData_, &dataH_, 3); + EVS(activeData_, &dataDl_, 4); + EVS(activeData_, &dataDh_, 5); + EES(activeData_, NULL); - EBS(activeSet, 0); - EVS(activeSet, &Rtc::setS, 1); - EVS(activeSet, &Rtc::setM, 2); - EVS(activeSet, &Rtc::setH, 3); - EVS(activeSet, &Rtc::setDl, 4); - EVS(activeSet, &Rtc::setDh, 5); - EES(activeSet, NULL); + EBS(activeSet_, 0); + EVS(activeSet_, &Rtc::setS, 1); + EVS(activeSet_, &Rtc::setM, 2); + EVS(activeSet_, &Rtc::setH, 3); + EVS(activeSet_, &Rtc::setDl, 4); + EVS(activeSet_, &Rtc::setDh, 5); + EES(activeSet_, NULL); - NSS(baseTime); - NSS(haltTime); - NSS(index); - NSS(dataDh); - NSS(dataDl); - NSS(dataH); - NSS(dataM); - NSS(dataS); - NSS(enabled); - NSS(lastLatchData); + NSS(baseTime_); + NSS(haltTime_); + NSS(index_); + NSS(dataDh_); + NSS(dataDl_); + NSS(dataH_); + NSS(dataM_); + NSS(dataS_); + NSS(enabled_); + NSS(lastLatchData_); } } diff --git a/libgambatte/src/mem/rtc.h b/libgambatte/src/mem/rtc.h index 67eedb863b..f7594e2eec 100644 --- a/libgambatte/src/mem/rtc.h +++ b/libgambatte/src/mem/rtc.h @@ -1,21 +1,21 @@ -/*************************************************************************** - * Copyright (C) 2007 by Sindre Aamås * - * aamas@stud.ntnu.no * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License version 2 as * - * published by the Free Software Foundation. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License version 2 for more details. * - * * - * You should have received a copy of the GNU General Public License * - * version 2 along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ +// +// Copyright (C) 2007 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + #ifndef RTC_H #define RTC_H @@ -27,69 +27,67 @@ namespace gambatte { struct SaveState; class Rtc { -private: - unsigned char *activeData; - void (Rtc::*activeSet)(unsigned); - std::uint32_t baseTime; - std::uint32_t haltTime; - unsigned char index; - unsigned char dataDh; - unsigned char dataDl; - unsigned char dataH; - unsigned char dataM; - unsigned char dataS; - bool enabled; - bool lastLatchData; - std::uint32_t (*timeCB)(); - - void doLatch(); - void doSwapActive(); - void setDh(unsigned new_dh); - void setDl(unsigned new_lowdays); - void setH(unsigned new_hours); - void setM(unsigned new_minutes); - void setS(unsigned new_seconds); - public: Rtc(); - const unsigned char* getActive() const { return activeData; } - std::uint32_t getBaseTime() const { return baseTime; } + unsigned char const * activeData() const { return activeData_; } + std::uint32_t getBaseTime() const { return baseTime_; } void setBaseTime(const std::uint32_t baseTime) { - this->baseTime = baseTime; -// doLatch(); + this->baseTime_ = baseTime; } - void latch(const unsigned data) { - if (!lastLatchData && data == 1) + void latch(unsigned data) { + if (!lastLatchData_ && data == 1) doLatch(); - lastLatchData = data; + lastLatchData_ = data; } - void loadState(const SaveState &state); + void loadState(SaveState const &state); - void set(const bool enabled, unsigned bank) { + void set(bool enabled, unsigned bank) { bank &= 0xF; bank -= 8; - this->enabled = enabled; - this->index = bank; - + enabled_ = enabled; + index_ = bank; doSwapActive(); } - void write(const unsigned data) { -// if (activeSet) - (this->*activeSet)(data); - *activeData = data; + void write(unsigned data) { + (this->*activeSet_)(data); + *activeData_ = data; } void setRTCCallback(std::uint32_t (*callback)()) { timeCB = callback; } +private: + unsigned char *activeData_; + void (Rtc::*activeSet_)(unsigned); + std::uint32_t baseTime_; + std::uint32_t haltTime_; + unsigned char index_; + unsigned char dataDh_; + unsigned char dataDl_; + unsigned char dataH_; + unsigned char dataM_; + unsigned char dataS_; + bool enabled_; + bool lastLatchData_; + std::uint32_t (*timeCB)(); + + void doLatch(); + void doSwapActive(); + void setDh(unsigned newDh); + void setDl(unsigned newLowdays); + void setH(unsigned newHours); + void setM(unsigned newMinutes); + void setS(unsigned newSeconds); + +public: templatevoid SyncState(NewState *ns); }; diff --git a/libgambatte/src/memory.cpp b/libgambatte/src/memory.cpp index 46bb190e16..8d014b4798 100644 --- a/libgambatte/src/memory.cpp +++ b/libgambatte/src/memory.cpp @@ -85,7 +85,7 @@ void Memory::loadState(const SaveState &state) { : 8; cart.setVrambank(ioamhram[0x14F] & isCgb()); - cart.setOamDmaSrc(OAM_DMA_SRC_OFF); + cart.setOamDmaSrc(oam_dma_src_off); cart.setWrambank(isCgb() && (ioamhram[0x170] & 0x07) ? ioamhram[0x170] & 0x07 : 1); if (lastOamDmaUpdate != disabled_time) { @@ -432,11 +432,11 @@ void Memory::updateOamDma(const unsigned long cycleCounter) { void Memory::oamDmaInitSetup() { if (ioamhram[0x146] < 0xA0) { - cart.setOamDmaSrc(ioamhram[0x146] < 0x80 ? OAM_DMA_SRC_ROM : OAM_DMA_SRC_VRAM); + cart.setOamDmaSrc(ioamhram[0x146] < 0x80 ? oam_dma_src_rom : oam_dma_src_vram); } else if (ioamhram[0x146] < 0xFE - isCgb() * 0x1E) { - cart.setOamDmaSrc(ioamhram[0x146] < 0xC0 ? OAM_DMA_SRC_SRAM : OAM_DMA_SRC_WRAM); + cart.setOamDmaSrc(ioamhram[0x146] < 0xC0 ? oam_dma_src_sram : oam_dma_src_wram); } else - cart.setOamDmaSrc(OAM_DMA_SRC_INVALID); + cart.setOamDmaSrc(oam_dma_src_invalid); } static const unsigned char * oamDmaSrcZero() { @@ -446,12 +446,12 @@ static const unsigned char * oamDmaSrcZero() { const unsigned char * Memory::oamDmaSrcPtr() const { switch (cart.oamDmaSrc()) { - case OAM_DMA_SRC_ROM: return cart.romdata(ioamhram[0x146] >> 6) + (ioamhram[0x146] << 8); - case OAM_DMA_SRC_SRAM: return cart.rsrambankptr() ? cart.rsrambankptr() + (ioamhram[0x146] << 8) : 0; - case OAM_DMA_SRC_VRAM: return cart.vrambankptr() + (ioamhram[0x146] << 8); - case OAM_DMA_SRC_WRAM: return cart.wramdata(ioamhram[0x146] >> 4 & 1) + (ioamhram[0x146] << 8 & 0xFFF); - case OAM_DMA_SRC_INVALID: - case OAM_DMA_SRC_OFF: break; + case oam_dma_src_rom: return cart.romdata(ioamhram[0x146] >> 6) + (ioamhram[0x146] << 8); + case oam_dma_src_sram: return cart.rsrambankptr() ? cart.rsrambankptr() + (ioamhram[0x146] << 8) : 0; + case oam_dma_src_vram: return cart.vrambankptr() + (ioamhram[0x146] << 8); + case oam_dma_src_wram: return cart.wramdata(ioamhram[0x146] >> 4 & 1) + (ioamhram[0x146] << 8 & 0xFFF); + case oam_dma_src_invalid: + case oam_dma_src_off: break; } return ioamhram[0x146] == 0xFF && !isCgb() ? oamDmaSrcZero() : cart.rdisabledRam(); @@ -463,7 +463,7 @@ void Memory::startOamDma(const unsigned long cycleCounter) { void Memory::endOamDma(const unsigned long cycleCounter) { oamDmaPos = 0xFE; - cart.setOamDmaSrc(OAM_DMA_SRC_OFF); + cart.setOamDmaSrc(oam_dma_src_off); display.oamChange(ioamhram, cycleCounter); } diff --git a/output/dll/libgambatte.dll b/output/dll/libgambatte.dll index 5176758d4b38917be743641deb308a04378ac8de..34cf5fec977d0027efb94d942b0e7b46473f63e7 100644 GIT binary patch delta 26812 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 From af42fbb714b489a0b93afecc25ac2fc14c8d527c Mon Sep 17 00:00:00 2001 From: MrWint Date: Sun, 26 May 2019 12:22:53 +0200 Subject: [PATCH 22/38] Reformat gambatte.cpp --- libgambatte/include/gambatte.h | 20 +++---- libgambatte/src/cinterface.cpp | 22 +++---- libgambatte/src/counterdef.h | 4 +- libgambatte/src/cpu.h | 4 +- libgambatte/src/gambatte.cpp | 90 +++++++++++++---------------- libgambatte/src/mem/cartridge.cpp | 4 +- libgambatte/src/memory.cpp | 4 +- libgambatte/src/memory.h | 8 +-- libgambatte/src/newstate.cpp | 16 ++--- libgambatte/src/newstate.h | 40 ++++++------- libgambatte/src/sound/channel3.cpp | 2 +- output/dll/libgambatte.dll | Bin 168960 -> 168960 bytes 12 files changed, 103 insertions(+), 111 deletions(-) diff --git a/libgambatte/include/gambatte.h b/libgambatte/include/gambatte.h index 0cccaafa9a..890574b48c 100644 --- a/libgambatte/include/gambatte.h +++ b/libgambatte/include/gambatte.h @@ -64,10 +64,10 @@ public: * @param flags ORed combination of LoadFlags. * @return 0 on success, negative value on failure. */ - LoadRes load(const char *romfiledata, unsigned romfilelength, std::uint32_t now, unsigned flags, unsigned div); + LoadRes load(char const *romfiledata, unsigned romfilelength, std::uint32_t now, unsigned flags, unsigned div); - int loadGBCBios(const char* biosfiledata); - int loadDMGBios(const char* biosfiledata); + int loadGBCBios(char const* biosfiledata); + int loadDMGBios(char const* biosfiledata); /** * Emulates until at least 'samples' audio samples are produced in the @@ -136,7 +136,7 @@ public: bool isLoaded() const; /** Writes persistent cartridge data to disk. NOT Done implicitly on ROM close. */ - void loadSavedata(const char *data); + void loadSavedata(char const *data); int saveSavedataLength(); void saveSavedata(char *dest); @@ -146,15 +146,15 @@ public: /** ROM header title of currently loaded ROM image. */ const std::string romTitle() const; - unsigned char ExternalRead(unsigned short addr); - void ExternalWrite(unsigned short addr, unsigned char val); + unsigned char externalRead(unsigned short addr); + void externalWrite(unsigned short addr, unsigned char val); - int LinkStatus(int which); + int linkStatus(int which); - void GetRegs(int *dest); + void getRegs(int *dest); - void SetInterruptAddresses(int *addrs, int numAddrs); - int GetHitInterruptAddress(); + void setInterruptAddresses(int *addrs, int numAddrs); + int getHitInterruptAddress(); templatevoid SyncState(NewState *ns); diff --git a/libgambatte/src/cinterface.cpp b/libgambatte/src/cinterface.cpp index 948f019775..d3bde8fc74 100644 --- a/libgambatte/src/cinterface.cpp +++ b/libgambatte/src/cinterface.cpp @@ -29,19 +29,19 @@ GBEXPORT void gambatte_destroy(GB *g) delete g; } -GBEXPORT int gambatte_load(GB *g, const char *romfiledata, unsigned romfilelength, long long now, unsigned flags, unsigned div) +GBEXPORT int gambatte_load(GB *g, char const *romfiledata, unsigned romfilelength, long long now, unsigned flags, unsigned div) { int ret = g->load(romfiledata, romfilelength, now, flags, div); return ret; } -GBEXPORT int gambatte_loadgbcbios(GB* g, const char* biosfiledata) +GBEXPORT int gambatte_loadgbcbios(GB* g, char const* biosfiledata) { int ret = g->loadGBCBios(biosfiledata); return ret; } -GBEXPORT int gambatte_loaddmgbios(GB* g, const char* biosfiledata) +GBEXPORT int gambatte_loaddmgbios(GB* g, char const* biosfiledata) { int ret = g->loadDMGBios(biosfiledata); return ret; @@ -141,7 +141,7 @@ GBEXPORT void gambatte_savesavedata(GB *g, char *dest) g->saveSavedata(dest); } -GBEXPORT void gambatte_loadsavedata(GB *g, const char *data) +GBEXPORT void gambatte_loadsavedata(GB *g, char const *data) { g->loadSavedata(data); } @@ -165,7 +165,7 @@ GBEXPORT int gambatte_newstatesave(GB *g, char *data, int len) return !saver.Overflow() && saver.GetLength() == len; } -GBEXPORT int gambatte_newstateload(GB *g, const char *data, int len) +GBEXPORT int gambatte_newstateload(GB *g, char const *data, int len) { NewStateExternalBuffer loader((char *)data, len); g->SyncState(&loader); @@ -196,30 +196,30 @@ GBEXPORT int gambatte_getmemoryarea(GB *g, int which, unsigned char **data, int GBEXPORT unsigned char gambatte_cpuread(GB *g, unsigned short addr) { - return g->ExternalRead(addr); + return g->externalRead(addr); } GBEXPORT void gambatte_cpuwrite(GB *g, unsigned short addr, unsigned char val) { - g->ExternalWrite(addr, val); + g->externalWrite(addr, val); } GBEXPORT int gambatte_linkstatus(GB *g, int which) { - return g->LinkStatus(which); + return g->linkStatus(which); } GBEXPORT void gambatte_getregs(GB *g, int *dest) { - g->GetRegs(dest); + g->getRegs(dest); } GBEXPORT void gambatte_setinterruptaddresses(GB *g, int *addrs, int numAddrs) { - g->SetInterruptAddresses(addrs, numAddrs); + g->setInterruptAddresses(addrs, numAddrs); } GBEXPORT int gambatte_gethitinterruptaddress(GB *g) { - return g->GetHitInterruptAddress(); + return g->getHitInterruptAddress(); } diff --git a/libgambatte/src/counterdef.h b/libgambatte/src/counterdef.h index 66d078ad1f..c48b80cdfa 100644 --- a/libgambatte/src/counterdef.h +++ b/libgambatte/src/counterdef.h @@ -2,7 +2,9 @@ #define COUNTERDEF_H namespace gambatte { -enum { disabled_time = 0xFFFFFFFFul }; + +enum { disabled_time = 0xfffffffful }; + } #endif diff --git a/libgambatte/src/cpu.h b/libgambatte/src/cpu.h index 0e37bb7066..784646c5bb 100644 --- a/libgambatte/src/cpu.h +++ b/libgambatte/src/cpu.h @@ -31,7 +31,7 @@ public: void setStatePtrs(SaveState &state); void loadState(SaveState const &state); void setLayers(unsigned mask) { mem_.setLayers(mask); } - void loadSavedata(const char *data) { mem_.loadSavedata(data); } + void loadSavedata(char const *data) { mem_.loadSavedata(data); } int saveSavedataLength() {return mem_.saveSavedataLength(); } void saveSavedata(char *dest) { mem_.saveSavedata(dest); } @@ -105,7 +105,7 @@ public: mem_.write_nocb(addr, val, cycleCounter_); } - int LinkStatus(int which) { return mem_.LinkStatus(which); } + int linkStatus(int which) { return mem_.linkStatus(which); } void getRegs(int *dest); void setInterruptAddresses(int *addrs, int numAddrs); diff --git a/libgambatte/src/gambatte.cpp b/libgambatte/src/gambatte.cpp index 71a9dc0aa1..691d64dd29 100644 --- a/libgambatte/src/gambatte.cpp +++ b/libgambatte/src/gambatte.cpp @@ -1,29 +1,30 @@ -/*************************************************************************** - * Copyright (C) 2007 by Sindre Aamås * - * aamas@stud.ntnu.no * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License version 2 as * - * published by the Free Software Foundation. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License version 2 for more details. * - * * - * You should have received a copy of the GNU General Public License * - * version 2 along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ +// +// Copyright (C) 2007 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + #include "gambatte.h" #include "cpu.h" -#include "savestate.h" #include "initstate.h" -#include +#include "savestate.h" #include +#include namespace gambatte { + struct GB::Priv { CPU cpu; unsigned loadflags; @@ -34,18 +35,11 @@ struct GB::Priv { Priv() : loadflags(0), layersMask(LAYER_MASK_BG | LAYER_MASK_OBJ) { } - - ~Priv() - { - } }; GB::GB() : p_(new Priv) {} GB::~GB() { - //if (p_->cpu.loaded()) - // p_->cpu.saveSavedata(); - delete p_; } @@ -57,19 +51,19 @@ std::ptrdiff_t GB::runFor(gambatte::uint_least32_t *const soundBuf, std::size_t p_->cpu.setVideoBuffer(p_->vbuff, 160); p_->cpu.setSoundBuffer(soundBuf); - const long cyclesSinceBlit = p_->cpu.runFor(samples * 2); - samples = p_->cpu.fillSoundBuffer(); - return cyclesSinceBlit < 0 ? cyclesSinceBlit : static_cast(samples) - (cyclesSinceBlit >> 1); + long const cyclesSinceBlit = p_->cpu.runFor(samples * 2); + samples = p_->cpu.fillSoundBuffer(); + return cyclesSinceBlit >= 0 + ? static_cast(samples) - (cyclesSinceBlit >> 1) + : cyclesSinceBlit; } -void GB::setLayers(unsigned mask) -{ +void GB::setLayers(unsigned mask) { p_->cpu.setLayers(mask); } -void GB::blitTo(gambatte::uint_least32_t *videoBuf, std::ptrdiff_t pitch) -{ +void GB::blitTo(gambatte::uint_least32_t *videoBuf, std::ptrdiff_t pitch) { gambatte::uint_least32_t *src = p_->vbuff; gambatte::uint_least32_t *dst = videoBuf; @@ -140,10 +134,7 @@ void GB::setLinkCallback(void(*callback)()) { p_->cpu.setLinkCallback(callback); } -LoadRes GB::load(const char *romfiledata, unsigned romfilelength, const std::uint32_t now, unsigned const flags, const unsigned div) { - //if (p_->cpu.loaded()) - // p_->cpu.saveSavedata(); - +LoadRes GB::load(char const *romfiledata, unsigned romfilelength, const std::uint32_t now, unsigned const flags, const unsigned div) { LoadRes const loadres = p_->cpu.load(romfiledata, romfilelength, flags & FORCE_DMG, flags & MULTICART_COMPAT); if (loadres == LOADRES_OK) { @@ -152,18 +143,17 @@ LoadRes GB::load(const char *romfiledata, unsigned romfilelength, const std::uin p_->loadflags = flags; setInitState(state, !(flags & FORCE_DMG), flags & GBA_CGB, now, div); p_->cpu.loadState(state); - //p_->cpu.loadSavedata(); } return loadres; } -int GB::loadGBCBios(const char* biosfiledata) { +int GB::loadGBCBios(char const* biosfiledata) { memcpy(p_->cpu.cgbBiosBuffer(), biosfiledata, 0x900); return 0; } -int GB::loadDMGBios(const char* biosfiledata) { +int GB::loadDMGBios(char const* biosfiledata) { memcpy(p_->cpu.dmgBiosBuffer(), biosfiledata, 0x100); return 0; } @@ -180,7 +170,7 @@ void GB::saveSavedata(char *dest) { if (p_->cpu.loaded()) p_->cpu.saveSavedata(dest); } -void GB::loadSavedata(const char *data) { +void GB::loadSavedata(char const *data) { if (p_->cpu.loaded()) p_->cpu.loadSavedata(data); } @@ -198,14 +188,14 @@ bool GB::getMemoryArea(int which, unsigned char **data, int *length) { return false; } -unsigned char GB::ExternalRead(unsigned short addr) { +unsigned char GB::externalRead(unsigned short addr) { if (p_->cpu.loaded()) return p_->cpu.externalRead(addr); else return 0; } -void GB::ExternalWrite(unsigned short addr, unsigned char val) { +void GB::externalWrite(unsigned short addr, unsigned char val) { if (p_->cpu.loaded()) p_->cpu.externalWrite(addr, val); } @@ -219,7 +209,7 @@ void GB::setCgbPalette(unsigned *lut) { p_->cpu.setCgbPalette(lut); } -const std::string GB::romTitle() const { +std::string const GB::romTitle() const { if (p_->cpu.loaded()) { char title[0x11]; std::memcpy(title, p_->cpu.romTitle(), 0x10); @@ -230,20 +220,20 @@ const std::string GB::romTitle() const { return std::string(); } -int GB::LinkStatus(int which) { - return p_->cpu.LinkStatus(which); +int GB::linkStatus(int which) { + return p_->cpu.linkStatus(which); } -void GB::GetRegs(int *dest) { +void GB::getRegs(int *dest) { p_->cpu.getRegs(dest); } -void GB::SetInterruptAddresses(int *addrs, int numAddrs) +void GB::setInterruptAddresses(int *addrs, int numAddrs) { p_->cpu.setInterruptAddresses(addrs, numAddrs); } -int GB::GetHitInterruptAddress() +int GB::getHitInterruptAddress() { return p_->cpu.getHitInterruptAddress(); } diff --git a/libgambatte/src/mem/cartridge.cpp b/libgambatte/src/mem/cartridge.cpp index 18105d9d21..7d855cf2fc 100644 --- a/libgambatte/src/mem/cartridge.cpp +++ b/libgambatte/src/mem/cartridge.cpp @@ -572,7 +572,7 @@ static bool presumedMulti64Mbc1(unsigned char const header[], unsigned rombanks) return header[0x147] == 1 && header[0x149] == 0 && rombanks == 64; } -LoadRes Cartridge::loadROM(const char *romfiledata, unsigned romfilelength, bool const forceDmg, bool const multicartCompat) { +LoadRes Cartridge::loadROM(char const *romfiledata, unsigned romfilelength, bool const forceDmg, bool const multicartCompat) { //const std::auto_ptr rom(newFileInstance(romfile)); //if (rom->fail()) @@ -698,7 +698,7 @@ static bool hasBattery(unsigned char headerByte0x147) { } } -void Cartridge::loadSavedata(const char *data) { +void Cartridge::loadSavedata(char const *data) { if (hasBattery(memptrs_.romdata()[0x147])) { int length = memptrs_.rambankdataend() - memptrs_.rambankdata(); std::memcpy(memptrs_.rambankdata(), data, length); diff --git a/libgambatte/src/memory.cpp b/libgambatte/src/memory.cpp index 8d014b4798..50693a4b06 100644 --- a/libgambatte/src/memory.cpp +++ b/libgambatte/src/memory.cpp @@ -1058,7 +1058,7 @@ void Memory::nontrivial_write(const unsigned P, const unsigned data, const unsig ioamhram[P - 0xFE00] = data; } -LoadRes Memory::loadROM(const char *romfiledata, unsigned romfilelength, const bool forceDmg, const bool multicartCompat) { +LoadRes Memory::loadROM(char const *romfiledata, unsigned romfilelength, const bool forceDmg, const bool multicartCompat) { if (LoadRes const fail = cart.loadROM(romfiledata, romfilelength, forceDmg, multicartCompat)) return fail; @@ -1104,7 +1104,7 @@ bool Memory::getMemoryArea(int which, unsigned char **data, int *length) { } } -int Memory::LinkStatus(int which) +int Memory::linkStatus(int which) { switch (which) { diff --git a/libgambatte/src/memory.h b/libgambatte/src/memory.h index a9182bb059..bf24eb33cf 100644 --- a/libgambatte/src/memory.h +++ b/libgambatte/src/memory.h @@ -99,13 +99,13 @@ public: bool loaded() const { return cart.loaded(); } unsigned curRomBank() const { return cart.curRomBank(); } - const char * romTitle() const { return cart.romTitle(); } + char const * romTitle() const { return cart.romTitle(); } int debugGetLY() const { return display.debugGetLY(); } void setStatePtrs(SaveState &state); void loadState(const SaveState &state/*, unsigned long oldCc*/); - void loadSavedata(const char *data) { cart.loadSavedata(data); } + void loadSavedata(char const *data) { cart.loadSavedata(data); } int saveSavedataLength() {return cart.saveSavedataLength(); } void saveSavedata(char *dest) { cart.saveSavedata(dest); } void updateInput(); @@ -295,7 +295,7 @@ public: unsigned long event(unsigned long cycleCounter); unsigned long resetCounters(unsigned long cycleCounter); - LoadRes loadROM(const char *romfiledata, unsigned romfilelength, bool forceDmg, bool multicartCompat); + LoadRes loadROM(char const *romfiledata, unsigned romfilelength, bool forceDmg, bool multicartCompat); void setInputGetter(unsigned (*getInput)()) { this->getInput = getInput; @@ -346,7 +346,7 @@ public: display.blackScreen(); } - int LinkStatus(int which); + int linkStatus(int which); templatevoid SyncState(NewState *ns); }; diff --git a/libgambatte/src/newstate.cpp b/libgambatte/src/newstate.cpp index 9d4214674a..f28a15fa7c 100644 --- a/libgambatte/src/newstate.cpp +++ b/libgambatte/src/newstate.cpp @@ -8,11 +8,11 @@ NewStateDummy::NewStateDummy() :length(0) { } -void NewStateDummy::Save(const void *ptr, size_t size, const char *name) +void NewStateDummy::Save(const void *ptr, size_t size, char const *name) { length += size; } -void NewStateDummy::Load(void *ptr, size_t size, const char *name) +void NewStateDummy::Load(void *ptr, size_t size, char const *name) { } @@ -21,7 +21,7 @@ NewStateExternalBuffer::NewStateExternalBuffer(char *buffer, long maxlength) { } -void NewStateExternalBuffer::Save(const void *ptr, size_t size, const char *name) +void NewStateExternalBuffer::Save(const void *ptr, size_t size, char const *name) { if (maxlength - length >= (long)size) { @@ -30,7 +30,7 @@ void NewStateExternalBuffer::Save(const void *ptr, size_t size, const char *name length += size; } -void NewStateExternalBuffer::Load(void *ptr, size_t size, const char *name) +void NewStateExternalBuffer::Load(void *ptr, size_t size, char const *name) { char *dst = static_cast(ptr); if (maxlength - length >= (long)size) @@ -48,19 +48,19 @@ NewStateExternalFunctions::NewStateExternalFunctions(const FPtrs *ff) { } -void NewStateExternalFunctions::Save(const void *ptr, size_t size, const char *name) +void NewStateExternalFunctions::Save(const void *ptr, size_t size, char const *name) { Save_(ptr, size, name); } -void NewStateExternalFunctions::Load(void *ptr, size_t size, const char *name) +void NewStateExternalFunctions::Load(void *ptr, size_t size, char const *name) { Load_(ptr, size, name); } -void NewStateExternalFunctions::EnterSection(const char *name) +void NewStateExternalFunctions::EnterSection(char const *name) { EnterSection_(name); } -void NewStateExternalFunctions::ExitSection(const char *name) +void NewStateExternalFunctions::ExitSection(char const *name) { ExitSection_(name); } diff --git a/libgambatte/src/newstate.h b/libgambatte/src/newstate.h index 576da30bfb..6ae468e5db 100644 --- a/libgambatte/src/newstate.h +++ b/libgambatte/src/newstate.h @@ -9,10 +9,10 @@ namespace gambatte { class NewState { public: - virtual void Save(const void *ptr, size_t size, const char *name) = 0; - virtual void Load(void *ptr, size_t size, const char *name) = 0; - virtual void EnterSection(const char *name) { } - virtual void ExitSection(const char *name) { } + virtual void Save(const void *ptr, size_t size, char const *name) = 0; + virtual void Load(void *ptr, size_t size, char const *name) = 0; + virtual void EnterSection(char const *name) { } + virtual void ExitSection(char const *name) { } }; class NewStateDummy : public NewState @@ -23,8 +23,8 @@ public: NewStateDummy(); long GetLength() { return length; } void Rewind() { length = 0; } - virtual void Save(const void *ptr, size_t size, const char *name); - virtual void Load(void *ptr, size_t size, const char *name); + virtual void Save(const void *ptr, size_t size, char const *name); + virtual void Load(void *ptr, size_t size, char const *name); }; class NewStateExternalBuffer : public NewState @@ -38,31 +38,31 @@ public: long GetLength() { return length; } void Rewind() { length = 0; } bool Overflow() { return length > maxlength; } - virtual void Save(const void *ptr, size_t size, const char *name); - virtual void Load(void *ptr, size_t size, const char *name); + virtual void Save(const void *ptr, size_t size, char const *name); + virtual void Load(void *ptr, size_t size, char const *name); }; struct FPtrs { - void (*Save_)(const void *ptr, size_t size, const char *name); - void (*Load_)(void *ptr, size_t size, const char *name); - void (*EnterSection_)(const char *name); - void (*ExitSection_)(const char *name); + void (*Save_)(const void *ptr, size_t size, char const *name); + void (*Load_)(void *ptr, size_t size, char const *name); + void (*EnterSection_)(char const *name); + void (*ExitSection_)(char const *name); }; class NewStateExternalFunctions : public NewState { private: - void (*Save_)(const void *ptr, size_t size, const char *name); - void (*Load_)(void *ptr, size_t size, const char *name); - void (*EnterSection_)(const char *name); - void (*ExitSection_)(const char *name); + void (*Save_)(const void *ptr, size_t size, char const *name); + void (*Load_)(void *ptr, size_t size, char const *name); + void (*EnterSection_)(char const *name); + void (*ExitSection_)(char const *name); public: NewStateExternalFunctions(const FPtrs *ff); - virtual void Save(const void *ptr, size_t size, const char *name); - virtual void Load(void *ptr, size_t size, const char *name); - virtual void EnterSection(const char *name); - virtual void ExitSection(const char *name); + virtual void Save(const void *ptr, size_t size, char const *name); + virtual void Load(void *ptr, size_t size, char const *name); + virtual void EnterSection(char const *name); + virtual void ExitSection(char const *name); }; // defines and explicitly instantiates diff --git a/libgambatte/src/sound/channel3.cpp b/libgambatte/src/sound/channel3.cpp index 06fe6dc2fd..223378b1ab 100644 --- a/libgambatte/src/sound/channel3.cpp +++ b/libgambatte/src/sound/channel3.cpp @@ -21,7 +21,7 @@ #include #include -static inline unsigned toPeriod(const unsigned nr3, const unsigned nr4) { +static inline unsigned toPeriod(unsigned nr3, unsigned nr4) { return 0x800 - ((nr4 << 8 & 0x700) | nr3); } diff --git a/output/dll/libgambatte.dll b/output/dll/libgambatte.dll index 34cf5fec977d0027efb94d942b0e7b46473f63e7..4ae26f6e50d9a7ec9ba604262dd54585caf3ec0e 100644 GIT binary patch delta 20373 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 delta 20362 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 From b58554f62e17f3ab1fa383f51c1d92150774806e Mon Sep 17 00:00:00 2001 From: MrWint Date: Sun, 26 May 2019 12:33:32 +0200 Subject: [PATCH 23/38] libgambatte: Remove unused interrupter --- libgambatte/Makefile | 1 - libgambatte/libgambatte.vcxproj | 1 - libgambatte/libgambatte.vcxproj.filters | 3 -- libgambatte/src/cpu.cpp | 2 +- libgambatte/src/initstate.cpp | 40 +++++++++++----------- libgambatte/src/initstate.h | 37 +++++++++++---------- libgambatte/src/insertion_sort.h | 40 +++++++++++----------- libgambatte/src/interrupter.cpp | 42 ------------------------ libgambatte/src/interrupter.h | 37 --------------------- libgambatte/src/memory.cpp | 4 +-- libgambatte/src/memory.h | 4 +-- output/dll/libgambatte.dll | Bin 168960 -> 168960 bytes 12 files changed, 61 insertions(+), 150 deletions(-) delete mode 100644 libgambatte/src/interrupter.cpp delete mode 100644 libgambatte/src/interrupter.h diff --git a/libgambatte/Makefile b/libgambatte/Makefile index 5ae56f6a29..1a2c7ddeed 100644 --- a/libgambatte/Makefile +++ b/libgambatte/Makefile @@ -25,7 +25,6 @@ SRCS = \ src/cpu.cpp \ src/gambatte.cpp \ src/initstate.cpp \ - src/interrupter.cpp \ src/interruptrequester.cpp \ src/memory.cpp \ src/mem/cartridge.cpp \ diff --git a/libgambatte/libgambatte.vcxproj b/libgambatte/libgambatte.vcxproj index 4eecef7e04..33e0eb0aee 100644 --- a/libgambatte/libgambatte.vcxproj +++ b/libgambatte/libgambatte.vcxproj @@ -197,7 +197,6 @@ - diff --git a/libgambatte/libgambatte.vcxproj.filters b/libgambatte/libgambatte.vcxproj.filters index 614dad17ef..38cbfcdb7d 100644 --- a/libgambatte/libgambatte.vcxproj.filters +++ b/libgambatte/libgambatte.vcxproj.filters @@ -161,9 +161,6 @@ Source Files - - Source Files - Source Files diff --git a/libgambatte/src/cpu.cpp b/libgambatte/src/cpu.cpp index 2de9fc51c4..4032210208 100644 --- a/libgambatte/src/cpu.cpp +++ b/libgambatte/src/cpu.cpp @@ -23,7 +23,7 @@ namespace gambatte { CPU::CPU() -: mem_(Interrupter(sp, pc), sp, pc) +: mem_(sp, pc) , cycleCounter_(0) , pc(0x100) , sp(0xFFFE) diff --git a/libgambatte/src/initstate.cpp b/libgambatte/src/initstate.cpp index c52a530773..50b29b07f7 100644 --- a/libgambatte/src/initstate.cpp +++ b/libgambatte/src/initstate.cpp @@ -1,21 +1,21 @@ -/*************************************************************************** - * Copyright (C) 2008 by Sindre Aamås * - * aamas@stud.ntnu.no * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License version 2 as * - * published by the Free Software Foundation. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License version 2 for more details. * - * * - * You should have received a copy of the GNU General Public License * - * version 2 along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ +// +// Copyright (C) 2008 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + #include "initstate.h" #include "counterdef.h" #include "savestate.h" @@ -1218,7 +1218,7 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM state.mem.gbIsCgb = cgb; - for (unsigned i = 0x00; i < 0x40; i += 0x02) { + for (int i = 0x00; i < 0x40; i += 0x02) { state.ppu.bgpData.ptr[i ] = 0xFF; state.ppu.bgpData.ptr[i + 1] = 0x7F; } @@ -1231,7 +1231,7 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM state.ppu.objpData.ptr[1] = state.mem.ioamhram.get()[0x149]; } - for (unsigned pos = 0; pos < 80; ++pos) + for (int pos = 0; pos < 80; ++pos) state.ppu.oamReaderBuf.ptr[pos] = state.mem.ioamhram.ptr[(pos * 2 & ~3) | (pos & 1)]; std::fill_n(state.ppu.oamReaderSzbuf.ptr, 40, false); diff --git a/libgambatte/src/initstate.h b/libgambatte/src/initstate.h index dd20a3d07a..02363941d8 100644 --- a/libgambatte/src/initstate.h +++ b/libgambatte/src/initstate.h @@ -1,27 +1,28 @@ -/*************************************************************************** - * Copyright (C) 2008 by Sindre Aams * - * aamas@stud.ntnu.no * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License version 2 as * - * published by the Free Software Foundation. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License version 2 for more details. * - * * - * You should have received a copy of the GNU General Public License * - * version 2 along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ +// +// Copyright (C) 2008 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + #ifndef INITSTATE_H #define INITSTATE_H #include namespace gambatte { + void setInitState(struct SaveState &state, bool cgb, bool gbaCgbMode, std::uint32_t now, unsigned div); } diff --git a/libgambatte/src/insertion_sort.h b/libgambatte/src/insertion_sort.h index cfcd3fbd25..8b76d9385a 100644 --- a/libgambatte/src/insertion_sort.h +++ b/libgambatte/src/insertion_sort.h @@ -1,21 +1,20 @@ -/*************************************************************************** - * Copyright (C) 2007 by Sindre Aam�s * - * aamas@stud.ntnu.no * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License version 2 as * - * published by the Free Software Foundation. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License version 2 for more details. * - * * - * You should have received a copy of the GNU General Public License * - * version 2 along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ +// +// Copyright (C) 2007 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// #ifndef INSERTION_SORT_H #define INSERTION_SORT_H @@ -30,8 +29,7 @@ void insertionSort(T *const start, T *const end, Less less) { T *a = start; while (++a < end) { - const T e = *a; - + T const e = *a; T *b = a; while (b != start && less(e, *(b - 1))) { @@ -44,7 +42,7 @@ void insertionSort(T *const start, T *const end, Less less) { } template -inline void insertionSort(T *const start, T *const end) { +inline void insertionSort(T *start, T *end) { insertionSort(start, end, std::less()); } diff --git a/libgambatte/src/interrupter.cpp b/libgambatte/src/interrupter.cpp deleted file mode 100644 index 4223c11a8b..0000000000 --- a/libgambatte/src/interrupter.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2007 by Sindre Aamås * - * aamas@stud.ntnu.no * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License version 2 as * - * published by the Free Software Foundation. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License version 2 for more details. * - * * - * You should have received a copy of the GNU General Public License * - * version 2 along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ -#include "interrupter.h" -#include "memory.h" - -namespace gambatte { - -Interrupter::Interrupter(unsigned short &SP_in, unsigned short &PC_in) : - SP(SP_in), - PC(PC_in) -{} - -unsigned long Interrupter::interrupt(const unsigned address, unsigned long cycleCounter, Memory &memory) { - cycleCounter += 8; - SP = (SP - 1) & 0xFFFF; - memory.write(SP, PC >> 8, cycleCounter); - cycleCounter += 4; - SP = (SP - 1) & 0xFFFF; - memory.write(SP, PC & 0xFF, cycleCounter); - PC = address; - cycleCounter += 8; - - return cycleCounter; -} - -} diff --git a/libgambatte/src/interrupter.h b/libgambatte/src/interrupter.h deleted file mode 100644 index 903368c391..0000000000 --- a/libgambatte/src/interrupter.h +++ /dev/null @@ -1,37 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2007 by Sindre Aamås * - * aamas@stud.ntnu.no * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License version 2 as * - * published by the Free Software Foundation. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License version 2 for more details. * - * * - * You should have received a copy of the GNU General Public License * - * version 2 along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ -#ifndef INTERRUPTER_H -#define INTERRUPTER_H - -#include -#include - -namespace gambatte { - -class Interrupter { - unsigned short &SP; - unsigned short &PC; -public: - Interrupter(unsigned short &SP, unsigned short &PC); - unsigned long interrupt(const unsigned address, unsigned long cycleCounter, class Memory &memory); -}; - -} - -#endif diff --git a/libgambatte/src/memory.cpp b/libgambatte/src/memory.cpp index 50693a4b06..fc18337001 100644 --- a/libgambatte/src/memory.cpp +++ b/libgambatte/src/memory.cpp @@ -24,7 +24,7 @@ namespace gambatte { -Memory::Memory(const Interrupter &interrupter_in, unsigned short &sp, unsigned short &pc) +Memory::Memory(unsigned short &sp, unsigned short &pc) : readCallback(0), writeCallback(0), execCallback(0), @@ -34,7 +34,6 @@ Memory::Memory(const Interrupter &interrupter_in, unsigned short &sp, unsigned s divLastUpdate(0), lastOamDmaUpdate(disabled_time), display(ioamhram, 0, VideoInterruptRequester(intreq)), - interrupter(interrupter_in), dmaSource(0), dmaDestination(0), oamDmaPos(0xFE), @@ -1148,7 +1147,6 @@ SYNCFUNC(Memory) SSS(tima); SSS(display); SSS(sound); - //SSS(interrupter); // no state NSS(dmaSource); NSS(dmaDestination); diff --git a/libgambatte/src/memory.h b/libgambatte/src/memory.h index bf24eb33cf..7d4a250dc8 100644 --- a/libgambatte/src/memory.h +++ b/libgambatte/src/memory.h @@ -24,7 +24,6 @@ static unsigned char const agbOverride[0xD] = { 0xFF, 0x00, 0xCD, 0x03, 0x35, 0x #include "mem/cartridge.h" #include "video.h" #include "sound.h" -#include "interrupter.h" #include "tima.h" #include "newstate.h" #include "gambatte.h" @@ -61,7 +60,6 @@ class Memory { Tima tima; LCD display; PSG sound; - Interrupter interrupter; unsigned short dmaSource; unsigned short dmaDestination; @@ -95,7 +93,7 @@ class Memory { bool isDoubleSpeed() const { return display.isDoubleSpeed(); } public: - explicit Memory(const Interrupter &interrupter, unsigned short &sp, unsigned short &pc); + explicit Memory(unsigned short &sp, unsigned short &pc); bool loaded() const { return cart.loaded(); } unsigned curRomBank() const { return cart.curRomBank(); } diff --git a/output/dll/libgambatte.dll b/output/dll/libgambatte.dll index 4ae26f6e50d9a7ec9ba604262dd54585caf3ec0e..706b7f64716fee0bb7421ea129a62298a9be3a45 100644 GIT binary patch delta 46919 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 delta 46876 zcmd?Sd0bUh7dL+P;WDFua1n4qMa3CHO;ZaL^^$0m^NbT|IiTTCRu{toyy4I(ceDD~ zU|FUQC8nvMrj`?0IS**2_oOA2rKZCBU3;Gc_afHw`+eU3UOu0%XRl$ewf5S>*=L`7 zuUTvSvex)5?V|dyWe<0=Piy&8xAu=Zf=zUJAUrq@^plNpf*ust#X%2=>yn_Z;<_j( z7FXvN3Be?9|LCINy14dh^ioh;adiZ>6W7dOdCe8{6BEZyARmhR-?CWW7O6cHW!wTU zW!Z$Ehnvz;rBRf>S~E~7Ws0&!a*JRi@ZT_Y3ISa)(DJ*z)>EN7?e;-5;rf@qBcuEpOXF zQ63&0lj7Jzyr>k%KAYp9v-NyMiMJQ-Sx^jxKv>9KuotDx3fh#rpcKU~%gV{_f}1F( z=Z4rE@Q|JxXmh}D?();%+V(8?9v7;;XF&-{ObJ2hB1*?uADB_s_SoA!}s7j?%K z?Xu^Ws!yH#|9B-ZHdOo3djPX&oqR^Hn;Q4g*ehB~-{!2JHo$j0Q?)I=&DEWkwM)KZ zSf-Zj*E@V^2gIJ2f?VY$QNpaverM=}Tr$Cn-7>sxWHJAvNg}mXc6vm*}`zZjI4K_=npD zk(9!!@Z6`W*?T=dR8<9@xB3r`8P(p+^BRnw>O9-Dt^wi6vqRBlfZlAZ_H#g-eZw{BF`?Yrp&jz=@E60=*?c5*rObOC>A3mLCeD5Q8oAD>s^TNM zLz#d+48jOX9kz4-{{7mUH3HSu@!FMuSo?37chq=VIT z9Ij0Ze4IU`?F_ulM&@r1s>y27yi9Z)=@GcGQ2(PyI~Ck_eD*BN-a^=q*_pUIYdcLn zlMLLL^@fUe8L~piDpVmOTF8*2{j_TMq&R$297ncb-YY#EZ_tEJbbQytF}GM-T&s(n z){?mw6tai~z$elFyTsgOq=eNdS4|8om`f{6Vs1A}V&KNPvI5q(H>*@z98#NI*FFe|4?E?g9G|e|vcGn)Oux-RXsuJ#u7$Ly zon4)1$3ZPNw0T&|Dl%G!oG;~14gG?tYj}Rsu=g2zT-#s!3>KS@!xu2D>Jjg<)>?5y zKlP(8wI-2ou}NA{aAS9$oQ0Hklzej*Qr1!O%UMV{N69~DA!Qt; zfSiStZC~lI4rsOUZsRbjuB(12#lDkl>=YkFb-8^?8bqi|L%vnfDM=3C8A*DQP zPLLQ8v4O7@<}AFdJzKA7rxEFBPy8;LYJ-y;JCJY3GK$_n$<^!5;I!d9l6Ck?qtL)Q_@L#U8k0 z{6Keti8B->sgp1kI6oZ2C9puF)TDIEzllrWyu)Z;U_rJRog0|LK`l#ie2v&*FupLf z0ykz;+Q4XqBIOOmSQII7D7Hh9GKXSE6fgS8au-MTX{~L8I?Q^;{MaO2+Gl<3sJjgAk(kolD8{&Km|DBLUIi5nQAqn6#eSzexA zo-O)$RNQAh z4wRoaGNO4z1=~cmydD;V*CYNLxXKYtbv|F|F#Btt9FYqBP1pzC<@%{^v}(vwC5$V+-yphVY`W|S`8%t=`i%l-SqAz zQWuJDMZiXSzPMqp$PauPCch(9*`av>aGvX+fs5e$igH9ZW8hIJdqf-UgA#h63eE&OeciB#**GM`2Qq z7#mG!aZ5yK{zbVO?Y?B=yX9F_kD}acid(dc?C3=a72SaL8&=VEMHhVhi=9D#mY2)s zp@v<5O(+Y;<$Ei-Cz{1nbkH+G!$G0lr-MkOr?{$B-$6nzn)J*LN;Z4wG@>#FuxN-- z{ri!M4sz@*I`8AZS9Z~1J;y=QP;XpTUM{D0fEL`ik(f1=z5aBi&3pPKC!3Zvr>#;& z3}c$Rdb)6{$rVuO>3i;=4>6x1ra??sF^EYA@#^uSirqo#wEt7e=#j&Lw|P}o%*@1A`X#u z3{E5MG&rg4l=!Ve!$A^9k-ggGgJk-0SH0>JFZ8C6Uezo${lh)+LNiHPq;crbDASIj z*4Xr5o)(q(9R9gj_d6_mAV(8pb=2+ha;(a%w0+rz1!>!K*e1Q1UN zVC@I%o>=A~L55y6gPN8T=jcimF{s6&U?*c6w}^$talq(Yn>X&RD3@q2G>sH%5e83= zLsWQ3R1m8V4SBLGhCB%=^N~U>bni3bg_%D(lV3xv`os&peVwaYTC;zvXsuWlgz74o zbZE3PI~=qYIzPoIPNG}vpk{3XDA`REJZNEw)Rr}C-+}r@-XgG=imvd60kbPYswDl& zLo(^won|h*vyHg26_^l|3y&kY!ntEdj-bT~UGy_8(Bi&?aPusW8Y4j5>h))Z5u*!e zT_!uUE?@TXzv(#U-2JOL_VpWgX}z(_>|^8`*{6|j=D(E;i6r;QKZ$ftSGDT%FZ9lI zlI=#FiWSuEeC?Wh{)K8jRR0o#dXH!-ZH)AH!Mq_>9GU|960FSkjNM+9*6ta*xKYMb ziZO1}i%OdNr2Fql&lUSI{b365o&ju*-)+q!?R0~sky+l+MYEbbr~FcmJ%iZW<@K?Y z7dnn%4iuszpj&5L_*-;_-1^I@xPzv> z@Z+&V4&=o6LJVIqj4@A0bzF&u_p>MUQ+PXuoGbU%Fxr=)7L# zdTlI#v>P?MX_%-kZshkFz+$qtb5*N;01LeUJ1JJp=q~h(kvy@02-OQ66lHEcwS>m1 znY8`jA}O@VjtuNaJM{ELl11Viag)|(OO6aoY+2GJM+PQ1{Ut{R zCOF*$=YD}L(bAoffz`nUm86#^HSYKIN1}0o|0f!bVip*FLjipO}rxf>nW6I)gDLk06 zmkb#NTcz5QhKQoBB1$nt6un_7(NV0o2fJc1K431LasnqJxgTIWW^27;69ed(S?~LR zqU^?l{Ohp~i$Ph%$#g8zu`*cV%SM)PJqA)~yQV!mWxO#Vw2~I@C zH&BuKu_?hLh5rtDYLQJ(rKFu_d0f9l zTSRFdVNs){*_Jp3(*2CA3EOVD+t#Kccc>Q+q* z$^WHVgi($1Z)E>}YTbp_uB{V8^M7%-R#dBIc-lpu{(atUe2NE`md3#Vo|*mR9Fbik z*8qJeEZU6yJszfTd`LZuy%KGk^e&LfQwMC_G#-tT zTJ8Xg?BGI^(|j(w+^W*5Ir4;AxS);}BdO9L*@sb>tC$e?7_}?pWqdBEPXP``lZU31C9bg5YJ{EM*e$PsE(m0f+~WUg4Eay7BI z%M+G&TZp+sD@l}CwT$75ZN4!q`xxWR7{ya$1+i=%gbIxYT0h19jn+@`oI}f}*q_ky z={m)<^&P{-{oT!KrBnTO^NN6CML_1G#xSu~iboB1K{qL&K->k*rGSQ(o4~FINeK-P zH;H~juCLU??t-&I;Mx+XR6LD*eukcYEk>xaroI+cj3ppNK4Tgyfn5);Y^0A{BWFwh zlwEhhC@G*ux(j+r0X5QH&{_(pjqZZb7E(fubeG%~Cn&Cs)TTZZW~ZBvRMhth4H2?i z2b!JKHl||S{7?5My{wsQg=+6iv>l-kq(l9HTMbIcKi?$ux7{lI``t)?+O5J*bUXSN z7Z17B`1d=I?kg&|W*SZ^hST6vuD2e$+-SAjE9W#V*XCV^CbGV*siI(jQMvNoqpty% zhNL__4V?EnXgulDL4xsu=Z}-(F<)@xD;O#w2plUNtCXeNaW;!HCB4r*7XDkQWt>eJ z7UZ+D^A*U&X(}x*I8h*}9t1NLQKi%qQPC4X#R-~o3z}bf{ty@|&nFNyRAhK>l7xQmE6)RQKLT1``cgZ+JI4N-tDeM0mHKTB1c-y& zRt|!+6T&(%5eA91k&o09Xa7`Zcy)C`-Ro?`%fNZ_Cp<&1X}>NnF%s`li1%XHUhg$` z%i?Y*Umu!BfsGK5Jk|0jG?q4p(8tz5)-@iGsGIb|Qk|BfD(Y3VNkd;PwP_vtXKm8Z zXGm>Y#Yx*PUarbmsu(8~y_Zy{73iOhlZGB4wC{hoo_@5voYEyTMGd#?#kNf5VS5FOE8 zR-iQBQ=y8!=n7A%Pl^6FebE^|L{W3aW1;=uDv0iQUsj-GSFgXR5X%y*z-K|=T9F~OryKZ2K@6%xuT zBp!w0F_i8-{vu{^Q*@mKUo~*CN7=2e7TD&|g6M#Su9Yao|58cx!V^@q0#5t=tEBW0=THiHuX%ExXd{rcRx8|>!m+l-%f#v zPmLIllsn`}116Ne@ecLy&*n~_?l*>0y|!qQcr^SUYEt)`HP6t!dZTy=?Qn?pJO0Tj zb+B1=wX7;8nmp(Dr<$Z@)|@D7QV+O=Yp12--j<45N08JT$4NssATT+iBap?pPH6pvhP&8WT= zGz`pG28)_vhE^=G#+}Rcqdsv`jrz-74ZCMUPb`ssQja>vU9Un-Ow1qaPzJ;^-F?)= zRQyV+QP%IHCU(#IYf_JLd>=g$=~IN-{g=NjhiHbmwjR5wLOn-5&rsIxwN49_F>8W^ zdE@?L7FI) zi1}eo`1@B8sZWws#KicgDt5WOAw4}<))6z}pX$geqD#(3{PR?3d5}IvVDt$_JyyRS zjd#f*hw0!rX2HsCEo>XNtba|exND`#Dzu!t<6Ydk0t} zA9_@TSSXDSH1-@-D;SHMs8AX%I$ZYNee_9aIw+L-vgfMPr#GA=H7hKY6=c6vR{{DY zH1*S@zU;I6=#$XYj}`irb4bm;q!m3H#*S;pA8jlaBUAHuZP8T9>wR-+tVAL;Yoxx+ zJw_)s9kcQ1QO)!?tjj=ijH6~zeIlycp=?eJv6B^MOl6YD$*p&lzN$`$O2Z^Ebc3aC z)tOMKOA@JT*MABVjjlQcDwRoM82(b|R;E%MXkj>;pFQ!~N1utH#?i1p?8FBcXeQ9? zp+Ud!z&(TBj!{2OMj+FUzM!KGw=|H%h@hj;6^nm%30?z`X_vFI2q~uVd*vLKYf9ji zqsF>@g+8!EO~0}W1!~rnEy(}<&Kx>_OVL`cybfiG;K~vdsIRk2)GG^6CNC)Kvnr3< zh@4P)$VTLl%9AxBAC)9|C$d&&LA%DF<@Bj94g^W=A=RZ@i+)`$?9#JE|4EY6%Ip%0 zepHe)5{TR*Nop~XnFOAI)R%PDuQll#V#Hw4r^$*k1|NNlBxMXfdOu0Z z7<}|3Ny->}^!AdJG5F|BB`IU@(Q6CxelZkO#NeZ!lgDf_h9JFIk}`%My-1QWh9LdF zl9Vw7>8m9vV+hh;m86UzNS`mr`^Av_n3;zly^pLYV~ElpmZXd!N^dJk8AFsFB}o}W zlpZWe8AFt=N>auUrT-y57<#`LPF2JZrEd|M#xP9KKar%2Awhpfk}`$_eX%5E3<&)E%_T``No>4JkCY^ZM5MnYDI_BA z+@|$Wtp7y*B1sB~$g`59kcd1aND9gB{X4l-*sMO3DL8LTK- zt0|l^{Zmnu-oMIk~Opi8494t$u)hnD3g!Do5$@hkn?fMTj%$C^oZ$z1#*!8WVOit|j22my_cKv-( zCMS0N4N)d1c0CW}Do(}?h!aEDt|yVA*w{0@qbQRTrneGha>Dc|Q6?u$4;5u{!gL=| zCMQh4gAXHCaq{N?nJ%V(4xx0S>T5)qoT&QSqD)RyeW@su6IGul%H%}VXNxj9QT0hE zS8+0bpmd_@(L#~>#G;3bGC8s60isM!EINJPBTIbB8+THFDw7k7?i6KmV$n~dT*ZlX zkaS|vUloev#7EB&Wpd)9KQGGU#7EB%Woij_Hlj>UeDuMhOiq0C6qKttdEap2qg#X` zISJBl`cahRBuKw1%H$+SFA-&O5~P1G%H$+S-z&=GBuM`XFI zh%z~e(ua#OIf>F!MVXvL=}DqYPNMXVqD)Sr^j0WWaq_j{Buc;NYj%8seq5BvNrJv# zl*vhgUMR}sBtidNl*vhgzDAVENrL`1%2k{U7$RGep!bI`$wy3i-6qTAMeicZ$`9;Rq=+V4?f!BdRYG8+G|`bU1xn$nE{roUiQS6@hpq;r0ScK5;pR zeI+*F@!%amt{?LaoYxR`UEB91UF%alzpDqnqAwu*L%*D&w?Q*!O%917SbRkbl7w!#J z(Lu(GKHc|}OUA0a#gd~a6fL;7osDQ0Zw?H4i}fpvZo)&bq95=yhHB6VtN4lo^#u9T z;zooUZ|1}|0dIa;(OUO6S@JWM&f}1a_n0(|%z-oz*2RZ3z@tGRKW;-~Mi=AGqPK&_ zeJ6&UY&jcH2F$s0&p4r(>b!$w>z6InjFbOhY#Ql9C+Th7-da|9zVn|IxtoCTi_`Tyi z3BQ#A-(HaF_kEBgK%IdcwsND7_r$iXXD1HAZ`Cv?VGu0wI(XDA!%r+x$DLfkY5qblr-QAASel~0_FpY0S>GLJ_7y=`~chp zf_o@R3!pnN9GC$t28w`Fz;&QTPeo}8JOT^`?9=eid%!N>PoQZpv>eC*z65>-+S|}N z;BDYXpi43`0yKP7QJw}~2F?S+AbX%U0tDKo8n6S$fSZ6Rr|aOSF%E!z zfX5^R10)*t^CsExGZJ7#O;(iYz;WOXFcf`m8kval+rVzL>N?OUMNv|L7lBWJlR#`A z%za=nunqVXsNWZ@1YQI-0Zu@D3+7;p*r6R-@!S^+cy;(!ETA}||R2rLKQ z2R;XO0!M(`fZuQ|NWH4*@-amw?5CqZViZbO5>meSx7sIQf=KhPT(1dIiy0&{?cz)s*0 za0<8#{4oaoUyg$RSVgG~^aDl#PXn`nY+woS7VshPBXAA44S0{k7yudoErADtX8=3k z064H3_zc(r>;XK+W0?o)0L_5-@of5MLu5 z7Z&iuuB<`+V^ddo1k#UrCFjmv6NX&md9KsSr^nTzp^%%sz6S0DkAF;Gbl9tPd!}Xd zp*Z@H0=fOHPmg*Zib@Wja6D*;@#CzUzwDVv`njl2GHvSKh1y@457%FcEo!pkL7U@d zieqnzV?jrB5`NSwr&l8EKdn76BbvqK&zjMhsY)H~^O^nBFXn5$vl_EGTI*RY*bHs( ztfs85W}nrCC2C*Iif1FV(piq^;PLp$ye#UFyd^>2I1b3`7UWEtExI^o#XP)5alV_s zVs;H-b=|W=)m;-bzvmuf{k5Ub&93$Pv-DepJL8YoD5RUt-)CqSpKIK5E7*M;dj#9& zIPTm!gDF?k_){r(PRpGdlszl0Q}9hy@QzyI{hoQTnS`kuLSp-#aj$2d1IImMscYHjiTbV8I35%eELEEy6)SPa2Y;$kz zh0W>5dd$11O?tj}XcQ=v1LysMgxef@67%||Y5Sh<>7DF27}y{|(ZXM7$A)QrUTD}q z{TYhjhi-udFF%xp&L`Usy@W!JrITRJGnorqm^mhMkqh%>ld0`^A$roU)0q+&O0{5Ki!t^@8>!8Y+cST3b*{ilr<<%1WfHj+B*xvd!It zTxyTK*x#PG9VJC?1oK;|M{JH8NshaTTZ!Ap@inFRpwkyo+42?}yv^m%bN)FEyt%s- zR7}PX^vyVuZr zH)xd0?aA4{vKeR5u}`8t1g}FeFqrx?9gM(WTM%SVi(*0)xEYcgOG9(!+(+z*+wRb( z4UkWpqq{9BuWy;n;aCGBJ@ST>CpvER$_w?y$2t?Ue`NSkz~sh9ZT`msH(J!Zf#r!g z7T+FukE$uC*r)M9qh25XechqJlJYDaax8v*8t*2eYrnj{%w`u( zExu55;O8lXMj!v3l`5(X%UuK3Dm6T+({Qt4sZK-Mup5zhnktUNBI0`k!LNo=e*Pg4 z@9H$%0>RCyiVZt5?baIKdsLv|X2h>L4L2M9)oHld2rxD5u8DUuQo~en)o?RX)6{Uy znVSv#dTW*TR&9(O+nRe-+>8WQr{NYzt?D$~Y~bf)@AX19@KGRR1cX+n;%3BJorYT= zVby84*{E%57&)`wJJ4`byEj%hBN5eUxCIhforar@I#R>#KAzo-)csc@^{Ufwvr)e~ z4VOUdd3_sHr{ZQL%G9{;Kx=4f+;^}xs!k)t-|n)IG_Fp?Es*HyG+bJf*SAS^8nBVQ zJEmgA>RFb3Gd1v~<$sr#+q5qh9%8Gt`8oM)i`FT3vd>!FOYtejwc5JeR$58!8~mI8 zEIcIHag%{;-Yw)W*2EKO^e*CBjDZf`ViIDW-BdskHA zsKk+pBNB&?+PPnwl6OsgY(oC1#oiuaWG1kn*KB;@Ycmx$_L`HE{gw9ovR-U!{;p*Y zGWJ#eomcuYwk^N+tNU2^b|@)<^H))uI`0Tv*b{}ojk#O%+bqvuY^%0{*ALzbot;AZ zU#t$h=N9H4?%QI`V#4T@Mu1C4`lYI8!7ar`WR`Bs^8QKnn z=J-k*zqS=J^~&0BDPbc%^I0jB{lGBTQJC7pfblbFx9u$tR^_(t?Fk+M_4ycpQ3)@zVJ$jP1}ioDA{mR|Ap_vRD(vl_-Yd(arB< zq*ih=v|&g0;H|MHZMZJctfv|VZ%cP;qIILz>s0+*x^sYrVZNUQd`_PSIFt4{W|l(oSKHji}7(PI5= zq+2UEEfh$}1+Wu0p`J_1#X^yi*PzDH>V&jtqvE$C#jvCM0jO<~+9s+0N{a3$kbNL1 zKf0HJdUGV>Z#!5WE$U3mT8Sf!pp1lnG*TOJCZg72_gJlqMrt`{>IFYtg*{f=c&1VF zgeu(khPyY^m{6|`*Vx%QYR+(N)wvKqepOC@IfB&sp7l{@4%f0SgqrhcqNLuGYQrV< zmZW+~%9v3P2}&zD>#a5s)@oVnm?0R=+OE|+7uKM3D5W}Dr20ECXYho9iQF$5X*o3% zafQax#5y4KS{YqJXJ)jf3nY3dC}BLree7Ipy0Gh1E;w#V@@X1Oxs~@W~|MI z(QFF-+ni+N4VC}aT9(vRe;G$XH3$IX9G0!hF=bd2^Xq{2d`U#img?lz8SX(_rw!G% zmegzbL>2a3p;qr9ZfP@T&&|QEv&YDaF@0)Wsa9xApR=0% zg4M^EJ%AH5$4qSc(9l{(OSfhLFzL@w3a{aQ@0J$ZvGgjJ~KGK<;6!; z_Lw&FQY>queS4|C7f!%xTo$pH~`gxLZ~}_wPFx$+ISAj>x{%Xx8(zrK$C@57LHx)42Sv z{)+I3ne}&BIjHsE+C083KmTPoYB;C)VaJW%N{_P5FU$!$U*(^R|pcm0r_uCg%5-cQt;-|o&zwaK@escRCo<+mTNJ;TQ^ zyjjc{I1Pd3lUmvB_O+UuGKz(aPtXqk-AtQw$JR7cWy-U_i$FGz2fPZr3;YN81URW` zC+{>J_?jof@3bNQtjP^Z(*#y+Pw>}>CbBO z-x-TiTXf-by+E{7SzXl=geQnJl|`^0`FWLv^H(5X+o)Wd?^anPdz;Gb*euCv?E%&R zshZ{i)mX`YmH0I!e-HSo{*bhN$Ad+vdq9xaHWo?R?<(G=JO& zIV_+{IDeN|ugLOuRGuu$=;6AsCv>_}xv?xyM_DcFARGN55ho1dG!dU0M6e&czQSAj zvBvC0{+J)D!>046{aAt;7tcTS!^o%~&x^rUBjWileyo8S6i*|gff^Xk+xoK(>g5MG ztI1-0voQBC&9@1%EkESXJk{3?S&o!Vl``+1K~PLKWJ{$iO3J)?hC}u@Usi*~`OqmR zsKZ3Xdi9J#d99SJl#-VW3ElmMQ@JCHH(0S?)sV{TrSiq)2-B-)M+ml|7{^!EWZvvG z$xQ%vs^A6&g4r@_4`x%C9eQ)@9F z^?GaBDVXRK!ajoxlH`93GF+0+8f26tQzgl#grH%~3|Xv{m9-Lfc?rpW5JX+>8Oov> zeAo(0v-n|hyzMAY^+~49$MXKN+MBKTB#fPcv7s#3gU#kMYO{r^(wvvoW_9C#k1=b~ zHbvga`-LKEy*dF6kJG$QI17(FB;@emvIXr&x0-F-PV!Hylh=j(ZN4F#g?3otBFBIK zQQ^BjROYzIDg+H6n9gs9vxhJ%J4CQFmd<$udq!28(dLjhh{Tw`*_6s&gDB9Sc~K;@ zdPbmlS{5n1Z)LF#ikoDSg8D!f>!J866(iV7g2PKAnT5?R_%o8tVGqCE6f1cyv*p#B zj#5f)BG&uFa9eIFrc`?_Sh2Ret}_sRa_&QTm9@4RmAc_EqIE(HUssoX!+zki>#>8F z?!D@>81_ETsLz^GOX{;JYO^K<-VIncZI zhxt`luCtxwc|Gn{wtIQLoO_@0sMajPxKkZ`6Jxwl1}F1`coyFDbj`{Hn)$cx0h79t{!=#^zkB$L@yOqr zD(ni{Wf))OMe(@V-sER+seKVEbJgCp_NvJP+p)gv7$4b=MX`0<-VQZ>=5OKB38zZN zdh0Tl-jl7{Eo5?GG-F-0>OjAm?v!rn#v*_Wc@^cw}i>-;W^M=q(Z;%LZU`G|m&vN-imCvYAgi!Ekm)X&Ealh_pN| zie8kK+TMIx7Zxhc9cqYjc>B)6fteV?fyj(rt%Oo@if`+}BHC}QRv`@v`PE7&IjeX; zSFGfAJWW+5*Yrx+d4nf(Wy#D@u&670*)p-d;x71Dh@`)ScP$WHx_1p1P^^?T&9IV6QR+j8i940Y05Con0v6a zPJb<8vS7|o5Yi{ll2hnJ)rJJ1C48O~9^g^kvD`e)yWkSmN0^b_WSX%~;FG(ve(ZBz z)SX4u>y3rGYFhZK(%3>?M#i4ukx8shy|HDa>28d_BQ^W+Cz4o~_z&(HSu?UkdG{05 zjHaaI&8k?RMDjCvQ4($~Bh89NPNtZ&OS=EdZzo|86uPy^xF?Eiuj6fdFl*Bwv#wz) z3jw$+nu936Nq6~EJ@7cxuZ%D4iM!2;9?ZLQPZ6m(5Dd$Ffo$P7uv*!AQ@BtHck%P^ z9CFsxGrUZ>qwrQ8SPLHC6OUxaKNG%ujpT;h5v2LJRK}O|#7g)J{{$D0)3+7=YX{bd z|I(8MsC72-+dbJhwa;BXx)(do7V+*jwu~Jw_`$}WP}zJQ)0>@CKfcME_hy6muoPzP z+T|~5^xyRV9OgalV|K8)TK2=fv&P{Yl|5e}pdI{P{%H!KQlFQHGj1q zTY^K8=>F^^yTr@;v*m17!SVrYva0%aD)1c4Mg~~tzJg7JV{s}*aB}CO%m93@r^BlS zv&XS7YSn1)0?uCWU*dMeu}e_FL$g>@uXP-i3VBd=|-W^XKNXI?b+ar^Lr% z=u|$wSQ~wARK)sK4g1W$8d0WV#Ihxi{0gymY+Wq1uQ zJR6pCd0liyOsMJ5$mno)HM1uNkV!h8!^P^mSaxsc%?0Stk3HB_-g61-tHw^`%a^d> zY)8Q#OR(Ezc|2hmp35&5OkBqLTiB9&!R%H+|2J7QtDp29Q{sU7fIsl(YNnh6z5%{i&6mH$8nX8bHob-O1T|t% zLHl4TbQc$<0-4ydu({YZ>vDJ>-z~)ngd?I`L#^h0%*Wwpcg<# z(Fy$qCJwaCy}DA&e$N*QN>;O579W3n!3C@2etv!p+oOi|KlfeB z>ZyHGc+6TvcAG!C7CBqTpIpmYtF}IT`C8VZc9-5Tki9ELkH^pJz>f`|Bh0IDi62|b zR;fw7g+2cEC;0!-NAtR$vc-5zTKg$0W;%a<9nNV6^E2x}P37x8W0%!lHqO_x7uknA zYy*xyQVY6nV3Sm}dlFxp&qm{!tj6ceuG+fu#hfdqh1##9LNgS$QlUNy^Qd4&p*Iz9Cu`p#w&45S>|0B0&DQv41pdp} zej=J0wL#Krl{9?n=1CuFg!Ot0Vx>f9;G-x?OMY-4`-DBk=j>nkPMjeCr};+i~}YEGXZ;n=K+iirWG_gh(5*UCHoML;y&ct z4zcKZugs*?D~p1yJY2yBmsZpTy!;SW&gXcYVyviBX7a=zSV+U}QaDCblqbzq{z@8pKw58LXQrs+tuPA0=5#>!P<+M4~X=jV-<@{hVs~vHy zX{FeU#MlUs_!=)G@iiAQva0BVrT9bM<}j-rIm$(h&pIn~CQ7y2HszxZ!?~;V0Fu*T zu&}-u)!nSuBrzQ-L5!SLwH`!bItY{2ufck&W=7L|(ezpnU3nV9ZZ6CaVh#keM-v`* zgoU)k|3f2^MK!D>Yz5(#=t^c7G4UqMx|mNrf^A$2pfz;3{_s1XMin+Wkz z#8fG>wwf+U`VJ4>7UfF6QN5KYCCGt_4DufJoB9vUHn#F%k0spWu# zN^&&~&|a3Z_L;ol_jm)>TwFtV>i5jM=iDh2K3eEv%o<}lHUe2wC9n$Gh(CkB^F3?C zX7IxAF$u==gdgZO2lMy)T2-zAQi?*g=9hb zj+a!|X?)pHgwb5;7m~hJl>H`14K_{TzqJGN_E9XumaSb z%AdluK_I@6l(>n8r07U_y0N^Q98v02zTpfD^)Z%-KY8IX>?v;WqsLgZ{pb3aEi_fL zZ>clr0bo@W7`+6;Lon75qk~{r1Y;ppX(AY&g0YwwIM#&?FTt4H01OYo@D_}hiSfHa z8a{%d5#u~QB?ykM;Ls05>%~IFPcVXsv0X6y1%rOqSzjj@0fNCv;~l}MAs8EpLBlZn zR?Qi{Xv|hHuDqCiD^O5&qB62?1qmvVDEpY~Tfu^?LuCK#TeSpL55BJS%)S*Os7HwE zn0+f$P{~BK$i8J2ls{2*vu}k7>OehEHM4JFVetG2Wm{m6owi(KAzm()L@^a7Po@sD zD16ol^u=JFfeU^3i^gkjLGkS;Fd`n7k_l20M^vbXwdG{q_B+&@#Dh=b-aVMdon+RC zkxv`lZ%miJ1g*t-iD%A6);LpcOt)anN!NM*5r6e0-k#?1O($`8nk{_!nX`KAWbu^h zr_uc#K@K$O#ghK?Ns@=g^+UsiQNzi1Rh(APMv!mu>8DuZ`YWEIe8__a977ue_+dE_ z7V~MRk)B5+HG?S3_Q%{Uo3q{av>5H?Y>$^DZ+jYt=k*ODO6IqM;)O86w#xDuDzA}R#tQJ3q{hfP3nVpGQqv@5ECu5P#iyKsjX_e9F7>)e ziZ&+5a%(}E3qtr)VnL`=@bej#?5Q?i#oPbLqJn!hETf%dLO)yf)hLX+6+Hb%);9VW zZiIBmW8IBHxwTl3dpG-9ltTK<^e|g_lU@AFAF&3U`w*Wt`5663AHeQt%k|v72IV{| zXBYZ_9!Rvq^E0~SdPYH=G!syc25XP!-yh&puCTku^EyfxtKgLxrATo%zfIh1j$@tUcf1;g?}?5bt{#XMVk*@_S%V zN_1>suxC#}1lQ<`>u%5RkNASitV!^yfU@!#&)UX%evblfu*>*Ym)Y{rZj?UC{7%_b zSzRAo!_%FtWzBtWLE<>!_|_J0^L`ENSw;Wq`G#PY-Cp0DwQ zpIIy(1*ZMX8njG;59c%Ze1F2Cn>pz9?5?!CS7)?$E`U+z25d2$F<$rzXT2kQ%g<~` zn?i`~-tmQcDZ(-z7mMg4jz0O80-@L&rcX)K{l~AZSjtP|ydD#^}#kN=fDR`&?RHpe?oDB1E}JC3b0rd^Nt!g6c47k~Fx_6W1{v%j)_ZH7S+ zsiK*cN>3mOw!9vtfv@5Qfv1!BZ_2HaWGOEtI-%U^?a4=7W9^goz6#cs2mW)e^&2fw z>b!GCrY(DXIMn)tms@`q&U)azd5@rS>s3&WLR%b_vt&81Pn4uf_?c_$q47P)9KJST zv)w4h^ld{VhUAEySUX~}|7Ms4*zC;@>sm-)~*`O<|$D zNx1mePyE|X{44oRp?T6HywXsaU>z6#svdmaZ!FjQ7=~YYlM_7jclKF8LqEin>*@Cj zT39eGQ=QA~CHTKR=RSBI^8gkVO%>H{3zo($+*rmt~TRp+Bs^_YpU>HHX8b(xDLnn zD-6bejB9{#{oSZoVqBLS{PhO?s6n?euBPMEC?$*5kdnK6=$|;g2;g7-$v(5Mf13$* z#vyq%)s=T7U0j7<_@?9s6}#(a6-YYcfV{qD7>Y9ZiowSW5r2hW2tB(Jv`t8Gb4Glq^igl-O0g*?jO2N~DGL6Tq6Kn}Ijcch)df)jU`cUSJySxqY_>7>Rm zZtJ3E*qwu=e(^P_A7xxKjH_Z?R~pwe<7%r?5AWY(JmvCg)+GK~l<~7Od(0d^C3C=(iJ54BiK%DQ&w5zu&l){*{1c-m zkD^(CKi;6bI>fKkFjCl!ztmlAV>jubWLc>s%{`dZK1ML3B|p~CD}7Y*GkVMG$}v*D z@(D>7_mkIzf$~a0r%rulK*qSynd1>s#`x(Ir;Z!N1C!Jt$y263AflW(b<&KH}vD`F8qdD(=YaYvGSJsYm4$EwQdLGp%Z z%CL!6LV@UUsuDfel<_i~8mTUuP!O4{{-yGpscH?cS5&3raFsujqFTLQmzwJ=(%0@q*hK1w8AWP+|;LAG|oQkkw5mpo$7=!oPYVBpQ7M|5dwODgp zyf{g}GLBdo2UT+Y%W>1uU zkM}{}8swrso5~eYdR7!eSPnHiTpe!K^Mr|t$XB8)`XGcI7Y*~k;4mGq^%Pu)aHb9( zB9iJ)fsqBQUCSCqP`W(TVQRs`RP}@>-!xDShr`1I)jDi1pNxxV&KMQr0h|y##;zKO z&MrnD6*F8p)fjWEA8MR_CyKv4P;t#xxy`yXs6;xsIK2oAHv4<_(9cA^U-yM)AH|(do#W194Fi;h4YCYa$q}t%&toQI!bU?yWXc+a+_nw08@-Yswh^I)o zD_vQKd%*^uKT>@#A_$))IJynrqBbg3(EJC8?Iq{%Rq_)2jN;R<8EjF4cljxv_4ABmZv#jg*c{X6p)9M4lxC=uz z7E0LpC}{wcgiZy`w~kU9C(}rL9V4+n9gE{{9gox=zC=x+=@=|}WNT@bP5@!SNfhhnW~bOjJKY^SE56a?R$7g zoQn>tAE4CF^yi)jJfxebV1pxqTT_Z_3Q?21i^QUsV{ zH`*`}JBTT71Ex*|&8Lk~8%Lz!JEPM9*#v}Z()`mgYJ<8_Bbd?yD48NN=}L3UST)L? zikEve#xo^+Drq1Gn0)R$>}*|ijJQ&tWXfp3jLVHSR>6enOc@B61J8}FWl?HndiNE} z<$5UH2Fwn3r5VomV#awXe(k&!zX!bJ&_{=tK7-fWnM{c>mOVl9-^Qwg1}M*C`~Y1t z(54`Z67;>d;%r74+lar4SGCk=&K8sKZ}KSL<}e zNs6-NU3`htG=cvkgXojMdxnXsbVVKhL%JFtL3hBx@Dx$sQwd<93huILYAo&!yy25- z1AEqI=rbVt85xi(&2U8LSIeu^Qfw^1yD3f>eTxJ>FTM)?gF+Becm ziEJ0BL{1MY3C##eGkqV1&X+fs(i-)?yupu5REOHXpQVbpn!+DpF!qL}Ko0mTvAImE zi_He!Z_?I|c5Uk$;P|Hw%6J=E8j1?omX$(Y3>j@kOK_#_+0rolt}k|z>m#t=tPLHM z3-RHLj7R`_w9ShR!zt5JG6EDsn1yR9uoO<#gDycijby-jU?q?NTtsl!0ngd0QX6On zbOZ(iQ-LgC39t(I0@w*02YvxOp2aQ~Xan?mHbqrNpfD4d4{+c^U@LF}Chx2_OX}~q1e~Zbvh7>GF_hq zl2CSiI-;kf-N110>ADBVK$+$h%`e&?(fr{6ZP;jzet@a66lK>(Cwios1-$np{=YgZ zybf%l3b>ljQgrpkMBNKMU1NaLC}UzOc>p=1iA)ptD)DhuF_EvMOq2VfQKrfLAtt(l z%>zyNiJ0V6{y$BedvIJ+9LLXHr9sM~9y9S+wCa(LsG61uQglQcV-b%&v^(liGsUWQ zs8((e+G5OVLs~U@Q^gKSjFM1oS9PZCh}z(fpxDtep&rp^Xj(qsdrsE<aRvaw| zRT$PV&ZdDr;?xKDu#Zo}@w06I6dX6(`e=oy%JIH>jwt=b7m>e(i2&1A=S6G4ZrVMH zyA!D1#K+KZyx5Ft@X_M$j0Se#34R@%cCj2~@D;GH3KZwhr3|AT`X*qaoeA+Zv;{A& zK)diQa2?X=$iuqlIAwf_k3Y}vOf4ok!zW;QU=Mo*%r@IoRO~?Z* z6@zEJL9xby#GVzb3@?_y#e(n^u=*Xlpd>uyJ#P3~F+BEt+n#_gwc3pc;Qls#EuKE+ z19l2&BMMNpJB+z4mWo zzQT*ope{VYzPa!pyW%QXwa;E31pJ1F0O1P6*(lsN=bz}@6d9**na>u1U?QckKZ#3N%&*$BI!6zq4A7v3i0@aswr87nTp>6e5vm>gVP zW%n`%*IZ`zK3erh69FXy=Uw5MX)H$^JerU4c=4vu)M^+D|gu)}5CKyUPR@}jnpuc#~oz&EL@iKJtz1;t!afV`!J$G~1=_GdFLtTd#E2rD@7mYR) zc`QLs(??XA5#Xb#hSH9T`+1FaaSYmsPr_|Je>#~6BMldEDC(Gnx@Z@-qh9ra>t@>X zmxs^H@=PhALk6yUh`q<>;a{_DdojujA+uK#N(BEju?H!*HJ%`1#u-Bh!@wOLHHabvhc&)Bbr@Aa}DJkjc6J(h>KA@ zUOf0wHWV)&g95zEG{UHYY$N=7U>ogyUlYvZ`@SwHKjm?@THhNCUw}KGuwEQI*Lrch z^Wvp^G&j^L#on`)FPUwC~28MQc_8~_y9`X&;7rc zdhkLtN{PlLicOAtmvoUn;%6v_7dufNFZQAWzGWpF{GPq}Fw(^w(#mziO^IPO=3(bE za9*2lJEd27=J?h2W|Dx6Y6V?G|2@vkPK4jQQqc+s=>~^T;ReP6xkO&%1z) zDCRYI{%&jOam*5x97a+rH z!uA}z{d2oeMnjp+NPEu+D8p!{(2MstpN2co95%vq@ctjt!3D?!!)eTH=j&lxCx?ml z96W%+e=uKr=Bsb)Ka%9Po@xD&+p79N2Bq4P3SRS@?H@_H!UXK{-?@0{FK$KMGr0eE zV>bO^XDC4LPyEc7DTO1D{?(3z)0`JC?&b=oPZfLzmEu2wyOA!m9$5aD_2MY!uYl8> z7a#H61jGBxHl+W};=mntX7Nnt#ru#pXa=m=X%D0L6FPy7=z^uYY#(ui^CRJ0=jXwt zNE_-0m~J;A9=eCvR(lU$Kn?g8;pcn#H;vE30X^;p1vmZ8`(7)C!}f9H$mfQ`0*d3s zgL>`CD&Sb>Yhk9>yDdDg7}Jh)j+NTpg0yuhxC*I^DnJF32^K0Uh#bj^MTL!X@S*}l zd2tHbHZ#`Rv4fxJXgwkmtBdt(r?6^zb3yFHvg&Lydvi9GotjN&AI#Qg=Vde5McE*m z&9*dcSP+|kRL|S=O|n101SLVdY4XC@nN9N-#)cd@uQ}J;(_FD^>at*2=dyUy+J&)E F{{ej-0O9}u From 79085f9a3a0d20874d75396cf350ea45f8249fd1 Mon Sep 17 00:00:00 2001 From: MrWint Date: Sun, 26 May 2019 12:36:08 +0200 Subject: [PATCH 24/38] libgambatte: Remove unused saveState() functions --- libgambatte/src/mem/cartridge.cpp | 41 ------------------------------ output/dll/libgambatte.dll | Bin 168960 -> 168960 bytes 2 files changed, 41 deletions(-) diff --git a/libgambatte/src/mem/cartridge.cpp b/libgambatte/src/mem/cartridge.cpp index 7d855cf2fc..829330b0a1 100644 --- a/libgambatte/src/mem/cartridge.cpp +++ b/libgambatte/src/mem/cartridge.cpp @@ -57,10 +57,6 @@ public: } } - virtual void saveState(SaveState::Mem &ss) const { - ss.enableRam = enableRam_; - } - virtual void loadState(SaveState::Mem const &ss) { enableRam_ = ss.enableRam; memptrs_.setRambank(enableRam_ ? MemPtrs::read_en | MemPtrs::write_en : 0, 0); @@ -124,13 +120,6 @@ public: } } - virtual void saveState(SaveState::Mem &ss) const { - ss.rombank = rombank_; - ss.rambank = rambank_; - ss.enableRam = enableRam_; - ss.rambankMode = rambankMode_; - } - virtual void loadState(SaveState::Mem const &ss) { rombank_ = ss.rombank; rambank_ = ss.rambank; @@ -200,12 +189,6 @@ public: } } - virtual void saveState(SaveState::Mem &ss) const { - ss.rombank = rombank_; - ss.enableRam = enableRam_; - ss.rambankMode = rombank0Mode_; - } - virtual void loadState(SaveState::Mem const &ss) { rombank_ = ss.rombank; enableRam_ = ss.enableRam; @@ -268,11 +251,6 @@ public: } } - virtual void saveState(SaveState::Mem &ss) const { - ss.rombank = rombank_; - ss.enableRam = enableRam_; - } - virtual void loadState(SaveState::Mem const &ss) { rombank_ = ss.rombank; enableRam_ = ss.enableRam; @@ -326,12 +304,6 @@ public: } } - virtual void saveState(SaveState::Mem &ss) const { - ss.rombank = rombank_; - ss.rambank = rambank_; - ss.enableRam = enableRam_; - } - virtual void loadState(SaveState::Mem const &ss) { rombank_ = ss.rombank; rambank_ = ss.rambank; @@ -406,13 +378,6 @@ public: } } - virtual void saveState(SaveState::Mem &ss) const { - ss.rombank = rombank_; - ss.rambank = rambank_; - ss.enableRam = enableRam_; - ss.rambankMode = rambankMode_; - } - virtual void loadState(SaveState::Mem const &ss) { rombank_ = ss.rombank; rambank_ = ss.rambank; @@ -480,12 +445,6 @@ public: } } - virtual void saveState(SaveState::Mem &ss) const { - ss.rombank = rombank_; - ss.rambank = rambank_; - ss.enableRam = enableRam_; - } - virtual void loadState(SaveState::Mem const &ss) { rombank_ = ss.rombank; rambank_ = ss.rambank; diff --git a/output/dll/libgambatte.dll b/output/dll/libgambatte.dll index 706b7f64716fee0bb7421ea129a62298a9be3a45..15e1421b9ef7c5da2791605070a84071db4b22c6 100644 GIT binary patch delta 24655 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 lag+u)3f|rOZt=S%@0PxM`rWd3%iqm<&+* Date: Sun, 26 May 2019 12:46:17 +0200 Subject: [PATCH 25/38] libgambatte: refactor Tima --- libgambatte/src/initstate.cpp | 1 + libgambatte/src/savestate.h | 1 + libgambatte/src/sound.h | 36 ++++----- libgambatte/src/tima.cpp | 138 +++++++++++++++++----------------- libgambatte/src/tima.h | 76 +++++++++---------- output/dll/libgambatte.dll | Bin 168960 -> 168960 bytes 6 files changed, 127 insertions(+), 125 deletions(-) diff --git a/libgambatte/src/initstate.cpp b/libgambatte/src/initstate.cpp index 50b29b07f7..1263191c3b 100644 --- a/libgambatte/src/initstate.cpp +++ b/libgambatte/src/initstate.cpp @@ -1199,6 +1199,7 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM state.mem.ioamhram.ptr[0x144] = 0x00; state.mem.divLastUpdate = 0 - div; + state.mem.timaBasetime = 0; state.mem.timaLastUpdate = 0; state.mem.tmatime = disabled_time; state.mem.nextSerialtime = disabled_time; diff --git a/libgambatte/src/savestate.h b/libgambatte/src/savestate.h index 5dd72a10cc..b4489d12af 100644 --- a/libgambatte/src/savestate.h +++ b/libgambatte/src/savestate.h @@ -63,6 +63,7 @@ struct SaveState { Ptr wram; Ptr ioamhram; unsigned long divLastUpdate; + unsigned long timaBasetime; unsigned long timaLastUpdate; unsigned long tmatime; unsigned long nextSerialtime; diff --git a/libgambatte/src/sound.h b/libgambatte/src/sound.h index ff3c7e0fb2..b664d8c616 100644 --- a/libgambatte/src/sound.h +++ b/libgambatte/src/sound.h @@ -1,21 +1,21 @@ -/*************************************************************************** - * Copyright (C) 2007 by Sindre Aamås * - * aamas@stud.ntnu.no * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License version 2 as * - * published by the Free Software Foundation. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License version 2 for more details. * - * * - * You should have received a copy of the GNU General Public License * - * version 2 along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ +// +// Copyright (C) 2007 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + #ifndef SOUND_H #define SOUND_H diff --git a/libgambatte/src/tima.cpp b/libgambatte/src/tima.cpp index b45fdfc987..aa4038821b 100644 --- a/libgambatte/src/tima.cpp +++ b/libgambatte/src/tima.cpp @@ -1,61 +1,61 @@ -/*************************************************************************** - * Copyright (C) 2007 by Sindre Aamås * - * aamas@stud.ntnu.no * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License version 2 as * - * published by the Free Software Foundation. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License version 2 for more details. * - * * - * You should have received a copy of the GNU General Public License * - * version 2 along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ +// +// Copyright (C) 2007 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + #include "tima.h" #include "savestate.h" -static const unsigned char timaClock[4] = { 10, 4, 6, 8 }; +static unsigned char const timaClock[4] = { 10, 4, 6, 8 }; namespace gambatte { -Tima::Tima() : -lastUpdate_(0), -tmatime_(disabled_time), -tima_(0), -tma_(0), -tac_(0) -{} +Tima::Tima() +: lastUpdate_(0) +, tmatime_(disabled_time) +, tima_(0) +, tma_(0) +, tac_(0) +{ +} -void Tima::loadState(const SaveState &state, const TimaInterruptRequester timaIrq) { +void Tima::loadState(SaveState const &state, TimaInterruptRequester timaIrq) { + basetime_ = state.mem.timaBasetime; lastUpdate_ = state.mem.timaLastUpdate; tmatime_ = state.mem.tmatime; - tima_ = state.mem.ioamhram.get()[0x105]; tma_ = state.mem.ioamhram.get()[0x106]; tac_ = state.mem.ioamhram.get()[0x107]; - timaIrq.setNextIrqEventTime((tac_ & 4) - ? - (tmatime_ != disabled_time && tmatime_ > state.cpu.cycleCounter - ? tmatime_ - : lastUpdate_ + ((256u - tima_) << timaClock[tac_ & 3]) + 3) - : - static_cast(disabled_time) - ); + unsigned long nextIrqEventTime = disabled_time; + if (tac_ & 4) { + nextIrqEventTime = tmatime_ != disabled_time && tmatime_ > state.cpu.cycleCounter + ? tmatime_ + : lastUpdate_ + ((256u - tima_) << timaClock[tac_ & 3]) + 3; + } + + timaIrq.setNextIrqEventTime(nextIrqEventTime); } -void Tima::resetCc(const unsigned long oldCc, const unsigned long newCc, const TimaInterruptRequester timaIrq) { - const unsigned long dec = oldCc - newCc; - +void Tima::resetCc(unsigned long const oldCc, unsigned long const newCc, TimaInterruptRequester timaIrq) { if (tac_ & 0x04) { updateIrq(oldCc, timaIrq); updateTima(oldCc); + unsigned long const dec = oldCc - newCc; lastUpdate_ -= dec; timaIrq.setNextIrqEventTime(timaIrq.nextIrqEventTime() - dec); @@ -64,20 +64,18 @@ void Tima::resetCc(const unsigned long oldCc, const unsigned long newCc, const T } } -void Tima::updateTima(const unsigned long cycleCounter) { - const unsigned long ticks = (cycleCounter - lastUpdate_) >> timaClock[tac_ & 3]; - +void Tima::updateTima(unsigned long const cc) { + unsigned long const ticks = (cc - lastUpdate_) >> timaClock[tac_ & 3]; lastUpdate_ += ticks << timaClock[tac_ & 3]; - if (cycleCounter >= tmatime_) { - if (cycleCounter >= tmatime_ + 4) + if (cc >= tmatime_) { + if (cc >= tmatime_ + 4) tmatime_ = disabled_time; tima_ = tma_; } unsigned long tmp = tima_ + ticks; - while (tmp > 0x100) tmp -= 0x100 - tma_; @@ -85,8 +83,8 @@ void Tima::updateTima(const unsigned long cycleCounter) { tmp = 0; tmatime_ = lastUpdate_ + 3; - if (cycleCounter >= tmatime_) { - if (cycleCounter >= tmatime_ + 4) + if (cc >= tmatime_) { + if (cc >= tmatime_ + 4) tmatime_ = disabled_time; tmp = tma_; @@ -96,12 +94,12 @@ void Tima::updateTima(const unsigned long cycleCounter) { tima_ = tmp; } -void Tima::setTima(const unsigned data, const unsigned long cycleCounter, const TimaInterruptRequester timaIrq) { +void Tima::setTima(unsigned const data, unsigned long const cc, TimaInterruptRequester timaIrq) { if (tac_ & 0x04) { - updateIrq(cycleCounter, timaIrq); - updateTima(cycleCounter); + updateIrq(cc, timaIrq); + updateTima(cc); - if (tmatime_ - cycleCounter < 4) + if (tmatime_ - cc < 4) tmatime_ = disabled_time; timaIrq.setNextIrqEventTime(lastUpdate_ + ((256u - data) << timaClock[tac_ & 3]) + 3); @@ -110,44 +108,44 @@ void Tima::setTima(const unsigned data, const unsigned long cycleCounter, const tima_ = data; } -void Tima::setTma(const unsigned data, const unsigned long cycleCounter, const TimaInterruptRequester timaIrq) { +void Tima::setTma(unsigned const data, unsigned long const cc, TimaInterruptRequester timaIrq) { if (tac_ & 0x04) { - updateIrq(cycleCounter, timaIrq); - updateTima(cycleCounter); + updateIrq(cc, timaIrq); + updateTima(cc); } tma_ = data; } -void Tima::setTac(const unsigned data, const unsigned long cycleCounter, const TimaInterruptRequester timaIrq, bool gbIsCgb) { +void Tima::setTac(unsigned const data, unsigned long const cc, TimaInterruptRequester timaIrq, bool gbIsCgb) { if (tac_ ^ data) { unsigned long nextIrqEventTime = timaIrq.nextIrqEventTime(); if (tac_ & 0x04) { - updateIrq(cycleCounter, timaIrq); - updateTima(cycleCounter); + updateIrq(cc, timaIrq); + updateTima(cc); lastUpdate_ -= (1u << (timaClock[tac_ & 3] - 1)) + 3; tmatime_ -= (1u << (timaClock[tac_ & 3] - 1)) + 3; nextIrqEventTime -= (1u << (timaClock[tac_ & 3] - 1)) + 3; - if (cycleCounter >= nextIrqEventTime) + if (cc >= nextIrqEventTime) timaIrq.flagIrq(); - updateTima(cycleCounter); + updateTima(cc); tmatime_ = disabled_time; nextIrqEventTime = disabled_time; } if (data & 4) { - lastUpdate_ = (cycleCounter >> timaClock[data & 3]) << timaClock[data & 3]; - unsigned long diff = cycleCounter - basetime_; + unsigned long diff = cc - basetime_; if (gbIsCgb) { if (((diff >> (timaClock[tac_ & 3] - 1)) & 1) == 1 && ((diff >> (timaClock[data & 3] - 1)) & 1) == 0) tima_++; } + lastUpdate_ = basetime_ + ((diff >> timaClock[data & 3]) << timaClock[data & 3]); nextIrqEventTime = lastUpdate_ + ((256u - tima_) << timaClock[data & 3]) + 3; } @@ -158,24 +156,26 @@ void Tima::setTac(const unsigned data, const unsigned long cycleCounter, const T tac_ = data; } -void Tima::resTac(unsigned long const cycleCounter, TimaInterruptRequester timaIrq) { - basetime_ = cycleCounter; +void Tima::resTac(unsigned long const cc, TimaInterruptRequester timaIrq) { + basetime_ = cc; + if (tac_ & 0x04) { - setTac(tac_ & ~0x04, cycleCounter, timaIrq, false); - setTac(tac_ | 0x04, cycleCounter, timaIrq, false); + setTac(tac_ & ~0x04, cc, timaIrq, false); + setTac(tac_ | 0x04, cc, timaIrq, false); } } -unsigned Tima::tima(unsigned long cycleCounter) { +unsigned Tima::tima(unsigned long cc) { if (tac_ & 0x04) - updateTima(cycleCounter); + updateTima(cc); return tima_; } -void Tima::doIrqEvent(const TimaInterruptRequester timaIrq) { +void Tima::doIrqEvent(TimaInterruptRequester timaIrq) { timaIrq.flagIrq(); - timaIrq.setNextIrqEventTime(timaIrq.nextIrqEventTime() + ((256u - tma_) << timaClock[tac_ & 3])); + timaIrq.setNextIrqEventTime(timaIrq.nextIrqEventTime() + + ((256u - tma_) << timaClock[tac_ & 3])); } SYNCFUNC(Tima) diff --git a/libgambatte/src/tima.h b/libgambatte/src/tima.h index a4a486f052..55bfe849ad 100644 --- a/libgambatte/src/tima.h +++ b/libgambatte/src/tima.h @@ -1,21 +1,21 @@ -/*************************************************************************** - * Copyright (C) 2007 by Sindre Aamås * - * aamas@stud.ntnu.no * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License version 2 as * - * published by the Free Software Foundation. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License version 2 for more details. * - * * - * You should have received a copy of the GNU General Public License * - * version 2 along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ +// +// Copyright (C) 2007 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + #ifndef TIMA_H #define TIMA_H @@ -24,25 +24,37 @@ namespace gambatte { class TimaInterruptRequester { - InterruptRequester &intreq; - public: - explicit TimaInterruptRequester(InterruptRequester &intreq) : intreq(intreq) {} - void flagIrq() const { intreq.flagIrq(4); } - unsigned long nextIrqEventTime() const { return intreq.eventTime(intevent_tima); } - void setNextIrqEventTime(const unsigned long time) const { intreq.setEventTime(time); } + explicit TimaInterruptRequester(InterruptRequester &intreq) : intreq_(intreq) {} + void flagIrq() const { intreq_.flagIrq(4); } + unsigned long nextIrqEventTime() const { return intreq_.eventTime(intevent_tima); } + void setNextIrqEventTime(unsigned long time) const { intreq_.setEventTime(time); } + +private: + InterruptRequester &intreq_; }; class Tima { +public: + Tima(); + void loadState(const SaveState &, TimaInterruptRequester timaIrq); + void resetCc(unsigned long oldCc, unsigned long newCc, TimaInterruptRequester timaIrq); + void setTima(unsigned tima, unsigned long cc, TimaInterruptRequester timaIrq); + void setTma(unsigned tma, unsigned long cc, TimaInterruptRequester timaIrq); + void setTac(unsigned tac, unsigned long cc, TimaInterruptRequester timaIrq, bool gbIsCgb); + void resTac(unsigned long cc, TimaInterruptRequester timaIrq); + unsigned tima(unsigned long cc); + void doIrqEvent(TimaInterruptRequester timaIrq); + +private: unsigned long basetime_; unsigned long lastUpdate_; unsigned long tmatime_; - unsigned char tima_; unsigned char tma_; unsigned char tac_; - void updateIrq(const unsigned long cc, const TimaInterruptRequester timaIrq) { + void updateIrq(unsigned long const cc, TimaInterruptRequester timaIrq) { while (cc >= timaIrq.nextIrqEventTime()) doIrqEvent(timaIrq); } @@ -50,18 +62,6 @@ class Tima { void updateTima(unsigned long cc); public: - Tima(); - void loadState(const SaveState &, TimaInterruptRequester timaIrq); - void resetCc(unsigned long oldCc, unsigned long newCc, TimaInterruptRequester timaIrq); - - void setTima(unsigned tima, unsigned long cc, TimaInterruptRequester timaIrq); - void setTma(unsigned tma, unsigned long cc, TimaInterruptRequester timaIrq); - void setTac(unsigned tac, unsigned long cc, TimaInterruptRequester timaIrq, bool gbIsCgb); - void resTac(unsigned long cc, TimaInterruptRequester timaIrq); - unsigned tima(unsigned long cc); - - void doIrqEvent(TimaInterruptRequester timaIrq); - templatevoid SyncState(NewState *ns); }; diff --git a/output/dll/libgambatte.dll b/output/dll/libgambatte.dll index 15e1421b9ef7c5da2791605070a84071db4b22c6..e9b8c6dbbc6f4ba0eb3dc1880951964f17aa6df6 100644 GIT binary patch delta 25268 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 Date: Sun, 26 May 2019 14:17:22 +0200 Subject: [PATCH 26/38] libgambatte: Refactor Memory --- libgambatte/src/initstate.cpp | 2 + libgambatte/src/memory.cpp | 1174 +++++++++++++++++---------------- libgambatte/src/memory.h | 411 ++++++------ libgambatte/src/savestate.h | 2 + output/dll/libgambatte.dll | Bin 168960 -> 168448 bytes 5 files changed, 816 insertions(+), 773 deletions(-) diff --git a/libgambatte/src/initstate.cpp b/libgambatte/src/initstate.cpp index 1263191c3b..ce0b7b0905 100644 --- a/libgambatte/src/initstate.cpp +++ b/libgambatte/src/initstate.cpp @@ -1206,6 +1206,7 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM state.mem.lastOamDmaUpdate = disabled_time; state.mem.unhaltTime = disabled_time; state.mem.minIntTime = 0; + state.mem.halttime = 0; state.mem.rombank = 1; state.mem.dmaSource = 0; state.mem.dmaDestination = 0; @@ -1217,6 +1218,7 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM state.mem.rambankMode = false; state.mem.hdmaTransfer = false; state.mem.gbIsCgb = cgb; + state.mem.stopped = false; for (int i = 0x00; i < 0x40; i += 0x02) { diff --git a/libgambatte/src/memory.cpp b/libgambatte/src/memory.cpp index fc18337001..54eea33844 100644 --- a/libgambatte/src/memory.cpp +++ b/libgambatte/src/memory.cpp @@ -1,300 +1,315 @@ -/*************************************************************************** - * Copyright (C) 2007 by Sindre Aamås * - * aamas@stud.ntnu.no * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License version 2 as * - * published by the Free Software Foundation. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License version 2 for more details. * - * * - * You should have received a copy of the GNU General Public License * - * version 2 along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ +// +// Copyright (C) 2007 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + #include "memory.h" -#include "video.h" -#include "sound.h" #include "savestate.h" +#include "sound.h" +#include "video.h" #include namespace gambatte { Memory::Memory(unsigned short &sp, unsigned short &pc) -: readCallback(0), - writeCallback(0), - execCallback(0), - cdCallback(0), - linkCallback(0), - getInput(0), - divLastUpdate(0), - lastOamDmaUpdate(disabled_time), - display(ioamhram, 0, VideoInterruptRequester(intreq)), - dmaSource(0), - dmaDestination(0), - oamDmaPos(0xFE), - serialCnt(0), - blanklcd(false), - LINKCABLE(false), - linkClockTrigger(false), - SP(sp), - PC(pc) +: readCallback_(0) +, writeCallback_(0) +, execCallback_(0) +, cdCallback_(0) +, linkCallback_(0) +, getInput_(0) +, divLastUpdate_(0) +, lastOamDmaUpdate_(disabled_time) +, lcd_(ioamhram_, 0, VideoInterruptRequester(intreq_)) +, dmaSource_(0) +, dmaDestination_(0) +, oamDmaPos_(0xFE) +, serialCnt_(0) +, blanklcd_(false) +, LINKCABLE_(false) +, linkClockTrigger_(false) +, sp_(sp) +, pc_(pc) { - intreq.setEventTime(144*456ul); - intreq.setEventTime(0); + intreq_.setEventTime(144 * 456ul); + intreq_.setEventTime(0); } void Memory::setStatePtrs(SaveState &state) { - state.mem.ioamhram.set(ioamhram, sizeof ioamhram); + state.mem.ioamhram.set(ioamhram_, sizeof ioamhram_); - cart.setStatePtrs(state); - display.setStatePtrs(state); - sound.setStatePtrs(state); + cart_.setStatePtrs(state); + lcd_.setStatePtrs(state); + psg_.setStatePtrs(state); } -static inline int serialCntFrom(const unsigned long cyclesUntilDone, const bool cgbFast) { +static int serialCntFrom(unsigned long cyclesUntilDone, bool cgbFast) { return cgbFast ? (cyclesUntilDone + 0xF) >> 4 : (cyclesUntilDone + 0x1FF) >> 9; } -void Memory::loadState(const SaveState &state) { - biosMode = state.mem.biosMode; - cgbSwitching = state.mem.cgbSwitching; - agbMode = state.mem.agbMode; +void Memory::loadState(SaveState const &state) { + biosMode_ = state.mem.biosMode; + cgbSwitching_ = state.mem.cgbSwitching; + agbMode_ = state.mem.agbMode; gbIsCgb_ = state.mem.gbIsCgb; - sound.loadState(state); - display.loadState(state, state.mem.oamDmaPos < 0xA0 ? cart.rdisabledRam() : ioamhram); - tima.loadState(state, TimaInterruptRequester(intreq)); - cart.loadState(state); - intreq.loadState(state); + stopped_ = state.mem.stopped; + psg_.loadState(state); + lcd_.loadState(state, state.mem.oamDmaPos < 0xA0 ? cart_.rdisabledRam() : ioamhram_); + tima_.loadState(state, TimaInterruptRequester(intreq_)); + cart_.loadState(state); + intreq_.loadState(state); - divLastUpdate = state.mem.divLastUpdate; - intreq.setEventTime(state.mem.nextSerialtime > state.cpu.cycleCounter ? state.mem.nextSerialtime : state.cpu.cycleCounter); - intreq.setEventTime(state.mem.unhaltTime); - lastOamDmaUpdate = state.mem.lastOamDmaUpdate; - dmaSource = state.mem.dmaSource; - dmaDestination = state.mem.dmaDestination; - oamDmaPos = state.mem.oamDmaPos; - serialCnt = intreq.eventTime(intevent_serial) != disabled_time - ? serialCntFrom(intreq.eventTime(intevent_serial) - state.cpu.cycleCounter, ioamhram[0x102] & isCgb() * 2) - : 8; + divLastUpdate_ = state.mem.divLastUpdate; + intreq_.setEventTime(state.mem.nextSerialtime > state.cpu.cycleCounter + ? state.mem.nextSerialtime + : state.cpu.cycleCounter); + intreq_.setEventTime(state.mem.unhaltTime); + halttime_ = state.mem.halttime; + lastOamDmaUpdate_ = state.mem.lastOamDmaUpdate; + dmaSource_ = state.mem.dmaSource; + dmaDestination_ = state.mem.dmaDestination; + oamDmaPos_ = state.mem.oamDmaPos; + serialCnt_ = intreq_.eventTime(intevent_serial) != disabled_time + ? serialCntFrom(intreq_.eventTime(intevent_serial) - state.cpu.cycleCounter, + ioamhram_[0x102] & isCgb() * 2) + : 8; - cart.setVrambank(ioamhram[0x14F] & isCgb()); - cart.setOamDmaSrc(oam_dma_src_off); - cart.setWrambank(isCgb() && (ioamhram[0x170] & 0x07) ? ioamhram[0x170] & 0x07 : 1); + cart_.setVrambank(ioamhram_[0x14F] & isCgb()); + cart_.setOamDmaSrc(oam_dma_src_off); + cart_.setWrambank(isCgb() && (ioamhram_[0x170] & 0x07) ? ioamhram_[0x170] & 0x07 : 1); - if (lastOamDmaUpdate != disabled_time) { + if (lastOamDmaUpdate_ != disabled_time) { oamDmaInitSetup(); - const unsigned oamEventPos = oamDmaPos < 0xA0 ? 0xA0 : 0x100; - - intreq.setEventTime(lastOamDmaUpdate + (oamEventPos - oamDmaPos) * 4); + unsigned oamEventPos = oamDmaPos_ < 0xA0 ? 0xA0 : 0x100; + intreq_.setEventTime( + lastOamDmaUpdate_ + (oamEventPos - oamDmaPos_) * 4); } - intreq.setEventTime((ioamhram[0x140] & 0x80) ? display.nextMode1IrqTime() : state.cpu.cycleCounter); - blanklcd = false; + intreq_.setEventTime(ioamhram_[0x140] & lcdc_en + ? lcd_.nextMode1IrqTime() + : state.cpu.cycleCounter); + blanklcd_ = false; if (!isCgb()) - std::memset(cart.vramdata() + 0x2000, 0, 0x2000); + std::memset(cart_.vramdata() + 0x2000, 0, 0x2000); } -void Memory::setEndtime(const unsigned long cycleCounter, const unsigned long inc) { - if (intreq.eventTime(intevent_blit) <= cycleCounter) - intreq.setEventTime(intreq.eventTime(intevent_blit) + (70224 << isDoubleSpeed())); +void Memory::setEndtime(unsigned long cc, unsigned long inc) { + if (intreq_.eventTime(intevent_blit) <= cc) { + intreq_.setEventTime(intreq_.eventTime(intevent_blit) + + (70224 << isDoubleSpeed())); + } - intreq.setEventTime(cycleCounter + (inc << isDoubleSpeed())); + intreq_.setEventTime(cc + (inc << isDoubleSpeed())); } -void Memory::updateSerial(const unsigned long cc) { - if (!LINKCABLE) { - if (intreq.eventTime(intevent_serial) != disabled_time) { - if (intreq.eventTime(intevent_serial) <= cc) { - ioamhram[0x101] = (((ioamhram[0x101] + 1) << serialCnt) - 1) & 0xFF; - ioamhram[0x102] &= 0x7F; - intreq.setEventTime(disabled_time); - intreq.flagIrq(8); +void Memory::updateSerial(unsigned long const cc) { + if (!LINKCABLE_) { + if (intreq_.eventTime(intevent_serial) != disabled_time) { + if (intreq_.eventTime(intevent_serial) <= cc) { + ioamhram_[0x101] = (((ioamhram_[0x101] + 1) << serialCnt_) - 1) & 0xFF; + ioamhram_[0x102] &= 0x7F; + intreq_.setEventTime(disabled_time); + intreq_.flagIrq(8); } else { - const int targetCnt = serialCntFrom(intreq.eventTime(intevent_serial) - cc, ioamhram[0x102] & isCgb() * 2); - ioamhram[0x101] = (((ioamhram[0x101] + 1) << (serialCnt - targetCnt)) - 1) & 0xFF; - serialCnt = targetCnt; + int const targetCnt = serialCntFrom(intreq_.eventTime(intevent_serial) - cc, + ioamhram_[0x102] & isCgb() * 2); + ioamhram_[0x101] = (((ioamhram_[0x101] + 1) << (serialCnt_ - targetCnt)) - 1) & 0xFF; + serialCnt_ = targetCnt; } } - } - else { - if (intreq.eventTime(intevent_serial) != disabled_time) { - if (intreq.eventTime(intevent_serial) <= cc) { - linkClockTrigger = true; - intreq.setEventTime(disabled_time); - if (linkCallback) - linkCallback(); + } else { + if (intreq_.eventTime(intevent_serial) != disabled_time) { + if (intreq_.eventTime(intevent_serial) <= cc) { + linkClockTrigger_ = true; + intreq_.setEventTime(disabled_time); + if (linkCallback_) + linkCallback_(); } } } } -void Memory::updateTimaIrq(const unsigned long cc) { - while (intreq.eventTime(intevent_tima) <= cc) - tima.doIrqEvent(TimaInterruptRequester(intreq)); +void Memory::updateTimaIrq(unsigned long cc) { + while (intreq_.eventTime(intevent_tima) <= cc) + tima_.doIrqEvent(TimaInterruptRequester(intreq_)); } -void Memory::updateIrqs(const unsigned long cc) { +void Memory::updateIrqs(unsigned long cc) { updateSerial(cc); updateTimaIrq(cc); - display.update(cc); + lcd_.update(cc); } -unsigned long Memory::event(unsigned long cycleCounter) { - if (lastOamDmaUpdate != disabled_time) - updateOamDma(cycleCounter); +unsigned long Memory::event(unsigned long cc) { + if (lastOamDmaUpdate_ != disabled_time) + updateOamDma(cc); - switch (intreq.minEventId()) { + switch (intreq_.minEventId()) { case intevent_unhalt: - nontrivial_ff_write(0xFF04, 0, cycleCounter); - PC = (PC + 1) & 0xFFFF; - cycleCounter += 4; - intreq.unhalt(); - intreq.setEventTime(disabled_time); + nontrivial_ff_write(0xFF04, 0, cc); + pc_ = (pc_ + 1) & 0xFFFF; + cc += 4; + intreq_.unhalt(); + intreq_.setEventTime(disabled_time); break; case intevent_end: - intreq.setEventTime(disabled_time - 1); + intreq_.setEventTime(disabled_time - 1); - while (cycleCounter >= intreq.minEventTime() && intreq.eventTime(intevent_end) != disabled_time) - cycleCounter = event(cycleCounter); + while (cc >= intreq_.minEventTime() + && intreq_.eventTime(intevent_end) != disabled_time) { + cc = event(cc); + } - intreq.setEventTime(disabled_time); + intreq_.setEventTime(disabled_time); break; case intevent_blit: { - const bool lcden = ioamhram[0x140] >> 7 & 1; - unsigned long blitTime = intreq.eventTime(intevent_blit); + bool const lcden = ioamhram_[0x140] & lcdc_en; + unsigned long blitTime = intreq_.eventTime(intevent_blit); - if (lcden | blanklcd) { - display.updateScreen(blanklcd, cycleCounter); - intreq.setEventTime(disabled_time); - intreq.setEventTime(disabled_time); + if (lcden | blanklcd_) { + lcd_.updateScreen(blanklcd_, cc); + intreq_.setEventTime(disabled_time); + intreq_.setEventTime(disabled_time); - while (cycleCounter >= intreq.minEventTime()) - cycleCounter = event(cycleCounter); + while (cc >= intreq_.minEventTime()) + cc = event(cc); } else blitTime += 70224 << isDoubleSpeed(); - blanklcd = lcden ^ 1; - intreq.setEventTime(blitTime); + blanklcd_ = lcden ^ 1; + intreq_.setEventTime(blitTime); } break; case intevent_serial: - updateSerial(cycleCounter); + updateSerial(cc); break; case intevent_oam: - intreq.setEventTime(lastOamDmaUpdate == disabled_time ? - static_cast(disabled_time) : intreq.eventTime(intevent_oam) + 0xA0 * 4); + intreq_.setEventTime(lastOamDmaUpdate_ == disabled_time + ? static_cast(disabled_time) + : intreq_.eventTime(intevent_oam) + 0xA0 * 4); break; case intevent_dma: { - const bool doubleSpeed = isDoubleSpeed(); - unsigned dmaSrc = dmaSource; - unsigned dmaDest = dmaDestination; - unsigned dmaLength = ((ioamhram[0x155] & 0x7F) + 0x1) * 0x10; - unsigned length = hdmaReqFlagged(intreq) ? 0x10 : dmaLength; + bool const doubleSpeed = isDoubleSpeed(); + unsigned dmaSrc = dmaSource_; + unsigned dmaDest = dmaDestination_; + unsigned dmaLength = ((ioamhram_[0x155] & 0x7F) + 0x1) * 0x10; + unsigned length = hdmaReqFlagged(intreq_) ? 0x10 : dmaLength; - ackDmaReq(intreq); + ackDmaReq(intreq_); if ((static_cast(dmaDest) + length) & 0x10000) { length = 0x10000 - dmaDest; - ioamhram[0x155] |= 0x80; + ioamhram_[0x155] |= 0x80; } dmaLength -= length; - if (!(ioamhram[0x140] & 0x80)) + if (!(ioamhram_[0x140] & lcdc_en)) dmaLength = 0; { - unsigned long lOamDmaUpdate = lastOamDmaUpdate; - lastOamDmaUpdate = disabled_time; + unsigned long lOamDmaUpdate = lastOamDmaUpdate_; + lastOamDmaUpdate_ = disabled_time; while (length--) { - const unsigned src = dmaSrc++ & 0xFFFF; - const unsigned data = ((src & 0xE000) == 0x8000 || src > 0xFDFF) ? 0xFF : read(src, cycleCounter); + unsigned const src = dmaSrc++ & 0xFFFF; + unsigned const data = (src & 0xE000) == 0x8000 || src > 0xFDFF + ? 0xFF + : read(src, cc); - cycleCounter += 2 << doubleSpeed; + cc += 2 << doubleSpeed; - if (cycleCounter - 3 > lOamDmaUpdate) { - oamDmaPos = (oamDmaPos + 1) & 0xFF; + if (cc - 3 > lOamDmaUpdate) { + oamDmaPos_ = (oamDmaPos_ + 1) & 0xFF; lOamDmaUpdate += 4; - if (oamDmaPos < 0xA0) { - if (oamDmaPos == 0) + if (oamDmaPos_ < 0xA0) { + if (oamDmaPos_ == 0) startOamDma(lOamDmaUpdate - 1); - ioamhram[src & 0xFF] = data; - } else if (oamDmaPos == 0xA0) { + ioamhram_[src & 0xFF] = data; + } else if (oamDmaPos_ == 0xA0) { endOamDma(lOamDmaUpdate - 1); lOamDmaUpdate = disabled_time; } } - nontrivial_write(0x8000 | (dmaDest++ & 0x1FFF), data, cycleCounter); + nontrivial_write(0x8000 | (dmaDest++ & 0x1FFF), data, cc); } - lastOamDmaUpdate = lOamDmaUpdate; + lastOamDmaUpdate_ = lOamDmaUpdate; } - cycleCounter += 4; + cc += 4; - dmaSource = dmaSrc; - dmaDestination = dmaDest; - ioamhram[0x155] = ((dmaLength / 0x10 - 0x1) & 0xFF) | (ioamhram[0x155] & 0x80); + dmaSource_ = dmaSrc; + dmaDestination_ = dmaDest; + ioamhram_[0x155] = ((dmaLength / 0x10 - 0x1) & 0xFF) | (ioamhram_[0x155] & 0x80); - if ((ioamhram[0x155] & 0x80) && display.hdmaIsEnabled()) { - if (lastOamDmaUpdate != disabled_time) - updateOamDma(cycleCounter); + if ((ioamhram_[0x155] & 0x80) && lcd_.hdmaIsEnabled()) { + if (lastOamDmaUpdate_ != disabled_time) + updateOamDma(cc); - display.disableHdma(cycleCounter); + lcd_.disableHdma(cc); } } break; case intevent_tima: - tima.doIrqEvent(TimaInterruptRequester(intreq)); + tima_.doIrqEvent(TimaInterruptRequester(intreq_)); break; case intevent_video: - display.update(cycleCounter); + lcd_.update(cc); break; case intevent_interrupts: - if (stopped) { - intreq.setEventTime(disabled_time); + if (stopped_) { + intreq_.setEventTime(disabled_time); break; } - if (halted()) { - if (gbIsCgb_ || (!gbIsCgb_ && cycleCounter <= halttime + 4)) - cycleCounter += 4; - intreq.unhalt(); - intreq.setEventTime(disabled_time); + if (halted()) { + if (gbIsCgb_ || (!gbIsCgb_ && cc <= halttime_ + 4)) + cc += 4; + + intreq_.unhalt(); + intreq_.setEventTime(disabled_time); } if (ime()) { unsigned address; - cycleCounter += 12; - display.update(cycleCounter); - SP = (SP - 2) & 0xFFFF; - write(SP + 1, PC >> 8, cycleCounter); - unsigned ie = intreq.iereg(); + cc += 12; + lcd_.update(cc); + sp_ = (sp_ - 2) & 0xFFFF; + write(sp_ + 1, pc_ >> 8, cc); + unsigned ie = intreq_.iereg(); - cycleCounter += 4; - display.update(cycleCounter); - write(SP, PC & 0xFF, cycleCounter); - const unsigned pendingIrqs = ie & intreq.ifreg(); + cc += 4; + lcd_.update(cc); + write(sp_, pc_ & 0xFF, cc); + const unsigned pendingIrqs = ie & intreq_.ifreg(); - cycleCounter += 4; - display.update(cycleCounter); + cc += 4; + lcd_.update(cc); const unsigned n = pendingIrqs & -pendingIrqs; if (n == 0) { @@ -306,199 +321,201 @@ unsigned long Memory::event(unsigned long cycleCounter) { } else address = 0x50 + n; - intreq.ackIrq(n); - PC = address; + intreq_.ackIrq(n); + pc_ = address; } break; } - return cycleCounter; + return cc; } -unsigned long Memory::stop(unsigned long cycleCounter) { - cycleCounter += 4; +unsigned long Memory::stop(unsigned long cc) { + cc += 4; - if (ioamhram[0x14D] & isCgb()) { - sound.generateSamples(cycleCounter, isDoubleSpeed()); + if (ioamhram_[0x14D] & isCgb()) { + psg_.generateSamples(cc, isDoubleSpeed()); + lcd_.speedChange(cc); + ioamhram_[0x14D] ^= 0x81; + intreq_.setEventTime(ioamhram_[0x140] & lcdc_en + ? lcd_.nextMode1IrqTime() + : cc + (70224 << isDoubleSpeed())); - display.speedChange(cycleCounter); - ioamhram[0x14D] ^= 0x81; - - intreq.setEventTime((ioamhram[0x140] & 0x80) ? display.nextMode1IrqTime() : cycleCounter + (70224 << isDoubleSpeed())); - - if (intreq.eventTime(intevent_end) > cycleCounter) { - intreq.setEventTime(cycleCounter + (isDoubleSpeed() ? - (intreq.eventTime(intevent_end) - cycleCounter) << 1 : (intreq.eventTime(intevent_end) - cycleCounter) >> 1)); + if (intreq_.eventTime(intevent_end) > cc) { + intreq_.setEventTime(cc + + ( isDoubleSpeed() + ? (intreq_.eventTime(intevent_end) - cc) << 1 + : (intreq_.eventTime(intevent_end) - cc) >> 1)); } - // when switching speed, it seems that the CPU spontaneously restarts soon? - // otherwise, the cpu should be allowed to stay halted as long as needed - // so only execute this line when switching speed - intreq.halt(); - intreq.setEventTime(cycleCounter + 0x20000); + intreq_.halt(); + intreq_.setEventTime(cc + 0x20000); } else { - stopped = true; - intreq.halt(); + stopped_ = true; + intreq_.halt(); } - return cycleCounter; + return cc; } -static void decCycles(unsigned long &counter, const unsigned long dec) { +static void decCycles(unsigned long &counter, unsigned long dec) { if (counter != disabled_time) counter -= dec; } -void Memory::decEventCycles(const IntEventId eventId, const unsigned long dec) { - if (intreq.eventTime(eventId) != disabled_time) - intreq.setEventTime(eventId, intreq.eventTime(eventId) - dec); +void Memory::decEventCycles(IntEventId eventId, unsigned long dec) { + if (intreq_.eventTime(eventId) != disabled_time) + intreq_.setEventTime(eventId, intreq_.eventTime(eventId) - dec); } -unsigned long Memory::resetCounters(unsigned long cycleCounter) { - if (lastOamDmaUpdate != disabled_time) - updateOamDma(cycleCounter); +unsigned long Memory::resetCounters(unsigned long cc) { + if (lastOamDmaUpdate_ != disabled_time) + updateOamDma(cc); - updateIrqs(cycleCounter); - - const unsigned long oldCC = cycleCounter; + updateIrqs(cc); { - const unsigned long divinc = (cycleCounter - divLastUpdate) >> 8; - ioamhram[0x104] = (ioamhram[0x104] + divinc) & 0xFF; - divLastUpdate += divinc << 8; + unsigned long divinc = (cc - divLastUpdate_) >> 8; + ioamhram_[0x104] = (ioamhram_[0x104] + divinc) & 0xFF; + divLastUpdate_ += divinc << 8; } - const unsigned long dec = cycleCounter < 0x10000 ? 0 : (cycleCounter & ~0x7FFFul) - 0x8000; - - decCycles(divLastUpdate, dec); - decCycles(lastOamDmaUpdate, dec); + unsigned long const dec = cc < 0x10000 + ? 0 + : (cc & ~0x7FFFul) - 0x8000; + decCycles(divLastUpdate_, dec); + decCycles(lastOamDmaUpdate_, dec); decEventCycles(intevent_serial, dec); decEventCycles(intevent_oam, dec); decEventCycles(intevent_blit, dec); decEventCycles(intevent_end, dec); decEventCycles(intevent_unhalt, dec); - cycleCounter -= dec; - - intreq.resetCc(oldCC, cycleCounter); - tima.resetCc(oldCC, cycleCounter, TimaInterruptRequester(intreq)); - display.resetCc(oldCC, cycleCounter); - sound.resetCounter(cycleCounter, oldCC, isDoubleSpeed()); - - return cycleCounter; + unsigned long const oldCC = cc; + cc -= dec; + intreq_.resetCc(oldCC, cc); + tima_.resetCc(oldCC, cc, TimaInterruptRequester(intreq_)); + lcd_.resetCc(oldCC, cc); + psg_.resetCounter(cc, oldCC, isDoubleSpeed()); + return cc; } void Memory::updateInput() { unsigned state = 0xF; - if ((ioamhram[0x100] & 0x30) != 0x30 && getInput) { - unsigned input = (*getInput)(); + if ((ioamhram_[0x100] & 0x30) != 0x30 && getInput_) { + unsigned input = (*getInput_)(); unsigned dpad_state = ~input >> 4; unsigned button_state = ~input; - if (!(ioamhram[0x100] & 0x10)) + if (!(ioamhram_[0x100] & 0x10)) state &= dpad_state; - if (!(ioamhram[0x100] & 0x20)) + if (!(ioamhram_[0x100] & 0x20)) state &= button_state; + + if (state != 0xF && (ioamhram_[0x100] & 0xF) == 0xF) + intreq_.flagIrq(0x10); } - if (state != 0xF && (ioamhram[0x100] & 0xF) == 0xF) - intreq.flagIrq(0x10); - - ioamhram[0x100] = (ioamhram[0x100] & -0x10u) | state; + ioamhram_[0x100] = (ioamhram_[0x100] & -0x10u) | state; } -void Memory::updateOamDma(const unsigned long cycleCounter) { - const unsigned char *const oamDmaSrc = oamDmaSrcPtr(); - unsigned cycles = (cycleCounter - lastOamDmaUpdate) >> 2; +void Memory::updateOamDma(unsigned long const cc) { + unsigned char const *const oamDmaSrc = oamDmaSrcPtr(); + unsigned cycles = (cc - lastOamDmaUpdate_) >> 2; while (cycles--) { - oamDmaPos = (oamDmaPos + 1) & 0xFF; - lastOamDmaUpdate += 4; + oamDmaPos_ = (oamDmaPos_ + 1) & 0xFF; + lastOamDmaUpdate_ += 4; - if (oamDmaPos < 0xA0) { - if (oamDmaPos == 0) - startOamDma(lastOamDmaUpdate - 1); + if (oamDmaPos_ < 0xA0) { + if (oamDmaPos_ == 0) + startOamDma(lastOamDmaUpdate_ - 1); - ioamhram[oamDmaPos] = oamDmaSrc ? oamDmaSrc[oamDmaPos] : cart.rtcRead(); - } else if (oamDmaPos == 0xA0) { - endOamDma(lastOamDmaUpdate - 1); - lastOamDmaUpdate = disabled_time; + ioamhram_[oamDmaPos_] = oamDmaSrc ? oamDmaSrc[oamDmaPos_] : cart_.rtcRead(); + } else if (oamDmaPos_ == 0xA0) { + endOamDma(lastOamDmaUpdate_ - 1); + lastOamDmaUpdate_ = disabled_time; break; } } } void Memory::oamDmaInitSetup() { - if (ioamhram[0x146] < 0xA0) { - cart.setOamDmaSrc(ioamhram[0x146] < 0x80 ? oam_dma_src_rom : oam_dma_src_vram); - } else if (ioamhram[0x146] < 0xFE - isCgb() * 0x1E) { - cart.setOamDmaSrc(ioamhram[0x146] < 0xC0 ? oam_dma_src_sram : oam_dma_src_wram); + if (ioamhram_[0x146] < 0xA0) { + cart_.setOamDmaSrc(ioamhram_[0x146] < 0x80 ? oam_dma_src_rom : oam_dma_src_vram); + } else if (ioamhram_[0x146] < 0xFE - isCgb() * 0x1E) { + cart_.setOamDmaSrc(ioamhram_[0x146] < 0xC0 ? oam_dma_src_sram : oam_dma_src_wram); } else - cart.setOamDmaSrc(oam_dma_src_invalid); + cart_.setOamDmaSrc(oam_dma_src_invalid); } -static const unsigned char * oamDmaSrcZero() { +static unsigned char const * oamDmaSrcZero() { static unsigned char zeroMem[0xA0]; return zeroMem; } -const unsigned char * Memory::oamDmaSrcPtr() const { - switch (cart.oamDmaSrc()) { - case oam_dma_src_rom: return cart.romdata(ioamhram[0x146] >> 6) + (ioamhram[0x146] << 8); - case oam_dma_src_sram: return cart.rsrambankptr() ? cart.rsrambankptr() + (ioamhram[0x146] << 8) : 0; - case oam_dma_src_vram: return cart.vrambankptr() + (ioamhram[0x146] << 8); - case oam_dma_src_wram: return cart.wramdata(ioamhram[0x146] >> 4 & 1) + (ioamhram[0x146] << 8 & 0xFFF); +unsigned char const * Memory::oamDmaSrcPtr() const { + switch (cart_.oamDmaSrc()) { + case oam_dma_src_rom: + return cart_.romdata(ioamhram_[0x146] >> 6) + (ioamhram_[0x146] << 8); + case oam_dma_src_sram: + return cart_.rsrambankptr() ? cart_.rsrambankptr() + (ioamhram_[0x146] << 8) : 0; + case oam_dma_src_vram: + return cart_.vrambankptr() + (ioamhram_[0x146] << 8); + case oam_dma_src_wram: + return cart_.wramdata(ioamhram_[0x146] >> 4 & 1) + (ioamhram_[0x146] << 8 & 0xFFF); case oam_dma_src_invalid: - case oam_dma_src_off: break; + case oam_dma_src_off: + break; } - return ioamhram[0x146] == 0xFF && !isCgb() ? oamDmaSrcZero() : cart.rdisabledRam(); + return ioamhram_[0x146] == 0xFF && !isCgb() ? oamDmaSrcZero() : cart_.rdisabledRam(); } -void Memory::startOamDma(const unsigned long cycleCounter) { - display.oamChange(cart.rdisabledRam(), cycleCounter); +void Memory::startOamDma(unsigned long cc) { + lcd_.oamChange(cart_.rdisabledRam(), cc); } -void Memory::endOamDma(const unsigned long cycleCounter) { - oamDmaPos = 0xFE; - cart.setOamDmaSrc(oam_dma_src_off); - display.oamChange(ioamhram, cycleCounter); +void Memory::endOamDma(unsigned long cc) { + oamDmaPos_ = 0xFE; + cart_.setOamDmaSrc(oam_dma_src_off); + lcd_.oamChange(ioamhram_, cc); } -unsigned Memory::nontrivial_ff_read(const unsigned P, const unsigned long cycleCounter) { - if (lastOamDmaUpdate != disabled_time) - updateOamDma(cycleCounter); +unsigned Memory::nontrivial_ff_read(unsigned const p, unsigned long const cc) { + if (lastOamDmaUpdate_ != disabled_time) + updateOamDma(cc); - switch (P) { + switch (p) { case 0x00: updateInput(); break; case 0x01: case 0x02: - updateSerial(cycleCounter); + updateSerial(cc); break; case 0x04: { - const unsigned long divcycles = (cycleCounter - divLastUpdate) >> 8; - ioamhram[0x104] = (ioamhram[0x104] + divcycles) & 0xFF; - divLastUpdate += divcycles << 8; + unsigned long divcycles = (cc - divLastUpdate_) >> 8; + ioamhram_[0x104] = (ioamhram_[0x104] + divcycles) & 0xFF; + divLastUpdate_ += divcycles << 8; } break; case 0x05: - ioamhram[0x105] = tima.tima(cycleCounter); + ioamhram_[0x105] = tima_.tima(cc); break; case 0x0F: - updateIrqs(cycleCounter); - ioamhram[0x10F] = intreq.ifreg(); + updateIrqs(cc); + ioamhram_[0x10F] = intreq_.ifreg(); break; case 0x26: - if (ioamhram[0x126] & 0x80) { - sound.generateSamples(cycleCounter, isDoubleSpeed()); - ioamhram[0x126] = 0xF0 | sound.getStatus(); + if (ioamhram_[0x126] & 0x80) { + psg_.generateSamples(cc, isDoubleSpeed()); + ioamhram_[0x126] = 0xF0 | psg_.getStatus(); } else - ioamhram[0x126] = 0x70; + ioamhram_[0x126] = 0x70; break; case 0x30: @@ -517,26 +534,27 @@ unsigned Memory::nontrivial_ff_read(const unsigned P, const unsigned long cycleC case 0x3D: case 0x3E: case 0x3F: - sound.generateSamples(cycleCounter, isDoubleSpeed()); - return sound.waveRamRead(P & 0xF); + psg_.generateSamples(cc, isDoubleSpeed()); + return psg_.waveRamRead(p & 0xF); case 0x41: - return ioamhram[0x141] | display.getStat(ioamhram[0x145], cycleCounter); + return ioamhram_[0x141] | lcd_.getStat(ioamhram_[0x145], cc); case 0x44: - return display.getLyReg(cycleCounter/*+4*/); + return lcd_.getLyReg(cc); case 0x69: - return display.cgbBgColorRead(ioamhram[0x168] & 0x3F, cycleCounter); + return lcd_.cgbBgColorRead(ioamhram_[0x168] & 0x3F, cc); case 0x6B: - return display.cgbSpColorRead(ioamhram[0x16A] & 0x3F, cycleCounter); - default: break; + return lcd_.cgbSpColorRead(ioamhram_[0x16A] & 0x3F, cc); + default: + break; } - return ioamhram[P + 0x100]; + return ioamhram_[p + 0x100]; } -static bool isInOamDmaConflictArea(const OamDmaSrc oamDmaSrc, const unsigned addr, const bool cgb) { +static bool isInOamDmaConflictArea(OamDmaSrc const oamDmaSrc, unsigned const p, bool const cgb) { struct Area { unsigned short areaUpper, exceptAreaLower, exceptAreaWidth, pad; }; - static const Area cgbAreas[] = { + static Area const cgbAreas[] = { { 0xC000, 0x8000, 0x2000, 0 }, { 0xC000, 0x8000, 0x2000, 0 }, { 0xA000, 0x0000, 0x8000, 0 }, @@ -545,7 +563,7 @@ static bool isInOamDmaConflictArea(const OamDmaSrc oamDmaSrc, const unsigned add { 0x0000, 0x0000, 0x0000, 0 } }; - static const Area dmgAreas[] = { + static Area const dmgAreas[] = { { 0xFE00, 0x8000, 0x2000, 0 }, { 0xFE00, 0x8000, 0x2000, 0 }, { 0xA000, 0x0000, 0x8000, 0 }, @@ -554,257 +572,297 @@ static bool isInOamDmaConflictArea(const OamDmaSrc oamDmaSrc, const unsigned add { 0x0000, 0x0000, 0x0000, 0 } }; - const Area *const a = cgb ? cgbAreas : dmgAreas; - - return addr < a[oamDmaSrc].areaUpper && addr - a[oamDmaSrc].exceptAreaLower >= a[oamDmaSrc].exceptAreaWidth; + Area const *a = cgb ? cgbAreas : dmgAreas; + return p < a[oamDmaSrc].areaUpper + && p - a[oamDmaSrc].exceptAreaLower >= a[oamDmaSrc].exceptAreaWidth; } -unsigned Memory::nontrivial_read(const unsigned P, const unsigned long cycleCounter) { - if (P < 0xFF80) { - if (lastOamDmaUpdate != disabled_time) { - updateOamDma(cycleCounter); +unsigned Memory::nontrivial_read(unsigned const p, unsigned long const cc) { + if (p < 0xFF80) { + if (lastOamDmaUpdate_ != disabled_time) { + updateOamDma(cc); - if (isInOamDmaConflictArea(cart.oamDmaSrc(), P, isCgb()) && oamDmaPos < 0xA0) - return ioamhram[oamDmaPos]; + if (isInOamDmaConflictArea(cart_.oamDmaSrc(), p, isCgb()) && oamDmaPos_ < 0xA0) + return ioamhram_[oamDmaPos_]; } - if (P < 0xC000) { - if (P < 0x8000) - return cart.romdata(P >> 14)[P]; + if (p < 0xC000) { + if (p < 0x8000) + return cart_.romdata(p >> 14)[p]; - if (P < 0xA000) { - if (!display.vramAccessible(cycleCounter)) + if (p < 0xA000) { + if (!lcd_.vramAccessible(cc)) return 0xFF; - return cart.vrambankptr()[P]; + return cart_.vrambankptr()[p]; } - if (cart.rsrambankptr()) - return cart.rsrambankptr()[P]; + if (cart_.rsrambankptr()) + return cart_.rsrambankptr()[p]; - return cart.rtcRead(); + return cart_.rtcRead(); } - if (P < 0xFE00) - return cart.wramdata(P >> 12 & 1)[P & 0xFFF]; + if (p < 0xFE00) + return cart_.wramdata(p >> 12 & 1)[p & 0xFFF]; - long const ffp = long(P) - 0xFF00; + long const ffp = long(p) - 0xFF00; if (ffp >= 0) - return nontrivial_ff_read(ffp, cycleCounter); + return nontrivial_ff_read(ffp, cc); - if (!display.oamReadable(cycleCounter) || oamDmaPos < 0xA0) + if (!lcd_.oamReadable(cc) || oamDmaPos_ < 0xA0) return 0xFF; } - return ioamhram[P - 0xFE00]; + return ioamhram_[p - 0xFE00]; } -unsigned Memory::nontrivial_peek(const unsigned P) { - if (P < 0xC000) { - if (P < 0x8000) - return cart.romdata(P >> 14)[P]; +unsigned Memory::nontrivial_peek(unsigned const p) { + if (p < 0xC000) { + if (p < 0x8000) + return cart_.romdata(p >> 14)[p]; - if (P < 0xA000) { - return cart.vrambankptr()[P]; + if (p < 0xA000) { + return cart_.vrambankptr()[p]; } - if (cart.rsrambankptr()) - return cart.rsrambankptr()[P]; + if (cart_.rsrambankptr()) + return cart_.rsrambankptr()[p]; - return cart.rtcRead(); // verified side-effect free + return cart_.rtcRead(); // verified side-effect free } - if (P < 0xFE00) - return cart.wramdata(P >> 12 & 1)[P & 0xFFF]; - if (P >= 0xFF00 && P < 0xFF80) - return nontrivial_ff_peek(P); - return ioamhram[P - 0xFE00]; + if (p < 0xFE00) + return cart_.wramdata(p >> 12 & 1)[p & 0xFFF]; + if (p >= 0xFF00 && p < 0xFF80) + return nontrivial_ff_peek(p); + return ioamhram_[p - 0xFE00]; } -unsigned Memory::nontrivial_ff_peek(const unsigned P) { +unsigned Memory::nontrivial_ff_peek(unsigned const p) { // some regs may be somewhat wrong with this - return ioamhram[P - 0xFE00]; + return ioamhram_[p - 0xFE00]; } -void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned long cycleCounter) { - if (lastOamDmaUpdate != disabled_time) - updateOamDma(cycleCounter); +void Memory::nontrivial_ff_write(unsigned const p, unsigned data, unsigned long const cc) { + if (lastOamDmaUpdate_ != disabled_time) + updateOamDma(cc); - switch (P & 0xFF) { + switch (p & 0xFF) { case 0x00: - if ((data ^ ioamhram[0x100]) & 0x30) { - ioamhram[0x100] = (ioamhram[0x100] & ~0x30u) | (data & 0x30); + if ((data ^ ioamhram_[0x100]) & 0x30) { + ioamhram_[0x100] = (ioamhram_[0x100] & ~0x30u) | (data & 0x30); updateInput(); } + return; case 0x01: - updateSerial(cycleCounter); + updateSerial(cc); break; case 0x02: - updateSerial(cycleCounter); + updateSerial(cc); + serialCnt_ = 8; - serialCnt = 8; - intreq.setEventTime((data & 0x81) == 0x81 - ? (data & isCgb() * 2 ? (cycleCounter & ~0x7ul) + 0x10 * 8 : (cycleCounter & ~0xFFul) + 0x200 * 8) - : static_cast(disabled_time)); + if ((data & 0x81) == 0x81) { + intreq_.setEventTime(data & isCgb() * 2 + ? (cc & ~0x07ul) + 0x010 * 8 + : (cc & ~0xFFul) + 0x200 * 8); + } else + intreq_.setEventTime(disabled_time); data |= 0x7E - isCgb() * 2; break; case 0x04: - ioamhram[0x104] = 0; - divLastUpdate = cycleCounter; - tima.resTac(cycleCounter, TimaInterruptRequester(intreq)); + ioamhram_[0x104] = 0; + divLastUpdate_ = cc; + tima_.resTac(cc, TimaInterruptRequester(intreq_)); return; case 0x05: - tima.setTima(data, cycleCounter, TimaInterruptRequester(intreq)); + tima_.setTima(data, cc, TimaInterruptRequester(intreq_)); break; case 0x06: - tima.setTma(data, cycleCounter, TimaInterruptRequester(intreq)); + tima_.setTma(data, cc, TimaInterruptRequester(intreq_)); break; case 0x07: data |= 0xF8; - tima.setTac(data, cycleCounter, TimaInterruptRequester(intreq), gbIsCgb_); + tima_.setTac(data, cc, TimaInterruptRequester(intreq_), gbIsCgb_); break; case 0x0F: - updateIrqs(cycleCounter); - intreq.setIfreg(0xE0 | data); + updateIrqs(cc); + intreq_.setIfreg(0xE0 | data); return; case 0x10: - if (!sound.isEnabled()) return; - sound.generateSamples(cycleCounter, isDoubleSpeed()); - sound.setNr10(data); + if (!psg_.isEnabled()) + return; + + psg_.generateSamples(cc, isDoubleSpeed()); + psg_.setNr10(data); data |= 0x80; break; case 0x11: - if (!sound.isEnabled()) { + if (!psg_.isEnabled()) { if (isCgb()) return; data &= 0x3F; } - sound.generateSamples(cycleCounter, isDoubleSpeed()); - sound.setNr11(data); + psg_.generateSamples(cc, isDoubleSpeed()); + psg_.setNr11(data); data |= 0x3F; break; case 0x12: - if (!sound.isEnabled()) return; - sound.generateSamples(cycleCounter, isDoubleSpeed()); - sound.setNr12(data); + if (!psg_.isEnabled()) + return; + + psg_.generateSamples(cc, isDoubleSpeed()); + psg_.setNr12(data); break; case 0x13: - if (!sound.isEnabled()) return; - sound.generateSamples(cycleCounter, isDoubleSpeed()); - sound.setNr13(data); + if (!psg_.isEnabled()) + return; + + psg_.generateSamples(cc, isDoubleSpeed()); + psg_.setNr13(data); return; case 0x14: - if (!sound.isEnabled()) return; - sound.generateSamples(cycleCounter, isDoubleSpeed()); - sound.setNr14(data); + if (!psg_.isEnabled()) + return; + + psg_.generateSamples(cc, isDoubleSpeed()); + psg_.setNr14(data); data |= 0xBF; break; case 0x16: - if (!sound.isEnabled()) { + if (!psg_.isEnabled()) { if (isCgb()) return; data &= 0x3F; } - sound.generateSamples(cycleCounter, isDoubleSpeed()); - sound.setNr21(data); + psg_.generateSamples(cc, isDoubleSpeed()); + psg_.setNr21(data); data |= 0x3F; break; case 0x17: - if (!sound.isEnabled()) return; - sound.generateSamples(cycleCounter, isDoubleSpeed()); - sound.setNr22(data); + if (!psg_.isEnabled()) + return; + + psg_.generateSamples(cc, isDoubleSpeed()); + psg_.setNr22(data); break; case 0x18: - if (!sound.isEnabled()) return; - sound.generateSamples(cycleCounter, isDoubleSpeed()); - sound.setNr23(data); + if (!psg_.isEnabled()) + return; + + psg_.generateSamples(cc, isDoubleSpeed()); + psg_.setNr23(data); return; case 0x19: - if (!sound.isEnabled()) return; - sound.generateSamples(cycleCounter, isDoubleSpeed()); - sound.setNr24(data); + if (!psg_.isEnabled()) + return; + + psg_.generateSamples(cc, isDoubleSpeed()); + psg_.setNr24(data); data |= 0xBF; break; case 0x1A: - if (!sound.isEnabled()) return; - sound.generateSamples(cycleCounter, isDoubleSpeed()); - sound.setNr30(data); + if (!psg_.isEnabled()) + return; + + psg_.generateSamples(cc, isDoubleSpeed()); + psg_.setNr30(data); data |= 0x7F; break; case 0x1B: - if (!sound.isEnabled() && isCgb()) return; - sound.generateSamples(cycleCounter, isDoubleSpeed()); - sound.setNr31(data); + if (!psg_.isEnabled() && isCgb()) + return; + + psg_.generateSamples(cc, isDoubleSpeed()); + psg_.setNr31(data); return; case 0x1C: - if (!sound.isEnabled()) return; - sound.generateSamples(cycleCounter, isDoubleSpeed()); - sound.setNr32(data); + if (!psg_.isEnabled()) + return; + + psg_.generateSamples(cc, isDoubleSpeed()); + psg_.setNr32(data); data |= 0x9F; break; case 0x1D: - if (!sound.isEnabled()) return; - sound.generateSamples(cycleCounter, isDoubleSpeed()); - sound.setNr33(data); + if (!psg_.isEnabled()) + return; + + psg_.generateSamples(cc, isDoubleSpeed()); + psg_.setNr33(data); return; case 0x1E: - if (!sound.isEnabled()) return; - sound.generateSamples(cycleCounter, isDoubleSpeed()); - sound.setNr34(data); + if (!psg_.isEnabled()) + return; + + psg_.generateSamples(cc, isDoubleSpeed()); + psg_.setNr34(data); data |= 0xBF; break; case 0x20: - if (!sound.isEnabled() && isCgb()) return; - sound.generateSamples(cycleCounter, isDoubleSpeed()); - sound.setNr41(data); + if (!psg_.isEnabled() && isCgb()) + return; + + psg_.generateSamples(cc, isDoubleSpeed()); + psg_.setNr41(data); return; case 0x21: - if (!sound.isEnabled()) return; - sound.generateSamples(cycleCounter, isDoubleSpeed()); - sound.setNr42(data); + if (!psg_.isEnabled()) + return; + + psg_.generateSamples(cc, isDoubleSpeed()); + psg_.setNr42(data); break; case 0x22: - if (!sound.isEnabled()) return; - sound.generateSamples(cycleCounter, isDoubleSpeed()); - sound.setNr43(data); + if (!psg_.isEnabled()) + return; + + psg_.generateSamples(cc, isDoubleSpeed()); + psg_.setNr43(data); break; case 0x23: - if (!sound.isEnabled()) return; - sound.generateSamples(cycleCounter, isDoubleSpeed()); - sound.setNr44(data); + if (!psg_.isEnabled()) + return; + + psg_.generateSamples(cc, isDoubleSpeed()); + psg_.setNr44(data); data |= 0xBF; break; case 0x24: - if (!sound.isEnabled()) return; - sound.generateSamples(cycleCounter, isDoubleSpeed()); - sound.setSoVolume(data); + if (!psg_.isEnabled()) + return; + + psg_.generateSamples(cc, isDoubleSpeed()); + psg_.setSoVolume(data); break; case 0x25: - if (!sound.isEnabled()) return; - sound.generateSamples(cycleCounter, isDoubleSpeed()); - sound.mapSo(data); + if (!psg_.isEnabled()) + return; + + psg_.generateSamples(cc, isDoubleSpeed()); + psg_.mapSo(data); break; case 0x26: - if ((ioamhram[0x126] ^ data) & 0x80) { - sound.generateSamples(cycleCounter, isDoubleSpeed()); + if ((ioamhram_[0x126] ^ data) & 0x80) { + psg_.generateSamples(cc, isDoubleSpeed()); if (!(data & 0x80)) { for (unsigned i = 0x10; i < 0x26; ++i) - ff_write(i, 0, cycleCounter); + ff_write(i, 0, cc); - sound.setEnabled(false); + psg_.setEnabled(false); } else { - sound.reset(); - sound.setEnabled(true); + psg_.reset(); + psg_.setEnabled(true); } } - data = (data & 0x80) | (ioamhram[0x126] & 0x7F); + data = (data & 0x80) | (ioamhram_[0x126] & 0x7F); break; case 0x30: case 0x31: @@ -822,178 +880,178 @@ void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned case 0x3D: case 0x3E: case 0x3F: - sound.generateSamples(cycleCounter, isDoubleSpeed()); - sound.waveRamWrite(P & 0xF, data); + psg_.generateSamples(cc, isDoubleSpeed()); + psg_.waveRamWrite(p & 0xF, data); break; case 0x40: - if (ioamhram[0x140] != data) { - if ((ioamhram[0x140] ^ data) & 0x80) { - const unsigned lyc = display.getStat(ioamhram[0x145], cycleCounter) & 4; - const bool hdmaEnabled = display.hdmaIsEnabled(); + if (ioamhram_[0x140] != data) { + if ((ioamhram_[0x140] ^ data) & lcdc_en) { + unsigned const lyc = lcd_.getStat(ioamhram_[0x145], cc) + & lcdstat_lycflag; + bool const hdmaEnabled = lcd_.hdmaIsEnabled(); - display.lcdcChange(data, cycleCounter); - ioamhram[0x144] = 0; - ioamhram[0x141] &= 0xF8; + lcd_.lcdcChange(data, cc); + ioamhram_[0x144] = 0; + ioamhram_[0x141] &= 0xF8; - if (data & 0x80) { - intreq.setEventTime(display.nextMode1IrqTime() + (blanklcd ? 0 : 70224 << isDoubleSpeed())); + if (data & lcdc_en) { + intreq_.setEventTime(blanklcd_ + ? lcd_.nextMode1IrqTime() + : lcd_.nextMode1IrqTime() + + (70224 << isDoubleSpeed())); } else { - ioamhram[0x141] |= lyc; - intreq.setEventTime(cycleCounter + (456 * 4 << isDoubleSpeed())); + ioamhram_[0x141] |= lyc; + intreq_.setEventTime( + cc + (456 * 4 << isDoubleSpeed())); if (hdmaEnabled) - flagHdmaReq(intreq); + flagHdmaReq(intreq_); } } else - display.lcdcChange(data, cycleCounter); + lcd_.lcdcChange(data, cc); - ioamhram[0x140] = data; + ioamhram_[0x140] = data; } return; case 0x41: - display.lcdstatChange(data, cycleCounter); - data = (ioamhram[0x141] & 0x87) | (data & 0x78); + lcd_.lcdstatChange(data, cc); + data = (ioamhram_[0x141] & 0x87) | (data & 0x78); break; case 0x42: - display.scyChange(data, cycleCounter); + lcd_.scyChange(data, cc); break; case 0x43: - display.scxChange(data, cycleCounter); + lcd_.scxChange(data, cc); break; case 0x45: - display.lycRegChange(data, cycleCounter); + lcd_.lycRegChange(data, cc); break; case 0x46: - if (lastOamDmaUpdate != disabled_time) - endOamDma(cycleCounter); + if (lastOamDmaUpdate_ != disabled_time) + endOamDma(cc); - lastOamDmaUpdate = cycleCounter; - intreq.setEventTime(cycleCounter + 8); - ioamhram[0x146] = data; + lastOamDmaUpdate_ = cc; + intreq_.setEventTime(cc + 8); + ioamhram_[0x146] = data; oamDmaInitSetup(); return; case 0x47: if (!isCgb()) - display.dmgBgPaletteChange(data, cycleCounter); + lcd_.dmgBgPaletteChange(data, cc); break; case 0x48: if (!isCgb()) - display.dmgSpPalette1Change(data, cycleCounter); + lcd_.dmgSpPalette1Change(data, cc); break; case 0x49: if (!isCgb()) - display.dmgSpPalette2Change(data, cycleCounter); + lcd_.dmgSpPalette2Change(data, cc); break; case 0x4A: - display.wyChange(data, cycleCounter); + lcd_.wyChange(data, cc); break; case 0x4B: - display.wxChange(data, cycleCounter); - break; - case 0x4C: - if (biosMode) { - //flagClockReq(&intreq); - } + lcd_.wxChange(data, cc); break; case 0x4D: if (isCgb()) - ioamhram[0x14D] = (ioamhram[0x14D] & ~1u) | (data & 1); return; + ioamhram_[0x14D] = (ioamhram_[0x14D] & ~1u) | (data & 1); + + return; case 0x4F: if (isCgb()) { - cart.setVrambank(data & 1); - ioamhram[0x14F] = 0xFE | data; + cart_.setVrambank(data & 1); + ioamhram_[0x14F] = 0xFE | data; } return; case 0x50: - biosMode = false; - if (cgbSwitching) { - display.copyCgbPalettesToDmg(); - display.setCgb(false); - cgbSwitching = false; + biosMode_ = false; + if(cgbSwitching_) { + lcd_.copyCgbPalettesToDmg(); + lcd_.setCgb(false); + cgbSwitching_ = false; } return; case 0x51: - dmaSource = data << 8 | (dmaSource & 0xFF); + dmaSource_ = data << 8 | (dmaSource_ & 0xFF); return; case 0x52: - dmaSource = (dmaSource & 0xFF00) | (data & 0xF0); + dmaSource_ = (dmaSource_ & 0xFF00) | (data & 0xF0); return; case 0x53: - dmaDestination = data << 8 | (dmaDestination & 0xFF); + dmaDestination_ = data << 8 | (dmaDestination_ & 0xFF); return; case 0x54: - dmaDestination = (dmaDestination & 0xFF00) | (data & 0xF0); + dmaDestination_ = (dmaDestination_ & 0xFF00) | (data & 0xF0); return; case 0x55: if (isCgb()) { - ioamhram[0x155] = data & 0x7F; + ioamhram_[0x155] = data & 0x7F; - if (display.hdmaIsEnabled()) { + if (lcd_.hdmaIsEnabled()) { if (!(data & 0x80)) { - ioamhram[0x155] |= 0x80; - display.disableHdma(cycleCounter); + ioamhram_[0x155] |= 0x80; + lcd_.disableHdma(cc); } } else { if (data & 0x80) { - if (ioamhram[0x140] & 0x80) { - display.enableHdma(cycleCounter); + if (ioamhram_[0x140] & lcdc_en) { + lcd_.enableHdma(cc); } else - flagHdmaReq(intreq); + flagHdmaReq(intreq_); } else - flagGdmaReq(intreq); + flagGdmaReq(intreq_); } } return; case 0x56: if (isCgb()) - ioamhram[0x156] = data | 0x3E; + ioamhram_[0x156] = data | 0x3E; return; case 0x68: if (isCgb()) - ioamhram[0x168] = data | 0x40; + ioamhram_[0x168] = data | 0x40; return; case 0x69: if (isCgb()) { - const unsigned index = ioamhram[0x168] & 0x3F; - - display.cgbBgColorChange(index, data, cycleCounter); - - ioamhram[0x168] = (ioamhram[0x168] & ~0x3F) | ((index + (ioamhram[0x168] >> 7)) & 0x3F); + unsigned index = ioamhram_[0x168] & 0x3F; + lcd_.cgbBgColorChange(index, data, cc); + ioamhram_[0x168] = (ioamhram_[0x168] & ~0x3F) + | ((index + (ioamhram_[0x168] >> 7)) & 0x3F); } return; case 0x6A: if (isCgb()) - ioamhram[0x16A] = data | 0x40; + ioamhram_[0x16A] = data | 0x40; return; case 0x6B: if (isCgb()) { - const unsigned index = ioamhram[0x16A] & 0x3F; - - display.cgbSpColorChange(index, data, cycleCounter); - - ioamhram[0x16A] = (ioamhram[0x16A] & ~0x3F) | ((index + (ioamhram[0x16A] >> 7)) & 0x3F); + unsigned index = ioamhram_[0x16A] & 0x3F; + lcd_.cgbSpColorChange(index, data, cc); + ioamhram_[0x16A] = (ioamhram_[0x16A] & ~0x3F) + | ((index + (ioamhram_[0x16A] >> 7)) & 0x3F); } return; case 0x6C: - ioamhram[0x16C] = data | 0xFE; - cgbSwitching = true; + ioamhram_[0x16C] = data | 0xFE; + cgbSwitching_ = true; return; case 0x70: if (isCgb()) { - cart.setWrambank((data & 0x07) ? (data & 0x07) : 1); - ioamhram[0x170] = data | 0xF8; + cart_.setWrambank(data & 0x07 ? data & 0x07 : 1); + ioamhram_[0x170] = data | 0xF8; } return; @@ -1006,74 +1064,74 @@ void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned return; case 0x75: if (isCgb()) - ioamhram[0x175] = data | 0x8F; + ioamhram_[0x175] = data | 0x8F; return; case 0xFF: - intreq.setIereg(data); + intreq_.setIereg(data); break; default: return; } - ioamhram[P + 0x100] = data; + ioamhram_[p + 0x100] = data; } -void Memory::nontrivial_write(const unsigned P, const unsigned data, const unsigned long cycleCounter) { - if (lastOamDmaUpdate != disabled_time) { - updateOamDma(cycleCounter); +void Memory::nontrivial_write(unsigned const p, unsigned const data, unsigned long const cc) { + if (lastOamDmaUpdate_ != disabled_time) { + updateOamDma(cc); - if (isInOamDmaConflictArea(cart.oamDmaSrc(), P, isCgb()) && oamDmaPos < 0xA0) { - ioamhram[oamDmaPos] = data; + if (isInOamDmaConflictArea(cart_.oamDmaSrc(), p, isCgb()) && oamDmaPos_ < 0xA0) { + ioamhram_[oamDmaPos_] = data; return; } } - if (P < 0xFE00) { - if (P < 0xA000) { - if (P < 0x8000) { - cart.mbcWrite(P, data); - } else if (display.vramAccessible(cycleCounter)) { - display.vramChange(cycleCounter); - cart.vrambankptr()[P] = data; + if (p < 0xFE00) { + if (p < 0xA000) { + if (p < 0x8000) { + cart_.mbcWrite(p, data); + } else if (lcd_.vramAccessible(cc)) { + lcd_.vramChange(cc); + cart_.vrambankptr()[p] = data; } - } else if (P < 0xC000) { - if (cart.wsrambankptr()) - cart.wsrambankptr()[P] = data; + } else if (p < 0xC000) { + if (cart_.wsrambankptr()) + cart_.wsrambankptr()[p] = data; else - cart.rtcWrite(data); + cart_.rtcWrite(data); } else - cart.wramdata(P >> 12 & 1)[P & 0xFFF] = data; - } else if (P - 0xFF80u >= 0x7Fu) { - long const ffp = long(P) - 0xFF00; + cart_.wramdata(p >> 12 & 1)[p & 0xFFF] = data; + } else if (p - 0xFF80u >= 0x7Fu) { + long const ffp = long(p) - 0xFF00; if (ffp < 0) { - if (display.oamWritable(cycleCounter) && oamDmaPos >= 0xA0 && (P < 0xFEA0 || isCgb())) { - display.oamChange(cycleCounter); - ioamhram[P - 0xFE00] = data; + if (lcd_.oamWritable(cc) && oamDmaPos_ >= 0xA0 && (p < 0xFEA0 || isCgb())) { + lcd_.oamChange(cc); + ioamhram_[p - 0xFE00] = data; } } else - nontrivial_ff_write(ffp, data, cycleCounter); + nontrivial_ff_write(ffp, data, cc); } else - ioamhram[P - 0xFE00] = data; + ioamhram_[p - 0xFE00] = data; } LoadRes Memory::loadROM(char const *romfiledata, unsigned romfilelength, const bool forceDmg, const bool multicartCompat) { - if (LoadRes const fail = cart.loadROM(romfiledata, romfilelength, forceDmg, multicartCompat)) + if (LoadRes const fail = cart_.loadROM(romfiledata, romfilelength, forceDmg, multicartCompat)) return fail; - sound.init(cart.isCgb()); - display.reset(ioamhram, cart.vramdata(), cart.isCgb()); + psg_.init(cart_.isCgb()); + lcd_.reset(ioamhram_, cart_.vramdata(), cart_.isCgb()); return LOADRES_OK; } -std::size_t Memory::fillSoundBuffer(const unsigned long cycleCounter) { - sound.generateSamples(cycleCounter, isDoubleSpeed()); - return sound.fillBuffer(); +std::size_t Memory::fillSoundBuffer(unsigned long cc) { + psg_.generateSamples(cc, isDoubleSpeed()); + return psg_.fillBuffer(); } void Memory::setCgbPalette(unsigned *lut) { - display.setCgbPalette(lut); + lcd_.setCgbPalette(lut); } bool Memory::getMemoryArea(int which, unsigned char **data, int *length) { @@ -1083,23 +1141,23 @@ bool Memory::getMemoryArea(int which, unsigned char **data, int *length) { switch (which) { case 4: // oam - *data = &ioamhram[0]; + *data = &ioamhram_[0]; *length = 160; return true; case 5: // hram - *data = &ioamhram[384]; + *data = &ioamhram_[384]; *length = 128; return true; case 6: // bgpal - *data = (unsigned char *)display.bgPalette(); + *data = (unsigned char *)lcd_.bgPalette(); *length = 32; return true; case 7: // sppal - *data = (unsigned char *)display.spPalette(); + *data = (unsigned char *)lcd_.spPalette(); *length = 32; return true; default: // pass to cartridge - return cart.getMemoryArea(which, data, length); + return cart_.getMemoryArea(which, data, length); } } @@ -1108,21 +1166,21 @@ int Memory::linkStatus(int which) switch (which) { case 256: // ClockSignaled - return linkClockTrigger; + return linkClockTrigger_; case 257: // AckClockSignal - linkClockTrigger = false; + linkClockTrigger_ = false; return 0; case 258: // GetOut - return ioamhram[0x101] & 0xff; + return ioamhram_[0x101] & 0xff; case 259: // connect link cable - LINKCABLE = true; + LINKCABLE_ = true; return 0; default: // ShiftIn - if (ioamhram[0x102] & 0x80) // was enabled + if (ioamhram_[0x102] & 0x80) // was enabled { - ioamhram[0x101] = which; - ioamhram[0x102] &= 0x7F; - intreq.flagIrq(8); + ioamhram_[0x101] = which; + ioamhram_[0x102] &= 0x7F; + intreq_.flagIrq(8); } return 0; } @@ -1132,30 +1190,30 @@ int Memory::linkStatus(int which) SYNCFUNC(Memory) { - SSS(cart); - NSS(ioamhram); - NSS(divLastUpdate); - NSS(lastOamDmaUpdate); - NSS(biosMode); - NSS(cgbSwitching); - NSS(agbMode); + SSS(cart_); + NSS(ioamhram_); + NSS(divLastUpdate_); + NSS(lastOamDmaUpdate_); + NSS(biosMode_); + NSS(cgbSwitching_); + NSS(agbMode_); NSS(gbIsCgb_); - NSS(stopped); - NSS(halttime); + NSS(stopped_); + NSS(halttime_); - SSS(intreq); - SSS(tima); - SSS(display); - SSS(sound); + SSS(intreq_); + SSS(tima_); + SSS(lcd_); + SSS(psg_); - NSS(dmaSource); - NSS(dmaDestination); - NSS(oamDmaPos); - NSS(serialCnt); - NSS(blanklcd); + NSS(dmaSource_); + NSS(dmaDestination_); + NSS(oamDmaPos_); + NSS(serialCnt_); + NSS(blanklcd_); - NSS(LINKCABLE); - NSS(linkClockTrigger); + NSS(LINKCABLE_); + NSS(linkClockTrigger_); } } diff --git a/libgambatte/src/memory.h b/libgambatte/src/memory.h index 7d4a250dc8..4999e6d34c 100644 --- a/libgambatte/src/memory.h +++ b/libgambatte/src/memory.h @@ -1,30 +1,30 @@ -/*************************************************************************** - * Copyright (C) 2007 by Sindre Aamås * - * aamas@stud.ntnu.no * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License version 2 as * - * published by the Free Software Foundation. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License version 2 for more details. * - * * - * You should have received a copy of the GNU General Public License * - * version 2 along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ +// +// Copyright (C) 2007 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + #ifndef MEMORY_H #define MEMORY_H static unsigned char const agbOverride[0xD] = { 0xFF, 0x00, 0xCD, 0x03, 0x35, 0xAA, 0x31, 0x90, 0x94, 0x00, 0x00, 0x00, 0x00 }; #include "mem/cartridge.h" -#include "video.h" #include "sound.h" #include "tima.h" +#include "video.h" #include "newstate.h" #include "gambatte.h" @@ -32,111 +32,59 @@ namespace gambatte { class FilterInfo; class Memory { - Cartridge cart; - unsigned char ioamhram[0x200]; - unsigned char cgbBios[0x900]; - unsigned char dmgBios[0x100]; - bool biosMode; - bool cgbSwitching; - bool agbMode; - bool gbIsCgb_; - bool stopped; - unsigned short &SP; - unsigned short &PC; - unsigned long basetime; - unsigned long halttime; - - MemoryCallback readCallback; - MemoryCallback writeCallback; - MemoryCallback execCallback; - CDCallback cdCallback; - void(*linkCallback)(); - - unsigned (*getInput)(); - unsigned long divLastUpdate; - unsigned long lastOamDmaUpdate; - - InterruptRequester intreq; - Tima tima; - LCD display; - PSG sound; - - unsigned short dmaSource; - unsigned short dmaDestination; - unsigned char oamDmaPos; - unsigned char serialCnt; - bool blanklcd; - - bool LINKCABLE; - bool linkClockTrigger; - - void decEventCycles(IntEventId eventId, unsigned long dec); - - void oamDmaInitSetup(); - void updateOamDma(unsigned long cycleCounter); - void startOamDma(unsigned long cycleCounter); - void endOamDma(unsigned long cycleCounter); - const unsigned char * oamDmaSrcPtr() const; - - unsigned nontrivial_ff_read(unsigned P, unsigned long cycleCounter); - unsigned nontrivial_read(unsigned P, unsigned long cycleCounter); - void nontrivial_ff_write(unsigned P, unsigned data, unsigned long cycleCounter); - void nontrivial_write(unsigned P, unsigned data, unsigned long cycleCounter); - - unsigned nontrivial_peek(unsigned P); - unsigned nontrivial_ff_peek(unsigned P); - - void updateSerial(unsigned long cc); - void updateTimaIrq(unsigned long cc); - void updateIrqs(unsigned long cc); - - bool isDoubleSpeed() const { return display.isDoubleSpeed(); } - public: explicit Memory(unsigned short &sp, unsigned short &pc); - bool loaded() const { return cart.loaded(); } - unsigned curRomBank() const { return cart.curRomBank(); } - char const * romTitle() const { return cart.romTitle(); } - - int debugGetLY() const { return display.debugGetLY(); } - + bool loaded() const { return cart_.loaded(); } + unsigned char curRomBank() const { return cart_.curRomBank(); } + char const * romTitle() const { return cart_.romTitle(); } + int debugGetLY() const { return lcd_.debugGetLY(); } void setStatePtrs(SaveState &state); - void loadState(const SaveState &state/*, unsigned long oldCc*/); - void loadSavedata(char const *data) { cart.loadSavedata(data); } - int saveSavedataLength() {return cart.saveSavedataLength(); } - void saveSavedata(char *dest) { cart.saveSavedata(dest); } + void loadState(SaveState const &state); + void loadSavedata(char const *data) { cart_.loadSavedata(data); } + int saveSavedataLength() {return cart_.saveSavedataLength(); } + void saveSavedata(char *dest) { cart_.saveSavedata(dest); } void updateInput(); - unsigned char* cgbBiosBuffer() { return (unsigned char*)cgbBios; } - unsigned char* dmgBiosBuffer() { return (unsigned char*)dmgBios; } + unsigned char* cgbBiosBuffer() { return (unsigned char*)cgbBios_; } + unsigned char* dmgBiosBuffer() { return (unsigned char*)dmgBios_; } bool gbIsCgb() { return gbIsCgb_; } bool getMemoryArea(int which, unsigned char **data, int *length); // { return cart.getMemoryArea(which, data, length); } unsigned long stop(unsigned long cycleCounter); - bool isCgb() const { return display.isCgb(); } - bool ime() const { return intreq.ime(); } - bool halted() const { return intreq.halted(); } - unsigned long nextEventTime() const { return intreq.minEventTime(); } + bool isCgb() const { return lcd_.isCgb(); } + bool ime() const { return intreq_.ime(); } + bool halted() const { return intreq_.halted(); } + unsigned long nextEventTime() const { return intreq_.minEventTime(); } + void setLayers(unsigned mask) { lcd_.setLayers(mask); } + bool isActive() const { return intreq_.eventTime(intevent_end) != disabled_time; } - void setLayers(unsigned mask) { display.setLayers(mask); } + long cyclesSinceBlit(unsigned long cc) const { + if (cc < intreq_.eventTime(intevent_blit)) + return -1; - bool isActive() const { return intreq.eventTime(intevent_end) != disabled_time; } - - long cyclesSinceBlit(const unsigned long cc) const { - return cc < intreq.eventTime(intevent_blit) ? -1 : static_cast((cc - intreq.eventTime(intevent_blit)) >> isDoubleSpeed()); + return (cc - intreq_.eventTime(intevent_blit)) >> isDoubleSpeed(); } - void halt(unsigned long cycleCounter) { halttime = cycleCounter; intreq.halt(); } - void ei(unsigned long cycleCounter) { if (!ime()) { intreq.ei(cycleCounter); } } + void halt(unsigned long cycleCounter) { halttime_ = cycleCounter; intreq_.halt(); } + void ei(unsigned long cycleCounter) { if (!ime()) { intreq_.ei(cycleCounter); } } + void di() { intreq_.di(); } - void di() { intreq.di(); } + unsigned readBios(unsigned p) { + if (gbIsCgb_) { + if (agbMode_ && p >= 0xF3 && p < 0x100) { + return (agbOverride[p - 0xF3] + cgbBios_[p]) & 0xFF; + } + return cgbBios_[p]; + } + return dmgBios_[p]; + } - unsigned ff_read(const unsigned P, const unsigned long cycleCounter) { - if (readCallback) - readCallback(P, (cycleCounter - basetime) >> 1); - return P < 0x80 ? nontrivial_ff_read(P, cycleCounter) : ioamhram[P + 0x100]; + unsigned ff_read(unsigned p, unsigned long cc) { + if (readCallback_) + readCallback_(p, (cc - basetime_) >> 1); + return p < 0x80 ? nontrivial_ff_read(p, cc) : ioamhram_[p + 0x100]; } struct CDMapResult @@ -145,30 +93,30 @@ public: unsigned addr; }; - CDMapResult CDMap(const unsigned P) const + CDMapResult CDMap(const unsigned p) const { - if(P<0x4000) + if(p < 0x4000) { - CDMapResult ret = { eCDLog_AddrType_ROM, P }; + CDMapResult ret = { eCDLog_AddrType_ROM, p }; return ret; } - else if(P<0x8000) + else if(p < 0x8000) { - unsigned bank = cart.rmem(P>>12) - cart.rmem(0); - unsigned addr = P+bank; + unsigned bank = cart_.rmem(p >> 12) - cart_.rmem(0); + unsigned addr = p + bank; CDMapResult ret = { eCDLog_AddrType_ROM, addr }; return ret; } - else if(P<0xA000) {} - else if(P<0xC000) + else if(p < 0xA000) {} + else if(p < 0xC000) { - if(cart.wsrambankptr()) + if(cart_.wsrambankptr()) { //not bankable. but. we're not sure how much might be here unsigned char *data; int length; - bool has = cart.getMemoryArea(3,&data,&length); - unsigned addr = P&(length-1); + bool has = cart_.getMemoryArea(3,&data,&length); + unsigned addr = p & (length-1); if(has && length!=0) { CDMapResult ret = { eCDLog_AddrType_CartRAM, addr }; @@ -176,14 +124,14 @@ public: } } } - else if(P<0xE000) + else if(p < 0xE000) { - unsigned bank = cart.wramdata(P >> 12 & 1) - cart.wramdata(0); - unsigned addr = (P&0xFFF)+bank; + unsigned bank = cart_.wramdata(p >> 12 & 1) - cart_.wramdata(0); + unsigned addr = (p & 0xFFF) + bank; CDMapResult ret = { eCDLog_AddrType_WRAM, addr }; return ret; } - else if(P<0xFF80) {} + else if(p < 0xFF80) {} else { ////this is just for debugging, really, it's pretty useless @@ -195,157 +143,190 @@ public: return ret; } - - unsigned readBios(const unsigned P) { - if (gbIsCgb_) { - if (agbMode && P >= 0xF3 && P < 0x100) { - return (agbOverride[P - 0xF3] + cgbBios[P]) & 0xFF; - } - return cgbBios[P]; - } - return dmgBios[P]; - } - - unsigned read(const unsigned P, const unsigned long cycleCounter) { - if (readCallback) - readCallback(P, (cycleCounter - basetime) >> 1); - bool biosRange = ((!gbIsCgb_ && P < 0x100) || (gbIsCgb_ && P < 0x900 && (P < 0x100 || P >= 0x200))); - if(biosMode) { + unsigned read(unsigned p, unsigned long cc) { + if (readCallback_) + readCallback_(p, (cc - basetime_) >> 1); + bool biosRange = ((!gbIsCgb_ && p < 0x100) || (gbIsCgb_ && p < 0x900 && (p < 0x100 || p >= 0x200))); + if(biosMode_) { if (biosRange) - return readBios(P); + return readBios(p); } - else - { - if(cdCallback) - { - CDMapResult map = CDMap(P); - if(map.type != eCDLog_AddrType_None) - cdCallback(map.addr,map.type,eCDLog_Flags_Data); - } + else if(cdCallback_) { + CDMapResult map = CDMap(p); + if(map.type != eCDLog_AddrType_None) + cdCallback_(map.addr, map.type, eCDLog_Flags_Data); } - return cart.rmem(P >> 12) ? cart.rmem(P >> 12)[P] : nontrivial_read(P, cycleCounter); + return cart_.rmem(p >> 12) ? cart_.rmem(p >> 12)[p] : nontrivial_read(p, cc); } - unsigned read_excb(const unsigned P, const unsigned long cycleCounter, bool first) { - if (execCallback) - execCallback(P, (cycleCounter - basetime) >> 1); - bool biosRange = ((!gbIsCgb_ && P < 0x100) || (gbIsCgb_ && P < 0x900 && (P < 0x100 || P >= 0x200))); - if (biosMode) { + unsigned read_excb(unsigned p, unsigned long cc, bool first) { + if (execCallback_) + execCallback_(p, (cc - basetime_) >> 1); + bool biosRange = ((!gbIsCgb_ && p < 0x100) || (gbIsCgb_ && p < 0x900 && (p < 0x100 || p >= 0x200))); + if (biosMode_) { if(biosRange) - return readBios(P); + return readBios(p); } - else - { - if(cdCallback) - { - CDMapResult map = CDMap(P); - if(map.type != eCDLog_AddrType_None) - cdCallback(map.addr,map.type,first?eCDLog_Flags_ExecFirst : eCDLog_Flags_ExecOperand); - } - } - return cart.rmem(P >> 12) ? cart.rmem(P >> 12)[P] : nontrivial_read(P, cycleCounter); - } - - unsigned peek(const unsigned P) { - if (biosMode && ((!gbIsCgb_ && P < 0x100) || (gbIsCgb_ && P < 0x900 && (P < 0x100 || P >= 0x200)))) { - return readBios(P); - } - return cart.rmem(P >> 12) ? cart.rmem(P >> 12)[P] : nontrivial_peek(P); - } - - void write_nocb(const unsigned P, const unsigned data, const unsigned long cycleCounter) { - if (cart.wmem(P >> 12)) { - cart.wmem(P >> 12)[P] = data; - } else - nontrivial_write(P, data, cycleCounter); - } - - void write(const unsigned P, const unsigned data, const unsigned long cycleCounter) { - if (cart.wmem(P >> 12)) { - cart.wmem(P >> 12)[P] = data; - } else - nontrivial_write(P, data, cycleCounter); - if (writeCallback) - writeCallback(P, (cycleCounter - basetime) >> 1); - if(cdCallback && !biosMode) - { - CDMapResult map = CDMap(P); + else if(cdCallback_) { + CDMapResult map = CDMap(p); if(map.type != eCDLog_AddrType_None) - cdCallback(map.addr,map.type,eCDLog_Flags_Data); + cdCallback_(map.addr, map.type, first ? eCDLog_Flags_ExecFirst : eCDLog_Flags_ExecOperand); + } + return cart_.rmem(p >> 12) ? cart_.rmem(p >> 12)[p] : nontrivial_read(p, cc); + } + + unsigned peek(unsigned p) { + bool biosRange = ((!gbIsCgb_ && p < 0x100) || (gbIsCgb_ && p < 0x900 && (p < 0x100 || p >= 0x200))); + if (biosMode_ && biosRange) { + return readBios(p); + } + return cart_.rmem(p >> 12) ? cart_.rmem(p >> 12)[p] : nontrivial_peek(p); + } + + void write_nocb(unsigned p, unsigned data, unsigned long cc) { + if (cart_.wmem(p >> 12)) { + cart_.wmem(p >> 12)[p] = data; + } else + nontrivial_write(p, data, cc); + } + + void write(unsigned p, unsigned data, unsigned long cc) { + if (cart_.wmem(p >> 12)) { + cart_.wmem(p >> 12)[p] = data; + } else + nontrivial_write(p, data, cc); + if (writeCallback_) + writeCallback_(p, (cc - basetime_) >> 1); + if(cdCallback_ && !biosMode_) { + CDMapResult map = CDMap(p); + if(map.type != eCDLog_AddrType_None) + cdCallback_(map.addr, map.type, eCDLog_Flags_Data); } } - void ff_write(const unsigned P, const unsigned data, const unsigned long cycleCounter) { - if (P - 0x80u < 0x7Fu) { - ioamhram[P + 0x100] = data; + void ff_write(unsigned p, unsigned data, unsigned long cc) { + if (p - 0x80u < 0x7Fu) { + ioamhram_[p + 0x100] = data; } else - nontrivial_ff_write(P, data, cycleCounter); - if (writeCallback) - writeCallback(P, (cycleCounter - basetime) >> 1); - if(cdCallback && !biosMode) + nontrivial_ff_write(p, data, cc); + if (writeCallback_) + writeCallback_(0xff00 + p, (cc - basetime_) >> 1); + if(cdCallback_ && !biosMode_) { - CDMapResult map = CDMap(P); + CDMapResult map = CDMap(0xff00 + p); if(map.type != eCDLog_AddrType_None) - cdCallback(map.addr,map.type,eCDLog_Flags_Data); + cdCallback_(map.addr, map.type, eCDLog_Flags_Data); } } unsigned long event(unsigned long cycleCounter); unsigned long resetCounters(unsigned long cycleCounter); - LoadRes loadROM(char const *romfiledata, unsigned romfilelength, bool forceDmg, bool multicartCompat); void setInputGetter(unsigned (*getInput)()) { - this->getInput = getInput; + getInput_ = getInput; } void setReadCallback(MemoryCallback callback) { - this->readCallback = callback; + this->readCallback_ = callback; } void setWriteCallback(MemoryCallback callback) { - this->writeCallback = callback; + this->writeCallback_ = callback; } void setExecCallback(MemoryCallback callback) { - this->execCallback = callback; + this->execCallback_ = callback; } void setCDCallback(CDCallback cdc) { - this->cdCallback = cdc; + this->cdCallback_ = cdc; } void setScanlineCallback(void (*callback)(), int sl) { - display.setScanlineCallback(callback, sl); + lcd_.setScanlineCallback(callback, sl); } void setRTCCallback(std::uint32_t (*callback)()) { - cart.setRTCCallback(callback); + cart_.setRTCCallback(callback); } void setLinkCallback(void(*callback)()) { - this->linkCallback = callback; + this->linkCallback_ = callback; } - void setBasetime(unsigned long cc) { basetime = cc; } void setEndtime(unsigned long cc, unsigned long inc); + void setBasetime(unsigned long cc) { basetime_ = cc; } - void setSoundBuffer(uint_least32_t *const buf) { sound.setBuffer(buf); } + void setSoundBuffer(uint_least32_t *buf) { psg_.setBuffer(buf); } std::size_t fillSoundBuffer(unsigned long cc); - void setVideoBuffer(uint_least32_t *const videoBuf, const std::ptrdiff_t pitch) { - display.setVideoBuffer(videoBuf, pitch); + void setVideoBuffer(uint_least32_t *videoBuf, std::ptrdiff_t pitch) { + lcd_.setVideoBuffer(videoBuf, pitch); } void setDmgPaletteColor(int palNum, int colorNum, unsigned long rgb32) { - display.setDmgPaletteColor(palNum, colorNum, rgb32); + lcd_.setDmgPaletteColor(palNum, colorNum, rgb32); } void setCgbPalette(unsigned *lut); void blackScreen() { - display.blackScreen(); + lcd_.blackScreen(); } int linkStatus(int which); +private: + Cartridge cart_; + unsigned char ioamhram_[0x200]; + unsigned char cgbBios_[0x900]; + unsigned char dmgBios_[0x100]; + unsigned (*getInput_)(); + unsigned long divLastUpdate_; + unsigned long lastOamDmaUpdate_; + InterruptRequester intreq_; + Tima tima_; + LCD lcd_; + PSG psg_; + unsigned short dmaSource_; + unsigned short dmaDestination_; + unsigned char oamDmaPos_; + unsigned char serialCnt_; + bool blanklcd_; + bool biosMode_; + bool cgbSwitching_; + bool agbMode_; + bool gbIsCgb_; + unsigned short &sp_; + unsigned short &pc_; + unsigned long basetime_; + unsigned long halttime_; + bool stopped_; + + MemoryCallback readCallback_; + MemoryCallback writeCallback_; + MemoryCallback execCallback_; + CDCallback cdCallback_; + void(*linkCallback_)(); + bool LINKCABLE_; + bool linkClockTrigger_; + + void decEventCycles(IntEventId eventId, unsigned long dec); + void oamDmaInitSetup(); + void updateOamDma(unsigned long cycleCounter); + void startOamDma(unsigned long cycleCounter); + void endOamDma(unsigned long cycleCounter); + unsigned char const * oamDmaSrcPtr() const; + unsigned nontrivial_ff_read(unsigned p, unsigned long cycleCounter); + unsigned nontrivial_read(unsigned p, unsigned long cycleCounter); + void nontrivial_ff_write(unsigned p, unsigned data, unsigned long cycleCounter); + void nontrivial_write(unsigned p, unsigned data, unsigned long cycleCounter); + unsigned nontrivial_peek(unsigned p); + unsigned nontrivial_ff_peek(unsigned p); + void updateSerial(unsigned long cc); + void updateTimaIrq(unsigned long cc); + void updateIrqs(unsigned long cc); + bool isDoubleSpeed() const { return lcd_.isDoubleSpeed(); } + +public: templatevoid SyncState(NewState *ns); }; diff --git a/libgambatte/src/savestate.h b/libgambatte/src/savestate.h index b4489d12af..e56290685d 100644 --- a/libgambatte/src/savestate.h +++ b/libgambatte/src/savestate.h @@ -70,6 +70,7 @@ struct SaveState { unsigned long lastOamDmaUpdate; unsigned long minIntTime; unsigned long unhaltTime; + unsigned long halttime; unsigned short rombank; unsigned short dmaSource; unsigned short dmaDestination; @@ -84,6 +85,7 @@ struct SaveState { unsigned char /*bool*/ cgbSwitching; unsigned char /*bool*/ agbMode; unsigned char /*bool*/ gbIsCgb; + unsigned char /*bool*/ stopped; } mem; struct PPU { diff --git a/output/dll/libgambatte.dll b/output/dll/libgambatte.dll index e9b8c6dbbc6f4ba0eb3dc1880951964f17aa6df6..18ca122371aa4d7b04eb68c038c45c11b69fbab5 100644 GIT binary patch delta 61850 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 delta 61844 zcmc${3s_WD_dkBl05V*3P(cxIsHhm46lxMyj1D>^nx>__-m}Qm%n~n|b)Z3EI!$V) z9J^}yR#v7Ky`WM7UI6c+=A}&0(&mIIU0&}xzt7tHoM8qB>-+uvp8uccS)9H1T6^ua z*IIk+%ehSUzeBTEhprSm44LukSV8-%6*|U-8Jd`cT2T;I^TL%%2XR8kzS#!huRuZA zs@TP@Vz%;y*xpb(QrR!|>GQxj5J!NRDuKlNFIEsH-u~=kld>iWLQFPc41(|yE`KTa z8oG*eluZ18QF#^rmnen!|60LuLuWBK89ddDpOw(S&f-EPF>r)*>7@X{YR`)6>y8?T zhLZN1H?ShP(kj=wnx7Q~>%t4B+!f$$wU3QU%y2CFI>sPa9m~F@i+w>{s#V_O`f?%g zGBQb}RvBcQ1PE5z>?;;+)shv9_8}z- z%|Uq(sUzYLu&G5VT`sR!^p|p1(5T+;MG3+tF6&-b?;k~BlaaW{J6-X}kT8i>+t%1I zTXyMx>JQH-TY@@@A1G&nmKr)wSDtIs)o>}M;Dbg1Vv{wHK_gXvn64BAckD8yi6B_) zujI@GWA*M4qK*eYs~lo=^bfS=luc2t2Hzw7{GcdMGgeu&+Lu%})_L*#hI;wZdilzF z`F-{Bq&Y4d=Xa^40fJ=<(R#5`WNhE`h<7bhVfJ3-7vliKvwtexL*fldF6H5n$HY@g zaY#3Dm2x^{vSH*O%Kf4DcbU?M%$X4vYqeYO&k=RkAEMxnnncBNnAI^dF4pz$iOQzX z9%8NH3hfAuDdAz=#Fk2*ur4OSo9xdC%Cs;ddozrYeG@iXyh~{j-o+H#+lyPkBxP_o zkv$jQiMYKNK3Ggqehu$uYSPP#tlM~{cVi-(+?a98Yur(MUiq|fKhw}8FS17_DF0<- zotiLi)+STMsY*eUq_95I1fix$rs?UR?#k~?o;5vnfczpOZnQONm#g0aAV=3jR-)GGsB`wqXezSuo|^g^M|R!*b|YFct03nupBWl$qT! z=L$LdtjzwV!dDE-!v#U!Y$`0>tHFmq* zwoBo2+sc(0j>a=ilW?2CBs`?Q0gUbpAj!(MaJI+*OL<(Z@(yd6D}0C?UOHsq_N)*y zDm4X8`SQ7rQn-P)F06!5rgS+w7Se%$6(bvutaV{&mJwwUM2fO0j07blwr~R#TiL3` zoRe-0igY=*G=j4bi2(gExZQFk$t5gjh_Wqrmg@egJQ|VEgvPyD-k%1|s~EEq3B|kx0ERlHSH}?wKyC^MyP4y!cV?{7oZzT?$mDSe2i?gDBENyYzFUmO?D7M(`7F-tQSn)qA zotYnH5y;t>rn3Lt+5cGlw@$XV!6hyEm3(Tz)W6C)d9&HRBpQWg`IT%cG~4rirP-Ac z;K->}ERo||QJ6K$wet5?`7(`*re=9x!qWASvN*DvA%1zmnaFQ}S7%2X)R;LhDXdqR$zjqd5|#q45?eq~~d7A^P?&T!=EL)dI}G@hjV z-ZD~I*D-e=3zTaQt`|BDXrZqlJYaA+^ znpL2u9Kt>B)N7>#DI^)aDJuzas-Y5^k=iGG>Y!UM7k%sC%Zl! z9c%d9sVt3t@b+$-Xh_&-UZT)oN+@Rf*drf?KHU4Pwqo=NttW9EG;PH@cWhGHds^+bBkZCq=a+f> zGMZodn%Gs2Wfz+*%@<4`fMgVCcvJz0qvWL1_Wl>^C-R`*$7DrB&Nav z#{4`ABS?kh=TQ(rDl|WjLI_e}`FRvTkP6Swqws-L3Rvl>1 zqi8||w(1DvyV>(7%AlZ0ejddTq)hpF6hX3|k6_wNG`&%ppLbYEZ`0+zr!vvoq^&f^ zkG9YZa#UVQlDmGMgAtc>0miV{3x1}O&9WfaivNO1-129vce!JfwQagfe|+b5XJExQ z(J|a@E^%XeGqq0@F4$MBLKMgtE+(&2z(L+epO8vg!y+SgA#pY0YSs_oOY{{$g)<<`HCrOzoiOhpPckda(K3nx=<71W7FS#1A8lx0$+McxKg#3MQ* zFq;Z(WF-`IN0Q8hWFnGeCnS3zNrpnQFOmmCdAgr$+oibMwwB0JW>Z>uSS7Gz3lLh6 zBx4|HMe;BuGib5A6D1k0o|oP3OgB@6e92T;3EVIY{Eu3|@)>a>*@Dciko+~#<}XBu zOI^Ku$y3Wzc*(Xc(9*ZyaExRSvP_v4v6y;o$wvvaYbva8SKfoETrlNb=#SyG93gb! z*rzd}2kdsA)O3`Vv|VGpqK>u2krr5oN4xn<5v~PwB(`nFe5NS%t^R@p1uERR86kC@ z^jUVwXpwh^?IX!^&|k=_PKPK!lVaVaoNt-SC@@}x&tQ0&xXERvO|CGzjm+gRi*_5S zS!=bkH<{E}z^Y3pbo&rqHPq@il(A{7RQi4Qf41uP~}^iz|}Tpi3v{520M`ZQ7s-~m?A9!rI_O^`OF_X z({yG=x5CQo3ayT~-ugQN4FP3#8}*s#g!rgssl=Mu7H*<(g#@r{*gjXpf8B0U4g8Nx z_XZ>8rmggtHcpK97%Z5dr0(ZRM7D%Gj~7^L#36~<+xSMKu| zSFS8OX$-66=B-lmWpY)u6V~WFoV)^c$}S$xC~%dasvgV=h)sQOnkLn?c$2~NZ4}pC z);bb4bSRprnM^2!PXUZht+)o~K!gFBh6i2Q@E{t}aT2Q{Q?^cn(7)ICV64Nv>HMpZ z(fxd{pANnAm#+pHksa`T&*YWVRA6F!*w;a zlj~C7N%7w>3Yjoxj8%L-{)F2f!M zgSf2PSoP~ZHMC0N!!}=Go;=|y!baZ$3YJ+tZfasH`d!Zuayh2y6a5518#NmO1*{oe z>Mj)BR6m#M^LfkT>+nAeKZFGyVO*2yXqdCJP$Chx!eSPlF66++i8w@YN1&UA{$qwVYt z@(yeTN^RR4#hLnrmJ00u-vxUb}E&K%1#;V%R6%x?VA%3=`iN66UT0B`DBKusni*-hk=DtM-ML7FX~2$z zQiHt7{M(VyNk1n0Dun6djq|>SWTXY4yue0M1?wuU57qZjc#+MGH1O2Lm@IB`i$QhV zJR?IN7!v0F_&PjBMJ7gXW>`h0oh?h%dr7{3FgiNHgpNUObrXUtY)*>^QkT20SM~bJ zhwi0}-`rRiv4tFveXe%)@=kwF|;QsvNd97U;b5^S@zv<*X0K%K9AVdvla% zfu@cjmi0$dbGQYZk91GV8+{s(OEs6KrA8G5b=}4ps!`xl;jx9bC{x{c`B>=G6=os6 z3zNU26@t8(LJeytU^91bb5bdsnmIQ{6`{g5)6&S}3&!)UrglRQ{R>Zv}0 zkW)ZCArIEIwhN_D3N4YeRq!TWLerjBvYMCBT%?sO<0YgJtt6M1kOsAqXITkfjmq03 zuXwZuDl|;exS}v%Ey& zlCh%iS&=STeZjgh!HAFqTg$6+!C38gcnKGbEsS2~C4|jq%LR2FFX4hQ8Yz<(amiTG zNM6DPV!Ubd1 zzUC!dFjn#@D``lu%Pta(2uZLPd2KEjt34ehWPdIhHKhKF6LFQX5-Ts^g0Yf5yo6Y2 zV=s=EaKRW&Ggi`&V3U|&L}-&B*_kA!4lWt1evp@N!HBK8nU`?ESjl=`!Uba`@9`2Y z7%O>&mk>`LMb!DMq)sr&GaEAR-~J1!jOvoghVtrMFvhSSN+?*7WLiMf{8W7x69Az| z#CoWgG!)5N4<$XJ;YjLwxC{+N;%lwo+*Iw2VvQ2pQayCignkMK=lfhA!5>NbrFTMB zdD|v87L639XPl;G&$dnMmY%#56W`d=Zpq#05|F7ED%xjtbY1(&?anRDYOEhws6)Y{ zoE?#fYJa5V?_8HaL+q+?rCnG8iYdPj`Gjq9<;A16lK}yh0jk=R1c2bAzj|GJ7E=Z= zRkTCZR!u+O;TaTK$$nnKElf<*QeMI>%u3es5^iBu@(wQ{b7_2DW+lw#ni|#lSj%E= zV>@If2X@WgI(<+~-_2VCA zrb|7Ux!L;LZmT=)2q>+gb9NeF+NO%F4yaDuTGw_8n_ISK;#D1iP0gtICMuF$YB2w^ zO=`{8NRSF^3XmYh)O?Br`9{tA%q8J}{QQK<;Kuy?WCwhiw~m3NyRf_iOA>z52<=;V z%eY$SM*VLtVkOa7B;t$eC6x4fyE#5L#B zyHh>9dq;-(M@spORcpvSublL8Pv({hmH84MXq#N2e8h9x@ED-}3mmQ5W&=A!9N?0n zutcNPn%WOo-O4aJ8ILbmF`kGnY%{jUI4BK;$u)1WBxu_-OTuOpfsF)q?ATJ@JpR(X zj;ro@j4hfVLxX47<~okVA6&uDGayK~dQ4wykf~J2&SCynT<>X+YoS6;@=uu3`14hO zE1_WpkPrP6t~W7N8e%Fzt>j_nlods zp*bl|sEA(o=^4BN3!-~KLNS7(DO*KSG-aDDil!|1Q#5U;`%8|qWk^jPXi0jXhCFge zf_k-^N617*sqcZtuxQF=T3 z^&{(@CMvI~eugSqkjUV-By_z-*Y(lp(?@9@Z=W>oiQj$GkhqtE!d!i0CGh>Q?I+?{ThwFr{Jqt2erO*ZBL{Ox^8M zfz4w#O)TE?iu#UE1@8$=@;}e2(|l@>er`PnsdxKS`1?sq4Ph0eh8%445B}yWKJY2R z>k}1?UiI59f&h=Bblr5Nu0|=XU-*%;DR(*YIFz8H7lbQXv;X-$?5~|xX-8v(1q!=^ z!{2*w+L@R-E0p&&ruy!Ml)(rXFt52uH)tR{BKwtEA=Tb@F=XH&CT2#{SBs~T34G} z@Pu^NNiB4~1i377xu$j7wr~;|yEYs!(s7nmmUiNxk)(HpVX%3s(0LoT6l{TIK)LHI z8blBIoyGlexF_IYzuSkD?M+?ZkbF8teQqqIG7YNILa@@*Tc`b0ZdH;?O{E(|Iw#y8 z@D|JV4N3kW+R`2!po2o47?Nwm=s>BfJCn#>z?hoQDsxSVIjw1-fs5hgo8yk4U4D@2 z>se5*xelkhW>F`P;j$t!ZA_;Ia*5SI+|v*>>QHg`297xMjpQrzCe+O1Mw)U#+aB!qD?N&R;PQVLbVO@J$Ixy|g#B^&Rh(djX zQFHy%BL!p*n433Q<*HIc%QVK?!RO<=0;&G*=$UzXb9z$#E$C_gtsPDV@d~8=TU225 z%tYVCQh&8uWw-7CGt!5go^)NuU-IrT7NqP!3Vw4>oy{sVaN`L2-+T~DR2Ye=?xc}# zn!+lpRCN?vaIi&dT~mHr;`=W(3{83NVbhR+(Xq)1C{_34eu@V}iUlU1lgiy%#&OK!cUE^A|Qm^1N%2iW{uO$1bzNgWi=$&wo>inWC+y680Z#CH*(G z*tl`EBZC(Z2cPYRTuhq4^C@AqW#^6ZA=@vatJ?t@LNr#wE^(j6*r|{YVU*d6NqpDG z-_hui^x?xNmPKg7qiq@|_2kmW1UGs5cxERw4QwNI!SB`d9JW*(6k@Ij5REfpF4@r}g-V zG*`LwEjHW%4~}Btw-)ZfLZ`$AkNO&G!~5XsRw^OK(4`yAYHCxQR>)1R`v@6ay@QjH z1wFx;$guv`>Db5(<#c2(AG$g+HdKFVO*&z_(gw)b;QX4Cks%r&lL)82!Rbir4bZUx zID?Tj91x55(xCJ1J*|Aye7^ninV57U?Fkh&B}Fjy+7ot~Dm_z@Tvp{Wcia`K#oK&F zrv4a1deENR`r9f@!=Lghq(-^SB60t0xEZGIxx5Y=GI|JTxC&Ffg;!xiFX*dVz@9jyv8$?;aV9vlgKXmDsY!9c(WNb9tfyBtP{EV1G6(LV2$MA<5er$*Jr zcx$uR-zt}xa2T&bF7KK6T!E!01M96Oz>V5aV`-^a1f0mw>Asx~1C~Oq;sS2%|~Rm(j9$ z0WU0Z)}B*8>;X8MT)XB3A~`9r=8zWBYv?I7Qc8`q6)EahO>x5~jVU`w+Q6FSpd|@v z79&BnwpANy79mZoAg#{jr-0NJWvB2g*@Lo=@GP}U+50Ig@zyDy!q5#J*KF9%WFW$3 zoDWGt*#Ms9@&u}X@yURT6sVr&SuRqbdWdJaNP%jF#PeLHKy?Gpl0t0Nf$AqL+mJ|I zbddtpY+jFx6sSJKvs@&j`Y6wGk&NmHo@F9Q)kf9A^IRsQ+M8#&NJg~_&vKEBYD<=F zNTk;*H7yy{b9KjvHZ*dAXSqlb>OP*`&mPaIj!?^Zo~tQB{gP+7ND=BPp5-D%sIT)Z z7b!wr$g&NIWY9&5P{;CmTuTw^5M*h2&Si>K@8xK&rdYKn&vKDs)mWb8BE_mrd6tV5 zs~UKgixjK=iA_zz{yein(^9M|?O>G5t!>q@YBA4pky6z)Jj+E&Ro~@VE>f!cD$jC} zQdNm(xk#z%OrGT;rK*!yw!W4m{f_AIa!saGwJ)#8)s(7s<5?2P+-a*C!?Pq3Wy5%u zM564qtJHC}ZlmmPJWC=`_BhXyNR-`s)h*pZO&MGyqWp+gB#|iVL^gXBNpwTq*i;a* zSCKe3)X!O(WVxZf&(fru8|oV@O;X%Y?JP|q+)!U=%C;vBhtVC|pq1(y6hjd|`O^Af z=1@k}%oveKH`LxNO#)q4yRkG0bX{%B(j?GzwJA%JK-bkEmWDtQGM6K$2PDyT^*5w% zwHLUX33S~@pmOF;M)eEEhXlH=e#p`!&{g$KmL`F&st%SWfv&1KNK4tPNTRFiY=$5e zT~(iCX%gtFIu7Yu3H0e!($G~Of%<3ybzpo*psQ*WOOrqs)ex2@fi6nwbv&+;y^18d zsQ%833OgfXK51Xym}APw-V^(Eu^9IJ_5DS1QI32h$K3% z{d-uRau$@I-&l^(j?FcwTh)lpcCpgmL`Ets2f?D1UjL9j`Xbry0V!BI>7{z zbgy1Sj)l|IXBZOOrq)>LV;o0+pykSegVXQOzt(0+pz}S(*eYQM<7;2~?uC zMY;ijB=+R_R3=f0kBTmk9U;)k#-tSzs6_parAZ)3-Okb^kfeUY(j<_ie!OzHq{3ENc6ZS(J;n|B$8AMrP)?ZRQs|tNhGS> zS(+pg)mWA$i9|J$rAZ=DH6kr#vxikg^%@@2Z9qp%Md^^pRw?=j^c{H?R8)$(*=!pl zs$a1*smP$NW@!@0puWS>B#=Q}!qOy=K~i&Bh9okm^H`d6WKgFgeJg=BmXbgQAA!;| zff5-X(vU%oV`&m7P>p735-3m&XK9Hf3RLMKK!NHpq;Dlq zHzrV^k3jz+$AX4dGCm|wp!zyXlR!o_Ut$@O$fz!0X%fh&&SYs)kx_k&rAZ*8I+~?P zAfuXr^sNMXvxGEc^bx4DCQt<9!yZpFs(~y+5=E$&LaFy8P=tDxrAeR&wVI_#pa}IK zOOrqmY9&jPKoRQKNZ(4J5GGKBw?LBX4RS1~D4Q`N6-B67EKLH%s#92+1d3J1vNQ=4 zs}5sn5-3)+urvu2tM+AS5-3*fj`XbrdhT0^R21tikt;|Os5V6R>R9z>mL`Ex)gvrT z0;Q_ESegV%RZCf#1WHxEVrdd6Rb9=}Bv7jQ4$?jXvB#6WHmB-0Bv7i4K(ooQprMJ3 z5ost@9f`EX$bz+Mg?Z?w3k($WUB#|1&(MOy!KQp?RE!ka=XrOOPv!R{Ai+S!}UjUHwB-i_u@i@S~h&-{>vu2zm zef`%!;IT=a1E7C6$qK8G_qo{kd zJnA>Z9RFMB*vs*?1|?FMx8pAd*@i?i)m(pVCZkP%jt>*n^ZlesQxP8bq$61F_dJl` zp&)*~&WC}dwx(wES3v6x12QNL>x+=*CP!UY$n4+nNO-l5fI=GFAAMybAkt$`iG1r# zu;*{!ulW+ne%W7Z{lN@ka(C3DDt|;Zp;A4y|L@e_pDFI9fikk5`rCkPLthA>JvD_V zsx}I#f`IRyqN=5lJ%0i1wu0cQXaJp^F@paRhGc0m{lm0$u2#9zzcvQfWSKhp$lLLU^ZX{U=N@r-l2I6@Cjf$pcW8~ zmyr4cjsZI3MXRZRHvmO|2>1^0GoVi&LC6P4d+_gPz@LEo?-GQ0 zfPBC@z+r$0E&ntF0^r&WkOCM2m;iVN@Gf8-fN08aJpecjxCjVI7KGk_L4Zd9L^BQ7 z7XXU^?k1Sfad)K;Wog7{cy0iABo=pZa~v}1fdh4H((Ip5dbloj_V4* zen1dDThJFU4e%bI24J`kngLhb zz;!^2fr8KzU_)#6~F;N_jG6k(DZ&m7z0=U zr~(WIuH#?`2xy-v2+x70AFks7UjViMLZ5)@0rvuiJOTSVk=OtTogxTr0M7%6;AQRl zCa#+R;ZF*}K!5{4c!G6EUk0?DDhN*lDgZ8kj;E`28vFoI4EPf;Zn_}cq1At3x+Dna zQ4sNzAUq1F1e^uj51Z>u9zpsQK;dAh5YQ|`5K;j%0Pg^H0Ahz=>;ql|tOXngG#QFs z0%ibK1C9Xx0Car-egIesD3|c>Dq!3&L3j3N{7|1s&H*k0VjclgKu^HEfFXdffN6mFfK`Am0cC)FfD?do zkHG%dkqG}6h8(~G7y)<`@C+auuo&Dj2Gk`w; zHvpj%Ff0KN0>%TT0_Ff-0=x=%3-A%(6yO}-3ZT(MXa~>^a2ucx;90^z^eF5PNC6B2i~vjk zOasgX6aY#9I{}9QKRpWjpGV>r+04D%7fQx{D$6yvf zYd}}PWWdva=K*;D1+W6}8K4O8C%_E|dmIxQpc73lJ&{NO3Z!ic{XRjMPP?7D0fQ#^lqt}FhyJT19PmO6;ZKvvizSZgGJxD6;%ATn) zhV^#k*Qqhmaz-ioQqn;gD6{?H=$Hz)DgJPD>;c>`e>ghE0MoZHj@6^+j?V z?d*Uv`oq!p(txYYcbfa*v$n0Kx9v3}zneV4VzeDC@p~@^ZR+Cv+4I@^H5FM`FdVc z@Y8x;Q|iN7UNJot9B_}0)D#z|=QU-8Y55RML4PbF1+_0|KVw;dw4sF{tc1bS9XXdE zOS(O0HA<|Gpl{o<+D`fQ|Y z_@`J_GhdeFODIUGniXQLz@0p|yQZ;xCE+qw{~v?%0n7P?`=4uq>aGXwq_64_53Agc zc65ZdI%H!s9@}B8T-VlN$s*gRW!bh8|A)}$t(AOb-m@!=l*bnamniLLjWIlGQWnfg zGklh(Y?(FC@V2CcWwjOuDqXYU4B-o2n-ilvk@fX$MtpwRYERo|b-ZNd$!a7cs9Aej zt<_dK3|ENoQnoT=_FedZ@xs}$hF=1ek7wU$P!=jb%?iBA)O6MfEqEAnoUN5ye7OxJ*E+K!>*XRu{KPk=TCU^e*F?{Vh8#)9& z0(gs~e}pS;9&_3J6}chUNxyhXnK}29o?O(7xS5csG~JP{$-56pP2Orv-dYr8E7o~^ zA#cvSE{4;h^2xmJQ6EoXdldmTGfyy&H;)}ZZp$v^{`vPAdd*ej`E#2Z=8MqnmLyoS zG6VbQ{YvxafBp_JPl;G?TaSexpx&m(yH;dT z;`wN5YOi^NkP0u7Q6}%UIz~0iaP%lIcw#}6Xh_LYUVI_eFmRT#@`X8L_B@MEq{avJ z#MhnSvaXHDHO*TIt{cfYP4SI817m8scs)jy^Kr>Ewa>-JX>;P^^Isu_*jA#@ z)RelChXPYmYXtiH3QMNOqI9XUK4*luq#)v@{}`P|e-^`DTM#a#%SWv875WH#EZVfn zzq=Y`(nsO(ZT68UAUUk|WrQ<-6T~y|$l$aBUu&>0OGJ8O2ruu8G>TIB#fpo?zIGVT z!KVm^j?#s?mtkLPxHlLR+VVCz-BM4X>@73ax;g}p}wST)3$@G+CprkMP;!=u> zY0wU&=nK2Ju&>G5mmT4`YFzBgT=aj@S^S@CvdUNg{4A4c46+LHfIxXia)3!GQ{I(R z`!KBvX^z?W=;kGJ)s3`-8)*wKyf|{mb=5%KA^ve1mlrD^gpvwUu-BV2yK5&2T) z5+&l5M9FIVO`N^MELWt-=h7CsXD_x+9smbkwj6y)`Yi*oF5Jd+Wgi>|7ob6IwND#q zpEfo%f7(R*+R-SmFB*&gnfA34aY@Zz`zS81RTZR#S1F{qf)|5m?P`i;+kjt>M8%>t z_vdZQ3dEJTBIKH(zj6So=#`HMz z*a`T&Glgm+{T4vPC=9&V>`gSv-~>nE67(h6oaNA7_NGNt%JS@H_NF{fo<2sLy@|Y$ zHvh9<4<6m6o%9HzRME!nH{MC2TlcSPO*g(yU9vlTrwuh zak$uRM*MGGYAX%4;KQ1BTR&vwTpM|5a;`!bi>d!+REh-x_;_3frB5!^X! zp{to_j6S%ntH?wuvddPj$W*w*jS}0b_I4!$Wv0Ts99ZkZk(lk-Ck@#p*_!PP_?RPo z)i*21ys&C^BQsMWSkrgA%^QsvsL%+L+bmase=5px>CP{#(^@z15pTOfoxrEOy*fc= z9&NYLsFWX=l48o3Y)b@@X`m^Y&HHxw8c?L+0C}HX=>=S!?rgRA;wv;ffHX|1g=A!w z^vRVn*M6N2y^KI=_Dh25T+O9E5+-ez2W6$$=tIa_J9cIS1PaL_mq7s}7AlkEGPfu! z%V;1R9xZ25X(FNJ|0hz(gQnYUy(o~GiU!Tc0VPEOBo`q0DJ2UB;cHJ64`hXOXuIOh zx}BCqdzGfI^)T+mDg|Tab7kOb+k+QR$CxbMtF%xCOZOmcD<-~fQ!ds^a3S1}iY6an z+%~R68O%hAzPrrS|M!Gi*+%4!@bxKjAWH4Bfd5bnjfU2xj$uJ|HfS`}XamDE01iP$ zBZuJ1BBin42hdEa4I01pGMoDELrv~eKIY0UK0uZK(B$I{eI3ueUWzWg)jY#l_A*&^Gi2WI(* zMgBem!!BiqDep6|GZort$-7}#GpxBLzeL`6pxP2pnU?Z0^*_!2vRBW|_SeX3@$TqL z6oCdL^DAjSU7GwF879sCk~W$^^%_x`lXG9@K@C)sUnBc~NVAWBqEZmhpu$I|Swm*~ z%aN2d6~0Wuf(tcu;HRYR`0_9mK|jYJC2OQF(U`+9!zfFi{4&W4ZqzTd#RTyZyG#>T z7X#z$e=BY}Yr#ie&#xX>|GvIX+j` zF6$(b*4^oomy+PEOU)IG0j-n})G%$N*>f~Y~hD6WUZ!a)& zO~Zz9}OhEdUq7TaJ6-jLnSEFrVsWWb|A=Io8bAR>NRft8qYXDsR}*mF`*2L2TFaUo&wa~ctJNq*g4+Zo-*EC>ryS2bVomS77s zwO9Ged7JpT(tde6>2oM^3sd8lG)Qu2Wh2@)GBsLtHP+Xng=;O0NRwC13gFQ%>bB^G*9{yV1@h&6Un%f%3D`Z2gXhtIM`h6qT1I2`zb5>jB z&@m1TaQcY@wdv2wkT=^zP!!DyaxG{|K@n4t^4yyp?=AZm9=VZobLXvvTmf6%)oJ(| zep2bCKBoH7F=1%(KjE$My}^uV>*zrpKWhRW?%Hr zYGGz?XS&wl$rFoQn}ViSDCg-tEuJ8``@b2Vd)?+>mYYnBElx+}&;)i_QUQ9xIrXNOb8gSXpgwZ5HliIZW zPlSOGTpBslYeBa1omh#qJnPpQ{0a{{pJ2h5qHuL;_60FDzkumCWfjE`>trTZ>r#uI z>HFq2PI9g$>)+_FW{zgfd!@^`W%?AhWY7w8rzRG;Sb$9CoQiD3C`(FlAX6fnoFM_( zgCt-?|9q;hlT5c5GoLldk7@ndO;9VbySLyT_@~P6??y^A8JKOSB$UXsk}YG|n(5w_w(w`u!hV=rGO*=~w@-&=lyfT@N0E#a9x|~yC-veA#Ye2{EUm#3$&`zq zpA*a}?yNA)k=_pl%anp8Kt5}tGQKx+T|NfCb;$WBH)Egn=})-%oQ%5l0^Vzg+-e6_ z&h~WR;QAI-)y62xE`A5n2w8(^>mZ?H?AL(oN-C0eg=P`EwqGiyNyL);HQOZdp8@i- z;8SsGL#xwaAYN@S<u{ z3usqmc03%YD{eaHLt#42F_a!~1GQGKGni~f*43A)7F$tOm%vtT>~w?V=<$Pc#|Nz+ z3mk;!2m%~ahuxtNS@tUG^o`jANtH6ve4mUDaXSLA>Lc8eSdAMmfLA`j z2wQ`7Dqs-6vm|Bb1o*kAy-Jf+t=Lf3w)=3AqHIrZkULrep$ksK3v%>zx)IuiJJsMf z$h<>lKm}j6V{lUR_FA^{tu{?Iez)3EJTTgx%kMT%n`%I!Etq^KBeq&f+K$N{6g1;_ zPdAv@XiiC~FfD>Y5W?9OCZR&E3@EKqEq&1g3C?IKbApbL`3vJ{)S;J(-f-7InX&!_=(>TP11|lvjFe(wqB)`sj zqpjuqDTk>8{dZsp8U(&&#1TO&!eW>5?niATIvlaczu|GjfU2D9boy#{QkmJ*|1a9r8xs!k0y&Sw z!L|fN<#v{l^Jt|(Ma|GK$pMTx+#OYj-^P^l1hiTo8R3q4pCM@-2jqC4sz!Iz0){1* zu3t6K9W@F_t78SVBnJ?_aRN>ociIEoQT?difRPF3%qiPu1)1foH9_WtZOXEbQ@W5w z%oheV2{h%>u_Epxm`NFXT_1c51W$gvTDkb~GtHTf&L5;m{OV}L>$JWfDbKIIQ{1Yo zUHwGLcdp0r3ytzNFgA;e60uJKel#vGyqNBIbph^(q}e~p#|6(4raPAP1;(Xd5;M!k zlwqH=HAtT-Pk+*Rz*b0SwlC@qLaems{yR*^r8X-w|#YW58(pXLm;efu#p6mgoUZ{Q%jsvC4wB^;s%{j)P zid6~4G^p@Nn^d9r9rO_oo{OxgrtpG|&Wr=Inqb$aZRzfQpJcU7M0vjLU69u-;dkb9 zRC+X#1=xJs$3UoASdL{Ee~YB>LVx&=&yC5$uG9`$jMYM@Bm9P;G}Lyg6<$@s^#$$v zoibxx`$xIg@!Oe)UBAzQ+0U4=i-}msipwtki4wD$;WyHdNVZktziHu*WQW3n6UbDZ zGZp5x%SK8)j7=Y&sr?ZZG@U_vuJ@Gl>sqvBf#^_Qg$2LTjH)Gm!OFfcJ_taTF>Ghe1;<^ zMcMJi?cy5c@)!L>i3hEAlD|^YzFZViiY6f8x+%)RFS{5FJCti*j*BV4+R~D6)$AB* zpi!4<4%nMg`D{pb$r@!=!B~l!%QE6P+Es9YZbprbo5F6u;lycMhW)X)ne@wb^KeL$ z`$#*AV6#nm+mOg`bpG=`?6jeyz#}I}idS-7$7##G3qO*^N2gqtRK6NR_zlpf-uZiK z_L)Rn5bsm-i)d81%>Q*uZgnou=mgCJWhb#68(o+G0(rT-JdchzjGc4PyM8&dyCS5J zP6A37mS=^UZD%f-ZRfIUnwxXZ2yh*9PC5PjGkyt1{;wsuh@x`FK_;w+V2X*FY=?2HErT0BD z`#(#iUTtvGgGq*M0n=AjJ2IU%N86WkOKRDdQ6HwJ+e+${)~r*s%h06D*>ef4<)arK z2I!3|2%%8j1!~PDK7cK(w%k;&xUec^!pq>tq(N`KsPY zLEG-6m~D5mkiCwD>^01Y+V04CQ@~ayf^Bxmzu|FcV+1mFQ&>}N4m;!$r_(@Nv0Qno zC{`+s#;N%LV{%?L60@41oc|o=3J)SxL*(^E!fMYWglW^cV0A}~r?J_Xlh{xO?f<1|g%11s9?z9qvIPX#G@}3~rmRlx$cj zCAGr9aSTdK$M1Z?bZRH$JxcA2jLrn2@pZu=*MTQdOueAKEv8<8?YUR~=i-}e(w1nd z=B;jZJ&HUctU|_*2*V))^G1@%bpS@r*;7LDKUP=S%TD7^>eo)~Tt;w$p%!5JfT8H8 zQ%!}son|V}Z%TMF9%bn6OkB{bE zzNb9*^@mbcl$Z2#kntmZTqZjmDv!&~&pQTzQgbiNYm9y@A>ru0uhF(gQg5r{G`q29 z0Cw`vvg&cB*y9pdWM0Jga29VyN_JLKzSEvecUdNe7&5CMU6%oVTR+NhoV;FVv!e9=Scq_A&{ivTeopizLZBKskud@S5vMG zLuHrn_0;_sz$kQ=Cg&Z*b@ttGL|RR`ORHSAYLi@PyA(JJM;E!lw6e<2qyIHg8?IsE zwsyj8=wvFevtp>1B$vx=)#yB}+eCT^tg<{$B97ROQA-JBK>=8D*;#Y;JX*|YV|GMy zkFLE;n}Je4O20ChnKl<;9~;nUTug=c$H2%KqVtf`&3k_|Y^%Ik(sF2DBDLqu#Dxvj zSgK?Dp&6Xk?xa$6FVdbdwiy{e?aoBI7^E)cM#&3OOVDV8G@k}(us4Gc{|t&Ag(jRN zA_YxcTxkoK)n||h+i-8hBSHl-englJ4zPM|Od}RAT(6Zy89F$WmSz7o41Qkurfj&B zi}jR;%OYg_aOpLPyjI>*<_ff(42H?dPDbEC6Hy2#E30l~&wGc&N%3ZMkAFrr6Tyg< z6f6dR1GbVH2vRr_?Fo{~b4yKjIxF=f#Cp`lI5*|fLZ%p%gv=!lnVyPq8hKpBFc^2TqVACh+_IM*XTp8dEve$BgNA9D0- z-X+T^K|jg8g?^HIA~E>ygY?Tg$k?s~W<}t5`7!^@2|q8-W5G}z z4O?xw(Z0Eey9Il4O_YJ2@@#G?UrjPiC^27S06&E%sEoQuDv+OMyk5#7mFBeNu3j zj#EEas%$o~sd_=&;Bi)1kjcB_9DD9)0>L&~+JJh9-`JFz!-$ z-Z5Me(I^Wad~SYr3EjLBvI3L#*EB&eqYddMdf4E=DS0s+;cDBGfE_7|X;)%jOnVjn zq=mH4&bBB2^W6!1-7xgXhS*>Tvx0=tn$}u7P~G1Vo_KSMz70zMpY4Y%DW|bRgytY0 zZMTy4+ns?;;58eAP>8KqKU%;q?u-Anuh||;Fb%4*vmF|H6y~Pwo3<%KRa*7?-M)c@ z7XIvjlGW#jHJB^OGt{ABohSym+I+wwDJx3}qoJrf3XnaO#uYJ=fMWOn+uU<3LMd6= z98eyx&(--qj6)d;)bDVQ=oX|`zVb8xIX0t)>*E!u_8zMFG~D!4l;)!}dEHrhpi8?6 zwb#{+enQM%L_2!3W1ul5FA#mio`^K4Y1htO{JQG zNwbgedR{x@22_g(iC!Itc3qwDeGBzy2u&&4e&-uxuu9fWGw2rG`2yv|%7I$jl1{0eF&~IfSbjs%UBSavRyMgenxGf-YL_ z21VM@qRIQ-MXxKXcHEx&I4h#3LU9VfW)@TKaNZO>hi=|PK@_7ngrX3RwXI>5>791K zaz?EGvE)&bceM-V>0`?HUF{^Eu7((pwh3yXvdMqtpE(gd-wvX5|3ofMbU!B=?4O8s z6lgA^B|eJ)Rzv(#)pDw#oYe#ViT>h5!#L4!|3sHK(Sw|5gcp(Ir5xJ#K&p|P>LD*G zFCqbYgvg^9QQpJ;i2^}X&IURz%vkCIgs998PIiMBANbA;DWaq*J&tmmcGbccSiwV_ zjvh12@T|$9PFyRqa@MOThxW1UmM_4Lox~ITrXYN=lnO z(d{!cun<9&r2QN_v_`-|PU2`;3T!KcsFT?AP?7 zd)GqpVgW?<#a{94??ajv0e$C;`{|^f&iwxOlZLthT7=mY$KEzLfB$rEn}Iy6x+Eov!zm2?BoCafp_EdJWAPUnMBv`yK_ur}udn zt|5&Vt~1=ku$nWRsBt)o`4kL?YuQgf(->Cmd!{jYuBcvs7D?s@s_yO9r3ZQIo=fmn zBRH$?a~uW^4#XGYzlxn7qb{s!H%_~4!j9b@T*z;q{69N>Q|{OwCC$r_aqLWI)l=}e zS0Y}X88-oM&txv?REk$Z0gutO3@3D~$5Z((o4QmR}KMgtmE z;N@08awQD09dGmkgdLdv0nY)R+=;im0sq;B_ZN2|*7n^)2S0c;6cAX27asvj4hX{6 zfL{RZ4hljqz#o7q-(e>Km~jZN@dLg-js_3o^-e%0;DMvK0_Ff5fOi4SkKyH5z!1QH z0nKpR;ak9WfZHT6=m*#d5b*v@Kwwa#6#f@%3<(VjPifr56w$O<^T?=_7A>P&#k6jt zp;J=Y*86MMKDI+j$4+sbyL653c3Vn9_a3+ROuXYx551m9>eai?UC8uJNluPRzMF0x zWyQsgGCyqg-2XXwAgX1HL*Dlz*5ehI3`f*+nM&h>J#a%J<=}*9iXN_UhzVFp=iUx? z#ZxadF)CjkbT;9)C-9JsmEEHl^<7~zx~H}B+2WiVD{tVo32sX4RoWja6El>Phwg4> zev}^T-NWAEY=l=gw?C%zINYk)TD--@it+e96@U7eGWu}aW^%n^+ha=J;SSv<)+_$^ zB%j(&+6v-@z&9o-Rfn6W(%Cz5b`a@raPFZOK+%qq)>Xj0;15T$HgL}=sYfD>G{lg3 zie>2K4rC^=3_t#EJcMQFsUBd=EJIJ-Ak#xxe~WW#=lB(LpTX)F5y)Evvpey^2pN#@$R;Vj9&H`ab*kvB%@tdSG0O1o zn>QNK7?==BN+X&edFe?Kxn9e%Z{MQw^n8T27_(xX{?aETEjh{*9jysEQO|1~=?jO3 zTu?I|Juo%nu9ULl`|zeMILgz zwd!$QtoL;^7fV@UV6O1mo`OSK@zc+pM@=alv?H)Q=sy zEvZ+0aExzHwPC((jPm}Eks;bJ-!MiopJ*PckL=I1tTvcG8l%LXiVV?4^P4=U4d+*Q zP8-j2c}^S9Q+ZAs(GM}E1EU7(0%^|nz`t~7^BL3GC^m11Vo`ds_h=bau zDXAx-5@~FYV`Sa6fvC|$)jZc?5loY(%5`C?%r}6|7|JM3ihqg*qqE*-YS~ zd7OQeo^NPCKd8qmk`-(pRCbQ+*7fB2{meY+F& zzMl6U-e*73$I(kVS{vT;^}MF>2YFnoH@tVcKFw&Qvq;oKm&PJq*)<0 zAIs_kHy*`x16Lz<@{!xPwO^z*a+@gaPemEEfh$rbL>sq%jbd)EjoV*XP9L}5k7DCC zPT6=W!cg#tvh$Q(oT@zZ(;Z?vCGV#@4O>Pl#XqfWJrOTI=9_WK-#;_o+%$XsRQ6t@ zxmou7X$84IPZAA#A5uouBr12GX%~c7jp5gKpXq3r_fWz3Gh@VPtu{{o`cF;6Is&gy z5|e2KRlj^C8hSpc{BpXd;oNW~@7~j(KA%=nsW#{>CZ`rSZ z*M289Bi@;Iz31rf)B^dBw?xC@K}y%ZdKzX70`m@rgh7n)`oC&Ap1gj;opa?H#_`-I zv&PvjcbvO`9;s+H)(FGm6`)?mf^YsC9T5NVl^bqkvr%jf*x^385qBO1*ZZOL#0Ku5 z{Y{1c5f2jv6GjVryIJ)+}C7GmC4NxS-VXNv)E zWdnu+MgYbE9s|q(JPTL=_*6N1wd+HV2Z=cI6NM#>MBxlz65c`m96-+_(H|b_vXx%H zhv_O7vw6nF8*VFpR3g`K1X_V|_UX!5FK)TBbjuE8cNaW#txKTz$Quj94&tCU-VneUcxJ1ug>fsu$>%;#4uBF-F`&>1d}LwBjecJgE`N^LTk?Ba}bN%ip5% zR9?QD%3JetF<6W-T)vC5g&Kf28+E6san}RMi8{t zGS5=xLLbibW6D%%nPZe$qh*?gKx;4eadr<8I~wNnaSjWCj8mP{Lc~ zZ5S}yStil>8(~)J7+O}$=a`TqT!!H~ZX$3A8m^k-3>?>J;1uAFIlqX2QWv>6u7I+4 z1kD7-p|B);SxFSLU+d3SxvazEYrpr)Le{hzoI+Myf{t6^DneWOQ%KJGWys9+JCLP zb9#u_zG!lDv1x!|W;f@Y7UCkqrFiF!7Gj%TzsBoT^-U0#caf-e(jS6+k3AIHUzJlG*uPGJF`LX*oXcI+JNBon-D0>^D4w+*!s$UL_6SSdEq>W_CQQ^UTrDf zZCKaUc~`VJK^*UNMvJoySGv&h#@W6VMno-32ah7jPjzvowi2Ta2iY~U5ed6lOSeXP zy_O~!-_z1lPL3f+F9F53^dH_Qgoq}IA6Ty^)8qS?V{Dub}CXCb~EM#bF|ey z3n5`jKwGJHv5LbC&9lwPTOI&d*zmI}E!!JD+PK?l#Qn?6kHOyBgl@ z?0lxJ*qQp&R-6I*HEJgg2sZq5$T_&Pc)MZep`zzIi+chMJFA?7Zxa_HfL7fmMoYyl zM4W))S!VrHQ4iBd>VZ-e;oF@zv@b|S#qc7wuEyKeb$Q4%ewg;9s?M=wT|!@tVjMi= zo;=pGz&|_>t=!i_Z0XzhGA$4dbxH|htB3l*>A4u!bur$r$JdkRe3xk{~z(HBqmCB|XbKf9xT)>te1~?8!r6#Fq@Q zu(`9t?P6=gk|^iC-eP2kolEk3lrzyHMmGBqw|$u?8s{IIIdg6oqYO=0wa6}-YDIry zEyK+v>iOAboVBLp!&>$$jxBG-T58(QyCu}>=e(n*IL5GVoAcG4knccKk~7j-(i0Kw zTb>EQxhFO7c~jnirp0%f78w%7ZiXlkU&9~^(bv?-@ko!4JIn4An|Eifey~ZMGIdi# zm7tJSLXXLtOPV;PJH@vBSJo@{XN5W+%^M2Co%y}Qwq2a{$~}26NBbe1JKC8h zK8{w^gquIfn+5C?;JmH3IK*(e#Q9urnBNt~%pbCnckF<8~4MUF&7)W zI{oYIlKPegk@~EL;*uh{pC|`D(Lx{{d7}3yiaPUArhCm!no9%Vmxu9_z<2S3h_dU9wDEhno~ZLs@f#))k2N|+ zXEq;pRrR~k82xbOa^yC_*`YtSC+W_9{l(TT(ik7^<2s+HiO#3`izDt^gPSIrI`9-) zBK4Jb&AEj0dLC0peSJA!=Q!zx(@pJ6bH)r1Telsj?3eW3bxog| z{>UPRIuT@qzqy|9$ILR_SvEk7>hiE&QPWqZp7%1ur`Mg=28hvZH~93IJcMjarufJ$ zIPb8C%{v?Q>KeOwkipwiNd5hsPg=xS!{8gvC25%MR#?Q~cK0$_btlnSI~VajmN`#i zq7HNB@$A>vcw@~Ec{c{enLu>!E#Br#N)tO6jO!VHqo&g^=i)RmDnwgBUARif)`rtp zouA_xaPX?&Jl7lN4Zo#{;o@NDl{E2jv9ojHK=Bkd&jSXDOAYI<79AQS4mXJ7obmUI z-y2@K+HLfRL3 zG8)OJc(N6eV<{OOF!-XNh%;I^`)7#lM2mA$hSji6Qj$f6EI#7{)A+5ai-+QXtO+WySQ3K%E|tKe}}P&3U< z$;?xWr`7|yDIW8%2r8r?aZt>|Vt7E!-YpNKy;fUR`kD!LTGGrdE49q9thDTn%nUP= z%nD26``ypX!b0Dl-*tU^T{pk`zUQ8Mj?X+Z^E}Vs{csEyoHvsv#`4p=k9VhoU*deE z+>A#bZ_(Kc1h#>bqt<-y(MQ_sV2GU+}xVL6dlNBfiobJC%|)b)bKa z=>K*ezBo6{@h(c`uQv}LyA1n4_(rKOwtybEorgJ_TZDa}l{k%W z6%f>Ija|_m#X=Y!e^#BCO@mtLSkbJ)}oDY7WVn2o}E{l1qSH zemF$6Z$&SuTQQl(VG?Ge$<%*I`WIH_ zx8XD(?K+wKArql;z$)Ixc@0y)hH=LyAp8d-piTWu&1v9t4vYd5x$ss%rs3rGw`Am6 z9wk3q#hZ5NWE4{k2esLJ${#~jk$KBoTUK*(`W2}C*)pD4s9G40Z-m0XGQ#Tn8E-X2UIUZ|<-gbQHh4M}E}d&|1%LLYT(*YC#_q?gC7=rZeWc_~Swd#d zzA29byKXinH~v0C0}XYG8cgzy$hwn!{HE$8mVH#9kc}t5X~A!irRX<}>6yvRQn{~~ zMjj$5_-STq<&~(GT+d0=0dA zB{UvS?R`=@*J0?cT8lk}ue>_FwfpO}fO*A!)zD%7eCWzfu5agW=_K;m2HTSWbI7Br z`FnexSjS&%B%Yot?|qafai`q;D4#B_&hcJ*l#k-#;B1-b=3RKSH^a>r2%had^Ef}x z#i1GALz{Rn5w?CBhM?4AiR(8NS9@=6;oUf&AiHkmpNNO2${SnxBz~(mc^i-8ZKI#V zEAgP8cX0L<=si#gXd7si-24Qp_OnXw8&6<|gOBhgY{z0I{CcwA``Yd$UiVXcKj*#V zC=Y*LJTlIE)dTs?c6&Ix{vv0egZ6>egHk~QK`lYFfo>Z=F>#`O#ZMk%m@vIm;r;em zUL6$Le+=~;(~Yt6+)nOWY}n zN67!a#6K2K+hxgKet|ENTS{R3#cxbx5*c)5wzc;LSyew*U25Whk3uModN@r%G;DuD4sVitf2 z?*(QKm;_>`gBe0hGMFaBi~@6#j0g7vS4!O7V7yi^*q*xczr=I_bD5ZSU|uE03}y*2 zxY6A?)%)pt{KKH0gL>dJ+o5sx@kVCC?}bPYf1G$%b#4fi!5nuRsc^6jJ`=Xek3Qfp zi6^?rr62NyiMvm7_B!Yt&9@&Tepw~okGfR-3|{XzclDBU%2IC zOh0pE>yI&X&RHlEPw}XZLsZ~YBcdAQjXBP0y7i-o|LH6l1rcvl3-7pZnr#wNy!CmTt8&fdm3KZO4{*B;P?E`{Qv+Uwz z75qhK*|VIti2YZ9_a+qHRfW%#Gs+uy52Ik(u{OLvir{+Q@oEfCc2dFceyE}M2nwg^ zB%)&if#|nQidT6LMai2Zb$fdB_7$C z--?sjCwUAXAlIJc?Rj_kE#k|AC-LYe#^6JoC@9)4j3k`?V<iCO)z>hN3Q#Xx9U*+pw{Cy zLksBgy!v+O^1V-Z$F@mOCmR2&tIp@fVk7}qcT;AgLN`95#CU1>6h(+7h?b5|dGPSX zSri+E3CvN9KP8>A$DhN7GVZ)|*GiX9e#$%YOzDGrG5tZKTgxi=?=cL8_=kT>eScf% z909nAm9|qnI%JBHOeD1yzi4mSr?7eVwGyxko%tDW9XefEzV@KpcM4OOXS8VrOy!Z! zuqbgVgB2*(c67Q65iqS#G@KKC#B{WG_CfBo> z{G~UZBmVUn?j}x)1>HQD1r=y zw;dGmhQeeh-XaAylHsGVp?H#DZA)8 z&N)HYgCx0T6&lDZXLHycualG+qq zZ(&F`+d^tubRFY>=?r*#vtftrYxS_Z#TWpM{yjUBnkbY>h#nhmB`La*xl^VwHlWi+p1}xXmv@}u=Gckz>R-)28}ddgwy?0b)yaITKU`mP z92ct5Ve~b(EmLkjgQ?T9O!>wcT-51CYEAr?`uI#^`PyV7-O_Pw4fo5BD*5@j6dv7c z2#RL7)yaI=AlX`3C%f?hnfoR0*iJq`RigG4>UuG~CXd8c%hI!`p?j2+Da*g)=9qiy zd-h-SHu>OJu+q4?2X@$i_xkX+#9Lf@w#p`VO|uyL^9EH1Da@)3aa zQRLT*5cxIn9+e$^^>0+tEahRjl2VnFp(H&BOfw{zbrwFxs*pL#uAh=<3W8k3lhmeB z!~@0%(AxXsSw6zVr^tkJyhHOv9saH{CZcpsI{Mki&E?#4{Py_ZNGj2XHRj)OzpF7b zLp-`aG>G}=z|Z^bHSyOXYMGtdoR1uZua#b*IZc$IiBZ(!|=`&^GFb?G?Pz%hr4MHS@s?F8=h(=tG>e{pEd1e@Oj=t zxL=fg&hyja{bn-ad%WM^-YtiJ&-+J?Lg`i=J3u*WHuP|@slR;ud)^QC+K;~H@$u*I zHf!bmO<>~7$793rA4wi%$a%TFe3+fCH!qYeFCf7+|B+)a@K$Y>z~pMPh|zJ@W|5|a zh7cLP9QeM;E>~UPotvKw`@1H8u|3rk6pp3F2J_ot^56x&IVy)Lks9EzZM<=`{lK|$ z_7A*!_+S4U-zA^=0he-1`PL7-ylD?q8$KI&6@QPI+RE*fyko*3OoEWr zo`A|d!A90^t$gsWni_ngfNo%Js>1)c1>vg^e#CV21$niS5546ns1j>V)|lRVK_*`0 zJx6VN0bh!IwaVve-rJ`RgvAL`Hse&-!!<|On(`1{nfqr=jcr{ujuz=#G%yw#k$-39 zYNS`W4^xlIu8r~SNLwKfT;x-FRy~iQJh->-Vij_{VKBZB=8J}*|5%VR-ek&ef*Cix z48Sf$*O)gpkwbpuY0=N4zUeimrlaW}QP2wj=2r5+kGylcc`777bi-thqjap7qG7WB z3&Z4#P+9XMUliUB^Ev8Oz0n7o%A!kH=)5Fl)g|7q#ea98y!4Oa*P1>BdkM$nn?Bef zZ9n0L`_c|M{U_ctW+?)i?Y|J6n{~ugy+ye?V!FCTKKB#1-u^B$IGgy0=@l@Ikjh%q zK5&V@AGYL0;h%LMF+Bkh)qsGlTco(mJBO`=25;FNE{Z?%tW3Df$FvrxBK!I$D`95e zu=zA*(6~Htif)bBE}y*2ZKA{^zq!oE^;i#2s4}_$@y}a^qA4~Et7^6hcMdF6dLOGX zkA#7J!{|6$4LZt(CgW6dThH5T>r!QdG?~&F|bOCsWEpX`}M={Ab%JR zR%-)kz0ZCJ@ukQG_zg)GB}d4wf9Cz>K`Hu^<0>f{ z*bL=i`HgGLYv4r9@QU8;diXec<$E4PsEMD00DShaA^bJS7f-+(8sJiM+NwscKr&(S*cBec^;vW{#NPj#oQtX# zBggcwB#s}iiEo0?n)n-NyPEhG27lV%|1|z8{X|1=H~#iB{-zm!l|IJco6rYqen+<( zsu1IEFIiN@*98X~{z7H+FT5n|z9z`hT2sHrQ9$o~IRdM3eDdR>Zi??^mMipWJf*Qp zV^eL{QDYyC<20Vr_`b$H8nU z@D>^?8~9nM^>&SZ|J`*)+5xANvawbS*0@JUcwA$l#y&dSq2(Vnp3vw|z-D2EG?dzt!I{hE4kde;&RKkb7*GDgzU7GyxIQj%*t4ho3tRUR!*He zYdqduPEF6qnoBk*b7xVo9NSmKxLA71{Jgx3Ov5@OD=%mEgZNrKBQwSCEiEHAdw$Ad zmb)N7D-FuD%#`s9@^e!C?jaAIotu}Dm6DgS0CRq16FG7$$kh&VXXj+3%pa1Kr{!7m zQ?lmGPbF9Qb;5*n#F$Y-5(keSrZjARM%KI`^B1JfOU}udJ9l;tlaY6exvtVFs`9K= zs*)_fDzsixSg5h;Hzmj1uk39%lx(Y3=((;i`-+zTQs`*l->K87P}raOBRH~!|E>I1Y4qE>nd&Xs+P_Wv^YMiM06uR@f3Xw)6>pld_jFbX`nz>Qte&py99?wAG`cl<{Id2} zs{JvoZ|E;I{f?RF8*QZnTgo0t+=;H>Y=EvF=kE+Y$4>Z>d4Dz3+G zuJd9^Qu=DGXCsvC8>!GXN`8=t9@BT0@|QhM`x&n=Vxq#VoL*EhY~h0W`I)n4vg!u* zzA4JiUpLOw`u=QF>xa)u$(>C@f#DPterT|08ib)>ruW!jQ6gmFFmd$mHtFiVO$l>Q zSI8C;@H2rrnl{FgKF}@{vPZ5ME}|wTW&Jn}k}@p8hzRK(A;Ll<9zzIH4Uvf>L=>M)KwBBpM~F_mOs*Iq z+M8?$FI3?L%B|Iqq-UfE;uZ2T*_O-5k)pNPF|{r`W#Dc13wBy2jub6TwQQ8k9w}mY zU->O!M0TBA7sCnOu#T3;5T#M|WE9PCej)!gQkccosq!tmXc1b8td}Ee5r+H6 ztU=N-N(2edWEIRdqCVlzLBUb{v|Kw1Wr={M*w9GdC=p@mJ4Ik#L>97g6f(smAC2mZ zkhhK&?K>318lzlb{VKj*$zf7jn(YD3y24sHdOHc}kZIuWD6$uj zj%5te*(H07LDNOZ`^Jba=8Ea{V^qUoU)fU-$M1)j8c7xWJwlC z5Gk1`-_=TKb)X3?a)g75Go!vI4`iczlP5{h!vZV{eO<5Mg13_#%(e!r-p!vjUo6(?NME^loM<1?mkdBg^K~fQjHFUP&@}v& zL0?x>MxpKU<~Y&1aRscaowD6{(alVJ;Ln0ss)VKrutu##QDJWh*@E$97E~#Ap@$wyn?-Znt#ZXA5jEX22USUKbMjK-MogVbQFaCD zk`oh2R|*|=HI-emvh$Iy8FU3I`EvX_k!x6+-#%N(@5*10#I0SIVAP?g8KjQuP3~;a z(W6kUOCX(F_LwX_jz12s9T;+)7R;PTat%o7uzvB&vcnWKR`gsl#d0SS|K}VOEt95* zyY8H<)eMgb?I1%o31&)Y%-oPq`*oD4UzTr75nV*YO?hsL7~IWsgEMO~zMjYL1@QE{ z*u?-A+d7DaT{TrSatJHz$4nLNX1Q-=>|2l(w|dG^pv)s0vxvM97J&jq^a`u!ptIsx zj)QMpj1}lOfpRQ%_geBq7E;x?Jjf>;Naw^<(JItk$kf#Je07s2$KQiVzvZBHwMoOgMatbSJ*W zmYH@`U&f{y>=+%=l@P+}`JistK*t*k*0`6*u5HC@IHD00H&663n%+~VP$fpM!^(|qkfzn&$<#Gls@O7<~xxH+QWp zpGgs|V|}@JwFXqFYr0mJpQVWL6U&#Nvw%)7KnZEQITg%I?}iw zYH~QM7Z)~lvRpn(v`euq$7^69N4D;1r@{^SJakUK?pk9;a*T_!0@A@xLz#Tk#zAr^s9#tr!hR`p^7j% zRKeHt5A(5XG>+Qpz%a-nfYk+PJ?`(yP|4K1X&AfKa@HMGsW0j}SzbsL{RWk9d$CRz=|&MjO26z+&Q+Q<1&L*5OYjdUm^g$K&DapY@h zqPL4~MU!C`+s?$ocr?T9(9s+eIJu=czCTF$r5MG;rd}Fq_Ew&VCZh8--qu0vv=4{cvUFk8-{sf&ig3;U4 z!dRV7_M0R6(VSx;FxK)DXZL}e82>0IwX!@m2g{F_ag*RsclwpHy&#$p?vfZ1{W zQ+@_EYyaTvCgQD?`{s)KhvzNAtr#hF)>MrtpnD|&SaEsS4XyQh*G*5fR z3R{E6OB)r@=+U5xwsKs$7~?8Lm2xy+5p-CDg&JW{Q$ZNYOz?bc+g#-dl!p7Y&w|5AU9|y=rHIk=o%>M zA;CI>?g32(WrH?=c6ujdh^HH+`Y@sO|6X2-?F}?Yd4R6ZGWdlq24Vi6V>t={oi#w0 z4P6yQpffG`Yel{E&P8z%fkOF{y%t@<=vMh#+n{)WZjY{`i{(Argm~I z8rf1Mmo8E0&}dz*WS_S4X*-9;>?PiZ^Tg_47cGivmnqc^%XSJfRn41z!%V0?|ari7RUjh-O}Nm2JUQ;{cBZkE?dYLo1)Tpfm&! z7J~{X0?@w#BK(8GpeKxJfdwOY3vd~T5-S8+@C48^4uY#vF-6;_96+nsOhkSF< zRXt#);2&)G5a0wpgzNn%c)IR~Vmu(H6&MrdV+hn<26o5djd&~YCMXOmCSO;^ z9>$`Lnk;a^Mhm$Cc%^SBYzQYW!ug2coxqWcmAwNvVu|7d7j73FMfdQlI8gIfWse7TY%z#z0+*At&P@6Z{Q!v(eh0FF#{)g){@sY{ zVt6*i4uB}K4>+(|X9DP7gA%4(M_VE?;ZvYA@ObjaZv9imwF0ewss7<1yrKO7=YhyS z9_X=OG>?aRY~W3o3cwRMmR+NGJbhzUj+qD|5N-|OEDwpO3ot|T*+6q|ooV2W1QnOv ziT7RJ?8A-g7?1bZB@j8X-i0Ok-P{-%D}etQ02?G+3LHF0*}H)oY^s!Q;G=_8=>wPS zfy;SX*7Kp9{RBURzkygL#(!eE3`3h`B4Oa4!*zAti|;k;njeAhFhEA;fd2JAVe&{c z3E~o#ffB$I?g!by+eUFV4@7aZfst7LPln!D`n&XA0PP8+fbtMP_#mhld?E1CSk>vO zfb+(wQsMz3`!|T3*6j}nW8WZdJUT7n5?Vofz?TCLB%|iQ+a_=pI+3%Z;9Y^62eheh z8FUr_gf*Zl@O7I9!q`;!2pbDHN{kWWY&sSVz5;j{?<-U1DhD2zqIf*9WPNawA?cmK z?$~IEgT3Bxa2Z1*?L_pN#@PS_5GH^e;A^)Xz|(fb&KaC#gC~3gR1DsYlm8N&swQl} z1F0xI^nv>kv{$ig4oVC?;isSq@So1X>bv<|ER87w(7!oB_2 zquMWUV}dp;=7GAxhLH9m27s^KlR&4Vy$Y6#^HrcHYz0blL9l>%6_f|Qc9#O}O1l>$ z@;F-wJ>db+R# zIGYBZa2IGkc*5hLrQi!+MP^=8gAd-4W1oX){)kudSdy1`jQ>tBlgpHm?Eq&#yr~A0 zDj?p7Ge$1LyAJ8$78vVOjtYUh-&T6OZpZFBtn8h@XWzwDfK1eGv4E$om%&GnS@49@ zT=?UIfLHF=aZovUyiUh%KdN}VLdPD}JYK(JKWn}UIOdqL!TWt|ujcDEYcTbO?!*V! zLWEC3yl2R&!4u|wh=B|nH!i$P$j*U$R4u@9#}$v457|4K$D50+*GEck13s(yQXpQ< zGvc~|cYds#;!R41?*Wa{WMLC2&Ko;3qIHQQ!^D z6Gr}`^mzN14IthHBVz8;0m;BuL3D2>Jg@bH)tV!@XZB9%c8l(fw(j!C zLvGO{sCo~3NmjW From 9d266ce281bcbca6ea8fc66ff3081ff10c1db499 Mon Sep 17 00:00:00 2001 From: MrWint Date: Sun, 26 May 2019 19:19:39 +0200 Subject: [PATCH 27/38] libgambatte: hang on invalid opcode --- libgambatte/src/cpu.cpp | 68 ++++++++++++++++++++++++++++--------- output/dll/libgambatte.dll | Bin 168448 -> 168448 bytes 2 files changed, 52 insertions(+), 16 deletions(-) diff --git a/libgambatte/src/cpu.cpp b/libgambatte/src/cpu.cpp index 4032210208..0e820e1dec 100644 --- a/libgambatte/src/cpu.cpp +++ b/libgambatte/src/cpu.cpp @@ -1701,9 +1701,14 @@ void CPU::process(unsigned long const cycles) { break; - case 0xD3: /*doesn't exist*/ - skip_ = true; + case 0xD3: // not specified. should freeze. mem_.di(); + cycleCounter = mem_.stop(cycleCounter); + + if (cycleCounter < mem_.nextEventTime()) { + unsigned long cycles = mem_.nextEventTime() - cycleCounter; + cycleCounter += cycles + (-cycles & 3); + } break; // call nc,nn (24;12 cycles): @@ -1770,9 +1775,14 @@ void CPU::process(unsigned long const cycles) { break; - case 0xDB: /*doesn't exist*/ - skip_ = true; + case 0xDB: // not specified. should freeze. mem_.di(); + cycleCounter = mem_.stop(cycleCounter); + + if (cycleCounter < mem_.nextEventTime()) { + unsigned long cycles = mem_.nextEventTime() - cycleCounter; + cycleCounter += cycles + (-cycles & 3); + } break; // call z,nn (24;12 cycles): @@ -1787,10 +1797,14 @@ void CPU::process(unsigned long const cycles) { } break; - - case 0xDD: /*doesn't exist*/ - skip_ = true; + case 0xDD: // not specified. should freeze. mem_.di(); + cycleCounter = mem_.stop(cycleCounter); + + if (cycleCounter < mem_.nextEventTime()) { + unsigned long cycles = mem_.nextEventTime() - cycleCounter; + cycleCounter += cycles + (-cycles & 3); + } break; case 0xDE: @@ -1826,10 +1840,16 @@ void CPU::process(unsigned long const cycles) { case 0xE2: FF_WRITE(c, a); break; - case 0xE3: - case 0xE4: /*doesn't exist*/ - skip_ = true; + + case 0xE3: + case 0xE4: // not specified. should freeze. mem_.di(); + cycleCounter = mem_.stop(cycleCounter); + + if (cycleCounter < mem_.nextEventTime()) { + unsigned long cycles = mem_.nextEventTime() - cycleCounter; + cycleCounter += cycles + (-cycles & 3); + } break; case 0xE5: @@ -1874,11 +1894,17 @@ void CPU::process(unsigned long const cycles) { } break; + case 0xEB: case 0xEC: - case 0xED: /*doesn't exist*/ - skip_ = true; + case 0xED: // not specified. should freeze. mem_.di(); + cycleCounter = mem_.stop(cycleCounter); + + if (cycleCounter < mem_.nextEventTime()) { + unsigned long cycles = mem_.nextEventTime() - cycleCounter; + cycleCounter += cycles + (-cycles & 3); + } break; case 0xEE: @@ -1927,9 +1953,14 @@ void CPU::process(unsigned long const cycles) { mem_.di(); break; - case 0xF4: /*doesn't exist*/ - skip_ = true; + case 0xF4: // not specified. should freeze. mem_.di(); + cycleCounter = mem_.stop(cycleCounter); + + if (cycleCounter < mem_.nextEventTime()) { + unsigned long cycles = mem_.nextEventTime() - cycleCounter; + cycleCounter += cycles + (-cycles & 3); + } break; case 0xF5: @@ -1994,9 +2025,14 @@ void CPU::process(unsigned long const cycles) { break; case 0xFC: - case 0xFD: /*doesn't exist*/ - skip_ = true; + case 0xFD: // not specified. should freeze mem_.di(); + cycleCounter = mem_.stop(cycleCounter); + + if (cycleCounter < mem_.nextEventTime()) { + unsigned long cycles = mem_.nextEventTime() - cycleCounter; + cycleCounter += cycles + (-cycles & 3); + } break; case 0xFE: { diff --git a/output/dll/libgambatte.dll b/output/dll/libgambatte.dll index 18ca122371aa4d7b04eb68c038c45c11b69fbab5..cf409b31f31fb4773f78ea157fbbd9d3d657386d 100644 GIT binary patch delta 27536 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 delta 27511 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 From ca31e9ad72949ef9218bba546b7b79885c75abb6 Mon Sep 17 00:00:00 2001 From: MrWint Date: Sun, 26 May 2019 19:29:17 +0200 Subject: [PATCH 28/38] libgambatte: Non-atomic interrupt handling --- libgambatte/src/initstate.cpp | 4 ++-- libgambatte/src/memory.cpp | 30 ++++++++++++++++-------------- libgambatte/src/memory.h | 4 ---- libgambatte/src/video.cpp | 17 ----------------- libgambatte/src/video.h | 1 - output/dll/libgambatte.dll | Bin 168448 -> 168448 bytes 6 files changed, 18 insertions(+), 38 deletions(-) diff --git a/libgambatte/src/initstate.cpp b/libgambatte/src/initstate.cpp index ce0b7b0905..d147eb03d6 100644 --- a/libgambatte/src/initstate.cpp +++ b/libgambatte/src/initstate.cpp @@ -1107,9 +1107,9 @@ static void setInitialDmgIoamhram(unsigned char ioamhram[]) { static unsigned char const ffxxDump[0x100] = { 0xCF, 0x00, 0x7E, 0xFF, 0xD3, 0x00, 0x00, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, - 0x80, 0xBF, 0xF3, 0xFF, 0xBF, 0xFF, 0x3F, 0x00, + 0x80, 0x3F, 0x00, 0xFF, 0xBF, 0xFF, 0x3F, 0x00, 0xFF, 0xBF, 0x7F, 0xFF, 0x9F, 0xFF, 0xBF, 0xFF, - 0xFF, 0x00, 0x00, 0xBF, 0x77, 0xF3, 0xF1, 0xFF, + 0xFF, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x70, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x71, 0x72, 0xD5, 0x91, 0x58, 0xBB, 0x2A, 0xFA, 0xCF, 0x3C, 0x54, 0x75, 0x48, 0xCF, 0x8F, 0xD9, diff --git a/libgambatte/src/memory.cpp b/libgambatte/src/memory.cpp index 54eea33844..5c85ccebeb 100644 --- a/libgambatte/src/memory.cpp +++ b/libgambatte/src/memory.cpp @@ -295,33 +295,35 @@ unsigned long Memory::event(unsigned long cc) { } if (ime()) { - unsigned address; - cc += 12; - lcd_.update(cc); - sp_ = (sp_ - 2) & 0xFFFF; - write(sp_ + 1, pc_ >> 8, cc); - unsigned ie = intreq_.iereg(); + + sp_ = (sp_ - 1) & 0xFFFF; + write(sp_, pc_ >> 8, cc); cc += 4; - lcd_.update(cc); + + updateIrqs(cc); + unsigned const pendingIrqs = intreq_.pendingIrqs(); + + sp_ = (sp_ - 1) & 0xFFFF; write(sp_, pc_ & 0xFF, cc); - const unsigned pendingIrqs = ie & intreq_.ifreg(); - cc += 4; - lcd_.update(cc); - const unsigned n = pendingIrqs & -pendingIrqs; + cc += 2; + unsigned const n = pendingIrqs & -pendingIrqs; + unsigned address; if (n == 0) { address = 0; - } - else if (n < 8) { - static const unsigned char lut[] = { 0x40, 0x48, 0x48, 0x50 }; + } else if (n <= 4) { + static unsigned char const lut[] = { 0x40, 0x48, 0x48, 0x50 }; address = lut[n-1]; } else address = 0x50 + n; intreq_.ackIrq(n); + + cc += 2; + pc_ = address; } diff --git a/libgambatte/src/memory.h b/libgambatte/src/memory.h index 4999e6d34c..c05b4cca05 100644 --- a/libgambatte/src/memory.h +++ b/libgambatte/src/memory.h @@ -268,10 +268,6 @@ public: void setCgbPalette(unsigned *lut); - void blackScreen() { - lcd_.blackScreen(); - } - int linkStatus(int which); private: diff --git a/libgambatte/src/video.cpp b/libgambatte/src/video.cpp index e96f285b2d..b8715f4c18 100644 --- a/libgambatte/src/video.cpp +++ b/libgambatte/src/video.cpp @@ -177,23 +177,6 @@ void LCD::copyCgbPalettesToDmg() { } } -void LCD::blackScreen() { - if (ppu_.cgb()) { - for (unsigned i = 0; i < 8 * 8; i += 2) { - ppu_.bgPalette()[i >> 1] = 0; - ppu_.spPalette()[i >> 1] = 0; - } - } - else { - for (unsigned i = 0; i < 4; i++) { - dmgColorsRgb32_[i] = 0; - } - for (unsigned i = 0; i < 8; i++) { - dmgColorsRgb32_[i + 4] = 0; - } - } -} - namespace { template diff --git a/libgambatte/src/video.h b/libgambatte/src/video.h index c13ca1ed53..b37beaa3e8 100644 --- a/libgambatte/src/video.h +++ b/libgambatte/src/video.h @@ -57,7 +57,6 @@ public: void setLayers(unsigned mask) { ppu_.setLayers(mask); } void setCgb(bool cgb); void copyCgbPalettesToDmg(); - void blackScreen(); int debugGetLY() const { return ppu_.lyCounter().ly(); } diff --git a/output/dll/libgambatte.dll b/output/dll/libgambatte.dll index cf409b31f31fb4773f78ea157fbbd9d3d657386d..1fb295ab0589a7be30a5dc37673499be7167f9e4 100644 GIT binary patch delta 21623 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 C|74#4 delta 21679 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 From d8c238c9fb5030f4bd3a2fa8f0837c9f9178457d Mon Sep 17 00:00:00 2001 From: MrWint Date: Sun, 26 May 2019 19:40:19 +0200 Subject: [PATCH 29/38] libgambatte: timar quirk accuracy --- libgambatte/src/memory.cpp | 6 +++--- libgambatte/src/tima.cpp | 4 ++-- libgambatte/src/tima.h | 2 +- output/dll/libgambatte.dll | Bin 168448 -> 168448 bytes 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/libgambatte/src/memory.cpp b/libgambatte/src/memory.cpp index 5c85ccebeb..7c20121b71 100644 --- a/libgambatte/src/memory.cpp +++ b/libgambatte/src/memory.cpp @@ -337,8 +337,8 @@ unsigned long Memory::stop(unsigned long cc) { cc += 4; if (ioamhram_[0x14D] & isCgb()) { - psg_.generateSamples(cc, isDoubleSpeed()); - lcd_.speedChange(cc); + psg_.generateSamples(cc + 4, isDoubleSpeed()); + lcd_.speedChange((cc + 7) & ~7); ioamhram_[0x14D] ^= 0x81; intreq_.setEventTime(ioamhram_[0x140] & lcdc_en ? lcd_.nextMode1IrqTime() @@ -686,7 +686,7 @@ void Memory::nontrivial_ff_write(unsigned const p, unsigned data, unsigned long break; case 0x07: data |= 0xF8; - tima_.setTac(data, cc, TimaInterruptRequester(intreq_), gbIsCgb_); + tima_.setTac(data, cc, TimaInterruptRequester(intreq_), agbMode_); break; case 0x0F: updateIrqs(cc); diff --git a/libgambatte/src/tima.cpp b/libgambatte/src/tima.cpp index aa4038821b..5b935c8a9c 100644 --- a/libgambatte/src/tima.cpp +++ b/libgambatte/src/tima.cpp @@ -117,7 +117,7 @@ void Tima::setTma(unsigned const data, unsigned long const cc, TimaInterruptRequ tma_ = data; } -void Tima::setTac(unsigned const data, unsigned long const cc, TimaInterruptRequester timaIrq, bool gbIsCgb) { +void Tima::setTac(unsigned const data, unsigned long const cc, TimaInterruptRequester timaIrq, bool agbFlag) { if (tac_ ^ data) { unsigned long nextIrqEventTime = timaIrq.nextIrqEventTime(); @@ -141,7 +141,7 @@ void Tima::setTac(unsigned const data, unsigned long const cc, TimaInterruptRequ if (data & 4) { unsigned long diff = cc - basetime_; - if (gbIsCgb) { + if (agbFlag) { if (((diff >> (timaClock[tac_ & 3] - 1)) & 1) == 1 && ((diff >> (timaClock[data & 3] - 1)) & 1) == 0) tima_++; } diff --git a/libgambatte/src/tima.h b/libgambatte/src/tima.h index 55bfe849ad..d1db0854b9 100644 --- a/libgambatte/src/tima.h +++ b/libgambatte/src/tima.h @@ -41,7 +41,7 @@ public: void resetCc(unsigned long oldCc, unsigned long newCc, TimaInterruptRequester timaIrq); void setTima(unsigned tima, unsigned long cc, TimaInterruptRequester timaIrq); void setTma(unsigned tma, unsigned long cc, TimaInterruptRequester timaIrq); - void setTac(unsigned tac, unsigned long cc, TimaInterruptRequester timaIrq, bool gbIsCgb); + void setTac(unsigned tac, unsigned long cc, TimaInterruptRequester timaIrq, bool agbFlag); void resTac(unsigned long cc, TimaInterruptRequester timaIrq); unsigned tima(unsigned long cc); void doIrqEvent(TimaInterruptRequester timaIrq); diff --git a/output/dll/libgambatte.dll b/output/dll/libgambatte.dll index 1fb295ab0589a7be30a5dc37673499be7167f9e4..386ccdb45ad7526c2209a24dfee904bf9bb69626 100644 GIT binary patch delta 174 zcmZoz!qu>ZYXKvZ-TBQ-jJ%7q8kravT==&&$BQvA^oFx|G#)%4&cyKIT0Mx%-u&o? zM{l0*sl>6Si%iu#J&rf|cF*S1}Ag05pri8;I8= zGcl|Q1@RadU~)V_wg?da0LuM<%S}J$#v}!F%=TAqOxO4rwYMJ(Wh&NWZYXKwE!84ng7Kp1OlQtaKlp@Ec(cN?d;!L+<_X)jPuRxDGQsNLnO89kKmas}!5fIzBr`Fr z2?g;O7+`WdK(+`F{{YJUfXhuk=f)%jbjtQuZcNws7`3(^3}q_TWaQXxTF-P^2mtnW BI3NH3 From 7e851b19f3ec58ae5759a04e55699b44ad0340ba Mon Sep 17 00:00:00 2001 From: MrWint Date: Sun, 26 May 2019 20:11:50 +0200 Subject: [PATCH 30/38] Gambatte core: unify DMG and CGB bios handling --- .../Consoles/Nintendo/Gameboy/Gambatte.cs | 21 ++++-------- .../Consoles/Nintendo/Gameboy/LibGambatte.cs | 14 ++------ libgambatte/include/gambatte.h | 3 +- libgambatte/src/cinterface.cpp | 10 ++---- libgambatte/src/cpu.h | 3 +- libgambatte/src/gambatte.cpp | 9 ++--- libgambatte/src/memory.cpp | 4 +++ libgambatte/src/memory.h | 31 +++++++++--------- output/dll/libgambatte.dll | Bin 168448 -> 168448 bytes 9 files changed, 35 insertions(+), 60 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs index ad53a9b698..1790f04703 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs @@ -95,28 +95,21 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy throw new InvalidOperationException($"{nameof(LibGambatte.gambatte_load)}() returned non-zero (is this not a gb or gbc rom?)"); } + byte[] Bios; if ((flags & LibGambatte.LoadFlags.FORCE_DMG) == LibGambatte.LoadFlags.FORCE_DMG) { - byte[] Bios = comm.CoreFileProvider.GetFirmware("GB", "World", true, "BIOS Not Found, Cannot Load"); - + Bios = comm.CoreFileProvider.GetFirmware("GB", "World", true, "BIOS Not Found, Cannot Load"); IsCgb = false; - - if (LibGambatte.gambatte_loaddmgbios(GambatteState, Bios) != 0) - { - throw new InvalidOperationException($"{nameof(LibGambatte.gambatte_loaddmgbios)}() returned non-zero (bios error)"); - } } else { - byte[] Bios = comm.CoreFileProvider.GetFirmware("GBC", "World", true, "BIOS Not Found, Cannot Load"); - + Bios = comm.CoreFileProvider.GetFirmware("GBC", "World", true, "BIOS Not Found, Cannot Load"); IsCgb = true; - - if (LibGambatte.gambatte_loadgbcbios(GambatteState, Bios) != 0) - { - throw new InvalidOperationException($"{nameof(LibGambatte.gambatte_loadgbcbios)}() returned non-zero (bios error)"); - } } + if (LibGambatte.gambatte_loadbios(GambatteState, Bios, (uint)Bios.Length) != 0) + { + throw new InvalidOperationException($"{nameof(LibGambatte.gambatte_loadbios)}() returned non-zero (bios error)"); + } // set real default colors (before anyone mucks with them at all) PutSettings((GambatteSettings)settings ?? new GambatteSettings()); diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibGambatte.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibGambatte.cs index 1a05daea20..06cb95ece3 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibGambatte.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibGambatte.cs @@ -60,22 +60,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy public static extern int gambatte_load(IntPtr core, byte[] romdata, uint length, long now, LoadFlags flags, uint div); ///

- /// Load GB BIOS image. + /// Load GB(C) BIOS image. /// /// opaque state pointer /// the bios data, can be disposed of once this function returns + /// length of romdata in bytes /// 0 on success, negative value on failure. [DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)] - public static extern int gambatte_loaddmgbios(IntPtr core, byte[] biosdata); - - /// - /// Load GBC BIOS image. - /// - /// opaque state pointer - /// the bios data, can be disposed of once this function returns - /// 0 on success, negative value on failure. - [DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)] - public static extern int gambatte_loadgbcbios(IntPtr core, byte[] biosdata); + public static extern int gambatte_loadbios(IntPtr core, byte[] biosdata, uint length); /// /// Emulates until at least 'samples' stereo sound samples are produced in the supplied buffer, diff --git a/libgambatte/include/gambatte.h b/libgambatte/include/gambatte.h index 890574b48c..9d51b5edce 100644 --- a/libgambatte/include/gambatte.h +++ b/libgambatte/include/gambatte.h @@ -66,8 +66,7 @@ public: */ LoadRes load(char const *romfiledata, unsigned romfilelength, std::uint32_t now, unsigned flags, unsigned div); - int loadGBCBios(char const* biosfiledata); - int loadDMGBios(char const* biosfiledata); + int loadBios(char const* biosfiledata, std::size_t size); /** * Emulates until at least 'samples' audio samples are produced in the diff --git a/libgambatte/src/cinterface.cpp b/libgambatte/src/cinterface.cpp index d3bde8fc74..3cc461460d 100644 --- a/libgambatte/src/cinterface.cpp +++ b/libgambatte/src/cinterface.cpp @@ -35,15 +35,9 @@ GBEXPORT int gambatte_load(GB *g, char const *romfiledata, unsigned romfilelengt return ret; } -GBEXPORT int gambatte_loadgbcbios(GB* g, char const* biosfiledata) +GBEXPORT int gambatte_loadbios(GB* g, char const* biosfiledata, unsigned size) { - int ret = g->loadGBCBios(biosfiledata); - return ret; -} - -GBEXPORT int gambatte_loaddmgbios(GB* g, char const* biosfiledata) -{ - int ret = g->loadDMGBios(biosfiledata); + int ret = g->loadBios(biosfiledata, size); return ret; } diff --git a/libgambatte/src/cpu.h b/libgambatte/src/cpu.h index 784646c5bb..5349514e05 100644 --- a/libgambatte/src/cpu.h +++ b/libgambatte/src/cpu.h @@ -95,8 +95,7 @@ public: mem_.setCgbPalette(lut); } - unsigned char* cgbBiosBuffer() { return mem_.cgbBiosBuffer(); } - unsigned char* dmgBiosBuffer() { return mem_.dmgBiosBuffer(); } + void setBios(char const *buffer, std::size_t size) { mem_.setBios(buffer, size); } bool gbIsCgb() { return mem_.gbIsCgb(); } unsigned char externalRead(unsigned short addr) {return mem_.peek(addr); } diff --git a/libgambatte/src/gambatte.cpp b/libgambatte/src/gambatte.cpp index 691d64dd29..f182507608 100644 --- a/libgambatte/src/gambatte.cpp +++ b/libgambatte/src/gambatte.cpp @@ -148,13 +148,8 @@ LoadRes GB::load(char const *romfiledata, unsigned romfilelength, const std::uin return loadres; } -int GB::loadGBCBios(char const* biosfiledata) { - memcpy(p_->cpu.cgbBiosBuffer(), biosfiledata, 0x900); - return 0; -} - -int GB::loadDMGBios(char const* biosfiledata) { - memcpy(p_->cpu.dmgBiosBuffer(), biosfiledata, 0x100); +int GB::loadBios(char const* biosfiledata, std::size_t size) { + p_->cpu.setBios(biosfiledata, size); return 0; } diff --git a/libgambatte/src/memory.cpp b/libgambatte/src/memory.cpp index 7c20121b71..48c8bfd948 100644 --- a/libgambatte/src/memory.cpp +++ b/libgambatte/src/memory.cpp @@ -30,6 +30,7 @@ Memory::Memory(unsigned short &sp, unsigned short &pc) , execCallback_(0) , cdCallback_(0) , linkCallback_(0) +, bios_(0) , getInput_(0) , divLastUpdate_(0) , lastOamDmaUpdate_(disabled_time) @@ -47,6 +48,9 @@ Memory::Memory(unsigned short &sp, unsigned short &pc) intreq_.setEventTime(144 * 456ul); intreq_.setEventTime(0); } +Memory::~Memory() { + delete []bios_; +} void Memory::setStatePtrs(SaveState &state) { state.mem.ioamhram.set(ioamhram_, sizeof ioamhram_); diff --git a/libgambatte/src/memory.h b/libgambatte/src/memory.h index c05b4cca05..64716ba79c 100644 --- a/libgambatte/src/memory.h +++ b/libgambatte/src/memory.h @@ -34,6 +34,7 @@ class FilterInfo; class Memory { public: explicit Memory(unsigned short &sp, unsigned short &pc); + ~Memory(); bool loaded() const { return cart_.loaded(); } unsigned char curRomBank() const { return cart_.curRomBank(); } @@ -46,8 +47,12 @@ public: void saveSavedata(char *dest) { cart_.saveSavedata(dest); } void updateInput(); - unsigned char* cgbBiosBuffer() { return (unsigned char*)cgbBios_; } - unsigned char* dmgBiosBuffer() { return (unsigned char*)dmgBios_; } + void setBios(char const *buffer, std::size_t size) { + delete []bios_; + bios_ = new unsigned char[size]; + memcpy(bios_, buffer, size); + biosSize_ = size; + } bool gbIsCgb() { return gbIsCgb_; } bool getMemoryArea(int which, unsigned char **data, int *length); // { return cart.getMemoryArea(which, data, length); } @@ -72,13 +77,10 @@ public: void di() { intreq_.di(); } unsigned readBios(unsigned p) { - if (gbIsCgb_) { - if (agbMode_ && p >= 0xF3 && p < 0x100) { - return (agbOverride[p - 0xF3] + cgbBios_[p]) & 0xFF; - } - return cgbBios_[p]; + if(agbMode_ && p >= 0xF3 && p < 0x100) { + return (agbOverride[p-0xF3] + bios_[p]) & 0xFF; } - return dmgBios_[p]; + return bios_[p]; } unsigned ff_read(unsigned p, unsigned long cc) { @@ -146,9 +148,8 @@ public: unsigned read(unsigned p, unsigned long cc) { if (readCallback_) readCallback_(p, (cc - basetime_) >> 1); - bool biosRange = ((!gbIsCgb_ && p < 0x100) || (gbIsCgb_ && p < 0x900 && (p < 0x100 || p >= 0x200))); if(biosMode_) { - if (biosRange) + if (p < biosSize_ && !(p >= 0x100 && p < 0x200)) return readBios(p); } else if(cdCallback_) { @@ -162,9 +163,8 @@ public: unsigned read_excb(unsigned p, unsigned long cc, bool first) { if (execCallback_) execCallback_(p, (cc - basetime_) >> 1); - bool biosRange = ((!gbIsCgb_ && p < 0x100) || (gbIsCgb_ && p < 0x900 && (p < 0x100 || p >= 0x200))); if (biosMode_) { - if(biosRange) + if(p < biosSize_ && !(p >= 0x100 && p < 0x200)) return readBios(p); } else if(cdCallback_) { @@ -176,8 +176,7 @@ public: } unsigned peek(unsigned p) { - bool biosRange = ((!gbIsCgb_ && p < 0x100) || (gbIsCgb_ && p < 0x900 && (p < 0x100 || p >= 0x200))); - if (biosMode_ && biosRange) { + if (biosMode_ && p < biosSize_ && !(p >= 0x100 && p < 0x200)) { return readBios(p); } return cart_.rmem(p >> 12) ? cart_.rmem(p >> 12)[p] : nontrivial_peek(p); @@ -273,8 +272,8 @@ public: private: Cartridge cart_; unsigned char ioamhram_[0x200]; - unsigned char cgbBios_[0x900]; - unsigned char dmgBios_[0x100]; + unsigned char *bios_; + std::size_t biosSize_; unsigned (*getInput_)(); unsigned long divLastUpdate_; unsigned long lastOamDmaUpdate_; diff --git a/output/dll/libgambatte.dll b/output/dll/libgambatte.dll index 386ccdb45ad7526c2209a24dfee904bf9bb69626..b7fcac1c4d94d85d0053d469265ca1ab180ba0a7 100644 GIT binary patch delta 59221 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 S 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 Ze7*u-iO>6b-s}0VZ+hJ)GS~AB{|hOxmw^BP delta 59406 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 z Date: Sun, 26 May 2019 20:24:58 +0200 Subject: [PATCH 31/38] libgambatte: fix interrupt execution anatomy --- libgambatte/src/cpu.cpp | 85 +++++++++++++++++++------------------ libgambatte/src/memory.cpp | 13 +++--- libgambatte/src/video.cpp | 6 +-- output/dll/libgambatte.dll | Bin 168448 -> 167936 bytes 4 files changed, 55 insertions(+), 49 deletions(-) diff --git a/libgambatte/src/cpu.cpp b/libgambatte/src/cpu.cpp index 0e820e1dec..58444ab32a 100644 --- a/libgambatte/src/cpu.cpp +++ b/libgambatte/src/cpu.cpp @@ -277,8 +277,8 @@ void CPU::loadState(SaveState const &state) { // push rr (16 cycles): // Push value of register pair onto stack: #define push_rr(r1, r2) do { \ - PUSH(r1, r2); \ cycleCounter += 4; \ + PUSH(r1, r2); \ } while (0) // pop rr (12 cycles): @@ -461,8 +461,9 @@ void CPU::loadState(SaveState const &state) { // rst n (16 Cycles): // Push present address onto stack, jump to address n (one of 00h,08h,10h,18h,20h,28h,30h,38h): #define rst_n(n) do { \ + cycleCounter += 4; \ PUSH(pc >> 8, pc & 0xFF); \ - PC_MOD(n); \ + pc = n; \ } while (0) // ret (16 cycles): @@ -475,9 +476,10 @@ void CPU::loadState(SaveState const &state) { void CPU::process(unsigned long const cycles) { mem_.setEndtime(cycleCounter_, cycles); - hitInterruptAddress = 0; mem_.updateInput(); + hitInterruptAddress = -1; + //unsigned char a = a_; unsigned long cycleCounter = cycleCounter_; @@ -490,50 +492,51 @@ void CPU::process(unsigned long const cycles) { cycleCounter += cycles + (-cycles & 3); } } else while (cycleCounter < mem_.nextEventTime()) { - unsigned char opcode = 0x00; + unsigned char opcode; - int FullPC = pc; +#ifdef DLLABLES + for (int i = 0; i < numInterruptAddresses; ++i) { + if (pc == (interruptAddresses[i] & 0xFFFF)) { + unsigned bank = interruptAddresses[i] >> 16; - if (pc >= 0x4000 && pc <= 0x7FFF) - FullPC |= mem_.curRomBank() << 16; - - for (int i = 0; i < numInterruptAddresses; i++) { - if (FullPC == interruptAddresses[i]) { - hitInterruptAddress = interruptAddresses[i]; - mem_.setEndtime(cycleCounter, 0); - break; + if (!bank || bank == mem_.curRomBank()) { + hitInterruptAddress = interruptAddresses[i]; + mem_.setEndtime(cycleCounter, 0); + break; + } } } - if (!hitInterruptAddress) - { - if (tracecallback) { - int result[14]; - result[0] = cycleCounter; - result[1] = pc; - result[2] = sp; - result[3] = a; - result[4] = b; - result[5] = c; - result[6] = d; - result[7] = e; - result[8] = toF(hf2, cf, zf); - result[9] = h; - result[10] = l; - result[11] = skip_; - PC_READ_FIRST(opcode); - result[12] = opcode; - result[13] = mem_.debugGetLY(); - tracecallback((void *)result); - } - else { - PC_READ_FIRST(opcode); - } + if (hitInterruptAddress != -1) + break; +#endif - if (skip_) { - pc = (pc - 1) & 0xFFFF; - skip_ = false; - } + if (tracecallback) { + int result[14]; + result[0] = cycleCounter; + result[1] = pc; + result[2] = sp; + result[3] = a; + result[4] = b; + result[5] = c; + result[6] = d; + result[7] = e; + result[8] = toF(hf2, cf, zf); + result[9] = h; + result[10] = l; + result[11] = skip_; + PC_READ_FIRST(opcode); + result[12] = opcode; + result[13] = mem_.debugGetLY(); + tracecallback((void *)result); + } + else { + PC_READ_FIRST(opcode); + } + + if (skip_) { + pc = (pc - 1) & 0xFFFF; + skip_ = false; } switch (opcode) { diff --git a/libgambatte/src/memory.cpp b/libgambatte/src/memory.cpp index 48c8bfd948..0e31f6141a 100644 --- a/libgambatte/src/memory.cpp +++ b/libgambatte/src/memory.cpp @@ -48,6 +48,7 @@ Memory::Memory(unsigned short &sp, unsigned short &pc) intreq_.setEventTime(144 * 456ul); intreq_.setEventTime(0); } + Memory::~Memory() { delete []bios_; } @@ -166,11 +167,11 @@ unsigned long Memory::event(unsigned long cc) { switch (intreq_.minEventId()) { case intevent_unhalt: + intreq_.unhalt(); + intreq_.setEventTime(disabled_time); nontrivial_ff_write(0xFF04, 0, cc); pc_ = (pc_ + 1) & 0xFFFF; cc += 4; - intreq_.unhalt(); - intreq_.setEventTime(disabled_time); break; case intevent_end: intreq_.setEventTime(disabled_time - 1); @@ -324,6 +325,7 @@ unsigned long Memory::event(unsigned long cc) { } else address = 0x50 + n; + updateIrqs(cc); intreq_.ackIrq(n); cc += 2; @@ -980,7 +982,6 @@ void Memory::nontrivial_ff_write(unsigned const p, unsigned data, unsigned long if(cgbSwitching_) { lcd_.copyCgbPalettesToDmg(); lcd_.setCgb(false); - cgbSwitching_ = false; } return; case 0x51: @@ -1050,8 +1051,10 @@ void Memory::nontrivial_ff_write(unsigned const p, unsigned data, unsigned long return; case 0x6C: - ioamhram_[0x16C] = data | 0xFE; - cgbSwitching_ = true; + if (isCgb()) { + ioamhram_[0x16C] = data | 0xFE; + cgbSwitching_ = true; + } return; case 0x70: diff --git a/libgambatte/src/video.cpp b/libgambatte/src/video.cpp index b8715f4c18..becb09a7b9 100644 --- a/libgambatte/src/video.cpp +++ b/libgambatte/src/video.cpp @@ -139,7 +139,7 @@ void LCD::loadState(SaveState const &state, unsigned char const *const oamram) { SpriteMapper::schedule(ppu_.lyCounter(), ppu_.now())); eventTimes_.setm(lycIrq_.time()); eventTimes_.setm( - ppu_.lyCounter().nextFrameCycle(144 * 456, ppu_.now())); + ppu_.lyCounter().nextFrameCycle(144 * 456, ppu_.now()) - 2); eventTimes_.setm( mode2IrqSchedule(statReg_, ppu_.lyCounter(), ppu_.now())); eventTimes_.setm(statReg_ & lcdstat_m0irqen @@ -230,7 +230,7 @@ void LCD::speedChange(unsigned long const cc) { eventTimes_.set(ppu_.lyCounter().time()); eventTimes_.setm(SpriteMapper::schedule(ppu_.lyCounter(), cc)); eventTimes_.setm(lycIrq_.time()); - eventTimes_.setm(ppu_.lyCounter().nextFrameCycle(144 * 456, cc)); + eventTimes_.setm(ppu_.lyCounter().nextFrameCycle(144 * 456, cc) - 2); eventTimes_.setm(mode2IrqSchedule(statReg_, ppu_.lyCounter(), cc)); if (eventTimes_(memevent_m0irq) != disabled_time @@ -458,7 +458,7 @@ void LCD::lcdcChange(unsigned const data, unsigned long const cc) { SpriteMapper::schedule(ppu_.lyCounter(), cc)); eventTimes_.setm(lycIrq_.time()); eventTimes_.setm( - ppu_.lyCounter().nextFrameCycle(144 * 456, cc)); + ppu_.lyCounter().nextFrameCycle(144 * 456, cc) - 2); eventTimes_.setm( mode2IrqSchedule(statReg_, ppu_.lyCounter(), cc)); if (statReg_ & lcdstat_m0irqen) { diff --git a/output/dll/libgambatte.dll b/output/dll/libgambatte.dll index b7fcac1c4d94d85d0053d469265ca1ab180ba0a7..984932df0eef38e880ecc18df747d00fc5afe580 100644 GIT binary patch delta 37923 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 Date: Tue, 28 May 2019 00:26:59 +0200 Subject: [PATCH 32/38] More consistency, restore layer masks --- libgambatte/include/gambatte.h | 4 +- libgambatte/src/cinterface.cpp | 146 +++++++++++------------- libgambatte/src/cinterface.h | 18 +++ libgambatte/src/cpu.cpp | 2 +- libgambatte/src/gambatte.cpp | 8 +- libgambatte/src/mem/cartridge.cpp | 5 - libgambatte/src/mem/memptrs.cpp | 27 ++--- libgambatte/src/memory.cpp | 15 +-- libgambatte/src/memory.h | 4 +- libgambatte/src/newstate.cpp | 6 +- libgambatte/src/newstate.h | 28 ++--- libgambatte/src/video.cpp | 3 +- libgambatte/src/video.h | 1 - libgambatte/src/video/ppu.cpp | 14 +-- libgambatte/src/video/ppu.h | 2 +- libgambatte/src/video/sprite_mapper.cpp | 1 - output/dll/libgambatte.dll | Bin 167936 -> 168448 bytes 17 files changed, 129 insertions(+), 155 deletions(-) diff --git a/libgambatte/include/gambatte.h b/libgambatte/include/gambatte.h index 9d51b5edce..8bebff11bc 100644 --- a/libgambatte/include/gambatte.h +++ b/libgambatte/include/gambatte.h @@ -66,7 +66,7 @@ public: */ LoadRes load(char const *romfiledata, unsigned romfilelength, std::uint32_t now, unsigned flags, unsigned div); - int loadBios(char const* biosfiledata, std::size_t size); + int loadBios(char const *biosfiledata, std::size_t size); /** * Emulates until at least 'samples' audio samples are produced in the @@ -143,7 +143,7 @@ public: bool getMemoryArea(int which, unsigned char **data, int *length); /** ROM header title of currently loaded ROM image. */ - const std::string romTitle() const; + std::string const romTitle() const; unsigned char externalRead(unsigned short addr); void externalWrite(unsigned short addr, unsigned char val); diff --git a/libgambatte/src/cinterface.cpp b/libgambatte/src/cinterface.cpp index 3cc461460d..c342d81418 100644 --- a/libgambatte/src/cinterface.cpp +++ b/libgambatte/src/cinterface.cpp @@ -1,200 +1,183 @@ +// +// Copyright (C) 2007 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + #include "cinterface.h" #include "gambatte.h" #include #include #include "newstate.h" -using namespace gambatte; - // new is actually called in a few different places, so replace all of them for determinism guarantees -void *operator new(std::size_t n) -{ +void *operator new(std::size_t n) { void *p = std::malloc(n); std::memset(p, 0, n); return p; } -void operator delete(void *p) -{ +void operator delete(void *p) { std::free(p); } -GBEXPORT GB *gambatte_create() -{ +namespace { + +using namespace gambatte; + + GBEXPORT GB * gambatte_create() { return new GB(); } -GBEXPORT void gambatte_destroy(GB *g) -{ +GBEXPORT void gambatte_destroy(GB *g) { delete g; } -GBEXPORT int gambatte_load(GB *g, char const *romfiledata, unsigned romfilelength, long long now, unsigned flags, unsigned div) -{ - int ret = g->load(romfiledata, romfilelength, now, flags, div); - return ret; +GBEXPORT int gambatte_load(GB *g, char const *romfiledata, unsigned romfilelength, long long now, unsigned flags, unsigned div) { + return g->load(romfiledata, romfilelength, now, flags, div); } -GBEXPORT int gambatte_loadbios(GB* g, char const* biosfiledata, unsigned size) -{ - int ret = g->loadBios(biosfiledata, size); - return ret; +GBEXPORT int gambatte_loadbios(GB *g, char const *biosfiledata, unsigned size) { + return g->loadBios(biosfiledata, size); } -GBEXPORT int gambatte_runfor(GB *g, short *soundbuf, unsigned *samples) -{ +GBEXPORT int gambatte_runfor(GB *g, short *soundbuf, unsigned *samples) { std::size_t sampv = *samples; int ret = g->runFor((unsigned int *) soundbuf, sampv); *samples = sampv; return ret; } -GBEXPORT void gambatte_blitto(GB *g, unsigned int *videobuf, int pitch) -{ +GBEXPORT void gambatte_blitto(GB *g, unsigned int *videobuf, int pitch) { g->blitTo((unsigned int *)videobuf, pitch); } -GBEXPORT void gambatte_setlayers(GB *g, unsigned mask) -{ +GBEXPORT void gambatte_setlayers(GB *g, unsigned mask) { g->setLayers(mask); } -GBEXPORT void gambatte_reset(GB *g, long long now, unsigned div) -{ +GBEXPORT void gambatte_reset(GB *g, long long now, unsigned div) { g->reset(now, div); } -GBEXPORT void gambatte_setdmgpalettecolor(GB *g, unsigned palnum, unsigned colornum, unsigned rgb32) -{ +GBEXPORT void gambatte_setdmgpalettecolor(GB *g, unsigned palnum, unsigned colornum, unsigned rgb32) { g->setDmgPaletteColor(palnum, colornum, rgb32); } -GBEXPORT void gambatte_setcgbpalette(GB *g, unsigned *lut) -{ +GBEXPORT void gambatte_setcgbpalette(GB *g, unsigned *lut) { g->setCgbPalette(lut); } -GBEXPORT void gambatte_setinputgetter(GB *g, unsigned (*getinput)(void)) -{ +GBEXPORT void gambatte_setinputgetter(GB *g, unsigned (*getinput)(void)) { g->setInputGetter(getinput); } -GBEXPORT void gambatte_setreadcallback(GB *g, MemoryCallback callback) -{ +GBEXPORT void gambatte_setreadcallback(GB *g, MemoryCallback callback) { g->setReadCallback(callback); } -GBEXPORT void gambatte_setwritecallback(GB *g, MemoryCallback callback) -{ +GBEXPORT void gambatte_setwritecallback(GB *g, MemoryCallback callback) { g->setWriteCallback(callback); } -GBEXPORT void gambatte_setexeccallback(GB *g, MemoryCallback callback) -{ +GBEXPORT void gambatte_setexeccallback(GB *g, MemoryCallback callback) { g->setExecCallback(callback); } -GBEXPORT void gambatte_setcdcallback(GB *g, CDCallback cdc) -{ +GBEXPORT void gambatte_setcdcallback(GB *g, CDCallback cdc) { g->setCDCallback(cdc); } -GBEXPORT void gambatte_settracecallback(GB *g, void (*callback)(void *)) -{ +GBEXPORT void gambatte_settracecallback(GB *g, void (*callback)(void *)) { g->setTraceCallback(callback); } -GBEXPORT void gambatte_setscanlinecallback(GB *g, void (*callback)(), int sl) -{ +GBEXPORT void gambatte_setscanlinecallback(GB *g, void (*callback)(), int sl) { g->setScanlineCallback(callback, sl); } -GBEXPORT void gambatte_setrtccallback(GB *g, unsigned int (*callback)()) -{ +GBEXPORT void gambatte_setrtccallback(GB *g, unsigned int (*callback)()) { g->setRTCCallback(callback); } -GBEXPORT void gambatte_setlinkcallback(GB *g, void(*callback)()) -{ +GBEXPORT void gambatte_setlinkcallback(GB *g, void(*callback)()) { g->setLinkCallback(callback); } -GBEXPORT int gambatte_iscgb(GB *g) -{ +GBEXPORT int gambatte_iscgb(GB *g) { return g->isCgb(); } -GBEXPORT int gambatte_isloaded(GB *g) -{ +GBEXPORT int gambatte_isloaded(GB *g) { return g->isLoaded(); } -GBEXPORT void gambatte_savesavedata(GB *g, char *dest) -{ +GBEXPORT void gambatte_savesavedata(GB *g, char *dest) { g->saveSavedata(dest); } -GBEXPORT void gambatte_loadsavedata(GB *g, char const *data) -{ +GBEXPORT void gambatte_loadsavedata(GB *g, char const *data) { g->loadSavedata(data); } -GBEXPORT int gambatte_savesavedatalength(GB *g) -{ +GBEXPORT int gambatte_savesavedatalength(GB *g) { return g->saveSavedataLength(); } -GBEXPORT int gambatte_newstatelen(GB *g) -{ +GBEXPORT int gambatte_newstatelen(GB *g) { NewStateDummy dummy; g->SyncState(&dummy); return dummy.GetLength(); } -GBEXPORT int gambatte_newstatesave(GB *g, char *data, int len) -{ +GBEXPORT int gambatte_newstatesave(GB *g, char *data, int len) { NewStateExternalBuffer saver(data, len); g->SyncState(&saver); return !saver.Overflow() && saver.GetLength() == len; } -GBEXPORT int gambatte_newstateload(GB *g, char const *data, int len) -{ +GBEXPORT int gambatte_newstateload(GB *g, char const *data, int len) { NewStateExternalBuffer loader((char *)data, len); g->SyncState(&loader); return !loader.Overflow() && loader.GetLength() == len; } -GBEXPORT void gambatte_newstatesave_ex(GB *g, FPtrs *ff) -{ +GBEXPORT void gambatte_newstatesave_ex(GB *g, FPtrs *ff) { NewStateExternalFunctions saver(ff); g->SyncState(&saver); } -GBEXPORT void gambatte_newstateload_ex(GB *g, FPtrs *ff) -{ +GBEXPORT void gambatte_newstateload_ex(GB *g, FPtrs *ff) { NewStateExternalFunctions loader(ff); g->SyncState(&loader); } -GBEXPORT void gambatte_romtitle(GB *g, char *dest) -{ +GBEXPORT void gambatte_romtitle(GB *g, char *dest) { std::strcpy(dest, g->romTitle().c_str()); } -GBEXPORT int gambatte_getmemoryarea(GB *g, int which, unsigned char **data, int *length) -{ +GBEXPORT int gambatte_getmemoryarea(GB *g, int which, unsigned char **data, int *length) { return g->getMemoryArea(which, data, length); } -GBEXPORT unsigned char gambatte_cpuread(GB *g, unsigned short addr) -{ +GBEXPORT unsigned char gambatte_cpuread(GB *g, unsigned short addr) { return g->externalRead(addr); } -GBEXPORT void gambatte_cpuwrite(GB *g, unsigned short addr, unsigned char val) -{ +GBEXPORT void gambatte_cpuwrite(GB *g, unsigned short addr, unsigned char val) { g->externalWrite(addr, val); } @@ -203,17 +186,16 @@ GBEXPORT int gambatte_linkstatus(GB *g, int which) return g->linkStatus(which); } -GBEXPORT void gambatte_getregs(GB *g, int *dest) -{ +GBEXPORT void gambatte_getregs(GB *g, int *dest) { g->getRegs(dest); } -GBEXPORT void gambatte_setinterruptaddresses(GB *g, int *addrs, int numAddrs) -{ +GBEXPORT void gambatte_setinterruptaddresses(GB *g, int *addrs, int numAddrs) { g->setInterruptAddresses(addrs, numAddrs); } -GBEXPORT int gambatte_gethitinterruptaddress(GB *g) -{ +GBEXPORT int gambatte_gethitinterruptaddress(GB *g) { return g->getHitInterruptAddress(); } + +} diff --git a/libgambatte/src/cinterface.h b/libgambatte/src/cinterface.h index 5e8a0087ec..031392a097 100644 --- a/libgambatte/src/cinterface.h +++ b/libgambatte/src/cinterface.h @@ -1,3 +1,21 @@ +// +// Copyright (C) 2007 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + #ifndef CINTERFACE_H #define CINTERFACE_H diff --git a/libgambatte/src/cpu.cpp b/libgambatte/src/cpu.cpp index 58444ab32a..d086c9842f 100644 --- a/libgambatte/src/cpu.cpp +++ b/libgambatte/src/cpu.cpp @@ -116,7 +116,7 @@ void CPU::loadState(SaveState const &state) { #define hl() ( h << 8 | l ) #define READ(dest, addr) do { (dest) = mem_.read(addr, cycleCounter); cycleCounter += 4; } while (0) -#define PEEK(dest, addr) do { (dest) = mem_.read(addr, cycleCounter); } while(0) +#define PEEK(dest, addr) do { (dest) = mem_.read(addr, cycleCounter); } while (0) #define PC_READ(dest) do { (dest) = mem_.read_excb(pc, cycleCounter, false); pc = (pc + 1) & 0xFFFF; cycleCounter += 4; } while (0) #define PC_READ_FIRST(dest) do { (dest) = mem_.read_excb(pc, cycleCounter, true); pc = (pc + 1) & 0xFFFF; cycleCounter += 4; } while (0) #define FF_READ(dest, addr) do { (dest) = mem_.ff_read(addr, cycleCounter); cycleCounter += 4; } while (0) diff --git a/libgambatte/src/gambatte.cpp b/libgambatte/src/gambatte.cpp index f182507608..d4beddafce 100644 --- a/libgambatte/src/gambatte.cpp +++ b/libgambatte/src/gambatte.cpp @@ -32,7 +32,7 @@ struct GB::Priv { uint_least32_t vbuff[160*144]; - Priv() : loadflags(0), layersMask(LAYER_MASK_BG | LAYER_MASK_OBJ) + Priv() : loadflags(0), layersMask(layer_mask_bg | layer_mask_window | layer_mask_obj) { } }; @@ -223,13 +223,11 @@ void GB::getRegs(int *dest) { p_->cpu.getRegs(dest); } -void GB::setInterruptAddresses(int *addrs, int numAddrs) -{ +void GB::setInterruptAddresses(int *addrs, int numAddrs) { p_->cpu.setInterruptAddresses(addrs, numAddrs); } -int GB::getHitInterruptAddress() -{ +int GB::getHitInterruptAddress() { return p_->cpu.getHitInterruptAddress(); } diff --git a/libgambatte/src/mem/cartridge.cpp b/libgambatte/src/mem/cartridge.cpp index 829330b0a1..5465548f22 100644 --- a/libgambatte/src/mem/cartridge.cpp +++ b/libgambatte/src/mem/cartridge.cpp @@ -532,11 +532,6 @@ static bool presumedMulti64Mbc1(unsigned char const header[], unsigned rombanks) } LoadRes Cartridge::loadROM(char const *romfiledata, unsigned romfilelength, bool const forceDmg, bool const multicartCompat) { - //const std::auto_ptr rom(newFileInstance(romfile)); - - //if (rom->fail()) - // return -1; - enum Cartridgetype { type_plain, type_mbc1, type_mbc2, diff --git a/libgambatte/src/mem/memptrs.cpp b/libgambatte/src/mem/memptrs.cpp index 08a05bf40b..65b5125bbf 100644 --- a/libgambatte/src/mem/memptrs.cpp +++ b/libgambatte/src/mem/memptrs.cpp @@ -45,7 +45,13 @@ MemPtrs::~MemPtrs() { void MemPtrs::reset(unsigned const rombanks, unsigned const rambanks, unsigned const wrambanks) { delete []memchunk_; - memchunk_len = 0x4000 + rombanks * 0x4000ul + 0x4000 + rambanks * 0x2000ul + wrambanks * 0x1000ul + 0x4000; + memchunk_len = + 0x4000 + + rombanks * 0x4000ul + + 0x4000 + + rambanks * 0x2000ul + + wrambanks * 0x1000ul + + 0x4000; memchunk_ = new unsigned char[memchunk_len]; romdata_[0] = romdata(); @@ -163,24 +169,10 @@ void MemPtrs::disconnectOamDmaAreas() { SYNCFUNC(MemPtrs) { - /* - int memchunk_len_old = memchunk_len; - int memchunk_saveoffs_old = memchunk_saveoffs; - int memchunk_savelen_old = memchunk_savelen; - */ - NSS(memchunk_len); NSS(memchunk_saveoffs); NSS(memchunk_savelen); - /* - if (isReader) - { - if (memchunk_len != memchunk_len_old || memchunk_saveoffs != memchunk_saveoffs_old || memchunk_savelen != memchunk_savelen_old) - __debugbreak(); - } - */ - PSS(memchunk_ + memchunk_saveoffs, memchunk_savelen); MSS(rmem_[0x0]); @@ -215,11 +207,6 @@ SYNCFUNC(MemPtrs) MSS(wmem_[0xe]); MSS(rmem_[0xf]); MSS(wmem_[0xf]); - //for (int i = 0; i < 0x10; i++) - //{ - // MSS(rmem_[i]); - // MSS(wmem_[i]); - //} MSS(romdata_[0]); MSS(romdata_[1]); MSS(wramdata_[0]); diff --git a/libgambatte/src/memory.cpp b/libgambatte/src/memory.cpp index 0e31f6141a..2e39855eda 100644 --- a/libgambatte/src/memory.cpp +++ b/libgambatte/src/memory.cpp @@ -1203,24 +1203,21 @@ SYNCFUNC(Memory) NSS(ioamhram_); NSS(divLastUpdate_); NSS(lastOamDmaUpdate_); - NSS(biosMode_); - NSS(cgbSwitching_); - NSS(agbMode_); - NSS(gbIsCgb_); - NSS(stopped_); - NSS(halttime_); - SSS(intreq_); SSS(tima_); SSS(lcd_); SSS(psg_); - NSS(dmaSource_); NSS(dmaDestination_); NSS(oamDmaPos_); NSS(serialCnt_); NSS(blanklcd_); - + NSS(biosMode_); + NSS(cgbSwitching_); + NSS(agbMode_); + NSS(gbIsCgb_); + NSS(halttime_); + NSS(stopped_); NSS(LINKCABLE_); NSS(linkClockTrigger_); } diff --git a/libgambatte/src/memory.h b/libgambatte/src/memory.h index 64716ba79c..051a5ea904 100644 --- a/libgambatte/src/memory.h +++ b/libgambatte/src/memory.h @@ -37,7 +37,7 @@ public: ~Memory(); bool loaded() const { return cart_.loaded(); } - unsigned char curRomBank() const { return cart_.curRomBank(); } + unsigned curRomBank() const { return cart_.curRomBank(); } char const * romTitle() const { return cart_.romTitle(); } int debugGetLY() const { return lcd_.debugGetLY(); } void setStatePtrs(SaveState &state); @@ -55,7 +55,7 @@ public: } bool gbIsCgb() { return gbIsCgb_; } - bool getMemoryArea(int which, unsigned char **data, int *length); // { return cart.getMemoryArea(which, data, length); } + bool getMemoryArea(int which, unsigned char **data, int *length); unsigned long stop(unsigned long cycleCounter); bool isCgb() const { return lcd_.isCgb(); } diff --git a/libgambatte/src/newstate.cpp b/libgambatte/src/newstate.cpp index f28a15fa7c..4aae0b1679 100644 --- a/libgambatte/src/newstate.cpp +++ b/libgambatte/src/newstate.cpp @@ -8,7 +8,7 @@ NewStateDummy::NewStateDummy() :length(0) { } -void NewStateDummy::Save(const void *ptr, size_t size, char const *name) +void NewStateDummy::Save(void const *ptr, size_t size, char const *name) { length += size; } @@ -21,7 +21,7 @@ NewStateExternalBuffer::NewStateExternalBuffer(char *buffer, long maxlength) { } -void NewStateExternalBuffer::Save(const void *ptr, size_t size, char const *name) +void NewStateExternalBuffer::Save(void const *ptr, size_t size, char const *name) { if (maxlength - length >= (long)size) { @@ -48,7 +48,7 @@ NewStateExternalFunctions::NewStateExternalFunctions(const FPtrs *ff) { } -void NewStateExternalFunctions::Save(const void *ptr, size_t size, char const *name) +void NewStateExternalFunctions::Save(void const *ptr, size_t size, char const *name) { Save_(ptr, size, name); } diff --git a/libgambatte/src/newstate.h b/libgambatte/src/newstate.h index 6ae468e5db..1ad9088224 100644 --- a/libgambatte/src/newstate.h +++ b/libgambatte/src/newstate.h @@ -9,8 +9,8 @@ namespace gambatte { class NewState { public: - virtual void Save(const void *ptr, size_t size, char const *name) = 0; - virtual void Load(void *ptr, size_t size, char const *name) = 0; + virtual void Save(void const *ptr, std::size_t size, char const *name) = 0; + virtual void Load(void *ptr, std::size_t size, char const *name) = 0; virtual void EnterSection(char const *name) { } virtual void ExitSection(char const *name) { } }; @@ -23,8 +23,8 @@ public: NewStateDummy(); long GetLength() { return length; } void Rewind() { length = 0; } - virtual void Save(const void *ptr, size_t size, char const *name); - virtual void Load(void *ptr, size_t size, char const *name); + virtual void Save(void const *ptr, std::size_t size, char const *name); + virtual void Load(void *ptr, std::size_t size, char const *name); }; class NewStateExternalBuffer : public NewState @@ -38,14 +38,14 @@ public: long GetLength() { return length; } void Rewind() { length = 0; } bool Overflow() { return length > maxlength; } - virtual void Save(const void *ptr, size_t size, char const *name); - virtual void Load(void *ptr, size_t size, char const *name); + virtual void Save(void const *ptr, std::size_t size, char const *name); + virtual void Load(void *ptr, std::size_t size, char const *name); }; struct FPtrs { - void (*Save_)(const void *ptr, size_t size, char const *name); - void (*Load_)(void *ptr, size_t size, char const *name); + void (*Save_)(void const *ptr, std::size_t size, char const *name); + void (*Load_)(void *ptr, std::size_t size, char const *name); void (*EnterSection_)(char const *name); void (*ExitSection_)(char const *name); }; @@ -53,14 +53,14 @@ struct FPtrs class NewStateExternalFunctions : public NewState { private: - void (*Save_)(const void *ptr, size_t size, char const *name); - void (*Load_)(void *ptr, size_t size, char const *name); + void (*Save_)(void const *ptr, std::size_t size, char const *name); + void (*Load_)(void *ptr, std::size_t size, char const *name); void (*EnterSection_)(char const *name); void (*ExitSection_)(char const *name); public: NewStateExternalFunctions(const FPtrs *ff); - virtual void Save(const void *ptr, size_t size, char const *name); - virtual void Load(void *ptr, size_t size, char const *name); + virtual void Save(void const *ptr, std::size_t size, char const *name); + virtual void Load(void *ptr, std::size_t size, char const *name); virtual void EnterSection(char const *name); virtual void ExitSection(char const *name); }; @@ -85,9 +85,9 @@ public: #define EES(x,d) else if (isReader) (x) = (d); if (!isReader) ns->Save(&_ttmp, sizeof _ttmp, #x); } while (0) #define RSS(x,b) do { if (isReader)\ -{ ptrdiff_t _ttmp; ns->Load(&_ttmp, sizeof _ttmp, #x); (x) = (_ttmp == (ptrdiff_t)0xdeadbeef ? 0 : (b) + _ttmp); }\ +{ std::ptrdiff_t _ttmp; ns->Load(&_ttmp, sizeof _ttmp, #x); (x) = (_ttmp == (std::ptrdiff_t)0xdeadbeef ? 0 : (b) + _ttmp); }\ else\ -{ ptrdiff_t _ttmp = (x) == 0 ? 0xdeadbeef : (x) - (b); ns->Save(&_ttmp, sizeof _ttmp, #x); } } while (0) +{ std::ptrdiff_t _ttmp = (x) == 0 ? 0xdeadbeef : (x) - (b); ns->Save(&_ttmp, sizeof _ttmp, #x); } } while (0) #define PSS(x,s) do { if (isReader) ns->Load((x), (s), #x); else ns->Save((x), (s), #x); } while (0) diff --git a/libgambatte/src/video.cpp b/libgambatte/src/video.cpp index becb09a7b9..997993c881 100644 --- a/libgambatte/src/video.cpp +++ b/libgambatte/src/video.cpp @@ -863,14 +863,13 @@ void LCD::setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned long r SYNCFUNC(LCD) { SSS(ppu_); + NSS(dmgColorsRgb32_); NSS(bgpData_); NSS(objpData_); - NSS(dmgColorsRgb32_); SSS(eventTimes_); SSS(m0Irq_); SSS(lycIrq_); SSS(nextM0Time_); - NSS(statReg_); NSS(m2IrqStatReg_); NSS(m1IrqStatReg_); diff --git a/libgambatte/src/video.h b/libgambatte/src/video.h index b37beaa3e8..abc5ec8f0e 100644 --- a/libgambatte/src/video.h +++ b/libgambatte/src/video.h @@ -208,7 +208,6 @@ public: { SSS(eventMin_); SSS(memEventMin_); - //SSS(memEventRequester_); // not needed } }; diff --git a/libgambatte/src/video/ppu.cpp b/libgambatte/src/video/ppu.cpp index a016cea84f..38a1b03790 100644 --- a/libgambatte/src/video/ppu.cpp +++ b/libgambatte/src/video/ppu.cpp @@ -102,10 +102,10 @@ enum { max_m3start_cycles = 80 }; enum { attr_yflip = 0x40, attr_bgpriority = 0x80 }; static inline int lcdcEn( PPUPriv const &p) { return p.lcdc & lcdc_en; } -static inline int lcdcWinEn(PPUPriv const &p) { return p.lcdc & lcdc_we; } +static inline int lcdcWinEn(PPUPriv const &p) { return (p.lcdc & lcdc_we) && (p.layersMask & layer_mask_window); } static inline int lcdcObj2x(PPUPriv const &p) { return p.lcdc & lcdc_obj2x; } -static inline int lcdcObjEn(PPUPriv const &p) { return p.lcdc & lcdc_objen; } -static inline int lcdcBgEn( PPUPriv const &p) { return p.lcdc & lcdc_bgen; } +static inline int lcdcObjEn(PPUPriv const &p) { return (p.lcdc & lcdc_objen) && (p.layersMask & layer_mask_obj); } +static inline int lcdcBgEn( PPUPriv const &p) { return (p.lcdc & lcdc_bgen) && (p.layersMask & layer_mask_bg); } static inline int weMasterCheckPriorToLyIncLineCycle(bool cgb) { return 450 - cgb; } static inline int weMasterCheckAfterLyIncLineCycle(bool cgb) { return 454 - cgb; } @@ -418,7 +418,7 @@ static void doFullTilesUnrolledDmg(PPUPriv &p, int const xend, uint_least32_t *c { uint_least32_t *const dst = dbufline + (xpos - 8); - unsigned const tileword = -(p.lcdc & 1U) & p.ntileword; + unsigned const tileword = -(p.lcdc & 1U & p.layersMask) & p.ntileword; dst[0] = p.bgPalette[ tileword & 0x0003 ]; dst[1] = p.bgPalette[(tileword & 0x000C) >> 2]; @@ -624,7 +624,7 @@ static void doFullTilesUnrolledCgb(PPUPriv &p, int const xend, uint_least32_t *c } while (i >= 0 && int(p.spriteList[i].spx) > xpos - 8); } else { unsigned char idtab[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; - unsigned const bgprioritymask = p.lcdc << 7; + unsigned const bgprioritymask = (p.lcdc & p.layersMask & layer_mask_bg) << 7; do { int n; @@ -804,7 +804,7 @@ static void plotPixel(PPUPriv &p) { p.winDrawState |= win_draw_start; } - unsigned const twdata = tileword & ((p.lcdc & 1) | p.cgb) * 3; + unsigned const twdata = tileword & ((p.lcdc & 1 & p.layersMask) | p.cgb) * 3; unsigned long pixel = p.bgPalette[twdata + (p.attrib & 7) * 4]; int i = static_cast(p.nextSprite) - 1; @@ -1488,6 +1488,7 @@ namespace gambatte { PPUPriv::PPUPriv(NextM0Time &nextM0Time, unsigned char const *const oamram, unsigned char const *const vram) : nextSprite(0) , currentSprite(0xFF) +, layersMask(layer_mask_bg | layer_mask_window | layer_mask_obj) , vram(vram) , nextCallPtr(&M2_Ly0::f0_) , now(0) @@ -1812,7 +1813,6 @@ SYNCFUNC(PPU) SSS(p_.spriteMapper); SSS(p_.lyCounter); - //SSS(p_.framebuf); // no state NSS(p_.lcdc); NSS(p_.scy); diff --git a/libgambatte/src/video/ppu.h b/libgambatte/src/video/ppu.h index f37e837bd2..edfb6a9b9f 100644 --- a/libgambatte/src/video/ppu.h +++ b/libgambatte/src/video/ppu.h @@ -29,7 +29,7 @@ namespace gambatte { -enum { LAYER_MASK_BG = 1, LAYER_MASK_OBJ = 2, LAYER_MASK_WINDOW = 4 }; +enum { layer_mask_bg = 1, layer_mask_obj = 2, layer_mask_window = 4 }; class PPUFrameBuf { public: diff --git a/libgambatte/src/video/sprite_mapper.cpp b/libgambatte/src/video/sprite_mapper.cpp index b44e8d0273..87ffa52759 100644 --- a/libgambatte/src/video/sprite_mapper.cpp +++ b/libgambatte/src/video/sprite_mapper.cpp @@ -212,7 +212,6 @@ SYNCFUNC(SpriteMapper) NSS(spritemap_); NSS(num_); - SSS(nextM0Time_); SSS(oamReader_); } diff --git a/output/dll/libgambatte.dll b/output/dll/libgambatte.dll index 984932df0eef38e880ecc18df747d00fc5afe580..76b0b4b77da81c4ed49c88dfa2ce6ad6275bb08f 100644 GIT binary patch delta 17227 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 H_iFzKsQwI# delta 16658 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 Date: Tue, 28 May 2019 17:42:30 +0200 Subject: [PATCH 33/38] Make BG layer mask work properly for CGB --- libgambatte/src/video/ppu.cpp | 18 +++++++++--------- output/dll/libgambatte.dll | Bin 168448 -> 168448 bytes 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/libgambatte/src/video/ppu.cpp b/libgambatte/src/video/ppu.cpp index 38a1b03790..514beee00c 100644 --- a/libgambatte/src/video/ppu.cpp +++ b/libgambatte/src/video/ppu.cpp @@ -557,8 +557,8 @@ static void doFullTilesUnrolledCgb(PPUPriv &p, int const xend, uint_least32_t *c n = (p.cycles & ~7) < n ? p.cycles & ~7 : n; p.cycles -= n; - unsigned ntileword = p.ntileword; - unsigned nattrib = p.nattrib; + unsigned ntileword = -(p.layersMask & layer_mask_bg) & p.ntileword; + unsigned nattrib = -(p.layersMask & layer_mask_bg) & p.nattrib; uint_least32_t * dst = dbufline + xpos - 8; uint_least32_t *const dstend = dst + n; xpos += n; @@ -576,7 +576,7 @@ static void doFullTilesUnrolledCgb(PPUPriv &p, int const xend, uint_least32_t *c dst += 8; unsigned const tno = tileMapLine[ tileMapXpos & 0x1F ]; - nattrib = tileMapLine[(tileMapXpos & 0x1F) + 0x2000]; + nattrib = -(p.layersMask & layer_mask_bg) & tileMapLine[(tileMapXpos & 0x1F) + 0x2000]; tileMapXpos = (tileMapXpos & 0x1F) + 1; unsigned const tdo = tdoffset & ~(tno << 5); @@ -584,7 +584,7 @@ static void doFullTilesUnrolledCgb(PPUPriv &p, int const xend, uint_least32_t *c + (nattrib & attr_yflip ? tdo ^ 14 : tdo) + (nattrib << 10 & 0x2000); unsigned short const *const explut = expand_lut + (nattrib << 3 & 0x100); - ntileword = explut[td[0]] + explut[td[1]] * 2; + ntileword = -(p.layersMask & layer_mask_bg) & (explut[td[0]] + explut[td[1]] * 2); } while (dst != dstend); p.ntileword = ntileword; @@ -601,8 +601,8 @@ static void doFullTilesUnrolledCgb(PPUPriv &p, int const xend, uint_least32_t *c { uint_least32_t *const dst = dbufline + (xpos - 8); - unsigned const tileword = p.ntileword; - unsigned const attrib = p.nattrib; + unsigned const tileword = -(p.layersMask & layer_mask_bg) & p.ntileword; + unsigned const attrib = -(p.layersMask & layer_mask_bg) & p.nattrib; unsigned long const *const bgPalette = p.bgPalette + (attrib & 7) * 4; dst[0] = bgPalette[ tileword & 0x0003 ]; @@ -624,7 +624,7 @@ static void doFullTilesUnrolledCgb(PPUPriv &p, int const xend, uint_least32_t *c } while (i >= 0 && int(p.spriteList[i].spx) > xpos - 8); } else { unsigned char idtab[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; - unsigned const bgprioritymask = (p.lcdc & p.layersMask & layer_mask_bg) << 7; + unsigned const bgprioritymask = p.lcdc << 7; do { int n; @@ -804,8 +804,8 @@ static void plotPixel(PPUPriv &p) { p.winDrawState |= win_draw_start; } - unsigned const twdata = tileword & ((p.lcdc & 1 & p.layersMask) | p.cgb) * 3; - unsigned long pixel = p.bgPalette[twdata + (p.attrib & 7) * 4]; + unsigned const twdata = tileword & (((p.lcdc & 1) | p.cgb) & p.layersMask) * 3; + unsigned long pixel = p.bgPalette[twdata + (p.attrib & 7 & -(p.layersMask & layer_mask_bg)) * 4]; int i = static_cast(p.nextSprite) - 1; if (i >= 0 && int(p.spriteList[i].spx) > xpos - 8) { diff --git a/output/dll/libgambatte.dll b/output/dll/libgambatte.dll index 76b0b4b77da81c4ed49c88dfa2ce6ad6275bb08f..2d96928fd030cb9b9bd00955371f0b3847f7a58e 100644 GIT binary patch delta 9345 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 DuZ9u( delta 9370 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 From 7d33d604aef1749833a7cf2807c39ccff7098f69 Mon Sep 17 00:00:00 2001 From: MrWint Date: Tue, 28 May 2019 22:56:39 +0200 Subject: [PATCH 34/38] cycle-based RTC --- .../Nintendo/Gameboy/Gambatte.ISaveRam.cs | 22 --- .../Nintendo/Gameboy/Gambatte.ISettable.cs | 22 --- .../Consoles/Nintendo/Gameboy/Gambatte.cs | 70 +-------- .../Consoles/Nintendo/Gameboy/LibGambatte.cs | 29 ++-- libgambatte/include/gambatte.h | 7 +- libgambatte/libgambatte.vcxproj | 2 + libgambatte/libgambatte.vcxproj.filters | 6 + libgambatte/src/cinterface.cpp | 16 +- libgambatte/src/cpu.h | 9 +- libgambatte/src/gambatte.cpp | 16 +- libgambatte/src/initstate.cpp | 13 +- libgambatte/src/initstate.h | 2 +- libgambatte/src/mem/cartridge.cpp | 53 +++++-- libgambatte/src/mem/cartridge.h | 20 +-- libgambatte/src/mem/rtc.cpp | 76 ++++----- libgambatte/src/mem/rtc.h | 41 +++-- libgambatte/src/mem/time.cpp | 144 ++++++++++++++++++ libgambatte/src/mem/time.h | 78 ++++++++++ libgambatte/src/memory.cpp | 6 +- libgambatte/src/memory.h | 12 +- libgambatte/src/savestate.h | 10 +- output/dll/libgambatte.dll | Bin 168448 -> 170496 bytes 22 files changed, 402 insertions(+), 252 deletions(-) create mode 100644 libgambatte/src/mem/time.cpp create mode 100644 libgambatte/src/mem/time.h diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISaveRam.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISaveRam.cs index 3776eb00f7..218d2bf3ec 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISaveRam.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISaveRam.cs @@ -41,31 +41,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy break; default: throw new ArgumentException("Size of saveram data does not match expected!"); - case 44: - data = FixRTC(data, 44); - break; - case 40: - data = FixRTC(data, 40); - break; } LibGambatte.gambatte_loadsavedata(GambatteState, data); } - - private byte[] FixRTC(byte[] data, int offset) - { - // length - offset is the start of the VBA-only data; so - // length - offset - 4 is the start of the RTC block - int idx = data.Length - offset - 4; - - byte[] ret = new byte[idx + 4]; - Buffer.BlockCopy(data, 0, ret, 0, idx); - data[idx] = (byte)zerotime; - data[idx + 1] = (byte)(zerotime >> 8); - data[idx + 2] = (byte)(zerotime >> 16); - data[idx + 3] = (byte)(zerotime >> 24); - - return ret; - } } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISettable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISettable.cs index 297b029f11..3681c532d3 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISettable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISettable.cs @@ -116,18 +116,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy [DefaultValue(false)] public bool RealTimeRTC { get; set; } - [DisplayName("RTC Initial Time")] - [Description("Set the initial RTC time in terms of elapsed seconds. Only used when RealTimeRTC is false.")] - [DefaultValue(0)] - public int RTCInitialTime - { - get { return _RTCInitialTime; } - set { _RTCInitialTime = Math.Max(0, Math.Min(1024 * 24 * 60 * 60, value)); } - } - - [JsonIgnore] - private int _RTCInitialTime; - [DisplayName("Equal Length Frames")] [Description("When false, emulation frames sync to vblank. Only useful for high level TASing.")] [DefaultValue(false)] @@ -141,11 +129,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy [DeepEqualsIgnore] private bool _equalLengthFrames; - [DisplayName("Initial DIV offset")] - [Description("Internal. Probably doesn't work. Leave this set to 0. Accepts values from 0 to 65532 in steps of 4")] - [DefaultValue(0)] - public int InitialDiv { get; set; } - public GambatteSyncSettings() { SettingsUtil.SetDefaultValues(this); @@ -160,11 +143,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy { return !DeepEquality.DeepEquals(x, y); } - - public uint GetInitialDivInternal() - { - return (uint)(InitialDiv & 0xfffc); - } } } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs index 1790f04703..3b837f5b78 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs @@ -58,13 +58,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy { _syncSettings = (GambatteSyncSettings)syncSettings ?? new GambatteSyncSettings(); - // copy over non-loadflag syncsettings now; they won't take effect if changed later - zerotime = (uint)_syncSettings.RTCInitialTime; - - real_rtc_time = !DeterministicEmulation && _syncSettings.RealTimeRTC; - - DivInternal = _syncSettings.GetInitialDivInternal(); - LibGambatte.LoadFlags flags = 0; switch (_syncSettings.ConsoleMode) @@ -90,7 +83,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy flags |= LibGambatte.LoadFlags.MULTICART_COMPAT; } - if (LibGambatte.gambatte_load(GambatteState, file, (uint)file.Length, GetCurrentTime(), flags, DivInternal) != 0) + if (LibGambatte.gambatte_load(GambatteState, file, (uint)file.Length, flags) != 0) { throw new InvalidOperationException($"{nameof(LibGambatte.gambatte_load)}() returned non-zero (is this not a gb or gbc rom?)"); } @@ -133,8 +126,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy string romname = System.Text.Encoding.ASCII.GetString(buff); Console.WriteLine("Core reported rom name: {0}", romname); - TimeCallback = new LibGambatte.RTCCallback(GetCurrentTime); - LibGambatte.gambatte_setrtccallback(GambatteState, TimeCallback); + if (!DeterministicEmulation && _syncSettings.RealTimeRTC) + { + LibGambatte.gambatte_settimemode(GambatteState, false); + } _cdCallback = new LibGambatte.CDCallback(CDCallbackProc); @@ -167,59 +162,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy /// private LibGambatte.Buttons CurrentButtons = 0; - private uint DivInternal = 0; - - #region RTC - - /// - /// RTC time when emulation begins. - /// - private readonly uint zerotime = 0; - - /// - /// if true, RTC will run off of real elapsed time - /// - private bool real_rtc_time = false; - - private LibGambatte.RTCCallback TimeCallback; - - private static long GetUnixNow() - { - // because internally the RTC works off of relative time, we don't need to base - // this off of any particular canonical epoch. - return DateTime.UtcNow.Ticks / 10000000L - 60000000000L; - } - - private uint GetCurrentTime() - { - if (real_rtc_time) - { - return (uint)GetUnixNow(); - } - - ulong fn = (ulong)Frame; - - // as we're exactly tracking cpu cycles, this can be pretty accurate - fn *= 4389; - fn /= 262144; - fn += zerotime; - return (uint)fn; - } - - private uint GetInitialTime() - { - if (real_rtc_time) - { - return (uint)GetUnixNow(); - } - - // setting the initial boot time to 0 will cause our zerotime - // to function as an initial offset, which is what we want - return 0; - } - - #endregion - #region ALL SAVESTATEABLE STATE GOES HERE /// @@ -320,7 +262,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy if (controller.IsPressed("Power")) { - LibGambatte.gambatte_reset(GambatteState, GetCurrentTime(), DivInternal); + LibGambatte.gambatte_reset(GambatteState); } if (Tracer.Enabled) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibGambatte.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibGambatte.cs index 06cb95ece3..038ff62021 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibGambatte.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibGambatte.cs @@ -53,11 +53,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy /// opaque state pointer /// the rom data, can be disposed of once this function returns /// length of romdata in bytes - /// RTC time when the rom is loaded /// ORed combination of LoadFlags. /// 0 on success, negative value on failure. [DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)] - public static extern int gambatte_load(IntPtr core, byte[] romdata, uint length, long now, LoadFlags flags, uint div); + public static extern int gambatte_load(IntPtr core, byte[] romdata, uint length, LoadFlags flags); /// /// Load GB(C) BIOS image. @@ -114,9 +113,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy /// Equivalent to reloading a ROM image, or turning a Game Boy Color off and on again. /// /// opaque state pointer - /// RTC time when the reset occurs [DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)] - public static extern void gambatte_reset(IntPtr core, long now, uint div); + public static extern void gambatte_reset(IntPtr core); /// /// palette type for gambatte_setdmgpalettecolor @@ -259,21 +257,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy /// 0-153 inclusive [DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)] public static extern void gambatte_setscanlinecallback(IntPtr core, ScanlineCallback callback, int sl); - - /// - /// type of the RTC callback - /// - /// what time is it, unixy - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate uint RTCCallback(); - - /// - /// sets RTC callback. probably mandatory. - /// - /// opaque state pointer - /// the callback - [DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)] - public static extern void gambatte_setrtccallback(IntPtr core, RTCCallback callback); /// /// type of the link data sent callback @@ -289,6 +272,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy [DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)] public static extern void gambatte_setlinkcallback(IntPtr core, LinkCallback callback); + /// + /// Changes between cycle-based and real-time RTC. Defaults to cycle-based. + /// + /// opaque state pointer + /// use cycle-based RTC + [DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)] + public static extern void gambatte_settimemode(IntPtr core, bool useCycles); + /// /// Returns true if the currently loaded ROM image is treated as having CGB support. /// diff --git a/libgambatte/include/gambatte.h b/libgambatte/include/gambatte.h index 8bebff11bc..d86187c0fd 100644 --- a/libgambatte/include/gambatte.h +++ b/libgambatte/include/gambatte.h @@ -64,7 +64,7 @@ public: * @param flags ORed combination of LoadFlags. * @return 0 on success, negative value on failure. */ - LoadRes load(char const *romfiledata, unsigned romfilelength, std::uint32_t now, unsigned flags, unsigned div); + LoadRes load(char const *romfiledata, unsigned romfilelength, unsigned flags); int loadBios(char const *biosfiledata, std::size_t size); @@ -106,7 +106,7 @@ public: * Reset to initial state. * Equivalent to reloading a ROM image, or turning a Game Boy Color off and on again. */ - void reset(std::uint32_t now, unsigned div); + void reset(); /** * @param palNum 0 <= palNum < 3. One of BG_PALETTE, SP1_PALETTE and SP2_PALETTE. @@ -128,6 +128,9 @@ public: void setRTCCallback(std::uint32_t (*callback)()); void setLinkCallback(void(*callback)()); + /** Use cycle-based RTC instead of real-time. */ + void setTimeMode(bool useCycles); + /** Returns true if the currently loaded ROM image is treated as having CGB support. */ bool isCgb() const; diff --git a/libgambatte/libgambatte.vcxproj b/libgambatte/libgambatte.vcxproj index 33e0eb0aee..6612bff667 100644 --- a/libgambatte/libgambatte.vcxproj +++ b/libgambatte/libgambatte.vcxproj @@ -170,6 +170,7 @@ + @@ -202,6 +203,7 @@ + diff --git a/libgambatte/libgambatte.vcxproj.filters b/libgambatte/libgambatte.vcxproj.filters index 38cbfcdb7d..f096a22f6b 100644 --- a/libgambatte/libgambatte.vcxproj.filters +++ b/libgambatte/libgambatte.vcxproj.filters @@ -126,6 +126,9 @@ Header Files + + Header Files + @@ -203,5 +206,8 @@ Source Files + + Source Files + \ No newline at end of file diff --git a/libgambatte/src/cinterface.cpp b/libgambatte/src/cinterface.cpp index c342d81418..51c4730ae1 100644 --- a/libgambatte/src/cinterface.cpp +++ b/libgambatte/src/cinterface.cpp @@ -45,8 +45,8 @@ GBEXPORT void gambatte_destroy(GB *g) { delete g; } -GBEXPORT int gambatte_load(GB *g, char const *romfiledata, unsigned romfilelength, long long now, unsigned flags, unsigned div) { - return g->load(romfiledata, romfilelength, now, flags, div); +GBEXPORT int gambatte_load(GB *g, char const *romfiledata, unsigned romfilelength, unsigned flags) { + return g->load(romfiledata, romfilelength, flags); } GBEXPORT int gambatte_loadbios(GB *g, char const *biosfiledata, unsigned size) { @@ -68,8 +68,12 @@ GBEXPORT void gambatte_setlayers(GB *g, unsigned mask) { g->setLayers(mask); } -GBEXPORT void gambatte_reset(GB *g, long long now, unsigned div) { - g->reset(now, div); +GBEXPORT void gambatte_setTimeMode(GB *g, bool useCycles) { + g->setTimeMode(useCycles); +} + +GBEXPORT void gambatte_reset(GB *g) { + g->reset(); } GBEXPORT void gambatte_setdmgpalettecolor(GB *g, unsigned palnum, unsigned colornum, unsigned rgb32) { @@ -109,10 +113,6 @@ GBEXPORT void gambatte_setscanlinecallback(GB *g, void (*callback)(), int sl) { g->setScanlineCallback(callback, sl); } -GBEXPORT void gambatte_setrtccallback(GB *g, unsigned int (*callback)()) { - g->setRTCCallback(callback); -} - GBEXPORT void gambatte_setlinkcallback(GB *g, void(*callback)()) { g->setLinkCallback(callback); } diff --git a/libgambatte/src/cpu.h b/libgambatte/src/cpu.h index 5349514e05..6059b0fad4 100644 --- a/libgambatte/src/cpu.h +++ b/libgambatte/src/cpu.h @@ -31,9 +31,9 @@ public: void setStatePtrs(SaveState &state); void loadState(SaveState const &state); void setLayers(unsigned mask) { mem_.setLayers(mask); } - void loadSavedata(char const *data) { mem_.loadSavedata(data); } + void loadSavedata(char const *data) { mem_.loadSavedata(data, cycleCounter_); } int saveSavedataLength() {return mem_.saveSavedataLength(); } - void saveSavedata(char *dest) { mem_.saveSavedata(dest); } + void saveSavedata(char *dest) { mem_.saveSavedata(dest, cycleCounter_); } bool getMemoryArea(int which, unsigned char **data, int *length) { return mem_.getMemoryArea(which, data, length); } @@ -69,10 +69,6 @@ public: mem_.setScanlineCallback(callback, sl); } - void setRTCCallback(std::uint32_t (*callback)()) { - mem_.setRTCCallback(callback); - } - void setLinkCallback(void(*callback)()) { mem_.setLinkCallback(callback); } @@ -94,6 +90,7 @@ public: void setCgbPalette(unsigned *lut) { mem_.setCgbPalette(lut); } + void setTimeMode(bool useCycles) { mem_.setTimeMode(useCycles, cycleCounter_); } void setBios(char const *buffer, std::size_t size) { mem_.setBios(buffer, size); } bool gbIsCgb() { return mem_.gbIsCgb(); } diff --git a/libgambatte/src/gambatte.cpp b/libgambatte/src/gambatte.cpp index d4beddafce..d222874ac6 100644 --- a/libgambatte/src/gambatte.cpp +++ b/libgambatte/src/gambatte.cpp @@ -75,7 +75,7 @@ void GB::blitTo(gambatte::uint_least32_t *videoBuf, std::ptrdiff_t pitch) { } } -void GB::reset(const std::uint32_t now, const unsigned div) { +void GB::reset() { if (p_->cpu.loaded()) { int length = p_->cpu.saveSavedataLength(); @@ -88,7 +88,7 @@ void GB::reset(const std::uint32_t now, const unsigned div) { SaveState state; p_->cpu.setStatePtrs(state); - setInitState(state, !(p_->loadflags & FORCE_DMG), p_->loadflags & GBA_CGB, now, div); + setInitState(state, !(p_->loadflags & FORCE_DMG), p_->loadflags & GBA_CGB); p_->cpu.loadState(state); if (length > 0) { @@ -126,22 +126,22 @@ void GB::setScanlineCallback(void (*callback)(), int sl) { p_->cpu.setScanlineCallback(callback, sl); } -void GB::setRTCCallback(std::uint32_t (*callback)()) { - p_->cpu.setRTCCallback(callback); -} - void GB::setLinkCallback(void(*callback)()) { p_->cpu.setLinkCallback(callback); } -LoadRes GB::load(char const *romfiledata, unsigned romfilelength, const std::uint32_t now, unsigned const flags, const unsigned div) { +void GB::setTimeMode(bool useCycles) { + p_->cpu.setTimeMode(useCycles); +} + +LoadRes GB::load(char const *romfiledata, unsigned romfilelength, unsigned const flags) { LoadRes const loadres = p_->cpu.load(romfiledata, romfilelength, flags & FORCE_DMG, flags & MULTICART_COMPAT); if (loadres == LOADRES_OK) { SaveState state; p_->cpu.setStatePtrs(state); p_->loadflags = flags; - setInitState(state, !(flags & FORCE_DMG), flags & GBA_CGB, now, div); + setInitState(state, !(flags & FORCE_DMG), flags & GBA_CGB); p_->cpu.loadState(state); } diff --git a/libgambatte/src/initstate.cpp b/libgambatte/src/initstate.cpp index d147eb03d6..72e26c6f85 100644 --- a/libgambatte/src/initstate.cpp +++ b/libgambatte/src/initstate.cpp @@ -20,6 +20,7 @@ #include "counterdef.h" #include "savestate.h" #include "sound/sound_unit.h" +#include "mem/time.h" #include #include @@ -1146,7 +1147,7 @@ static void setInitialDmgIoamhram(unsigned char ioamhram[]) { } // anon namespace -void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbMode, const std::uint32_t now, const unsigned div) { +void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbMode) { static unsigned char const cgbObjpDump[0x40] = { 0x00, 0x00, 0xF2, 0xAB, 0x61, 0xC2, 0xD9, 0xBA, @@ -1198,7 +1199,7 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM state.mem.ioamhram.ptr[0x140] = 0; state.mem.ioamhram.ptr[0x144] = 0x00; - state.mem.divLastUpdate = 0 - div; + state.mem.divLastUpdate = 0; state.mem.timaBasetime = 0; state.mem.timaLastUpdate = 0; state.mem.tmatime = disabled_time; @@ -1315,8 +1316,12 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM state.spu.ch4.nr4 = 0; state.spu.ch4.master = false; - state.rtc.baseTime = now; - state.rtc.haltTime = state.rtc.baseTime; + state.time.seconds = 0; + state.time.lastTimeSec = Time::now().tv_sec; + state.time.lastTimeUsec = Time::now().tv_usec; + state.time.lastCycles = state.cpu.cycleCounter; + + state.rtc.haltTime = state.time.seconds; state.rtc.dataDh = 0; state.rtc.dataDl = 0; state.rtc.dataH = 0; diff --git a/libgambatte/src/initstate.h b/libgambatte/src/initstate.h index 02363941d8..0e31dd35bb 100644 --- a/libgambatte/src/initstate.h +++ b/libgambatte/src/initstate.h @@ -23,7 +23,7 @@ namespace gambatte { -void setInitState(struct SaveState &state, bool cgb, bool gbaCgbMode, std::uint32_t now, unsigned div); +void setInitState(struct SaveState &state, bool cgb, bool gbaCgbMode); } #endif diff --git a/libgambatte/src/mem/cartridge.cpp b/libgambatte/src/mem/cartridge.cpp index 5465548f22..0d6539af68 100644 --- a/libgambatte/src/mem/cartridge.cpp +++ b/libgambatte/src/mem/cartridge.cpp @@ -50,7 +50,7 @@ public: { } - virtual void romWrite(unsigned const p, unsigned const data) { + virtual void romWrite(unsigned const p, unsigned const data, unsigned long const /*cc*/) { if (p < 0x2000) { enableRam_ = (data & 0xF) == 0xA; memptrs_.setRambank(enableRam_ ? MemPtrs::read_en | MemPtrs::write_en : 0, 0); @@ -92,7 +92,7 @@ public: { } - virtual void romWrite(unsigned const p, unsigned const data) { + virtual void romWrite(unsigned const p, unsigned const data, unsigned long const /*cc*/) { switch (p >> 13 & 3) { case 0: enableRam_ = (data & 0xF) == 0xA; @@ -166,7 +166,7 @@ public: { } - virtual void romWrite(unsigned const p, unsigned const data) { + virtual void romWrite(unsigned const p, unsigned const data, unsigned long const /*cc*/) { switch (p >> 13 & 3) { case 0: enableRam_ = (data & 0xF) == 0xA; @@ -238,7 +238,7 @@ public: { } - virtual void romWrite(unsigned const p, unsigned const data) { + virtual void romWrite(unsigned const p, unsigned const data, unsigned long const /*cc*/) { switch (p & 0x6100) { case 0x0000: enableRam_ = (data & 0xF) == 0xA; @@ -282,7 +282,7 @@ public: { } - virtual void romWrite(unsigned const p, unsigned const data) { + virtual void romWrite(unsigned const p, unsigned const data, unsigned long const cc) { switch (p >> 13 & 3) { case 0: enableRam_ = (data & 0xF) == 0xA; @@ -298,7 +298,7 @@ public: break; case 3: if (rtc_) - rtc_->latch(data); + rtc_->latch(data, cc); break; } @@ -356,7 +356,7 @@ public: { } - virtual void romWrite(unsigned const p, unsigned const data) { + virtual void romWrite(unsigned const p, unsigned const data, unsigned long const /*cc*/) { switch (p >> 13 & 3) { case 0: enableRam_ = (data & 0xF) == 0xA; @@ -424,7 +424,7 @@ public: { } - virtual void romWrite(unsigned const p, unsigned const data) { + virtual void romWrite(unsigned const p, unsigned const data, unsigned long const /*cc*/) { switch (p >> 13 & 3) { case 0: enableRam_ = (data & 0xF) == 0xA; @@ -487,6 +487,11 @@ static bool hasRtc(unsigned headerByte0x147) { } +Cartridge::Cartridge() +: rtc_(time_) +{ +} + void Cartridge::setStatePtrs(SaveState &state) { state.mem.vram.set(memptrs_.vramdata(), memptrs_.vramdataend() - memptrs_.vramdata()); state.mem.sram.set(memptrs_.rambankdata(), memptrs_.rambankdataend() - memptrs_.rambankdata()); @@ -652,7 +657,7 @@ static bool hasBattery(unsigned char headerByte0x147) { } } -void Cartridge::loadSavedata(char const *data) { +void Cartridge::loadSavedata(char const *data, unsigned long const cc) { if (hasBattery(memptrs_.romdata()[0x147])) { int length = memptrs_.rambankdataend() - memptrs_.rambankdata(); std::memcpy(memptrs_.rambankdata(), data, length); @@ -661,9 +666,17 @@ void Cartridge::loadSavedata(char const *data) { } if (hasRtc(memptrs_.romdata()[0x147])) { - unsigned long basetime; - std::memcpy(&basetime, data, 4); - rtc_.setBaseTime(basetime); + timeval basetime; + basetime.tv_sec = (*data++); + basetime.tv_sec = basetime.tv_sec << 8 | (*data++); + basetime.tv_sec = basetime.tv_sec << 8 | (*data++); + basetime.tv_sec = basetime.tv_sec << 8 | (*data++); + basetime.tv_usec = (*data++); + basetime.tv_usec = basetime.tv_usec << 8 | (*data++); + basetime.tv_usec = basetime.tv_usec << 8 | (*data++); + basetime.tv_usec = basetime.tv_usec << 8 | (*data++); + + time_.setBaseTime(basetime, cc); } } @@ -673,12 +686,12 @@ int Cartridge::saveSavedataLength() { ret = memptrs_.rambankdataend() - memptrs_.rambankdata(); } if (hasRtc(memptrs_.romdata()[0x147])) { - ret += 4; + ret += 8; } return ret; } -void Cartridge::saveSavedata(char *dest) { +void Cartridge::saveSavedata(char *dest, unsigned long const cc) { if (hasBattery(memptrs_.romdata()[0x147])) { int length = memptrs_.rambankdataend() - memptrs_.rambankdata(); std::memcpy(dest, memptrs_.rambankdata(), length); @@ -686,8 +699,15 @@ void Cartridge::saveSavedata(char *dest) { } if (hasRtc(memptrs_.romdata()[0x147])) { - const unsigned long basetime = rtc_.getBaseTime(); - std::memcpy(dest, &basetime, 4); + timeval basetime = time_.baseTime(cc); + *dest++ = (basetime.tv_sec >> 24 & 0xFF); + *dest++ = (basetime.tv_sec >> 16 & 0xFF); + *dest++ = (basetime.tv_sec >> 8 & 0xFF); + *dest++ = (basetime.tv_sec & 0xFF); + *dest++ = (basetime.tv_usec >> 24 & 0xFF); + *dest++ = (basetime.tv_usec >> 16 & 0xFF); + *dest++ = (basetime.tv_usec >> 8 & 0xFF); + *dest++ = (basetime.tv_usec & 0xFF); } } @@ -723,6 +743,7 @@ bool Cartridge::getMemoryArea(int which, unsigned char **data, int *length) cons SYNCFUNC(Cartridge) { SSS(memptrs_); + SSS(time_); SSS(rtc_); TSS(mbc_); } diff --git a/libgambatte/src/mem/cartridge.h b/libgambatte/src/mem/cartridge.h index 245bf537aa..db46afb262 100644 --- a/libgambatte/src/mem/cartridge.h +++ b/libgambatte/src/mem/cartridge.h @@ -21,6 +21,7 @@ #include "loadres.h" #include "memptrs.h" +#include "time.h" #include "rtc.h" #include "savestate.h" #include @@ -33,7 +34,7 @@ namespace gambatte { class Mbc { public: virtual ~Mbc() {} - virtual void romWrite(unsigned P, unsigned data) = 0; + virtual void romWrite(unsigned P, unsigned data, unsigned long cycleCounter) = 0; virtual void loadState(SaveState::Mem const &ss) = 0; virtual bool isAddressWithinAreaRombankCanBeMappedTo(unsigned address, unsigned rombank) const = 0; @@ -47,6 +48,7 @@ public: class Cartridge { public: + Cartridge(); void setStatePtrs(SaveState &); void loadState(SaveState const &); bool loaded() const { return mbc_.get(); } @@ -64,23 +66,23 @@ public: void setWrambank(unsigned bank) { memptrs_.setWrambank(bank); } void setOamDmaSrc(OamDmaSrc oamDmaSrc) { memptrs_.setOamDmaSrc(oamDmaSrc); } unsigned curRomBank() const { return memptrs_.curRomBank(); } - void mbcWrite(unsigned addr, unsigned data) { mbc_->romWrite(addr, data); } + void mbcWrite(unsigned addr, unsigned data, unsigned long const cc) { mbc_->romWrite(addr, data, cc); } bool isCgb() const { return gambatte::isCgb(memptrs_); } - void rtcWrite(unsigned data) { rtc_.write(data); } + void resetCc(unsigned long const oldCc, unsigned long const newCc) { time_.resetCc(oldCc, newCc); } + void speedChange(unsigned long const cc) { time_.speedChange(cc); } + void setTimeMode(bool useCycles, unsigned long const cc) { time_.setTimeMode(useCycles, cc); } + void rtcWrite(unsigned data, unsigned long const cc) { rtc_.write(data, cc); } unsigned char rtcRead() const { return *rtc_.activeData(); } - void loadSavedata(char const *data); + void loadSavedata(char const *data, unsigned long cycleCounter); int saveSavedataLength(); - void saveSavedata(char *dest); + void saveSavedata(char *dest, unsigned long cycleCounter); bool getMemoryArea(int which, unsigned char **data, int *length) const; LoadRes loadROM(char const *romfiledata, unsigned romfilelength, bool forceDmg, bool multicartCompat); char const * romTitle() const { return reinterpret_cast(memptrs_.romdata() + 0x134); } - void setRTCCallback(std::uint32_t (*callback)()) { - rtc_.setRTCCallback(callback); - } - private: MemPtrs memptrs_; + Time time_; Rtc rtc_; std::unique_ptr mbc_; diff --git a/libgambatte/src/mem/rtc.cpp b/libgambatte/src/mem/rtc.cpp index 5495201c4c..5b79c04d7b 100644 --- a/libgambatte/src/mem/rtc.cpp +++ b/libgambatte/src/mem/rtc.cpp @@ -22,10 +22,10 @@ namespace gambatte { -Rtc::Rtc() -: activeData_(0) +Rtc::Rtc(Time &time) +: time_(time) +, activeData_(0) , activeSet_(0) -, baseTime_(0) , haltTime_(0) , index_(5) , dataDh_(0) @@ -35,16 +35,15 @@ Rtc::Rtc() , dataS_(0) , enabled_(false) , lastLatchData_(false) -, timeCB(0) { } -void Rtc::doLatch() { - std::uint32_t tmp = ((dataDh_ & 0x40) ? haltTime_ : timeCB()) - baseTime_; +void Rtc::doLatch(unsigned long const cc) { + std::uint32_t tmp = time(cc); - while (tmp > 0x1FF * 86400) { - baseTime_ += 0x1FF * 86400; - tmp -= 0x1FF * 86400; + if (tmp >= 0x200 * 86400) { + tmp %= 0x200 * 86400; + time_.set(tmp, cc); dataDh_ |= 0x80; } @@ -91,7 +90,6 @@ void Rtc::doSwapActive() { } void Rtc::loadState(SaveState const &state) { - baseTime_ = state.rtc.baseTime; haltTime_ = state.rtc.haltTime; dataDh_ = state.rtc.dataDh; dataDl_ = state.rtc.dataDl; @@ -102,45 +100,50 @@ void Rtc::loadState(SaveState const &state) { doSwapActive(); } -void Rtc::setDh(unsigned const newDh) { - const std::uint32_t unixtime = (dataDh_ & 0x40) ? haltTime_ : timeCB(); - const std::uint32_t oldHighdays = ((unixtime - baseTime_) / 86400) & 0x100; - baseTime_ += oldHighdays * 86400; - baseTime_ -= ((newDh & 0x1) << 8) * 86400; +void Rtc::setDh(unsigned const newDh, unsigned const long cc) { + std::uint32_t seconds = time(cc); + std::uint32_t const oldHighdays = (seconds / 86400) & 0x100; + seconds -= oldHighdays * 86400; + seconds += ((newDh & 0x1) << 8) * 86400; + time_.set(seconds, cc); if ((dataDh_ ^ newDh) & 0x40) { if (newDh & 0x40) - haltTime_ = timeCB(); + haltTime_ = seconds; else - baseTime_ += timeCB() - haltTime_; + time_.set(haltTime_, cc); } } -void Rtc::setDl(unsigned const newLowdays) { - const std::uint32_t unixtime = (dataDh_ & 0x40) ? haltTime_ : timeCB(); - const std::uint32_t oldLowdays = ((unixtime - baseTime_) / 86400) & 0xFF; - baseTime_ += oldLowdays * 86400; - baseTime_ -= newLowdays * 86400; +void Rtc::setDl(unsigned const newLowdays, unsigned const long cc) { + std::uint32_t seconds = time(cc); + std::uint32_t const oldLowdays = (seconds / 86400) & 0xFF; + seconds -= oldLowdays * 86400; + seconds += newLowdays * 86400; + time_.set(seconds, cc); } -void Rtc::setH(unsigned const newHours) { - const std::uint32_t unixtime = (dataDh_ & 0x40) ? haltTime_ : timeCB(); - const std::uint32_t oldHours = ((unixtime - baseTime_) / 3600) % 24; - baseTime_ += oldHours * 3600; - baseTime_ -= newHours * 3600; +void Rtc::setH(unsigned const newHours, unsigned const long cc) { + std::uint32_t seconds = time(cc); + std::uint32_t const oldHours = (seconds / 3600) % 24; + seconds -= oldHours * 3600; + seconds += newHours * 3600; + time_.set(seconds, cc); } -void Rtc::setM(unsigned const newMinutes) { - const std::uint32_t unixtime = (dataDh_ & 0x40) ? haltTime_ : timeCB(); - const std::uint32_t oldMinutes = ((unixtime - baseTime_) / 60) % 60; - baseTime_ += oldMinutes * 60; - baseTime_ -= newMinutes * 60; +void Rtc::setM(unsigned const newMinutes, unsigned const long cc) { + std::uint32_t seconds = time(cc); + std::uint32_t const oldMinutes = (seconds / 60) % 60; + seconds -= oldMinutes * 60; + seconds += newMinutes * 60; + time_.set(seconds, cc); } -void Rtc::setS(unsigned const newSeconds) { - const std::uint32_t unixtime = (dataDh_ & 0x40) ? haltTime_ : timeCB(); - baseTime_ += (unixtime - baseTime_) % 60; - baseTime_ -= newSeconds; +void Rtc::setS(unsigned const newSeconds, unsigned const long cc) { + std::uint32_t seconds = time(cc); + seconds -= seconds % 60; + seconds += newSeconds; + time_.reset(seconds, cc); } SYNCFUNC(Rtc) @@ -161,7 +164,6 @@ SYNCFUNC(Rtc) EVS(activeSet_, &Rtc::setDh, 5); EES(activeSet_, NULL); - NSS(baseTime_); NSS(haltTime_); NSS(index_); NSS(dataDh_); diff --git a/libgambatte/src/mem/rtc.h b/libgambatte/src/mem/rtc.h index f7594e2eec..853027ed58 100644 --- a/libgambatte/src/mem/rtc.h +++ b/libgambatte/src/mem/rtc.h @@ -20,6 +20,7 @@ #define RTC_H #include +#include "time.h" #include "newstate.h" namespace gambatte { @@ -28,18 +29,12 @@ struct SaveState; class Rtc { public: - Rtc(); - + Rtc(Time &time); unsigned char const * activeData() const { return activeData_; } - std::uint32_t getBaseTime() const { return baseTime_; } - void setBaseTime(const std::uint32_t baseTime) { - this->baseTime_ = baseTime; - } - - void latch(unsigned data) { + void latch(unsigned data, unsigned long const cc) { if (!lastLatchData_ && data == 1) - doLatch(); + doLatch(cc); lastLatchData_ = data; } @@ -55,19 +50,15 @@ public: doSwapActive(); } - void write(unsigned data) { - (this->*activeSet_)(data); + void write(unsigned data, unsigned long const cc) { + (this->*activeSet_)(data, cc); *activeData_ = data; } - void setRTCCallback(std::uint32_t (*callback)()) { - timeCB = callback; - } - private: + Time &time_; unsigned char *activeData_; - void (Rtc::*activeSet_)(unsigned); - std::uint32_t baseTime_; + void (Rtc::*activeSet_)(unsigned, unsigned long); std::uint32_t haltTime_; unsigned char index_; unsigned char dataDh_; @@ -77,16 +68,18 @@ private: unsigned char dataS_; bool enabled_; bool lastLatchData_; - std::uint32_t (*timeCB)(); - void doLatch(); + void doLatch(unsigned long cycleCounter); void doSwapActive(); - void setDh(unsigned newDh); - void setDl(unsigned newLowdays); - void setH(unsigned newHours); - void setM(unsigned newMinutes); - void setS(unsigned newSeconds); + void setDh(unsigned newDh, unsigned long cycleCounter); + void setDl(unsigned newLowdays, unsigned long cycleCounter); + void setH(unsigned newHours, unsigned long cycleCounter); + void setM(unsigned newMinutes, unsigned long cycleCounter); + void setS(unsigned newSeconds, unsigned long cycleCounter); + std::uint32_t time(unsigned long const cc) { + return dataDh_ & 0x40 ? haltTime_ : time_.get(cc); + } public: templatevoid SyncState(NewState *ns); }; diff --git a/libgambatte/src/mem/time.cpp b/libgambatte/src/mem/time.cpp new file mode 100644 index 0000000000..71e92d1356 --- /dev/null +++ b/libgambatte/src/mem/time.cpp @@ -0,0 +1,144 @@ +// +// Copyright (C) 2007 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + +#include "time.h" +#include "../savestate.h" + +namespace gambatte { + +static timeval operator-(timeval l, timeval r) { + timeval t; + t.tv_sec = l.tv_sec - r.tv_sec; + t.tv_usec = l.tv_usec - r.tv_usec; + if (t.tv_usec < 0) { + t.tv_sec--; + t.tv_usec += 1000000; + } + return t; +} + +Time::Time() +: useCycles_(true) +{ +} + +void Time::loadState(SaveState const &state) { + seconds_ = state.time.seconds; + lastTime_.tv_sec = state.time.lastTimeSec; + lastTime_.tv_usec = state.time.lastTimeUsec; + lastCycles_ = state.time.lastCycles; + ds_ = state.ppu.isCgb & state.mem.ioamhram.get()[0x14D] >> 7; +} + +std::uint32_t Time::get(unsigned long const cc) { + update(cc); + return seconds_; +} + +void Time::set(std::uint32_t seconds, unsigned long const cc) { + update(cc); + seconds_ = seconds; +} + +void Time::reset(std::uint32_t seconds, unsigned long const cc) { + set(seconds, cc); + lastTime_ = now(); + lastCycles_ = cc; +} + +void Time::resetCc(unsigned long const oldCc, unsigned long const newCc) { + update(oldCc); + lastCycles_ -= oldCc - newCc; +} + +void Time::speedChange(unsigned long const cc) { + update(cc); + + if (useCycles_) { + unsigned long diff = cc - lastCycles_; + lastCycles_ = cc - (ds_ ? diff >> 1 : diff << 1); + } + + ds_ = !ds_; +} + +timeval Time::baseTime(unsigned long const cc) { + if (useCycles_) + timeFromCycles(cc); + + timeval baseTime = lastTime_; + baseTime.tv_sec -= seconds_; + return baseTime; +} + +void Time::setBaseTime(timeval baseTime, unsigned long const cc) { + seconds_ = (now() - baseTime).tv_sec; + lastTime_ = baseTime; + lastTime_.tv_sec += seconds_; + + if (useCycles_) + cyclesFromTime(cc); +} + +void Time::setTimeMode(bool useCycles, unsigned long const cc) { + if (useCycles != useCycles_) { + if (useCycles_) + timeFromCycles(cc); + else + cyclesFromTime(cc); + + useCycles_ = useCycles; + } +} + +void Time::update(unsigned long const cc) { + if (useCycles_) { + std::uint32_t diff = (cc - lastCycles_) / (0x400000 << ds_); + seconds_ += diff; + lastCycles_ += diff * (0x400000 << ds_); + } else { + std::uint32_t diff = (now() - lastTime_).tv_sec; + seconds_ += diff; + lastTime_.tv_sec += diff; + } +} + +void Time::cyclesFromTime(unsigned long const cc) { + update(cc); + timeval diff = now() - lastTime_; + lastCycles_ = cc - diff.tv_usec * ((0x400000 << ds_) / 1000000.0f); +} + +void Time::timeFromCycles(unsigned long const cc) { + update(cc); + unsigned long diff = cc - lastCycles_; + timeval usec = { 0, (long)(diff / ((0x400000 << ds_) / 1000000.0f)) }; + lastTime_ = now() - usec; +} + +SYNCFUNC(Time) +{ + NSS(seconds_); + NSS(lastTime_.tv_sec); + NSS(lastTime_.tv_usec); + NSS(lastCycles_); + NSS(useCycles_); + NSS(ds_); +} + +} diff --git a/libgambatte/src/mem/time.h b/libgambatte/src/mem/time.h new file mode 100644 index 0000000000..aa8e49b5e3 --- /dev/null +++ b/libgambatte/src/mem/time.h @@ -0,0 +1,78 @@ +// +// Copyright (C) 2007 by sinamas +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + +#ifndef TIME_H +#define TIME_H + +#include +#include +#include +#include "newstate.h" + +namespace gambatte { + +struct SaveState; + +struct timeval { + std::uint32_t tv_sec; + std::uint32_t tv_usec; +}; + +class Time { +public: + static timeval now() { + long long micros = std::chrono::duration_cast( + std::chrono::high_resolution_clock::now().time_since_epoch()) + .count(); + timeval t; + t.tv_usec = micros % 1000000; + t.tv_sec = micros / 1000000; + return t; + } + + Time(); + void loadState(SaveState const &state); + + std::uint32_t get(unsigned long cycleCounter); + void set(std::uint32_t seconds, unsigned long cycleCounter); + void reset(std::uint32_t seconds, unsigned long cycleCounter); + void resetCc(unsigned long oldCc, unsigned long newCc); + void speedChange(unsigned long cycleCounter); + + timeval baseTime(unsigned long cycleCounter); + void setBaseTime(timeval baseTime, unsigned long cycleCounter); + void setTimeMode(bool useCycles, unsigned long cycleCounter); + +private: + std::uint32_t seconds_; + timeval lastTime_; + unsigned long lastCycles_; + bool useCycles_; + bool ds_; + + void update(unsigned long cycleCounter); + void cyclesFromTime(unsigned long cycleCounter); + void timeFromCycles(unsigned long cycleCounter); + +public: + templatevoid SyncState(NewState *ns); +}; + +} + +#endif diff --git a/libgambatte/src/memory.cpp b/libgambatte/src/memory.cpp index 2e39855eda..6ddce9da05 100644 --- a/libgambatte/src/memory.cpp +++ b/libgambatte/src/memory.cpp @@ -345,6 +345,7 @@ unsigned long Memory::stop(unsigned long cc) { if (ioamhram_[0x14D] & isCgb()) { psg_.generateSamples(cc + 4, isDoubleSpeed()); lcd_.speedChange((cc + 7) & ~7); + cart_.speedChange(cc); ioamhram_[0x14D] ^= 0x81; intreq_.setEventTime(ioamhram_[0x140] & lcdc_en ? lcd_.nextMode1IrqTime() @@ -403,6 +404,7 @@ unsigned long Memory::resetCounters(unsigned long cc) { unsigned long const oldCC = cc; cc -= dec; intreq_.resetCc(oldCC, cc); + cart_.resetCc(oldCC, cc); tima_.resetCc(oldCC, cc, TimaInterruptRequester(intreq_)); lcd_.resetCc(oldCC, cc); psg_.resetCounter(cc, oldCC, isDoubleSpeed()); @@ -1099,7 +1101,7 @@ void Memory::nontrivial_write(unsigned const p, unsigned const data, unsigned lo if (p < 0xFE00) { if (p < 0xA000) { if (p < 0x8000) { - cart_.mbcWrite(p, data); + cart_.mbcWrite(p, data, cc); } else if (lcd_.vramAccessible(cc)) { lcd_.vramChange(cc); cart_.vrambankptr()[p] = data; @@ -1108,7 +1110,7 @@ void Memory::nontrivial_write(unsigned const p, unsigned const data, unsigned lo if (cart_.wsrambankptr()) cart_.wsrambankptr()[p] = data; else - cart_.rtcWrite(data); + cart_.rtcWrite(data, cc); } else cart_.wramdata(p >> 12 & 1)[p & 0xFFF] = data; } else if (p - 0xFF80u >= 0x7Fu) { diff --git a/libgambatte/src/memory.h b/libgambatte/src/memory.h index 051a5ea904..f00e28ac24 100644 --- a/libgambatte/src/memory.h +++ b/libgambatte/src/memory.h @@ -29,6 +29,7 @@ static unsigned char const agbOverride[0xD] = { 0xFF, 0x00, 0xCD, 0x03, 0x35, 0x #include "gambatte.h" namespace gambatte { + class FilterInfo; class Memory { @@ -42,9 +43,9 @@ public: int debugGetLY() const { return lcd_.debugGetLY(); } void setStatePtrs(SaveState &state); void loadState(SaveState const &state); - void loadSavedata(char const *data) { cart_.loadSavedata(data); } + void loadSavedata(char const *data, unsigned long const cc) { cart_.loadSavedata(data, cc); } int saveSavedataLength() {return cart_.saveSavedataLength(); } - void saveSavedata(char *dest) { cart_.saveSavedata(dest); } + void saveSavedata(char *dest, unsigned long const cc) { cart_.saveSavedata(dest, cc); } void updateInput(); void setBios(char const *buffer, std::size_t size) { @@ -243,10 +244,6 @@ public: lcd_.setScanlineCallback(callback, sl); } - void setRTCCallback(std::uint32_t (*callback)()) { - cart_.setRTCCallback(callback); - } - void setLinkCallback(void(*callback)()) { this->linkCallback_ = callback; } @@ -266,6 +263,9 @@ public: } void setCgbPalette(unsigned *lut); + void setTimeMode(bool useCycles, unsigned long const cc) { + cart_.setTimeMode(useCycles, cc); + } int linkStatus(int which); diff --git a/libgambatte/src/savestate.h b/libgambatte/src/savestate.h index e56290685d..c130ab5ecb 100644 --- a/libgambatte/src/savestate.h +++ b/libgambatte/src/savestate.h @@ -36,7 +36,7 @@ struct SaveState { void set(T *p, std::size_t size) { ptr = p; size_ = size; } friend class SaverList; - friend void setInitState(SaveState &, bool, bool, std::uint32_t, unsigned); + friend void setInitState(SaveState &, bool, bool); private: T *ptr; std::size_t size_; @@ -190,8 +190,14 @@ struct SaveState { unsigned long cycleCounter; } spu; + struct Time { + unsigned long seconds; + unsigned long lastTimeSec; + unsigned long lastTimeUsec; + unsigned long lastCycles; + } time; + struct RTC { - unsigned long baseTime; unsigned long haltTime; unsigned char dataDh; unsigned char dataDl; diff --git a/output/dll/libgambatte.dll b/output/dll/libgambatte.dll index 2d96928fd030cb9b9bd00955371f0b3847f7a58e..17b09b4e0ac45dce42e273eb93cc6acafc2ba02c 100644 GIT binary patch delta 70820 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 AcmMzZ From cb7e0b0a8290834e4643cb86e01f4560ed23f870 Mon Sep 17 00:00:00 2001 From: MrWint Date: Thu, 30 May 2019 20:16:54 +0200 Subject: [PATCH 35/38] libgambatte: Remove unused RTC callback --- libgambatte/include/gambatte.h | 1 - output/dll/libgambatte.dll | Bin 170496 -> 170496 bytes 2 files changed, 1 deletion(-) diff --git a/libgambatte/include/gambatte.h b/libgambatte/include/gambatte.h index d86187c0fd..1a841deccd 100644 --- a/libgambatte/include/gambatte.h +++ b/libgambatte/include/gambatte.h @@ -125,7 +125,6 @@ public: void setCDCallback(CDCallback); void setTraceCallback(void (*callback)(void *)); void setScanlineCallback(void (*callback)(), int sl); - void setRTCCallback(std::uint32_t (*callback)()); void setLinkCallback(void(*callback)()); /** Use cycle-based RTC instead of real-time. */ diff --git a/output/dll/libgambatte.dll b/output/dll/libgambatte.dll index 17b09b4e0ac45dce42e273eb93cc6acafc2ba02c..c129498a5a16a3b8c5530974f8b960729f199a42 100644 GIT binary patch delta 109 zcmZqJ!qu>aYXb)(Gn4FxW^TrIZbruK+>A^weXW54F$_S!1jODz%u~X|z!L}J0mYzl lJV3Sx5Jv#zA`o)Z_5GP7K_+hZ_Gh}v$H=-}F^=iJH2{+h6*m9? delta 109 zcmZqJ!qu>aYXb)(^Sn84o4Fa=xfvO^b2Bo%^tA>G#4rE>6A*g?F;58-15X@?$G`xS l;{mcofH(pu7lDwQuJ6wz2{LiJw?ETWK1P=9ig8T$tpUKu8m<5U From 73f4b0bf52fb1af879bf61dc93dc5b2469638981 Mon Sep 17 00:00:00 2001 From: MrWint Date: Thu, 30 May 2019 20:53:17 +0200 Subject: [PATCH 36/38] libgambatte: fix setTimeMode typo --- libgambatte/src/cinterface.cpp | 2 +- output/dll/libgambatte.dll | Bin 170496 -> 170496 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/libgambatte/src/cinterface.cpp b/libgambatte/src/cinterface.cpp index 51c4730ae1..9d6e862e36 100644 --- a/libgambatte/src/cinterface.cpp +++ b/libgambatte/src/cinterface.cpp @@ -68,7 +68,7 @@ GBEXPORT void gambatte_setlayers(GB *g, unsigned mask) { g->setLayers(mask); } -GBEXPORT void gambatte_setTimeMode(GB *g, bool useCycles) { +GBEXPORT void gambatte_settimemode(GB *g, bool useCycles) { g->setTimeMode(useCycles); } diff --git a/output/dll/libgambatte.dll b/output/dll/libgambatte.dll index c129498a5a16a3b8c5530974f8b960729f199a42..de86acd89ffbcd566dfa3495ec62a43b03e4462c 100644 GIT binary patch delta 182 zcmZqJ!qu>aYXb)(ldozs7h^jYBja{1My6N3R=%nqViaYXb)(6O(K+7h^jYBja{1My6N3R!p)VVi-jTD0nOX)<0*UUTGL$$nQ~Nhx|kT8 zfjA0?%Ye8ah?fKLF(AGP#Gined;8TwCO;;Qkj&gv-~5!+?P4WNT#VaIOPNlq007*! BDHi|$ From 1c0f8ff36b3876d13b05d9452c924f7dc30fbe12 Mon Sep 17 00:00:00 2001 From: MrWint Date: Thu, 30 May 2019 21:11:58 +0200 Subject: [PATCH 37/38] libgambatte: Update makefiles --- libgambatte/Makefile | 1 + libgambatte/libgambatte.vcxproj | 6 +- libgambatte/libgambatte.vcxproj.filters | 180 ++++++++++++------------ 3 files changed, 90 insertions(+), 97 deletions(-) diff --git a/libgambatte/Makefile b/libgambatte/Makefile index 1a2c7ddeed..56452b0f38 100644 --- a/libgambatte/Makefile +++ b/libgambatte/Makefile @@ -30,6 +30,7 @@ SRCS = \ src/mem/cartridge.cpp \ src/mem/memptrs.cpp \ src/mem/rtc.cpp \ + src/mem/time.cpp \ src/newstate.cpp \ src/sound.cpp \ src/sound/channel1.cpp \ diff --git a/libgambatte/libgambatte.vcxproj b/libgambatte/libgambatte.vcxproj index 6612bff667..37d3a96a03 100644 --- a/libgambatte/libgambatte.vcxproj +++ b/libgambatte/libgambatte.vcxproj @@ -156,15 +156,12 @@ + - - - - @@ -189,6 +186,7 @@ + diff --git a/libgambatte/libgambatte.vcxproj.filters b/libgambatte/libgambatte.vcxproj.filters index f096a22f6b..b34a8d79f1 100644 --- a/libgambatte/libgambatte.vcxproj.filters +++ b/libgambatte/libgambatte.vcxproj.filters @@ -15,46 +15,58 @@ - - Header Files - Header Files - + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + Header Files Header Files - - Header Files - - - Header Files - Header Files Header Files - + Header Files - + + Header Files + + + Header Files + + Header Files Header Files - - Header Files - - - Header Files - - + Header Files @@ -69,39 +81,12 @@ Header Files - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - Header Files Header Files - - Header Files - Header Files @@ -111,22 +96,31 @@ Header Files - - Header Files - - - Header Files - - - Header Files - - - Header Files - Header Files - + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + Header Files @@ -134,37 +128,37 @@ Source Files - - Source Files - - - Source Files - - - Source Files - Source Files - - Source Files - - - Source Files - - - Source Files - - + Source Files Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + Source Files - + + Source Files + + + Source Files + + Source Files @@ -185,28 +179,28 @@ Source Files - - Source Files - Source Files - - Source Files - - - Source Files - - - Source Files - Source Files - + Source Files - + + Source Files + + + Source Files + + + Source Files + + + Source Files + + Source Files From a93916e24cf0abc1d329986dd6bf897f607a66a5 Mon Sep 17 00:00:00 2001 From: MrWint Date: Sun, 2 Jun 2019 13:24:54 +0200 Subject: [PATCH 38/38] Gambatte core: Add RTC divisor offset support. --- .../Nintendo/Gameboy/Gambatte.ISettable.cs | 5 +++++ .../Consoles/Nintendo/Gameboy/Gambatte.cs | 1 + .../Consoles/Nintendo/Gameboy/LibGambatte.cs | 10 ++++++++++ libgambatte/include/gambatte.h | 3 +++ libgambatte/src/cinterface.cpp | 4 ++++ libgambatte/src/cpu.h | 1 + libgambatte/src/gambatte.cpp | 4 ++++ libgambatte/src/mem/cartridge.h | 1 + libgambatte/src/mem/time.cpp | 9 +++++---- libgambatte/src/mem/time.h | 2 ++ libgambatte/src/memory.h | 1 + output/dll/libgambatte.dll | Bin 170496 -> 171008 bytes 12 files changed, 37 insertions(+), 4 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISettable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISettable.cs index 3681c532d3..70a2aef2fe 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISettable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISettable.cs @@ -116,6 +116,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy [DefaultValue(false)] public bool RealTimeRTC { get; set; } + [DisplayName("RTC Divisor Offset")] + [Description("CPU clock frequency relative to real time clock. Base value is 2^22 Hz. Used in cycle-based RTC to sync on real hardware to account for RTC imperfections.")] + [DefaultValue(0)] + public int RTCDivisorOffset { get; set; } + [DisplayName("Equal Length Frames")] [Description("When false, emulation frames sync to vblank. Only useful for high level TASing.")] [DefaultValue(false)] diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs index 3b837f5b78..0de16d0723 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs @@ -130,6 +130,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy { LibGambatte.gambatte_settimemode(GambatteState, false); } + LibGambatte.gambatte_setrtcdivisoroffset(GambatteState, _syncSettings.RTCDivisorOffset); _cdCallback = new LibGambatte.CDCallback(CDCallbackProc); diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibGambatte.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibGambatte.cs index 038ff62021..d156afdb63 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibGambatte.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibGambatte.cs @@ -280,6 +280,16 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy [DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)] public static extern void gambatte_settimemode(IntPtr core, bool useCycles); + /// + /// Adjusts the CPU clock frequency relative to real time. Base value is 2^22 Hz. + /// This is used to account for drift in the RTC when syncing cycle-based RTC to real hardware. + /// RTCs in carts are not perfectly accurate, and the value will differ from cart to cart. + /// + /// opaque state pointer + /// CPU frequency adjustment + [DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)] + public static extern void gambatte_setrtcdivisoroffset(IntPtr core, int rtcDivisorOffset); + /// /// Returns true if the currently loaded ROM image is treated as having CGB support. /// diff --git a/libgambatte/include/gambatte.h b/libgambatte/include/gambatte.h index 1a841deccd..368b2a4727 100644 --- a/libgambatte/include/gambatte.h +++ b/libgambatte/include/gambatte.h @@ -130,6 +130,9 @@ public: /** Use cycle-based RTC instead of real-time. */ void setTimeMode(bool useCycles); + /** adjust the assumed clock speed of the CPU compared to the RTC */ + void setRtcDivisorOffset(long const rtcDivisorOffset); + /** Returns true if the currently loaded ROM image is treated as having CGB support. */ bool isCgb() const; diff --git a/libgambatte/src/cinterface.cpp b/libgambatte/src/cinterface.cpp index 9d6e862e36..f9b4ced4fb 100644 --- a/libgambatte/src/cinterface.cpp +++ b/libgambatte/src/cinterface.cpp @@ -72,6 +72,10 @@ GBEXPORT void gambatte_settimemode(GB *g, bool useCycles) { g->setTimeMode(useCycles); } +GBEXPORT void gambatte_setrtcdivisoroffset(GB *g, int rtcDivisorOffset) { + g->setRtcDivisorOffset(rtcDivisorOffset); +} + GBEXPORT void gambatte_reset(GB *g) { g->reset(); } diff --git a/libgambatte/src/cpu.h b/libgambatte/src/cpu.h index 6059b0fad4..96c1a25b4f 100644 --- a/libgambatte/src/cpu.h +++ b/libgambatte/src/cpu.h @@ -91,6 +91,7 @@ public: mem_.setCgbPalette(lut); } void setTimeMode(bool useCycles) { mem_.setTimeMode(useCycles, cycleCounter_); } + void setRtcDivisorOffset(long const rtcDivisorOffset) { mem_.setRtcDivisorOffset(rtcDivisorOffset); } void setBios(char const *buffer, std::size_t size) { mem_.setBios(buffer, size); } bool gbIsCgb() { return mem_.gbIsCgb(); } diff --git a/libgambatte/src/gambatte.cpp b/libgambatte/src/gambatte.cpp index d222874ac6..0bc0639a76 100644 --- a/libgambatte/src/gambatte.cpp +++ b/libgambatte/src/gambatte.cpp @@ -134,6 +134,10 @@ void GB::setTimeMode(bool useCycles) { p_->cpu.setTimeMode(useCycles); } +void GB::setRtcDivisorOffset(long const rtcDivisorOffset) { + p_->cpu.setRtcDivisorOffset(rtcDivisorOffset); +} + LoadRes GB::load(char const *romfiledata, unsigned romfilelength, unsigned const flags) { LoadRes const loadres = p_->cpu.load(romfiledata, romfilelength, flags & FORCE_DMG, flags & MULTICART_COMPAT); diff --git a/libgambatte/src/mem/cartridge.h b/libgambatte/src/mem/cartridge.h index db46afb262..e07baa4aa8 100644 --- a/libgambatte/src/mem/cartridge.h +++ b/libgambatte/src/mem/cartridge.h @@ -71,6 +71,7 @@ public: void resetCc(unsigned long const oldCc, unsigned long const newCc) { time_.resetCc(oldCc, newCc); } void speedChange(unsigned long const cc) { time_.speedChange(cc); } void setTimeMode(bool useCycles, unsigned long const cc) { time_.setTimeMode(useCycles, cc); } + void setRtcDivisorOffset(long const rtcDivisorOffset) { time_.setRtcDivisorOffset(rtcDivisorOffset); } void rtcWrite(unsigned data, unsigned long const cc) { rtc_.write(data, cc); } unsigned char rtcRead() const { return *rtc_.activeData(); } void loadSavedata(char const *data, unsigned long cycleCounter); diff --git a/libgambatte/src/mem/time.cpp b/libgambatte/src/mem/time.cpp index 71e92d1356..87ffb7e10c 100644 --- a/libgambatte/src/mem/time.cpp +++ b/libgambatte/src/mem/time.cpp @@ -34,6 +34,7 @@ static timeval operator-(timeval l, timeval r) { Time::Time() : useCycles_(true) +, rtcDivisor_(0x400000) { } @@ -108,9 +109,9 @@ void Time::setTimeMode(bool useCycles, unsigned long const cc) { void Time::update(unsigned long const cc) { if (useCycles_) { - std::uint32_t diff = (cc - lastCycles_) / (0x400000 << ds_); + std::uint32_t diff = (cc - lastCycles_) / (rtcDivisor_ << ds_); seconds_ += diff; - lastCycles_ += diff * (0x400000 << ds_); + lastCycles_ += diff * (rtcDivisor_ << ds_); } else { std::uint32_t diff = (now() - lastTime_).tv_sec; seconds_ += diff; @@ -121,13 +122,13 @@ void Time::update(unsigned long const cc) { void Time::cyclesFromTime(unsigned long const cc) { update(cc); timeval diff = now() - lastTime_; - lastCycles_ = cc - diff.tv_usec * ((0x400000 << ds_) / 1000000.0f); + lastCycles_ = cc - diff.tv_usec * ((rtcDivisor_ << ds_) / 1000000.0f); } void Time::timeFromCycles(unsigned long const cc) { update(cc); unsigned long diff = cc - lastCycles_; - timeval usec = { 0, (long)(diff / ((0x400000 << ds_) / 1000000.0f)) }; + timeval usec = { 0, (long)(diff / ((rtcDivisor_ << ds_) / 1000000.0f)) }; lastTime_ = now() - usec; } diff --git a/libgambatte/src/mem/time.h b/libgambatte/src/mem/time.h index aa8e49b5e3..3b720bea78 100644 --- a/libgambatte/src/mem/time.h +++ b/libgambatte/src/mem/time.h @@ -57,12 +57,14 @@ public: timeval baseTime(unsigned long cycleCounter); void setBaseTime(timeval baseTime, unsigned long cycleCounter); void setTimeMode(bool useCycles, unsigned long cycleCounter); + void setRtcDivisorOffset(long const rtcDivisorOffset) { rtcDivisor_ = 0x400000L + rtcDivisorOffset; } private: std::uint32_t seconds_; timeval lastTime_; unsigned long lastCycles_; bool useCycles_; + unsigned long rtcDivisor_; bool ds_; void update(unsigned long cycleCounter); diff --git a/libgambatte/src/memory.h b/libgambatte/src/memory.h index f00e28ac24..7d7dcc1d73 100644 --- a/libgambatte/src/memory.h +++ b/libgambatte/src/memory.h @@ -266,6 +266,7 @@ public: void setTimeMode(bool useCycles, unsigned long const cc) { cart_.setTimeMode(useCycles, cc); } + void setRtcDivisorOffset(long const rtcDivisorOffset) { cart_.setRtcDivisorOffset(rtcDivisorOffset); } int linkStatus(int which); diff --git a/output/dll/libgambatte.dll b/output/dll/libgambatte.dll index de86acd89ffbcd566dfa3495ec62a43b03e4462c..e482dab5d7df3febde5246fe253eec2c6ed619d5 100644 GIT binary patch delta 58193 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 XNIYRFf4b-xZ}3vby&>twty2F7GS^vK delta 58091 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