uint8_t GBMemoryWriteHDMA5(struct GB* gb, uint8_t value) { gb->memory.hdmaSource = gb->memory.io[REG_HDMA1] << 8; gb->memory.hdmaSource |= gb->memory.io[REG_HDMA2]; gb->memory.hdmaDest = gb->memory.io[REG_HDMA3] << 8; gb->memory.hdmaDest |= gb->memory.io[REG_HDMA4]; gb->memory.hdmaSource &= 0xFFF0; if (gb->memory.hdmaSource >= 0x8000 && gb->memory.hdmaSource < 0xA000) { mLOG(GB_MEM, GAME_ERROR, "Invalid HDMA source: %04X", gb->memory.hdmaSource); return value | 0x80; } gb->memory.hdmaDest &= 0x1FF0; gb->memory.hdmaDest |= 0x8000; bool wasHdma = gb->memory.isHdma; gb->memory.isHdma = value & 0x80; if ((!wasHdma && !gb->memory.isHdma) || gb->video.mode == 0) { if (gb->memory.isHdma) { gb->memory.hdmaRemaining = 0x10; } else { gb->memory.hdmaRemaining = ((value & 0x7F) + 1) * 0x10; } gb->cpuBlocked = true; mTimingSchedule(&gb->timing, &gb->memory.hdmaEvent, 0); } else if (gb->memory.isHdma && !GBRegisterLCDCIsEnable(gb->memory.io[REG_LCDC])) { return 0x80 | ((value + 1) & 0x7F); } return value & 0x7F; }
void GBVideoWriteLYC(struct GBVideo* video, uint8_t value) { GBRegisterSTAT oldStat = video->stat; if (GBRegisterLCDCIsEnable(video->p->memory.io[REG_LCDC])) { video->stat = GBRegisterSTATSetLYC(video->stat, value == video->ly); if (!_statIRQAsserted(video, oldStat) && _statIRQAsserted(video, video->stat)) { video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT); GBUpdateIRQs(video->p); } } video->p->memory.io[REG_STAT] = video->stat; }
void GBVideoWriteSTAT(struct GBVideo* video, GBRegisterSTAT value) { GBRegisterSTAT oldStat = video->stat; video->stat = (video->stat & 0x7) | (value & 0x78); if (!GBRegisterLCDCIsEnable(video->p->memory.io[REG_LCDC]) || video->p->model >= GB_MODEL_CGB) { return; } if (!_statIRQAsserted(video, oldStat) && video->mode < 3) { // TODO: variable for the IRQ line value? video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT); GBUpdateIRQs(video->p); } }
void GBVideoWriteLCDC(struct GBVideo* video, GBRegisterLCDC value) { if (!GBRegisterLCDCIsEnable(video->p->memory.io[REG_LCDC]) && GBRegisterLCDCIsEnable(value)) { video->mode = 2; video->nextMode = GB_VIDEO_MODE_2_LENGTH - 5; // TODO: Why is this fudge factor needed? Might be related to T-cycles for load/store differing video->nextEvent = video->nextMode; video->eventDiff = -video->p->cpu->cycles >> video->p->doubleSpeed; video->ly = 0; video->p->memory.io[REG_LY] = 0; // TODO: Does this read as 0 for 4 T-cycles? video->stat = GBRegisterSTATSetMode(video->stat, 2); video->stat = GBRegisterSTATSetLYC(video->stat, video->ly == video->p->memory.io[REG_LYC]); if (GBRegisterSTATIsLYCIRQ(video->stat) && video->ly == video->p->memory.io[REG_LYC]) { video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT); GBUpdateIRQs(video->p); } video->p->memory.io[REG_STAT] = video->stat; if (video->p->cpu->cycles + (video->nextEvent << video->p->doubleSpeed) < video->p->cpu->nextEvent) { video->p->cpu->nextEvent = video->p->cpu->cycles + (video->nextEvent << video->p->doubleSpeed); } return; }
void GBVideoWriteLCDC(struct GBVideo* video, GBRegisterLCDC value) { if (!GBRegisterLCDCIsEnable(video->p->memory.io[REG_LCDC]) && GBRegisterLCDCIsEnable(value)) { video->mode = 2; video->modeEvent.callback = _endMode2; int32_t next = GB_VIDEO_MODE_2_LENGTH - 5; // TODO: Why is this fudge factor needed? Might be related to T-cycles for load/store differing mTimingDeschedule(&video->p->timing, &video->modeEvent); mTimingSchedule(&video->p->timing, &video->modeEvent, next << video->p->doubleSpeed); video->ly = 0; video->p->memory.io[REG_LY] = 0; GBRegisterSTAT oldStat = video->stat; video->stat = GBRegisterSTATSetMode(video->stat, 0); video->stat = GBRegisterSTATSetLYC(video->stat, video->ly == video->p->memory.io[REG_LYC]); if (!_statIRQAsserted(video, oldStat) && _statIRQAsserted(video, video->stat)) { video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT); GBUpdateIRQs(video->p); } video->p->memory.io[REG_STAT] = video->stat; video->renderer->writePalette(video->renderer, 0, video->palette[0]); mTimingDeschedule(&video->p->timing, &video->frameEvent); } if (GBRegisterLCDCIsEnable(video->p->memory.io[REG_LCDC]) && !GBRegisterLCDCIsEnable(value)) { // TODO: Fix serialization; this gets internal and visible modes out of sync video->mode = 0; video->stat = GBRegisterSTATSetMode(video->stat, 0); video->p->memory.io[REG_STAT] = video->stat; video->ly = 0; video->p->memory.io[REG_LY] = 0; video->renderer->writePalette(video->renderer, 0, video->dmgPalette[0]); mTimingDeschedule(&video->p->timing, &video->modeEvent); mTimingDeschedule(&video->p->timing, &video->frameEvent); mTimingSchedule(&video->p->timing, &video->frameEvent, GB_VIDEO_TOTAL_LENGTH); } video->p->memory.io[REG_STAT] = video->stat; }
void _updateFrameCount(struct mTiming* timing, void* context, uint32_t cyclesLate) { UNUSED(cyclesLate); struct GBVideo* video = context; if (video->p->cpu->executionState != LR35902_CORE_FETCH) { mTimingSchedule(timing, &video->frameEvent, 4 - ((video->p->cpu->executionState + 1) & 3)); return; } GBFrameEnded(video->p); mCoreSyncPostFrame(video->p->sync); --video->frameskipCounter; if (video->frameskipCounter < 0) { video->renderer->finishFrame(video->renderer); video->frameskipCounter = video->frameskip; } ++video->frameCounter; if (!GBRegisterLCDCIsEnable(video->p->memory.io[REG_LCDC])) { mTimingSchedule(timing, &video->frameEvent, GB_VIDEO_TOTAL_LENGTH); } GBFrameStarted(video->p); }
void _endMode1(struct mTiming* timing, void* context, uint32_t cyclesLate) { struct GBVideo* video = context; if (!GBRegisterLCDCIsEnable(video->p->memory.io[REG_LCDC])) { return; } int lyc = video->p->memory.io[REG_LYC]; // TODO: One M-cycle delay ++video->ly; int32_t next; if (video->ly == GB_VIDEO_VERTICAL_TOTAL_PIXELS + 1) { video->ly = 0; video->p->memory.io[REG_LY] = video->ly; next = GB_VIDEO_MODE_2_LENGTH; video->mode = 2; video->modeEvent.callback = _endMode2; } else if (video->ly == GB_VIDEO_VERTICAL_TOTAL_PIXELS) { video->p->memory.io[REG_LY] = 0; next = GB_VIDEO_HORIZONTAL_LENGTH - 8; } else if (video->ly == GB_VIDEO_VERTICAL_TOTAL_PIXELS - 1) { video->p->memory.io[REG_LY] = video->ly; next = 8; } else { video->p->memory.io[REG_LY] = video->ly; next = GB_VIDEO_HORIZONTAL_LENGTH; } GBRegisterSTAT oldStat = video->stat; video->stat = GBRegisterSTATSetMode(video->stat, video->mode); video->stat = GBRegisterSTATSetLYC(video->stat, lyc == video->p->memory.io[REG_LY]); if (!_statIRQAsserted(video, oldStat) && _statIRQAsserted(video, video->stat)) { video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT); GBUpdateIRQs(video->p); } video->p->memory.io[REG_STAT] = video->stat; mTimingSchedule(timing, &video->modeEvent, (next << video->p->doubleSpeed) - cyclesLate); }