Update PPU

This commit is contained in:
MrWint 2019-05-25 21:01:50 +02:00
parent b99b5a05d6
commit a6aeef840a
20 changed files with 552 additions and 499 deletions

View File

@ -2,7 +2,7 @@
#define COUNTERDEF_H
namespace gambatte {
enum { DISABLED_TIME = 0xFFFFFFFFul };
enum { disabled_time = 0xFFFFFFFFul };
}
#endif

View File

@ -1200,10 +1200,10 @@ void gambatte::setInitState(SaveState &state, const bool cgb, const bool gbaCgbM
state.mem.divLastUpdate = 0 - div;
state.mem.timaLastUpdate = 0;
state.mem.tmatime = DISABLED_TIME;
state.mem.nextSerialtime = DISABLED_TIME;
state.mem.lastOamDmaUpdate = DISABLED_TIME;
state.mem.unhaltTime = DISABLED_TIME;
state.mem.tmatime = disabled_time;
state.mem.nextSerialtime = disabled_time;
state.mem.lastOamDmaUpdate = disabled_time;
state.mem.unhaltTime = disabled_time;
state.mem.minIntTime = 0;
state.mem.rombank = 1;
state.mem.dmaSource = 0;

View File

@ -29,13 +29,13 @@ void InterruptRequester::loadState(const SaveState &state) {
iereg_ = state.mem.ioamhram.get()[0x1FF] & 0x1F;
intFlags.set(state.mem.IME, state.mem.halted);
eventTimes.setValue<INTERRUPTS>(intFlags.imeOrHalted() && pendingIrqs() ? minIntTime : static_cast<unsigned long>(DISABLED_TIME));
eventTimes.setValue<INTERRUPTS>(intFlags.imeOrHalted() && pendingIrqs() ? minIntTime : static_cast<unsigned long>(disabled_time));
}
void InterruptRequester::resetCc(const unsigned long oldCc, const unsigned long newCc) {
minIntTime = minIntTime < oldCc ? 0 : minIntTime - (oldCc - newCc);
if (eventTimes.value(INTERRUPTS) != DISABLED_TIME)
if (eventTimes.value(INTERRUPTS) != disabled_time)
eventTimes.setValue<INTERRUPTS>(minIntTime);
}
@ -51,7 +51,7 @@ void InterruptRequester::di() {
intFlags.unsetIme();
if (!intFlags.imeOrHalted())
eventTimes.setValue<INTERRUPTS>(DISABLED_TIME);
eventTimes.setValue<INTERRUPTS>(disabled_time);
}
void InterruptRequester::halt() {
@ -65,7 +65,7 @@ void InterruptRequester::unhalt() {
intFlags.unsetHalted();
if (!intFlags.imeOrHalted())
eventTimes.setValue<INTERRUPTS>(DISABLED_TIME);
eventTimes.setValue<INTERRUPTS>(disabled_time);
}
void InterruptRequester::flagIrq(const unsigned bit) {
@ -84,14 +84,14 @@ void InterruptRequester::setIereg(const unsigned iereg) {
iereg_ = iereg & 0x1F;
if (intFlags.imeOrHalted())
eventTimes.setValue<INTERRUPTS>(pendingIrqs() ? minIntTime : static_cast<unsigned long>(DISABLED_TIME));
eventTimes.setValue<INTERRUPTS>(pendingIrqs() ? minIntTime : static_cast<unsigned long>(disabled_time));
}
void InterruptRequester::setIfreg(const unsigned ifreg) {
ifreg_ = ifreg;
if (intFlags.imeOrHalted())
eventTimes.setValue<INTERRUPTS>(pendingIrqs() ? minIntTime : static_cast<unsigned long>(DISABLED_TIME));
eventTimes.setValue<INTERRUPTS>(pendingIrqs() ? minIntTime : static_cast<unsigned long>(disabled_time));
}
SYNCFUNC(InterruptRequester)

View File

@ -87,7 +87,7 @@ public:
inline void flagHdmaReq(InterruptRequester *const intreq) { intreq->setEventTime<DMA>(0); }
inline void flagGdmaReq(InterruptRequester *const intreq) { intreq->setEventTime<DMA>(1); }
inline void ackDmaReq(InterruptRequester *const intreq) { intreq->setEventTime<DMA>(DISABLED_TIME); }
inline void ackDmaReq(InterruptRequester *const intreq) { intreq->setEventTime<DMA>(disabled_time); }
inline bool hdmaReqFlagged(const InterruptRequester &intreq) { return intreq.eventTime(DMA) == 0; }
inline bool gdmaReqFlagged(const InterruptRequester &intreq) { return intreq.eventTime(DMA) == 1; }

View File

@ -32,7 +32,7 @@ Memory::Memory(const Interrupter &interrupter_in, unsigned short &sp, unsigned s
linkCallback(0),
getInput(0),
divLastUpdate(0),
lastOamDmaUpdate(DISABLED_TIME),
lastOamDmaUpdate(disabled_time),
display(ioamhram, 0, VideoInterruptRequester(&intreq)),
interrupter(interrupter_in),
dmaSource(0),
@ -80,7 +80,7 @@ void Memory::loadState(const SaveState &state) {
dmaSource = state.mem.dmaSource;
dmaDestination = state.mem.dmaDestination;
oamDmaPos = state.mem.oamDmaPos;
serialCnt = intreq.eventTime(SERIAL) != DISABLED_TIME
serialCnt = intreq.eventTime(SERIAL) != disabled_time
? serialCntFrom(intreq.eventTime(SERIAL) - state.cpu.cycleCounter, ioamhram[0x102] & isCgb() * 2)
: 8;
@ -88,7 +88,7 @@ void Memory::loadState(const SaveState &state) {
cart.setOamDmaSrc(OAM_DMA_SRC_OFF);
cart.setWrambank(isCgb() && (ioamhram[0x170] & 0x07) ? ioamhram[0x170] & 0x07 : 1);
if (lastOamDmaUpdate != DISABLED_TIME) {
if (lastOamDmaUpdate != disabled_time) {
oamDmaInitSetup();
const unsigned oamEventPos = oamDmaPos < 0xA0 ? 0xA0 : 0x100;
@ -112,11 +112,11 @@ void Memory::setEndtime(const unsigned long cycleCounter, const unsigned long in
void Memory::updateSerial(const unsigned long cc) {
if (!LINKCABLE) {
if (intreq.eventTime(SERIAL) != DISABLED_TIME) {
if (intreq.eventTime(SERIAL) != disabled_time) {
if (intreq.eventTime(SERIAL) <= cc) {
ioamhram[0x101] = (((ioamhram[0x101] + 1) << serialCnt) - 1) & 0xFF;
ioamhram[0x102] &= 0x7F;
intreq.setEventTime<SERIAL>(DISABLED_TIME);
intreq.setEventTime<SERIAL>(disabled_time);
intreq.flagIrq(8);
} else {
const int targetCnt = serialCntFrom(intreq.eventTime(SERIAL) - cc, ioamhram[0x102] & isCgb() * 2);
@ -126,10 +126,10 @@ void Memory::updateSerial(const unsigned long cc) {
}
}
else {
if (intreq.eventTime(SERIAL) != DISABLED_TIME) {
if (intreq.eventTime(SERIAL) != disabled_time) {
if (intreq.eventTime(SERIAL) <= cc) {
linkClockTrigger = true;
intreq.setEventTime<SERIAL>(DISABLED_TIME);
intreq.setEventTime<SERIAL>(disabled_time);
if (linkCallback)
linkCallback();
}
@ -149,7 +149,7 @@ void Memory::updateIrqs(const unsigned long cc) {
}
unsigned long Memory::event(unsigned long cycleCounter) {
if (lastOamDmaUpdate != DISABLED_TIME)
if (lastOamDmaUpdate != disabled_time)
updateOamDma(cycleCounter);
switch (intreq.minEventId()) {
@ -158,15 +158,15 @@ unsigned long Memory::event(unsigned long cycleCounter) {
PC = (PC + 1) & 0xFFFF;
cycleCounter += 4;
intreq.unhalt();
intreq.setEventTime<UNHALT>(DISABLED_TIME);
intreq.setEventTime<UNHALT>(disabled_time);
break;
case END:
intreq.setEventTime<END>(DISABLED_TIME - 1);
intreq.setEventTime<END>(disabled_time - 1);
while (cycleCounter >= intreq.minEventTime() && intreq.eventTime(END) != DISABLED_TIME)
while (cycleCounter >= intreq.minEventTime() && intreq.eventTime(END) != disabled_time)
cycleCounter = event(cycleCounter);
intreq.setEventTime<END>(DISABLED_TIME);
intreq.setEventTime<END>(disabled_time);
break;
case BLIT:
@ -176,8 +176,8 @@ unsigned long Memory::event(unsigned long cycleCounter) {
if (lcden | blanklcd) {
display.updateScreen(blanklcd, cycleCounter);
intreq.setEventTime<BLIT>(DISABLED_TIME);
intreq.setEventTime<END>(DISABLED_TIME);
intreq.setEventTime<BLIT>(disabled_time);
intreq.setEventTime<END>(disabled_time);
while (cycleCounter >= intreq.minEventTime())
cycleCounter = event(cycleCounter);
@ -192,8 +192,8 @@ unsigned long Memory::event(unsigned long cycleCounter) {
updateSerial(cycleCounter);
break;
case OAM:
intreq.setEventTime<OAM>(lastOamDmaUpdate == DISABLED_TIME ?
static_cast<unsigned long>(DISABLED_TIME) : intreq.eventTime(OAM) + 0xA0 * 4);
intreq.setEventTime<OAM>(lastOamDmaUpdate == disabled_time ?
static_cast<unsigned long>(disabled_time) : intreq.eventTime(OAM) + 0xA0 * 4);
break;
case DMA:
{
@ -217,7 +217,7 @@ unsigned long Memory::event(unsigned long cycleCounter) {
{
unsigned long lOamDmaUpdate = lastOamDmaUpdate;
lastOamDmaUpdate = DISABLED_TIME;
lastOamDmaUpdate = disabled_time;
while (length--) {
const unsigned src = dmaSrc++ & 0xFFFF;
@ -236,7 +236,7 @@ unsigned long Memory::event(unsigned long cycleCounter) {
ioamhram[src & 0xFF] = data;
} else if (oamDmaPos == 0xA0) {
endOamDma(lOamDmaUpdate - 1);
lOamDmaUpdate = DISABLED_TIME;
lOamDmaUpdate = disabled_time;
}
}
@ -253,7 +253,7 @@ unsigned long Memory::event(unsigned long cycleCounter) {
ioamhram[0x155] = ((dmaLength / 0x10 - 0x1) & 0xFF) | (ioamhram[0x155] & 0x80);
if ((ioamhram[0x155] & 0x80) && display.hdmaIsEnabled()) {
if (lastOamDmaUpdate != DISABLED_TIME)
if (lastOamDmaUpdate != disabled_time)
updateOamDma(cycleCounter);
display.disableHdma(cycleCounter);
@ -269,7 +269,7 @@ unsigned long Memory::event(unsigned long cycleCounter) {
break;
case INTERRUPTS:
if (stopped) {
intreq.setEventTime<INTERRUPTS>(DISABLED_TIME);
intreq.setEventTime<INTERRUPTS>(disabled_time);
break;
}
if (halted()) {
@ -277,7 +277,7 @@ unsigned long Memory::event(unsigned long cycleCounter) {
cycleCounter += 4;
intreq.unhalt();
intreq.setEventTime<UNHALT>(DISABLED_TIME);
intreq.setEventTime<UNHALT>(disabled_time);
}
if (ime()) {
@ -347,17 +347,17 @@ unsigned long Memory::stop(unsigned long cycleCounter) {
}
static void decCycles(unsigned long &counter, const unsigned long dec) {
if (counter != DISABLED_TIME)
if (counter != disabled_time)
counter -= dec;
}
void Memory::decEventCycles(const MemEventId eventId, const unsigned long dec) {
if (intreq.eventTime(eventId) != DISABLED_TIME)
if (intreq.eventTime(eventId) != disabled_time)
intreq.setEventTime(eventId, intreq.eventTime(eventId) - dec);
}
unsigned long Memory::resetCounters(unsigned long cycleCounter) {
if (lastOamDmaUpdate != DISABLED_TIME)
if (lastOamDmaUpdate != disabled_time)
updateOamDma(cycleCounter);
updateIrqs(cycleCounter);
@ -424,7 +424,7 @@ void Memory::updateOamDma(const unsigned long cycleCounter) {
ioamhram[oamDmaPos] = oamDmaSrc ? oamDmaSrc[oamDmaPos] : cart.rtcRead();
} else if (oamDmaPos == 0xA0) {
endOamDma(lastOamDmaUpdate - 1);
lastOamDmaUpdate = DISABLED_TIME;
lastOamDmaUpdate = disabled_time;
break;
}
}
@ -468,7 +468,7 @@ void Memory::endOamDma(const unsigned long cycleCounter) {
}
unsigned Memory::nontrivial_ff_read(const unsigned P, const unsigned long cycleCounter) {
if (lastOamDmaUpdate != DISABLED_TIME)
if (lastOamDmaUpdate != disabled_time)
updateOamDma(cycleCounter);
switch (P & 0x7F) {
@ -562,7 +562,7 @@ static bool isInOamDmaConflictArea(const OamDmaSrc oamDmaSrc, const unsigned add
unsigned Memory::nontrivial_read(const unsigned P, const unsigned long cycleCounter) {
if (P < 0xFF80) {
if (lastOamDmaUpdate != DISABLED_TIME) {
if (lastOamDmaUpdate != disabled_time) {
updateOamDma(cycleCounter);
if (isInOamDmaConflictArea(cart.oamDmaSrc(), P, isCgb()) && oamDmaPos < 0xA0)
@ -626,7 +626,7 @@ unsigned Memory::nontrivial_ff_peek(const unsigned P) {
}
void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned long cycleCounter) {
if (lastOamDmaUpdate != DISABLED_TIME)
if (lastOamDmaUpdate != disabled_time)
updateOamDma(cycleCounter);
switch (P & 0xFF) {
@ -645,7 +645,7 @@ void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned
serialCnt = 8;
intreq.setEventTime<SERIAL>((data & 0x81) == 0x81
? (data & isCgb() * 2 ? (cycleCounter & ~0x7ul) + 0x10 * 8 : (cycleCounter & ~0xFFul) + 0x200 * 8)
: static_cast<unsigned long>(DISABLED_TIME));
: static_cast<unsigned long>(disabled_time));
data |= 0x7E - isCgb() * 2;
break;
@ -865,7 +865,7 @@ void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned
display.lycRegChange(data, cycleCounter);
break;
case 0x46:
if (lastOamDmaUpdate != DISABLED_TIME)
if (lastOamDmaUpdate != disabled_time)
endOamDma(cycleCounter);
lastOamDmaUpdate = cycleCounter;
@ -1020,7 +1020,7 @@ void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned
}
void Memory::nontrivial_write(const unsigned P, const unsigned data, const unsigned long cycleCounter) {
if (lastOamDmaUpdate != DISABLED_TIME) {
if (lastOamDmaUpdate != disabled_time) {
updateOamDma(cycleCounter);
if (isInOamDmaConflictArea(cart.oamDmaSrc(), P, isCgb()) && oamDmaPos < 0xA0) {

View File

@ -125,7 +125,7 @@ public:
void setLayers(unsigned mask) { display.setLayers(mask); }
bool isActive() const { return intreq.eventTime(END) != DISABLED_TIME; }
bool isActive() const { return intreq.eventTime(END) != disabled_time; }
long cyclesSinceBlit(const unsigned long cc) const {
return cc < intreq.eventTime(BLIT) ? -1 : static_cast<long>((cc - intreq.eventTime(BLIT)) >> isDoubleSpeed());

View File

@ -25,7 +25,7 @@ namespace gambatte {
Tima::Tima() :
lastUpdate_(0),
tmatime_(DISABLED_TIME),
tmatime_(disabled_time),
tima_(0),
tma_(0),
tac_(0)
@ -41,11 +41,11 @@ void Tima::loadState(const SaveState &state, const TimaInterruptRequester timaIr
timaIrq.setNextIrqEventTime((tac_ & 4)
?
(tmatime_ != DISABLED_TIME && tmatime_ > state.cpu.cycleCounter
(tmatime_ != disabled_time && tmatime_ > state.cpu.cycleCounter
? tmatime_
: lastUpdate_ + ((256u - tima_) << timaClock[tac_ & 3]) + 3)
:
static_cast<unsigned long>(DISABLED_TIME)
static_cast<unsigned long>(disabled_time)
);
}
@ -59,7 +59,7 @@ void Tima::resetCc(const unsigned long oldCc, const unsigned long newCc, const T
lastUpdate_ -= dec;
timaIrq.setNextIrqEventTime(timaIrq.nextIrqEventTime() - dec);
if (tmatime_ != DISABLED_TIME)
if (tmatime_ != disabled_time)
tmatime_ -= dec;
}
}
@ -71,7 +71,7 @@ void Tima::updateTima(const unsigned long cycleCounter) {
if (cycleCounter >= tmatime_) {
if (cycleCounter >= tmatime_ + 4)
tmatime_ = DISABLED_TIME;
tmatime_ = disabled_time;
tima_ = tma_;
}
@ -87,7 +87,7 @@ void Tima::updateTima(const unsigned long cycleCounter) {
if (cycleCounter >= tmatime_) {
if (cycleCounter >= tmatime_ + 4)
tmatime_ = DISABLED_TIME;
tmatime_ = disabled_time;
tmp = tma_;
}
@ -102,7 +102,7 @@ void Tima::setTima(const unsigned data, const unsigned long cycleCounter, const
updateTima(cycleCounter);
if (tmatime_ - cycleCounter < 4)
tmatime_ = DISABLED_TIME;
tmatime_ = disabled_time;
timaIrq.setNextIrqEventTime(lastUpdate_ + ((256u - data) << timaClock[tac_ & 3]) + 3);
}
@ -136,8 +136,8 @@ void Tima::setTac(const unsigned data, const unsigned long cycleCounter, const T
updateTima(cycleCounter);
tmatime_ = DISABLED_TIME;
nextIrqEventTime = DISABLED_TIME;
tmatime_ = disabled_time;
nextIrqEventTime = disabled_time;
}
if (data & 4) {

View File

@ -76,7 +76,7 @@ void LCD::setCgb(bool cgb) {
static unsigned long mode2IrqSchedule(const unsigned statReg, const LyCounter &lyCounter, const unsigned long cycleCounter) {
if (!(statReg & 0x20))
return DISABLED_TIME;
return disabled_time;
unsigned next = lyCounter.time() - cycleCounter;
@ -127,20 +127,20 @@ void LCD::loadState(const SaveState &state, const unsigned char *const oamram) {
lycIrq.reschedule(ppu.lyCounter(), ppu.now());
eventTimes_.setm<ONESHOT_LCDSTATIRQ>(state.ppu.pendingLcdstatIrq
? ppu.now() + 1 : static_cast<unsigned long>(DISABLED_TIME));
? ppu.now() + 1 : static_cast<unsigned long>(disabled_time));
eventTimes_.setm<ONESHOT_UPDATEWY2>(state.ppu.oldWy != state.mem.ioamhram.get()[0x14A]
? ppu.now() + 1 : static_cast<unsigned long>(DISABLED_TIME));
? ppu.now() + 1 : static_cast<unsigned long>(disabled_time));
eventTimes_.set<LY_COUNT>(ppu.lyCounter().time());
eventTimes_.setm<SPRITE_MAP>(SpriteMapper::schedule(ppu.lyCounter(), ppu.now()));
eventTimes_.setm<LYC_IRQ>(lycIrq.time());
eventTimes_.setm<MODE1_IRQ>(ppu.lyCounter().nextFrameCycle(144 * 456, ppu.now()));
eventTimes_.setm<MODE2_IRQ>(mode2IrqSchedule(statReg, ppu.lyCounter(), ppu.now()));
eventTimes_.setm<MODE0_IRQ>((statReg & 0x08) ? ppu.now() + state.ppu.nextM0Irq : static_cast<unsigned long>(DISABLED_TIME));
eventTimes_.setm<MODE0_IRQ>((statReg & 0x08) ? ppu.now() + state.ppu.nextM0Irq : static_cast<unsigned long>(disabled_time));
eventTimes_.setm<HDMA_REQ>(state.mem.hdmaTransfer
? nextHdmaTime(ppu.lastM0Time(), nextM0Time_.predictedNextM0Time(), ppu.now(), isDoubleSpeed())
: static_cast<unsigned long>(DISABLED_TIME));
: static_cast<unsigned long>(disabled_time));
} else for (int i = 0; i < NUM_MEM_EVENTS; ++i)
eventTimes_.set(MemEvent(i), DISABLED_TIME);
eventTimes_.set(MemEvent(i), disabled_time);
refreshPalettes();
}
@ -218,7 +218,7 @@ void LCD::resetCc(const unsigned long oldCc, const unsigned long newCc) {
lycIrq.reschedule(ppu.lyCounter(), newCc);
for (int i = 0; i < NUM_MEM_EVENTS; ++i) {
if (eventTimes_(MemEvent(i)) != DISABLED_TIME)
if (eventTimes_(MemEvent(i)) != disabled_time)
eventTimes_.set(MemEvent(i), eventTimes_(MemEvent(i)) - dec);
}
@ -240,7 +240,7 @@ void LCD::speedChange(const unsigned long cycleCounter) {
eventTimes_.setm<MODE1_IRQ>(ppu.lyCounter().nextFrameCycle(144 * 456, cycleCounter));
eventTimes_.setm<MODE2_IRQ>(mode2IrqSchedule(statReg, ppu.lyCounter(), cycleCounter));
if (eventTimes_(MODE0_IRQ) != DISABLED_TIME && eventTimes_(MODE0_IRQ) - cycleCounter > 1)
if (eventTimes_(MODE0_IRQ) != disabled_time && eventTimes_(MODE0_IRQ) - cycleCounter > 1)
eventTimes_.setm<MODE0_IRQ>(m0IrqTimeFromXpos166Time(ppu.predictedNextXposTime(166), ppu.cgb(), isDoubleSpeed()));
if (hdmaIsEnabled() && eventTimes_(HDMA_REQ) - cycleCounter > 1) {
@ -294,7 +294,7 @@ void LCD::disableHdma(const unsigned long cycleCounter) {
if (cycleCounter >= eventTimes_.nextEventTime())
update(cycleCounter);
eventTimes_.setm<HDMA_REQ>(DISABLED_TIME);
eventTimes_.setm<HDMA_REQ>(disabled_time);
}
bool LCD::vramAccessible(const unsigned long cycleCounter) {
@ -365,12 +365,12 @@ bool LCD::oamWritable(const unsigned long cycleCounter) {
void LCD::mode3CyclesChange() {
nextM0Time_.invalidatePredictedNextM0Time();
if (eventTimes_(MODE0_IRQ) != DISABLED_TIME
if (eventTimes_(MODE0_IRQ) != disabled_time
&& eventTimes_(MODE0_IRQ) > m0IrqTimeFromXpos166Time(ppu.now(), ppu.cgb(), isDoubleSpeed())) {
eventTimes_.setm<MODE0_IRQ>(m0IrqTimeFromXpos166Time(ppu.predictedNextXposTime(166), ppu.cgb(), isDoubleSpeed()));
}
if (eventTimes_(HDMA_REQ) != DISABLED_TIME
if (eventTimes_(HDMA_REQ) != disabled_time
&& eventTimes_(HDMA_REQ) > hdmaTimeFromM0Time(ppu.lastM0Time(), isDoubleSpeed())) {
nextM0Time_.predictNextM0Time(ppu);
eventTimes_.setm<HDMA_REQ>(hdmaTimeFromM0Time(nextM0Time_.predictedNextM0Time(), isDoubleSpeed()));
@ -456,7 +456,7 @@ void LCD::lcdcChange(const unsigned data, const unsigned long cycleCounter) {
nextM0Time_.predictedNextM0Time(), cycleCounter, isDoubleSpeed()));
}
} else for (int i = 0; i < NUM_MEM_EVENTS; ++i)
eventTimes_.set(MemEvent(i), DISABLED_TIME);
eventTimes_.set(MemEvent(i), disabled_time);
} else if (data & 0x80) {
if (ppu.cgb()) {
ppu.setLcdc((oldLcdc & ~0x14) | (data & 0x14), cycleCounter);
@ -557,7 +557,7 @@ void LCD::lcdstatChange(unsigned const data, unsigned long const cycleCounter) {
}
}
if ((data & 0x08) && eventTimes_(MODE0_IRQ) == DISABLED_TIME) {
if ((data & 0x08) && eventTimes_(MODE0_IRQ) == disabled_time) {
update(cycleCounter);
eventTimes_.setm<MODE0_IRQ>(m0IrqTimeFromXpos166Time(ppu.predictedNextXposTime(166), ppu.cgb(), isDoubleSpeed()));
}
@ -712,18 +712,18 @@ inline void LCD::event() {
eventTimes_.setm<MODE0_IRQ>(statReg & 0x08
? m0IrqTimeFromXpos166Time(ppu.predictedNextXposTime(166), ppu.cgb(), isDoubleSpeed())
: static_cast<unsigned long>(DISABLED_TIME));
: static_cast<unsigned long>(disabled_time));
break;
case ONESHOT_LCDSTATIRQ:
eventTimes_.flagIrq(2);
eventTimes_.setm<ONESHOT_LCDSTATIRQ>(DISABLED_TIME);
eventTimes_.setm<ONESHOT_LCDSTATIRQ>(disabled_time);
break;
case ONESHOT_UPDATEWY2:
ppu.updateWy2();
mode3CyclesChange();
eventTimes_.setm<ONESHOT_UPDATEWY2>(DISABLED_TIME);
eventTimes_.setm<ONESHOT_UPDATEWY2>(disabled_time);
break;
}

View File

@ -21,6 +21,7 @@
#include "video/ppu.h"
#include "video/lyc_irq.h"
#include "video/m0_irq.h"
#include "video/next_m0_time.h"
#include "interruptrequester.h"
#include "minkeeper.h"
@ -39,53 +40,6 @@ public:
void setNextEventTime(const unsigned long time) const { intreq->setEventTime<VIDEO>(time); }
};
class M0Irq {
unsigned char statReg_;
unsigned char lycReg_;
public:
M0Irq() : statReg_(0), lycReg_(0) {}
void lcdReset(const unsigned statReg, const unsigned lycReg) {
statReg_ = statReg;
lycReg_ = lycReg;
}
void statRegChange(const unsigned statReg,
const unsigned long nextM0IrqTime, const unsigned long cc, const bool cgb) {
if (nextM0IrqTime - cc > cgb * 2U)
statReg_ = statReg;
}
void lycRegChange(const unsigned lycReg,
const unsigned long nextM0IrqTime, const unsigned long cc, const bool ds, const bool cgb) {
if (nextM0IrqTime - cc > cgb * 5 + 1U - ds)
lycReg_ = lycReg;
}
void doEvent(unsigned char *const ifreg, const unsigned ly, const unsigned statReg, const unsigned lycReg) {
if (((statReg_ | statReg) & lcdstat_m0irqen) && (!(statReg_ & lcdstat_lycirqen) || ly != lycReg_))
*ifreg |= 2;
statReg_ = statReg;
lycReg_ = lycReg;
}
void loadState(const SaveState &state) {
lycReg_ = state.ppu.m0lyc;
statReg_ = state.mem.ioamhram.get()[0x141];
}
unsigned statReg() const { return statReg_; }
template<bool isReader>
void SyncState(NewState *ns)
{
NSS(statReg_);
NSS(lycReg_);
}
};
class LCD {
enum Event { MEM_EVENT, LY_COUNT }; enum { NUM_EVENTS = LY_COUNT + 1 };
enum MemEvent { ONESHOT_LCDSTATIRQ, ONESHOT_UPDATEWY2, MODE1_IRQ, LYC_IRQ, SPRITE_MAP,
@ -264,7 +218,7 @@ public:
void enableHdma(unsigned long cycleCounter);
void disableHdma(unsigned long cycleCounter);
bool hdmaIsEnabled() const { return eventTimes_(HDMA_REQ) != DISABLED_TIME; }
bool hdmaIsEnabled() const { return eventTimes_(HDMA_REQ) != disabled_time; }
void update(unsigned long cycleCounter);

View File

@ -1,28 +1,31 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License version 2 for more details.
//
// You should have received a copy of the GNU General Public License
// version 2 along with this program; if not, write to the
// Free Software Foundation, Inc.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "ly_counter.h"
#include "../savestate.h"
namespace gambatte {
LyCounter::LyCounter()
: time_(0), lineTime_(0), ly_(0), ds(false)
: time_(0)
, lineTime_(0)
, ly_(0)
, ds_(false)
{
setDoubleSpeed(false);
reset(0, 0);
@ -30,39 +33,36 @@ LyCounter::LyCounter()
void LyCounter::doEvent() {
++ly_;
if (ly_ == 154)
ly_ = 0;
time_ = time_ + lineTime_;
}
unsigned long LyCounter::nextLineCycle(const unsigned lineCycle, const unsigned long cycleCounter) const {
unsigned long tmp = time_ + (lineCycle << ds);
if (tmp - cycleCounter > lineTime_)
unsigned long LyCounter::nextLineCycle(unsigned const lineCycle, unsigned long const cc) const {
unsigned long tmp = time_ + (lineCycle << ds_);
if (tmp - cc > lineTime_)
tmp -= lineTime_;
return tmp;
}
unsigned long LyCounter::nextFrameCycle(const unsigned long frameCycle, const unsigned long cycleCounter) const {
unsigned long tmp = time_ + (((153U - ly()) * 456U + frameCycle) << ds);
if (tmp - cycleCounter > 70224U << ds)
tmp -= 70224U << ds;
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_;
return tmp;
}
void LyCounter::reset(const unsigned long videoCycles, const unsigned long lastUpdate) {
void LyCounter::reset(unsigned long videoCycles, unsigned long lastUpdate) {
ly_ = videoCycles / 456;
time_ = lastUpdate + ((456 - (videoCycles - ly_ * 456ul)) << isDoubleSpeed());
}
void LyCounter::setDoubleSpeed(const bool ds_in) {
ds = ds_in;
lineTime_ = 456U << ds_in;
void LyCounter::setDoubleSpeed(bool ds) {
ds_ = ds;
lineTime_ = 456U << ds;
}
SYNCFUNC(LyCounter)
@ -70,7 +70,7 @@ SYNCFUNC(LyCounter)
NSS(time_);
NSS(lineTime_);
NSS(ly_);
NSS(ds);
NSS(ds_);
}
}

View File

@ -1,21 +1,21 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License version 2 for more details.
//
// You should have received a copy of the GNU General Public License
// version 2 along with this program; if not, write to the
// Free Software Foundation, Inc.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef LY_COUNTER_H
#define LY_COUNTER_H
@ -26,21 +26,16 @@ namespace gambatte {
struct SaveState;
class LyCounter {
unsigned long time_;
unsigned short lineTime_;
unsigned char ly_;
bool ds;
public:
LyCounter();
void doEvent();
bool isDoubleSpeed() const { return ds; }
bool isDoubleSpeed() const { return ds_; }
unsigned long frameCycles(const unsigned long cc) const {
unsigned long frameCycles(unsigned long cc) const {
return ly_ * 456ul + lineCycles(cc);
}
unsigned lineCycles(const unsigned long cc) const {
unsigned lineCycles(unsigned long cc) const {
return 456u - ((time_ - cc) >> isDoubleSpeed());
}
@ -49,9 +44,16 @@ public:
unsigned long nextLineCycle(unsigned lineCycle, unsigned long cycleCounter) const;
unsigned long nextFrameCycle(unsigned long frameCycle, unsigned long cycleCounter) const;
void reset(unsigned long videoCycles, unsigned long lastUpdate);
void setDoubleSpeed(bool ds_in);
void setDoubleSpeed(bool ds);
unsigned long time() const { return time_; }
private:
unsigned long time_;
unsigned short lineTime_;
unsigned char ly_;
bool ds_;
public:
template<bool isReader>void SyncState(NewState *ns);
};

View File

@ -1,47 +1,50 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License version 2 for more details.
//
// You should have received a copy of the GNU General Public License
// version 2 along with this program; if not, write to the
// Free Software Foundation, Inc.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "lyc_irq.h"
#include "counterdef.h"
#include "lcddef.h"
#include "ly_counter.h"
#include "savestate.h"
#include <algorithm>
namespace gambatte {
LycIrq::LycIrq() :
time_(DISABLED_TIME),
lycRegSrc_(0),
statRegSrc_(0),
lycReg_(0),
statReg_(0),
cgb_(false)
LycIrq::LycIrq()
: time_(disabled_time)
, lycRegSrc_(0)
, statRegSrc_(0)
, lycReg_(0)
, statReg_(0)
, cgb_(false)
{
}
static unsigned long schedule(const unsigned statReg, const unsigned lycReg, const LyCounter &lyCounter, const unsigned long cc) {
return (statReg & 0x40) && lycReg < 154
? lyCounter.nextFrameCycle(lycReg ? lycReg * 456 : 153 * 456 + 8, cc)
: static_cast<unsigned long>(DISABLED_TIME);
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(const unsigned statReg, const unsigned lycReg, const LyCounter &lyCounter, const unsigned long cc) {
const unsigned long timeSrc = schedule(statReg, lycReg, lyCounter, cc);
void LycIrq::regChange(unsigned const statReg,
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);
@ -59,18 +62,21 @@ void LycIrq::regChange(const unsigned statReg, const unsigned lycReg, const LyCo
if (time_ - cc > 4 || lycReg_ != 0)
statReg_ = statReg;
statReg_ = (statReg_ & 0x40) | (statReg & ~0x40);
statReg_ = (statReg_ & lcdstat_lycirqen) | (statReg & ~lcdstat_lycirqen);
}
}
void LycIrq::doEvent(unsigned char *const ifreg, const LyCounter &lyCounter) {
if ((statReg_ | statRegSrc_) & 0x40) {
const unsigned cmpLy = lyCounter.time() - time_ < lyCounter.lineTime() ? 0 : lyCounter.ly();
static bool lycIrqBlockedByM2OrM1StatIrq(unsigned ly, unsigned statreg) {
return ly - 1u < 144u - 1u
? statreg & lcdstat_m2irqen
: statreg & lcdstat_m1irqen;
}
if (lycReg_ == cmpLy &&
(lycReg_ - 1U < 144U - 1U ? !(statReg_ & 0x20) : !(statReg_ & 0x10))) {
void LycIrq::doEvent(unsigned char *const ifreg, LyCounter const &lyCounter) {
if ((statReg_ | statRegSrc_) & lcdstat_lycirqen) {
unsigned cmpLy = lyCounter.time() - time_ < lyCounter.lineTime() ? 0 : lyCounter.ly();
if (lycReg_ == cmpLy && !lycIrqBlockedByM2OrM1StatIrq(lycReg_, statReg_))
*ifreg |= 2;
}
}
lycReg_ = lycRegSrc_;
@ -78,14 +84,14 @@ void LycIrq::doEvent(unsigned char *const ifreg, const LyCounter &lyCounter) {
time_ = schedule(statReg_, lycReg_, lyCounter, time_);
}
void LycIrq::loadState(const SaveState &state) {
void LycIrq::loadState(SaveState const &state) {
lycRegSrc_ = state.mem.ioamhram.get()[0x145];
statRegSrc_ = state.mem.ioamhram.get()[0x141];
lycReg_ = state.ppu.lyc;
statReg_ = statRegSrc_;
}
void LycIrq::reschedule(const LyCounter & lyCounter, const unsigned long cc) {
void LycIrq::reschedule(LyCounter const &lyCounter, unsigned long cc) {
time_ = std::min(schedule(statReg_ , lycReg_ , lyCounter, cc),
schedule(statRegSrc_, lycRegSrc_, lyCounter, cc));
}

View File

@ -1,21 +1,21 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License version 2 for more details.
//
// You should have received a copy of the GNU General Public License
// version 2 along with this program; if not, write to the
// Free Software Foundation, Inc.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef VIDEO_LYC_IRQ_H
#define VIDEO_LYC_IRQ_H
@ -27,6 +27,25 @@ struct SaveState;
class LyCounter;
class LycIrq {
public:
LycIrq();
void doEvent(unsigned char *ifreg, LyCounter const &lyCounter);
unsigned lycReg() const { return lycRegSrc_; }
void loadState(SaveState const &state);
unsigned long time() const { return time_; }
void setCgb(bool cgb) { cgb_ = cgb; }
void lcdReset();
void reschedule(LyCounter const &lyCounter, unsigned long cc);
void statRegChange(unsigned statReg, LyCounter const &lyCounter, unsigned long cc) {
regChange(statReg, lycRegSrc_, lyCounter, cc);
}
void lycRegChange(unsigned lycReg, LyCounter const &lyCounter, unsigned long cc) {
regChange(statRegSrc_, lycReg, lyCounter, cc);
}
private:
unsigned long time_;
unsigned char lycRegSrc_;
unsigned char statRegSrc_;
@ -34,26 +53,10 @@ class LycIrq {
unsigned char statReg_;
bool cgb_;
void regChange(unsigned statReg, unsigned lycReg, const LyCounter &lyCounter, unsigned long cc);
void regChange(unsigned statReg, unsigned lycReg,
LyCounter const &lyCounter, unsigned long cc);
public:
LycIrq();
void doEvent(unsigned char *ifreg, const LyCounter &lyCounter);
unsigned lycReg() const { return lycRegSrc_; }
void loadState(const SaveState &state);
unsigned long time() const { return time_; }
void setCgb(const bool cgb) { cgb_ = cgb; }
void lcdReset();
void reschedule(const LyCounter & lyCounter, unsigned long cc);
void statRegChange(unsigned statReg, const LyCounter &lyCounter, unsigned long cc) {
regChange(statReg, lycRegSrc_, lyCounter, cc);
}
void lycRegChange(unsigned lycReg, const LyCounter &lyCounter, unsigned long cc) {
regChange(statRegSrc_, lycReg, lyCounter, cc);
}
template<bool isReader>void SyncState(NewState *ns);
};

View File

@ -0,0 +1,68 @@
#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

@ -1,15 +1,11 @@
#include "next_m0_time.h"
#include "ppu.h"
namespace gambatte {
void NextM0Time::predictNextM0Time(const PPU &ppu) {
void gambatte::NextM0Time::predictNextM0Time(PPU const &ppu) {
predictedNextM0Time_ = ppu.predictedNextXposTime(167);
}
SYNCFUNC(NextM0Time)
SYNCFUNC(gambatte::NextM0Time)
{
NSS(predictedNextM0Time_);
}
}

View File

@ -6,14 +6,16 @@
namespace gambatte {
class NextM0Time {
unsigned predictedNextM0Time_;
public:
NextM0Time() : predictedNextM0Time_(0) {}
void predictNextM0Time(const class PPU &v);
void predictNextM0Time(class PPU const &v);
void invalidatePredictedNextM0Time() { predictedNextM0Time_ = 0; }
unsigned predictedNextM0Time() const { return predictedNextM0Time_; }
private:
unsigned predictedNextM0Time_;
public:
template<bool isReader>void SyncState(NewState *ns);
};

View File

@ -1,28 +1,29 @@
/***************************************************************************
* Copyright (C) 2010 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
//
// Copyright (C) 2010 by sinamas <sinamas at users.sourceforge.net>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License version 2 for more details.
//
// You should have received a copy of the GNU General Public License
// version 2 along with this program; if not, write to the
// Free Software Foundation, Inc.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef PPU_H
#define PPU_H
#include "lcddef.h"
#include "video/ly_counter.h"
#include "video/sprite_mapper.h"
#include "ly_counter.h"
#include "sprite_mapper.h"
#include "gbint.h"
#include <cstddef>
#include "newstate.h"
@ -31,28 +32,30 @@ namespace gambatte {
enum { LAYER_MASK_BG = 1, LAYER_MASK_OBJ = 2, LAYER_MASK_WINDOW = 4 };
class PPUFrameBuf {
uint_least32_t *buf_;
uint_least32_t *fbline_;
int pitch_;
static uint_least32_t * nullfbline() { static uint_least32_t nullfbline_[160]; return nullfbline_; }
public:
PPUFrameBuf() : buf_(0), fbline_(nullfbline()), pitch_(0) {}
uint_least32_t * fb() const { return buf_; }
uint_least32_t * fbline() const { return fbline_; }
int pitch() const { return pitch_; }
void setBuf(uint_least32_t *const buf, const int pitch) { buf_ = buf; pitch_ = pitch; fbline_ = nullfbline(); }
void setFbline(const unsigned ly) { fbline_ = buf_ ? buf_ + static_cast<long>(ly) * static_cast<long>(pitch_) : nullfbline(); }
std::ptrdiff_t pitch() const { return pitch_; }
void setBuf(uint_least32_t *buf, std::ptrdiff_t pitch) { buf_ = buf; pitch_ = pitch; fbline_ = nullfbline(); }
void setFbline(unsigned ly) { fbline_ = buf_ ? buf_ + std::ptrdiff_t(ly) * pitch_ : nullfbline(); }
private:
uint_least32_t *buf_;
uint_least32_t *fbline_;
std::ptrdiff_t pitch_;
static uint_least32_t * nullfbline() { static uint_least32_t nullfbline_[160]; return nullfbline_; }
};
struct PPUPriv;
struct PPUState {
void (*f)(struct PPUPriv &v);
unsigned (*predictCyclesUntilXpos_f)(const struct PPUPriv &v, int targetxpos, unsigned cycles);
void (*f)(PPUPriv &v);
unsigned (*predictCyclesUntilXpos_f)(PPUPriv const &v, int targetxpos, unsigned cycles);
unsigned char id;
};
// The PPU loop accesses a lot of state at once, so it's difficult to split this up much beyond grouping stuff into smaller structs.
struct PPUPriv {
unsigned long bgPalette[8 * 4];
unsigned long spPalette[8 * 4];
@ -62,8 +65,8 @@ struct PPUPriv {
unsigned char currentSprite;
unsigned layersMask;
const unsigned char *vram;
const PPUState *nextCallPtr;
unsigned char const *vram;
PPUState const *nextCallPtr;
unsigned long now;
unsigned long lastM0Time;
@ -95,13 +98,12 @@ struct PPUPriv {
bool cgb;
bool weMaster;
PPUPriv(NextM0Time &nextM0Time, const unsigned char *oamram, const unsigned char *vram);
PPUPriv(NextM0Time &nextM0Time, unsigned char const *oamram, unsigned char const *vram);
};
class PPU {
PPUPriv p_;
public:
PPU(NextM0Time &nextM0Time, const unsigned char *oamram, const unsigned char *vram)
PPU(NextM0Time &nextM0Time, unsigned char const *oamram, unsigned char const *vram)
: p_(nextM0Time, oamram, vram)
{
}
@ -110,25 +112,29 @@ public:
bool cgb() const { return p_.cgb; }
void doLyCountEvent() { p_.lyCounter.doEvent(); }
unsigned long doSpriteMapEvent(unsigned long time) { return p_.spriteMapper.doEvent(time); }
const PPUFrameBuf & frameBuf() const { return p_.framebuf; }
bool inactivePeriodAfterDisplayEnable(unsigned long cc) const { return p_.spriteMapper.inactivePeriodAfterDisplayEnable(cc); }
PPUFrameBuf const & frameBuf() const { return p_.framebuf; }
bool inactivePeriodAfterDisplayEnable(unsigned long cc) const {
return p_.spriteMapper.inactivePeriodAfterDisplayEnable(cc);
}
unsigned long lastM0Time() const { return p_.lastM0Time; }
unsigned lcdc() const { return p_.lcdc; }
void loadState(const SaveState &state, const unsigned char *oamram);
const LyCounter & lyCounter() const { return p_.lyCounter; }
void loadState(SaveState const &state, unsigned char const *oamram);
LyCounter const & lyCounter() const { return p_.lyCounter; }
unsigned long now() const { return p_.now; }
void oamChange(unsigned long cc) { p_.spriteMapper.oamChange(cc); }
void oamChange(const unsigned char *oamram, unsigned long cc) { p_.spriteMapper.oamChange(oamram, cc); }
void oamChange(unsigned char const *oamram, unsigned long cc) { p_.spriteMapper.oamChange(oamram, cc); }
unsigned long predictedNextXposTime(unsigned xpos) const;
void reset(const unsigned char *oamram, const unsigned char *vram, bool cgb);
void reset(unsigned char const *oamram, unsigned char const *vram, bool cgb);
void resetCc(unsigned long oldCc, unsigned long newCc);
void setFrameBuf(uint_least32_t *buf, unsigned pitch) { p_.framebuf.setBuf(buf, pitch); }
void setFrameBuf(uint_least32_t *buf, std::ptrdiff_t pitch) { p_.framebuf.setBuf(buf, pitch); }
void setLcdc(unsigned lcdc, unsigned long cc);
void setScx(const unsigned scx) { p_.scx = scx; }
void setScy(const unsigned scy) { p_.scy = scy; }
void setScx(unsigned scx) { p_.scx = scx; }
void setScy(unsigned scy) { p_.scy = scy; }
void setStatePtrs(SaveState &ss) { p_.spriteMapper.setStatePtrs(ss); }
void setWx(const unsigned wx) { p_.wx = wx; }
void setWy(const unsigned wy) { p_.wy = wy; }
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);
unsigned long * spPalette() { return p_.spPalette; }
@ -136,6 +142,10 @@ public:
void setLayers(unsigned mask) { p_.layersMask = mask; }
void setCgb(bool cgb) { p_.cgb = cgb; }
private:
PPUPriv p_;
public:
template<bool isReader>void SyncState(NewState *ns);
};

View File

@ -1,81 +1,96 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License version 2 for more details.
//
// You should have received a copy of the GNU General Public License
// version 2 along with this program; if not, write to the
// Free Software Foundation, Inc.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "sprite_mapper.h"
#include "counterdef.h"
#include "next_m0_time.h"
#include "../insertion_sort.h"
#include <cstring>
#include <algorithm>
#include <cstring>
namespace {
class SpxLess {
public:
explicit SpxLess(unsigned char const *spxlut) : spxlut_(spxlut) {}
bool operator()(unsigned char lhs, unsigned char rhs) const {
return spxlut_[lhs] < spxlut_[rhs];
}
private:
unsigned char const *const spxlut_;
};
}
namespace gambatte {
SpriteMapper::OamReader::OamReader(const LyCounter &lyCounter, const unsigned char *oamram)
: lyCounter(lyCounter), oamram(oamram), cgb_(false) {
SpriteMapper::OamReader::OamReader(LyCounter const &lyCounter, unsigned char const *oamram)
: lyCounter_(lyCounter)
, oamram_(oamram)
, cgb_(false)
{
reset(oamram, false);
}
void SpriteMapper::OamReader::reset(const unsigned char *const oamram, const bool cgb) {
this->oamram = oamram;
this->cgb_ = 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_n(szbuf, 40, largeSpritesSrc);
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)];
buf_[pos] = oamram[((pos * 2) & ~3) | (pos & 1)];
++pos;
}
}
static unsigned toPosCycles(const unsigned long cc, const LyCounter &lyCounter) {
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(const unsigned long cc) {
if (cc > lu) {
void SpriteMapper::OamReader::update(unsigned long const cc) {
if (cc > lu_) {
if (changed()) {
const unsigned lulc = toPosCycles(lu, lyCounter);
unsigned const lulc = toPosCycles(lu_, lyCounter_);
unsigned pos = std::min(lulc, 80u);
unsigned distance = 80;
if ((cc - lu) >> lyCounter.isDoubleSpeed() < 456) {
const unsigned cclc = toPosCycles(cc, lyCounter);
if ((cc - lu_) >> lyCounter_.isDoubleSpeed() < 456) {
unsigned cclc = toPosCycles(cc, lyCounter_);
distance = std::min(cclc, 80u) - pos + (cclc < lulc ? 80 : 0);
}
{
const unsigned targetDistance = lastChange - pos + (lastChange <= pos ? 80 : 0);
unsigned targetDistance =
lastChange_ - pos + (lastChange_ <= pos ? 80 : 0);
if (targetDistance <= distance) {
distance = targetDistance;
lastChange = 0xFF;
lastChange_ = 0xFF;
}
}
@ -85,91 +100,90 @@ void SpriteMapper::OamReader::update(const unsigned long cc) {
pos = 0;
if (cgb_)
szbuf[pos >> 1] = largeSpritesSrc;
szbuf_[pos >> 1] = largeSpritesSrc_;
buf[pos ] = oamram[pos * 2 ];
buf[pos + 1] = oamram[pos * 2 + 1];
buf_[pos ] = oamram_[pos * 2 ];
buf_[pos + 1] = oamram_[pos * 2 + 1];
} else
szbuf[pos >> 1] = (szbuf[pos >> 1] & cgb_) | largeSpritesSrc;
szbuf_[pos >> 1] = (szbuf_[pos >> 1] & cgb_) | largeSpritesSrc_;
++pos;
}
}
lu = cc;
lu_ = cc;
}
}
void SpriteMapper::OamReader::change(const unsigned long cc) {
void SpriteMapper::OamReader::change(unsigned long cc) {
update(cc);
lastChange = std::min(toPosCycles(lu, lyCounter), 80u);
lastChange_ = std::min(toPosCycles(lu_, lyCounter_), 80u);
}
void SpriteMapper::OamReader::setStatePtrs(SaveState &state) {
state.ppu.oamReaderBuf.set(buf, sizeof buf);
state.ppu.oamReaderSzbuf.set(szbuf, sizeof szbuf / sizeof *szbuf);
state.ppu.oamReaderBuf.set(buf_, sizeof buf_);
state.ppu.oamReaderSzbuf.set(szbuf_, sizeof szbuf_ / sizeof *szbuf_);
}
void SpriteMapper::OamReader::loadState(const SaveState &ss, const unsigned char *const oamram) {
this->oamram = oamram;
largeSpritesSrc = ss.mem.ioamhram.get()[0x140] >> 2 & 1;
lu = ss.ppu.enableDisplayM0Time;
change(lu);
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;
change(lu_);
}
SYNCFUNC(SpriteMapper::OamReader)
{
NSS(buf);
NSS(szbuf);
NSS(buf_);
NSS(szbuf_);
NSS(lu);
NSS(lastChange);
NSS(largeSpritesSrc);
NSS(lu_);
NSS(lastChange_);
NSS(largeSpritesSrc_);
NSS(cgb_);
}
void SpriteMapper::OamReader::enableDisplay(const unsigned long cc) {
std::memset(buf, 0x00, sizeof buf);
std::fill(szbuf, szbuf + 40, false);
lu = cc + (80 << lyCounter.isDoubleSpeed());
lastChange = 80;
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;
}
SpriteMapper::SpriteMapper(NextM0Time &nextM0Time,
const LyCounter &lyCounter,
const unsigned char *const oamram) :
nextM0Time_(nextM0Time),
oamReader(lyCounter, oamram)
LyCounter const &lyCounter,
unsigned char const *oamram)
: nextM0Time_(nextM0Time)
, oamReader_(lyCounter, oamram)
{
clearMap();
}
void SpriteMapper::reset(const unsigned char *const oamram, const bool cgb) {
oamReader.reset(oamram, cgb);
void SpriteMapper::reset(unsigned char const *oamram, bool cgb) {
oamReader_.reset(oamram, cgb);
clearMap();
}
void SpriteMapper::clearMap() {
std::memset(num, NEED_SORTING_MASK, sizeof num);
std::memset(num_, need_sorting_mask, sizeof num_);
}
void SpriteMapper::mapSprites() {
clearMap();
for (unsigned i = 0x00; i < 0x50; i += 2) {
const int spriteHeight = 8 << largeSprites(i >> 1);
const unsigned bottom_pos = posbuf()[i] - (17u - spriteHeight);
int const spriteHeight = 8 << largeSprites(i >> 1);
unsigned const bottomPos = posbuf()[i] - (17u - spriteHeight);
if (bottom_pos < 143u + spriteHeight) {
const unsigned startly = static_cast<int>(bottom_pos) + 1 - spriteHeight >= 0
? static_cast<int>(bottom_pos) + 1 - spriteHeight : 0;
unsigned char *map = spritemap + startly * 10;
unsigned char *n = num + startly;
unsigned char *const nend = num + (bottom_pos < 143 ? bottom_pos : 143) + 1;
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;
do {
if (*n < NEED_SORTING_MASK + 10)
map[(*n)++ - NEED_SORTING_MASK] = i;
if (*n < need_sorting_mask + 10)
map[(*n)++ - need_sorting_mask] = i;
map += 10;
} while (++n != nend);
@ -179,24 +193,27 @@ void SpriteMapper::mapSprites() {
nextM0Time_.invalidatePredictedNextM0Time();
}
void SpriteMapper::sortLine(const unsigned ly) const {
num[ly] &= ~NEED_SORTING_MASK;
insertionSort(spritemap + ly * 10, spritemap + ly * 10 + num[ly], SpxLess(posbuf()));
void SpriteMapper::sortLine(unsigned const ly) const {
num_[ly] &= ~need_sorting_mask;
insertionSort(spritemap_ + ly * 10, spritemap_ + ly * 10 + num_[ly],
SpxLess(posbuf() + 1));
}
unsigned long SpriteMapper::doEvent(const unsigned long time) {
oamReader.update(time);
unsigned long SpriteMapper::doEvent(unsigned long const time) {
oamReader_.update(time);
mapSprites();
return oamReader.changed() ? time + oamReader.lyCounter.lineTime() : static_cast<unsigned long>(DISABLED_TIME);
return oamReader_.changed()
? time + oamReader_.lineTime()
: static_cast<unsigned long>(disabled_time);
}
SYNCFUNC(SpriteMapper)
{
NSS(spritemap);
NSS(num);
NSS(spritemap_);
NSS(num_);
SSS(nextM0Time_);
SSS(oamReader);
SSS(oamReader_);
}
}

View File

@ -1,21 +1,21 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License version 2 for more details.
//
// You should have received a copy of the GNU General Public License
// version 2 along with this program; if not, write to the
// Free Software Foundation, Inc.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef SPRITE_MAPPER_H
#define SPRITE_MAPPER_H
@ -24,106 +24,101 @@
#include "newstate.h"
namespace gambatte {
class NextM0Time;
class SpriteMapper {
class OamReader {
unsigned char buf[80];
bool szbuf[40];
public:
SpriteMapper(NextM0Time &nextM0Time,
LyCounter const &lyCounter,
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; }
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);
oamReader_.resetCycleCounter(oldCc, newCc);
}
void setLargeSpritesSource(bool src) { oamReader_.setLargeSpritesSrc(src); }
unsigned char const * sprites(unsigned ly) const {
if (num_[ly] & need_sorting_mask)
sortLine(ly);
return spritemap_ + ly * 10;
}
void setStatePtrs(SaveState &state) { oamReader_.setStatePtrs(state); }
void enableDisplay(unsigned long cc) { oamReader_.enableDisplay(cc); }
void loadState(SaveState const &state, unsigned char const *oamram) {
oamReader_.loadState(state, oamram);
mapSprites();
}
bool inactivePeriodAfterDisplayEnable(unsigned long cc) const {
return oamReader_.inactivePeriodAfterDisplayEnable(cc);
}
static unsigned long schedule(LyCounter const &lyCounter, unsigned long cc) {
return lyCounter.nextLineCycle(80, cc);
}
private:
class OamReader {
public:
const LyCounter &lyCounter;
OamReader(LyCounter const &lyCounter, unsigned char const *oamram);
void reset(unsigned char const *oamram, bool cgb);
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]; }
unsigned char const * oam() const { return oamram_; }
void resetCycleCounter(unsigned long oldCc, unsigned long newCc) { lu_ -= oldCc - newCc; }
void setLargeSpritesSrc(bool src) { largeSpritesSrc_ = src; }
void update(unsigned long cc);
unsigned char const * spritePosBuf() const { return buf_; }
void setStatePtrs(SaveState &state);
void enableDisplay(unsigned long cc);
void loadState(SaveState const &ss, unsigned char const *oamram);
bool inactivePeriodAfterDisplayEnable(unsigned long cc) const { return cc < lu_; }
unsigned lineTime() const { return lyCounter_.lineTime(); }
private:
const unsigned char *oamram;
unsigned long lu;
unsigned char lastChange;
bool largeSpritesSrc;
unsigned char buf_[80];
bool szbuf_[40];
LyCounter const &lyCounter_;
unsigned char const *oamram_;
unsigned long lu_;
unsigned char lastChange_;
bool largeSpritesSrc_;
bool cgb_;
public:
OamReader(const LyCounter &lyCounter, const unsigned char *oamram);
void reset(const unsigned char *oamram, bool cgb);
void change(unsigned long cc);
void change(const unsigned char *oamram, unsigned long cc) { change(cc); this->oamram = oamram; }
bool changed() const { return lastChange != 0xFF; }
bool largeSprites(unsigned spNr) const { return szbuf[spNr]; }
const unsigned char *oam() const { return oamram; }
void resetCycleCounter(const unsigned long oldCc, const unsigned long newCc) { lu -= oldCc - newCc; }
void setLargeSpritesSrc(const bool src) { largeSpritesSrc = src; }
void update(unsigned long cc);
const unsigned char *spritePosBuf() const { return buf; }
void setStatePtrs(SaveState &state);
void enableDisplay(unsigned long cc);
void loadState(const SaveState &ss, const unsigned char *oamram);
bool inactivePeriodAfterDisplayEnable(const unsigned long cc) const { return cc < lu; }
template<bool isReader>void SyncState(NewState *ns);
};
enum { NEED_SORTING_MASK = 0x80 };
public:
class SpxLess {
const unsigned char *const posbuf_plus1;
public:
explicit SpxLess(const unsigned char *const posbuf) : posbuf_plus1(posbuf + 1) {}
bool operator()(const unsigned char l, const unsigned char r) const {
return posbuf_plus1[l] < posbuf_plus1[r];
}
};
private:
mutable unsigned char spritemap[144*10];
mutable unsigned char num[144];
enum { need_sorting_mask = 0x80 };
mutable unsigned char spritemap_[144 * 10];
mutable unsigned char num_[144];
NextM0Time &nextM0Time_;
OamReader oamReader;
OamReader oamReader_;
void clearMap();
void mapSprites();
void sortLine(unsigned ly) const;
public:
SpriteMapper(NextM0Time &nextM0Time,
const LyCounter &lyCounter,
const unsigned char *oamram_in);
void reset(const unsigned char *oamram, bool cgb);
unsigned long doEvent(unsigned long time);
bool largeSprites(unsigned spNr) const { return oamReader.largeSprites(spNr); }
unsigned numSprites(const unsigned ly) const { return num[ly] & ~NEED_SORTING_MASK; }
void oamChange(unsigned long cc) { oamReader.change(cc); }
void oamChange(const unsigned char *oamram, unsigned long cc) { oamReader.change(oamram, cc); }
const unsigned char *oamram() const { return oamReader.oam(); }
const unsigned char *posbuf() const { return oamReader.spritePosBuf(); }
void preSpeedChange(const unsigned long cc) { oamReader.update(cc); }
void postSpeedChange(const unsigned long cc) { oamReader.change(cc); }
void resetCycleCounter(const unsigned long oldCc, const unsigned long newCc) {
oamReader.update(oldCc);
oamReader.resetCycleCounter(oldCc, newCc);
}
static unsigned long schedule(const LyCounter &lyCounter, const unsigned long cycleCounter) {
return lyCounter.nextLineCycle(80, cycleCounter);
}
void setLargeSpritesSource(bool src) { oamReader.setLargeSpritesSrc(src); }
const unsigned char* sprites(const unsigned ly) const {
if (num[ly] & NEED_SORTING_MASK)
sortLine(ly);
return spritemap + ly * 10;
}
void setStatePtrs(SaveState &state) { oamReader.setStatePtrs(state); }
void enableDisplay(unsigned long cc) { oamReader.enableDisplay(cc); }
void loadState(const SaveState &state, const unsigned char *const oamram) { oamReader.loadState(state, oamram); mapSprites(); }
bool inactivePeriodAfterDisplayEnable(unsigned long cc) const { return oamReader.inactivePeriodAfterDisplayEnable(cc); }
template<bool isReader>void SyncState(NewState *ns);
};

Binary file not shown.