void Memory::setEndtime(unsigned long cc, unsigned long inc) { if (intreq_.eventTime(intevent_blit) <= cc) { intreq_.setEventTime<intevent_blit>(intreq_.eventTime(intevent_blit) + (70224 << isDoubleSpeed())); } intreq_.setEventTime<intevent_end>(cc + (inc << isDoubleSpeed())); }
unsigned long Memory::stop(unsigned long cycleCounter) { cycleCounter += 4; if (ioamhram[0x14D] & isCgb()) { sound.generate_samples(cycleCounter, isDoubleSpeed()); display.speedChange(cycleCounter); ioamhram[0x14D] ^= 0x81; intreq.setEventTime<BLIT>((ioamhram[0x140] & 0x80) ? display.nextMode1IrqTime() : cycleCounter + (70224 << isDoubleSpeed())); if (intreq.eventTime(END) > cycleCounter) { intreq.setEventTime<END>(cycleCounter + (isDoubleSpeed() ? (intreq.eventTime(END) - cycleCounter) << 1 : (intreq.eventTime(END) - cycleCounter) >> 1)); }
unsigned long Memory::stop(unsigned long cc) { cc += 4 + 4 * isDoubleSpeed(); if (ioamhram_[0x14D] & isCgb()) { psg_.generateSamples(cc, isDoubleSpeed()); lcd_.speedChange(cc); ioamhram_[0x14D] ^= 0x81; intreq_.setEventTime<intevent_blit>(ioamhram_[0x140] & lcdc_en ? lcd_.nextMode1IrqTime() : cc + (70224 << isDoubleSpeed())); if (intreq_.eventTime(intevent_end) > cc) { intreq_.setEventTime<intevent_end>(cc + ( isDoubleSpeed() ? (intreq_.eventTime(intevent_end) - cc) << 1 : (intreq_.eventTime(intevent_end) - cc) >> 1)); }
unsigned long Memory::event(unsigned long cycleCounter) { if (lastOamDmaUpdate != DISABLED_TIME) updateOamDma(cycleCounter); switch (intreq.minEventId()) { case UNHALT: nontrivial_ff_write(0xFF04, 0, cycleCounter); PC = (PC + 1) & 0xFFFF; cycleCounter += 4; intreq.unhalt(); intreq.setEventTime<UNHALT>(DISABLED_TIME); break; case END: intreq.setEventTime<END>(DISABLED_TIME - 1); while (cycleCounter >= intreq.minEventTime() && intreq.eventTime(END) != DISABLED_TIME) cycleCounter = event(cycleCounter); intreq.setEventTime<END>(DISABLED_TIME); break; case BLIT: { const bool lcden = ioamhram[0x140] >> 7 & 1; unsigned long blitTime = intreq.eventTime(BLIT); if (lcden | blanklcd) { display.updateScreen(blanklcd, cycleCounter); intreq.setEventTime<BLIT>(DISABLED_TIME); intreq.setEventTime<END>(DISABLED_TIME); while (cycleCounter >= intreq.minEventTime()) cycleCounter = event(cycleCounter); } else blitTime += 70224 << isDoubleSpeed(); blanklcd = lcden ^ 1; intreq.setEventTime<BLIT>(blitTime); } break; case SERIAL: updateSerial(cycleCounter); break; case OAM: intreq.setEventTime<OAM>(lastOamDmaUpdate == DISABLED_TIME ? static_cast<unsigned long>(DISABLED_TIME) : intreq.eventTime(OAM) + 0xA0 * 4); break; case DMA: { const bool doubleSpeed = isDoubleSpeed(); unsigned dmaSrc = dmaSource; unsigned dmaDest = dmaDestination; unsigned dmaLength = ((ioamhram[0x155] & 0x7F) + 0x1) * 0x10; unsigned length = hdmaReqFlagged(intreq) ? 0x10 : dmaLength; ackDmaReq(&intreq); if ((static_cast<unsigned long>(dmaDest) + length) & 0x10000) { length = 0x10000 - dmaDest; ioamhram[0x155] |= 0x80; } dmaLength -= length; if (!(ioamhram[0x140] & 0x80)) dmaLength = 0; { unsigned long lOamDmaUpdate = lastOamDmaUpdate; lastOamDmaUpdate = DISABLED_TIME; while (length--) { const unsigned src = dmaSrc++ & 0xFFFF; const unsigned data = ((src & 0xE000) == 0x8000 || src > 0xFDFF) ? 0xFF : read(src, cycleCounter); cycleCounter += 2 << doubleSpeed; if (cycleCounter - 3 > lOamDmaUpdate) { oamDmaPos = (oamDmaPos + 1) & 0xFF; lOamDmaUpdate += 4; if (oamDmaPos < 0xA0) { if (oamDmaPos == 0) startOamDma(lOamDmaUpdate - 1); ioamhram[src & 0xFF] = data; } else if (oamDmaPos == 0xA0) { endOamDma(lOamDmaUpdate - 1); lOamDmaUpdate = DISABLED_TIME; } } nontrivial_write(0x8000 | (dmaDest++ & 0x1FFF), data, cycleCounter); } lastOamDmaUpdate = lOamDmaUpdate; } cycleCounter += 4; dmaSource = dmaSrc; dmaDestination = dmaDest; ioamhram[0x155] = ((dmaLength / 0x10 - 0x1) & 0xFF) | (ioamhram[0x155] & 0x80); if ((ioamhram[0x155] & 0x80) && display.hdmaIsEnabled()) { if (lastOamDmaUpdate != DISABLED_TIME) updateOamDma(cycleCounter); display.disableHdma(cycleCounter); } } break; case TIMA: tima.doIrqEvent(TimaInterruptRequester(intreq)); break; case VIDEO: display.update(cycleCounter); break; case INTERRUPTS: if (stopped) { intreq.setEventTime<INTERRUPTS>(DISABLED_TIME); break; } if (halted()) { if (gbIsCgb_ || (!gbIsCgb_ && cycleCounter <= halttime + 4)) cycleCounter += 4; intreq.unhalt(); intreq.setEventTime<UNHALT>(DISABLED_TIME); } if (ime()) { unsigned address; cycleCounter += 12; display.update(cycleCounter); SP = (SP - 2) & 0xFFFF; write(SP + 1, PC >> 8, cycleCounter); unsigned ie = intreq.iereg(); cycleCounter += 4; display.update(cycleCounter); write(SP, PC & 0xFF, cycleCounter); const unsigned pendingIrqs = ie & intreq.ifreg(); cycleCounter += 4; display.update(cycleCounter); const unsigned n = pendingIrqs & -pendingIrqs; if (n == 0) { address = 0; } else if (n < 8) { static const unsigned char lut[] = { 0x40, 0x48, 0x48, 0x50 }; address = lut[n-1]; } else address = 0x50 + n; intreq.ackIrq(n); PC = address; } break; } return cycleCounter; }
void Memory::setEndtime(const unsigned long cycleCounter, const unsigned long inc) { if (intreq.eventTime(BLIT) <= cycleCounter) intreq.setEventTime<BLIT>(intreq.eventTime(BLIT) + (70224 << isDoubleSpeed())); intreq.setEventTime<END>(cycleCounter + (inc << isDoubleSpeed())); }
unsigned long Memory::event(unsigned long cc) { if (lastOamDmaUpdate_ != disabled_time) updateOamDma(cc); switch (intreq_.minEventId()) { case intevent_unhalt: intreq_.unhalt(); intreq_.setEventTime<intevent_unhalt>(disabled_time); break; case intevent_end: intreq_.setEventTime<intevent_end>(disabled_time - 1); while (cc >= intreq_.minEventTime() && intreq_.eventTime(intevent_end) != disabled_time) { cc = event(cc); } intreq_.setEventTime<intevent_end>(disabled_time); break; case intevent_blit: { bool const lcden = ioamhram_[0x140] & lcdc_en; unsigned long blitTime = intreq_.eventTime(intevent_blit); if (lcden | blanklcd_) { lcd_.updateScreen(blanklcd_, cc); intreq_.setEventTime<intevent_blit>(disabled_time); intreq_.setEventTime<intevent_end>(disabled_time); while (cc >= intreq_.minEventTime()) cc = event(cc); } else blitTime += 70224 << isDoubleSpeed(); blanklcd_ = lcden ^ 1; intreq_.setEventTime<intevent_blit>(blitTime); } break; case intevent_serial: updateSerial(cc); break; case intevent_oam: intreq_.setEventTime<intevent_oam>(lastOamDmaUpdate_ == disabled_time ? static_cast<unsigned long>(disabled_time) : intreq_.eventTime(intevent_oam) + 0xA0 * 4); break; case intevent_dma: { bool const doubleSpeed = isDoubleSpeed(); unsigned dmaSrc = dmaSource_; unsigned dmaDest = dmaDestination_; unsigned dmaLength = ((ioamhram_[0x155] & 0x7F) + 0x1) * 0x10; unsigned length = hdmaReqFlagged(intreq_) ? 0x10 : dmaLength; ackDmaReq(intreq_); if ((static_cast<unsigned long>(dmaDest) + length) & 0x10000) { length = 0x10000 - dmaDest; ioamhram_[0x155] |= 0x80; } dmaLength -= length; if (!(ioamhram_[0x140] & lcdc_en)) dmaLength = 0; { unsigned long lOamDmaUpdate = lastOamDmaUpdate_; lastOamDmaUpdate_ = disabled_time; while (length--) { unsigned const src = dmaSrc++ & 0xFFFF; unsigned const data = (src & 0xE000) == 0x8000 || src > 0xFDFF ? 0xFF : read(src, cc); cc += 2 << doubleSpeed; if (cc - 3 > lOamDmaUpdate) { oamDmaPos_ = (oamDmaPos_ + 1) & 0xFF; lOamDmaUpdate += 4; if (oamDmaPos_ < 0xA0) { if (oamDmaPos_ == 0) startOamDma(lOamDmaUpdate - 1); ioamhram_[src & 0xFF] = data; } else if (oamDmaPos_ == 0xA0) { endOamDma(lOamDmaUpdate - 1); lOamDmaUpdate = disabled_time; } } nontrivial_write(0x8000 | (dmaDest++ & 0x1FFF), data, cc); } lastOamDmaUpdate_ = lOamDmaUpdate; } cc += 4; dmaSource_ = dmaSrc; dmaDestination_ = dmaDest; ioamhram_[0x155] = ((dmaLength / 0x10 - 0x1) & 0xFF) | (ioamhram_[0x155] & 0x80); if ((ioamhram_[0x155] & 0x80) && lcd_.hdmaIsEnabled()) { if (lastOamDmaUpdate_ != disabled_time) updateOamDma(cc); lcd_.disableHdma(cc); } } break; case intevent_tima: tima_.doIrqEvent(TimaInterruptRequester(intreq_)); break; case intevent_video: lcd_.update(cc); break; case intevent_interrupts: if (halted()) { if (isCgb()) cc += 4; intreq_.unhalt(); intreq_.setEventTime<intevent_unhalt>(disabled_time); } if (ime()) { unsigned const pendingIrqs = intreq_.pendingIrqs(); unsigned const n = pendingIrqs & -pendingIrqs; unsigned address; if (n <= 4) { static unsigned char const lut[] = { 0x40, 0x48, 0x48, 0x50 }; address = lut[n-1]; } else address = 0x50 + n; intreq_.ackIrq(n); cc = interrupter_.interrupt(address, cc, *this); } break; } return cc; }
void LyCounter::reset(const unsigned long videoCycles, const unsigned long lastUpdate) { ly_ = videoCycles / 456; time_ = lastUpdate + ((456 - (videoCycles - ly_ * 456ul)) << isDoubleSpeed()); }