Sinamas commits mostly done, just need to fix DMG games on CGB using LCDC
This commit is contained in:
parent
85ee2af0b2
commit
23c1c74030
|
@ -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" />
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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_);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -1303,7 +1303,6 @@ SYNCFUNC(Memory)
|
|||
NSS(cgbSwitching_);
|
||||
NSS(agbMode_);
|
||||
NSS(gbIsCgb_);
|
||||
NSS(halttime_);
|
||||
NSS(stopped_);
|
||||
NSS(LINKCABLE_);
|
||||
NSS(linkClockTrigger_);
|
||||
|
|
|
@ -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_);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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_; }
|
||||
|
|
|
@ -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_);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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_; }
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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
|
@ -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; }
|
||||
|
|
|
@ -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_);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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_;
|
||||
|
||||
|
|
Loading…
Reference in New Issue