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_;