Sinamas commits mostly done, just need to fix DMG games on CGB using LCDC

This commit is contained in:
TiKevin83 2020-02-16 20:07:28 -05:00 committed by Travis
parent 85ee2af0b2
commit 23c1c74030
19 changed files with 1413 additions and 1336 deletions

View File

@ -187,9 +187,10 @@
<ClInclude Include="src\sound\static_output_tester.h" />
<ClInclude Include="src\tima.h" />
<ClInclude Include="src\video.h" />
<ClInclude Include="src\video\lcddef.h" />
<ClInclude Include="src\video\lyc_irq.h" />
<ClInclude Include="src\video\ly_counter.h" />
<ClInclude Include="src\video\m0_irq.h" />
<ClInclude Include="src\video\mstat_irq.h" />
<ClInclude Include="src\video\next_m0_time.h" />
<ClInclude Include="src\video\ppu.h" />
<ClInclude Include="src\video\sprite_mapper.h" />

View File

@ -111,9 +111,6 @@
<ClInclude Include="src\video\ly_counter.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\video\m0_irq.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\video\next_m0_time.h">
<Filter>Header Files</Filter>
</ClInclude>
@ -132,6 +129,12 @@
<ClInclude Include="src\sound\psgdef.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\video\mstat_irq.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\video\lcddef.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\cinterface.cpp">

View File

@ -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_);
}
}

View File

@ -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:

View File

@ -1303,7 +1303,6 @@ SYNCFUNC(Memory)
NSS(cgbSwitching_);
NSS(agbMode_);
NSS(gbIsCgb_);
NSS(halttime_);
NSS(stopped_);
NSS(LINKCABLE_);
NSS(linkClockTrigger_);

View File

@ -21,7 +21,7 @@
#include <algorithm>
#include <cstring>
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<memevent_oneshot_statirq>(
state.ppu.pendingLcdstatIrq
eventTimes_.setm<memevent_oneshot_statirq>(state.ppu.pendingLcdstatIrq
? ppu_.now() + 1
: static_cast<unsigned long>(disabled_time));
: 1 * disabled_time);
eventTimes_.setm<memevent_oneshot_updatewy2>(
state.ppu.oldWy != state.mem.ioamhram.get()[0x14A]
? ppu_.now() + 1
: static_cast<unsigned long>(disabled_time));
state.ppu.oldWy != state.mem.ioamhram.get()[0x14A]
? ppu_.now() + 2 - isDoubleSpeed()
: 1 * disabled_time);
eventTimes_.set<event_ly>(ppu_.lyCounter().time());
eventTimes_.setm<memevent_spritemap>(
SpriteMapper::schedule(ppu_.lyCounter(), ppu_.now()));
eventTimes_.setm<memevent_lycirq>(lycIrq_.time());
eventTimes_.setm<memevent_m1irq>(
ppu_.lyCounter().nextFrameCycle(144 * 456, ppu_.now()) - 2);
eventTimes_.setm<memevent_m1irq>(mode1IrqSchedule(ppu_.lyCounter(), ppu_.now()));
eventTimes_.setm<memevent_m2irq>(
mode2IrqSchedule(statReg_, ppu_.lyCounter(), ppu_.now()));
eventTimes_.setm<memevent_m0irq>(statReg_ & lcdstat_m0irqen
? ppu_.now() + state.ppu.nextM0Irq
: static_cast<unsigned long>(disabled_time));
: 1 * disabled_time);
eventTimes_.setm<memevent_hdma>(state.mem.hdmaTransfer
? nextHdmaTime(ppu_.lastM0Time(), nextM0Time_.predictedNextM0Time(),
ppu_.now(), isDoubleSpeed())
: static_cast<unsigned long>(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<event_ly>(ppu_.lyCounter().time());
eventTimes_.setm<memevent_spritemap>(SpriteMapper::schedule(ppu_.lyCounter(), cc));
eventTimes_.setm<memevent_spritemap>(SpriteMapper::schedule(ppu_.lyCounter(), ppu_.now()));
eventTimes_.setm<memevent_lycirq>(lycIrq_.time());
eventTimes_.setm<memevent_m1irq>(ppu_.lyCounter().nextFrameCycle(144 * 456, cc) - 2);
eventTimes_.setm<memevent_m2irq>(mode2IrqSchedule(statReg_, ppu_.lyCounter(), cc));
eventTimes_.setm<memevent_m1irq>(mode1IrqSchedule(ppu_.lyCounter(), ppu_.now()));
eventTimes_.setm<memevent_m2irq>(mode2IrqSchedule(statReg_, ppu_.lyCounter(), ppu_.now()));
if (eventTimes_(memevent_m0irq) != disabled_time
&& eventTimes_(memevent_m0irq) - cc > 1) {
eventTimes_.setm<memevent_m0irq>(m0IrqTimeFromXpos166Time(
ppu_.predictedNextXposTime(166), ppu_.cgb(), isDoubleSpeed()));
if (eventTimes_(memevent_m0irq) != disabled_time) {
eventTimes_.setm<memevent_m0irq>(ppu_.predictedNextXposTime(lcd_hres + 6));
}
if (hdmaIsEnabled() && eventTimes_(memevent_hdma) - cc > 1) {
eventTimes_.setm<memevent_hdma>(nextHdmaTime(ppu_.lastM0Time(),
nextM0Time_.predictedNextM0Time(), cc, isDoubleSpeed()));
if (hdmaIsEnabled()) {
eventTimes_.setm<memevent_hdma>(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<memevent_hdma>(nextHdmaTime(
ppu_.lastM0Time(), nextM0Time_.predictedNextM0Time(),
cycleCounter, isDoubleSpeed()));
eventTimes_.setm<memevent_hdma>(nextM0Time_.predictedNextM0Time());
}
void LCD::disableHdma(unsigned long const cycleCounter) {
@ -296,14 +281,33 @@ void LCD::disableHdma(unsigned long const cycleCounter) {
eventTimes_.setm<memevent_hdma>(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<memevent_m0irq>(t);
}
if (eventTimes_(memevent_hdma) != disabled_time
&& eventTimes_(memevent_hdma) > hdmaTimeFromM0Time(ppu_.lastM0Time(), ds)) {
&& eventTimes_(memevent_hdma) > ppu_.lastM0Time()) {
nextM0Time_.predictNextM0Time(ppu_);
eventTimes_.setm<memevent_hdma>(
hdmaTimeFromM0Time(nextM0Time_.predictedNextM0Time(), ds));
eventTimes_.setm<memevent_hdma>(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<memevent_oneshot_updatewy2>(cc + 5);
} else {
eventTimes_.setm<memevent_oneshot_updatewy2>(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<memevent_spritemap>(
SpriteMapper::schedule(ppu_.lyCounter(), cc));
eventTimes_.setm<memevent_lycirq>(lycIrq_.time());
eventTimes_.setm<memevent_m1irq>(
ppu_.lyCounter().nextFrameCycle(144 * 456, cc) - 2);
eventTimes_.setm<memevent_m1irq>(mode1IrqSchedule(ppu_.lyCounter(), cc));
eventTimes_.setm<memevent_m2irq>(
mode2IrqSchedule(statReg_, ppu_.lyCounter(), cc));
if (statReg_ & lcdstat_m0irqen) {
eventTimes_.setm<memevent_m0irq>(m0IrqTimeFromXpos166Time(
ppu_.predictedNextXposTime(166), ppu_.cgb(), isDoubleSpeed()));
eventTimes_.setm<memevent_m0irq>(ppu_.predictedNextXposTime(lcd_hres + 6));
}
if (hdmaIsEnabled()) {
eventTimes_.setm<memevent_hdma>(nextHdmaTime(ppu_.lastM0Time(),
nextM0Time_.predictedNextM0Time(), cc, isDoubleSpeed()));
eventTimes_.setm<memevent_hdma>(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<memevent_spritemap>(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<memevent_spritemap>(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<memevent_m0irq>(m0IrqTimeFromXpos166Time(
ppu_.predictedNextXposTime(166), ppu_.cgb(), isDoubleSpeed()));
eventTimes_.setm<memevent_m0irq>(ppu_.predictedNextXposTime(lcd_hres + 6));
}
eventTimes_.setm<memevent_m2irq>(mode2IrqSchedule(data, ppu_.lyCounter(), cc));
eventTimes_.setm<memevent_lycirq>(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<memevent_lycirq>(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<memevent_oneshot_statirq>(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<memevent_m2irq>(nextTime);
} else {
eventTimes_.setm<memevent_m2irq>(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<memevent_m2irq>(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<memevent_m1irq>(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<memevent_lycirq>(lycIrq_.time());
break;
}
case memevent_spritemap:
eventTimes_.setm<memevent_spritemap>(
@ -789,8 +782,7 @@ inline void LCD::event() {
case memevent_hdma:
eventTimes_.flagHdmaReq();
nextM0Time_.predictNextM0Time(ppu_);
eventTimes_.setm<memevent_hdma>(hdmaTimeFromM0Time(
nextM0Time_.predictedNextM0Time(), isDoubleSpeed()));
eventTimes_.setm<memevent_hdma>(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<memevent_m0irq>(statReg_ & lcdstat_m0irqen
? m0IrqTimeFromXpos166Time(ppu_.predictedNextXposTime(166),
ppu_.cgb(), isDoubleSpeed())
: static_cast<unsigned long>(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<event_ly>(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_);
}
}

View File

@ -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<intevent_video>(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);

View File

@ -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)

View File

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

View File

@ -23,7 +23,26 @@
#include "savestate.h"
#include <algorithm>
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<unsigned long>(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_);
}
}

View File

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

View File

@ -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<bool isReader>
void SyncState(NewState *ns)
{
NSS(statReg_);
NSS(lycReg_);
}
};
}
#endif

View File

@ -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<bool isReader>
void SyncState(NewState *ns)
{
NSS(statReg_);
NSS(lycReg_);
}
};
}
#endif

View File

@ -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)

View File

@ -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:
template<bool isReader>void SyncState(NewState *ns);

File diff suppressed because it is too large Load Diff

View File

@ -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; }

View File

@ -21,7 +21,8 @@
#include "next_m0_time.h"
#include "../insertion_sort.h"
#include <algorithm>
#include <cstring>
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<int>(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<unsigned long>(disabled_time);
? time + oamReader_.lineTime()
: static_cast<unsigned long>(disabled_time);
}
SYNCFUNC(SpriteMapper)
@ -214,5 +207,3 @@ SYNCFUNC(SpriteMapper)
SSS(oamReader_);
}
}

View File

@ -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:
template<bool isReader>void 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_;