Ejemplo n.º 1
0
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;
}
Ejemplo n.º 2
0
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;
}