static bool _statIRQAsserted(struct GBVideo* video, GBRegisterSTAT stat) { // TODO: variable for the IRQ line value? if (GBRegisterSTATIsLYCIRQ(stat) && GBRegisterSTATIsLYC(stat)) { return true; } switch (GBRegisterSTATGetMode(stat)) { case 0: if (GBRegisterSTATIsHblankIRQ(stat)) { return true; } break; case 1: if (GBRegisterSTATIsVblankIRQ(stat)) { return true; } break; case 2: if (GBRegisterSTATIsOAMIRQ(stat)) { return true; } break; case 3: break; } return false; }
int32_t GBVideoProcessEvents(struct GBVideo* video, int32_t cycles) { video->eventDiff += cycles; if (video->nextEvent != INT_MAX) { video->nextEvent -= cycles; } if (video->nextEvent <= 0) { if (video->nextEvent != INT_MAX) { video->nextMode -= video->eventDiff; video->nextFrame -= video->eventDiff; } video->nextEvent = INT_MAX; GBVideoProcessDots(video); if (video->nextMode <= 0) { int lyc = video->p->memory.io[REG_LYC]; switch (video->mode) { case 0: if (video->frameskipCounter <= 0) { video->renderer->finishScanline(video->renderer, video->ly); } ++video->ly; video->p->memory.io[REG_LY] = video->ly; video->stat = GBRegisterSTATSetLYC(video->stat, lyc == video->ly); if (video->ly < GB_VIDEO_VERTICAL_PIXELS) { video->nextMode = GB_VIDEO_MODE_2_LENGTH; video->mode = 2; if (!GBRegisterSTATIsHblankIRQ(video->stat) && GBRegisterSTATIsOAMIRQ(video->stat)) { video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT); } } else { video->nextMode = GB_VIDEO_HORIZONTAL_LENGTH; video->mode = 1; --video->frameskipCounter; if (video->frameskipCounter < 0) { mCoreSyncPostFrame(video->p->sync); video->frameskipCounter = video->frameskip; } ++video->frameCounter; if (video->nextFrame != 0) { video->nextFrame = 0; } if (video->p->stream && video->p->stream->postVideoFrame) { const color_t* pixels; unsigned stride; video->renderer->getPixels(video->renderer, &stride, (const void**) &pixels); video->p->stream->postVideoFrame(video->p->stream, pixels, stride); } if (GBRegisterSTATIsVblankIRQ(video->stat) || GBRegisterSTATIsOAMIRQ(video->stat)) { video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT); } video->p->memory.io[REG_IF] |= (1 << GB_IRQ_VBLANK); } if (GBRegisterSTATIsLYCIRQ(video->stat) && lyc == video->ly) { video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT); } GBUpdateIRQs(video->p); break; case 1: // TODO: One M-cycle delay ++video->ly; if (video->ly == GB_VIDEO_VERTICAL_TOTAL_PIXELS + 1) { video->ly = 0; video->p->memory.io[REG_LY] = video->ly; video->nextMode = GB_VIDEO_MODE_2_LENGTH; video->mode = 2; if (GBRegisterSTATIsOAMIRQ(video->stat)) { video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT); GBUpdateIRQs(video->p); } video->renderer->finishFrame(video->renderer); break; } else if (video->ly == GB_VIDEO_VERTICAL_TOTAL_PIXELS) { video->p->memory.io[REG_LY] = 0; video->nextMode = GB_VIDEO_HORIZONTAL_LENGTH - 8; } else if (video->ly == GB_VIDEO_VERTICAL_TOTAL_PIXELS - 1) { video->p->memory.io[REG_LY] = video->ly; video->nextMode = 8; } else { video->p->memory.io[REG_LY] = video->ly; video->nextMode = GB_VIDEO_HORIZONTAL_LENGTH; } video->stat = GBRegisterSTATSetLYC(video->stat, lyc == video->p->memory.io[REG_LY]); if (GBRegisterSTATIsLYCIRQ(video->stat) && lyc == video->p->memory.io[REG_LY]) { video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT); GBUpdateIRQs(video->p); } if (video->p->memory.mbcType == GB_MBC7 && video->p->memory.rotation && video->p->memory.rotation->sample) { video->p->memory.rotation->sample(video->p->memory.rotation); } break; case 2: _cleanOAM(video, video->ly); video->dotCounter = 0; video->nextEvent = GB_VIDEO_HORIZONTAL_LENGTH; video->x = 0; video->nextMode = GB_VIDEO_MODE_3_LENGTH_BASE + video->objMax * 12; video->mode = 3; break; case 3: video->nextMode = GB_VIDEO_MODE_0_LENGTH_BASE - video->objMax * 12; video->mode = 0; if (GBRegisterSTATIsHblankIRQ(video->stat)) { video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT); GBUpdateIRQs(video->p); } if (video->ly < GB_VIDEO_VERTICAL_PIXELS && video->p->memory.isHdma && video->p->memory.io[REG_HDMA5] != 0xFF) { video->p->memory.hdmaRemaining = 0x10; video->p->memory.hdmaNext = video->p->cpu->cycles; } break; } video->stat = GBRegisterSTATSetMode(video->stat, video->mode); video->p->memory.io[REG_STAT] = video->stat; } if (video->nextFrame <= 0) { if (video->p->cpu->executionState == LR35902_CORE_FETCH) { GBFrameEnded(video->p); struct mCoreThread* thread = mCoreThreadGet(); if (thread && thread->frameCallback) { thread->frameCallback(thread); } video->nextFrame = GB_VIDEO_TOTAL_LENGTH; } else { video->nextFrame = 4 - ((video->p->cpu->executionState + 1) & 3); if (video->nextFrame < video->nextEvent) { video->nextEvent = video->nextFrame; } } } if (video->nextMode < video->nextEvent) { video->nextEvent = video->nextMode; } video->eventDiff = 0; } return video->nextEvent; }