diff --git a/libgambatte/array.h b/libgambatte/array.h new file mode 100644 index 0000000000..9b692d8412 --- /dev/null +++ b/libgambatte/array.h @@ -0,0 +1,52 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre Aamås * + * sinamas@users.sourceforge.net * + * * + * 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 ARRAY_H +#define ARRAY_H + +#include + +template +class SimpleArray : Uncopyable { +public: + explicit SimpleArray(std::size_t size = 0) : a_(size ? new T[size] : 0) {} + ~SimpleArray() { delete[] defined_ptr(a_); } + void reset(std::size_t size = 0) { delete[] defined_ptr(a_); a_ = size ? new T[size] : 0; } + T* get() const { return a_; } + operator T* () const { return a_; } + +private: + T* a_; +}; + +class Uncopyable { +protected: + Uncopyable() {} +private: + Uncopyable(Uncopyable const&); + Uncopyable& operator=(Uncopyable const&); +}; + +template +inline T* defined_ptr(T* t) { + typedef char type_is_defined[sizeof * t ? 1 : -1]; + (void)sizeof(type_is_defined); + return t; +} + +#endif \ No newline at end of file diff --git a/libgambatte/libgambatte.vcxproj b/libgambatte/libgambatte.vcxproj index 37d3a96a03..e0e201771f 100644 --- a/libgambatte/libgambatte.vcxproj +++ b/libgambatte/libgambatte.vcxproj @@ -22,32 +22,32 @@ {5D630682-7BDA-474D-B387-0EB420DDC199} Win32Proj libgambatte - 8.1 + 10.0 DynamicLibrary true - v141 + v142 Unicode DynamicLibrary false - v141 + v142 true Unicode DynamicLibrary true - v141 + v142 Unicode DynamicLibrary false - v141 + v142 true Unicode @@ -154,9 +154,11 @@ + + @@ -180,18 +182,21 @@ + + - + + diff --git a/libgambatte/libgambatte.vcxproj.filters b/libgambatte/libgambatte.vcxproj.filters index b34a8d79f1..504d75698f 100644 --- a/libgambatte/libgambatte.vcxproj.filters +++ b/libgambatte/libgambatte.vcxproj.filters @@ -111,9 +111,6 @@ Header Files - - Header Files - Header Files @@ -123,6 +120,21 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + @@ -203,5 +215,8 @@ Source Files + + Source Files + \ No newline at end of file diff --git a/libgambatte/src/Interrupter.cpp b/libgambatte/src/Interrupter.cpp new file mode 100644 index 0000000000..3ee5af603e --- /dev/null +++ b/libgambatte/src/Interrupter.cpp @@ -0,0 +1,103 @@ +// +// 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 "interrupter.h" +#include "memory.h" + +namespace gambatte { + + Interrupter::Interrupter(unsigned short& sp, unsigned short& pc, unsigned char& opcode, bool& prefetched) + : sp_(sp) + , pc_(pc) + , opcode_(opcode) + , prefetched_(prefetched) + { + } + + void Interrupter::prefetch(unsigned long cc, Memory& mem) { + if (!prefetched_) { + opcode_ = mem.read(pc_, cc); + pc_ = (pc_ + 1) & 0xFFFF; + prefetched_ = true; + } + } + + unsigned long Interrupter::interrupt(unsigned long cc, Memory& memory) { + // undo prefetch (presumably unconditional on hw). + if (prefetched_) { + pc_ = (pc_ - 1) & 0xFFFF; + prefetched_ = false; + } + cc += 12; + sp_ = (sp_ - 1) & 0xFFFF; + memory.write(sp_, pc_ >> 8, cc); + cc += 4; + + unsigned const pendingIrqs = memory.pendingIrqs(cc); + unsigned const n = pendingIrqs & -pendingIrqs; + unsigned address; + if (n <= 4) { + static unsigned char const lut[] = { 0x00, 0x40, 0x48, 0x48, 0x50 }; + address = lut[n]; + } + else + address = 0x50 + n; + + sp_ = (sp_ - 1) & 0xFFFF; + memory.write(sp_, pc_ & 0xFF, cc); + memory.ackIrq(n, cc); + pc_ = address; + cc += 4; + + if (address == 0x40 && !gsCodes_.empty()) + applyVblankCheats(cc, memory); + + return cc; + } + + static int asHex(char c) { + return c >= 'A' ? c - 'A' + 0xA : c - '0'; + } + + void Interrupter::setGameShark(std::string const& codes) { + std::string code; + gsCodes_.clear(); + + for (std::size_t pos = 0; pos < codes.length(); pos += code.length() + 1) { + code = codes.substr(pos, codes.find(';', pos) - pos); + if (code.length() >= 8) { + GsCode gs; + gs.type = asHex(code[0]) << 4 | asHex(code[1]); + gs.value = (asHex(code[2]) << 4 | asHex(code[3])) & 0xFF; + gs.address = (asHex(code[4]) << 4 + | asHex(code[5]) + | asHex(code[6]) << 12 + | asHex(code[7]) << 8) & 0xFFFF; + gsCodes_.push_back(gs); + } + } + } + + void Interrupter::applyVblankCheats(unsigned long const cc, Memory& memory) { + for (std::size_t i = 0, size = gsCodes_.size(); i < size; ++i) { + if (gsCodes_[i].type == 0x01) + memory.write(gsCodes_[i].address, gsCodes_[i].value, cc); + } + } + +} \ No newline at end of file diff --git a/libgambatte/src/array.h b/libgambatte/src/array.h new file mode 100644 index 0000000000..239af8f2ca --- /dev/null +++ b/libgambatte/src/array.h @@ -0,0 +1,57 @@ +/*************************************************************************** + * Copyright (C) 2008 by Sindre Aamås * + * sinamas@users.sourceforge.net * + * * + * 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 ARRAY_H +#define ARRAY_H + +#include + +template +class SimpleArray : Uncopyable { +public: + explicit SimpleArray(std::size_t size = 0) : a_(size ? new T[size] : 0) {} + ~SimpleArray() { delete[] defined_ptr(a_); } + void reset(std::size_t size = 0) { delete[] defined_ptr(a_); a_ = size ? new T[size] : 0; } + T* get() const { return a_; } + operator T* () const { return a_; } + +private: + T* a_; +}; + +class Uncopyable { +protected: + Uncopyable() {} +private: + Uncopyable(Uncopyable const&); + Uncopyable& operator=(Uncopyable const&); +}; + +template +inline T* defined_ptr(T* t) { + typedef char type_is_defined[sizeof * t ? 1 : -1]; + (void)sizeof(type_is_defined); + return t; +} + +template +inline void defined_delete(T* t) { delete defined_ptr(t); } + +struct defined_deleter { template static void del(T* p) { defined_delete(p); } }; + +#endif \ No newline at end of file diff --git a/libgambatte/src/cpu.cpp b/libgambatte/src/cpu.cpp index d086c9842f..72e3d43fbd 100644 --- a/libgambatte/src/cpu.cpp +++ b/libgambatte/src/cpu.cpp @@ -23,7 +23,7 @@ namespace gambatte { CPU::CPU() -: mem_(sp, pc) +: mem_(Interrupter(sp, pc, opcode_, prefetched_)) , cycleCounter_(0) , pc(0x100) , sp(0xFFFE) @@ -38,7 +38,8 @@ CPU::CPU() , e(0xD8) , h(0x01) , l(0x4D) -, skip_(false) +, opcode_(0) +, prefetched_(false) , numInterruptAddresses() , tracecallback(0) { @@ -103,7 +104,13 @@ void CPU::loadState(SaveState const &state) { cf = cfFromF(state.cpu.f); h = state.cpu.h & 0xFF; l = state.cpu.l & 0xFF; - skip_ = state.cpu.skip; + opcode_ = state.cpu.opcode; + prefetched_ = state.cpu.prefetched; + if (state.cpu.skip) { + opcode_ = mem_.read(pc, cycleCounter_); + prefetched_ = true; + } + } // The main reasons for the use of macros is to more conveniently be able to tweak @@ -111,12 +118,11 @@ void CPU::loadState(SaveState const &state) { // time they were written GCC had a tendency to not be able to keep hot variables // in regs if you took an address/reference in an inline function. -#define bc() ( b << 8 | c ) -#define de() ( d << 8 | e ) -#define hl() ( h << 8 | l ) +#define bc() ( b * 0x100u | c ) +#define de() ( d * 0x100u | e ) +#define hl() ( h * 0x100u | 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 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) @@ -174,7 +180,7 @@ void CPU::loadState(SaveState const &state) { // Rotate 8-bit register right through CF, store old bit0 in CF, old CF value becomes bit7. Reset SF and HCF, Check ZF: #define rr_r(r) do { \ unsigned const oldcf = cf & 0x100; \ - cf = (r) << 8; \ + cf = (r) * 0x100u; \ (r) = zf = ((r) | oldcf) >> 1; \ hf2 = 0; \ } while (0) @@ -190,7 +196,7 @@ void CPU::loadState(SaveState const &state) { // sra r (8 cycles): // Shift 8-bit register right, store old bit0 in CF. bit7=old bit7. Reset SF and HCF, Check ZF: #define sra_r(r) do { \ - cf = (r) << 8; \ + cf = (r) * 0x100u; \ zf = (r) >> 1; \ (r) = zf | ((r) & 0x80); \ hf2 = 0; \ @@ -200,7 +206,7 @@ void CPU::loadState(SaveState const &state) { // Shift 8-bit register right, store old bit0 in CF. Reset SF and HCF, Check ZF: #define srl_r(r) do { \ zf = (r); \ - cf = (r) << 8; \ + cf = (r) * 0x100u; \ zf >>= 1; \ (r) = zf; \ hf2 = 0; \ @@ -261,7 +267,7 @@ void CPU::loadState(SaveState const &state) { unsigned const hl = hl(); \ unsigned val; \ READ(val, hl); \ - val &= ~(1 << (n)); \ + val &= ~(1u << (n)); \ WRITE(hl, val); \ } while (0) @@ -461,9 +467,8 @@ 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 = n; \ + push_rr(pc >> 8, pc & 0xFF); \ + pc = (n); \ } while (0) // ret (16 cycles): @@ -474,6 +479,17 @@ void CPU::loadState(SaveState const &state) { PC_MOD(high << 8 | low); \ } while (0) +namespace { + unsigned long freeze(Memory & mem, unsigned long cc) { + mem.freeze(cc); + if (cc < mem.nextEventTime()) { + unsigned long cycles = mem.nextEventTime() - cc; + cc += cycles + (-cycles & 3); + } + return cc; + } +} + void CPU::process(unsigned long const cycles) { mem_.setEndtime(cycleCounter_, cycles); mem_.updateInput(); @@ -524,19 +540,23 @@ void CPU::process(unsigned long const cycles) { result[8] = toF(hf2, cf, zf); result[9] = h; result[10] = l; - result[11] = skip_; - PC_READ_FIRST(opcode); + result[11] = prefetched_; + //PC_READ_FIRST(opcode); result[12] = opcode; result[13] = mem_.debugGetLY(); tracecallback((void *)result); } else { - PC_READ_FIRST(opcode); + //PC_READ_FIRST(opcode); } - if (skip_) { - pc = (pc - 1) & 0xFFFF; - skip_ = false; + if (!prefetched_) { + PC_READ(opcode); + } + else { + opcode = opcode_; + cycleCounter += 4; + prefetched_ = false; } switch (opcode) { @@ -607,7 +627,7 @@ void CPU::process(unsigned long const cycles) { // rrca (4 cycles): // Rotate 8-bit register A right, store old bit0 in CF. Reset SF, HCF, ZF: case 0x0F: - cf = a << 8 | a; + cf = a * 0x100u | a; a = cf >> 1 & 0xFF; hf2 = 0; zf = 1; @@ -616,14 +636,13 @@ void CPU::process(unsigned long const cycles) { // stop (4 cycles): // Halt CPU and LCD display until button pressed: case 0x10: - { - cycleCounter = mem_.stop(cycleCounter); - - if (cycleCounter < mem_.nextEventTime()) { - unsigned long cycles = mem_.nextEventTime() - cycleCounter; - cycleCounter += cycles + (-cycles & 3); - } + PC_READ(opcode_); + cycleCounter = mem_.stop(cycleCounter - 4, prefetched_); + if (cycleCounter < mem_.nextEventTime()) { + unsigned long cycles = mem_.nextEventTime() - cycleCounter; + cycleCounter += cycles + (-cycles & 3); } + break; case 0x11: @@ -687,7 +706,7 @@ void CPU::process(unsigned long const cycles) { case 0x1F: { unsigned oldcf = cf & 0x100; - cf = a << 8; + cf = a * 0x100u; a = (a | oldcf) >> 1; } @@ -1023,14 +1042,12 @@ void CPU::process(unsigned long const cycles) { // halt (4 cycles): case 0x76: - if (mem_.ff_read(0x0F, cycleCounter) & mem_.ff_read(0xFF, cycleCounter) & 0x1F) { - if (mem_.ime()) - pc = (pc - 1) & 0xFFFF; - else - skip_ = true; + opcode_ = mem_.read(pc, cycleCounter); + if (mem_.pendingIrqs(cycleCounter)) { + prefetched_ = true; } else { - mem_.halt(cycleCounter); - + prefetched_ = mem_.halt(cycleCounter); + cycleCounter += 4 + 4 * !mem_.isCgb(); if (cycleCounter < mem_.nextEventTime()) { unsigned long cycles = mem_.nextEventTime() - cycleCounter; cycleCounter += cycles + (-cycles & 3); @@ -1705,13 +1722,7 @@ void CPU::process(unsigned long const cycles) { break; 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); - } + cycleCounter = freeze(mem_, cycleCounter); break; // call nc,nn (24;12 cycles): @@ -1779,13 +1790,7 @@ void CPU::process(unsigned long const cycles) { break; 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); - } + cycleCounter = freeze(mem_, cycleCounter); break; // call z,nn (24;12 cycles): @@ -1801,13 +1806,7 @@ void CPU::process(unsigned long const cycles) { break; 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); - } + cycleCounter = freeze(mem_, cycleCounter); break; case 0xDE: @@ -1846,13 +1845,7 @@ void CPU::process(unsigned long const cycles) { 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); - } + cycleCounter = freeze(mem_, cycleCounter); break; case 0xE5: @@ -1898,16 +1891,10 @@ void CPU::process(unsigned long const cycles) { break; - case 0xEB: - case 0xEC: + case 0xEB: // not specified. should freeze. + case 0xEC: // not specified. should freeze. 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); - } + cycleCounter = freeze(mem_, cycleCounter); break; case 0xEE: @@ -1957,13 +1944,7 @@ void CPU::process(unsigned long const cycles) { break; 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); - } + cycleCounter = freeze(mem_, cycleCounter); break; case 0xF5: @@ -2027,15 +2008,9 @@ void CPU::process(unsigned long const cycles) { mem_.ei(cycleCounter); break; - case 0xFC: + case 0xFC: // not specified. should freeze. 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); - } + cycleCounter = freeze(mem_, cycleCounter); break; case 0xFE: { @@ -2102,7 +2077,8 @@ SYNCFUNC(CPU) NSS(e); NSS(h); NSS(l); - NSS(skip_); + NSS(opcode_); + NSS(prefetched_); } } diff --git a/libgambatte/src/cpu.h b/libgambatte/src/cpu.h index 96c1a25b4f..85df4edfe6 100644 --- a/libgambatte/src/cpu.h +++ b/libgambatte/src/cpu.h @@ -73,8 +73,8 @@ public: mem_.setLinkCallback(callback); } - LoadRes load(char const *romfiledata, unsigned romfilelength, bool forceDmg, bool multicartCompat) { - return mem_.loadROM(romfiledata, romfilelength, forceDmg, multicartCompat); + LoadRes load(char const *romfiledata, unsigned romfilelength, unsigned flags) { + return mem_.loadROM(romfiledata, romfilelength, flags); } bool loaded() const { return mem_.loaded(); } @@ -94,7 +94,6 @@ public: 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(); } unsigned char externalRead(unsigned short addr) {return mem_.peek(addr); } @@ -115,7 +114,8 @@ private: unsigned short sp; unsigned hf1, hf2, zf, cf; unsigned char a, b, c, d, e, /*f,*/ h, l; - bool skip_; + unsigned char opcode_; + bool prefetched_; int *interruptAddresses; int numInterruptAddresses; diff --git a/libgambatte/src/gambatte.cpp b/libgambatte/src/gambatte.cpp index 0bc0639a76..50a3447fce 100644 --- a/libgambatte/src/gambatte.cpp +++ b/libgambatte/src/gambatte.cpp @@ -88,7 +88,7 @@ void GB::reset() { SaveState state; p_->cpu.setStatePtrs(state); - setInitState(state, !(p_->loadflags & FORCE_DMG), p_->loadflags & GBA_CGB); + setInitState(state, !(p_->loadflags & FORCE_DMG)); p_->cpu.loadState(state); if (length > 0) { @@ -139,13 +139,13 @@ void GB::setRtcDivisorOffset(long const 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); + LoadRes const loadres = p_->cpu.load(romfiledata, romfilelength, flags); if (loadres == LOADRES_OK) { SaveState state; p_->cpu.setStatePtrs(state); p_->loadflags = flags; - setInitState(state, !(flags & FORCE_DMG), flags & GBA_CGB); + setInitState(state, !(flags & FORCE_DMG)); p_->cpu.loadState(state); } diff --git a/libgambatte/src/initstate.cpp b/libgambatte/src/initstate.cpp index 72e26c6f85..fb3588dc1b 100644 --- a/libgambatte/src/initstate.cpp +++ b/libgambatte/src/initstate.cpp @@ -1147,7 +1147,7 @@ static void setInitialDmgIoamhram(unsigned char ioamhram[]) { } // anon namespace -void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbMode) { +void gambatte::setInitState(SaveState &state, const bool cgb) { static unsigned char const cgbObjpDump[0x40] = { 0x00, 0x00, 0xF2, 0xAB, 0x61, 0xC2, 0xD9, 0xBA, @@ -1178,10 +1178,10 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM state.cpu.f = 0; state.cpu.h = 0; state.cpu.l = 0; + state.cpu.opcode = 0x00; + state.cpu.prefetched = false; state.cpu.skip = false; state.mem.biosMode = true; - state.mem.cgbSwitching = false; - state.mem.agbMode = gbaCgbMode; std::memset(state.mem.sram.ptr, 0xFF, state.mem.sram.size()); @@ -1199,26 +1199,27 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM state.mem.ioamhram.ptr[0x140] = 0; state.mem.ioamhram.ptr[0x144] = 0x00; + // DIV, TIMA, and the PSG frame sequencer are clocked by bits of the + // cycle counter less divLastUpdate (equivalent to a counter that is + // reset on DIV write). state.mem.divLastUpdate = 0; - state.mem.timaBasetime = 0; 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.minIntTime = 0; - state.mem.halttime = 0; state.mem.rombank = 1; state.mem.dmaSource = 0; state.mem.dmaDestination = 0; state.mem.rambank = 0; state.mem.oamDmaPos = 0xFE; + state.mem.haltHdmaState = 0; state.mem.IME = false; state.mem.halted = false; state.mem.enableRam = false; state.mem.rambankMode = false; state.mem.hdmaTransfer = false; - state.mem.gbIsCgb = cgb; state.mem.stopped = false; @@ -1265,15 +1266,16 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM state.ppu.nextM0Irq = 0; state.ppu.oldWy = state.mem.ioamhram.get()[0x14A]; state.ppu.pendingLcdstatIrq = false; - state.ppu.isCgb = cgb; + state.ppu.notCgbDmg = true; // spu.cycleCounter >> 12 & 7 represents the frame sequencer position. state.spu.cycleCounter = state.cpu.cycleCounter >> 1; + state.spu.lastUpdate = 0; 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.sweep.neg = false; state.spu.ch1.duty.nextPosUpdate = SoundUnit::counter_disabled; state.spu.ch1.duty.pos = 0; state.spu.ch1.duty.high = false; diff --git a/libgambatte/src/initstate.h b/libgambatte/src/initstate.h index 0e31dd35bb..13b0f28e16 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); +void setInitState(struct SaveState &state, bool cgb); } #endif diff --git a/libgambatte/src/interrupter.h b/libgambatte/src/interrupter.h new file mode 100644 index 0000000000..5daa808835 --- /dev/null +++ b/libgambatte/src/interrupter.h @@ -0,0 +1,54 @@ +// +// 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 INTERRUPTER_H +#define INTERRUPTER_H + +#include +#include + +namespace gambatte { + + struct GsCode { + unsigned short address; + unsigned char value; + unsigned char type; + }; + + class Memory; + + class Interrupter { + public: + Interrupter(unsigned short& sp, unsigned short& pc, unsigned char& opcode, bool& prefetched); + void prefetch(unsigned long cc, Memory& mem); + unsigned long interrupt(unsigned long cycleCounter, Memory& memory); + void setGameShark(std::string const& codes); + + private: + unsigned short& sp_; + unsigned short& pc_; + unsigned char& opcode_; + bool& prefetched_; + std::vector gsCodes_; + + void applyVblankCheats(unsigned long cc, Memory& mem); + }; + +} + +#endif diff --git a/libgambatte/src/interruptrequester.cpp b/libgambatte/src/interruptrequester.cpp index af37ed584e..eaa0fce20f 100644 --- a/libgambatte/src/interruptrequester.cpp +++ b/libgambatte/src/interruptrequester.cpp @@ -83,9 +83,14 @@ void InterruptRequester::flagIrq(unsigned bit) { eventTimes_.setValue(minIntTime_); } -void InterruptRequester::ackIrq(unsigned bit) { - ifreg_ ^= bit; - di(); +void InterruptRequester::flagIrq(unsigned bit, unsigned long cc) { + unsigned const prevPending = pendingIrqs(); + ifreg_ |= bit; + + if (!prevPending && pendingIrqs() && intFlags_.imeOrHalted()) { + minIntTime_ = std::max(minIntTime_, cc); + eventTimes_.setValue(minIntTime_); + } } void InterruptRequester::setIereg(unsigned iereg) { @@ -108,6 +113,13 @@ void InterruptRequester::setIfreg(unsigned ifreg) { } } +void InterruptRequester::setMinIntTime(unsigned long cc) { + minIntTime_ = cc; + + if (eventTimes_.value(intevent_interrupts) < minIntTime_) + eventTimes_.setValue(minIntTime_); +} + SYNCFUNC(InterruptRequester) { SSS(eventTimes_); diff --git a/libgambatte/src/interruptrequester.h b/libgambatte/src/interruptrequester.h index cb54265078..1b8aa3230f 100644 --- a/libgambatte/src/interruptrequester.h +++ b/libgambatte/src/interruptrequester.h @@ -52,9 +52,11 @@ public: void halt(); void unhalt(); void flagIrq(unsigned bit); - void ackIrq(unsigned bit); + void flagIrq(unsigned bit, unsigned long cc); + void ackIrq(unsigned bit) { ifreg_ &= ~bit; } void setIereg(unsigned iereg); void setIfreg(unsigned ifreg); + void setMinIntTime(unsigned long cc); IntEventId minEventId() const { return static_cast(eventTimes_.min()); } unsigned long minEventTime() const { return eventTimes_.minValue(); } @@ -71,9 +73,9 @@ private: bool halted() const { return flags_ & flag_halted; } bool imeOrHalted() const { return flags_; } void setIme() { flags_ |= flag_ime; } - void unsetIme() { flags_ &= ~flag_ime; } + void unsetIme() { flags_ &= ~(1u * flag_ime); } void setHalted() { flags_ |= flag_halted; } - void unsetHalted() { flags_ &= ~flag_halted; } + void unsetHalted() { flags_ &= ~(1u * flag_halted); } void set(bool ime, bool halted) { flags_ = halted * flag_halted + ime * flag_ime; } private: diff --git a/libgambatte/src/mem/cartridge.cpp b/libgambatte/src/mem/cartridge.cpp index 0d6539af68..165f5e77be 100644 --- a/libgambatte/src/mem/cartridge.cpp +++ b/libgambatte/src/mem/cartridge.cpp @@ -23,18 +23,18 @@ #include #include -namespace gambatte { +using namespace gambatte; namespace { -static unsigned toMulti64Rombank(unsigned rombank) { +unsigned toMulti64Rombank(unsigned rombank) { return (rombank >> 1 & 0x30) | (rombank & 0xF); } class DefaultMbc : public Mbc { public: virtual bool isAddressWithinAreaRombankCanBeMappedTo(unsigned addr, unsigned bank) const { - return (addr< 0x4000) == (bank == 0); + return (addr < rombank_size()) == (bank == 0); } virtual void SyncState(NewState *ns, bool isReader) @@ -50,8 +50,12 @@ public: { } + virtual unsigned char curRomBank() const { + return 1; + } + virtual void romWrite(unsigned const p, unsigned const data, unsigned long const /*cc*/) { - if (p < 0x2000) { + if (p < rambank_size()) { enableRam_ = (data & 0xF) == 0xA; memptrs_.setRambank(enableRam_ ? MemPtrs::read_en | MemPtrs::write_en : 0, 0); } @@ -73,12 +77,12 @@ public: } }; -static inline unsigned rambanks(MemPtrs const &memptrs) { - return std::size_t(memptrs.rambankdataend() - memptrs.rambankdata()) / 0x2000; +inline unsigned rambanks(MemPtrs const &memptrs) { + return (memptrs.rambankdataend() - memptrs.rambankdata()) / rambank_size(); } -static inline unsigned rombanks(MemPtrs const &memptrs) { - return std::size_t(memptrs.romdataend() - memptrs.romdata() ) / 0x4000; +inline unsigned rombanks(MemPtrs const &memptrs) { + return (memptrs.romdataend() - memptrs.romdata()) / rombank_size(); } class Mbc1 : public DefaultMbc { @@ -92,6 +96,10 @@ public: { } + virtual unsigned char curRomBank() const { + return rombank_; + } + virtual void romWrite(unsigned const p, unsigned const data, unsigned long const /*cc*/) { switch (p >> 13 & 3) { case 0: @@ -113,8 +121,7 @@ public: break; 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. + // Should this take effect immediately rather? rambankMode_ = data & 1; break; } @@ -143,7 +150,7 @@ private: rambank_ & (rambanks(memptrs_) - 1)); } - void setRombank() const { memptrs_.setRombank(adjustedRombank(rombank_) & (rombanks(memptrs_) - 1)); } + void setRombank() const { memptrs_.setRombank(rombank_ & (rombanks(memptrs_) - 1)); } public: @@ -166,6 +173,10 @@ public: { } + virtual unsigned char curRomBank() const { + return rombank_; + } + virtual void romWrite(unsigned const p, unsigned const data, unsigned long const /*cc*/) { switch (p >> 13 & 3) { case 0: @@ -238,6 +249,10 @@ public: { } + virtual unsigned char curRomBank() const { + return rombank_; + } + virtual void romWrite(unsigned const p, unsigned const data, unsigned long const /*cc*/) { switch (p & 0x6100) { case 0x0000: @@ -282,6 +297,10 @@ public: { } + virtual unsigned char curRomBank() const { + return rombank_; + } + virtual void romWrite(unsigned const p, unsigned const data, unsigned long const cc) { switch (p >> 13 & 3) { case 0: @@ -356,6 +375,10 @@ public: { } + virtual unsigned char curRomBank() const { + return rombank_; + } + virtual void romWrite(unsigned const p, unsigned const data, unsigned long const /*cc*/) { switch (p >> 13 & 3) { case 0: @@ -424,6 +447,10 @@ public: { } + virtual unsigned char curRomBank() const { + return rombank_; + } + virtual void romWrite(unsigned const p, unsigned const data, unsigned long const /*cc*/) { switch (p >> 13 & 3) { case 0: @@ -459,14 +486,12 @@ private: 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)); } + void setRombank() const { memptrs_.setRombank(rombank_ & (rombanks(memptrs_) - 1)); } public: virtual void SyncState(NewState *ns, bool isReader) @@ -477,12 +502,76 @@ public: } }; -static bool hasRtc(unsigned headerByte0x147) { +std::string stripExtension(std::string const& str) { + std::string::size_type const lastDot = str.find_last_of('.'); + std::string::size_type const lastSlash = str.find_last_of('/'); + + if (lastDot != std::string::npos && (lastSlash == std::string::npos || lastSlash < lastDot)) + return str.substr(0, lastDot); + + return str; +} + +std::string stripDir(std::string const& str) { + std::string::size_type const lastSlash = str.find_last_of('/'); + if (lastSlash != std::string::npos) + return str.substr(lastSlash + 1); + + return str; +} + +void enforce8bit(unsigned char* data, std::size_t size) { + if (static_cast(0x100)) + while (size--) + *data++ &= 0xFF; +} + +unsigned pow2ceil(unsigned n) { + --n; + n |= n >> 1; + n |= n >> 2; + n |= n >> 4; + n |= n >> 8; + ++n; + + return n; +} + +bool presumedMulti64Mbc1(unsigned char const header[], unsigned rombanks) { + return header[0x147] == 1 && header[0x149] == 0 && rombanks == 64; +} + +bool hasBattery(unsigned char headerByte0x147) { + switch (headerByte0x147) { + case 0x03: + case 0x06: + case 0x09: + case 0x0F: + case 0x10: + case 0x13: + case 0x1B: + case 0x1E: + case 0xFE: // huc3 + case 0xFF: + return true; + } + + return false; +} + +bool hasRtc(unsigned headerByte0x147) { switch (headerByte0x147) { case 0x0F: - case 0x10: return true; - default: return false; + case 0x10: + case 0xFE: // huc3 + return true; } + + return false; +} + +int asHex(char c) { + return c >= 'A' ? c - 'A' + 0xA : c - '0'; } } @@ -503,23 +592,6 @@ void Cartridge::loadState(SaveState const &state) { mbc_->loadState(state.mem); } -static void enforce8bit(unsigned char *data, std::size_t size) { - if (static_cast(0x100)) - while (size--) - *data++ &= 0xFF; -} - -static unsigned pow2ceil(unsigned n) { - --n; - n |= n >> 1; - n |= n >> 2; - n |= n >> 4; - n |= n >> 8; - ++n; - - return n; -} - static bool isMbc2(unsigned char h147) { return h147 == 5 || h147 == 6; } static unsigned numRambanksFromH14x(unsigned char h147, unsigned char h149) { @@ -532,10 +604,6 @@ static unsigned numRambanksFromH14x(unsigned char h147, unsigned char h149) { return 4; } -static bool presumedMulti64Mbc1(unsigned char const header[], unsigned rombanks) { - return header[0x147] == 1 && header[0x149] == 0 && rombanks == 64; -} - LoadRes Cartridge::loadROM(char const *romfiledata, unsigned romfilelength, bool const forceDmg, bool const multicartCompat) { enum Cartridgetype { type_plain, type_mbc1, @@ -610,17 +678,17 @@ LoadRes Cartridge::loadROM(char const *romfiledata, unsigned romfilelength, bool cgb = !forceDmg; } std::size_t const filesize = romfilelength; - rombanks = std::max(pow2ceil(filesize / 0x4000), 2u); + rombanks = std::max(pow2ceil(filesize / rombank_size()), 2u); mbc_.reset(); memptrs_.reset(rombanks, rambanks, cgb ? 8 : 2); rtc_.set(false, 0); - std::memcpy(memptrs_.romdata(), romfiledata, (filesize / 0x4000) * 0x4000ul); - std::memset(memptrs_.romdata() + filesize / 0x4000 * 0x4000ul, + std::memcpy(memptrs_.romdata(), romfiledata, (filesize / rombank_size() * rombank_size())); + std::memset(memptrs_.romdata() + filesize / rombank_size() * rombank_size(), 0xFF, - (rombanks - filesize / 0x4000) * 0x4000ul); - enforce8bit(memptrs_.romdata(), rombanks * 0x4000ul); + (rombanks - filesize / rombank_size()) * rombank_size()); + enforce8bit(memptrs_.romdata(), rombanks * rombank_size()); switch (type) { case type_plain: mbc_.reset(new Mbc0(memptrs_)); break; @@ -642,21 +710,6 @@ LoadRes Cartridge::loadROM(char const *romfiledata, unsigned romfilelength, bool return LOADRES_OK; } -static bool hasBattery(unsigned char headerByte0x147) { - switch (headerByte0x147) { - case 0x03: - case 0x06: - case 0x09: - case 0x0F: - case 0x10: - case 0x13: - case 0x1B: - case 0x1E: - case 0xFF: return true; - default: return false; - } -} - void Cartridge::loadSavedata(char const *data, unsigned long const cc) { if (hasBattery(memptrs_.romdata()[0x147])) { int length = memptrs_.rambankdataend() - memptrs_.rambankdata(); @@ -747,5 +800,3 @@ SYNCFUNC(Cartridge) SSS(rtc_); TSS(mbc_); } - -} diff --git a/libgambatte/src/mem/cartridge.h b/libgambatte/src/mem/cartridge.h index e07baa4aa8..2260c31349 100644 --- a/libgambatte/src/mem/cartridge.h +++ b/libgambatte/src/mem/cartridge.h @@ -34,6 +34,7 @@ namespace gambatte { class Mbc { public: virtual ~Mbc() {} + virtual unsigned char curRomBank() const = 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; @@ -62,10 +63,11 @@ public: unsigned char * wsrambankptr() const { return memptrs_.wsrambankptr(); } unsigned char * vrambankptr() const { return memptrs_.vrambankptr(); } OamDmaSrc oamDmaSrc() const { return memptrs_.oamDmaSrc(); } + bool isInOamDmaConflictArea(unsigned p) const { return memptrs_.isInOamDmaConflictArea(p); } 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(); } + unsigned char curRomBank() const { return mbc_->curRomBank(); } void mbcWrite(unsigned addr, unsigned data, unsigned long const cc) { mbc_->romWrite(addr, data, cc); } bool isCgb() const { return gambatte::isCgb(memptrs_); } void resetCc(unsigned long const oldCc, unsigned long const newCc) { time_.resetCc(oldCc, newCc); } diff --git a/libgambatte/src/mem/memptrs.cpp b/libgambatte/src/mem/memptrs.cpp index 65b5125bbf..82fd17a9bb 100644 --- a/libgambatte/src/mem/memptrs.cpp +++ b/libgambatte/src/mem/memptrs.cpp @@ -20,7 +20,56 @@ #include #include -namespace gambatte { +using namespace gambatte; + +namespace { + + template struct OamDmaConflictMap; + template struct OamDmaConflictMap { enum { r = 0xFCFF }; }; + template struct OamDmaConflictMap { enum { r = 0xFCFF }; }; + template struct OamDmaConflictMap { enum { r = 0x0300 }; }; + template struct OamDmaConflictMap { enum { r = cgb ? 0xF000 : 0xFCFF }; }; + template struct OamDmaConflictMap { enum { r = cgb ? 0xFCFF : 0x0000 }; }; + + template + bool isInOamDmaConflictArea(OamDmaSrc src, unsigned p) + { + static unsigned short const m[] = { + OamDmaConflictMap::r, + OamDmaConflictMap::r, + OamDmaConflictMap::r, + OamDmaConflictMap::r, + OamDmaConflictMap::r, + 0 }; + return p < mm_oam_begin && (m[src] >> (p >> 12) & 1); + } + + template + void disconnectOamDmaAreas(unsigned char const* (&rmem)[0x10], unsigned char* (&wmem)[0x10]) + { + if (OamDmaConflictMap::r & 0x00FF) + std::fill_n(rmem, 8, static_cast(0)); + if (OamDmaConflictMap::r & 0x0C00) + rmem[0xB] = rmem[0xA] = wmem[0xB] = wmem[0xA] = 0; + if (OamDmaConflictMap::r & 0x7000) + rmem[0xE] = rmem[0xD] = rmem[0xC] = wmem[0xE] = wmem[0xD] = wmem[0xC] = 0; + } + + template + void disconnectOamDmaAreas(unsigned char const* (&rmem)[0x10], unsigned char* (&wmem)[0x10], + OamDmaSrc src) + { + switch (src) { + case oam_dma_src_rom: disconnectOamDmaAreas(rmem, wmem); break; + case oam_dma_src_sram: disconnectOamDmaAreas(rmem, wmem); break; + case oam_dma_src_vram: disconnectOamDmaAreas(rmem, wmem); break; + case oam_dma_src_wram: disconnectOamDmaAreas(rmem, wmem); break; + case oam_dma_src_invalid: disconnectOamDmaAreas(rmem, wmem); break; + case oam_dma_src_off: break; + } + } + +} // unnamed namespace. MemPtrs::MemPtrs() : rmem_() @@ -30,7 +79,6 @@ MemPtrs::MemPtrs() , vrambankptr_(0) , rsrambankptr_(0) , wsrambankptr_(0) -, memchunk_(0) , rambankdata_(0) , wramdataend_(0) , oamDmaSrc_(oam_dma_src_off) @@ -39,32 +87,27 @@ MemPtrs::MemPtrs() { } -MemPtrs::~MemPtrs() { - delete []memchunk_; -} - 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]; + int const num_disabled_ram_areas = 2; + memchunk_.reset( + pre_rom_pad_size() + + rombanks * rombank_size() + + max_num_vrambanks * vrambank_size() + + rambanks * rambank_size() + + wrambanks * wrambank_size() + + num_disabled_ram_areas * rambank_size()); romdata_[0] = romdata(); - rambankdata_ = romdata_[0] + rombanks * 0x4000ul + 0x4000; - wramdata_[0] = rambankdata_ + rambanks * 0x2000ul; - wramdataend_ = wramdata_[0] + wrambanks * 0x1000ul; + rambankdata_ = romdata_[0] + rombanks * rombank_size() + max_num_vrambanks * vrambank_size(); + wramdata_[0] = rambankdata_ + rambanks * rambank_size(); + wramdataend_ = wramdata_[0] + wrambanks * wrambank_size(); - std::memset(rdisabledRamw(), 0xFF, 0x2000); + std::fill_n(rdisabledRamw(), rambank_size(), 0xFF); 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; + rmem_[0xC] = wmem_[0xC] = wramdata_[0] - mm_wram_begin; + rmem_[0xE] = wmem_[0xE] = wramdata_[0] - mm_wram_mirror_begin; setRombank(1); setRambank(0, 0); setVrambank(0); @@ -76,38 +119,40 @@ void MemPtrs::reset(unsigned const rombanks, unsigned const rambanks, unsigned c } void MemPtrs::setRombank0(unsigned bank) { - romdata_[0] = romdata() + bank * 0x4000ul; + romdata_[0] = romdata() + bank * rombank_size(); rmem_[0x3] = rmem_[0x2] = rmem_[0x1] = rmem_[0x0] = romdata_[0]; disconnectOamDmaAreas(); } void MemPtrs::setRombank(unsigned bank) { curRomBank_ = bank; - romdata_[1] = romdata() + bank * 0x4000ul - 0x4000; + romdata_[1] = romdata() + bank * rombank_size() - mm_rom1_begin; rmem_[0x7] = rmem_[0x6] = rmem_[0x5] = rmem_[0x4] = romdata_[1]; disconnectOamDmaAreas(); } void MemPtrs::setRambank(unsigned const flags, unsigned const rambank) { - unsigned char *srambankptr = 0; + unsigned char* srambankptr = 0; if (!(flags & rtc_en)) { srambankptr = rambankdata() != rambankdataend() - ? rambankdata_ + rambank * 0x2000ul - 0xA000 - : wdisabledRam() - 0xA000; + ? rambankdata_ + rambank * rambank_size() - mm_sram_begin + : wdisabledRam() - mm_sram_begin; } - rsrambankptr_ = (flags & read_en) && srambankptr != wdisabledRam() - 0xA000 - ? srambankptr - : rdisabledRamw() - 0xA000; - wsrambankptr_ = flags & write_en ? srambankptr : wdisabledRam() - 0xA000; + rsrambankptr_ = (flags & read_en) && srambankptr != wdisabledRam() - mm_sram_begin + ? srambankptr + : rdisabledRamw() - mm_sram_begin; + wsrambankptr_ = flags & write_en + ? srambankptr + : wdisabledRam() - mm_sram_begin; rmem_[0xB] = rmem_[0xA] = rsrambankptr_; wmem_[0xB] = wmem_[0xA] = wsrambankptr_; disconnectOamDmaAreas(); } void MemPtrs::setWrambank(unsigned bank) { - wramdata_[1] = wramdata_[0] + (bank & 0x07 ? bank & 0x07 : 1) * 0x1000; - rmem_[0xD] = wmem_[0xD] = wramdata_[1] - 0xD000; + wramdata_[1] = wramdata_[0] + (bank & 0x07 ? bank & 0x07 : 1) * wrambank_size(); + rmem_[0xD] = wmem_[0xD] = wramdata_[1] - mm_wram1_begin; disconnectOamDmaAreas(); } @@ -116,51 +161,25 @@ void MemPtrs::setOamDmaSrc(OamDmaSrc oamDmaSrc) { rmem_[0x7] = rmem_[0x6] = rmem_[0x5] = rmem_[0x4] = romdata_[1]; rmem_[0xB] = rmem_[0xA] = rsrambankptr_; wmem_[0xB] = wmem_[0xA] = wsrambankptr_; - rmem_[0xC] = wmem_[0xC] = wramdata_[0] - 0xC000; - rmem_[0xD] = wmem_[0xD] = wramdata_[1] - 0xD000; - rmem_[0xE] = wmem_[0xE] = wramdata_[0] - 0xE000; + rmem_[0xC] = wmem_[0xC] = wramdata_[0] - mm_wram_begin; + rmem_[0xD] = wmem_[0xD] = wramdata_[1] - mm_wram1_begin; + rmem_[0xE] = wmem_[0xE] = wramdata_[0] - mm_wram_mirror_begin; oamDmaSrc_ = oamDmaSrc; disconnectOamDmaAreas(); } 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: - std::fill(rmem_, rmem_ + 8, static_cast(0)); - rmem_[0xB] = rmem_[0xA] = 0; - wmem_[0xB] = wmem_[0xA] = 0; - break; - case oam_dma_src_vram: - break; - 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: - 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: - 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: - break; - case oam_dma_src_off: - break; - } - } + return isCgb(*this) + ? ::disconnectOamDmaAreas(rmem_, wmem_, oamDmaSrc_) + : ::disconnectOamDmaAreas(rmem_, wmem_, oamDmaSrc_); +} + +bool MemPtrs::isInOamDmaConflictArea(unsigned p) const +{ + return isCgb(*this) + ? ::isInOamDmaConflictArea(oamDmaSrc_, p) + : ::isInOamDmaConflictArea(oamDmaSrc_, p); } // all pointers here are relative to memchunk_ @@ -220,4 +239,3 @@ SYNCFUNC(MemPtrs) NSS(curRomBank_); } -} diff --git a/libgambatte/src/mem/memptrs.h b/libgambatte/src/mem/memptrs.h index d41a265394..6191624481 100644 --- a/libgambatte/src/mem/memptrs.h +++ b/libgambatte/src/mem/memptrs.h @@ -20,46 +20,65 @@ #define MEMPTRS_H #include "newstate.h" +#include "array.h" 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 }; + +enum { + mm_rom_begin = 0x0000, + mm_rom1_begin = 0x4000, + mm_vram_begin = 0x8000, + mm_sram_begin = 0xA000, + mm_wram_begin = 0xC000, + mm_wram1_begin = 0xD000, + mm_wram_mirror_begin = 0xE000, + mm_oam_begin = 0xFE00, + mm_io_begin = 0xFF00, + mm_hram_begin = 0xFF80 }; + +enum { max_num_vrambanks = 2 }; +inline std::size_t rambank_size() { return 0x2000; } +inline std::size_t rombank_size() { return 0x4000; } +inline std::size_t vrambank_size() { return 0x2000; } +inline std::size_t wrambank_size() { return 0x1000; } class MemPtrs { public: enum RamFlag { read_en = 1, write_en = 2, rtc_en = 4 }; MemPtrs(); - ~MemPtrs(); void reset(unsigned rombanks, unsigned rambanks, unsigned wrambanks); - 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_; } - unsigned char * romdata() const { return memchunk_ + 0x4000; } - unsigned char * romdata(unsigned area) const { return romdata_[area]; } - unsigned char * romdataend() const { return rambankdata_ - 0x4000; } - unsigned char * wramdata(unsigned area) const { return wramdata_[area]; } - unsigned char * wramdataend() const { return wramdataend_; } - unsigned char * rambankdata() const { return rambankdata_; } - unsigned char * rambankdataend() const { return wramdata_[0]; } - 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_; } + unsigned char const* rmem(unsigned area) const { return rmem_[area]; } + unsigned char* wmem(unsigned area) const { return wmem_[area]; } + unsigned char* romdata() const { return memchunk_ + pre_rom_pad_size(); } + unsigned char* romdata(unsigned area) const { return romdata_[area]; } + unsigned char* romdataend() const { return rambankdata_ - max_num_vrambanks * vrambank_size(); } + unsigned char* vramdata() const { return romdataend(); } + unsigned char* vramdataend() const { return rambankdata_; } + unsigned char* rambankdata() const { return rambankdata_; } + unsigned char* rambankdataend() const { return wramdata_[0]; } + unsigned char* wramdata(unsigned area) const { return wramdata_[area]; } + unsigned char* wramdataend() const { return wramdataend_; } + 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_; } - unsigned curRomBank() const { return curRomBank_; } + bool isInOamDmaConflictArea(unsigned p) const; void setRombank0(unsigned bank); void setRombank(unsigned bank); void setRambank(unsigned ramFlags, unsigned rambank); - void setVrambank(unsigned bank) { vrambankptr_ = vramdata() + bank * 0x2000ul - 0x8000; } + void setVrambank(unsigned bank) { vrambankptr_ = vramdata() + bank * vrambank_size() - mm_vram_begin; } void setWrambank(unsigned bank); void setOamDmaSrc(OamDmaSrc oamDmaSrc); @@ -71,7 +90,7 @@ private: unsigned char *vrambankptr_; unsigned char *rsrambankptr_; unsigned char *wsrambankptr_; - unsigned char *memchunk_; + SimpleArray memchunk_; unsigned char *rambankdata_; unsigned char *wramdataend_; OamDmaSrc oamDmaSrc_; @@ -82,18 +101,19 @@ private: int memchunk_saveoffs; int memchunk_savelen; - MemPtrs(MemPtrs const &); - MemPtrs & operator=(MemPtrs const &); + static std::size_t pre_rom_pad_size() { return mm_rom1_begin; } void disconnectOamDmaAreas(); - unsigned char * rdisabledRamw() const { return wramdataend_ ; } - unsigned char * wdisabledRam() const { return wramdataend_ + 0x2000; } + unsigned char * rdisabledRamw() const { return wramdataend_; } + unsigned char * wdisabledRam() const { return wramdataend_ + rambank_size(); } public: templatevoid SyncState(NewState *ns); }; -inline bool isCgb(MemPtrs const &memptrs) { - return memptrs.wramdataend() - memptrs.wramdata(0) == 0x8000; +inline bool isCgb(MemPtrs const& memptrs) { + int const num_cgb_wrambanks = 8; + std::size_t const wramsize = memptrs.wramdataend() - memptrs.wramdata(0); + return wramsize == num_cgb_wrambanks * wrambank_size(); } } diff --git a/libgambatte/src/mem/time.cpp b/libgambatte/src/mem/time.cpp index 87ffb7e10c..f85433bea9 100644 --- a/libgambatte/src/mem/time.cpp +++ b/libgambatte/src/mem/time.cpp @@ -43,7 +43,7 @@ void Time::loadState(SaveState const &state) { 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; + ds_ = state.mem.ioamhram.get()[0x14D] >> 7; } std::uint32_t Time::get(unsigned long const cc) { diff --git a/libgambatte/src/memory.cpp b/libgambatte/src/memory.cpp index 6ddce9da05..3b255d7d62 100644 --- a/libgambatte/src/memory.cpp +++ b/libgambatte/src/memory.cpp @@ -17,14 +17,31 @@ // #include "memory.h" +#include "gambatte.h" #include "savestate.h" #include "sound.h" #include "video.h" +#include #include -namespace gambatte { +using namespace gambatte; -Memory::Memory(unsigned short &sp, unsigned short &pc) +namespace { + + int const oam_size = 4 * lcd_num_oam_entries; + + void decCycles(unsigned long& counter, unsigned long dec) { + if (counter != disabled_time) + counter -= dec; + } + + int serialCntFrom(unsigned long cyclesUntilDone, bool cgbFast) { + return cgbFast ? (cyclesUntilDone + 0xF) >> 4 : (cyclesUntilDone + 0x1FF) >> 9; + } + +} // unnamed namespace. + +Memory::Memory(Interrupter const& interrupter) : readCallback_(0) , writeCallback_(0) , execCallback_(0) @@ -35,17 +52,18 @@ Memory::Memory(unsigned short &sp, unsigned short &pc) , divLastUpdate_(0) , lastOamDmaUpdate_(disabled_time) , lcd_(ioamhram_, 0, VideoInterruptRequester(intreq_)) +, interrupter_(interrupter) , dmaSource_(0) , dmaDestination_(0) -, oamDmaPos_(0xFE) +, oamDmaPos_(-2u & 0xFF) +, oamDmaStartPos_(0) , serialCnt_(0) , blanklcd_(false) , LINKCABLE_(false) , linkClockTrigger_(false) -, sp_(sp) -, pc_(pc) +, haltHdmaState_(hdma_low) { - intreq_.setEventTime(144 * 456ul); + intreq_.setEventTime(1l * lcd_vres * lcd_cycles_per_line); intreq_.setEventTime(0); } @@ -61,63 +79,59 @@ void Memory::setStatePtrs(SaveState &state) { psg_.setStatePtrs(state); } - -static int serialCntFrom(unsigned long cyclesUntilDone, bool cgbFast) { - return cgbFast ? (cyclesUntilDone + 0xF) >> 4 : (cyclesUntilDone + 0x1FF) >> 9; -} - void Memory::loadState(SaveState const &state) { biosMode_ = state.mem.biosMode; - cgbSwitching_ = state.mem.cgbSwitching; - agbMode_ = state.mem.agbMode; - gbIsCgb_ = state.mem.gbIsCgb; stopped_ = state.mem.stopped; psg_.loadState(state); - lcd_.loadState(state, state.mem.oamDmaPos < 0xA0 ? cart_.rdisabledRam() : ioamhram_); + lcd_.loadState(state, state.mem.oamDmaPos < oam_size ? 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); - halttime_ = state.mem.halttime; lastOamDmaUpdate_ = state.mem.lastOamDmaUpdate; dmaSource_ = state.mem.dmaSource; dmaDestination_ = state.mem.dmaDestination; oamDmaPos_ = state.mem.oamDmaPos; + oamDmaStartPos_ = 0; + haltHdmaState_ = static_cast(std::min(1u * state.mem.haltHdmaState, 1u * hdma_requested)); serialCnt_ = intreq_.eventTime(intevent_serial) != disabled_time - ? serialCntFrom(intreq_.eventTime(intevent_serial) - state.cpu.cycleCounter, - ioamhram_[0x102] & isCgb() * 2) - : 8; + ? 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); if (lastOamDmaUpdate_ != disabled_time) { + if (lastOamDmaUpdate_ > state.cpu.cycleCounter) { + oamDmaStartPos_ = (oamDmaPos_ + (lastOamDmaUpdate_ - state.cpu.cycleCounter) / 4) & 0xFF; + lastOamDmaUpdate_ = state.cpu.cycleCounter; + } oamDmaInitSetup(); - unsigned oamEventPos = oamDmaPos_ < 0xA0 ? 0xA0 : 0x100; + unsigned oamEventPos = oamDmaPos_ < oam_size ? oam_size : oamDmaStartPos_; intreq_.setEventTime( - lastOamDmaUpdate_ + (oamEventPos - oamDmaPos_) * 4); + lastOamDmaUpdate_ + ((oamEventPos - oamDmaPos_) & 0xFF) * 4); } intreq_.setEventTime(ioamhram_[0x140] & lcdc_en - ? lcd_.nextMode1IrqTime() - : state.cpu.cycleCounter); + ? lcd_.nextMode1IrqTime() + : state.cpu.cycleCounter); blanklcd_ = false; if (!isCgb()) - std::memset(cart_.vramdata() + 0x2000, 0, 0x2000); + std::fill_n(cart_.vramdata() + vrambank_size(), vrambank_size(), 0); } void Memory::setEndtime(unsigned long cc, unsigned long inc) { if (intreq_.eventTime(intevent_blit) <= cc) { intreq_.setEventTime(intreq_.eventTime(intevent_blit) - + (70224 << isDoubleSpeed())); + + (lcd_cycles_per_frame << isDoubleSpeed())); } intreq_.setEventTime(cc + (inc << isDoubleSpeed())); @@ -129,11 +143,11 @@ void Memory::updateSerial(unsigned long const cc) { if (intreq_.eventTime(intevent_serial) <= cc) { ioamhram_[0x101] = (((ioamhram_[0x101] + 1) << serialCnt_) - 1) & 0xFF; ioamhram_[0x102] &= 0x7F; + intreq_.flagIrq(8, intreq_.eventTime(intevent_serial)); intreq_.setEventTime(disabled_time); - intreq_.flagIrq(8); } else { int const targetCnt = serialCntFrom(intreq_.eventTime(intevent_serial) - cc, - ioamhram_[0x102] & isCgb() * 2); + ioamhram_[0x102] & isCgb() * 2); ioamhram_[0x101] = (((ioamhram_[0x101] + 1) << (serialCnt_ - targetCnt)) - 1) & 0xFF; serialCnt_ = targetCnt; } @@ -167,11 +181,14 @@ unsigned long Memory::event(unsigned long cc) { switch (intreq_.minEventId()) { case intevent_unhalt: + if ((lcd_.hdmaIsEnabled() && lcd_.isHdmaPeriod(cc) && haltHdmaState_ == hdma_low) + || haltHdmaState_ == hdma_requested) { + flagHdmaReq(intreq_); + } + intreq_.unhalt(); intreq_.setEventTime(disabled_time); - nontrivial_ff_write(0xFF04, 0, cc); - pc_ = (pc_ + 1) & 0xFFFF; - cc += 4; + stopped_ = false; break; case intevent_end: intreq_.setEventTime(disabled_time - 1); @@ -197,7 +214,7 @@ unsigned long Memory::event(unsigned long cc) { while (cc >= intreq_.minEventTime()) cc = event(cc); } else - blitTime += 70224 << isDoubleSpeed(); + blitTime += lcd_cycles_per_frame << isDoubleSpeed(); blanklcd_ = lcden ^ 1; intreq_.setEventTime(blitTime); @@ -207,77 +224,23 @@ unsigned long Memory::event(unsigned long cc) { updateSerial(cc); break; case intevent_oam: - intreq_.setEventTime(lastOamDmaUpdate_ == disabled_time - ? static_cast(disabled_time) - : intreq_.eventTime(intevent_oam) + 0xA0 * 4); + if (lastOamDmaUpdate_ != disabled_time) { + unsigned const oamEventPos = oamDmaPos_ < oam_size ? oam_size : oamDmaStartPos_; + intreq_.setEventTime( + lastOamDmaUpdate_ + ((oamEventPos - oamDmaPos_) & 0xFF) * 4); + } + else + intreq_.setEventTime(disabled_time); + break; case intevent_dma: - { - 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_); - - if ((static_cast(dmaDest) + length) & 0x10000) { - length = 0x10000 - dmaDest; - ioamhram_[0x155] |= 0x80; - } - - dmaLength -= length; - - if (!(ioamhram_[0x140] & lcdc_en)) - dmaLength = 0; - - { - unsigned long lOamDmaUpdate = lastOamDmaUpdate_; - lastOamDmaUpdate_ = disabled_time; - - while (length--) { - unsigned const src = dmaSrc++ & 0xFFFF; - unsigned const data = (src & 0xE000) == 0x8000 || src > 0xFDFF - ? 0xFF - : read(src, cc); - - cc += 2 << doubleSpeed; - - if (cc - 3 > lOamDmaUpdate) { - oamDmaPos_ = (oamDmaPos_ + 1) & 0xFF; - lOamDmaUpdate += 4; - - if (oamDmaPos_ < 0xA0) { - if (oamDmaPos_ == 0) - startOamDma(lOamDmaUpdate - 1); - - ioamhram_[src & 0xFF] = data; - } else if (oamDmaPos_ == 0xA0) { - endOamDma(lOamDmaUpdate - 1); - lOamDmaUpdate = disabled_time; - } - } - - nontrivial_write(0x8000 | (dmaDest++ & 0x1FFF), data, cc); - } - - lastOamDmaUpdate_ = lOamDmaUpdate; - } - - cc += 4; - - dmaSource_ = dmaSrc; - dmaDestination_ = dmaDest; - ioamhram_[0x155] = ((dmaLength / 0x10 - 0x1) & 0xFF) | (ioamhram_[0x155] & 0x80); - - if ((ioamhram_[0x155] & 0x80) && lcd_.hdmaIsEnabled()) { - if (lastOamDmaUpdate_ != disabled_time) - updateOamDma(cc); - - lcd_.disableHdma(cc); - } + interrupter_.prefetch(cc, *this); + cc = dma(cc); + if (haltHdmaState_ == hdma_requested) { + haltHdmaState_ = hdma_low; + intreq_.setMinIntTime(cc); + cc -= 4; } - break; case intevent_tima: tima_.doIrqEvent(TimaInterruptRequester(intreq_)); @@ -292,45 +255,21 @@ unsigned long Memory::event(unsigned long cc) { } if (halted()) { - if (gbIsCgb_ || (!gbIsCgb_ && cc <= halttime_ + 4)) - cc += 4; + cc += 4 * (isCgb() || cc - intreq_.eventTime(intevent_interrupts) < 2); + if (cc > lastOamDmaUpdate_) + updateOamDma(cc); + if ((lcd_.hdmaIsEnabled() && lcd_.isHdmaPeriod(cc) && haltHdmaState_ == hdma_low) + || haltHdmaState_ == hdma_requested) { + flagHdmaReq(intreq_); + } intreq_.unhalt(); intreq_.setEventTime(disabled_time); } if (ime()) { - cc += 12; - - sp_ = (sp_ - 1) & 0xFFFF; - write(sp_, pc_ >> 8, cc); - - cc += 4; - - updateIrqs(cc); - unsigned const pendingIrqs = intreq_.pendingIrqs(); - - sp_ = (sp_ - 1) & 0xFFFF; - write(sp_, pc_ & 0xFF, cc); - - cc += 2; - - unsigned const n = pendingIrqs & -pendingIrqs; - unsigned address; - if (n == 0) { - address = 0; - } else if (n <= 4) { - static unsigned char const lut[] = { 0x40, 0x48, 0x48, 0x50 }; - address = lut[n-1]; - } else - address = 0x50 + n; - - updateIrqs(cc); - intreq_.ackIrq(n); - - cc += 2; - - pc_ = address; + di(); + cc = interrupter_.interrupt(cc, *this); } break; @@ -339,38 +278,175 @@ unsigned long Memory::event(unsigned long cc) { return cc; } -unsigned long Memory::stop(unsigned long cc) { +unsigned long Memory::dma(unsigned long cc) { + bool const doubleSpeed = isDoubleSpeed(); + unsigned dmaSrc = dmaSource_; + unsigned dmaDest = dmaDestination_; + unsigned dmaLength = ((ioamhram_[0x155] & 0x7F) + 1) * 0x10; + unsigned length = hdmaReqFlagged(intreq_) ? 0x10 : dmaLength; + + if (1ul * dmaDest + length >= 0x10000) { + length = 0x10000 - dmaDest; + ioamhram_[0x155] |= 0x80; + } + + dmaLength -= length; + + if (!(ioamhram_[0x140] & lcdc_en)) + dmaLength = 0; + + unsigned long lOamDmaUpdate = lastOamDmaUpdate_; + lastOamDmaUpdate_ = disabled_time; + + while (length--) { + unsigned const src = dmaSrc++ & 0xFFFF; + unsigned const data = (src & -vrambank_size()) == mm_vram_begin || src >= mm_oam_begin + ? 0xFF + : read(src, cc); + + cc += 2 + 2 * doubleSpeed; + + if (cc - 3 > lOamDmaUpdate && !halted()) { + oamDmaPos_ = (oamDmaPos_ + 1) & 0xFF; + lOamDmaUpdate += 4; + if (oamDmaPos_ == oamDmaStartPos_) + startOamDma(lOamDmaUpdate); + + if (oamDmaPos_ < oam_size) { + ioamhram_[src & 0xFF] = data; + } + else if (oamDmaPos_ == oam_size) { + endOamDma(lOamDmaUpdate); + if (oamDmaStartPos_ == 0) + lOamDmaUpdate = disabled_time; + } + } + + nontrivial_write(mm_vram_begin | dmaDest++ % vrambank_size(), data, cc); + } + + lastOamDmaUpdate_ = lOamDmaUpdate; + ackDmaReq(intreq_); cc += 4; - 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() - : cc + (70224 << isDoubleSpeed())); + dmaSource_ = dmaSrc; + dmaDestination_ = dmaDest; + ioamhram_[0x155] = halted() + ? ioamhram_[0x155] | 0x80 + : ((dmaLength / 0x10 - 1) & 0xFF) | (ioamhram_[0x155] & 0x80); - if (intreq_.eventTime(intevent_end) > cc) { - intreq_.setEventTime(cc - + ( isDoubleSpeed() - ? (intreq_.eventTime(intevent_end) - cc) << 1 - : (intreq_.eventTime(intevent_end) - cc) >> 1)); - } - intreq_.halt(); - intreq_.setEventTime(cc + 0x20000); - } - else { - stopped_ = true; - intreq_.halt(); + if ((ioamhram_[0x155] & 0x80) && lcd_.hdmaIsEnabled()) { + if (lastOamDmaUpdate_ != disabled_time) + updateOamDma(cc); + + lcd_.disableHdma(cc); } return cc; } -static void decCycles(unsigned long &counter, unsigned long dec) { - if (counter != disabled_time) - counter -= dec; +void Memory::freeze(unsigned long cc) { + // permanently halt CPU. + // simply halt and clear IE to avoid unhalt from occuring, + // which avoids additional state to represent a "frozen" state. + nontrivial_ff_write(0xFF, 0, cc); + ackDmaReq(intreq_); + intreq_.halt(); +} + +bool Memory::halt(unsigned long cc) { + if (lastOamDmaUpdate_ != disabled_time) + updateOamDma(cc); + + haltHdmaState_ = lcd_.hdmaIsEnabled() && lcd_.isHdmaPeriod(cc) + ? hdma_high : hdma_low; + bool const hdmaReq = hdmaReqFlagged(intreq_); + if (hdmaReq) + haltHdmaState_ = hdma_requested; + if (lastOamDmaUpdate_ != disabled_time) + updateOamDma(cc + 4); + + ackDmaReq(intreq_); + intreq_.halt(); + return hdmaReq; +} + +unsigned Memory::pendingIrqs(unsigned long cc) { + if (lastOamDmaUpdate_ != disabled_time) + updateOamDma(cc); + + updateIrqs(cc); + return intreq_.pendingIrqs(); +} + +void Memory::ackIrq(unsigned bit, unsigned long cc) { + if (lastOamDmaUpdate_ != disabled_time) + updateOamDma(cc); + + // TODO: adjust/extend IRQ assertion time rather than use the odd cc offsets? + // NOTE: a minimum offset of 2 is required for the LCD due to implementation assumptions w.r.t. cc headroom. + updateSerial(cc + 3 + isCgb()); + updateTimaIrq(cc + 2 + isCgb()); + lcd_.update(cc + 2); + intreq_.ackIrq(bit); +} + +unsigned long Memory::stop(unsigned long cc, bool &skip) { + // FIXME: this is incomplete. + intreq_.setEventTime(cc + 0x20000 + 4); + + // speed change. + if (ioamhram_[0x14D] & isCgb()) { + tima_.speedChange(TimaInterruptRequester(intreq_)); + // DIV reset. + nontrivial_ff_write(0x04, 0, cc); + haltHdmaState_ = lcd_.hdmaIsEnabled() && lcd_.isHdmaPeriod(cc) + ? hdma_high : hdma_low; + skip = hdmaReqFlagged(intreq_); + if (skip && isDoubleSpeed()) + haltHdmaState_ = hdma_requested; + unsigned long const cc_ = cc + 8 * !isDoubleSpeed(); + if (cc_ >= cc + 4) { + if (lastOamDmaUpdate_ != disabled_time) + updateOamDma(cc + 4); + if (!skip || isDoubleSpeed()) + ackDmaReq(intreq_); + intreq_.halt(); + } + psg_.speedChange(cc_, isDoubleSpeed()); + lcd_.speedChange(cc_); + cart_.speedChange(cc_); + ioamhram_[0x14D] ^= 0x81; + // TODO: perhaps make this a bit nicer? + intreq_.setEventTime(ioamhram_[0x140] & lcdc_en + ? lcd_.nextMode1IrqTime() + : cc + (lcd_cycles_per_frame << isDoubleSpeed())); + if (intreq_.eventTime(intevent_end) > cc_) { + intreq_.setEventTime(cc_ + + (isDoubleSpeed() + ? (intreq_.eventTime(intevent_end) - cc_) * 2 + : (intreq_.eventTime(intevent_end) - cc_) / 2)); + } + if (cc_ < cc + 4) { + if (lastOamDmaUpdate_ != disabled_time) + updateOamDma(cc + 4); + if (!skip || !isDoubleSpeed()) + ackDmaReq(intreq_); + intreq_.halt(); + } + // ensure that no updates with a previous cc occur. + cc += 8; + } + else { + // FIXME: test and implement stop correctly. + skip = halt(cc); + cc += 4; + + stopped_ = true; + intreq_.setEventTime(disabled_time); + } + + return cc; } void Memory::decEventCycles(IntEventId eventId, unsigned long dec) { @@ -384,15 +460,9 @@ unsigned long Memory::resetCounters(unsigned long cc) { updateIrqs(cc); - { - unsigned long divinc = (cc - divLastUpdate_) >> 8; - ioamhram_[0x104] = (ioamhram_[0x104] + divinc) & 0xFF; - divLastUpdate_ += divinc << 8; - } - - unsigned long const dec = cc < 0x10000 - ? 0 - : (cc & ~0x7FFFul) - 0x8000; + unsigned long const dec = cc < 0x20000 + ? 0 + : (cc & -0x10000l) - 0x10000; decCycles(divLastUpdate_, dec); decCycles(lastOamDmaUpdate_, dec); decEventCycles(intevent_serial, dec); @@ -434,62 +504,68 @@ void Memory::updateOamDma(unsigned long const cc) { unsigned char const *const oamDmaSrc = oamDmaSrcPtr(); unsigned cycles = (cc - lastOamDmaUpdate_) >> 2; - while (cycles--) { + if (halted()) { + lastOamDmaUpdate_ += 4 * cycles; + } + else while (cycles--) { oamDmaPos_ = (oamDmaPos_ + 1) & 0xFF; lastOamDmaUpdate_ += 4; + if (oamDmaPos_ == oamDmaStartPos_) + startOamDma(lastOamDmaUpdate_); - 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; - break; + if (oamDmaPos_ < oam_size) { + ioamhram_[oamDmaPos_] = ((oamDmaSrc) ? oamDmaSrc[oamDmaPos_] : cart_.rtcRead()); + } + else if (oamDmaPos_ == oam_size) { + endOamDma(lastOamDmaUpdate_); + if (oamDmaStartPos_ == 0) { + 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); - } else + if (ioamhram_[0x146] < mm_sram_begin / 0x100) { + cart_.setOamDmaSrc(ioamhram_[0x146] < mm_vram_begin / 0x100 ? oam_dma_src_rom : oam_dma_src_vram); + } + else if (ioamhram_[0x146] < 0x100 - isCgb() * 0x20) { + cart_.setOamDmaSrc(ioamhram_[0x146] < mm_wram_begin / 0x100 ? oam_dma_src_sram : oam_dma_src_wram); + } + else cart_.setOamDmaSrc(oam_dma_src_invalid); } -static unsigned char const * oamDmaSrcZero() { - static unsigned char zeroMem[0xA0]; - return zeroMem; -} - -unsigned char const * Memory::oamDmaSrcPtr() const { +unsigned char const* Memory::oamDmaSrcPtr() const { switch (cart_.oamDmaSrc()) { case oam_dma_src_rom: - return cart_.romdata(ioamhram_[0x146] >> 6) + (ioamhram_[0x146] << 8); + return cart_.romdata(ioamhram_[0x146] >> 6) + ioamhram_[0x146] * 0x100l; case oam_dma_src_sram: - return cart_.rsrambankptr() ? cart_.rsrambankptr() + (ioamhram_[0x146] << 8) : 0; + return cart_.rsrambankptr() ? cart_.rsrambankptr() + ioamhram_[0x146] * 0x100l : 0; case oam_dma_src_vram: - return cart_.vrambankptr() + (ioamhram_[0x146] << 8); + return cart_.vrambankptr() + ioamhram_[0x146] * 0x100l; case oam_dma_src_wram: - return cart_.wramdata(ioamhram_[0x146] >> 4 & 1) + (ioamhram_[0x146] << 8 & 0xFFF); + return cart_.wramdata(ioamhram_[0x146] >> 4 & 1) + (ioamhram_[0x146] * 0x100l & 0xFFF); case oam_dma_src_invalid: case oam_dma_src_off: break; } - return ioamhram_[0x146] == 0xFF && !isCgb() ? oamDmaSrcZero() : cart_.rdisabledRam(); + return cart_.rdisabledRam(); } void Memory::startOamDma(unsigned long cc) { + oamDmaPos_ = 0; + oamDmaStartPos_ = 0; lcd_.oamChange(cart_.rdisabledRam(), cc); } void Memory::endOamDma(unsigned long cc) { - oamDmaPos_ = 0xFE; - cart_.setOamDmaSrc(oam_dma_src_off); + if (oamDmaStartPos_ == 0) { + oamDmaPos_ = -2u & 0xFF; + cart_.setOamDmaSrc(oam_dma_src_off); + } lcd_.oamChange(ioamhram_, cc); } @@ -506,13 +582,7 @@ unsigned Memory::nontrivial_ff_read(unsigned const p, unsigned long const cc) { updateSerial(cc); break; case 0x04: - { - unsigned long divcycles = (cc - divLastUpdate_) >> 8; - ioamhram_[0x104] = (ioamhram_[0x104] + divcycles) & 0xFF; - divLastUpdate_ += divcycles << 8; - } - - break; + return (cc - tima_.divLastUpdate()) >> 8 & 0xFF; case 0x05: ioamhram_[0x105] = tima_.tima(cc); break; @@ -561,47 +631,28 @@ unsigned Memory::nontrivial_ff_read(unsigned const p, unsigned long const cc) { return ioamhram_[p + 0x100]; } -static bool isInOamDmaConflictArea(OamDmaSrc const oamDmaSrc, unsigned const p, bool const cgb) { - struct Area { unsigned short areaUpper, exceptAreaLower, exceptAreaWidth, pad; }; - - static Area const cgbAreas[] = { - { 0xC000, 0x8000, 0x2000, 0 }, - { 0xC000, 0x8000, 0x2000, 0 }, - { 0xA000, 0x0000, 0x8000, 0 }, - { 0xFE00, 0x0000, 0xC000, 0 }, - { 0xC000, 0x8000, 0x2000, 0 }, - { 0x0000, 0x0000, 0x0000, 0 } - }; - - static Area const dmgAreas[] = { - { 0xFE00, 0x8000, 0x2000, 0 }, - { 0xFE00, 0x8000, 0x2000, 0 }, - { 0xA000, 0x0000, 0x8000, 0 }, - { 0xFE00, 0x8000, 0x2000, 0 }, - { 0xFE00, 0x8000, 0x2000, 0 }, - { 0x0000, 0x0000, 0x0000, 0 } - }; - - Area const *a = cgb ? cgbAreas : dmgAreas; - return p < a[oamDmaSrc].areaUpper - && p - a[oamDmaSrc].exceptAreaLower >= a[oamDmaSrc].exceptAreaWidth; -} - unsigned Memory::nontrivial_read(unsigned const p, unsigned long const cc) { - if (p < 0xFF80) { + if (p < mm_hram_begin) { if (lastOamDmaUpdate_ != disabled_time) { updateOamDma(cc); - if (isInOamDmaConflictArea(cart_.oamDmaSrc(), p, isCgb()) && oamDmaPos_ < 0xA0) - return ioamhram_[oamDmaPos_]; + if (cart_.isInOamDmaConflictArea(p) && oamDmaPos_ < oam_size) { + int const r = isCgb() && cart_.oamDmaSrc() != oam_dma_src_wram && p >= mm_wram_begin + ? cart_.wramdata(ioamhram_[0x146] >> 4 & 1)[p & 0xFFF] + : ioamhram_[oamDmaPos_]; + if (isCgb() && cart_.oamDmaSrc() == oam_dma_src_vram) + ioamhram_[oamDmaPos_] = 0; + + return r; + } } - if (p < 0xC000) { - if (p < 0x8000) + if (p < mm_wram_begin) { + if (p < mm_vram_begin) return cart_.romdata(p >> 14)[p]; - if (p < 0xA000) { - if (!lcd_.vramAccessible(cc)) + if (p < mm_sram_begin) { + if (!lcd_.vramReadable(cc)) return 0xFF; return cart_.vrambankptr()[p]; @@ -613,18 +664,18 @@ unsigned Memory::nontrivial_read(unsigned const p, unsigned long const cc) { return cart_.rtcRead(); } - if (p < 0xFE00) + if (p < mm_oam_begin) return cart_.wramdata(p >> 12 & 1)[p & 0xFFF]; - long const ffp = long(p) - 0xFF00; + long const ffp = static_cast(p) - mm_io_begin; if (ffp >= 0) return nontrivial_ff_read(ffp, cc); - if (!lcd_.oamReadable(cc) || oamDmaPos_ < 0xA0) + if (!lcd_.oamReadable(cc) || oamDmaPos_ < oam_size) return 0xFF; } - return ioamhram_[p - 0xFE00]; + return ioamhram_[p - mm_oam_begin]; } unsigned Memory::nontrivial_peek(unsigned const p) { @@ -674,17 +725,25 @@ void Memory::nontrivial_ff_write(unsigned const p, unsigned data, unsigned long if ((data & 0x81) == 0x81) { intreq_.setEventTime(data & isCgb() * 2 - ? (cc & ~0x07ul) + 0x010 * 8 - : (cc & ~0xFFul) + 0x200 * 8); + ? cc - (cc - tima_.divLastUpdate()) % 8 + 0x10 * serialCnt_ + : cc - (cc - tima_.divLastUpdate()) % 0x100 + 0x200 * serialCnt_); } else intreq_.setEventTime(disabled_time); data |= 0x7E - isCgb() * 2; break; case 0x04: - ioamhram_[0x104] = 0; - divLastUpdate_ = cc; - tima_.resTac(cc, TimaInterruptRequester(intreq_)); + if (intreq_.eventTime(intevent_serial) != disabled_time + && intreq_.eventTime(intevent_serial) > cc) { + unsigned long const t = intreq_.eventTime(intevent_serial); + unsigned long const n = ioamhram_[0x102] & isCgb() * 2 + ? t + (cc - t) % 8 - 2 * ((cc - t) & 4) + : t + (cc - t) % 0x100 - 2 * ((cc - t) & 0x80); + intreq_.setEventTime(std::max(cc, n)); + } + psg_.generateSamples(cc, isDoubleSpeed()); + psg_.divReset(isDoubleSpeed()); + tima_.divReset(cc, TimaInterruptRequester(intreq_)); return; case 0x05: tima_.setTima(data, cc, TimaInterruptRequester(intreq_)); @@ -697,7 +756,7 @@ void Memory::nontrivial_ff_write(unsigned const p, unsigned data, unsigned long tima_.setTac(data, cc, TimaInterruptRequester(intreq_), agbMode_); break; case 0x0F: - updateIrqs(cc); + updateIrqs(cc + 1 + isDoubleSpeed()); intreq_.setIfreg(0xE0 | data); return; case 0x10: @@ -739,7 +798,7 @@ void Memory::nontrivial_ff_write(unsigned const p, unsigned data, unsigned long return; psg_.generateSamples(cc, isDoubleSpeed()); - psg_.setNr14(data); + psg_.setNr14(data, isDoubleSpeed()); data |= 0xBF; break; case 0x16: @@ -773,7 +832,7 @@ void Memory::nontrivial_ff_write(unsigned const p, unsigned data, unsigned long return; psg_.generateSamples(cc, isDoubleSpeed()); - psg_.setNr24(data); + psg_.setNr24(data, isDoubleSpeed()); data |= 0xBF; break; case 0x1A: @@ -866,8 +925,9 @@ void Memory::nontrivial_ff_write(unsigned const p, unsigned data, unsigned long ff_write(i, 0, cc); psg_.setEnabled(false); - } else { - psg_.reset(); + } + else { + psg_.reset(isDoubleSpeed()); psg_.setEnabled(true); } } @@ -896,8 +956,7 @@ void Memory::nontrivial_ff_write(unsigned const p, unsigned data, unsigned long case 0x40: if (ioamhram_[0x140] != data) { if ((ioamhram_[0x140] ^ data) & lcdc_en) { - unsigned const lyc = lcd_.getStat(ioamhram_[0x145], cc) - & lcdstat_lycflag; + unsigned const stat = data & lcdc_en ? ioamhram_[0x141] : lcd_.getStat(ioamhram_[0x145], cc); bool const hdmaEnabled = lcd_.hdmaIsEnabled(); lcd_.lcdcChange(data, cc); @@ -905,19 +964,23 @@ void Memory::nontrivial_ff_write(unsigned const p, unsigned data, unsigned long ioamhram_[0x141] &= 0xF8; if (data & lcdc_en) { + if (ioamhram_[0x141] & lcdstat_lycirqen && ioamhram_[0x145] == 0 && !(stat & lcdstat_lycflag)) + intreq_.flagIrq(2); + intreq_.setEventTime(blanklcd_ ? lcd_.nextMode1IrqTime() - : lcd_.nextMode1IrqTime() - + (70224 << isDoubleSpeed())); - } else { - ioamhram_[0x141] |= lyc; + : lcd_.nextMode1IrqTime() + (lcd_cycles_per_frame << isDoubleSpeed())); + } + else { + ioamhram_[0x141] |= stat & lcdstat_lycflag; intreq_.setEventTime( - cc + (456 * 4 << isDoubleSpeed())); + cc + (lcd_cycles_per_line * 4 << isDoubleSpeed())); if (hdmaEnabled) flagHdmaReq(intreq_); } - } else + } + else lcd_.lcdcChange(data, cc); ioamhram_[0x140] = data; @@ -926,6 +989,10 @@ void Memory::nontrivial_ff_write(unsigned const p, unsigned data, unsigned long return; case 0x41: lcd_.lcdstatChange(data, cc); + if (!(ioamhram_[0x140] & lcdc_en) && (ioamhram_[0x141] & lcdstat_lycflag) + && (~ioamhram_[0x141] & lcdstat_lycirqen & (isCgb() ? data : -1))) { + intreq_.flagIrq(2); + } data = (ioamhram_[0x141] & 0x87) | (data & 0x78); break; case 0x42: @@ -938,26 +1005,24 @@ void Memory::nontrivial_ff_write(unsigned const p, unsigned data, unsigned long lcd_.lycRegChange(data, cc); break; case 0x46: - if (lastOamDmaUpdate_ != disabled_time) - endOamDma(cc); - lastOamDmaUpdate_ = cc; - intreq_.setEventTime(cc + 8); + oamDmaStartPos_ = (oamDmaPos_ + 2) & 0xFF; + intreq_.setEventTime(std::min(intreq_.eventTime(intevent_oam), cc + 8)); ioamhram_[0x146] = data; oamDmaInitSetup(); return; case 0x47: - if (!isCgb()) + if (!isCgb() || isCgbDmg()) lcd_.dmgBgPaletteChange(data, cc); break; case 0x48: - if (!isCgb()) + if (!isCgb() || isCgbDmg()) lcd_.dmgSpPalette1Change(data, cc); break; case 0x49: - if (!isCgb()) + if (!isCgb() || isCgbDmg()) lcd_.dmgSpPalette2Change(data, cc); break; @@ -966,25 +1031,34 @@ void Memory::nontrivial_ff_write(unsigned const p, unsigned data, unsigned long break; case 0x4B: lcd_.wxChange(data, cc); + break; + case 0x4C: + if (!biosMode_) + return; + break; case 0x4D: - if (isCgb()) + if (isCgb() && !isCgbDmg()) ioamhram_[0x14D] = (ioamhram_[0x14D] & ~1u) | (data & 1); return; case 0x4F: - if (isCgb()) { + if (isCgb() && !isCgbDmg()) { cart_.setVrambank(data & 1); ioamhram_[0x14F] = 0xFE | data; } return; case 0x50: - biosMode_ = false; - if(cgbSwitching_) { + if (!biosMode_) + return; + + if (isCgb() && (ioamhram_[0x14C] & 0x04)) { lcd_.copyCgbPalettesToDmg(); - lcd_.setCgb(false); + lcd_.setCgbDmg(true); } + + biosMode_ = false; return; case 0x51: dmaSource_ = data << 8 | (dmaSource_ & 0xFF); @@ -1033,8 +1107,8 @@ void Memory::nontrivial_ff_write(unsigned const p, unsigned data, unsigned long if (isCgb()) { unsigned index = ioamhram_[0x168] & 0x3F; lcd_.cgbBgColorChange(index, data, cc); - ioamhram_[0x168] = (ioamhram_[0x168] & ~0x3F) - | ((index + (ioamhram_[0x168] >> 7)) & 0x3F); + ioamhram_[0x168] = (ioamhram_[0x168] & ~0x3Fu) + | ((index + (ioamhram_[0x168] >> 7)) & 0x3F); } return; @@ -1047,20 +1121,18 @@ void Memory::nontrivial_ff_write(unsigned const p, unsigned data, unsigned long if (isCgb()) { unsigned index = ioamhram_[0x16A] & 0x3F; lcd_.cgbSpColorChange(index, data, cc); - ioamhram_[0x16A] = (ioamhram_[0x16A] & ~0x3F) - | ((index + (ioamhram_[0x16A] >> 7)) & 0x3F); + ioamhram_[0x16A] = (ioamhram_[0x16A] & ~0x3Fu) + | ((index + (ioamhram_[0x16A] >> 7)) & 0x3F); } return; case 0x6C: - if (isCgb()) { + if (isCgb()) ioamhram_[0x16C] = data | 0xFE; - cgbSwitching_ = true; - } return; case 0x70: - if (isCgb()) { + if (isCgb() && !isCgbDmg()) { cart_.setWrambank(data & 0x07 ? data & 0x07 : 1); ioamhram_[0x170] = data | 0xF8; } @@ -1092,47 +1164,70 @@ void Memory::nontrivial_write(unsigned const p, unsigned const data, unsigned lo if (lastOamDmaUpdate_ != disabled_time) { updateOamDma(cc); - if (isInOamDmaConflictArea(cart_.oamDmaSrc(), p, isCgb()) && oamDmaPos_ < 0xA0) { - ioamhram_[oamDmaPos_] = data; + if (cart_.isInOamDmaConflictArea(p) && oamDmaPos_ < oam_size) { + if (isCgb()) { + if (p < mm_wram_begin) + ioamhram_[oamDmaPos_] = cart_.oamDmaSrc() != oam_dma_src_vram ? data : 0; + else if (cart_.oamDmaSrc() != oam_dma_src_wram) + cart_.wramdata(ioamhram_[0x146] >> 4 & 1)[p & 0xFFF] = data; + } + else { + ioamhram_[oamDmaPos_] = cart_.oamDmaSrc() == oam_dma_src_wram + ? ioamhram_[oamDmaPos_] & data + : data; + } + return; } } - if (p < 0xFE00) { - if (p < 0xA000) { - if (p < 0x8000) { + if (p < mm_oam_begin) { + if (p < mm_sram_begin) { + if (p < mm_vram_begin) { cart_.mbcWrite(p, data, cc); - } else if (lcd_.vramAccessible(cc)) { + } + else if (lcd_.vramWritable(cc)) { lcd_.vramChange(cc); cart_.vrambankptr()[p] = data; } - } else if (p < 0xC000) { + } + else if (p < mm_wram_begin) { if (cart_.wsrambankptr()) cart_.wsrambankptr()[p] = data; else cart_.rtcWrite(data, cc); - } else + } + else cart_.wramdata(p >> 12 & 1)[p & 0xFFF] = data; - } else if (p - 0xFF80u >= 0x7Fu) { - long const ffp = long(p) - 0xFF00; + } + else if (p - mm_hram_begin >= 0x7Fu) { + long const ffp = static_cast(p) - mm_io_begin; if (ffp < 0) { - if (lcd_.oamWritable(cc) && oamDmaPos_ >= 0xA0 && (p < 0xFEA0 || isCgb())) { + if (lcd_.oamWritable(cc) && oamDmaPos_ >= oam_size + && (p < mm_oam_begin + oam_size || isCgb())) { lcd_.oamChange(cc); - ioamhram_[p - 0xFE00] = data; + ioamhram_[p - mm_oam_begin] = data; } - } else + } + else nontrivial_ff_write(ffp, data, cc); - } else - ioamhram_[p - 0xFE00] = data; + } + else + ioamhram_[p - mm_oam_begin] = data; } -LoadRes Memory::loadROM(char const *romfiledata, unsigned romfilelength, const bool forceDmg, const bool multicartCompat) { +LoadRes Memory::loadROM(char const *romfiledata, unsigned romfilelength, unsigned const flags) { + bool const forceDmg = flags & GB::LoadFlag::FORCE_DMG; + bool const multicartCompat = flags & GB::LoadFlag::MULTICART_COMPAT; + if (LoadRes const fail = cart_.loadROM(romfiledata, romfilelength, forceDmg, multicartCompat)) return fail; psg_.init(cart_.isCgb()); lcd_.reset(ioamhram_, cart_.vramdata(), cart_.isCgb()); + agbMode_ = flags & GB::LoadFlag::GBA_CGB; + return LOADRES_OK; } @@ -1215,13 +1310,7 @@ SYNCFUNC(Memory) 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 7d7dcc1d73..601c0bf0c9 100644 --- a/libgambatte/src/memory.h +++ b/libgambatte/src/memory.h @@ -22,6 +22,7 @@ static unsigned char const agbOverride[0xD] = { 0xFF, 0x00, 0xCD, 0x03, 0x35, 0xAA, 0x31, 0x90, 0x94, 0x00, 0x00, 0x00, 0x00 }; #include "mem/cartridge.h" +#include "interrupter.h" #include "sound.h" #include "tima.h" #include "video.h" @@ -34,7 +35,7 @@ class FilterInfo; class Memory { public: - explicit Memory(unsigned short &sp, unsigned short &pc); + explicit Memory(Interrupter const &interrupter); ~Memory(); bool loaded() const { return cart_.loaded(); } @@ -54,12 +55,12 @@ public: memcpy(bios_, buffer, size); biosSize_ = size; } - bool gbIsCgb() { return gbIsCgb_; } bool getMemoryArea(int which, unsigned char **data, int *length); - unsigned long stop(unsigned long cycleCounter); + unsigned long stop(unsigned long cycleCounter, bool& skip); bool isCgb() const { return lcd_.isCgb(); } + bool isCgbDmg() const { return lcd_.isCgbDmg(); } bool ime() const { return intreq_.ime(); } bool halted() const { return intreq_.halted(); } unsigned long nextEventTime() const { return intreq_.minEventTime(); } @@ -73,10 +74,14 @@ public: return (cc - intreq_.eventTime(intevent_blit)) >> isDoubleSpeed(); } - void halt(unsigned long cycleCounter) { halttime_ = cycleCounter; intreq_.halt(); } + void freeze(unsigned long cc); + bool halt(unsigned long cc); void ei(unsigned long cycleCounter) { if (!ime()) { intreq_.ei(cycleCounter); } } void di() { intreq_.di(); } + unsigned pendingIrqs(unsigned long cc); + void ackIrq(unsigned bit, unsigned long cc); + unsigned readBios(unsigned p) { if(agbMode_ && p >= 0xF3 && p < 0x100) { return (agbOverride[p-0xF3] + bios_[p]) & 0xFF; @@ -221,7 +226,7 @@ public: unsigned long event(unsigned long cycleCounter); unsigned long resetCounters(unsigned long cycleCounter); - LoadRes loadROM(char const *romfiledata, unsigned romfilelength, bool forceDmg, bool multicartCompat); + LoadRes loadROM(char const *romfiledata, unsigned romfilelength, unsigned flags); void setInputGetter(unsigned (*getInput)()) { getInput_ = getInput; @@ -262,6 +267,10 @@ public: lcd_.setDmgPaletteColor(palNum, colorNum, rgb32); } + void blackScreen() { + lcd_.blackScreen(); + } + void setCgbPalette(unsigned *lut); void setTimeMode(bool useCycles, unsigned long const cc) { cart_.setTimeMode(useCycles, cc); @@ -282,20 +291,18 @@ private: Tima tima_; LCD lcd_; PSG psg_; + Interrupter interrupter_; unsigned short dmaSource_; unsigned short dmaDestination_; unsigned char oamDmaPos_; + unsigned char oamDmaStartPos_; 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_; + enum HdmaState { hdma_low, hdma_high, hdma_requested } haltHdmaState_; MemoryCallback readCallback_; MemoryCallback writeCallback_; @@ -311,6 +318,7 @@ private: void startOamDma(unsigned long cycleCounter); void endOamDma(unsigned long cycleCounter); unsigned char const * oamDmaSrcPtr() const; + unsigned long dma(unsigned long cc); 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); diff --git a/libgambatte/src/savestate.h b/libgambatte/src/savestate.h index c130ab5ecb..e07908cacf 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); + friend void setInitState(SaveState &, bool); private: T *ptr; std::size_t size_; @@ -54,6 +54,8 @@ struct SaveState { unsigned char f; unsigned char h; unsigned char l; + unsigned char opcode; + unsigned char /*bool*/ prefetched; unsigned char /*bool*/ skip; } cpu; @@ -63,28 +65,24 @@ struct SaveState { Ptr wram; Ptr ioamhram; unsigned long divLastUpdate; - unsigned long timaBasetime; unsigned long timaLastUpdate; unsigned long tmatime; unsigned long nextSerialtime; unsigned long lastOamDmaUpdate; unsigned long minIntTime; unsigned long unhaltTime; - unsigned long halttime; unsigned short rombank; unsigned short dmaSource; unsigned short dmaDestination; unsigned char rambank; unsigned char oamDmaPos; + unsigned char haltHdmaState; 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; unsigned char /*bool*/ stopped; } mem; @@ -121,7 +119,7 @@ struct SaveState { unsigned char wscx; unsigned char /*bool*/ weMaster; unsigned char /*bool*/ pendingLcdstatIrq; - unsigned char /*bool*/ isCgb; + unsigned char /*bool*/ notCgbDmg; } ppu; struct SPU { @@ -147,7 +145,7 @@ struct SaveState { unsigned long counter; unsigned short shadow; unsigned char nr0; - unsigned char /*bool*/ negging; + unsigned char /*bool*/ neg; } sweep; Duty duty; Env env; @@ -188,6 +186,7 @@ struct SaveState { } ch4; unsigned long cycleCounter; + unsigned char lastUpdate; } spu; struct Time { diff --git a/libgambatte/src/sound.cpp b/libgambatte/src/sound.cpp index 69ebe39f18..e6db1c9d55 100644 --- a/libgambatte/src/sound.cpp +++ b/libgambatte/src/sound.cpp @@ -43,12 +43,13 @@ Clock) clock timer on transition to step. */ -namespace gambatte { +using namespace gambatte; PSG::PSG() : buffer_(0) , bufferPos_(0) , lastUpdate_(0) +, cycleCounter_(0) , soVol_(0) , rsum_(0x8000) // initialize to 0x8000 to prevent borrows from high word, xor away later , enabled_(false) @@ -60,11 +61,41 @@ void PSG::init(bool cgb) { ch3_.init(cgb); } -void PSG::reset() { +void PSG::reset(bool ds) { + int const divOffset = lastUpdate_ & ds; + unsigned long const cc = cycleCounter_ + divOffset; + // cycleCounter >> 12 & 7 represents the frame sequencer position. + cycleCounter_ = (cc & 0xFFF) + 2 * (~(cc + 1 + !ds) & 0x800); + lastUpdate_ = ((lastUpdate_ + 3) & -4) - !ds; ch1_.reset(); ch2_.reset(); ch3_.reset(); - ch4_.reset(); + ch4_.reset(cycleCounter_); +} + +void PSG::divReset(bool ds) { + int const divOffset = lastUpdate_ & ds; + unsigned long const cc = cycleCounter_ + divOffset; + cycleCounter_ = (cc & -0x1000) + 2 * (cc & 0x800) - divOffset; + ch1_.resetCc(cc - divOffset, cycleCounter_); + ch2_.resetCc(cc - divOffset, cycleCounter_); + ch3_.resetCc(cc - divOffset, cycleCounter_); + ch4_.resetCc(cc - divOffset, cycleCounter_); +} + +void PSG::speedChange(unsigned long const cpuCc, bool const ds) { + generateSamples(cpuCc, ds); + lastUpdate_ -= ds; + // correct for cycles since DIV reset (if any). + if (!ds) { + unsigned long const cc = cycleCounter_; + unsigned const divCycles = cc & 0xFFF; + cycleCounter_ = cc - divCycles / 2 - lastUpdate_ % 2; + ch1_.resetCc(cc, cycleCounter_); + ch2_.resetCc(cc, cycleCounter_); + ch3_.resetCc(cc, cycleCounter_); + ch4_.resetCc(cc, cycleCounter_); + } } void PSG::setStatePtrs(SaveState &state) { @@ -77,23 +108,26 @@ void PSG::loadState(SaveState const &state) { ch3_.loadState(state); ch4_.loadState(state); - lastUpdate_ = state.cpu.cycleCounter; + cycleCounter_ = state.spu.cycleCounter; + lastUpdate_ = state.cpu.cycleCounter - (1 - state.spu.lastUpdate) % 4u; setSoVolume(state.mem.ioamhram.get()[0x124]); mapSo(state.mem.ioamhram.get()[0x125]); enabled_ = state.mem.ioamhram.get()[0x126] >> 7 & 1; } -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); +inline void PSG::accumulateChannels(unsigned long const cycles) { + unsigned long const cc = cycleCounter_; + uint_least32_t* const buf = buffer_ + bufferPos_; + std::memset(buf, 0, cycles * sizeof * buf); + ch1_.update(buf, soVol_, cc, cc + cycles); + ch2_.update(buf, soVol_, cc, cc + cycles); + ch3_.update(buf, soVol_, cc, cc + cycles); + ch4_.update(buf, soVol_, cc, cc + cycles); + cycleCounter_ = (cc + cycles) % SoundUnit::counter_max; } -void PSG::generateSamples(unsigned long const cycleCounter, bool const doubleSpeed) { - unsigned long const cycles = (cycleCounter - lastUpdate_) >> (1 + doubleSpeed); +void PSG::generateSamples(unsigned long const cpuCc, bool const doubleSpeed) { + unsigned long const cycles = (cpuCc - lastUpdate_) >> (1 + doubleSpeed); lastUpdate_ += cycles << (1 + doubleSpeed); if (cycles) @@ -168,10 +202,10 @@ void PSG::setSoVolume(unsigned nr50) { void PSG::mapSo(unsigned nr51) { unsigned long so = nr51 * so1Mul() + (nr51 >> 4) * so2Mul(); - ch1_.setSo((so & 0x00010001) * 0xFFFF); - ch2_.setSo((so >> 1 & 0x00010001) * 0xFFFF); + ch1_.setSo((so & 0x00010001) * 0xFFFF, cycleCounter_); + ch2_.setSo((so >> 1 & 0x00010001) * 0xFFFF, cycleCounter_); ch3_.setSo((so >> 2 & 0x00010001) * 0xFFFF); - ch4_.setSo((so >> 3 & 0x00010001) * 0xFFFF); + ch4_.setSo((so >> 3 & 0x00010001) * 0xFFFF, cycleCounter_); } unsigned PSG::getStatus() const { @@ -189,9 +223,8 @@ SYNCFUNC(PSG) SSS(ch3_); SSS(ch4_); NSS(lastUpdate_); + NSS(cycleCounter_); NSS(soVol_); NSS(rsum_); NSS(enabled_); } - -} diff --git a/libgambatte/src/sound.h b/libgambatte/src/sound.h index b664d8c616..232d52a518 100644 --- a/libgambatte/src/sound.h +++ b/libgambatte/src/sound.h @@ -31,12 +31,14 @@ class PSG { public: PSG(); void init(bool cgb); - void reset(); + void reset(bool ds); + void divReset(bool ds); void setStatePtrs(SaveState &state); void loadState(SaveState const &state); void generateSamples(unsigned long cycleCounter, bool doubleSpeed); void resetCounter(unsigned long newCc, unsigned long oldCc, bool doubleSpeed); + void speedChange(unsigned long cc, bool doubleSpeed); std::size_t fillBuffer(); void setBuffer(uint_least32_t *buf) { buffer_ = buf; bufferPos_ = 0; } @@ -44,28 +46,28 @@ public: void setEnabled(bool value) { enabled_ = value; } 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 setNr11(unsigned data) { ch1_.setNr1(data, cycleCounter_); } + void setNr12(unsigned data) { ch1_.setNr2(data, cycleCounter_); } + void setNr13(unsigned data) { ch1_.setNr3(data, cycleCounter_); } + void setNr14(unsigned data, bool ds) { ch1_.setNr4(data, cycleCounter_, !(lastUpdate_ & ds)); } - 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 setNr21(unsigned data) { ch2_.setNr1(data, cycleCounter_); } + void setNr22(unsigned data) { ch2_.setNr2(data, cycleCounter_); } + void setNr23(unsigned data) { ch2_.setNr3(data, cycleCounter_); } + void setNr24(unsigned data, bool ds) { ch2_.setNr4(data, cycleCounter_, !(lastUpdate_ & ds)); } void setNr30(unsigned data) { ch3_.setNr0(data); } - void setNr31(unsigned data) { ch3_.setNr1(data); } + void setNr31(unsigned data) { ch3_.setNr1(data, cycleCounter_); } 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 setNr34(unsigned data) { ch3_.setNr4(data, cycleCounter_); } + unsigned waveRamRead(unsigned index) const { return ch3_.waveRamRead(index, cycleCounter_); } + void waveRamWrite(unsigned index, unsigned data) { ch3_.waveRamWrite(index, data, cycleCounter_); } - 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 setNr41(unsigned data) { ch4_.setNr1(data, cycleCounter_); } + void setNr42(unsigned data) { ch4_.setNr2(data, cycleCounter_); } + void setNr43(unsigned data) { ch4_.setNr3(data, cycleCounter_); } + void setNr44(unsigned data) { ch4_.setNr4(data, cycleCounter_); } void setSoVolume(unsigned nr50); void mapSo(unsigned nr51); @@ -79,6 +81,7 @@ private: uint_least32_t *buffer_; std::size_t bufferPos_; unsigned long lastUpdate_; + unsigned long cycleCounter_; unsigned long soVol_; uint_least32_t rsum_; bool enabled_; diff --git a/libgambatte/src/sound/channel1.cpp b/libgambatte/src/sound/channel1.cpp index 9e94ab2a20..58d90edbf1 100644 --- a/libgambatte/src/sound/channel1.cpp +++ b/libgambatte/src/sound/channel1.cpp @@ -17,30 +17,28 @@ // #include "channel1.h" +#include "psgdef.h" #include "../savestate.h" #include -namespace gambatte { +using namespace gambatte; Channel1::SweepUnit::SweepUnit(MasterDisabler &disabler, DutyUnit &dutyUnit) : disableMaster_(disabler) , dutyUnit_(dutyUnit) , shadow_(0) , nr0_(0) -, negging_(false) +, neg_(false) , cgb_(false) { } unsigned Channel1::SweepUnit::calcFreq() { - unsigned freq = shadow_ >> (nr0_ & 0x07); - - if (nr0_ & 0x08) { - freq = shadow_ - freq; - negging_ = true; - } else - freq = shadow_ + freq; - + unsigned const freq = nr0_ & psg_nr10_neg + ? shadow_ - (shadow_ >> (nr0_ & psg_nr10_rsh)) + : shadow_ + (shadow_ >> (nr0_ & psg_nr10_rsh)); + if (nr0_ & psg_nr10_neg) + neg_ = true; if (freq & 2048) disableMaster_(); @@ -48,12 +46,12 @@ unsigned Channel1::SweepUnit::calcFreq() { } void Channel1::SweepUnit::event() { - unsigned long const period = nr0_ >> 4 & 0x07; + unsigned long const period = (nr0_ & psg_nr10_time) / (1u * psg_nr10_time & -psg_nr10_time); if (period) { unsigned const freq = calcFreq(); - if (!(freq & 2048) && (nr0_ & 0x07)) { + if (!(freq & 2048) && nr0_ & psg_nr10_rsh) { shadow_ = freq; dutyUnit_.setFreq(freq, counter_); calcFreq(); @@ -65,25 +63,25 @@ void Channel1::SweepUnit::event() { } void Channel1::SweepUnit::nr0Change(unsigned newNr0) { - if (negging_ && !(newNr0 & 0x08)) + if (neg_ && !(newNr0 & 0x08)) disableMaster_(); nr0_ = newNr0; } void Channel1::SweepUnit::nr4Init(unsigned long const cc) { - negging_ = false; + neg_ = false; shadow_ = dutyUnit_.freq(); - unsigned const period = nr0_ >> 4 & 0x07; - unsigned const shift = nr0_ & 0x07; + unsigned const period = (nr0_ & psg_nr10_time) / (1u * psg_nr10_time & -psg_nr10_time); + unsigned const rsh = nr0_ & psg_nr10_rsh; - if (period | shift) + if (period | rsh) counter_ = ((((cc + 2 + cgb_ * 2) >> 14) + (period ? period : 8)) << 14) + 2; else counter_ = counter_disabled; - if (shift) + if (rsh) calcFreq(); } @@ -95,7 +93,7 @@ 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; + neg_ = state.spu.ch1.sweep.neg; } template @@ -104,7 +102,7 @@ void Channel1::SweepUnit::SyncState(NewState *ns) NSS(counter_); NSS(shadow_); NSS(nr0_); - NSS(negging_); + NSS(neg_); NSS(cgb_); } @@ -115,7 +113,6 @@ Channel1::Channel1() , envelopeUnit_(staticOutputTest_) , sweepUnit_(disableMaster_, dutyUnit_) , nextEventUnit_(0) -, cycleCounter_(0) , soMask_(0) , prevOut_(0) , nr4_(0) @@ -137,52 +134,48 @@ void Channel1::setNr0(unsigned data) { setEvent(); } -void Channel1::setNr1(unsigned data) { - lengthCounter_.nr1Change(data, nr4_, cycleCounter_); - dutyUnit_.nr1Change(data, cycleCounter_); +void Channel1::setNr1(unsigned data, unsigned long cc) { + lengthCounter_.nr1Change(data, nr4_, cc); + dutyUnit_.nr1Change(data, cc); setEvent(); } -void Channel1::setNr2(unsigned data) { +void Channel1::setNr2(unsigned data, unsigned long cc) { if (envelopeUnit_.nr2Change(data)) disableMaster_(); else - staticOutputTest_(cycleCounter_); + staticOutputTest_(cc); setEvent(); } -void Channel1::setNr3(unsigned data) { - dutyUnit_.nr3Change(data, cycleCounter_); +void Channel1::setNr3(unsigned data, unsigned long cc) { + dutyUnit_.nr3Change(data, cc); setEvent(); } -void Channel1::setNr4(unsigned const data) { - lengthCounter_.nr4Change(nr4_, data, cycleCounter_); +void Channel1::setNr4(unsigned data, unsigned long cc, unsigned long ref) { + lengthCounter_.nr4Change(nr4_, data, cc); + dutyUnit_.nr4Change(data, cc, ref, master_); nr4_ = data; - dutyUnit_.nr4Change(data, cycleCounter_, master_); - if (data & 0x80) { // init-bit - nr4_ &= 0x7F; - master_ = !envelopeUnit_.nr4Init(cycleCounter_); - sweepUnit_.nr4Init(cycleCounter_); - staticOutputTest_(cycleCounter_); + if (nr4_ & psg_nr4_init) { + nr4_ -= psg_nr4_init; + master_ = !envelopeUnit_.nr4Init(cc); + sweepUnit_.nr4Init(cc); + staticOutputTest_(cc); } setEvent(); } -void Channel1::setSo(unsigned long soMask) { +void Channel1::setSo(unsigned long soMask, unsigned long cc) { soMask_ = soMask; - staticOutputTest_(cycleCounter_); + staticOutputTest_(cc); setEvent(); } void Channel1::reset() { - // cycleCounter >> 12 & 7 represents the frame sequencer position. - cycleCounter_ &= 0xFFF; - cycleCounter_ += ~(cycleCounter_ + 2) << 1 & 0x1000; - dutyUnit_.reset(); envelopeUnit_.reset(); sweepUnit_.reset(); @@ -196,58 +189,51 @@ void Channel1::init(bool cgb) { 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); + state.spu.ch1.nr4, state.spu.cycleCounter); envelopeUnit_.loadState(state.spu.ch1.env, state.mem.ioamhram.get()[0x112], - state.spu.cycleCounter); + 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; } -void Channel1::update(uint_least32_t *buf, unsigned long const soBaseVol, unsigned long cycles) { +void Channel1::update(uint_least32_t* buf, unsigned long const soBaseVol, unsigned long cc, unsigned long const end) { unsigned long const outBase = envelopeUnit_.dacIsOn() ? soBaseVol & soMask_ : 0; - unsigned long const outLow = outBase * (0 - 15ul); - unsigned long const endCycles = cycleCounter_ + cycles; + unsigned long const outLow = outBase * -15; - for (;;) { + while (cc < end) { unsigned long const outHigh = master_ - ? outBase * (envelopeUnit_.getVolume() * 2 - 15ul) - : outLow; - unsigned long const nextMajorEvent = std::min(nextEventUnit_->counter(), endCycles); + ? outBase * (envelopeUnit_.getVolume() * 2l - 15) + : outLow; + unsigned long const nextMajorEvent = std::min(nextEventUnit_->counter(), end); unsigned long out = dutyUnit_.isHighState() ? outHigh : outLow; while (dutyUnit_.counter() <= nextMajorEvent) { *buf = out - prevOut_; prevOut_ = out; - buf += dutyUnit_.counter() - cycleCounter_; - cycleCounter_ = dutyUnit_.counter(); - + buf += dutyUnit_.counter() - cc; + cc = dutyUnit_.counter(); dutyUnit_.event(); out = dutyUnit_.isHighState() ? outHigh : outLow; } - - if (cycleCounter_ < nextMajorEvent) { + if (cc < nextMajorEvent) { *buf = out - prevOut_; prevOut_ = out; - buf += nextMajorEvent - cycleCounter_; - cycleCounter_ = nextMajorEvent; + buf += nextMajorEvent - cc; + cc = nextMajorEvent; } - 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 (cc >= SoundUnit::counter_max) { + dutyUnit_.resetCounters(cc); + lengthCounter_.resetCounters(cc); + envelopeUnit_.resetCounters(cc); + sweepUnit_.resetCounters(cc); } } @@ -265,12 +251,9 @@ SYNCFUNC(Channel1) EVS(nextEventUnit_, &lengthCounter_, 4); EES(nextEventUnit_, NULL); - NSS(cycleCounter_); NSS(soMask_); NSS(prevOut_); NSS(nr4_); NSS(master_); } - -} diff --git a/libgambatte/src/sound/channel1.h b/libgambatte/src/sound/channel1.h index 36c62e646b..99e03431f8 100644 --- a/libgambatte/src/sound/channel1.h +++ b/libgambatte/src/sound/channel1.h @@ -35,14 +35,15 @@ class Channel1 { public: Channel1(); void setNr0(unsigned data); - void setNr1(unsigned data); - void setNr2(unsigned data); - void setNr3(unsigned data); - void setNr4(unsigned data); - void setSo(unsigned long soMask); + void setNr1(unsigned data, unsigned long cc); + void setNr2(unsigned data, unsigned long cc); + void setNr3(unsigned data, unsigned long cc); + void setNr4(unsigned data, unsigned long cc, unsigned long ref); + void setSo(unsigned long soMask, unsigned long cc); bool isActive() const { return master_; } - void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles); + void update(uint_least32_t* buf, unsigned long soBaseVol, unsigned long cc, unsigned long end); void reset(); + void resetCc(unsigned long cc, unsigned long ncc) { dutyUnit_.resetCc(cc, ncc); } void init(bool cgb); void loadState(SaveState const &state); @@ -54,6 +55,7 @@ private: void nr0Change(unsigned newNr0); void nr4Init(unsigned long cycleCounter); void reset(); + void resetCc(unsigned long cc, unsigned long ncc) { dutyUnit_.resetCc(cc, ncc); } void init(bool cgb) { cgb_ = cgb; } void loadState(SaveState const &state); @@ -62,7 +64,7 @@ private: DutyUnit &dutyUnit_; unsigned short shadow_; unsigned char nr0_; - bool negging_; + bool neg_; bool cgb_; unsigned calcFreq(); @@ -80,7 +82,6 @@ private: EnvelopeUnit envelopeUnit_; SweepUnit sweepUnit_; SoundUnit *nextEventUnit_; - unsigned long cycleCounter_; unsigned long soMask_; unsigned long prevOut_; unsigned char nr4_; diff --git a/libgambatte/src/sound/channel2.cpp b/libgambatte/src/sound/channel2.cpp index 6f32e4dd46..efbfa18670 100644 --- a/libgambatte/src/sound/channel2.cpp +++ b/libgambatte/src/sound/channel2.cpp @@ -17,17 +17,17 @@ // #include "channel2.h" +#include "psgdef.h" #include "../savestate.h" #include -namespace gambatte { +using namespace gambatte; Channel2::Channel2() : staticOutputTest_(*this, dutyUnit_) , disableMaster_(master_, dutyUnit_) , lengthCounter_(disableMaster_, 0x3F) , envelopeUnit_(staticOutputTest_) -, cycleCounter_(0) , soMask_(0) , prevOut_(0) , nr4_(0) @@ -42,51 +42,47 @@ void Channel2::setEvent() { nextEventUnit = &lengthCounter_; } -void Channel2::setNr1(unsigned data) { - lengthCounter_.nr1Change(data, nr4_, cycleCounter_); - dutyUnit_.nr1Change(data, cycleCounter_); +void Channel2::setNr1(unsigned data, unsigned long cc) { + lengthCounter_.nr1Change(data, nr4_, cc); + dutyUnit_.nr1Change(data, cc); setEvent(); } -void Channel2::setNr2(unsigned data) { +void Channel2::setNr2(unsigned data, unsigned long cc) { if (envelopeUnit_.nr2Change(data)) disableMaster_(); else - staticOutputTest_(cycleCounter_); + staticOutputTest_(cc); setEvent(); } -void Channel2::setNr3(unsigned data) { - dutyUnit_.nr3Change(data, cycleCounter_); +void Channel2::setNr3(unsigned data, unsigned long cc) { + dutyUnit_.nr3Change(data, cc); setEvent(); } -void Channel2::setNr4(unsigned const data) { - lengthCounter_.nr4Change(nr4_, data, cycleCounter_); +void Channel2::setNr4(unsigned data, unsigned long cc, unsigned long ref) { + lengthCounter_.nr4Change(nr4_, data, cc); nr4_ = data; - dutyUnit_.nr4Change(data, cycleCounter_, master_); - if (data & 0x80) { // init-bit - nr4_ &= 0x7F; - master_ = !envelopeUnit_.nr4Init(cycleCounter_); - staticOutputTest_(cycleCounter_); + if (nr4_ & psg_nr4_init) { + nr4_ -= psg_nr4_init; + master_ = !envelopeUnit_.nr4Init(cc); + staticOutputTest_(cc); } + dutyUnit_.nr4Change(data, cc, ref, master_); setEvent(); } -void Channel2::setSo(unsigned long soMask) { +void Channel2::setSo(unsigned long soMask, unsigned long cc) { soMask_ = soMask; - staticOutputTest_(cycleCounter_); + staticOutputTest_(cc); setEvent(); } void Channel2::reset() { - // cycleCounter >> 12 & 7 represents the frame sequencer position. - cycleCounter_ &= 0xFFF; - cycleCounter_ += ~(cycleCounter_ + 2) << 1 & 0x1000; - dutyUnit_.reset(); envelopeUnit_.reset(); setEvent(); @@ -94,57 +90,50 @@ void Channel2::reset() { void Channel2::loadState(SaveState const &state) { dutyUnit_.loadState(state.spu.ch2.duty, state.mem.ioamhram.get()[0x116], - state.spu.ch2.nr4, state.spu.cycleCounter); + state.spu.ch2.nr4, state.spu.cycleCounter); envelopeUnit_.loadState(state.spu.ch2.env, state.mem.ioamhram.get()[0x117], - state.spu.cycleCounter); + 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, unsigned long const soBaseVol, unsigned long cycles) { +void Channel2::update(uint_least32_t* buf, unsigned long const soBaseVol, unsigned long cc, unsigned long const end) { unsigned long const outBase = envelopeUnit_.dacIsOn() ? soBaseVol & soMask_ : 0; - unsigned long const outLow = outBase * (0 - 15ul); - unsigned long const endCycles = cycleCounter_ + cycles; + unsigned long const outLow = outBase * -15; - for (;;) { + while (cc < end) { unsigned long const outHigh = master_ - ? outBase * (envelopeUnit_.getVolume() * 2 - 15ul) - : outLow; - unsigned long const nextMajorEvent = std::min(nextEventUnit->counter(), endCycles); + ? outBase * (envelopeUnit_.getVolume() * 2l - 15) + : outLow; + unsigned long const nextMajorEvent = std::min(nextEventUnit->counter(), end); unsigned long out = dutyUnit_.isHighState() ? outHigh : outLow; while (dutyUnit_.counter() <= nextMajorEvent) { *buf += out - prevOut_; prevOut_ = out; - buf += dutyUnit_.counter() - cycleCounter_; - cycleCounter_ = dutyUnit_.counter(); - + buf += dutyUnit_.counter() - cc; + cc = dutyUnit_.counter(); dutyUnit_.event(); out = dutyUnit_.isHighState() ? outHigh : outLow; } - - if (cycleCounter_ < nextMajorEvent) { + if (cc < nextMajorEvent) { *buf += out - prevOut_; prevOut_ = out; - buf += nextMajorEvent - cycleCounter_; - cycleCounter_ = nextMajorEvent; + buf += nextMajorEvent - cc; + cc = 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 (cc >= SoundUnit::counter_max) { + dutyUnit_.resetCounters(cc); + lengthCounter_.resetCounters(cc); + envelopeUnit_.resetCounters(cc); } } @@ -160,7 +149,6 @@ SYNCFUNC(Channel2) EVS(nextEventUnit, &lengthCounter_, 3); EES(nextEventUnit, NULL); - NSS(cycleCounter_); NSS(soMask_); NSS(prevOut_); @@ -168,4 +156,3 @@ SYNCFUNC(Channel2) NSS(master_); } -} diff --git a/libgambatte/src/sound/channel2.h b/libgambatte/src/sound/channel2.h index 8934cc7991..978b9bc8ed 100644 --- a/libgambatte/src/sound/channel2.h +++ b/libgambatte/src/sound/channel2.h @@ -33,14 +33,15 @@ struct SaveState; class Channel2 { public: Channel2(); - void setNr1(unsigned data); - void setNr2(unsigned data); - void setNr3(unsigned data); - void setNr4(unsigned data); - void setSo(unsigned long soMask); + void setNr1(unsigned data, unsigned long cc); + void setNr2(unsigned data, unsigned long cc); + void setNr3(unsigned data, unsigned long cc); + void setNr4(unsigned data, unsigned long cc, unsigned long ref); + void setSo(unsigned long soMask, unsigned long cc); bool isActive() const { return master_; } - void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles); + void update(uint_least32_t* buf, unsigned long soBaseVol, unsigned long cc, unsigned long end); void reset(); + void resetCc(unsigned long cc, unsigned long ncc) { dutyUnit_.resetCc(cc, ncc); } void loadState(SaveState const &state); private: @@ -52,7 +53,6 @@ private: DutyUnit dutyUnit_; EnvelopeUnit envelopeUnit_; SoundUnit *nextEventUnit; - unsigned long cycleCounter_; unsigned long soMask_; unsigned long prevOut_; unsigned char nr4_; diff --git a/libgambatte/src/sound/channel3.cpp b/libgambatte/src/sound/channel3.cpp index 223378b1ab..0f6cdb3eee 100644 --- a/libgambatte/src/sound/channel3.cpp +++ b/libgambatte/src/sound/channel3.cpp @@ -17,20 +17,22 @@ // #include "channel3.h" +#include "psgdef.h" #include "../savestate.h" #include #include -static inline unsigned toPeriod(unsigned nr3, unsigned nr4) { - return 0x800 - ((nr4 << 8 & 0x700) | nr3); -} +using namespace gambatte; -namespace gambatte { +namespace { + unsigned toPeriod(unsigned nr3, unsigned nr4) { + return 0x800 - ((nr4 << 8 & 0x700) | nr3); + } +} Channel3::Channel3() : disableMaster_(master_, waveCounter_) , lengthCounter_(disableMaster_, 0xFF) -, cycleCounter_(0) , soMask_(0) , prevOut_(0) , waveCounter_(SoundUnit::counter_disabled) @@ -47,25 +49,22 @@ Channel3::Channel3() } void Channel3::setNr0(unsigned data) { - nr0_ = data & 0x80; - - if (!(data & 0x80)) + nr0_ = data & psg_nr4_init; + if (!nr0_) disableMaster_(); } void Channel3::setNr2(unsigned data) { - rshift_ = (data >> 5 & 3U) - 1; - if (rshift_ > 3) - rshift_ = 4; + rshift_ = std::min((data >> 5 & 3) - 1, 4u); } -void Channel3::setNr4(unsigned const data) { - lengthCounter_.nr4Change(nr4_, data, cycleCounter_); - nr4_ = data & 0x7F; +void Channel3::setNr4(unsigned const data, unsigned long const cc) { + lengthCounter_.nr4Change(nr4_, data, cc); + nr4_ = data & ~(1u * psg_nr4_init); - if (data & nr0_/* & 0x80*/) { - if (!cgb_ && waveCounter_ == cycleCounter_ + 1) { - unsigned const pos = ((wavePos_ + 1) & 0x1F) >> 1; + if (data & nr0_) { + if (!cgb_ && waveCounter_ == cc + 1) { + int const pos = (wavePos_ + 1) / 2 % sizeof waveRam_; if (pos < 4) waveRam_[0] = waveRam_[pos]; @@ -75,7 +74,7 @@ void Channel3::setNr4(unsigned const data) { master_ = true; wavePos_ = 0; - lastReadTime_ = waveCounter_ = cycleCounter_ + toPeriod(nr3_, data) + 3; + lastReadTime_ = waveCounter_ = cc + toPeriod(nr3_, data) + 3; } } @@ -84,13 +83,15 @@ void Channel3::setSo(unsigned long soMask) { } void Channel3::reset() { - // cycleCounter >> 12 & 7 represents the frame sequencer position. - cycleCounter_ &= 0xFFF; - cycleCounter_ += ~(cycleCounter_ + 2) << 1 & 0x1000; - sampleBuf_ = 0; } +void Channel3::resetCc(unsigned long cc, unsigned long newCc) { + lastReadTime_ -= cc - newCc; + if (waveCounter_ != SoundUnit::counter_disabled) + waveCounter_ -= cc - newCc; +} + void Channel3::init(bool cgb) { cgb_ = cgb; } @@ -102,16 +103,15 @@ void Channel3::setStatePtrs(SaveState &state) { 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; + wavePos_ = state.spu.ch3.wavePos % (2 * sizeof waveRam_); sampleBuf_ = state.spu.ch3.sampleBuf; master_ = state.spu.ch3.master; - nr0_ = state.mem.ioamhram.get()[0x11A] & 0x80; + nr0_ = state.mem.ioamhram.get()[0x11A] & psg_nr4_init; setNr2(state.mem.ioamhram.get()[0x11C]); } @@ -122,77 +122,80 @@ void Channel3::updateWaveCounter(unsigned long const cc) { lastReadTime_ = waveCounter_ + periods * period; waveCounter_ = lastReadTime_ + period; - - wavePos_ += periods + 1; - wavePos_ &= 0x1F; - - sampleBuf_ = waveRam_[wavePos_ >> 1]; + wavePos_ = (wavePos_ + periods + 1) % (2 * sizeof waveRam_); + sampleBuf_ = waveRam_[wavePos_ / 2]; } } -void Channel3::update(uint_least32_t *buf, unsigned long const soBaseVol, unsigned long cycles) { - unsigned long const outBase = nr0_/* & 0x80*/ ? soBaseVol & soMask_ : 0; +void Channel3::update(uint_least32_t* buf, unsigned long const soBaseVol, unsigned long cc, unsigned long const end) { + unsigned long const outBase = nr0_ ? soBaseVol & soMask_ : 0; if (outBase && rshift_ != 4) { - unsigned long const endCycles = cycleCounter_ + cycles; - - for (;;) { + while (std::min(waveCounter_, lengthCounter_.counter()) <= end) { + unsigned pos = wavePos_; + unsigned const period = toPeriod(nr3_, nr4_), rsh = rshift_; unsigned long const nextMajorEvent = - std::min(lengthCounter_.counter(), endCycles); + std::min(lengthCounter_.counter(), end); + unsigned long cnt = waveCounter_, prevOut = prevOut_; unsigned long out = master_ - ? ((sampleBuf_ >> (~wavePos_ << 2 & 4) & 0xF) >> rshift_) * 2 - 15ul - : 0 - 15ul; + ? ((pos % 2 ? sampleBuf_ & 0xF : sampleBuf_ >> 4) >> rsh) * 2l - 15 + : -15; out *= outBase; - - 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 = ((sampleBuf_ >> (~wavePos_ << 2 & 4) & 0xF) >> rshift_) * 2 - 15ul; + while (cnt <= nextMajorEvent) { + *buf += out - prevOut; + prevOut = out; + buf += cnt - cc; + cc = cnt; + cnt += period; + ++pos; + unsigned const s = waveRam_[pos / 2 % sizeof waveRam_]; + out = ((pos % 2 ? s & 0xF : s >> 4) >> rsh) * 2l - 15; out *= outBase; } - - if (cycleCounter_ < nextMajorEvent) { + if (cnt != waveCounter_) { + wavePos_ = pos % (2 * sizeof waveRam_); + sampleBuf_ = waveRam_[wavePos_ / 2]; + prevOut_ = prevOut; + waveCounter_ = cnt; + lastReadTime_ = cc; + } + if (cc < nextMajorEvent) { *buf += out - prevOut_; prevOut_ = out; - buf += nextMajorEvent - cycleCounter_; - cycleCounter_ = nextMajorEvent; + buf += nextMajorEvent - cc; + cc = nextMajorEvent; } - - if (lengthCounter_.counter() == nextMajorEvent) { + if (lengthCounter_.counter() == nextMajorEvent) lengthCounter_.event(); - } else - break; } - } else { - unsigned long const out = outBase * (0 - 15ul); + if (cc < end) { + unsigned long out = master_ + ? ((wavePos_ % 2 ? sampleBuf_ & 0xF : sampleBuf_ >> 4) >> rshift_) * 2l - 15 + : -15; + out *= outBase; + *buf += out - prevOut_; + prevOut_ = out; + cc = end; + } + } + else { + unsigned long const out = outBase * -15; *buf += out - prevOut_; prevOut_ = out; - cycleCounter_ += cycles; - - while (lengthCounter_.counter() <= cycleCounter_) { + cc = end; + while (lengthCounter_.counter() <= cc) { updateWaveCounter(lengthCounter_.counter()); lengthCounter_.event(); } - updateWaveCounter(cycleCounter_); + updateWaveCounter(cc); } - if (cycleCounter_ >= SoundUnit::counter_max) { - lengthCounter_.resetCounters(cycleCounter_); - + if (cc >= SoundUnit::counter_max) { + lengthCounter_.resetCounters(cc); + lastReadTime_ -= SoundUnit::counter_max; if (waveCounter_ != SoundUnit::counter_disabled) waveCounter_ -= SoundUnit::counter_max; - - lastReadTime_ -= SoundUnit::counter_max; - cycleCounter_ -= SoundUnit::counter_max; } } @@ -202,7 +205,6 @@ SYNCFUNC(Channel3) SSS(lengthCounter_); - NSS(cycleCounter_); NSS(soMask_); NSS(prevOut_); NSS(waveCounter_); @@ -218,5 +220,3 @@ SYNCFUNC(Channel3) NSS(master_); NSS(cgb_); } - -} diff --git a/libgambatte/src/sound/channel3.h b/libgambatte/src/sound/channel3.h index fe2e43fd1b..a64ce4f5c3 100644 --- a/libgambatte/src/sound/channel3.h +++ b/libgambatte/src/sound/channel3.h @@ -32,35 +32,37 @@ class Channel3 { public: Channel3(); bool isActive() const { return master_; } + bool isCgb() const { return cgb_; } void reset(); + void resetCc(unsigned long cc, unsigned long newCc); void init(bool cgb); void setStatePtrs(SaveState &state); - void loadState(const SaveState &state); + void loadState(SaveState const &state); void setNr0(unsigned data); - void setNr1(unsigned data) { lengthCounter_.nr1Change(data, nr4_, cycleCounter_); } + void setNr1(unsigned data, unsigned long cc) { lengthCounter_.nr1Change(data, nr4_, cc); } void setNr2(unsigned data); void setNr3(unsigned data) { nr3_ = data; } - void setNr4(unsigned data); + void setNr4(unsigned data, unsigned long cc); void setSo(unsigned long soMask); - void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles); + void update(uint_least32_t* buf, unsigned long soBaseVol, unsigned long cc, unsigned long end); - unsigned waveRamRead(unsigned index) const { + unsigned waveRamRead(unsigned index, unsigned long cc) const { if (master_) { - if (!cgb_ && cycleCounter_ != lastReadTime_) + if (!cgb_ && cc != lastReadTime_) return 0xFF; - index = wavePos_ >> 1; + index = wavePos_ / 2; } return waveRam_[index]; } - void waveRamWrite(unsigned index, unsigned data) { + void waveRamWrite(unsigned index, unsigned data, unsigned long cc) { if (master_) { - if (!cgb_ && cycleCounter_ != lastReadTime_) + if (!cgb_ && cc != lastReadTime_) return; - index = wavePos_ >> 1; + index = wavePos_ / 2; } waveRam_[index] = data; @@ -83,7 +85,6 @@ private: unsigned char waveRam_[0x10]; Ch3MasterDisabler disableMaster_; LengthCounter lengthCounter_; - unsigned long cycleCounter_; unsigned long soMask_; unsigned long prevOut_; unsigned long waveCounter_; diff --git a/libgambatte/src/sound/channel4.cpp b/libgambatte/src/sound/channel4.cpp index 9528ceb943..17c7fde5a1 100644 --- a/libgambatte/src/sound/channel4.cpp +++ b/libgambatte/src/sound/channel4.cpp @@ -17,23 +17,26 @@ // #include "channel4.h" +#include "psgdef.h" #include "../savestate.h" #include -static unsigned long toPeriod(unsigned const nr3) { - unsigned s = (nr3 >> 4) + 3; - unsigned r = nr3 & 7; +using namespace gambatte; - if (!r) { - r = 1; - --s; +namespace { + static unsigned long toPeriod(unsigned const nr3) { + unsigned s = nr3 / (1u * psg_nr43_s & -psg_nr43_s) + 3; + unsigned r = nr3 & psg_nr43_r; + + if (!r) { + r = 1; + --s; + } + + return r << s; } - - return r << s; } -namespace gambatte { - Channel4::Lfsr::Lfsr() : backupCounter_(counter_disabled) , reg_(0x7FFF) @@ -48,17 +51,18 @@ void Channel4::Lfsr::updateBackupCounter(unsigned long const cc) { unsigned long periods = (cc - backupCounter_) / period + 1; backupCounter_ += periods * period; - if (master_ && nr3_ < 0xE0) { - if (nr3_ & 8) { + if (master_ && nr3_ < 0xE * (1u * psg_nr43_s & -psg_nr43_s)) { + if (nr3_ & psg_nr43_7biten) { while (periods > 6) { unsigned const xored = (reg_ << 1 ^ reg_) & 0x7E; - reg_ = (reg_ >> 6 & ~0x7E) | xored | xored << 8; + reg_ = (reg_ >> 6 & ~0x7Eu) | xored | xored << 8; periods -= 6; } unsigned const xored = ((reg_ ^ reg_ >> 1) << (7 - periods)) & 0x7F; - reg_ = (reg_ >> periods & ~(0x80 - (0x80 >> periods))) | xored | xored << 8; - } else { + reg_ = (reg_ >> periods & ~(0x80u - (0x80 >> periods))) | xored | xored << 8; + } + else { while (periods > 15) { reg_ = reg_ ^ reg_ >> 1; periods -= 15; @@ -76,13 +80,13 @@ void Channel4::Lfsr::reviveCounter(unsigned long cc) { } inline void Channel4::Lfsr::event() { - if (nr3_ < 0xE0) { + if (nr3_ < 0xE * (1u * psg_nr43_s & -psg_nr43_s)) { unsigned const shifted = reg_ >> 1; unsigned const xored = (reg_ ^ shifted) & 1; reg_ = shifted | xored << 14; - if (nr3_ & 8) - reg_ = (reg_ & ~0x40) | xored << 6; + if (nr3_ & psg_nr43_7biten) + reg_ = (reg_ & ~0x40u) | xored << 6; } counter_ += toPeriod(nr3_); @@ -92,6 +96,7 @@ inline void Channel4::Lfsr::event() { void Channel4::Lfsr::nr3Change(unsigned newNr3, unsigned long cc) { updateBackupCounter(cc); nr3_ = newNr3; + counter_ = cc; } void Channel4::Lfsr::nr4Init(unsigned long cc) { @@ -108,6 +113,13 @@ void Channel4::Lfsr::reset(unsigned long cc) { backupCounter_ = cc + toPeriod(nr3_); } +void Channel4::Lfsr::resetCc(unsigned long cc, unsigned long newCc) { + updateBackupCounter(cc); + backupCounter_ -= cc - newCc; + if (counter_ != counter_disabled) + counter_ -= cc - newCc; +} + void Channel4::Lfsr::resetCounters(unsigned long oldCc) { updateBackupCounter(oldCc); backupCounter_ -= counter_max; @@ -137,7 +149,6 @@ Channel4::Channel4() , lengthCounter_(disableMaster_, 0x3F) , envelopeUnit_(staticOutputTest_) , nextEventUnit_(0) -, cycleCounter_(0) , soMask_(0) , prevOut_(0) , nr4_(0) @@ -152,49 +163,44 @@ void Channel4::setEvent() { nextEventUnit_ = &lengthCounter_; } -void Channel4::setNr1(unsigned data) { - lengthCounter_.nr1Change(data, nr4_, cycleCounter_); +void Channel4::setNr1(unsigned data, unsigned long cc) { + lengthCounter_.nr1Change(data, nr4_, cc); setEvent(); } -void Channel4::setNr2(unsigned data) { +void Channel4::setNr2(unsigned data, unsigned long cc) { if (envelopeUnit_.nr2Change(data)) disableMaster_(); else - staticOutputTest_(cycleCounter_); + staticOutputTest_(cc); setEvent(); } -void Channel4::setNr4(unsigned const data) { - lengthCounter_.nr4Change(nr4_, data, cycleCounter_); +void Channel4::setNr4(unsigned const data, unsigned long const cc) { + lengthCounter_.nr4Change(nr4_, data, cc); nr4_ = data; - if (data & 0x80) { // init-bit - nr4_ &= 0x7F; - master_ = !envelopeUnit_.nr4Init(cycleCounter_); - + if (nr4_ & psg_nr4_init) { + nr4_ -= psg_nr4_init; + master_ = !envelopeUnit_.nr4Init(cc); if (master_) - lfsr_.nr4Init(cycleCounter_); + lfsr_.nr4Init(cc); - staticOutputTest_(cycleCounter_); + staticOutputTest_(cc); } setEvent(); } -void Channel4::setSo(unsigned long soMask) { +void Channel4::setSo(unsigned long soMask, unsigned long cc) { soMask_ = soMask; - staticOutputTest_(cycleCounter_); + staticOutputTest_(cc); setEvent(); } -void Channel4::reset() { - // cycleCounter >> 12 & 7 represents the frame sequencer position. - cycleCounter_ &= 0xFFF; - cycleCounter_ += ~(cycleCounter_ + 2) << 1 & 0x1000; - - lfsr_.reset(cycleCounter_); +void Channel4::reset(unsigned long cc) { + lfsr_.reset(cc); envelopeUnit_.reset(); setEvent(); } @@ -202,53 +208,49 @@ void Channel4::reset() { void Channel4::loadState(SaveState const &state) { lfsr_.loadState(state); envelopeUnit_.loadState(state.spu.ch4.env, state.mem.ioamhram.get()[0x121], - state.spu.cycleCounter); + 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, unsigned long const soBaseVol, unsigned long cycles) { +void Channel4::update(uint_least32_t* buf, unsigned long const soBaseVol, unsigned long cc, unsigned long const end) { unsigned long const outBase = envelopeUnit_.dacIsOn() ? soBaseVol & soMask_ : 0; - unsigned long const outLow = outBase * (0 - 15ul); - unsigned long const endCycles = cycleCounter_ + cycles; + unsigned long const outLow = outBase * -15; - for (;;) { - unsigned long const outHigh = outBase * (envelopeUnit_.getVolume() * 2 - 15ul); - unsigned long const nextMajorEvent = std::min(nextEventUnit_->counter(), endCycles); + while (cc < end) { + unsigned long const outHigh = outBase * (envelopeUnit_.getVolume() * 2l - 15); + unsigned long const nextMajorEvent = std::min(nextEventUnit_->counter(), end); unsigned long out = lfsr_.isHighState() ? outHigh : outLow; - - while (lfsr_.counter() <= nextMajorEvent) { + if (lfsr_.counter() <= nextMajorEvent) { + Lfsr lfsr = lfsr_; + while (lfsr.counter() <= nextMajorEvent) { + *buf += out - prevOut_; + prevOut_ = out; + buf += lfsr.counter() - cc; + cc = lfsr.counter(); + lfsr.event(); + out = lfsr.isHighState() ? outHigh : outLow; + } + lfsr_ = lfsr; + } + if (cc < nextMajorEvent) { *buf += out - prevOut_; prevOut_ = out; - buf += lfsr_.counter() - cycleCounter_; - cycleCounter_ = lfsr_.counter(); - - lfsr_.event(); - out = lfsr_.isHighState() ? outHigh : outLow; + buf += nextMajorEvent - cc; + cc = nextMajorEvent; } - - if (cycleCounter_ < nextMajorEvent) { - *buf += out - prevOut_; - prevOut_ = out; - buf += nextMajorEvent - cycleCounter_; - cycleCounter_ = nextMajorEvent; - } - 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 (cc >= SoundUnit::counter_max) { + lengthCounter_.resetCounters(cc); + lfsr_.resetCounters(cc); + envelopeUnit_.resetCounters(cc); } } @@ -264,12 +266,9 @@ SYNCFUNC(Channel4) EVS(nextEventUnit_, &lengthCounter_, 3); EES(nextEventUnit_, NULL); - 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 0ad159181f..629ba11b92 100644 --- a/libgambatte/src/sound/channel4.h +++ b/libgambatte/src/sound/channel4.h @@ -33,14 +33,15 @@ struct SaveState; class Channel4 { public: Channel4(); - void setNr1(unsigned data); - void setNr2(unsigned data); - void setNr3(unsigned data) { lfsr_.nr3Change(data, cycleCounter_); } - void setNr4(unsigned data); - void setSo(unsigned long soMask); + void setNr1(unsigned data, unsigned long cc); + void setNr2(unsigned data, unsigned long cc); + void setNr3(unsigned data, unsigned long cc) { lfsr_.nr3Change(data, cc); } + void setNr4(unsigned data, unsigned long cc); + void setSo(unsigned long soMask, unsigned long cc); bool isActive() const { return master_; } - void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles); - void reset(); + void update(uint_least32_t* buf, unsigned long soBaseVol, unsigned long cc, unsigned long end); + void reset(unsigned long cc); + void resetCc(unsigned long cc, unsigned long newCc) { lfsr_.resetCc(cc, newCc); } void loadState(SaveState const &state); private: @@ -53,6 +54,7 @@ private: void nr3Change(unsigned newNr3, unsigned long cc); void nr4Init(unsigned long cc); void reset(unsigned long cc); + void resetCc(unsigned long cc, unsigned long newCc); void loadState(SaveState const &state); void disableMaster() { killCounter(); master_ = false; reg_ = 0x7FFF; } void killCounter() { counter_ = counter_disabled; } @@ -87,7 +89,6 @@ private: EnvelopeUnit envelopeUnit_; Lfsr lfsr_; SoundUnit *nextEventUnit_; - unsigned long cycleCounter_; unsigned long soMask_; unsigned long prevOut_; unsigned char nr4_; diff --git a/libgambatte/src/sound/duty_unit.cpp b/libgambatte/src/sound/duty_unit.cpp index 5f9f484742..2c91fa10d3 100644 --- a/libgambatte/src/sound/duty_unit.cpp +++ b/libgambatte/src/sound/duty_unit.cpp @@ -17,17 +17,24 @@ // #include "duty_unit.h" +#include "psgdef.h" #include -static inline bool toOutState(unsigned duty, unsigned pos) { - return 0x7EE18180 >> (duty * 8 + pos) & 1; +namespace { + + int const duty_pattern_len = 8; + + bool toOutState(unsigned duty, unsigned pos) { + return 0x7EE18180 >> (duty * duty_pattern_len + pos) & 1; + } + + unsigned toPeriod(unsigned freq) { + return (2048 - freq) * 2; + } + } -static inline unsigned toPeriod(unsigned freq) { - return (2048 - freq) * 2; -} - -namespace gambatte { +using namespace gambatte; DutyUnit::DutyUnit() : nextPosUpdate_(counter_disabled) @@ -44,27 +51,26 @@ 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; + pos_ = (pos_ + inc) % duty_pattern_len; high_ = toOutState(duty_, pos_); } } void DutyUnit::setCounter() { - 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 + static unsigned char const nextStateDistance[][duty_pattern_len] = { + { 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) { - unsigned const npos = (pos_ + 1) & 7; + unsigned const npos = (pos_ + 1) % duty_pattern_len; counter_ = nextPosUpdate_; - inc_ = nextStateDistance[duty_ * 8 + npos]; + inc_ = nextStateDistance[duty_][npos]; if (toOutState(duty_, npos) == high_) { counter_ += period_ * inc_; - inc_ = nextStateDistance[duty_ * 8 + ((npos + inc_) & 7)]; + inc_ = nextStateDistance[duty_][(npos + inc_) % duty_pattern_len]; } } else counter_ = counter_disabled; @@ -77,16 +83,16 @@ void DutyUnit::setFreq(unsigned newFreq, unsigned long cc) { } void DutyUnit::event() { - static unsigned char const inc[] = { - 1, 7, - 2, 6, - 4, 4, - 6, 2, + static unsigned char const inc[][2] = { + { 1, 7 }, + { 2, 6 }, + { 4, 4 }, + { 6, 2 } }; high_ ^= true; counter_ += inc_ * period_; - inc_ = inc[duty_ * 2 + high_]; + inc_ = inc[duty_][high_]; } void DutyUnit::nr1Change(unsigned newNr1, unsigned long cc) { @@ -99,12 +105,11 @@ void DutyUnit::nr3Change(unsigned newNr3, unsigned long cc) { setFreq((freq() & 0x700) | newNr3, cc); } -void DutyUnit::nr4Change(unsigned const newNr4, unsigned long const cc, - bool const master) { +void DutyUnit::nr4Change(unsigned const newNr4, unsigned long const cc, unsigned long const ref, bool const master) { setFreq((newNr4 << 8 & 0x700) | (freq() & 0xFF), cc); - if (newNr4 & 0x80) { - nextPosUpdate_ = (cc & ~1ul) + period_ + 4 - (master << 1); + if (newNr4 & psg_nr4_init) { + nextPosUpdate_ = cc - (cc - ref) % 2 + period_ + 4 - (master << 1); setCounter(); } } @@ -116,6 +121,15 @@ void DutyUnit::reset() { setCounter(); } +void DutyUnit::resetCc(unsigned long cc, unsigned long newCc) { + if (nextPosUpdate_ == counter_disabled) + return; + + updatePos(cc); + nextPosUpdate_ -= cc - newCc; + setCounter(); +} + void DutyUnit::loadState(SaveState::SPU::Duty const &dstate, unsigned const nr1, unsigned const nr4, unsigned long const cc) { nextPosUpdate_ = std::max(dstate.nextPosUpdate, cc); @@ -127,13 +141,8 @@ void DutyUnit::loadState(SaveState::SPU::Duty const &dstate, setCounter(); } -void DutyUnit::resetCounters(unsigned long const oldCc) { - if (nextPosUpdate_ == counter_disabled) - return; - - updatePos(oldCc); - nextPosUpdate_ -= counter_max; - setCounter(); +void DutyUnit::resetCounters(unsigned long cc) { + resetCc(cc, cc - counter_max); } void DutyUnit::killCounter() { @@ -158,5 +167,3 @@ SYNCFUNC(DutyUnit) NSS(high_); NSS(enableEvents_); } - -} diff --git a/libgambatte/src/sound/duty_unit.h b/libgambatte/src/sound/duty_unit.h index 0f96140c9e..902beaad74 100644 --- a/libgambatte/src/sound/duty_unit.h +++ b/libgambatte/src/sound/duty_unit.h @@ -34,8 +34,9 @@ public: 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 nr4Change(unsigned newNr4, unsigned long cc, unsigned long ref, bool master); void reset(); + void resetCc(unsigned long cc, unsigned long newCc); void loadState(SaveState::SPU::Duty const &dstate, unsigned nr1, unsigned nr4, unsigned long cc); void killCounter(); void reviveCounter(unsigned long cc); diff --git a/libgambatte/src/sound/envelope_unit.cpp b/libgambatte/src/sound/envelope_unit.cpp index 7e9a81bb81..02369c0a41 100644 --- a/libgambatte/src/sound/envelope_unit.cpp +++ b/libgambatte/src/sound/envelope_unit.cpp @@ -17,9 +17,10 @@ // #include "envelope_unit.h" +#include "psgdef.h" #include -namespace gambatte { +using namespace gambatte; EnvelopeUnit::VolOnOffEvent EnvelopeUnit::nullEvent_; @@ -41,51 +42,53 @@ void EnvelopeUnit::loadState(SaveState::SPU::Env const &estate, unsigned nr2, un } void EnvelopeUnit::event() { - unsigned long const period = nr2_ & 7; + unsigned long const period = nr2_ & psg_nr2_step; if (period) { unsigned newVol = volume_; - if (nr2_ & 8) + if (nr2_ & psg_nr2_inc) ++newVol; else --newVol; - if (newVol < 0x10U) { + if (newVol < 0x10) { volume_ = newVol; if (volume_ < 2) volOnOffEvent_(counter_); counter_ += period << 15; - } else + } + else counter_ = counter_disabled; - } else + } + else counter_ += 8ul << 15; } bool EnvelopeUnit::nr2Change(unsigned const newNr2) { - if (!(nr2_ & 7) && counter_ != counter_disabled) + if (!(nr2_ & psg_nr2_step) && counter_ != counter_disabled) ++volume_; - else if (!(nr2_ & 8)) + else if (!(nr2_ & psg_nr2_inc)) volume_ += 2; - if ((nr2_ ^ newNr2) & 8) + if ((nr2_ ^ newNr2) & psg_nr2_inc) volume_ = 0x10 - volume_; volume_ &= 0xF; nr2_ = newNr2; - return !(newNr2 & 0xF8); + return !(newNr2 & (psg_nr2_initvol | psg_nr2_inc)); } bool EnvelopeUnit::nr4Init(unsigned long const cc) { - unsigned long period = nr2_ & 7 ? nr2_ & 7 : 8; + unsigned long period = nr2_ & psg_nr2_step ? nr2_ & psg_nr2_step : 8; if (((cc + 2) & 0x7000) == 0x0000) ++period; counter_ = cc - ((cc - 0x1000) & 0x7FFF) + period * 0x8000; - volume_ = nr2_ >> 4; - return !(nr2_ & 0xF8); + volume_ = nr2_ / (1u * psg_nr2_initvol & -psg_nr2_initvol); + return !(nr2_ & (psg_nr2_initvol | psg_nr2_inc)); } SYNCFUNC(EnvelopeUnit) @@ -94,5 +97,3 @@ SYNCFUNC(EnvelopeUnit) NSS(nr2_); NSS(volume_); } - -} diff --git a/libgambatte/src/sound/length_counter.cpp b/libgambatte/src/sound/length_counter.cpp index cdfd4c18bb..50ac6310e9 100644 --- a/libgambatte/src/sound/length_counter.cpp +++ b/libgambatte/src/sound/length_counter.cpp @@ -18,9 +18,10 @@ #include "length_counter.h" #include "master_disabler.h" +#include "psgdef.h" #include -namespace gambatte { +using namespace gambatte; LengthCounter::LengthCounter(MasterDisabler &disabler, unsigned const mask) : disableMaster_(disabler) @@ -38,35 +39,30 @@ void LengthCounter::event() { 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); + counter_ = nr4 & psg_nr4_lcen + ? ((cc >> 13) + lengthCounter_) << 13 + : 1 * counter_disabled; } 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 = ~cc >> 12 & 1; - - if (!(oldNr4 & 0x40) && lengthCounter_) { - if (!(lengthCounter_ -= dec)) - disableMaster_(); - } + unsigned dec = 0; + if (newNr4 & psg_nr4_lcen) { + dec = ~cc >> 12 & 1; + if (!(oldNr4 & psg_nr4_lcen) && lengthCounter_) { + if (!(lengthCounter_ -= dec)) + disableMaster_(); } - - if ((newNr4 & 0x80) && !lengthCounter_) - lengthCounter_ = lengthMask_ + 1 - dec; } - if ((newNr4 & 0x40) && lengthCounter_) - counter_ = ((cc >> 13) + lengthCounter_) << 13; - else - counter_ = counter_disabled; + if (newNr4 & psg_nr4_init && !lengthCounter_) + lengthCounter_ = lengthMask_ + 1 - dec; + + counter_ = newNr4 & psg_nr4_lcen && lengthCounter_ + ? ((cc >> 13) + lengthCounter_) << 13 + : 1 * counter_disabled; } void LengthCounter::loadState(SaveState::SPU::LCounter const &lstate, unsigned long const cc) { @@ -79,5 +75,3 @@ SYNCFUNC(LengthCounter) NSS(counter_); NSS(lengthCounter_); } - -} diff --git a/libgambatte/src/sound/psgdef.h b/libgambatte/src/sound/psgdef.h new file mode 100644 index 0000000000..e0aa66eb32 --- /dev/null +++ b/libgambatte/src/sound/psgdef.h @@ -0,0 +1,31 @@ +#ifndef PSGDEF_H +#define PSGDEF_H + +namespace gambatte { + + enum { + psg_nr10_rsh = 0x07, + psg_nr10_neg = 0x08, + psg_nr10_time = 0x70 + }; + + enum { + psg_nr2_step = 0x07, + psg_nr2_inc = 0x08, + psg_nr2_initvol = 0xF0 + }; + + enum { + psg_nr43_r = 0x07, + psg_nr43_7biten = 0x08, + psg_nr43_s = 0xF0 + }; + + enum { + psg_nr4_lcen = 0x40, + psg_nr4_init = 0x80 + }; + +} + +#endif \ No newline at end of file diff --git a/libgambatte/src/tima.cpp b/libgambatte/src/tima.cpp index 5b935c8a9c..854d247367 100644 --- a/libgambatte/src/tima.cpp +++ b/libgambatte/src/tima.cpp @@ -19,12 +19,17 @@ #include "tima.h" #include "savestate.h" -static unsigned char const timaClock[4] = { 10, 4, 6, 8 }; +using namespace gambatte; + +namespace { + unsigned char const timaClock[4] = { 10, 4, 6, 8 }; +} namespace gambatte { Tima::Tima() -: lastUpdate_(0) +: divLastUpdate_(0) +, lastUpdate_(0) , tmatime_(disabled_time) , tima_(0) , tma_(0) @@ -33,7 +38,7 @@ Tima::Tima() } void Tima::loadState(SaveState const &state, TimaInterruptRequester timaIrq) { - basetime_ = state.mem.timaBasetime; + divLastUpdate_ = state.mem.divLastUpdate - 0x100l * state.mem.ioamhram.get()[0x104]; lastUpdate_ = state.mem.timaLastUpdate; tmatime_ = state.mem.tmatime; tima_ = state.mem.ioamhram.get()[0x105]; @@ -43,8 +48,8 @@ void Tima::loadState(SaveState const &state, TimaInterruptRequester timaIrq) { unsigned long nextIrqEventTime = disabled_time; if (tac_ & 4) { nextIrqEventTime = tmatime_ != disabled_time && tmatime_ > state.cpu.cycleCounter - ? tmatime_ - : lastUpdate_ + ((256u - tima_) << timaClock[tac_ & 3]) + 3; + ? tmatime_ + : lastUpdate_ + ((256l - tima_) << timaClock[tac_ & 3]) + 3; } timaIrq.setNextIrqEventTime(nextIrqEventTime); @@ -82,7 +87,6 @@ void Tima::updateTima(unsigned long const cc) { if (tmp == 0x100) { tmp = 0; tmatime_ = lastUpdate_ + 3; - if (cc >= tmatime_) { if (cc >= tmatime_ + 4) tmatime_ = disabled_time; @@ -102,7 +106,7 @@ void Tima::setTima(unsigned const data, unsigned long const cc, TimaInterruptReq if (tmatime_ - cc < 4) tmatime_ = disabled_time; - timaIrq.setNextIrqEventTime(lastUpdate_ + ((256u - data) << timaClock[tac_ & 3]) + 3); + timaIrq.setNextIrqEventTime(lastUpdate_ + ((256l - data) << timaClock[tac_ & 3]) + 3); } tima_ = data; @@ -122,32 +126,26 @@ void Tima::setTac(unsigned const data, unsigned long const cc, TimaInterruptRequ unsigned long nextIrqEventTime = timaIrq.nextIrqEventTime(); if (tac_ & 0x04) { - 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; - + unsigned const inc = ~(data >> 2 & (cc - divLastUpdate_) >> (timaClock[data & 3] - 1)) & 1; + lastUpdate_ -= (inc << (timaClock[tac_ & 3] - 1)) + 3; + nextIrqEventTime -= (inc << (timaClock[tac_ & 3] - 1)) + 3; if (cc >= nextIrqEventTime) timaIrq.flagIrq(); updateTima(cc); - tmatime_ = disabled_time; nextIrqEventTime = disabled_time; } if (data & 4) { - unsigned long diff = cc - basetime_; - if (agbFlag) { + unsigned long diff = cc - divLastUpdate_; 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; + lastUpdate_ = cc - ((cc - divLastUpdate_) & ((1u << timaClock[data & 3]) - 1)); + nextIrqEventTime = lastUpdate_ + ((256l - tima_) << timaClock[data & 3]) + 3; } timaIrq.setNextIrqEventTime(nextIrqEventTime); @@ -156,12 +154,26 @@ void Tima::setTac(unsigned const data, unsigned long const cc, TimaInterruptRequ tac_ = data; } -void Tima::resTac(unsigned long const cc, TimaInterruptRequester timaIrq) { - basetime_ = cc; - +void Tima::divReset(unsigned long cc, TimaInterruptRequester timaIrq) { if (tac_ & 0x04) { - setTac(tac_ & ~0x04, cc, timaIrq, false); - setTac(tac_ | 0x04, cc, timaIrq, false); + unsigned long nextIrqEventTime = timaIrq.nextIrqEventTime(); + lastUpdate_ -= (1u << (timaClock[tac_ & 3] - 1)) + 3; + nextIrqEventTime -= (1u << (timaClock[tac_ & 3] - 1)) + 3; + if (cc >= nextIrqEventTime) + timaIrq.flagIrq(); + + updateTima(cc); + lastUpdate_ = cc; + timaIrq.setNextIrqEventTime(lastUpdate_ + ((256l - tima_) << timaClock[tac_ & 3]) + 3); + } + + divLastUpdate_ = cc; +} + +void Tima::speedChange(TimaInterruptRequester timaIrq) { + if ((tac_ & 0x07) >= 0x05) { + lastUpdate_ -= 4; + timaIrq.setNextIrqEventTime(timaIrq.nextIrqEventTime() - 4); } } @@ -173,15 +185,15 @@ unsigned Tima::tima(unsigned long cc) { } void Tima::doIrqEvent(TimaInterruptRequester timaIrq) { - timaIrq.flagIrq(); + timaIrq.flagIrq(timaIrq.nextIrqEventTime()); timaIrq.setNextIrqEventTime(timaIrq.nextIrqEventTime() - + ((256u - tma_) << timaClock[tac_ & 3])); + + ((256l - tma_) << timaClock[tac_ & 3])); } SYNCFUNC(Tima) { NSS(lastUpdate_); - NSS(basetime_); + NSS(divLastUpdate_); NSS(tmatime_); NSS(tima_); NSS(tma_); diff --git a/libgambatte/src/tima.h b/libgambatte/src/tima.h index d1db0854b9..8079dcccab 100644 --- a/libgambatte/src/tima.h +++ b/libgambatte/src/tima.h @@ -27,6 +27,7 @@ class TimaInterruptRequester { public: explicit TimaInterruptRequester(InterruptRequester &intreq) : intreq_(intreq) {} void flagIrq() const { intreq_.flagIrq(4); } + void flagIrq(unsigned long cc) const { intreq_.flagIrq(4, cc); } unsigned long nextIrqEventTime() const { return intreq_.eventTime(intevent_tima); } void setNextIrqEventTime(unsigned long time) const { intreq_.setEventTime(time); } @@ -42,12 +43,14 @@ public: 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 agbFlag); - void resTac(unsigned long cc, TimaInterruptRequester timaIrq); + void divReset(unsigned long cc, TimaInterruptRequester); + void speedChange(TimaInterruptRequester); + unsigned long divLastUpdate() const { return divLastUpdate_; } unsigned tima(unsigned long cc); void doIrqEvent(TimaInterruptRequester timaIrq); private: - unsigned long basetime_; + unsigned long divLastUpdate_; unsigned long lastUpdate_; unsigned long tmatime_; unsigned char tima_; diff --git a/libgambatte/src/video.cpp b/libgambatte/src/video.cpp index 997993c881..654c8bbd50 100644 --- a/libgambatte/src/video.cpp +++ b/libgambatte/src/video.cpp @@ -21,18 +21,58 @@ #include #include -namespace gambatte { +using namespace gambatte; unsigned long LCD::gbcToRgb32(const unsigned bgr15) { - unsigned long const r = bgr15 & 0x1F; - unsigned long const g = bgr15 >> 5 & 0x1F; + unsigned long const r = bgr15 & 0x1F; + unsigned long const g = bgr15 >> 5 & 0x1F; unsigned long const b = bgr15 >> 10 & 0x1F; return cgbColorsRgb32_[bgr15 & 0x7FFF]; } +namespace { + + // TODO: simplify cycle offsets. + + long const mode1_irq_frame_cycle = 1l * lcd_vres * lcd_cycles_per_line - 2; + int const mode2_irq_line_cycle = lcd_cycles_per_line - 4; + int const mode2_irq_line_cycle_ly0 = lcd_cycles_per_line - 2; + + unsigned long mode1IrqSchedule(LyCounter const& lyCounter, unsigned long cc) { + return lyCounter.nextFrameCycle(mode1_irq_frame_cycle, cc); + } + + unsigned long mode2IrqSchedule(unsigned const statReg, + LyCounter const& lyCounter, unsigned long const cc) { + if (!(statReg & lcdstat_m2irqen)) + return disabled_time; + + unsigned long const lastM2Fc = (lcd_vres - 1l) * lcd_cycles_per_line + mode2_irq_line_cycle; + unsigned long const ly0M2Fc = (lcd_lines_per_frame - 1l) * lcd_cycles_per_line + mode2_irq_line_cycle_ly0; + return lyCounter.frameCycles(cc) - lastM2Fc < ly0M2Fc - lastM2Fc || (statReg & lcdstat_m0irqen) + ? lyCounter.nextFrameCycle(ly0M2Fc, cc) + : lyCounter.nextLineCycle(mode2_irq_line_cycle, cc); + } + + unsigned long m0TimeOfCurrentLine( + unsigned long nextLyTime, + unsigned long lastM0Time, + unsigned long nextM0Time) { + return nextM0Time < nextLyTime ? nextM0Time : lastM0Time; + } + + bool isHdmaPeriod(LyCounter const& lyCounter, + unsigned long m0TimeOfCurrentLy, unsigned long cc) { + return lyCounter.ly() < lcd_vres + && cc + 3 + 3 * lyCounter.isDoubleSpeed() < lyCounter.time() + && cc >= m0TimeOfCurrentLy; + } + +} // unnamed namespace. + void LCD::setDmgPalette(unsigned long palette[], const unsigned long dmgColors[], unsigned data) { - palette[0] = dmgColors[data & 3]; + palette[0] = dmgColors[data & 3]; palette[1] = dmgColors[data >> 2 & 3]; palette[2] = dmgColors[data >> 4 & 3]; palette[3] = dmgColors[data >> 6 & 3]; @@ -47,10 +87,10 @@ void LCD::setCgbPalette(unsigned *lut) { LCD::LCD(unsigned char const *oamram, unsigned char const *vram, VideoInterruptRequester memEventRequester) : ppu_(nextM0Time_, oamram, vram) +, bgpData_() +, objpData_() , eventTimes_(memEventRequester) , statReg_(0) -, m2IrqStatReg_(0) -, m1IrqStatReg_(0) , scanlinecallback(0) , scanlinecallbacksl(0) { @@ -60,7 +100,7 @@ LCD::LCD(unsigned char const *oamram, unsigned char const *vram, std::memset(objpData_, 0, sizeof objpData_); reset(oamram, vram, false); - setVideoBuffer(0, 160); + setVideoBuffer(0, lcd_hres); } void LCD::reset(unsigned char const *oamram, unsigned char const *vram, bool cgb) { @@ -69,44 +109,6 @@ void LCD::reset(unsigned char const *oamram, unsigned char const *vram, bool cgb refreshPalettes(); } -void LCD::setCgb(bool cgb) { - ppu_.setCgb(cgb); -} - -static unsigned long mode2IrqSchedule(unsigned const statReg, - LyCounter const &lyCounter, unsigned long const cc) { - if (!(statReg & lcdstat_m2irqen)) - return disabled_time; - - int next = lyCounter.time() - cc; - if (lyCounter.ly() >= 143 - || (lyCounter.ly() == 142 && next <= 4) - || (statReg & lcdstat_m0irqen)) { - next += (153u - lyCounter.ly()) * lyCounter.lineTime(); - } else { - next -= 4; - if (next <= 0) - next += lyCounter.lineTime(); - } - - return cc + next; -} - -static unsigned long m0IrqTimeFromXpos166Time(unsigned long xpos166Time, bool cgb, bool ds) { - return xpos166Time + cgb - ds; -} - -static unsigned long hdmaTimeFromM0Time(unsigned long m0Time, bool ds) { - return m0Time + 1 - ds; -} - -static unsigned long nextHdmaTime(unsigned long lastM0Time, - unsigned long nextM0Time, unsigned long cc, bool ds) { - return cc < hdmaTimeFromM0Time(lastM0Time, ds) - ? hdmaTimeFromM0Time(lastM0Time, ds) - : hdmaTimeFromM0Time(nextM0Time, ds); -} - void LCD::setStatePtrs(SaveState &state) { state.ppu.bgpData.set( bgpData_, sizeof bgpData_); state.ppu.objpData.set(objpData_, sizeof objpData_); @@ -115,40 +117,35 @@ void LCD::setStatePtrs(SaveState &state) { void LCD::loadState(SaveState const &state, unsigned char const *const oamram) { statReg_ = state.mem.ioamhram.get()[0x141]; - m2IrqStatReg_ = statReg_; - m1IrqStatReg_ = statReg_; ppu_.loadState(state, oamram); lycIrq_.loadState(state); - m0Irq_.loadState(state); + mstatIrq_.loadState(state); if (ppu_.lcdc() & lcdc_en) { nextM0Time_.predictNextM0Time(ppu_); lycIrq_.reschedule(ppu_.lyCounter(), ppu_.now()); - eventTimes_.setm( - state.ppu.pendingLcdstatIrq + eventTimes_.setm(state.ppu.pendingLcdstatIrq ? ppu_.now() + 1 - : static_cast(disabled_time)); + : 1 * disabled_time); eventTimes_.setm( - state.ppu.oldWy != state.mem.ioamhram.get()[0x14A] - ? ppu_.now() + 1 - : static_cast(disabled_time)); + state.ppu.oldWy != state.mem.ioamhram.get()[0x14A] + ? ppu_.now() + 2 - isDoubleSpeed() + : 1 * 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()) - 2); + eventTimes_.setm(mode1IrqSchedule(ppu_.lyCounter(), ppu_.now())); eventTimes_.setm( mode2IrqSchedule(statReg_, ppu_.lyCounter(), ppu_.now())); eventTimes_.setm(statReg_ & lcdstat_m0irqen ? ppu_.now() + state.ppu.nextM0Irq - : static_cast(disabled_time)); + : 1 * disabled_time); eventTimes_.setm(state.mem.hdmaTransfer - ? nextHdmaTime(ppu_.lastM0Time(), nextM0Time_.predictedNextM0Time(), - ppu_.now(), isDoubleSpeed()) - : static_cast(disabled_time)); + ? nextM0Time_.predictedNextM0Time() + : 1 * disabled_time); } else for (int i = 0; i < num_memevents; ++i) eventTimes_.set(MemEvent(i), disabled_time); @@ -156,10 +153,10 @@ void LCD::loadState(SaveState const &state, unsigned char const *const oamram) { } void LCD::refreshPalettes() { - if (ppu_.cgb()) { - for (unsigned i = 0; i < 8 * 8; i += 2) { - ppu_.bgPalette()[i >> 1] = gbcToRgb32( bgpData_[i] | bgpData_[i + 1] << 8); - ppu_.spPalette()[i >> 1] = gbcToRgb32(objpData_[i] | objpData_[i + 1] << 8); + if (isCgb() && !isCgbDmg()) { + for (int i = 0; i < max_num_palettes * num_palette_entries; ++i) { + ppu_.bgPalette()[i] = gbcToRgb32(bgpData_[2 * i] | bgpData_[2 * i + 1] * 0x100l); + ppu_.spPalette()[i] = gbcToRgb32(objpData_[2 * i] | objpData_[2 * i + 1] * 0x100l); } } else { setDmgPalette(ppu_.bgPalette() , dmgColorsRgb32_ , bgpData_[0]); @@ -180,7 +177,7 @@ void LCD::copyCgbPalettesToDmg() { namespace { template -static void clear(T *buf, unsigned long color, std::ptrdiff_t dpitch) { +void clear(T *buf, unsigned long color, std::ptrdiff_t dpitch) { unsigned lines = 144; while (lines--) { @@ -200,6 +197,12 @@ void LCD::updateScreen(bool const blanklcd, unsigned long const cycleCounter) { } } +void LCD::blackScreen() { + if (ppu_.frameBuf().fb()) { + clear(ppu_.frameBuf().fb(), gbcToRgb32(0x0000), ppu_.frameBuf().pitch()); + } +} + void LCD::resetCc(unsigned long const oldCc, unsigned long const newCc) { update(oldCc); ppu_.resetCc(oldCc, newCc); @@ -221,72 +224,45 @@ void LCD::resetCc(unsigned long const oldCc, unsigned long const newCc) { void LCD::speedChange(unsigned long const cc) { update(cc); - ppu_.speedChange(cc); + ppu_.speedChange(); if (ppu_.lcdc() & lcdc_en) { nextM0Time_.predictNextM0Time(ppu_); - lycIrq_.reschedule(ppu_.lyCounter(), cc); + lycIrq_.reschedule(ppu_.lyCounter(), ppu_.now()); eventTimes_.set(ppu_.lyCounter().time()); - eventTimes_.setm(SpriteMapper::schedule(ppu_.lyCounter(), cc)); + eventTimes_.setm(SpriteMapper::schedule(ppu_.lyCounter(), ppu_.now())); eventTimes_.setm(lycIrq_.time()); - eventTimes_.setm(ppu_.lyCounter().nextFrameCycle(144 * 456, cc) - 2); - eventTimes_.setm(mode2IrqSchedule(statReg_, ppu_.lyCounter(), cc)); + eventTimes_.setm(mode1IrqSchedule(ppu_.lyCounter(), ppu_.now())); + eventTimes_.setm(mode2IrqSchedule(statReg_, ppu_.lyCounter(), ppu_.now())); - if (eventTimes_(memevent_m0irq) != disabled_time - && eventTimes_(memevent_m0irq) - cc > 1) { - eventTimes_.setm(m0IrqTimeFromXpos166Time( - ppu_.predictedNextXposTime(166), ppu_.cgb(), isDoubleSpeed())); + if (eventTimes_(memevent_m0irq) != disabled_time) { + eventTimes_.setm(ppu_.predictedNextXposTime(lcd_hres + 6)); } - - if (hdmaIsEnabled() && eventTimes_(memevent_hdma) - cc > 1) { - eventTimes_.setm(nextHdmaTime(ppu_.lastM0Time(), - nextM0Time_.predictedNextM0Time(), cc, isDoubleSpeed())); + if (hdmaIsEnabled()) { + eventTimes_.setm(nextM0Time_.predictedNextM0Time()); } } } -static unsigned long m0TimeOfCurrentLine( - unsigned long nextLyTime, - unsigned long lastM0Time, - unsigned long nextM0Time) { - return nextM0Time < nextLyTime ? nextM0Time : lastM0Time; -} - unsigned long LCD::m0TimeOfCurrentLine(unsigned long const cc) { if (cc >= nextM0Time_.predictedNextM0Time()) { update(cc); nextM0Time_.predictNextM0Time(ppu_); } - return gambatte::m0TimeOfCurrentLine(ppu_.lyCounter().time(), ppu_.lastM0Time(), - nextM0Time_.predictedNextM0Time()); + return ::m0TimeOfCurrentLine(ppu_.lyCounter().time(), ppu_.lastM0Time(), + nextM0Time_.predictedNextM0Time()); } -static bool isHdmaPeriod(LyCounter const &lyCounter, - unsigned long m0TimeOfCurrentLy, unsigned long cc) { - int timeToNextLy = lyCounter.time() - cc; - return lyCounter.ly() < 144 && timeToNextLy > 4 - && cc >= hdmaTimeFromM0Time(m0TimeOfCurrentLy, lyCounter.isDoubleSpeed()); -} +void LCD::enableHdma(unsigned long const cc) { + if (cc >= eventTimes_.nextEventTime()) + update(cc); -void LCD::enableHdma(unsigned long const cycleCounter) { - if (cycleCounter >= nextM0Time_.predictedNextM0Time()) { - update(cycleCounter); - nextM0Time_.predictNextM0Time(ppu_); - } else if (cycleCounter >= eventTimes_.nextEventTime()) - update(cycleCounter); - - unsigned long const m0TimeCurLy = - gambatte::m0TimeOfCurrentLine(ppu_.lyCounter().time(), - ppu_.lastM0Time(), - nextM0Time_.predictedNextM0Time()); - if (isHdmaPeriod(ppu_.lyCounter(), m0TimeCurLy, cycleCounter)) + if (::isHdmaPeriod(ppu_.lyCounter(), m0TimeOfCurrentLine(cc), cc + 4)) eventTimes_.flagHdmaReq(); - eventTimes_.setm(nextHdmaTime( - ppu_.lastM0Time(), nextM0Time_.predictedNextM0Time(), - cycleCounter, isDoubleSpeed())); + eventTimes_.setm(nextM0Time_.predictedNextM0Time()); } void LCD::disableHdma(unsigned long const cycleCounter) { @@ -296,14 +272,33 @@ void LCD::disableHdma(unsigned long const cycleCounter) { eventTimes_.setm(disabled_time); } -bool LCD::vramAccessible(unsigned long const cc) { +bool LCD::isHdmaPeriod(unsigned long const cc) { + if (cc >= eventTimes_.nextEventTime()) + update(cc); + + return ::isHdmaPeriod(ppu_.lyCounter(), m0TimeOfCurrentLine(cc), cc); +} + +bool LCD::vramReadable(unsigned long const cc) { if (cc >= eventTimes_.nextEventTime()) update(cc); return !(ppu_.lcdc() & lcdc_en) - || ppu_.lyCounter().ly() >= 144 - || ppu_.lyCounter().lineCycles(cc) < 80U - || cc + isDoubleSpeed() - ppu_.cgb() + 2 >= m0TimeOfCurrentLine(cc); + || ppu_.lyCounter().ly() >= lcd_vres + || ppu_.inactivePeriodAfterDisplayEnable(cc + 1 - ppu_.cgb() + isDoubleSpeed()) + || ppu_.lyCounter().lineCycles(cc) + isDoubleSpeed() < 76u + 3 * ppu_.cgb() + || cc + 2 >= m0TimeOfCurrentLine(cc); +} + +bool LCD::vramWritable(unsigned long const cc) { + if (cc >= eventTimes_.nextEventTime()) + update(cc); + + return !(ppu_.lcdc() & lcdc_en) + || ppu_.lyCounter().ly() >= lcd_vres + || ppu_.inactivePeriodAfterDisplayEnable(cc + 1 - ppu_.cgb() + isDoubleSpeed()) + || ppu_.lyCounter().lineCycles(cc) + isDoubleSpeed() < 79 + || cc + 2 >= m0TimeOfCurrentLine(cc); } bool LCD::cgbpAccessible(unsigned long const cc) { @@ -311,9 +306,10 @@ bool LCD::cgbpAccessible(unsigned long const cc) { update(cc); return !(ppu_.lcdc() & lcdc_en) - || ppu_.lyCounter().ly() >= 144 - || ppu_.lyCounter().lineCycles(cc) < 80U + isDoubleSpeed() - || cc >= m0TimeOfCurrentLine(cc) + 3 - isDoubleSpeed(); + || ppu_.lyCounter().ly() >= lcd_vres + || ppu_.inactivePeriodAfterDisplayEnable(cc) + || ppu_.lyCounter().lineCycles(cc) + isDoubleSpeed() < 80 + || cc >= m0TimeOfCurrentLine(cc) + 2; } void LCD::doCgbColorChange(unsigned char *pdata, @@ -338,71 +334,67 @@ void LCD::doCgbSpColorChange(unsigned index, unsigned data, unsigned long cc) { } bool LCD::oamReadable(unsigned long const cc) { - if (!(ppu_.lcdc() & lcdc_en) || ppu_.inactivePeriodAfterDisplayEnable(cc)) + if (!(ppu_.lcdc() & lcdc_en) || ppu_.inactivePeriodAfterDisplayEnable(cc + 4)) return true; if (cc >= eventTimes_.nextEventTime()) update(cc); - if (ppu_.lyCounter().lineCycles(cc) + 4 - isDoubleSpeed() * 3u >= 456) - return ppu_.lyCounter().ly() >= 144-1 && ppu_.lyCounter().ly() != 153; + if (ppu_.lyCounter().lineCycles(cc) + 4 - isDoubleSpeed() >= lcd_cycles_per_line) + return ppu_.lyCounter().ly() >= lcd_vres - 1 && ppu_.lyCounter().ly() < lcd_lines_per_frame - 1; - return ppu_.lyCounter().ly() >= 144 - || cc + isDoubleSpeed() - ppu_.cgb() + 2 >= m0TimeOfCurrentLine(cc); + return ppu_.lyCounter().ly() >= lcd_vres || cc + 2 >= m0TimeOfCurrentLine(cc); } bool LCD::oamWritable(unsigned long const cc) { - if (!(ppu_.lcdc() & lcdc_en) || ppu_.inactivePeriodAfterDisplayEnable(cc)) + if (!(ppu_.lcdc() & lcdc_en) || ppu_.inactivePeriodAfterDisplayEnable(cc + 4 + isDoubleSpeed())) return true; if (cc >= eventTimes_.nextEventTime()) update(cc); - if (ppu_.lyCounter().lineCycles(cc) + 3 + ppu_.cgb() - isDoubleSpeed() * 2u >= 456) - return ppu_.lyCounter().ly() >= 144-1 && ppu_.lyCounter().ly() != 153; + if (ppu_.lyCounter().lineCycles(cc) + 3 + ppu_.cgb() >= lcd_cycles_per_line) + return ppu_.lyCounter().ly() >= lcd_vres - 1 && ppu_.lyCounter().ly() < lcd_lines_per_frame - 1; - return ppu_.lyCounter().ly() >= 144 - || cc + isDoubleSpeed() - ppu_.cgb() + 2 >= m0TimeOfCurrentLine(cc); + return ppu_.lyCounter().ly() >= lcd_vres || cc + 2 >= m0TimeOfCurrentLine(cc) + || (ppu_.lyCounter().lineCycles(cc) == 76 && !ppu_.cgb()); } void LCD::mode3CyclesChange() { - bool const ds = isDoubleSpeed(); nextM0Time_.invalidatePredictedNextM0Time(); if (eventTimes_(memevent_m0irq) != disabled_time - && eventTimes_(memevent_m0irq) - > m0IrqTimeFromXpos166Time(ppu_.now(), ppu_.cgb(), ds)) { - unsigned long t = m0IrqTimeFromXpos166Time(ppu_.predictedNextXposTime(166), - ppu_.cgb(), ds); + && eventTimes_(memevent_m0irq) > ppu_.now()) { + unsigned long t = ppu_.predictedNextXposTime(lcd_hres + 6); eventTimes_.setm(t); } if (eventTimes_(memevent_hdma) != disabled_time - && eventTimes_(memevent_hdma) > hdmaTimeFromM0Time(ppu_.lastM0Time(), ds)) { + && eventTimes_(memevent_hdma) > ppu_.lastM0Time()) { nextM0Time_.predictNextM0Time(ppu_); - eventTimes_.setm( - hdmaTimeFromM0Time(nextM0Time_.predictedNextM0Time(), ds)); + eventTimes_.setm(nextM0Time_.predictedNextM0Time()); } } void LCD::wxChange(unsigned newValue, unsigned long cycleCounter) { - update(cycleCounter + isDoubleSpeed() + 1); + update(cycleCounter + 1 + ppu_.cgb()); ppu_.setWx(newValue); mode3CyclesChange(); } void LCD::wyChange(unsigned const newValue, unsigned long const cc) { - update(cc + 1); - ppu_.setWy(newValue); + update(cc + 1 + ppu_.cgb()); + 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. + // wy2 is a delayed version of wy for convenience (is this really simpler?). if (ppu_.cgb() && (ppu_.lcdc() & lcdc_en)) { - eventTimes_.setm(cc + 5); - } else { + eventTimes_.setm(cc + 6 - isDoubleSpeed()); + } + else { update(cc + 2); ppu_.updateWy2(); mode3CyclesChange(); @@ -410,13 +402,13 @@ void LCD::wyChange(unsigned const newValue, unsigned long const cc) { } void LCD::scxChange(unsigned newScx, unsigned long cycleCounter) { - update(cycleCounter + ppu_.cgb() + isDoubleSpeed()); + update(cycleCounter + 2 * ppu_.cgb()); ppu_.setScx(newScx); mode3CyclesChange(); } void LCD::scyChange(unsigned newValue, unsigned long cycleCounter) { - update(cycleCounter + ppu_.cgb() + isDoubleSpeed()); + update(cycleCounter + 2 * ppu_.cgb()); ppu_.setScy(newValue); } @@ -438,18 +430,14 @@ void LCD::oamChange(unsigned char const *oamram, unsigned long cc) { void LCD::lcdcChange(unsigned const data, unsigned long const cc) { unsigned const oldLcdc = ppu_.lcdc(); - update(cc); if ((oldLcdc ^ data) & lcdc_en) { + update(cc); ppu_.setLcdc(data, cc); if (data & lcdc_en) { lycIrq_.lcdReset(); - m0Irq_.lcdReset(statReg_, lycIrq_.lycReg()); - - if (lycIrq_.lycReg() == 0 && (statReg_ & lcdstat_lycirqen)) - eventTimes_.flagIrq(2); - + mstatIrq_.lcdReset(lycIrq_.lycReg()); nextM0Time_.predictNextM0Time(ppu_); lycIrq_.reschedule(ppu_.lyCounter(), cc); @@ -457,48 +445,49 @@ void LCD::lcdcChange(unsigned const data, unsigned long const cc) { eventTimes_.setm( SpriteMapper::schedule(ppu_.lyCounter(), cc)); eventTimes_.setm(lycIrq_.time()); - eventTimes_.setm( - ppu_.lyCounter().nextFrameCycle(144 * 456, cc) - 2); + eventTimes_.setm(mode1IrqSchedule(ppu_.lyCounter(), cc)); eventTimes_.setm( mode2IrqSchedule(statReg_, ppu_.lyCounter(), cc)); if (statReg_ & lcdstat_m0irqen) { - eventTimes_.setm(m0IrqTimeFromXpos166Time( - ppu_.predictedNextXposTime(166), ppu_.cgb(), isDoubleSpeed())); + eventTimes_.setm(ppu_.predictedNextXposTime(lcd_hres + 6)); } if (hdmaIsEnabled()) { - eventTimes_.setm(nextHdmaTime(ppu_.lastM0Time(), - nextM0Time_.predictedNextM0Time(), cc, isDoubleSpeed())); + eventTimes_.setm(nextM0Time_.predictedNextM0Time()); } - } else for (int i = 0; i < num_memevents; ++i) + } + else for (int i = 0; i < num_memevents; ++i) eventTimes_.set(MemEvent(i), disabled_time); - } else if (data & lcdc_en) { + } + else if (data & lcdc_en) { if (ppu_.cgb()) { - ppu_.setLcdc( (oldLcdc & ~(lcdc_tdsel | lcdc_obj2x)) - | (data & (lcdc_tdsel | lcdc_obj2x)), cc); - + update(cc + 1); + ppu_.setLcdc((oldLcdc & ~(1u * lcdc_tdsel)) | (data & lcdc_tdsel), cc + 1); + update(cc + 2); + ppu_.setLcdc(data, cc + 2); if ((oldLcdc ^ data) & lcdc_obj2x) { - unsigned long t = SpriteMapper::schedule(ppu_.lyCounter(), cc); + unsigned long t = SpriteMapper::schedule(ppu_.lyCounter(), cc + 2); eventTimes_.setm(t); } - - update(cc + isDoubleSpeed() + 1); - ppu_.setLcdc(data, cc + isDoubleSpeed() + 1); - if ((oldLcdc ^ data) & lcdc_we) mode3CyclesChange(); - } else { - ppu_.setLcdc(data, cc); - + } + else { + update(cc); + ppu_.setLcdc((oldLcdc & lcdc_obj2x) | (data & ~(1u * lcdc_obj2x)), cc); if ((oldLcdc ^ data) & lcdc_obj2x) { - unsigned long t = SpriteMapper::schedule(ppu_.lyCounter(), cc); + update(cc + 2); + ppu_.setLcdc(data, cc + 2); + unsigned long t = SpriteMapper::schedule(ppu_.lyCounter(), cc + 2); eventTimes_.setm(t); } - if ((oldLcdc ^ data) & (lcdc_we | lcdc_objen)) mode3CyclesChange(); } - } else + } + else { + update(cc); ppu_.setLcdc(data, cc); + } } namespace { @@ -508,98 +497,116 @@ struct LyCnt { LyCnt(unsigned ly, int timeToNextLy) : ly(ly), timeToNextLy(timeToNextLy) {} }; -static LyCnt const getLycCmpLy(LyCounter const &lyCounter, unsigned long cc) { +LyCnt const getLycCmpLy(LyCounter const &lyCounter, unsigned long cc) { unsigned ly = lyCounter.ly(); int timeToNextLy = lyCounter.time() - cc; - if (ly == 153) { - if (timeToNextLy - (448 << lyCounter.isDoubleSpeed()) > 0) { - timeToNextLy -= (448 << lyCounter.isDoubleSpeed()); - } else { - ly = 0; - timeToNextLy += lyCounter.lineTime(); - } + if (ly == lcd_lines_per_frame - 1) { + int const lineTime = lyCounter.lineTime(); + if ((timeToNextLy -= (lineTime - 6 - 6 * lyCounter.isDoubleSpeed())) <= 0) + ly = 0, timeToNextLy += lineTime; } + else if ((timeToNextLy -= (2 + 2 * lyCounter.isDoubleSpeed())) <= 0) + ++ly, timeToNextLy += lyCounter.lineTime(); return LyCnt(ly, timeToNextLy); } -} // anon ns +bool statChangeTriggersM2IrqCgb(unsigned const old, + unsigned const data, int const ly, int const timeToNextLy, bool const ds) { + if ((old & lcdstat_m2irqen) + || (data & (lcdstat_m2irqen | lcdstat_m0irqen)) != lcdstat_m2irqen) { + return false; + } + if (ly < lcd_vres - 1) + return timeToNextLy <= (lcd_cycles_per_line - mode2_irq_line_cycle) * (1 + ds) && timeToNextLy > 2; + if (ly == lcd_vres - 1) + return timeToNextLy <= (lcd_cycles_per_line - mode2_irq_line_cycle) * (1 + ds) && timeToNextLy > 4 + 2 * ds; + if (ly == lcd_lines_per_frame - 1) + return timeToNextLy <= (lcd_cycles_per_line - mode2_irq_line_cycle_ly0) * (1 + ds) && timeToNextLy > 2; + return false; +} + +unsigned incLy(unsigned ly) { return ly == lcd_lines_per_frame - 1 ? 0 : ly + 1; } + +} // unnamed namespace. inline bool LCD::statChangeTriggersStatIrqDmg(unsigned const old, unsigned long const cc) { LyCnt const lycCmp = getLycCmpLy(ppu_.lyCounter(), cc); - if (ppu_.lyCounter().ly() < 144) { - if (cc + 1 < m0TimeOfCurrentLine(cc)) + if (ppu_.lyCounter().ly() < lcd_vres) { + int const m0_cycles_upper_bound = lcd_cycles_per_line - 80 - 160; + unsigned long m0IrqTime = eventTimes_(memevent_m0irq); + if (m0IrqTime == disabled_time && ppu_.lyCounter().time() - cc < m0_cycles_upper_bound) { + update(cc); + m0IrqTime = ppu_.predictedNextXposTime(lcd_hres + 6); + } + if (m0IrqTime == disabled_time || m0IrqTime < ppu_.lyCounter().time()) return lycCmp.ly == lycIrq_.lycReg() && !(old & lcdstat_lycirqen); return !(old & lcdstat_m0irqen) - && !(lycCmp.ly == lycIrq_.lycReg() && (old & lcdstat_lycirqen)); + && !(lycCmp.ly == lycIrq_.lycReg() && (old & lcdstat_lycirqen)); } return !(old & lcdstat_m1irqen) - && !(lycCmp.ly == lycIrq_.lycReg() && (old & lcdstat_lycirqen)); -} - -static bool statChangeTriggersM2IrqCgb(unsigned const old, - unsigned const data, unsigned const ly, int const timeToNextLy) { - if ((old & lcdstat_m2irqen) - || (data & (lcdstat_m2irqen | lcdstat_m0irqen)) != lcdstat_m2irqen - || ly >= 144) { - return false; - } - - return timeToNextLy == 456 * 2 - || (timeToNextLy <= 4 && ly < 143); + && !(lycCmp.ly == lycIrq_.lycReg() && (old & lcdstat_lycirqen)); } inline bool LCD::statChangeTriggersM0LycOrM1StatIrqCgb( - unsigned const old, unsigned const data, unsigned long const cc) { - unsigned const ly = ppu_.lyCounter().ly(); + unsigned const old, unsigned const data, bool const lycperiod, + unsigned long const cc) { + int const ly = ppu_.lyCounter().ly(); int const timeToNextLy = ppu_.lyCounter().time() - cc; - LyCnt const lycCmp = getLycCmpLy(ppu_.lyCounter(), cc); - bool const lycperiod = lycCmp.ly == lycIrq_.lycReg() - && lycCmp.timeToNextLy > 4 - isDoubleSpeed() * 4; - if (lycperiod && (old & lcdstat_lycirqen)) - return false; + bool const ds = isDoubleSpeed(); + int const m1_irq_lc_inv = lcd_cycles_per_line - mode1_irq_frame_cycle % lcd_cycles_per_line; - if (ly < 144) { - if (cc + isDoubleSpeed() * 2 < m0TimeOfCurrentLine(cc) || timeToNextLy <= 4) + if (ly < lcd_vres - 1 || (ly == lcd_vres - 1 && timeToNextLy > m1_irq_lc_inv* (1 + ds))) { + if (eventTimes_(memevent_m0irq) < ppu_.lyCounter().time() + || timeToNextLy <= (ly < lcd_vres - 1 ? 4 + 4 * ds : 4 + 2 * ds)) { return lycperiod && (data & lcdstat_lycirqen); + } if (old & lcdstat_m0irqen) return false; return (data & lcdstat_m0irqen) - || (lycperiod && (data & lcdstat_lycirqen)); + || (lycperiod && (data & lcdstat_lycirqen)); } - if (old & lcdstat_m1irqen) + if (old & lcdstat_m1irqen && (ly < lcd_lines_per_frame - 1 || timeToNextLy > 3 + 3 * ds)) return false; - return ((data & lcdstat_m1irqen) && (ly < 153 || timeToNextLy > 4 - isDoubleSpeed() * 4)) - || (lycperiod && (data & lcdstat_lycirqen)); + return ((data & lcdstat_m1irqen) + && (ly < lcd_lines_per_frame - 1 || timeToNextLy > 4 + 2 * ds)) + || (lycperiod && (data & lcdstat_lycirqen)); } inline bool LCD::statChangeTriggersStatIrqCgb( - unsigned const old, unsigned const data, unsigned long const cc) { - if (!(data & ~old & ( lcdstat_lycirqen - | lcdstat_m2irqen - | lcdstat_m1irqen - | lcdstat_m0irqen))) { + unsigned const old, unsigned const data, unsigned long const cc) { + if (!(data & ~old & (lcdstat_lycirqen + | lcdstat_m2irqen + | lcdstat_m1irqen + | lcdstat_m0irqen))) { return false; } - unsigned const ly = ppu_.lyCounter().ly(); + int const ly = ppu_.lyCounter().ly(); int const timeToNextLy = ppu_.lyCounter().time() - cc; - return statChangeTriggersM0LycOrM1StatIrqCgb(old, data, cc) - || statChangeTriggersM2IrqCgb(old, data, ly, timeToNextLy); + LyCnt const lycCmp = getLycCmpLy(ppu_.lyCounter(), cc); + bool const lycperiod = lycCmp.ly == lycIrq_.lycReg() + && lycCmp.timeToNextLy > 2; + if (lycperiod && (old & lcdstat_lycirqen)) + return false; + + return statChangeTriggersM0LycOrM1StatIrqCgb(old, data, lycperiod, cc) + || statChangeTriggersM2IrqCgb(old, data, ly, timeToNextLy, isDoubleSpeed()); } + inline bool LCD::statChangeTriggersStatIrq(unsigned old, unsigned data, unsigned long cc) { return ppu_.cgb() - ? statChangeTriggersStatIrqCgb(old, data, cc) - : statChangeTriggersStatIrqDmg(old, cc); + ? statChangeTriggersStatIrqCgb(old, data, cc) + : statChangeTriggersStatIrqDmg(old, cc); } void LCD::lcdstatChange(unsigned const data, unsigned long const cc) { @@ -611,56 +618,45 @@ void LCD::lcdstatChange(unsigned const data, unsigned long const cc) { lycIrq_.statRegChange(data, ppu_.lyCounter(), cc); if (ppu_.lcdc() & lcdc_en) { - if (statChangeTriggersStatIrq(old, data, cc)) - eventTimes_.flagIrq(2); - if ((data & lcdstat_m0irqen) && eventTimes_(memevent_m0irq) == disabled_time) { update(cc); - eventTimes_.setm(m0IrqTimeFromXpos166Time( - ppu_.predictedNextXposTime(166), ppu_.cgb(), isDoubleSpeed())); + eventTimes_.setm(ppu_.predictedNextXposTime(lcd_hres + 6)); } eventTimes_.setm(mode2IrqSchedule(data, ppu_.lyCounter(), cc)); eventTimes_.setm(lycIrq_.time()); + + if (statChangeTriggersStatIrq(old, data, cc)) + eventTimes_.flagIrq(2); } - m2IrqStatReg_ = eventTimes_(memevent_m2irq) - cc > (ppu_.cgb() - isDoubleSpeed()) * 4U - ? data - : (m2IrqStatReg_ & lcdstat_m1irqen) | (statReg_ & ~lcdstat_m1irqen); - m1IrqStatReg_ = eventTimes_(memevent_m1irq) - cc > (ppu_.cgb() - isDoubleSpeed()) * 4U - ? data - : (m1IrqStatReg_ & lcdstat_m0irqen) | (statReg_ & ~lcdstat_m0irqen); - - m0Irq_.statRegChange(data, eventTimes_(memevent_m0irq), cc, ppu_.cgb()); + mstatIrq_.statRegChange(data, eventTimes_(memevent_m0irq), eventTimes_(memevent_m1irq), + eventTimes_(memevent_m2irq), cc, ppu_.cgb()); } -static unsigned incLy(unsigned ly) { return ly == 153 ? 0 : ly + 1; } - -inline bool LCD::lycRegChangeStatTriggerBlockedByM0OrM1Irq(unsigned long const cc) { +inline bool LCD::lycRegChangeStatTriggerBlockedByM0OrM1Irq(unsigned data, unsigned long cc) { int const timeToNextLy = ppu_.lyCounter().time() - cc; - if (ppu_.lyCounter().ly() < 144) { + if (ppu_.lyCounter().ly() < lcd_vres) { return (statReg_ & lcdstat_m0irqen) - && cc >= m0TimeOfCurrentLine(cc) - && timeToNextLy > 4 << ppu_.cgb(); + && eventTimes_(memevent_m0irq) > ppu_.lyCounter().time() + && data == ppu_.lyCounter().ly(); } return (statReg_ & lcdstat_m1irqen) - && !(ppu_.lyCounter().ly() == 153 - && timeToNextLy <= 4 - && ppu_.cgb() && !isDoubleSpeed()); + && !(ppu_.lyCounter().ly() == lcd_lines_per_frame - 1 + && timeToNextLy <= 2 + 2 * isDoubleSpeed() + 2 * ppu_.cgb()); } bool LCD::lycRegChangeTriggersStatIrq( - unsigned const old, unsigned const data, unsigned long const cc) { - if (!(statReg_ & lcdstat_lycirqen) || data >= 154 - || lycRegChangeStatTriggerBlockedByM0OrM1Irq(cc)) { + unsigned const old, unsigned const data, unsigned long const cc) { + if (!(statReg_ & lcdstat_lycirqen) || data >= lcd_lines_per_frame + || lycRegChangeStatTriggerBlockedByM0OrM1Irq(data, cc)) { return false; } LyCnt lycCmp = getLycCmpLy(ppu_.lyCounter(), cc); - if (lycCmp.timeToNextLy <= 4 << ppu_.cgb()) { - bool const ds = isDoubleSpeed(); - if (old == lycCmp.ly && !(lycCmp.timeToNextLy <= 4 && ppu_.cgb() && !ds)) + if (lycCmp.timeToNextLy <= 4 + 4 * isDoubleSpeed() + 2 * ppu_.cgb()) { + if (old == lycCmp.ly && lycCmp.timeToNextLy > 2 * ppu_.cgb()) return false; // simultaneous ly/lyc inc. lyc flag never goes low -> no trigger. lycCmp.ly = incLy(lycCmp.ly); @@ -677,8 +673,9 @@ void LCD::lycRegChange(unsigned const data, unsigned long const cc) { if (cc >= eventTimes_.nextEventTime()) update(cc); - m0Irq_.lycRegChange(data, eventTimes_(memevent_m0irq), cc, isDoubleSpeed(), ppu_.cgb()); lycIrq_.lycRegChange(data, ppu_.lyCounter(), cc); + mstatIrq_.lycRegChange(data, eventTimes_(memevent_m0irq), + eventTimes_(memevent_m2irq), cc, isDoubleSpeed(), ppu_.cgb()); if (ppu_.lcdc() & lcdc_en) { eventTimes_.setm(lycIrq_.time()); @@ -686,7 +683,8 @@ void LCD::lycRegChange(unsigned const data, unsigned long const cc) { if (lycRegChangeTriggersStatIrq(old, data, cc)) { if (ppu_.cgb() && !isDoubleSpeed()) { eventTimes_.setm(cc + 5); - } else + } + else eventTimes_.flagIrq(2); } } @@ -701,61 +699,51 @@ unsigned LCD::getStat(unsigned const lycReg, unsigned long const cc) { unsigned const ly = ppu_.lyCounter().ly(); int const timeToNextLy = ppu_.lyCounter().time() - cc; - if (ly > 143) { - if (ly < 153 || timeToNextLy > 4 - isDoubleSpeed() * 4) + int const lineCycles = lcd_cycles_per_line - (timeToNextLy >> isDoubleSpeed()); + long const frameCycles = 1l * ly * lcd_cycles_per_line + lineCycles; + if (frameCycles >= lcd_vres * lcd_cycles_per_line - 3 && frameCycles < lcd_cycles_per_frame - 3) { + if (frameCycles >= lcd_vres * lcd_cycles_per_line - 2 + && frameCycles < lcd_cycles_per_frame - 4 + isDoubleSpeed()) { stat = 1; - } else { - int const lineCycles = 456 - (timeToNextLy >> isDoubleSpeed()); - if (lineCycles < 80) { - if (!ppu_.inactivePeriodAfterDisplayEnable(cc)) - stat = 2; - } else if (cc + isDoubleSpeed() - ppu_.cgb() + 2 < m0TimeOfCurrentLine(cc)) + } + } + else if (lineCycles < 77 || lineCycles >= lcd_cycles_per_line - 3) { + if (!ppu_.inactivePeriodAfterDisplayEnable(cc + 1)) + stat = 2; + } + else if (cc + 2 < m0TimeOfCurrentLine(cc)) { + if (!ppu_.inactivePeriodAfterDisplayEnable(cc + 1)) stat = 3; } LyCnt const lycCmp = getLycCmpLy(ppu_.lyCounter(), cc); - if (lycReg == lycCmp.ly && lycCmp.timeToNextLy > 4 - isDoubleSpeed() * 4) + if (lycReg == lycCmp.ly && lycCmp.timeToNextLy > 2) stat |= lcdstat_lycflag; } return stat; } -static bool isMode2IrqEventBlockedByM1Irq(unsigned ly, unsigned statreg) { - return ly == 0 && (statreg & lcdstat_m1irqen); -} - -static bool isMode2IrqEventBlockedByLycIrq(unsigned ly, unsigned statreg, unsigned lycreg) { - return (statreg & lcdstat_lycirqen) - && (ly == 0 ? ly : ly - 1) == lycreg; -} - -static bool isMode2IrqEventBlocked(unsigned ly, unsigned statreg, unsigned lycreg) { - return isMode2IrqEventBlockedByM1Irq(ly, statreg) - || isMode2IrqEventBlockedByLycIrq(ly, statreg, lycreg); -} - inline void LCD::doMode2IrqEvent() { - unsigned const ly = eventTimes_(event_ly) - eventTimes_(memevent_m2irq) < 8 - ? incLy(ppu_.lyCounter().ly()) - : ppu_.lyCounter().ly(); - if (!isMode2IrqEventBlocked(ly, m2IrqStatReg_, lycIrq_.lycReg())) - eventTimes_.flagIrq(2); - - m2IrqStatReg_ = statReg_; + unsigned const ly = eventTimes_(event_ly) - eventTimes_(memevent_m2irq) < 16 + ? incLy(ppu_.lyCounter().ly()) + : ppu_.lyCounter().ly(); + if (mstatIrq_.doM2Event(ly, statReg_, lycIrq_.lycReg())) + eventTimes_.flagIrq(2, eventTimes_(memevent_m2irq)); + bool const ds = isDoubleSpeed(); + unsigned long next = lcd_cycles_per_frame; if (!(statReg_ & lcdstat_m0irqen)) { - unsigned long nextTime = eventTimes_(memevent_m2irq) + ppu_.lyCounter().lineTime(); + next = lcd_cycles_per_line; if (ly == 0) { - nextTime -= 4; - } else if (ly == 143) - nextTime += ppu_.lyCounter().lineTime() * 10 + 4; - - eventTimes_.setm(nextTime); - } else { - eventTimes_.setm(eventTimes_(memevent_m2irq) - + (70224 << isDoubleSpeed())); + next -= mode2_irq_line_cycle_ly0 - mode2_irq_line_cycle; + } + else if (ly == lcd_vres) { + next += lcd_cycles_per_line * (lcd_lines_per_frame - lcd_vres - 1) + + mode2_irq_line_cycle_ly0 - mode2_irq_line_cycle; + } } + eventTimes_.setm(eventTimes_(memevent_m2irq) + (next << ds)); } inline void LCD::event() { @@ -763,22 +751,18 @@ inline void LCD::event() { case event_mem: switch (eventTimes_.nextMemEvent()) { case memevent_m1irq: - eventTimes_.flagIrq((m1IrqStatReg_ & (lcdstat_m1irqen | lcdstat_m0irqen)) - == lcdstat_m1irqen - ? 3 - : 1); - m1IrqStatReg_ = statReg_; + eventTimes_.flagIrq(mstatIrq_.doM1Event(statReg_) ? 3 : 1, + eventTimes_(memevent_m1irq)); eventTimes_.setm(eventTimes_(memevent_m1irq) - + (70224 << isDoubleSpeed())); + + (lcd_cycles_per_frame << isDoubleSpeed())); break; - case memevent_lycirq: { - unsigned char ifreg = 0; - lycIrq_.doEvent(&ifreg, ppu_.lyCounter()); - eventTimes_.flagIrq(ifreg); + case memevent_lycirq: + if (lycIrq_.doEvent(ppu_.lyCounter())) + eventTimes_.flagIrq(2, eventTimes_(memevent_lycirq)); + eventTimes_.setm(lycIrq_.time()); break; - } case memevent_spritemap: eventTimes_.setm( @@ -789,8 +773,7 @@ inline void LCD::event() { case memevent_hdma: eventTimes_.flagHdmaReq(); nextM0Time_.predictNextM0Time(ppu_); - eventTimes_.setm(hdmaTimeFromM0Time( - nextM0Time_.predictedNextM0Time(), isDoubleSpeed())); + eventTimes_.setm(nextM0Time_.predictedNextM0Time()); break; case memevent_m2irq: @@ -798,17 +781,12 @@ inline void LCD::event() { break; case memevent_m0irq: - { - unsigned char ifreg = 0; - m0Irq_.doEvent(&ifreg, ppu_.lyCounter().ly(), statReg_, - lycIrq_.lycReg()); - eventTimes_.flagIrq(ifreg); - } + if (mstatIrq_.doM0Event(ppu_.lyCounter().ly(), statReg_, lycIrq_.lycReg())) + eventTimes_.flagIrq(2, eventTimes_(memevent_m0irq)); eventTimes_.setm(statReg_ & lcdstat_m0irqen - ? m0IrqTimeFromXpos166Time(ppu_.predictedNextXposTime(166), - ppu_.cgb(), isDoubleSpeed()) - : static_cast(disabled_time)); + ? ppu_.predictedNextXposTime(lcd_hres + 6) + : 1 * disabled_time); break; case memevent_oneshot_statirq: @@ -828,8 +806,6 @@ inline void LCD::event() { case event_ly: ppu_.doLyCountEvent(); eventTimes_.set(ppu_.lyCounter().time()); - if (scanlinecallback && ppu_.lyCounter().ly() == (unsigned)scanlinecallbacksl) - scanlinecallback(); break; } } @@ -864,15 +840,12 @@ SYNCFUNC(LCD) { SSS(ppu_); NSS(dmgColorsRgb32_); + NSS(cgbColorsRgb32_); NSS(bgpData_); NSS(objpData_); SSS(eventTimes_); - SSS(m0Irq_); + SSS(mstatIrq_); SSS(lycIrq_); SSS(nextM0Time_); NSS(statReg_); - NSS(m2IrqStatReg_); - NSS(m1IrqStatReg_); -} - } diff --git a/libgambatte/src/video.h b/libgambatte/src/video.h index abc5ec8f0e..ab8b25475c 100644 --- a/libgambatte/src/video.h +++ b/libgambatte/src/video.h @@ -22,7 +22,7 @@ #include "interruptrequester.h" #include "minkeeper.h" #include "video/lyc_irq.h" -#include "video/m0_irq.h" +#include "video/mstat_irq.h" #include "video/next_m0_time.h" #include "video/ppu.h" #include "newstate.h" @@ -36,8 +36,9 @@ public: { } - void flagHdmaReq() const { gambatte::flagHdmaReq(intreq_); } + void flagHdmaReq() const { if (!intreq_.halted()) gambatte::flagHdmaReq(intreq_); } void flagIrq(unsigned bit) const { intreq_.flagIrq(bit); } + void flagIrq(unsigned bit, unsigned long cc) const { intreq_.flagIrq(bit, cc); } void setNextEventTime(unsigned long time) const { intreq_.setEventTime(time); } private: @@ -49,13 +50,13 @@ public: LCD(unsigned char const *oamram, unsigned char const *vram, VideoInterruptRequester memEventRequester); void reset(unsigned char const *oamram, unsigned char const *vram, bool cgb); + void setCgbDmg(bool enabled) { ppu_.setCgbDmg(enabled); } void setStatePtrs(SaveState &state); void loadState(SaveState const &state, unsigned char const *oamram); void setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned long rgb32); void setCgbPalette(unsigned *lut); void setVideoBuffer(uint_least32_t *videoBuf, std::ptrdiff_t pitch); void setLayers(unsigned mask) { ppu_.setLayers(mask); } - void setCgb(bool cgb); void copyCgbPalettesToDmg(); int debugGetLY() const { return ppu_.lyCounter().ly(); } @@ -89,17 +90,19 @@ public: } unsigned cgbBgColorRead(unsigned index, unsigned long cycleCounter) { - return ppu_.cgb() & cgbpAccessible(cycleCounter) ? bgpData_[index] : 0xFF; + return ppu_.cgb() && cgbpAccessible(cycleCounter) ? bgpData_[index] : 0xFF; } unsigned cgbSpColorRead(unsigned index, unsigned long cycleCounter) { - return ppu_.cgb() & cgbpAccessible(cycleCounter) ? objpData_[index] : 0xFF; + return ppu_.cgb() && cgbpAccessible(cycleCounter) ? objpData_[index] : 0xFF; } void updateScreen(bool blanklcd, unsigned long cc); + void blackScreen(); void resetCc(unsigned long oldCC, unsigned long newCc); void speedChange(unsigned long cycleCounter); - bool vramAccessible(unsigned long cycleCounter); + bool vramReadable(unsigned long cycleCounter); + bool vramWritable(unsigned long cycleCounter); bool oamReadable(unsigned long cycleCounter); bool oamWritable(unsigned long cycleCounter); void wxChange(unsigned newValue, unsigned long cycleCounter); @@ -119,15 +122,16 @@ public: update(cc); lyReg = ppu_.lyCounter().ly(); - - if (lyReg == 153) { - if (isDoubleSpeed()) { - if (ppu_.lyCounter().time() - cc <= 456 * 2 - 8) - lyReg = 0; - } else + if (lyReg == lcd_lines_per_frame - 1) { + if (ppu_.lyCounter().time() - cc <= 2 * lcd_cycles_per_line - 2) lyReg = 0; - } else if (ppu_.lyCounter().time() - cc <= 4) - ++lyReg; + } + else if (ppu_.lyCounter().time() - cc <= 10 + && ppu_.lyCounter().time() - cc <= 6u + 4 * isDoubleSpeed()) { + lyReg = ppu_.lyCounter().time() - cc == 6u + 4 * isDoubleSpeed() + ? lyReg & (lyReg + 1) + : lyReg + 1; + } } return lyReg; @@ -139,9 +143,11 @@ public: void lycRegChange(unsigned data, unsigned long cycleCounter); void enableHdma(unsigned long cycleCounter); void disableHdma(unsigned long cycleCounter); + bool isHdmaPeriod(unsigned long cycleCounter); bool hdmaIsEnabled() const { return eventTimes_(memevent_hdma) != disabled_time; } void update(unsigned long cycleCounter); bool isCgb() const { return ppu_.cgb(); } + bool isCgbDmg() const { return ppu_.cgbDmg(); } bool isDoubleSpeed() const { return ppu_.lyCounter().isDoubleSpeed(); } unsigned long *bgPalette() { return ppu_.bgPalette(); } @@ -189,6 +195,7 @@ private: void set(MemEvent e, unsigned long time) { memEventMin_.setValue(e, time); setMemEvent(); } void flagIrq(unsigned bit) { memEventRequester_.flagIrq(bit); } + void flagIrq(unsigned bit, unsigned long cc) { memEventRequester_.flagIrq(bit, cc); } void flagHdmaReq() { memEventRequester_.flagHdmaReq(); } private: @@ -214,15 +221,13 @@ public: PPU ppu_; unsigned long dmgColorsRgb32_[3 * 4]; unsigned long cgbColorsRgb32_[32768]; - unsigned char bgpData_[8 * 8]; - unsigned char objpData_[8 * 8]; + unsigned char bgpData_[2 * max_num_palettes * num_palette_entries]; + unsigned char objpData_[2 * max_num_palettes * num_palette_entries]; EventTimes eventTimes_; - M0Irq m0Irq_; + MStatIrqEvent mstatIrq_; LycIrq lycIrq_; NextM0Time nextM0Time_; unsigned char statReg_; - unsigned char m2IrqStatReg_; - unsigned char m1IrqStatReg_; static void setDmgPalette(unsigned long palette[], unsigned long const dmgColors[], @@ -237,9 +242,9 @@ public: void event(); unsigned long m0TimeOfCurrentLine(unsigned long cc); bool cgbpAccessible(unsigned long cycleCounter); - bool lycRegChangeStatTriggerBlockedByM0OrM1Irq(unsigned long cc); + bool lycRegChangeStatTriggerBlockedByM0OrM1Irq(unsigned data, unsigned long cc); bool lycRegChangeTriggersStatIrq(unsigned old, unsigned data, unsigned long cc); - bool statChangeTriggersM0LycOrM1StatIrqCgb(unsigned old, unsigned data, unsigned long cc); + bool statChangeTriggersM0LycOrM1StatIrqCgb(unsigned old, unsigned data, bool lycperiod, unsigned long cc); bool statChangeTriggersStatIrqCgb(unsigned old, unsigned data, unsigned long cc); bool statChangeTriggersStatIrqDmg(unsigned old, unsigned long cc); bool statChangeTriggersStatIrq(unsigned old, unsigned data, unsigned long cc); diff --git a/libgambatte/src/video/lcddef.h b/libgambatte/src/video/lcddef.h index 5fbf3756cb..0ef743537e 100644 --- a/libgambatte/src/video/lcddef.h +++ b/libgambatte/src/video/lcddef.h @@ -3,18 +3,38 @@ namespace gambatte { -enum { lcdc_bgen = 0x01, - lcdc_objen = 0x02, - lcdc_obj2x = 0x04, - lcdc_tdsel = 0x10, - lcdc_we = 0x20, - lcdc_en = 0x80 }; + enum { + lcdc_bgen = 0x01, + lcdc_objen = 0x02, + lcdc_obj2x = 0x04, + lcdc_bgtmsel = 0x08, + lcdc_tdsel = 0x10, + lcdc_we = 0x20, + lcdc_wtmsel = 0x40, + lcdc_en = 0x80 + }; -enum { lcdstat_lycflag = 0x04, - lcdstat_m0irqen = 0x08, - lcdstat_m1irqen = 0x10, - lcdstat_m2irqen = 0x20, - lcdstat_lycirqen = 0x40 }; + enum { + lcdstat_lycflag = 0x04, + lcdstat_m0irqen = 0x08, + lcdstat_m1irqen = 0x10, + lcdstat_m2irqen = 0x20, + lcdstat_lycirqen = 0x40 + }; + + enum { + lcd_hres = 160, + lcd_vres = 144, + lcd_lines_per_frame = 154, + lcd_max_num_sprites_per_line = 10, + lcd_num_oam_entries = 40, + lcd_cycles_per_line = 456, + lcd_force_signed_enum1 = -1 + }; + enum { + lcd_cycles_per_frame = 1l * lcd_lines_per_frame * lcd_cycles_per_line, + lcd_force_signed_enum2 = -1 + }; } diff --git a/libgambatte/src/video/ly_counter.cpp b/libgambatte/src/video/ly_counter.cpp index d3c09184c2..1f985a23bc 100644 --- a/libgambatte/src/video/ly_counter.cpp +++ b/libgambatte/src/video/ly_counter.cpp @@ -33,7 +33,7 @@ LyCounter::LyCounter() void LyCounter::doEvent() { ++ly_; - if (ly_ == 154) + if (ly_ == lcd_lines_per_frame) ly_ = 0; time_ = time_ + lineTime_; @@ -48,21 +48,22 @@ unsigned long LyCounter::nextLineCycle(unsigned const lineCycle, unsigned long c } unsigned long LyCounter::nextFrameCycle(unsigned long const frameCycle, unsigned long const cc) const { - unsigned long tmp = time_ + (((153U - ly()) * 456U + frameCycle) << ds_); - if (tmp - cc > 70224U << ds_) - tmp -= 70224U << ds_; + unsigned long tmp = time_ + (((lcd_lines_per_frame - 1l - ly()) * lcd_cycles_per_line + frameCycle) << ds_); + if (tmp - cc > 1ul * lcd_cycles_per_frame << ds_) + tmp -= 1ul * lcd_cycles_per_frame << ds_; return tmp; } void LyCounter::reset(unsigned long videoCycles, unsigned long lastUpdate) { - ly_ = videoCycles / 456; - time_ = lastUpdate + ((456 - (videoCycles - ly_ * 456ul)) << isDoubleSpeed()); + ly_ = videoCycles / lcd_cycles_per_line; + time_ = lastUpdate + ((lcd_cycles_per_line + - (videoCycles - 1l * ly_ * lcd_cycles_per_line)) << isDoubleSpeed()); } void LyCounter::setDoubleSpeed(bool ds) { ds_ = ds; - lineTime_ = 456U << ds; + lineTime_ = lcd_cycles_per_line << ds; } SYNCFUNC(LyCounter) diff --git a/libgambatte/src/video/ly_counter.h b/libgambatte/src/video/ly_counter.h index 53a7cefee3..5700d052b1 100644 --- a/libgambatte/src/video/ly_counter.h +++ b/libgambatte/src/video/ly_counter.h @@ -20,6 +20,7 @@ #define LY_COUNTER_H #include "newstate.h" +#include "lcddef.h" namespace gambatte { @@ -32,11 +33,11 @@ public: bool isDoubleSpeed() const { return ds_; } unsigned long frameCycles(unsigned long cc) const { - return ly_ * 456ul + lineCycles(cc); + return 1l * ly_ * lcd_cycles_per_line + lineCycles(cc); } unsigned lineCycles(unsigned long cc) const { - return 456u - ((time_ - cc) >> isDoubleSpeed()); + return lcd_cycles_per_line - ((time_ - cc) >> isDoubleSpeed()); } unsigned lineTime() const { return lineTime_; } diff --git a/libgambatte/src/video/lyc_irq.cpp b/libgambatte/src/video/lyc_irq.cpp index b56a416efa..92ea67ea3c 100644 --- a/libgambatte/src/video/lyc_irq.cpp +++ b/libgambatte/src/video/lyc_irq.cpp @@ -23,7 +23,26 @@ #include "savestate.h" #include -namespace gambatte { +using namespace gambatte; + +namespace { + + unsigned long schedule(unsigned statReg, + unsigned lycReg, LyCounter const& lyCounter, unsigned long cc) { + return (statReg & lcdstat_lycirqen) && lycReg < lcd_lines_per_frame + ? lyCounter.nextFrameCycle(lycReg + ? 1l * lycReg * lcd_cycles_per_line - 2 + : (lcd_lines_per_frame - 1l) * lcd_cycles_per_line + 6, cc) + : 1 * disabled_time; + } + + bool lycIrqBlockedByM2OrM1StatIrq(unsigned ly, unsigned statreg) { + return ly <= lcd_vres && ly > 0 + ? statreg & lcdstat_m2irqen + : statreg & lcdstat_m1irqen; + } + +} LycIrq::LycIrq() : time_(disabled_time) @@ -35,53 +54,40 @@ LycIrq::LycIrq() { } -static unsigned long schedule(unsigned statReg, - unsigned lycReg, LyCounter const &lyCounter, unsigned long cc) { - return (statReg & lcdstat_lycirqen) && lycReg < 154 - ? lyCounter.nextFrameCycle(lycReg ? lycReg * 456 : 153 * 456 + 8, cc) - : static_cast(disabled_time); -} - void LycIrq::regChange(unsigned const statReg, - unsigned const lycReg, LyCounter const &lyCounter, unsigned long const cc) { + unsigned const lycReg, LyCounter const& lyCounter, unsigned long const cc) { unsigned long const timeSrc = schedule(statReg, lycReg, lyCounter, cc); statRegSrc_ = statReg; lycRegSrc_ = lycReg; time_ = std::min(time_, timeSrc); if (cgb_) { - if (time_ - cc > 8 || (timeSrc != time_ && time_ - cc > 4U - lyCounter.isDoubleSpeed() * 4U)) + if (time_ - cc > 6u + 4 * lyCounter.isDoubleSpeed() || (timeSrc != time_ && time_ - cc > 2)) lycReg_ = lycReg; - - if (time_ - cc > 4U - lyCounter.isDoubleSpeed() * 4U) + if (time_ - cc > 2) statReg_ = statReg; - } else { + } + else { if (time_ - cc > 4 || timeSrc != time_) lycReg_ = lycReg; - if (time_ - cc > 4 || lycReg_ != 0) - statReg_ = statReg; - - statReg_ = (statReg_ & lcdstat_lycirqen) | (statReg & ~lcdstat_lycirqen); + statReg_ = statReg; } } -static bool lycIrqBlockedByM2OrM1StatIrq(unsigned ly, unsigned statreg) { - return ly - 1u < 144u - 1u - ? statreg & lcdstat_m2irqen - : statreg & lcdstat_m1irqen; -} - -void LycIrq::doEvent(unsigned char *const ifreg, LyCounter const &lyCounter) { +bool LycIrq::doEvent(LyCounter const& lyCounter) { + bool flagIrq = false; if ((statReg_ | statRegSrc_) & lcdstat_lycirqen) { - unsigned cmpLy = lyCounter.time() - time_ < lyCounter.lineTime() ? 0 : lyCounter.ly(); - if (lycReg_ == cmpLy && !lycIrqBlockedByM2OrM1StatIrq(lycReg_, statReg_)) - *ifreg |= 2; + unsigned const cmpLy = lyCounter.ly() == lcd_lines_per_frame - 1 + ? 0 + : lyCounter.ly() + 1; + flagIrq = lycReg_ == cmpLy && !lycIrqBlockedByM2OrM1StatIrq(lycReg_, statReg_); } lycReg_ = lycRegSrc_; statReg_ = statRegSrc_; time_ = schedule(statReg_, lycReg_, lyCounter, time_); + return flagIrq; } void LycIrq::loadState(SaveState const &state) { @@ -110,5 +116,3 @@ SYNCFUNC(LycIrq) NSS(statReg_); NSS(cgb_); } - -} diff --git a/libgambatte/src/video/lyc_irq.h b/libgambatte/src/video/lyc_irq.h index 1f35ce47a5..92958a9e36 100644 --- a/libgambatte/src/video/lyc_irq.h +++ b/libgambatte/src/video/lyc_irq.h @@ -29,7 +29,7 @@ class LyCounter; class LycIrq { public: LycIrq(); - void doEvent(unsigned char *ifreg, LyCounter const &lyCounter); + bool doEvent(LyCounter const& lyCounter); unsigned lycReg() const { return lycRegSrc_; } void loadState(SaveState const &state); unsigned long time() const { return time_; } diff --git a/libgambatte/src/video/m0_irq.h b/libgambatte/src/video/m0_irq.h deleted file mode 100644 index 8ea69528db..0000000000 --- a/libgambatte/src/video/m0_irq.h +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef M0_IRQ_H -#define M0_IRQ_H - -#include "lcddef.h" -#include "../savestate.h" -#include "../newstate.h" - -namespace gambatte { - -class M0Irq { -public: - M0Irq() - : statReg_(0) - , lycReg_(0) - { - } - - void lcdReset(unsigned statReg, unsigned lycReg) { - statReg_ = statReg; - lycReg_ = lycReg; - } - - void statRegChange(unsigned statReg, - unsigned long nextM0IrqTime, unsigned long cc, bool cgb) { - if (nextM0IrqTime - cc > cgb * 2U) - statReg_ = statReg; - } - - void lycRegChange(unsigned lycReg, - unsigned long nextM0IrqTime, unsigned long cc, - bool ds, bool cgb) { - if (nextM0IrqTime - cc > cgb * 5 + 1U - ds) - lycReg_ = lycReg; - } - - void doEvent(unsigned char *ifreg, unsigned ly, unsigned statReg, unsigned lycReg) { - if (((statReg_ | statReg) & lcdstat_m0irqen) - && (!(statReg_ & lcdstat_lycirqen) || ly != lycReg_)) { - *ifreg |= 2; - } - - statReg_ = statReg; - lycReg_ = lycReg; - } - - void loadState(SaveState const &state) { - lycReg_ = state.ppu.m0lyc; - statReg_ = state.mem.ioamhram.get()[0x141]; - } - - unsigned statReg() const { return statReg_; } - -private: - unsigned char statReg_; - unsigned char lycReg_; - -public: - template - void SyncState(NewState *ns) - { - NSS(statReg_); - NSS(lycReg_); - } -}; - -} - -#endif diff --git a/libgambatte/src/video/mstat_irq.h b/libgambatte/src/video/mstat_irq.h new file mode 100644 index 0000000000..d00d1fa038 --- /dev/null +++ b/libgambatte/src/video/mstat_irq.h @@ -0,0 +1,72 @@ +#ifndef M0_IRQ_H +#define M0_IRQ_H + +#include "lcddef.h" +#include "../savestate.h" +#include "../newstate.h" + +namespace gambatte { + +class MStatIrqEvent { +public: + MStatIrqEvent() : lycReg_(0), statReg_(0) {} + void lcdReset(unsigned lycReg) { lycReg_ = lycReg; } + + void lycRegChange(unsigned lycReg, unsigned long nextM0IrqTime, + unsigned long nextM2IrqTime, unsigned long cc, bool ds, bool cgb) { + if (cc + 5 * cgb + 1 - ds < std::min(nextM0IrqTime, nextM2IrqTime)) + lycReg_ = lycReg; + } + + void statRegChange(unsigned statReg, unsigned long nextM0IrqTime, unsigned long nextM1IrqTime, + unsigned long nextM2IrqTime, unsigned long cc, bool cgb) { + if (cc + 2 * cgb < std::min(std::min(nextM0IrqTime, nextM1IrqTime), nextM2IrqTime)) + statReg_ = statReg; + } + + bool doM0Event(unsigned ly, unsigned statReg, unsigned lycReg) { + bool const flagIrq = ((statReg | statReg_) & lcdstat_m0irqen) + && (!(statReg_ & lcdstat_lycirqen) || ly != lycReg_); + lycReg_ = lycReg; + statReg_ = statReg; + return flagIrq; + } + + bool doM1Event(unsigned statReg) { + bool const flagIrq = (statReg & lcdstat_m1irqen) + && !(statReg_ & (lcdstat_m2irqen | lcdstat_m0irqen)); + statReg_ = statReg; + return flagIrq; + } + + bool doM2Event(unsigned ly, unsigned statReg, unsigned lycReg) { + bool const blockedByM1Irq = ly == 0 && (statReg_ & lcdstat_m1irqen); + bool const blockedByLycIrq = (statReg_ & lcdstat_lycirqen) + && (ly == 0 ? ly : ly - 1) == lycReg_; + bool const flagIrq = !blockedByM1Irq && !blockedByLycIrq; + lycReg_ = lycReg; + statReg_ = statReg; + return flagIrq; + } + + void loadState(SaveState const& state) { + lycReg_ = state.ppu.m0lyc; + statReg_ = state.mem.ioamhram.get()[0x141]; + } + +private: + unsigned char statReg_; + unsigned char lycReg_; + +public: + template + void SyncState(NewState *ns) + { + NSS(statReg_); + NSS(lycReg_); + } +}; + +} + +#endif diff --git a/libgambatte/src/video/next_m0_time.cpp b/libgambatte/src/video/next_m0_time.cpp index 9a853c8c38..008e3ba595 100644 --- a/libgambatte/src/video/next_m0_time.cpp +++ b/libgambatte/src/video/next_m0_time.cpp @@ -2,7 +2,7 @@ #include "ppu.h" void gambatte::NextM0Time::predictNextM0Time(PPU const &ppu) { - predictedNextM0Time_ = ppu.predictedNextXposTime(167); + predictedNextM0Time_ = ppu.predictedNextXposTime(lcd_hres + 7); } SYNCFUNC(gambatte::NextM0Time) diff --git a/libgambatte/src/video/next_m0_time.h b/libgambatte/src/video/next_m0_time.h index 547de18608..110c0a489b 100644 --- a/libgambatte/src/video/next_m0_time.h +++ b/libgambatte/src/video/next_m0_time.h @@ -10,10 +10,10 @@ public: NextM0Time() : predictedNextM0Time_(0) {} void predictNextM0Time(class PPU const &v); void invalidatePredictedNextM0Time() { predictedNextM0Time_ = 0; } - unsigned predictedNextM0Time() const { return predictedNextM0Time_; } + unsigned long predictedNextM0Time() const { return predictedNextM0Time_; } private: - unsigned predictedNextM0Time_; + unsigned long predictedNextM0Time_; public: templatevoid SyncState(NewState *ns); diff --git a/libgambatte/src/video/ppu.cpp b/libgambatte/src/video/ppu.cpp index 514beee00c..23dffe870e 100644 --- a/libgambatte/src/video/ppu.cpp +++ b/libgambatte/src/video/ppu.cpp @@ -19,13 +19,14 @@ #include "ppu.h" #include "savestate.h" #include -#include #include -namespace { +#include using namespace gambatte; +namespace { + #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)) @@ -44,7 +45,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 unsigned short const expand_lut[0x200] = { +unsigned short const expand_lut[0x200] = { EXPAND_TABLE, #undef PREP @@ -60,9 +61,9 @@ static unsigned short const expand_lut[0x200] = { #define DECLARE_FUNC(n, id) \ enum { ID##n = id }; \ - static void f##n (PPUPriv &); \ - static unsigned predictCyclesUntilXpos_f##n (PPUPriv const &, int targetxpos, unsigned cycles); \ - static PPUState const f##n##_ = { f##n, predictCyclesUntilXpos_f##n, ID##n } + void f##n (PPUPriv &); \ + unsigned predictCyclesUntilXpos_f##n (PPUPriv const &, int targetxpos, unsigned cycles); \ + 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); } @@ -96,22 +97,39 @@ namespace StartWindowDraw { #undef DECLARE_FUNC +enum { + attr_cgbpalno = 0x07, attr_tdbank = 0x08, attr_dmgpalno = 0x10, attr_xflip = 0x20, + attr_yflip = 0x40, attr_bgpriority = 0x80 +}; 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 }; -static inline int lcdcEn( PPUPriv const &p) { return p.lcdc & lcdc_en; } -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) && (p.layersMask & layer_mask_obj); } -static inline int lcdcBgEn( PPUPriv const &p) { return (p.lcdc & lcdc_bgen) && (p.layersMask & layer_mask_bg); } +int const max_m3start_cycles = 80; +int const tile_bpp = 2; +int const tile_bpp_mask = (1 << tile_bpp) - 1; +int const tile_len = 8; +int const tile_line_size = tile_bpp * tile_len / 8; +int const tile_size = tile_line_size * tile_len; +int const tile_map_begin = 0x1800; +int const tile_map_len = 0x20; +int const tile_map_size = tile_map_len * tile_map_len; +int const tile_pattern_table_size = 0x1000; +int const vram_bank_size = 0x2000; +int const xpos_end = 168; -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; } +inline int spx(PPUPriv::Sprite const& s) { return s.spx; } -static inline void nextCall(int const cycles, PPUState const &state, PPUPriv &p) { +inline int lcdcEn(PPUPriv const& p) { return p.lcdc & lcdc_en; } +inline int lcdcWinEn(PPUPriv const& p) { return p.lcdc & lcdc_we; } +inline int lcdcObj2x(PPUPriv const& p) { return p.lcdc & lcdc_obj2x; } +inline int lcdcObjEn(PPUPriv const& p) { return p.lcdc & lcdc_objen; } +inline int lcdcBgEn(PPUPriv const& p) { return p.lcdc & lcdc_bgen; } + +inline int weMasterCheckLy0LineCycle(bool cgb) { return 1 + cgb; } +inline int weMasterCheckPriorToLyIncLineCycle(bool /*cgb*/) { return 450; } +inline int weMasterCheckAfterLyIncLineCycle(bool /*cgb*/) { return 454; } +inline int m3StartLineCycle(bool cgb) { return 83 + cgb; } + +inline void nextCall(int const cycles, PPUState const &state, PPUPriv &p) { int const c = p.cycles - cycles; if (c >= 0) { p.cycles = c; @@ -122,25 +140,32 @@ static inline void nextCall(int const cycles, PPUState const &state, PPUPriv &p) p.nextCallPtr = &state; } +inline unsigned long const* cgbSpPalette(PPUPriv const& p, unsigned const attrib) { + if (!p.cgbDmg) + return p.spPalette + (attrib & attr_cgbpalno) * num_palette_entries; + else + return p.spPalette + (attrib & attr_dmgpalno ? num_palette_entries : 0); +} + namespace M2_Ly0 { - static void f0(PPUPriv &p) { + void f0(PPUPriv& p) { p.weMaster = lcdcWinEn(p) && 0 == p.wy; p.winYPos = 0xFF; - nextCall(m3StartLineCycle(p.cgb), M3Start::f0_, p); + nextCall(m3StartLineCycle(p.cgb) - weMasterCheckLy0LineCycle(p.cgb), M3Start::f0_, p); } } namespace M2_LyNon0 { - static void f0(PPUPriv &p) { + void f0(PPUPriv& p) { p.weMaster |= lcdcWinEn(p) && p.lyCounter.ly() == p.wy; - nextCall( weMasterCheckAfterLyIncLineCycle(p.cgb) - - weMasterCheckPriorToLyIncLineCycle(p.cgb), f1_, p); + nextCall(weMasterCheckAfterLyIncLineCycle(p.cgb) + - weMasterCheckPriorToLyIncLineCycle(p.cgb), f1_, p); } - static void f1(PPUPriv &p) { + 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); + nextCall(lcd_cycles_per_line - weMasterCheckAfterLyIncLineCycle(p.cgb) + m3StartLineCycle(p.cgb), + M3Start::f0_, p); } } @@ -152,7 +177,7 @@ namespace M2 { } }; - static void f0(PPUPriv &p) { + void f0(PPUPriv &p) { std::memset(&p.spLut, 0, sizeof p.spLut); p.reg0 = 0; p.nextSprite = 0; @@ -160,38 +185,40 @@ namespace M2 { f1(p); } - static void f1(PPUPriv &p) { + void f1(PPUPriv &p) { + int const oam_entry_size = 4; + int const oam_size = oam_entry_size * lcd_num_oam_entries; int cycles = p.cycles; unsigned oampos = p.reg0; unsigned nextSprite = p.nextSprite; - unsigned const nly = (p.lyCounter.ly() + 1 == 154 ? 0 : p.lyCounter.ly() + 1) - + ((p.lyCounter.time()-(p.now-p.cycles)) <= 4); + unsigned const nly = (p.lyCounter.ly() + 1 == lcd_lines_per_frame ? 0 : p.lyCounter.ly() + 1) + + (p.lyCounter.time() - (p.now - p.cycles) <= 4); bool const ls = p.spriteMapper.largeSpritesSource(); do { - unsigned const spy = p.spriteMapper.oamram()[oampos ]; - unsigned const spx = p.spriteMapper.oamram()[oampos+1]; + 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) { + if (ls ? ydiff < 2u * tile_len : ydiff - 1u * tile_len < 1u * tile_len) { p.spriteList[nextSprite].spx = spx; - p.spriteList[nextSprite].line = 15u - ydiff; + p.spriteList[nextSprite].line = 2 * tile_len - 1 - ydiff; p.spriteList[nextSprite].oampos = oampos; - if (++nextSprite == 10) { - cycles -= (0xA0 - 4 - oampos) >> 1; - oampos = 0xA0 - 4; + if (++nextSprite == lcd_max_num_sprites_per_line) { + cycles -= (oam_size - oam_entry_size - oampos) >> 1; + oampos = oam_size - oam_entry_size; } } - oampos += 4; - } while ((cycles-=2) >= 0 && oampos != 0xA0); + oampos += oam_entry_size; + } while ((cycles -= 2) >= 0 && oampos != oam_size); p.reg0 = oampos; p.nextSprite = nextSprite; p.cycles = cycles; - if (oampos == 0xA0) { + if (oampos == oam_size) { insertionSort(p.spriteList, p.spriteList + nextSprite, SpriteLess()); p.spriteList[nextSprite].spx = 0xFF; p.nextSprite = 0; @@ -201,60 +228,74 @@ namespace M2 { } */ -static int loadTileDataByte0(PPUPriv const &p) { +int loadTileDataByte0(PPUPriv const& p) { unsigned const yoffset = p.winDrawState & win_draw_started - ? p.winYPos - : p.scy + p.lyCounter.ly(); + ? 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]; + return p.vram[tile_pattern_table_size + + vram_bank_size / attr_tdbank * (p.nattrib & attr_tdbank) + - ((2 * tile_size * p.reg1 | tile_pattern_table_size / lcdc_tdsel * p.lcdc) + & tile_pattern_table_size) + + p.reg1 * tile_size + + ((p.nattrib & attr_yflip ? -1 : 0) ^ yoffset) % tile_len * tile_line_size]; } -static int loadTileDataByte1(PPUPriv const &p) { +int loadTileDataByte1(PPUPriv const& p) { unsigned const yoffset = p.winDrawState & win_draw_started - ? p.winYPos - : p.scy + p.lyCounter.ly(); + ? 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]; + return p.vram[tile_pattern_table_size + + vram_bank_size / attr_tdbank * (p.nattrib & attr_tdbank) + - ((2 * tile_size * p.reg1 | tile_pattern_table_size / lcdc_tdsel * p.lcdc) + & tile_pattern_table_size) + + p.reg1 * tile_size + + ((p.nattrib & attr_yflip ? -1 : 0) ^ yoffset) % tile_len * tile_line_size + 1]; } namespace M3Start { - static void f0(PPUPriv &p) { + void f0(PPUPriv& p) { p.xpos = 0; if ((p.winDrawState & win_draw_start) && lcdcWinEn(p)) { p.winDrawState = win_draw_started; - p.wscx = 8 + (p.scx & 7); + p.wscx = tile_len + p.scx % tile_len; ++p.winYPos; - } else + } + else p.winDrawState = 0; p.nextCallPtr = &f1_; f1(p); } - static void f1(PPUPriv &p) { + void f1(PPUPriv& p) { while (p.xpos < max_m3start_cycles) { - if ((p.xpos & 7) == (p.scx & 7)) + if (p.xpos % tile_len == p.scx % tile_len) break; - switch (p.xpos & 7) { + switch (p.xpos % tile_len) { case 0: 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[tile_map_size / lcdc_wtmsel * (p.lcdc & lcdc_wtmsel) + + tile_map_len / tile_len * (p.winYPos & (0x100 - tile_len)) + + p.wscx / tile_len % tile_map_len + + tile_map_begin]; + p.nattrib = p.vram[tile_map_size / lcdc_wtmsel * (p.lcdc & lcdc_wtmsel) + + tile_map_len / tile_len * (p.winYPos & (0x100 - tile_len)) + + p.wscx / tile_len % tile_map_len + + tile_map_begin + vram_bank_size]; + } + else { + p.reg1 = p.vram[((tile_map_size / lcdc_bgtmsel * p.lcdc | p.scx / tile_len) + & (tile_map_size + tile_map_len - 1)) + + tile_map_len / tile_len * ((p.scy + p.lyCounter.ly()) & (0x100 - tile_len)) + + tile_map_begin]; + p.nattrib = p.vram[((tile_map_size / lcdc_bgtmsel * p.lcdc | p.scx / tile_len) + & (tile_map_size + tile_map_len - 1)) + + tile_map_len / tile_len * ((p.scy + p.lyCounter.ly()) & (0x100 - tile_len)) + + tile_map_begin + vram_bank_size]; } break; @@ -262,13 +303,13 @@ namespace M3Start { p.reg0 = loadTileDataByte0(p); break; case 4: - { - int const r1 = loadTileDataByte1(p); - 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 + (0x100 / attr_xflip * p.nattrib & 0x100))[p.reg0] + + (expand_lut + (0x100 / attr_xflip * p.nattrib & 0x100))[r1] * 2; + } - break; + break; } ++p.xpos; @@ -278,17 +319,16 @@ namespace M3Start { } { - unsigned const ly = p.lyCounter.ly(); - unsigned const numSprites = p.spriteMapper.numSprites(ly); - unsigned char const *const sprites = p.spriteMapper.sprites(ly); + int const ly = p.lyCounter.ly(); + int const numSprites = p.spriteMapper.numSprites(ly); + unsigned char const* const sprites = p.spriteMapper.sprites(ly); + for (int i = 0; i < numSprites; ++i) { + int const pos = sprites[i]; + int const spy = p.spriteMapper.posbuf()[pos]; + int const spx = p.spriteMapper.posbuf()[pos + 1]; - for (unsigned i = 0; i < numSprites; ++i) { - 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; + p.spriteList[i].spx = spx; + p.spriteList[i].line = ly + 2 * tile_len - spy; p.spriteList[i].oampos = pos * 2; p.spwordList[i] = 0; } @@ -298,9 +338,9 @@ namespace M3Start { } p.xpos = 0; - p.endx = 8 - (p.scx & 7); + p.endx = tile_len - p.scx % tile_len; - static PPUState const *const flut[8] = { + static PPUState const* const flut[] = { &M3Loop::Tile::f0_, &M3Loop::Tile::f1_, &M3Loop::Tile::f2_, @@ -311,28 +351,232 @@ namespace M3Start { &M3Loop::Tile::f5_ }; - nextCall(1-p.cgb, *flut[p.scx & 7], p); + nextCall(1 - p.cgb, *flut[p.scx % tile_len], p); } } namespace M3Loop { -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; + void doFullTilesUnrolledDmg(PPUPriv& p, int const xend, uint_least32_t* const dbufline, + unsigned char const* const tileMapLine, unsigned const tileline, unsigned tileMapXpos) { + int const tileIndexSign = p.lcdc & lcdc_tdsel ? 0 : tile_pattern_table_size / tile_size / 2; + unsigned char const* const tileDataLine = p.vram + 2 * tile_size * tileIndexSign + + tileline * tile_line_size; + int xpos = p.xpos; - do { - int nextSprite = p.nextSprite; + do { + int nextSprite = p.nextSprite; - if (int(p.spriteList[nextSprite].spx) < xpos + 8) { - int cycles = p.cycles - 8; + if (spx(p.spriteList[nextSprite]) < xpos + tile_len) { + int cycles = p.cycles - tile_len; - if (lcdcObjEn(p)) { - cycles -= std::max(11 - (int(p.spriteList[nextSprite].spx) - xpos), 6); + if (lcdcObjEn(p)) { + cycles -= std::max(11 - (spx(p.spriteList[nextSprite]) - xpos), 6); + for (int i = nextSprite + 1; spx(p.spriteList[i]) < xpos + tile_len; ++i) + cycles -= 6; - for (unsigned i = nextSprite + 1; int(p.spriteList[i].spx) < xpos + 8; ++i) + 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] * tile_size; + unsigned const attrib = oam[p.spriteList[nextSprite].oampos + 3]; + unsigned const spline = (attrib & attr_yflip + ? p.spriteList[nextSprite].line ^ (2 * tile_len - 1) + : p.spriteList[nextSprite].line) * tile_line_size; + unsigned const ts = tile_size; + reg0 = p.vram[(lcdcObj2x(p) ? (reg1 & ~ts) | spline : reg1 | (spline & ~ts))]; + reg1 = p.vram[(lcdcObj2x(p) ? (reg1 & ~ts) | spline : reg1 | (spline & ~ts)) + 1]; + + p.spwordList[nextSprite] = + expand_lut[reg0 + (0x100 / attr_xflip * attrib & 0x100)] + + expand_lut[reg1 + (0x100 / attr_xflip * attrib & 0x100)] * 2; + p.spriteList[nextSprite].attrib = attrib; + ++nextSprite; + } while (spx(p.spriteList[nextSprite]) < xpos + tile_len); + } + else { + if (cycles < 0) + break; + + p.cycles = cycles; + + do { + ++nextSprite; + } while (spx(p.spriteList[nextSprite]) < xpos + tile_len); + } + + p.nextSprite = nextSprite; + } + else if (nextSprite - 1 < 0 || spx(p.spriteList[nextSprite - 1]) <= xpos - tile_len) { + if (!(p.cycles & -1ul * tile_len)) + break; + + int n = (std::min(spx(p.spriteList[nextSprite]), xend + tile_len - 1) - xpos) & -1u * tile_len; + n = std::min(n, p.cycles & -1ul * tile_len); + p.cycles -= n; + + unsigned ntileword = p.ntileword; + uint_least32_t* dst = dbufline + xpos - tile_len; + uint_least32_t* const dstend = dst + n; + xpos += n; + + if (!lcdcBgEn(p)) { + do { *dst++ = p.bgPalette[0]; } while (dst != dstend); + tileMapXpos += n / (1u * tile_len); + + unsigned const tno = tileMapLine[(tileMapXpos - 1) % tile_map_len]; + int const ts = tile_size; + ntileword = expand_lut[(tileDataLine + ts * tno - 2 * ts * (tno & tileIndexSign))[0]] + + expand_lut[(tileDataLine + ts * tno - 2 * ts * (tno & tileIndexSign))[1]] * 2; + } + else do { + dst[0] = p.bgPalette[ntileword & tile_bpp_mask]; + dst[1] = p.bgPalette[(ntileword & tile_bpp_mask << 1 * tile_bpp) >> 1 * tile_bpp]; + dst[2] = p.bgPalette[(ntileword & tile_bpp_mask << 2 * tile_bpp) >> 2 * tile_bpp]; + dst[3] = p.bgPalette[(ntileword & tile_bpp_mask << 3 * tile_bpp) >> 3 * tile_bpp]; + dst[4] = p.bgPalette[(ntileword & tile_bpp_mask << 4 * tile_bpp) >> 4 * tile_bpp]; + dst[5] = p.bgPalette[(ntileword & tile_bpp_mask << 5 * tile_bpp) >> 5 * tile_bpp]; + dst[6] = p.bgPalette[(ntileword & tile_bpp_mask << 6 * tile_bpp) >> 6 * tile_bpp]; + dst[7] = p.bgPalette[ntileword >> 7 * tile_bpp]; + dst += tile_len; + + unsigned const tno = tileMapLine[tileMapXpos % tile_map_len]; + int const ts = tile_size; + tileMapXpos = tileMapXpos % tile_map_len + 1; + ntileword = expand_lut[(tileDataLine + ts * tno - 2 * ts * (tno & tileIndexSign))[0]] + + expand_lut[(tileDataLine + ts * tno - 2 * ts * (tno & tileIndexSign))[1]] * 2; + } while (dst != dstend); + + + p.ntileword = ntileword; + continue; + } + else { + int cycles = p.cycles - tile_len; + if (cycles < 0) + break; + + p.cycles = cycles; + } + + + uint_least32_t* const dst = dbufline + (xpos - tile_len); + unsigned const tileword = -(p.lcdc & 1u * lcdc_bgen) & p.ntileword; + + dst[0] = p.bgPalette[tileword & tile_bpp_mask]; + dst[1] = p.bgPalette[(tileword & tile_bpp_mask << 1 * tile_bpp) >> 1 * tile_bpp]; + dst[2] = p.bgPalette[(tileword & tile_bpp_mask << 2 * tile_bpp) >> 2 * tile_bpp]; + dst[3] = p.bgPalette[(tileword & tile_bpp_mask << 3 * tile_bpp) >> 3 * tile_bpp]; + dst[4] = p.bgPalette[(tileword & tile_bpp_mask << 4 * tile_bpp) >> 4 * tile_bpp]; + dst[5] = p.bgPalette[(tileword & tile_bpp_mask << 5 * tile_bpp) >> 5 * tile_bpp]; + dst[6] = p.bgPalette[(tileword & tile_bpp_mask << 6 * tile_bpp) >> 6 * tile_bpp]; + dst[7] = p.bgPalette[tileword >> 7 * tile_bpp]; + + int i = nextSprite - 1; + + if (!lcdcObjEn(p)) { + do { + int const pos = spx(p.spriteList[i]) - xpos; + int const sa = pos * tile_bpp >= 0 + ? tile_len * tile_bpp - pos * tile_bpp + : tile_len * tile_bpp + pos * tile_bpp; + p.spwordList[i] = 1l * p.spwordList[i] >> sa; + --i; + } while (i >= 0 && spx(p.spriteList[i]) > xpos - tile_len); + } + else { + do { + int n; + int pos = spx(p.spriteList[i]) - xpos; + if (pos < 0) { + n = pos + tile_len; + pos = 0; + } + else + n = tile_len - pos; + + unsigned const attrib = p.spriteList[i].attrib; + long spword = p.spwordList[i]; + unsigned long const* const spPalette = p.spPalette + + (attrib & attr_dmgpalno) / (attr_dmgpalno / num_palette_entries); + uint_least32_t* d = dst + pos; + + if (!(attrib & attr_bgpriority)) { + int const bpp = tile_bpp, m = tile_bpp_mask; + switch (n) { + case 8: if (spword >> 7 * bpp) { d[7] = spPalette[spword >> 7 * bpp]; } // fall through + case 7: if (spword >> 6 * bpp & m) { d[6] = spPalette[spword >> 6 * bpp & m]; } // fall through + case 6: if (spword >> 5 * bpp & m) { d[5] = spPalette[spword >> 5 * bpp & m]; } // fall through + case 5: if (spword >> 4 * bpp & m) { d[4] = spPalette[spword >> 4 * bpp & m]; } // fall through + case 4: if (spword >> 3 * bpp & m) { d[3] = spPalette[spword >> 3 * bpp & m]; } // fall through + case 3: if (spword >> 2 * bpp & m) { d[2] = spPalette[spword >> 2 * bpp & m]; } // fall through + case 2: if (spword >> 1 * bpp & m) { d[1] = spPalette[spword >> 1 * bpp & m]; } // fall through + case 1: if (spword & m) { d[0] = spPalette[spword & m]; } + } + + spword >>= n * bpp; + + /*do { + if (spword & tile_bpp_mask) + dst[pos] = spPalette[spword & tile_bpp_mask]; + + spword >>= tile_bpp; + ++pos; + } while (--n);*/ + } + else { + unsigned tw = tileword >> pos * tile_bpp; + d += n; + n = -n; + + do { + if (spword & tile_bpp_mask) { + d[n] = tw & tile_bpp_mask + ? p.bgPalette[tw & tile_bpp_mask] + : spPalette[spword & tile_bpp_mask]; + } + + spword >>= tile_bpp; + tw >>= tile_bpp; + } while (++n); + } + + p.spwordList[i] = spword; + --i; + } while (i >= 0 && spx(p.spriteList[i]) > xpos - tile_len); + } + + + unsigned const tno = tileMapLine[tileMapXpos % tile_map_len]; + int const ts = tile_size; + tileMapXpos = tileMapXpos % tile_map_len + 1; + p.ntileword = expand_lut[(tileDataLine + ts * tno - 2 * ts * (tno & tileIndexSign))[0]] + + expand_lut[(tileDataLine + ts * tno - 2 * ts * (tno & tileIndexSign))[1]] * 2; + + xpos = xpos + tile_len; + } while (xpos < xend); + + p.xpos = xpos; + } + + 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 * tile_line_size + + tile_pattern_table_size / lcdc_tdsel * (~p.lcdc & lcdc_tdsel); + + do { + int nextSprite = p.nextSprite; + + if (spx(p.spriteList[nextSprite]) < xpos + tile_len) { + int cycles = p.cycles - tile_len; + cycles -= std::max(11 - (spx(p.spriteList[nextSprite]) - xpos), 6); + for (int i = nextSprite + 1; spx(p.spriteList[i]) < xpos + tile_len; ++i) cycles -= 6; if (cycles < 0) @@ -341,518 +585,342 @@ static void doFullTilesUnrolledDmg(PPUPriv &p, int const xend, uint_least32_t *c p.cycles = cycles; do { - unsigned char const *const oam = p.spriteMapper.oamram(); - unsigned reg0, reg1 = oam[p.spriteList[nextSprite].oampos + 2] * 16; + unsigned char const* const oam = p.spriteMapper.oamram(); + unsigned reg0, reg1 = oam[p.spriteList[nextSprite].oampos + 2] * tile_size; 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; + unsigned const spline = (attrib & attr_yflip + ? p.spriteList[nextSprite].line ^ (2 * tile_len - 1) + : p.spriteList[nextSprite].line) * tile_line_size; + unsigned const ts = tile_size; + reg0 = vram[vram_bank_size / attr_tdbank * (attrib & attr_tdbank) + + (lcdcObj2x(p) ? (reg1 & ~ts) | spline : reg1 | (spline & ~ts))]; + reg1 = vram[vram_bank_size / attr_tdbank * (attrib & attr_tdbank) + + (lcdcObj2x(p) ? (reg1 & ~ts) | spline : reg1 | (spline & ~ts)) + 1]; - reg0 = p.vram[(lcdcObj2x(p) ? (reg1 & ~16) | spline : reg1 | (spline & ~16)) ]; - reg1 = p.vram[(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.spwordList[nextSprite] = + expand_lut[reg0 + (0x100 / attr_xflip * attrib & 0x100)] + + expand_lut[reg1 + (0x100 / attr_xflip * attrib & 0x100)] * 2; p.spriteList[nextSprite].attrib = attrib; ++nextSprite; - } while (int(p.spriteList[nextSprite].spx) < xpos + 8); - } else { + } while (spx(p.spriteList[nextSprite]) < xpos + tile_len); + + p.nextSprite = nextSprite; + } + else if (nextSprite - 1 < 0 || spx(p.spriteList[nextSprite - 1]) <= xpos - tile_len) { + if (!(p.cycles & -1ul * tile_len)) + break; + + int n = (std::min(spx(p.spriteList[nextSprite]), xend + tile_len - 1) - xpos) & -1u * tile_len; + n = std::min(n, p.cycles & -1ul * tile_len); + p.cycles -= n; + + unsigned ntileword = p.ntileword; + unsigned nattrib = p.nattrib; + uint_least32_t* dst = dbufline + xpos - tile_len; + uint_least32_t* const dstend = dst + n; + xpos += n; + + do { + unsigned long const* const bgPalette = p.bgPalette + + (nattrib & attr_cgbpalno) * num_palette_entries; + dst[0] = bgPalette[ntileword & tile_bpp_mask]; + dst[1] = bgPalette[(ntileword & tile_bpp_mask << 1 * tile_bpp) >> 1 * tile_bpp]; + dst[2] = bgPalette[(ntileword & tile_bpp_mask << 2 * tile_bpp) >> 2 * tile_bpp]; + dst[3] = bgPalette[(ntileword & tile_bpp_mask << 3 * tile_bpp) >> 3 * tile_bpp]; + dst[4] = bgPalette[(ntileword & tile_bpp_mask << 4 * tile_bpp) >> 4 * tile_bpp]; + dst[5] = bgPalette[(ntileword & tile_bpp_mask << 5 * tile_bpp) >> 5 * tile_bpp]; + dst[6] = bgPalette[(ntileword & tile_bpp_mask << 6 * tile_bpp) >> 6 * tile_bpp]; + dst[7] = bgPalette[ntileword >> 7 * tile_bpp]; + dst += tile_len; + + unsigned const tno = tileMapLine[tileMapXpos % tile_map_len]; + nattrib = tileMapLine[tileMapXpos % tile_map_len + vram_bank_size]; + tileMapXpos = tileMapXpos % tile_map_len + 1; + + unsigned const tdo = tdoffset & ~(tno << 5); + unsigned char const* const td = vram + tno * tile_size + + (nattrib & attr_yflip ? tdo ^ tile_line_size * (tile_len - 1) : tdo) + + vram_bank_size / attr_tdbank * (nattrib & attr_tdbank); + unsigned short const* const explut = expand_lut + (0x100 / attr_xflip * nattrib & 0x100); + ntileword = explut[td[0]] + explut[td[1]] * 2; + } while (dst != dstend); + + + p.ntileword = ntileword; + p.nattrib = nattrib; + continue; + } + else { + int cycles = p.cycles - tile_len; 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 (!lcdcBgEn(p)) { - 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); - unsigned const tileword = -(p.lcdc & 1U & p.layersMask) & 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]; + uint_least32_t* const dst = dbufline + (xpos - tile_len); + unsigned const tileword = p.ntileword; + unsigned const attrib = p.nattrib; + unsigned long const* const bgPalette = p.bgPalette + + (attrib & attr_cgbpalno) * num_palette_entries; + dst[0] = bgPalette[tileword & tile_bpp_mask]; + dst[1] = bgPalette[(tileword & tile_bpp_mask << 1 * tile_bpp) >> 1 * tile_bpp]; + dst[2] = bgPalette[(tileword & tile_bpp_mask << 2 * tile_bpp) >> 2 * tile_bpp]; + dst[3] = bgPalette[(tileword & tile_bpp_mask << 3 * tile_bpp) >> 3 * tile_bpp]; + dst[4] = bgPalette[(tileword & tile_bpp_mask << 4 * tile_bpp) >> 4 * tile_bpp]; + dst[5] = bgPalette[(tileword & tile_bpp_mask << 5 * tile_bpp) >> 5 * tile_bpp]; + dst[6] = bgPalette[(tileword & tile_bpp_mask << 6 * tile_bpp) >> 6 * tile_bpp]; + dst[7] = bgPalette[tileword >> 7 * tile_bpp]; 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; + int const pos = spx(p.spriteList[i]) - xpos; + int const sa = pos * tile_bpp >= 0 + ? tile_len * tile_bpp - pos * tile_bpp + : tile_len * tile_bpp + pos * tile_bpp; + p.spwordList[i] = 1l * p.spwordList[i] >> sa; --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; - 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); + } while (i >= 0 && spx(p.spriteList[i]) > xpos - tile_len); } - } - - 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.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; - - 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 = -(p.layersMask & layer_mask_bg) & tileMapLine[(tileMapXpos & 0x1F) + 0x2000]; - tileMapXpos = (tileMapXpos & 0x1F) + 1; - - 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); - ntileword = -(p.layersMask & layer_mask_bg) & (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.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 ]; - 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 }; + else { + unsigned char idtab[] = { 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; - + int pos = spx(p.spriteList[i]) - xpos; if (pos < 0) { - n = pos + 8; + n = pos + tile_len; pos = 0; - } else - n = 8 - pos; + } + else + n = tile_len - 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; + long spword = p.spwordList[i]; + unsigned long const* const spPalette = cgbSpPalette(p, sattrib); if (!((attrib | sattrib) & bgprioritymask)) { - unsigned char *const idt = idtab + pos; - uint_least32_t *const d = dst + pos; + 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]; - } + case 8: if ((spword >> 7 * tile_bpp) && id < idt[7]) { + idt[7] = id; + d[7] = spPalette[spword >> 7 * tile_bpp]; + } // fall through + case 7: if ((spword >> 6 * tile_bpp & tile_bpp_mask) && id < idt[6]) { + idt[6] = id; + d[6] = spPalette[spword >> 6 * tile_bpp & tile_bpp_mask]; + } // fall through + case 6: if ((spword >> 5 * tile_bpp & tile_bpp_mask) && id < idt[5]) { + idt[5] = id; + d[5] = spPalette[spword >> 5 * tile_bpp & tile_bpp_mask]; + } // fall through + case 5: if ((spword >> 4 * tile_bpp & tile_bpp_mask) && id < idt[4]) { + idt[4] = id; + d[4] = spPalette[spword >> 4 * tile_bpp & tile_bpp_mask]; + } // fall through + case 4: if ((spword >> 3 * tile_bpp & tile_bpp_mask) && id < idt[3]) { + idt[3] = id; + d[3] = spPalette[spword >> 3 * tile_bpp & tile_bpp_mask]; + } // fall through + case 3: if ((spword >> 2 * tile_bpp & tile_bpp_mask) && id < idt[2]) { + idt[2] = id; + d[2] = spPalette[spword >> 2 * tile_bpp & tile_bpp_mask]; + } // fall through + case 2: if ((spword >> 1 * tile_bpp & tile_bpp_mask) && id < idt[1]) { + idt[1] = id; + d[1] = spPalette[spword >> 1 * tile_bpp & tile_bpp_mask]; + } // fall through + case 1: if ((spword & tile_bpp_mask) && id < idt[0]) { + idt[0] = id; + d[0] = spPalette[spword & tile_bpp_mask]; + } } - spword >>= n * 2; + spword >>= n * tile_bpp; /*do { - if ((spword & 3) && id < idtab[pos]) { + if ((spword & tile_bpp_mask) && id < idtab[pos]) { idtab[pos] = id; - dst[pos] = spPalette[spword & 3]; + dst[pos] = spPalette[spword & tile_bpp_mask]; } - spword >>= 2; + spword >>= tile_bpp; ++pos; } while (--n);*/ - } else { - unsigned tw = tileword >> pos * 2; + } + else { + unsigned tw = tileword >> pos * tile_bpp; do { - if ((spword & 3) && id < idtab[pos]) { + if ((spword & tile_bpp_mask) && id < idtab[pos]) { idtab[pos] = id; - dst[pos] = tw & 3 - ? bgPalette[ tw & 3] - : spPalette[spword & 3]; + dst[pos] = tw & tile_bpp_mask + ? bgPalette[tw & tile_bpp_mask] + : spPalette[spword & tile_bpp_mask]; } - spword >>= 2; - tw >>= 2; + spword >>= tile_bpp; + tw >>= tile_bpp; ++pos; } while (--n); } p.spwordList[i] = spword; --i; - } while (i >= 0 && int(p.spriteList[i].spx) > xpos - 8); + } while (i >= 0 && spx(p.spriteList[i]) > xpos - tile_len); } - } - { - 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 & 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; - } + { + unsigned const tno = tileMapLine[tileMapXpos % tile_map_len]; + unsigned const nattrib = tileMapLine[tileMapXpos % tile_map_len + vram_bank_size]; + tileMapXpos = tileMapXpos % tile_map_len + 1; - xpos = xpos + 8; - } while (xpos < xend); + unsigned const tdo = tdoffset & ~(tno << 5); + unsigned char const* const td = vram + tno * tile_size + + (nattrib & attr_yflip ? tdo ^ tile_line_size * (tile_len - 1) : tdo) + + vram_bank_size / attr_tdbank * (nattrib & attr_tdbank); + unsigned short const* const explut = expand_lut + (0x100 / attr_xflip * nattrib & 0x100); + p.ntileword = explut[td[0]] + explut[td[1]] * 2; + p.nattrib = nattrib; + } - p.xpos = xpos; -} + xpos = xpos + tile_len; + } while (xpos < xend); -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; + p.xpos = xpos; } - if (xpos < 8) { - uint_least32_t prebuf[16]; + void doFullTilesUnrolled(PPUPriv& p) { + int xpos = p.xpos; + int const xend = p.wx < p.xpos || p.wx >= xpos_end + ? lcd_hres + 1 + : static_cast(p.wx) - 7; + if (xpos >= xend) + return; - 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); + 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 + tile_map_size / lcdc_wtmsel * (p.lcdc & lcdc_wtmsel) + + tile_map_len / tile_len * (p.winYPos & (0x100 - tile_len)) + + tile_map_begin; + tileMapXpos = (xpos + p.wscx) / (1u * tile_len); + tileline = p.winYPos % tile_len; + } + else { + tileMapLine = p.vram + tile_map_size / lcdc_bgtmsel * (p.lcdc & lcdc_bgtmsel) + + tile_map_len / tile_len * ((p.scy + p.lyCounter.ly()) & (0x100 - tile_len)) + + tile_map_begin; + tileMapXpos = (p.scx + xpos + 1 - p.cgb) / (1u * tile_len); + tileline = (p.scy + p.lyCounter.ly()) % tile_len; } - int const newxpos = p.xpos; + if (xpos < tile_len) { + uint_least32_t prebuf[2 * tile_len]; + if (p.cgb) { + doFullTilesUnrolledCgb(p, std::min(tile_len, xend), prebuf + (tile_len - xpos), + tileMapLine, tileline, tileMapXpos); + } + else { + doFullTilesUnrolledDmg(p, std::min(tile_len, xend), prebuf + (tile_len - xpos), + tileMapLine, tileline, tileMapXpos); + } - if (newxpos > 8) { - std::memcpy(dbufline, prebuf + (8 - xpos), (newxpos - 8) * sizeof *dbufline); - } else if (newxpos < 8) - return; + int const newxpos = p.xpos; + if (newxpos > tile_len) { + std::memcpy(dbufline, prebuf + (tile_len - xpos), (newxpos - tile_len) * sizeof * dbufline); + } + else if (newxpos < tile_len) + return; - if (newxpos >= xend) - return; + if (newxpos >= xend) + return; - tileMapXpos += (newxpos - xpos) >> 3; + tileMapXpos += (newxpos - xpos) / (1u * tile_len); + } + + p.cgb + ? doFullTilesUnrolledCgb(p, xend, dbufline, tileMapLine, tileline, tileMapXpos) + : doFullTilesUnrolledDmg(p, xend, dbufline, tileMapLine, tileline, tileMapXpos); } - if (p.cgb) { - doFullTilesUnrolledCgb(p, xend, dbufline, tileMapLine, tileline, tileMapXpos); - } else - doFullTilesUnrolledDmg(p, xend, dbufline, tileMapLine, tileline, tileMapXpos); -} + void plotPixel(PPUPriv& p) { + int const xpos = p.xpos; + unsigned const tileword = p.tileword; -static void plotPixel(PPUPriv &p) { - int const xpos = p.xpos; - unsigned const tileword = p.tileword; - uint_least32_t *const fbline = p.framebuf.fbline(); + uint_least32_t* const fbline = p.framebuf.fbline(); - if (static_cast(p.wx) == xpos + if (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; - } - - 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) { - 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 && int(p.spriteList[i].spx) > xpos - 8); - - if (spdata && lcdcObjEn(p) - && (!((attrib | p.attrib) & attr_bgpriority) || !twdata || !lcdcBgEn(p))) { - pixel = p.spPalette[(attrib & 7) * 4 + spdata]; + && xpos < lcd_hres + 7) { + if (p.winDrawState == 0 && lcdcWinEn(p)) { + p.winDrawState = win_draw_start | win_draw_started; + ++p.winYPos; } - } 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]; + else if (!p.cgb && (p.winDrawState == 0 || xpos == lcd_hres + 6)) + p.winDrawState |= win_draw_start; } + + unsigned const twdata = tileword & ((p.lcdc & lcdc_bgen) | p.cgb) * tile_bpp_mask; + unsigned long pixel = p.bgPalette[twdata + (p.attrib & attr_cgbpalno) * num_palette_entries]; + int i = static_cast(p.nextSprite) - 1; + + if (i >= 0 && spx(p.spriteList[i]) > xpos - tile_len) { + unsigned spdata = 0; + unsigned attrib = 0; + + if (p.cgb) { + unsigned minId = 0xFF; + + do { + if ((p.spwordList[i] & tile_bpp_mask) && p.spriteList[i].oampos < minId) { + spdata = p.spwordList[i] & tile_bpp_mask; + attrib = p.spriteList[i].attrib; + minId = p.spriteList[i].oampos; + } + + p.spwordList[i] >>= tile_bpp; + --i; + } while (i >= 0 && spx(p.spriteList[i]) > xpos - tile_len); + + if (spdata && lcdcObjEn(p) + && (!((attrib | p.attrib) & attr_bgpriority) || !twdata || !lcdcBgEn(p))) { + pixel = *(cgbSpPalette(p, attrib) + spdata); + } + } + else { + do { + if (p.spwordList[i] & tile_bpp_mask) { + spdata = p.spwordList[i] & tile_bpp_mask; + attrib = p.spriteList[i].attrib; + } + + p.spwordList[i] >>= tile_bpp; + --i; + } while (i >= 0 && spx(p.spriteList[i]) > xpos - tile_len); + + if (spdata && lcdcObjEn(p) && (!(attrib & attr_bgpriority) || !twdata)) + pixel = p.spPalette[(attrib & attr_dmgpalno ? num_palette_entries : 0) + spdata]; + } + } + + if (xpos - tile_len >= 0) + fbline[xpos - tile_len] = pixel; + + + p.xpos = xpos + 1; + p.tileword = tileword >> tile_bpp; } - 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 (!(lcdcObjEn(p) | p.cgb)) { @@ -866,33 +934,29 @@ static void plotPixelIfNoSprite(PPUPriv &p) { 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; +unsigned long nextM2Time(PPUPriv const& p) { + int const nm2 = p.lyCounter.ly() < lcd_vres - 1 + ? weMasterCheckPriorToLyIncLineCycle(p.cgb) + : lcd_cycles_per_line * (lcd_lines_per_frame - p.lyCounter.ly()) + + weMasterCheckLy0LineCycle(p.cgb); + return p.lyCounter.time() - p.lyCounter.lineTime() + (nm2 << p.lyCounter.isDoubleSpeed()); } -static void xpos168(PPUPriv &p) { +void xposEnd(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_cast((p.now - nextm2) >> p.lyCounter.isDoubleSpeed()) + : -static_cast((nextm2 - p.now) >> p.lyCounter.isDoubleSpeed()); + nextCall(0, p.lyCounter.ly() == lcd_vres - 1 ? 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); +bool handleWinDrawStartReq(PPUPriv const &p, int const xpos, unsigned char &winDrawState) { + bool const startWinDraw = (xpos < lcd_hres + 7 || p.cgb) + && (winDrawState &= win_draw_started); if (!lcdcWinEn(p)) - winDrawState &= ~win_draw_started; + winDrawState &= ~(1u * win_draw_started); return startWinDraw; } @@ -902,15 +966,16 @@ static bool handleWinDrawStartReq(PPUPriv &p) { } namespace StartWindowDraw { - static void inc(PPUState const &nextf, PPUPriv &p) { + void inc(PPUState const& nextf, PPUPriv& p) { if (!lcdcWinEn(p) && p.cgb) { plotPixelIfNoSprite(p); if (p.xpos == p.endx) { - if (p.xpos < 168) { + if (p.xpos < xpos_end) { nextCall(1, Tile::f0_, p); - } else - xpos168(p); + } + else + xposEnd(p); return; } @@ -919,76 +984,83 @@ namespace StartWindowDraw { nextCall(1, nextf, p); } - static void f0(PPUPriv &p) { + 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.attrib = p.nattrib; + p.endx = std::min(1u * xpos_end, p.xpos + 1u * tile_len); } - p.wscx = 8 - p.xpos; + p.wscx = tile_len - 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]; + p.reg1 = p.vram[tile_map_size / lcdc_wtmsel * (p.lcdc & lcdc_wtmsel) + + tile_map_len / tile_len * (p.winYPos & (0x100 - tile_len)) + + tile_map_begin]; + p.nattrib = p.vram[tile_map_size / lcdc_wtmsel * (p.lcdc & lcdc_wtmsel) + + tile_map_len / tile_len * (p.winYPos & (0x100 - tile_len)) + + tile_map_begin + vram_bank_size]; + } + else { + p.reg1 = p.vram[tile_map_size / lcdc_bgtmsel * (p.lcdc & lcdc_bgtmsel) + + tile_map_len / tile_len * ((p.scy + p.lyCounter.ly()) & (0x100 - tile_len)) + + tile_map_begin]; + p.nattrib = p.vram[tile_map_size / lcdc_bgtmsel * (p.lcdc & lcdc_bgtmsel) + + tile_map_len / tile_len * ((p.scy + p.lyCounter.ly()) & (0x100 - tile_len)) + + tile_map_begin + vram_bank_size]; } inc(f1_, p); } - static void f1(PPUPriv &p) { + void f1(PPUPriv& p) { inc(f2_, p); } - static void f2(PPUPriv &p) { + void f2(PPUPriv& p) { p.reg0 = loadTileDataByte0(p); inc(f3_, p); } - static void f3(PPUPriv &p) { + void f3(PPUPriv& p) { inc(f4_, p); } - static void f4(PPUPriv &p) { + 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; + p.ntileword = (expand_lut + (0x100 / attr_xflip * p.nattrib & 0x100))[p.reg0] + + (expand_lut + (0x100 / attr_xflip * p.nattrib & 0x100))[r1] * 2; inc(f5_, p); } - static void f5(PPUPriv &p) { + void f5(PPUPriv& p) { inc(Tile::f0_, p); } } namespace LoadSprites { - static void inc(PPUState const &nextf, PPUPriv &p) { + void inc(PPUState const& nextf, PPUPriv& p) { plotPixelIfNoSprite(p); if (p.xpos == p.endx) { - if (p.xpos < 168) { + if (p.xpos < xpos_end) { nextCall(1, Tile::f0_, p); - } else - xpos168(p); - } else + } + else + xposEnd(p); + } + else nextCall(1, nextf, p); } - static void f0(PPUPriv &p) { + void f0(PPUPriv& p) { p.reg1 = p.spriteMapper.oamram()[p.spriteList[p.currentSprite].oampos + 2]; nextCall(1, f1_, p); } - static void f1(PPUPriv &p) { + void f1(PPUPriv& p) { if ((p.winDrawState & win_draw_start) && handleWinDrawStartReq(p)) return StartWindowDraw::f0(p); @@ -997,40 +1069,44 @@ namespace LoadSprites { inc(f2_, p); } - static void f2(PPUPriv &p) { + 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))]; + (p.spriteList[p.currentSprite].attrib & attr_yflip + ? p.spriteList[p.currentSprite].line ^ (2 * tile_len - 1) + : p.spriteList[p.currentSprite].line) * tile_line_size; + unsigned const ts = tile_size; + p.reg0 = p.vram[vram_bank_size / attr_tdbank + * (p.spriteList[p.currentSprite].attrib & p.cgb * attr_tdbank) + + (lcdcObj2x(p) ? (p.reg1 * ts & ~ts) | spline : p.reg1 * ts | (spline & ~ts))]; inc(f3_, p); } - static void f3(PPUPriv &p) { + void f3(PPUPriv& p) { if ((p.winDrawState & win_draw_start) && handleWinDrawStartReq(p)) return StartWindowDraw::f0(p); inc(f4_, p); } - static void f4(PPUPriv &p) { + 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]; + (p.spriteList[p.currentSprite].attrib & attr_yflip + ? p.spriteList[p.currentSprite].line ^ (2 * tile_len - 1) + : p.spriteList[p.currentSprite].line) * tile_line_size; + unsigned const ts = tile_size; + p.reg1 = p.vram[vram_bank_size / attr_tdbank + * (p.spriteList[p.currentSprite].attrib & p.cgb * attr_tdbank) + + (lcdcObj2x(p) ? (p.reg1 * ts & ~ts) | spline : p.reg1 * ts | (spline & ~ts)) + 1]; inc(f5_, p); } - static void f5(PPUPriv &p) { + void f5(PPUPriv& p) { if ((p.winDrawState & win_draw_start) && handleWinDrawStartReq(p)) return StartWindowDraw::f0(p); @@ -1040,21 +1116,25 @@ namespace LoadSprites { if (entry == p.nextSprite) { ++p.nextSprite; - } else { + } + 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.spwordList[entry] = + expand_lut[p.reg0 + (0x100 / attr_xflip * p.spriteList[entry].attrib & 0x100)] + + expand_lut[p.reg1 + (0x100 / attr_xflip * p.spriteList[entry].attrib & 0x100)] * 2; p.spriteList[entry].spx = p.xpos; if (p.xpos == p.endx) { - if (p.xpos < 168) { + if (p.xpos < xpos_end) { nextCall(1, Tile::f0_, p); - } else - xpos168(p); - } else { + } + else + xposEnd(p); + } + else { p.nextCallPtr = &Tile::f5_; nextCall(1, Tile::f5_, p); } @@ -1062,55 +1142,62 @@ namespace LoadSprites { } namespace Tile { - static void inc(PPUState const &nextf, PPUPriv &p) { + void inc(PPUState const& nextf, PPUPriv& p) { plotPixelIfNoSprite(p); - if (p.xpos == 168) { - xpos168(p); - } else + if (p.xpos == xpos_end) { + xposEnd(p); + } + else nextCall(1, nextf, p); } - static void f0(PPUPriv &p) { + void f0(PPUPriv& p) { if ((p.winDrawState & win_draw_start) && handleWinDrawStartReq(p)) return StartWindowDraw::f0(p); doFullTilesUnrolled(p); - if (p.xpos == 168) { + if (p.xpos == xpos_end) { ++p.cycles; - return xpos168(p); + return xposEnd(p); } p.tileword = p.ntileword; - p.attrib = p.nattrib; - p.endx = p.xpos < 160 ? p.xpos + 8 : 168; + p.attrib = p.nattrib; + p.endx = std::min(1u * xpos_end, p.xpos + 1u * tile_len); 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]; + p.reg1 = p.vram[tile_map_size / lcdc_wtmsel * (p.lcdc & lcdc_wtmsel) + + tile_map_len / tile_len * (p.winYPos & (0x100 - tile_len)) + + (p.xpos + p.wscx) / tile_len % tile_map_len + tile_map_begin]; + p.nattrib = p.vram[tile_map_size / lcdc_wtmsel * (p.lcdc & lcdc_wtmsel) + + tile_map_len / tile_len * (p.winYPos & (0x100 - tile_len)) + + (p.xpos + p.wscx) / tile_len % tile_map_len + tile_map_begin + + vram_bank_size]; + } + else { + p.reg1 = p.vram[((tile_map_size / lcdc_bgtmsel * p.lcdc | (p.scx + p.xpos + 1u - p.cgb) / tile_len) + & (tile_map_size + tile_map_len - 1)) + + tile_map_len / tile_len * ((p.scy + p.lyCounter.ly()) & (0x100 - tile_len)) + + tile_map_begin]; + p.nattrib = p.vram[((tile_map_size / lcdc_bgtmsel * p.lcdc | (p.scx + p.xpos + 1u - p.cgb) / tile_len) + & (tile_map_size + tile_map_len - 1)) + + tile_map_len / tile_len * ((p.scy + p.lyCounter.ly()) & (0x100 - tile_len)) + + tile_map_begin + vram_bank_size]; } inc(f1_, p); } - static void f1(PPUPriv &p) { + void f1(PPUPriv& p) { if ((p.winDrawState & win_draw_start) && handleWinDrawStartReq(p)) return StartWindowDraw::f0(p); inc(f2_, p); } - static void f2(PPUPriv &p) { + void f2(PPUPriv& p) { if ((p.winDrawState & win_draw_start) && handleWinDrawStartReq(p)) return StartWindowDraw::f0(p); @@ -1118,31 +1205,32 @@ namespace Tile { inc(f3_, p); } - static void f3(PPUPriv &p) { + void f3(PPUPriv& p) { if ((p.winDrawState & win_draw_start) && handleWinDrawStartReq(p)) return StartWindowDraw::f0(p); inc(f4_, p); } - static void f4(PPUPriv &p) { + 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; + p.ntileword = (expand_lut + (0x100 / attr_xflip * p.nattrib & 0x100))[p.reg0] + + (expand_lut + (0x100 / attr_xflip * p.nattrib & 0x100))[r1] * 2; plotPixelIfNoSprite(p); - if (p.xpos == 168) { - xpos168(p); - } else + if (p.xpos == xpos_end) { + xposEnd(p); + } + else nextCall(1, f5_, p); } - static void f5(PPUPriv &p) { + void f5(PPUPriv& p) { int endx = p.endx; p.nextCallPtr = &f5_; @@ -1164,10 +1252,11 @@ namespace Tile { plotPixel(p); if (p.xpos == endx) { - if (endx < 168) { + if (endx < xpos_end) { nextCall(1, f0_, p); - } else - xpos168(p); + } + else + xposEnd(p); return; } @@ -1179,29 +1268,30 @@ namespace Tile { namespace M2_Ly0 { static unsigned predictCyclesUntilXpos_f0(PPUPriv const &p, unsigned winDrawState, - int targetxpos, unsigned cycles); + int targetxpos, unsigned cycles); } namespace M2_LyNon0 { static unsigned predictCyclesUntilXpos_f0(PPUPriv const &p, unsigned winDrawState, - int targetxpos, unsigned cycles); + int targetxpos, unsigned cycles); } namespace M3Loop { -static unsigned predictCyclesUntilXposNextLine( - PPUPriv const &p, unsigned winDrawState, int const targetx) { - if (p.wx == 166 && !p.cgb && p.xpos < 167 + unsigned predictCyclesUntilXposNextLine( + PPUPriv const& p, unsigned winDrawState, int const targetx) { + if (p.wx == lcd_hres + 6 && !p.cgb && p.xpos < lcd_hres + 7 && (p.weMaster || (p.wy2 == p.lyCounter.ly() && lcdcWinEn(p)))) { - winDrawState = win_draw_start | (lcdcWinEn(p) ? win_draw_started : 0); + winDrawState = win_draw_start | (lcdcWinEn(p) ? win_draw_started : 0); + } + + unsigned const cycles = (nextM2Time(p) - p.now) >> p.lyCounter.isDoubleSpeed(); + + return p.lyCounter.ly() == lcd_vres - 1 + ? M2_Ly0::predictCyclesUntilXpos_f0(p, winDrawState, targetx, cycles) + : M2_LyNon0::predictCyclesUntilXpos_f0(p, winDrawState, targetx, cycles); } - unsigned const 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(PPUPriv const &p, int xpos, @@ -1210,23 +1300,22 @@ namespace StartWindowDraw { } 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; + 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) { + int sum = 0; - while (nextSprite < spriteEnd && spxOf[*nextSprite] <= maxSpx) { - unsigned cycles = 6; - unsigned const distanceFromTileStart = (spxOf[*nextSprite] - firstTileXpos) & 7; - unsigned const tileNo = (spxOf[*nextSprite] - firstTileXpos) & ~7; + for (; nextSprite < spriteEnd && spxOf[*nextSprite] <= maxSpx; ++nextSprite) { + int cycles = 6; + int const distanceFromTileStart = (spxOf[*nextSprite] - firstTileXpos) % tile_len; + unsigned const tileNo = (spxOf[*nextSprite] - firstTileXpos) & -tile_len; if (distanceFromTileStart < 5 && tileNo != prevSpriteTileNo) cycles = 11 - distanceFromTileStart; prevSpriteTileNo = tileNo; sum += cycles; - ++nextSprite; } *cyclesAccumulator += sum; @@ -1234,12 +1323,12 @@ namespace Tile { return nextSprite; } - 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) { + 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)) { + && handleWinDrawStartReq(p, xpos, winDrawState)) { return StartWindowDraw::predictCyclesUntilXpos_fn(p, xpos, endx, ly, nextSprite, weMaster, winDrawState, 0, targetx, cycles); } @@ -1247,31 +1336,31 @@ namespace Tile { 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. + enum { tileno_none = 1 }; // low bit set, so it will never be equal to an actual tile number. int nwx = 0xFF; cycles += targetx - xpos; - if (p.wx - unsigned(xpos) < targetx - unsigned(xpos) - && lcdcWinEn(p) && (weMaster || p.wy2 == ly) - && !(winDrawState & win_draw_started) - && (p.cgb || p.wx != 166)) { + if (p.wx - 1u * xpos < targetx - 1u * xpos + && lcdcWinEn(p) && (weMaster || p.wy2 == ly) + && !(winDrawState & win_draw_started) + && (p.cgb || p.wx != lcd_hres + 6)) { nwx = p.wx; cycles += 6; } if (lcdcObjEn(p) | p.cgb) { - unsigned char const *sprite = p.spriteMapper.sprites(ly); - unsigned char const *const spriteEnd = sprite + p.spriteMapper.numSprites(ly); + unsigned char const* sprite = p.spriteMapper.sprites(ly); + unsigned char const* const spriteEnd = sprite + p.spriteMapper.numSprites(ly); sprite += nextSprite; 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 + unsigned firstTileXpos = endx % (1u * tile_len); // ok even if endx is capped at 168, + // because fno will be used. + unsigned prevSpriteTileNo = (xpos - firstTileXpos) & -tile_len; // 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; @@ -1279,56 +1368,56 @@ namespace Tile { if (nwx < targetx) { sprite = addSpriteCycles(sprite, spriteEnd, p.spriteMapper.posbuf() + 1, - nwx, firstTileXpos, prevSpriteTileNo, &cycles); + nwx, firstTileXpos, prevSpriteTileNo, &cycles); firstTileXpos = nwx + 1; - prevSpriteTileNo = NO_TILE_NUMBER; + prevSpriteTileNo = tileno_none; } addSpriteCycles(sprite, spriteEnd, p.spriteMapper.posbuf() + 1, - targetx, firstTileXpos, prevSpriteTileNo, &cycles); + targetx, firstTileXpos, prevSpriteTileNo, &cycles); } } return cycles; } - static unsigned predictCyclesUntilXpos_fn(PPUPriv const &p, - int endx, int fno, int targetx, unsigned cycles) { + 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) { - return predictCyclesUntilXpos_fn(p, p.xpos < 160 ? p.xpos + 8 : 168, 0, targetx, cycles); + unsigned predictCyclesUntilXpos_f0(PPUPriv const& p, int targetx, unsigned cycles) { + return predictCyclesUntilXpos_fn(p, std::min(1u * xpos_end, p.xpos + 1u * tile_len), 0, targetx, cycles); } - static unsigned predictCyclesUntilXpos_f1(PPUPriv const &p, int targetx, unsigned cycles) { + 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) { + 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) { + 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) { + 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) { + unsigned predictCyclesUntilXpos_f5(PPUPriv const& p, int targetx, unsigned cycles) { return predictCyclesUntilXpos_fn(p, p.endx, 5, targetx, cycles); } } namespace StartWindowDraw { - static unsigned predictCyclesUntilXpos_fn(PPUPriv const &p, int xpos, + 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; + int cinc = 6 - fno; if (!lcdcWinEn(p) && p.cgb) { - unsigned xinc = std::min(cinc, std::min(endx, targetx + 1) - xpos); + int 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; @@ -1341,44 +1430,44 @@ namespace StartWindowDraw { cycles += cinc; if (xpos <= targetx) { - return Tile::predictCyclesUntilXpos_fn(p, xpos, xpos < 160 ? xpos + 8 : 168, + return Tile::predictCyclesUntilXpos_fn(p, xpos, std::min(xpos_end, xpos + tile_len), ly, nextSprite, weMaster, winDrawState, 0, targetx, cycles); } return cycles - 1; } - static unsigned predictCyclesUntilXpos_fn(PPUPriv const &p, + 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) { + 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; + ? std::min(1u * xpos_end, p.xpos + 1u * tile_len) + : p.endx; return predictCyclesUntilXpos_fn(p, endx, 0, targetx, cycles); } - static unsigned predictCyclesUntilXpos_f1(PPUPriv const &p, int targetx, unsigned cycles) { + 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) { + 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) { + 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) { + 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) { + 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, + unsigned predictCyclesUntilXpos_fn(PPUPriv const &p, int const fno, int const targetx, unsigned cycles) { unsigned nextSprite = p.nextSprite; if (lcdcObjEn(p) | p.cgb) { @@ -1390,22 +1479,22 @@ namespace LoadSprites { nextSprite, p.weMaster, p.winDrawState, 5, targetx, cycles); } - static unsigned predictCyclesUntilXpos_f0(PPUPriv const &p, int targetx, unsigned cycles) { + 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) { + 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) { + 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) { + 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) { + 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) { + unsigned predictCyclesUntilXpos_f5(PPUPriv const &p, int targetx, unsigned cycles) { return predictCyclesUntilXpos_fn(p, 5, targetx, cycles); } } @@ -1413,82 +1502,81 @@ namespace LoadSprites { } // namespace M3Loop namespace M3Start { - static unsigned predictCyclesUntilXpos_f1(PPUPriv const &p, unsigned xpos, unsigned ly, - bool weMaster, unsigned winDrawState, int targetx, unsigned cycles) { - 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); + unsigned predictCyclesUntilXpos_f1(PPUPriv const& p, unsigned xpos, unsigned ly, + bool weMaster, unsigned winDrawState, int targetx, unsigned cycles) { + cycles += std::min((p.scx - xpos) % tile_len, max_m3start_cycles - xpos) + 1 - p.cgb; + return M3Loop::Tile::predictCyclesUntilXpos_fn(p, 0, tile_len - p.scx % tile_len, ly, 0, + weMaster, winDrawState, std::min(p.scx % (1u * tile_len), 5u), targetx, cycles); } - static unsigned predictCyclesUntilXpos_f0(PPUPriv const &p, unsigned ly, + 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(PPUPriv const &p, int targetx, unsigned cycles) { + 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(PPUPriv const &p, int targetx, unsigned cycles) { + 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_Ly0 { - static unsigned predictCyclesUntilXpos_f0(PPUPriv const &p, - unsigned winDrawState, int targetx, unsigned cycles) { + 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) - weMasterCheckLy0LineCycle(p.cgb)); } - static unsigned predictCyclesUntilXpos_f0(PPUPriv const &p, int targetx, unsigned cycles) { + unsigned predictCyclesUntilXpos_f0(PPUPriv const& p, int targetx, unsigned cycles) { return predictCyclesUntilXpos_f0(p, p.winDrawState, targetx, cycles); } } namespace M2_LyNon0 { - static unsigned predictCyclesUntilXpos_f1(PPUPriv const &p, bool weMaster, - unsigned winDrawState, int targetx, unsigned cycles) { + 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)); + cycles + lcd_cycles_per_line - weMasterCheckAfterLyIncLineCycle(p.cgb) + m3StartLineCycle(p.cgb)); } - static unsigned predictCyclesUntilXpos_f1(PPUPriv const &p, int targetx, unsigned cycles) { + unsigned predictCyclesUntilXpos_f1(PPUPriv const& p, int targetx, unsigned cycles) { return predictCyclesUntilXpos_f1(p, p.weMaster, p.winDrawState, targetx, cycles); } - static unsigned predictCyclesUntilXpos_f0(PPUPriv const &p, - unsigned winDrawState, int targetx, unsigned cycles) { + 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)); + - weMasterCheckPriorToLyIncLineCycle(p.cgb)); } - static unsigned predictCyclesUntilXpos_f0(PPUPriv const &p, int targetx, unsigned cycles) { + unsigned predictCyclesUntilXpos_f0(PPUPriv const& p, int targetx, unsigned cycles) { return predictCyclesUntilXpos_f0(p, p.winDrawState, targetx, cycles); } } } // anon namespace -namespace gambatte { - PPUPriv::PPUPriv(NextM0Time &nextM0Time, unsigned char const *const oamram, unsigned char const *const vram) -: nextSprite(0) +: spriteList() +, spwordList() +, nextSprite(0) , currentSprite(0xFF) -, layersMask(layer_mask_bg | layer_mask_window | layer_mask_obj) , vram(vram) , nextCallPtr(&M2_Ly0::f0_) , now(0) @@ -1515,19 +1603,6 @@ PPUPriv::PPUPriv(NextM0Time &nextM0Time, unsigned char const *const oamram, unsi , 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 { @@ -1560,7 +1635,7 @@ struct CycleState { operator long() const { return cycle; } }; -static PPUState const * decodeM3LoopState(unsigned state) { +PPUState const * decodeM3LoopState(unsigned state) { switch (state) { case M3Loop::Tile::ID0: return &M3Loop::Tile::f0_; case M3Loop::Tile::ID1: return &M3Loop::Tile::f1_; @@ -1587,57 +1662,65 @@ static PPUState const * decodeM3LoopState(unsigned state) { return 0; } -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) +long cyclesUntilM0Upperbound(PPUPriv const& p) { + long cycles = xpos_end - p.xpos + 6; + for (int i = p.nextSprite; i < lcd_max_num_sprites_per_line && p.spriteList[i].spx < xpos_end; ++i) cycles += 11; return cycles; } -static void loadSpriteList(PPUPriv &p, SaveState const &ss) { - if (ss.ppu.videoCycles < 144 * 456UL && ss.ppu.xpos < 168) { - unsigned const ly = ss.ppu.videoCycles / 456; - unsigned const numSprites = p.spriteMapper.numSprites(ly); - unsigned char const *const sprites = p.spriteMapper.sprites(ly); +void saveSpriteList(PPUPriv const& p, SaveState& ss) { + for (int i = 0; i < lcd_max_num_sprites_per_line; ++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; + } - for (unsigned i = 0; i < numSprites; ++i) { - unsigned pos = sprites[i]; - unsigned spy = p.spriteMapper.posbuf()[pos ]; - unsigned spx = p.spriteMapper.posbuf()[pos+1]; + ss.ppu.nextSprite = p.nextSprite; + ss.ppu.currentSprite = p.currentSprite; +} - p.spriteList[i].spx = spx; - p.spriteList[i].line = ly + 16u - spy; +void loadSpriteList(PPUPriv& p, SaveState const& ss) { + if (ss.ppu.videoCycles < 1ul * lcd_vres * lcd_cycles_per_line && ss.ppu.xpos < xpos_end) { + int const ly = ss.ppu.videoCycles / lcd_cycles_per_line; + int const numSprites = p.spriteMapper.numSprites(ly); + unsigned char const* const sprites = p.spriteMapper.sprites(ly); + + for (int i = 0; i < numSprites; ++i) { + int const pos = sprites[i]; + int const spy = p.spriteMapper.posbuf()[pos]; + int const spx = p.spriteMapper.posbuf()[pos + 1]; + + p.spriteList[i].spx = spx; + p.spriteList[i].line = ly + 2 * tile_len - spy; p.spriteList[i].oampos = pos * 2; p.spriteList[i].attrib = ss.ppu.spAttribList[i] & 0xFF; - p.spwordList[i] = (ss.ppu.spByte1List[i] * 0x100 + ss.ppu.spByte0List[i]) & 0xFFFF; + p.spwordList[i] = (ss.ppu.spByte1List[i] * 0x100l + ss.ppu.spByte0List[i]) & 0xFFFF; } p.spriteList[numSprites].spx = 0xFF; - p.nextSprite = std::min(ss.ppu.nextSprite, numSprites); + p.nextSprite = std::min(1u * ss.ppu.nextSprite, 1u * numSprites); while (p.spriteList[p.nextSprite].spx < ss.ppu.xpos) ++p.nextSprite; - p.currentSprite = std::min(p.nextSprite, ss.ppu.currentSprite); + p.currentSprite = std::min(p.nextSprite, ss.ppu.currentSprite); } } } -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); +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, lcd_cycles_per_frame - 1ul); 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; + long const lineCycles = static_cast(videoCycles) % lcd_cycles_per_line; p_.now = ss.cpu.cycleCounter; p_.lcdc = ss.mem.ioamhram.get()[0x140]; p_.lyCounter.setDoubleSpeed(ds); - p_.lyCounter.reset(std::min(ss.ppu.videoCycles, 70223ul), ss.cpu.cycleCounter); + p_.lyCounter.reset(videoCycles, ss.cpu.cycleCounter); p_.spriteMapper.loadState(ss, oamram); p_.winYPos = ss.ppu.winYPos; p_.scy = ss.mem.ioamhram.get()[0x142]; @@ -1645,9 +1728,9 @@ void PPU::loadState(SaveState const &ss, unsigned char const *const oamram) { p_.wy = ss.mem.ioamhram.get()[0x14A]; p_.wy2 = ss.ppu.oldWy; p_.wx = ss.mem.ioamhram.get()[0x14B]; - p_.xpos = std::min(ss.ppu.xpos, 168); - p_.endx = (p_.xpos & ~7) + (ss.ppu.endx & 7); - p_.endx = std::min(p_.endx <= p_.xpos ? p_.endx + 8 : p_.endx, 168); + p_.xpos = std::min(1u * xpos_end, 1u * ss.ppu.xpos); + p_.endx = (p_.xpos & -1u * tile_len) + ss.ppu.endx % tile_len; + p_.endx = std::min(1u * xpos_end, p_.endx <= p_.xpos ? p_.endx + 1u * tile_len : p_.endx); p_.reg0 = ss.ppu.reg0 & 0xFF; p_.reg1 = ss.ppu.reg1 & 0xFF; p_.tileword = ss.ppu.tileword & 0xFFFF; @@ -1658,34 +1741,36 @@ void PPU::loadState(SaveState const &ss, unsigned char const *const oamram) { p_.weMaster = ss.ppu.weMaster; p_.winDrawState = ss.ppu.winDrawState & (win_draw_start | win_draw_started); p_.lastM0Time = p_.now - ss.ppu.lastM0Time; - p_.cgb = ss.ppu.isCgb; + p_.cgbDmg = !ss.ppu.notCgbDmg; loadSpriteList(p_, ss); - if (m3loopState && videoCycles < 144 * 456L && p_.xpos < 168 - && lineCycles + cyclesUntilM0Upperbound(p_) < weMasterCheckPriorToLyIncLineCycle(p_.cgb)) { + if (m3loopState && videoCycles < 1l * lcd_vres * lcd_cycles_per_line && p_.xpos < xpos_end + && lineCycles + cyclesUntilM0Upperbound(p_) < weMasterCheckPriorToLyIncLineCycle(p_.cgb)) { p_.nextCallPtr = m3loopState; p_.cycles = -1; - } else if (vcycs < 143 * 456L + static_cast(m3StartLineCycle(p_.cgb)) + max_m3start_cycles) { + } + else if (videoCycles < (lcd_vres - 1l) * lcd_cycles_per_line + 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 } + { &M3Start::f0_, m3StartLineCycle(p_.cgb) + lcd_cycles_per_line } }; std::size_t const pos = - upperBound(lineCycleStates, lineCycles); + 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_.xpos = lineCycles - m3StartLineCycle(p_.cgb) + 1; p_.cycles = -1; } - } else { - p_.cycles = vcycs - 70224; + } + else { + p_.cycles = videoCycles - lcd_cycles_per_frame - weMasterCheckLy0LineCycle(p_.cgb); p_.nextCallPtr = &M2_Ly0::f0_; } } @@ -1693,6 +1778,7 @@ void PPU::loadState(SaveState const &ss, unsigned char const *const oamram) { void PPU::reset(unsigned char const *oamram, unsigned char const *vram, bool cgb) { p_.vram = vram; p_.cgb = cgb; + p_.cgbDmg = false; p_.spriteMapper.reset(oamram, cgb); } @@ -1706,20 +1792,14 @@ void PPU::resetCc(unsigned long const oldCc, unsigned long const newCc) { p_.spriteMapper.resetCycleCounter(oldCc, newCc); } -void PPU::speedChange(unsigned long const cycleCounter) { - unsigned long const videoCycles = lcdcEn(p_) ? p_.lyCounter.frameCycles(p_.now) : 0; +void PPU::speedChange() { + unsigned long const now = p_.now; + unsigned long const videoCycles = lcdcEn(p_) ? p_.lyCounter.frameCycles(now) : 0; - p_.spriteMapper.preSpeedChange(cycleCounter); + p_.now -= p_.lyCounter.isDoubleSpeed(); + p_.spriteMapper.resetCycleCounter(now, p_.now); 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; - } else - p_.cycles += m2_ds_offset; - } } unsigned long PPU::predictedNextXposTime(unsigned xpos) const { @@ -1736,12 +1816,14 @@ void PPU::setLcdc(unsigned const lcdc, unsigned long const cc) { 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) & lcdc_we) { + p_.cycles = -(m3StartLineCycle(p_.cgb) + 2); + } + 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) { + if (p_.winDrawState == win_draw_started || p_.xpos == xpos_end) + p_.winDrawState &= ~(1u * win_draw_started); + } + else if (p_.winDrawState == win_draw_start) { p_.winDrawState |= win_draw_started; ++p_.winYPos; } @@ -1758,7 +1840,7 @@ void PPU::setLcdc(unsigned const lcdc, unsigned long const cc) { } void PPU::update(unsigned long const cc) { - int const cycles = (cc - p_.now) >> p_.lyCounter.isDoubleSpeed(); + long const cycles = (cc - p_.now) >> p_.lyCounter.isDoubleSpeed(); p_.now += cycles << p_.lyCounter.isDoubleSpeed(); p_.cycles += cycles; @@ -1777,6 +1859,7 @@ SYNCFUNC(PPU) NSS(p_.spwordList); NSS(p_.nextSprite); NSS(p_.currentSprite); + NSS(p_.layersMask); EBS(p_.nextCallPtr, 0); EVS(p_.nextCallPtr, &M2_Ly0::f0_, 1); @@ -1831,7 +1914,6 @@ SYNCFUNC(PPU) NSS(p_.endx); NSS(p_.cgb); + NSS(p_.cgbDmg); NSS(p_.weMaster); } - -} diff --git a/libgambatte/src/video/ppu.h b/libgambatte/src/video/ppu.h index edfb6a9b9f..b711cea3dd 100644 --- a/libgambatte/src/video/ppu.h +++ b/libgambatte/src/video/ppu.h @@ -29,7 +29,14 @@ 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 }; +enum { + max_num_palettes = 8, + num_palette_entries = 4, + ppu_force_signed_enum = -1 }; class PPUFrameBuf { public: @@ -57,10 +64,10 @@ struct PPUState { }; struct PPUPriv { - unsigned long bgPalette[8 * 4]; - unsigned long spPalette[8 * 4]; - struct Sprite { unsigned char spx, oampos, line, attrib; } spriteList[11]; - unsigned short spwordList[11]; + unsigned long bgPalette[max_num_palettes * num_palette_entries]; + unsigned long spPalette[max_num_palettes * num_palette_entries]; + struct Sprite { unsigned char spx, oampos, line, attrib; } spriteList[lcd_max_num_sprites_per_line + 1]; + unsigned short spwordList[lcd_max_num_sprites_per_line + 1]; unsigned char nextSprite; unsigned char currentSprite; unsigned layersMask; @@ -96,6 +103,7 @@ struct PPUPriv { unsigned char endx; bool cgb; + bool cgbDmg; bool weMaster; PPUPriv(NextM0Time &nextM0Time, unsigned char const *oamram, unsigned char const *vram); @@ -110,6 +118,7 @@ public: unsigned long * bgPalette() { return p_.bgPalette; } bool cgb() const { return p_.cgb; } + bool cgbDmg() const { return p_.cgbDmg; } void doLyCountEvent() { p_.lyCounter.doEvent(); } unsigned long doSpriteMapEvent(unsigned long time) { return p_.spriteMapper.doEvent(time); } PPUFrameBuf const & frameBuf() const { return p_.framebuf; } @@ -127,6 +136,7 @@ public: void oamChange(unsigned char const *oamram, unsigned long cc) { p_.spriteMapper.oamChange(oamram, cc); } unsigned long predictedNextXposTime(unsigned xpos) const; void reset(unsigned char const *oamram, unsigned char const *vram, bool cgb); + void setCgbDmg(bool enabled) { p_.cgbDmg = enabled; } void resetCc(unsigned long oldCc, unsigned long newCc); void setFrameBuf(uint_least32_t *buf, std::ptrdiff_t pitch) { p_.framebuf.setBuf(buf, pitch); } void setLcdc(unsigned lcdc, unsigned long cc); @@ -136,11 +146,10 @@ public: void setWx(unsigned wx) { p_.wx = wx; } void setWy(unsigned wy) { p_.wy = wy; } void updateWy2() { p_.wy2 = p_.wy; } - void speedChange(unsigned long cycleCounter); + void speedChange(); unsigned long * spPalette() { return p_.spPalette; } void update(unsigned long cc); void setLayers(unsigned mask) { p_.layersMask = mask; } - void setCgb(bool cgb) { p_.cgb = cgb; } private: PPUPriv p_; diff --git a/libgambatte/src/video/sprite_mapper.cpp b/libgambatte/src/video/sprite_mapper.cpp index 87ffa52759..2bc5cdcdb8 100644 --- a/libgambatte/src/video/sprite_mapper.cpp +++ b/libgambatte/src/video/sprite_mapper.cpp @@ -21,7 +21,8 @@ #include "next_m0_time.h" #include "../insertion_sort.h" #include -#include + +using namespace gambatte; namespace { @@ -37,9 +38,15 @@ private: unsigned char const *const spxlut_; }; +unsigned toPosCycles(unsigned long const cc, LyCounter const& lyCounter) { + unsigned lc = lyCounter.lineCycles(cc) + 1; + if (lc >= lcd_cycles_per_line) + lc -= lcd_cycles_per_line; + + return lc; } -namespace gambatte { +} SpriteMapper::OamReader::OamReader(LyCounter const &lyCounter, unsigned char const *oamram) : lyCounter_(lyCounter) @@ -49,45 +56,35 @@ SpriteMapper::OamReader::OamReader(LyCounter const &lyCounter, unsigned char con reset(oamram, false); } -void SpriteMapper::OamReader::reset(unsigned char const *const oamram, bool const cgb) { +void SpriteMapper::OamReader::reset(unsigned char const* const oamram, bool const cgb) { oamram_ = oamram; cgb_ = cgb; setLargeSpritesSrc(false); lu_ = 0; lastChange_ = 0xFF; - std::fill(szbuf_, szbuf_ + 40, largeSpritesSrc_); - - unsigned pos = 0; - unsigned distance = 80; - while (distance--) { - buf_[pos] = oamram[((pos * 2) & ~3) | (pos & 1)]; - ++pos; + std::fill_n(lsbuf_, sizeof lsbuf_ / sizeof * lsbuf_, largeSpritesSrc_); + for (int i = 0; i < lcd_num_oam_entries; ++i) { + buf_[2 * i] = oamram[4 * i]; + buf_[2 * i + 1] = oamram[4 * i + 1]; } } -static unsigned toPosCycles(unsigned long const cc, LyCounter const &lyCounter) { - unsigned lc = lyCounter.lineCycles(cc) + 3 - lyCounter.isDoubleSpeed() * 3u; - if (lc >= 456) - lc -= 456; - - return lc; -} - void SpriteMapper::OamReader::update(unsigned long const cc) { if (cc > lu_) { if (changed()) { unsigned const lulc = toPosCycles(lu_, lyCounter_); - unsigned pos = std::min(lulc, 80u); - unsigned distance = 80; + unsigned pos = std::min(lulc, 2u * lcd_num_oam_entries); + unsigned distance = 2 * lcd_num_oam_entries; - if ((cc - lu_) >> lyCounter_.isDoubleSpeed() < 456) { + if ((cc - lu_) >> lyCounter_.isDoubleSpeed() < lcd_cycles_per_line) { unsigned cclc = toPosCycles(cc, lyCounter_); - distance = std::min(cclc, 80u) - pos + (cclc < lulc ? 80 : 0); + distance = std::min(cclc, 2u * lcd_num_oam_entries) + - pos + (cclc < lulc ? 2 * lcd_num_oam_entries : 0); } { unsigned targetDistance = - lastChange_ - pos + (lastChange_ <= pos ? 80 : 0); + lastChange_ - pos + (lastChange_ <= pos ? 2 * lcd_num_oam_entries : 0); if (targetDistance <= distance) { distance = targetDistance; lastChange_ = 0xFF; @@ -96,16 +93,16 @@ void SpriteMapper::OamReader::update(unsigned long const cc) { while (distance--) { if (!(pos & 1)) { - if (pos == 80) + if (pos == 2 * lcd_num_oam_entries) pos = 0; - if (cgb_) - szbuf_[pos >> 1] = largeSpritesSrc_; + lsbuf_[pos / 2] = largeSpritesSrc_; - buf_[pos ] = oamram_[pos * 2 ]; - buf_[pos + 1] = oamram_[pos * 2 + 1]; - } else - szbuf_[pos >> 1] = (szbuf_[pos >> 1] & cgb_) | largeSpritesSrc_; + buf_[pos] = oamram_[2 * pos]; + buf_[pos + 1] = oamram_[2 * pos + 1]; + } + else + lsbuf_[pos / 2] = (lsbuf_[pos / 2] & cgb_) | largeSpritesSrc_; ++pos; } @@ -117,15 +114,15 @@ void SpriteMapper::OamReader::update(unsigned long const cc) { void SpriteMapper::OamReader::change(unsigned long cc) { update(cc); - lastChange_ = std::min(toPosCycles(lu_, lyCounter_), 80u); + lastChange_ = std::min(toPosCycles(lu_, lyCounter_), 2u * lcd_num_oam_entries); } -void SpriteMapper::OamReader::setStatePtrs(SaveState &state) { - state.ppu.oamReaderBuf.set(buf_, sizeof buf_); - state.ppu.oamReaderSzbuf.set(szbuf_, sizeof szbuf_ / sizeof *szbuf_); +void SpriteMapper::OamReader::setStatePtrs(SaveState& state) { + state.ppu.oamReaderBuf.set(buf_, sizeof buf_ / sizeof * buf_); + state.ppu.oamReaderSzbuf.set(lsbuf_, sizeof lsbuf_ / sizeof * lsbuf_); } -void SpriteMapper::OamReader::loadState(SaveState const &ss, unsigned char const *const oamram) { +void SpriteMapper::OamReader::loadState(SaveState const& ss, unsigned char const* const oamram) { oamram_ = oamram; largeSpritesSrc_ = ss.mem.ioamhram.get()[0x140] >> 2 & 1; lu_ = ss.ppu.enableDisplayM0Time; @@ -135,7 +132,7 @@ void SpriteMapper::OamReader::loadState(SaveState const &ss, unsigned char const SYNCFUNC(SpriteMapper::OamReader) { NSS(buf_); - NSS(szbuf_); + NSS(lsbuf_); NSS(lu_); NSS(lastChange_); @@ -144,10 +141,10 @@ SYNCFUNC(SpriteMapper::OamReader) } void SpriteMapper::OamReader::enableDisplay(unsigned long cc) { - std::memset(buf_, 0x00, sizeof buf_); - std::fill(szbuf_, szbuf_ + 40, false); - lu_ = cc + (80 << lyCounter_.isDoubleSpeed()); - lastChange_ = 80; + std::fill_n(buf_, sizeof buf_ / sizeof * buf_, 0); + std::fill_n(lsbuf_, sizeof lsbuf_ / sizeof * lsbuf_, false); + lu_ = cc + (2 * lcd_num_oam_entries << lyCounter_.isDoubleSpeed()) + 1; + lastChange_ = 2 * lcd_num_oam_entries; } SpriteMapper::SpriteMapper(NextM0Time &nextM0Time, @@ -165,28 +162,24 @@ void SpriteMapper::reset(unsigned char const *oamram, bool cgb) { } void SpriteMapper::clearMap() { - std::memset(num_, need_sorting_mask, sizeof num_); + std::fill_n(num_, sizeof num_ / sizeof * num_, 1 * need_sorting_flag); } void SpriteMapper::mapSprites() { clearMap(); - for (unsigned i = 0x00; i < 0x50; i += 2) { - int const spriteHeight = 8 << largeSprites(i >> 1); - unsigned const bottomPos = posbuf()[i] - (17u - spriteHeight); + for (int i = 0; i < lcd_num_oam_entries; ++i) { + int const spriteHeight = 8 + 8 * largeSprites(i); + unsigned const bottomPos = posbuf()[2 * i] - 17 + spriteHeight; - if (bottomPos < 143u + spriteHeight) { - unsigned const startly = std::max(int(bottomPos) + 1 - spriteHeight, 0); - unsigned char *map = spritemap_ + startly * 10; - unsigned char *n = num_ + startly; - unsigned char *const nend = num_ + std::min(bottomPos, 143u) + 1; + if (bottomPos < lcd_vres - 1u + spriteHeight) { + int ly = std::max(static_cast(bottomPos) + 1 - spriteHeight, 0); + int const end = std::min(bottomPos, lcd_vres - 1u) + 1; do { - if (*n < need_sorting_mask + 10) - map[(*n)++ - need_sorting_mask] = i; - - map += 10; - } while (++n != nend); + if (num_[ly] < need_sorting_flag + lcd_max_num_sprites_per_line) + spritemap_[ly][num_[ly]++ - need_sorting_flag] = 2 * i; + } while (++ly != end); } } @@ -194,17 +187,17 @@ void SpriteMapper::mapSprites() { } void SpriteMapper::sortLine(unsigned const ly) const { - num_[ly] &= ~need_sorting_mask; - insertionSort(spritemap_ + ly * 10, spritemap_ + ly * 10 + num_[ly], - SpxLess(posbuf() + 1)); + num_[ly] &= ~(1u * need_sorting_flag); + insertionSort(spritemap_[ly], spritemap_[ly] + num_[ly], + SpxLess(posbuf() + 1)); } unsigned long SpriteMapper::doEvent(unsigned long const time) { oamReader_.update(time); mapSprites(); return oamReader_.changed() - ? time + oamReader_.lineTime() - : static_cast(disabled_time); + ? time + oamReader_.lineTime() + : static_cast(disabled_time); } SYNCFUNC(SpriteMapper) @@ -214,5 +207,3 @@ SYNCFUNC(SpriteMapper) SSS(oamReader_); } - -} diff --git a/libgambatte/src/video/sprite_mapper.h b/libgambatte/src/video/sprite_mapper.h index 35c2b3e3f9..b474f7d864 100644 --- a/libgambatte/src/video/sprite_mapper.h +++ b/libgambatte/src/video/sprite_mapper.h @@ -34,14 +34,12 @@ public: unsigned char const *oamram); void reset(unsigned char const *oamram, bool cgb); unsigned long doEvent(unsigned long time); - bool largeSprites(unsigned spNo) const { return oamReader_.largeSprites(spNo); } - unsigned numSprites(unsigned ly) const { return num_[ly] & ~need_sorting_mask; } + bool largeSprites(int spno) const { return oamReader_.largeSprites(spno); } + int numSprites(unsigned ly) const { return num_[ly] & ~(1u * need_sorting_flag); } void oamChange(unsigned long cc) { oamReader_.change(cc); } void oamChange(unsigned char const *oamram, unsigned long cc) { oamReader_.change(oamram, cc); } unsigned char const * oamram() const { return oamReader_.oam(); } unsigned char const * posbuf() const { return oamReader_.spritePosBuf(); } - void preSpeedChange(unsigned long cc) { oamReader_.update(cc); } - void postSpeedChange(unsigned long cc) { oamReader_.change(cc); } void resetCycleCounter(unsigned long oldCc, unsigned long newCc) { oamReader_.update(oldCc); @@ -50,11 +48,11 @@ public: void setLargeSpritesSource(bool src) { oamReader_.setLargeSpritesSrc(src); } - unsigned char const * sprites(unsigned ly) const { - if (num_[ly] & need_sorting_mask) + unsigned char const* sprites(unsigned ly) const { + if (num_[ly] & need_sorting_flag) sortLine(ly); - return spritemap_ + ly * 10; + return spritemap_[ly]; } void setStatePtrs(SaveState &state) { oamReader_.setStatePtrs(state); } @@ -69,8 +67,8 @@ public: return oamReader_.inactivePeriodAfterDisplayEnable(cc); } - static unsigned long schedule(LyCounter const &lyCounter, unsigned long cc) { - return lyCounter.nextLineCycle(80, cc); + static unsigned long schedule(LyCounter const& lyCounter, unsigned long cc) { + return lyCounter.nextLineCycle(2 * lcd_num_oam_entries, cc); } private: @@ -81,7 +79,7 @@ private: void change(unsigned long cc); void change(unsigned char const *oamram, unsigned long cc) { change(cc); oamram_ = oamram; } bool changed() const { return lastChange_ != 0xFF; } - bool largeSprites(unsigned spNo) const { return szbuf_[spNo]; } + bool largeSprites(int spNo) const { return lsbuf_[spNo]; } unsigned char const * oam() const { return oamram_; } void resetCycleCounter(unsigned long oldCc, unsigned long newCc) { lu_ -= oldCc - newCc; } void setLargeSpritesSrc(bool src) { largeSpritesSrc_ = src; } @@ -94,8 +92,8 @@ private: unsigned lineTime() const { return lyCounter_.lineTime(); } private: - unsigned char buf_[80]; - bool szbuf_[40]; + unsigned char buf_[2 * lcd_num_oam_entries]; + bool lsbuf_[lcd_num_oam_entries]; LyCounter const &lyCounter_; unsigned char const *oamram_; unsigned long lu_; @@ -107,10 +105,10 @@ private: templatevoid SyncState(NewState *ns); }; - enum { need_sorting_mask = 0x80 }; + enum { need_sorting_flag = 0x80 }; - mutable unsigned char spritemap_[144 * 10]; - mutable unsigned char num_[144]; + mutable unsigned char spritemap_[lcd_vres][lcd_max_num_sprites_per_line]; + mutable unsigned char num_[lcd_vres]; NextM0Time &nextM0Time_; OamReader oamReader_; diff --git a/output/dll/libgambatte.dll b/output/dll/libgambatte.dll index e482dab5d7..fce190d429 100644 Binary files a/output/dll/libgambatte.dll and b/output/dll/libgambatte.dll differ