From 23c1c74030e320af86e3ff8e65b2a3d85de4c778 Mon Sep 17 00:00:00 2001 From: TiKevin83 Date: Sun, 16 Feb 2020 20:07:28 -0500 Subject: [PATCH] Sinamas commits mostly done, just need to fix DMG games on CGB using LCDC --- libgambatte/libgambatte.vcxproj | 3 +- libgambatte/libgambatte.vcxproj.filters | 9 +- libgambatte/src/cpu.cpp | 5 +- libgambatte/src/mem/cartridge.cpp | 8 + libgambatte/src/memory.cpp | 1 - libgambatte/src/video.cpp | 599 ++++---- libgambatte/src/video.h | 44 +- libgambatte/src/video/ly_counter.cpp | 15 +- libgambatte/src/video/ly_counter.h | 5 +- libgambatte/src/video/lyc_irq.cpp | 62 +- libgambatte/src/video/lyc_irq.h | 2 +- libgambatte/src/video/m0_irq.h | 68 - libgambatte/src/video/mstat_irq.h | 72 + libgambatte/src/video/next_m0_time.cpp | 2 +- libgambatte/src/video/next_m0_time.h | 4 +- libgambatte/src/video/ppu.cpp | 1687 ++++++++++++----------- libgambatte/src/video/ppu.h | 22 +- libgambatte/src/video/sprite_mapper.cpp | 113 +- libgambatte/src/video/sprite_mapper.h | 28 +- 19 files changed, 1413 insertions(+), 1336 deletions(-) delete mode 100644 libgambatte/src/video/m0_irq.h create mode 100644 libgambatte/src/video/mstat_irq.h diff --git a/libgambatte/libgambatte.vcxproj b/libgambatte/libgambatte.vcxproj index 549134284f..e0e201771f 100644 --- a/libgambatte/libgambatte.vcxproj +++ b/libgambatte/libgambatte.vcxproj @@ -187,9 +187,10 @@ + - + diff --git a/libgambatte/libgambatte.vcxproj.filters b/libgambatte/libgambatte.vcxproj.filters index 93ddd6e40b..504d75698f 100644 --- a/libgambatte/libgambatte.vcxproj.filters +++ b/libgambatte/libgambatte.vcxproj.filters @@ -111,9 +111,6 @@ Header Files - - Header Files - Header Files @@ -132,6 +129,12 @@ Header Files + + Header Files + + + Header Files + diff --git a/libgambatte/src/cpu.cpp b/libgambatte/src/cpu.cpp index 1833b0290b..67a411c0e9 100644 --- a/libgambatte/src/cpu.cpp +++ b/libgambatte/src/cpu.cpp @@ -23,7 +23,7 @@ namespace gambatte { CPU::CPU() -: mem_(sp, pc, opcode_, prefetched_) +: mem_(Interrupter(sp, pc, opcode_, prefetched_)) , cycleCounter_(0) , pc(0x100) , sp(0xFFFE) @@ -2077,7 +2077,8 @@ SYNCFUNC(CPU) NSS(e); NSS(h); NSS(l); - NSS(skip_); + NSS(opcode_); + NSS(prefetched_); } } diff --git a/libgambatte/src/mem/cartridge.cpp b/libgambatte/src/mem/cartridge.cpp index 1cdd7cc968..165f5e77be 100644 --- a/libgambatte/src/mem/cartridge.cpp +++ b/libgambatte/src/mem/cartridge.cpp @@ -375,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: @@ -443,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: diff --git a/libgambatte/src/memory.cpp b/libgambatte/src/memory.cpp index b63a243fe8..95a02ea4a4 100644 --- a/libgambatte/src/memory.cpp +++ b/libgambatte/src/memory.cpp @@ -1303,7 +1303,6 @@ SYNCFUNC(Memory) NSS(cgbSwitching_); NSS(agbMode_); NSS(gbIsCgb_); - NSS(halttime_); NSS(stopped_); NSS(LINKCABLE_); NSS(linkClockTrigger_); diff --git a/libgambatte/src/video.cpp b/libgambatte/src/video.cpp index 997993c881..ce58c4a392 100644 --- a/libgambatte/src/video.cpp +++ b/libgambatte/src/video.cpp @@ -21,7 +21,7 @@ #include #include -namespace gambatte { +using namespace gambatte; unsigned long LCD::gbcToRgb32(const unsigned bgr15) { unsigned long const r = bgr15 & 0x1F; @@ -31,11 +31,56 @@ unsigned long LCD::gbcToRgb32(const unsigned bgr15) { 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; + } + + void doCgbColorChange(unsigned char* pdata, + unsigned long* palette, unsigned index, unsigned data, bool trueColor) { + pdata[index] = data; + index /= 2; + palette[index] = gbcToRgb32(pdata[index * 2] | pdata[index * 2 + 1] * 0x100l); + } + +} // unnamed namespace. + void LCD::setDmgPalette(unsigned long palette[], const unsigned long dmgColors[], unsigned data) { - palette[0] = dmgColors[data & 3]; - palette[1] = dmgColors[data >> 2 & 3]; - palette[2] = dmgColors[data >> 4 & 3]; - palette[3] = dmgColors[data >> 6 & 3]; + for (int i = 0; i < num_palette_entries; ++i, data /= num_palette_entries) + palette[i] = gbcToRgb32(dmgColors[data % num_palette_entries]); } void LCD::setCgbPalette(unsigned *lut) { @@ -47,10 +92,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 +105,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) { @@ -73,40 +118,6 @@ 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 +126,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); @@ -157,9 +163,9 @@ 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); + 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]); @@ -200,6 +206,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 +233,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 +281,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 +315,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 +343,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 +411,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 +439,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 +454,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 +506,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 +627,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 +682,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 +692,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 +708,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 +760,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 +782,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 +790,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 +815,6 @@ inline void LCD::event() { case event_ly: ppu_.doLyCountEvent(); eventTimes_.set(ppu_.lyCounter().time()); - if (scanlinecallback && ppu_.lyCounter().ly() == (unsigned)scanlinecallbacksl) - scanlinecallback(); break; } } @@ -867,12 +852,10 @@ SYNCFUNC(LCD) 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..cedd2cf86a 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: @@ -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,6 +143,7 @@ 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(); } @@ -189,6 +194,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 +220,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 +241,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/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..257a7906a3 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; @@ -123,24 +141,24 @@ static inline void nextCall(int const cycles, PPUState const &state, PPUPriv &p) } 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 +170,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 +178,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 +221,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 +296,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 +312,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 +331,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 +344,229 @@ 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 +575,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 tno = tileMapLine[tileMapXpos % tile_map_len]; + unsigned const 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 * 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 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; + } - xpos = xpos + 8; - } while (xpos < xend); + xpos = xpos + tile_len; + } while (xpos < xend); - p.xpos = xpos; -} - -static void doFullTilesUnrolled(PPUPriv &p) { - int xpos = p.xpos; - int const xend = static_cast(p.wx) < xpos || p.wx >= 168 - ? 161 - : static_cast(p.wx) - 7; - if (xpos >= xend) - return; - - uint_least32_t *const dbufline = p.framebuf.fbline(); - unsigned char const *tileMapLine; - unsigned tileline; - unsigned tileMapXpos; - - if (p.winDrawState & win_draw_started) { - tileMapLine = p.vram + (p.lcdc << 4 & 0x400) - + (p.winYPos & 0xF8) * 4 + 0x1800; - tileMapXpos = (xpos + p.wscx) >> 3; - tileline = p.winYPos & 7; - } else { - tileMapLine = p.vram + (p.lcdc << 7 & 0x400) - + ((p.scy + p.lyCounter.ly()) & 0xF8) * 4 + 0x1800; - tileMapXpos = (p.scx + xpos + 1 - p.cgb) >> 3; - tileline = (p.scy + p.lyCounter.ly()) & 7; + p.xpos = xpos; } - if (xpos < 8) { - uint_least32_t prebuf[16]; - if (p.cgb) { - doFullTilesUnrolledCgb(p, xend < 8 ? xend : 8, prebuf + (8 - xpos), - tileMapLine, tileline, tileMapXpos); - } else { - doFullTilesUnrolledDmg(p, xend < 8 ? xend : 8, prebuf + (8 - xpos), - tileMapLine, tileline, tileMapXpos); + 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; + + 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 +924,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 +956,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 +974,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 +1059,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 +1106,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 +1132,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 +1195,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 +1242,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 +1258,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 +1290,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 +1313,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 +1326,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 +1358,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 +1420,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 +1469,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 +1492,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 +1593,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 +1625,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 +1652,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 +1718,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 +1731,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_; } } @@ -1706,20 +1781,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 +1805,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 +1829,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; @@ -1833,5 +1904,3 @@ SYNCFUNC(PPU) NSS(p_.cgb); NSS(p_.weMaster); } - -} diff --git a/libgambatte/src/video/ppu.h b/libgambatte/src/video/ppu.h index edfb6a9b9f..927613b3f1 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,7 +146,7 @@ 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; } 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_;