diff --git a/src/GPU.cpp b/src/GPU.cpp index 680f08c6..dcf61a6a 100644 --- a/src/GPU.cpp +++ b/src/GPU.cpp @@ -30,6 +30,8 @@ namespace GPU #define FRAME_CYCLES (LINE_CYCLES * 263) u16 VCount; +u32 NextVCount; +u16 TotalScanlines; u16 DispStat[2], VMatch[2]; @@ -50,9 +52,6 @@ u8* VRAM[9] = {VRAM_A, VRAM_B, VRAM_C, VRAM_D, VRAM_E, VRAM_F, VRAM_G, VRAM_H, V u8 VRAMCNT[9]; u8 VRAMSTAT; -//u32 VRAM_Base[9]; -//u32 VRAM_Mask[9]; - u32 VRAMMap_LCDC; u32 VRAMMap_ABG[0x20]; @@ -95,6 +94,8 @@ void DeInit() void Reset() { VCount = 0; + NextVCount = -1; + TotalScanlines = 0; DispStat[0] = 0; DispStat[1] = 0; @@ -607,6 +608,7 @@ void DisplaySwap(u32 val) void StartFrame() { + TotalScanlines = 0; StartScanline(0); } @@ -615,23 +617,37 @@ void StartHBlank(u32 line) DispStat[0] |= (1<<1); DispStat[1] |= (1<<1); - if (line < 192) NDS::CheckDMAs(0, 0x02); + if (VCount < 192) NDS::CheckDMAs(0, 0x02); if (DispStat[0] & (1<<4)) NDS::SetIRQ(0, NDS::IRQ_HBlank); if (DispStat[1] & (1<<4)) NDS::SetIRQ(1, NDS::IRQ_HBlank); - if (line < 262) + if (VCount < 262) NDS::ScheduleEvent(NDS::Event_LCD, true, (LINE_CYCLES - HBLANK_CYCLES), StartScanline, line+1); + else + NDS::ScheduleEvent(NDS::Event_LCD, true, (LINE_CYCLES - HBLANK_CYCLES), FinishFrame, line+1); +} + +void FinishFrame(u32 lines) +{ + TotalScanlines = lines; } void StartScanline(u32 line) { - VCount = line; + if (line == 0) + VCount = 0; + else if (NextVCount != -1) + VCount = NextVCount; + else + VCount++; + + NextVCount = -1; DispStat[0] &= ~(1<<1); DispStat[1] &= ~(1<<1); - if (line == VMatch[0]) + if (VCount == VMatch[0]) { DispStat[0] |= (1<<2); @@ -640,7 +656,7 @@ void StartScanline(u32 line) else DispStat[0] &= ~(1<<2); - if (line == VMatch[1]) + if (VCount == VMatch[1]) { DispStat[1] |= (1<<2); @@ -649,12 +665,12 @@ void StartScanline(u32 line) else DispStat[1] &= ~(1<<2); - GPU2D_A->CheckWindows(line); - GPU2D_B->CheckWindows(line); + GPU2D_A->CheckWindows(VCount); + GPU2D_B->CheckWindows(VCount); - if (line >= 2 && line < 194) + if (VCount >= 2 && VCount < 194) NDS::CheckDMAs(0, 0x03); - else if (line == 194) + else if (VCount == 194) NDS::StopDMAs(0, 0x03); if (line < 192) @@ -664,7 +680,8 @@ void StartScanline(u32 line) // properly would be too much trouble given barely anything // uses FIFO display // (TODO, eventually: emulate it properly) - NDS::CheckDMAs(0, 0x04); + if (VCount < 192) + NDS::CheckDMAs(0, 0x04); if (line == 0) { @@ -676,7 +693,8 @@ void StartScanline(u32 line) GPU2D_A->DrawScanline(line); GPU2D_B->DrawScanline(line); } - else if (line == 262) + + if (VCount == 262) { // frame end @@ -685,7 +703,7 @@ void StartScanline(u32 line) } else { - if (line == 192) + if (VCount == 192) { // VBlank DispStat[0] |= (1<<0); @@ -703,7 +721,7 @@ void StartScanline(u32 line) GPU2D_B->VBlank(); GPU3D::VBlank(); } - else if (line == 215) + else if (VCount == 215) { GPU3D::VCount215(); } @@ -722,4 +740,14 @@ void SetDispStat(u32 cpu, u16 val) VMatch[cpu] = (val >> 8) | ((val & 0x80) << 1); } +void SetVCount(u16 val) +{ + // VCount write is delayed until the next scanline + + // TODO: how does the 3D engine react to VCount writes while it's rendering? + // TODO: also check the various DMA types that can be involved + + NextVCount = val; +} + } diff --git a/src/GPU.h b/src/GPU.h index 7009b868..4624495e 100644 --- a/src/GPU.h +++ b/src/GPU.h @@ -26,6 +26,7 @@ namespace GPU { extern u16 VCount; +extern u16 TotalScanlines; extern u16 DispStat[2]; @@ -385,10 +386,13 @@ T ReadVRAM_TexPal(u32 addr) void DisplaySwap(u32 val); void StartFrame(); +void FinishFrame(u32 lines); void StartScanline(u32 line); void SetDispStat(u32 cpu, u16 val); +void SetVCount(u16 val); + } #endif diff --git a/src/GPU2D.cpp b/src/GPU2D.cpp index 77b79b4e..0b3afd03 100644 --- a/src/GPU2D.cpp +++ b/src/GPU2D.cpp @@ -380,6 +380,18 @@ void GPU2D::DrawScanline(u32 line) { u32* dst = &Framebuffer[256*line]; + line = GPU::VCount; + + // scanlines that end up outside of the GPU drawing range + // (as a result of writing to VCount) are filled white + if (line > 192) + { + for (int i = 0; i < 256; i++) + dst[i] = 0xFF3F3F3F; + + return; + } + u32 dispmode = DispCnt >> 16; dispmode &= (Num ? 0x1 : 0x3); diff --git a/src/NDS.cpp b/src/NDS.cpp index 4ad325b3..ddd6c035 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -380,16 +380,16 @@ void RunSystem(s32 cycles) } } -void RunFrame() +u32 RunFrame() { s32 framecycles = 560190; - if (!Running) return; // dorp + if (!Running) return 263; // dorp GPU::StartFrame(); - while (Running && framecycles>0) + while (Running && GPU::TotalScanlines==0) { s32 ndscyclestorun; s32 ndscycles = 0; @@ -430,21 +430,9 @@ void RunFrame() RunSystem(ndscyclestorun); //GPU3D::Run(ndscyclestorun); - - /*while (ndscycles < ndscyclestorun) - { - ARM7->CyclesToRun = ndscyclestorun - ndscycles - ARM7Offset; - ARM7->Execute(); - ARM7Offset = 0; - - RunEvents(ARM7->Cycles); - ndscycles += ARM7->Cycles; - } - - ARM7Offset = ndscycles - ndscyclestorun;*/ - - framecycles -= ndscyclestorun; } + + return GPU::TotalScanlines; } void Reschedule() @@ -1657,6 +1645,7 @@ void ARM9IOWrite16(u32 addr, u16 val) switch (addr) { case 0x04000004: GPU::SetDispStat(0, val); return; + case 0x04000006: GPU::SetVCount(val); return; case 0x04000060: GPU3D::Write16(addr, val); return; @@ -2198,6 +2187,7 @@ void ARM7IOWrite16(u32 addr, u16 val) switch (addr) { case 0x04000004: GPU::SetDispStat(1, val); return; + case 0x04000006: GPU::SetVCount(val); return; case 0x040000B8: DMAs[4]->WriteCnt((DMAs[4]->Cnt & 0xFFFF0000) | val); return; case 0x040000BA: DMAs[4]->WriteCnt((DMAs[4]->Cnt & 0x0000FFFF) | (val << 16)); return; diff --git a/src/NDS.h b/src/NDS.h index 84616d96..38f2b894 100644 --- a/src/NDS.h +++ b/src/NDS.h @@ -104,7 +104,7 @@ void LoadROM(const char* path, bool direct); void LoadBIOS(); void SetupDirectBoot(); -void RunFrame(); +u32 RunFrame(); void PressKey(u32 key); void ReleaseKey(u32 key); diff --git a/src/wx/main.cpp b/src/wx/main.cpp index 9864382a..e07d3c99 100644 --- a/src/wx/main.cpp +++ b/src/wx/main.cpp @@ -386,7 +386,7 @@ wxThread::ExitCode EmuThread::Entry() if (emustatus == 1) { - NDS::RunFrame(); + u32 nlines = NDS::RunFrame(); SDL_LockTexture(sdltex, NULL, &texpixels, &texstride); if (texstride == 256*4) @@ -410,7 +410,9 @@ wxThread::ExitCode EmuThread::Entry() SDL_RenderPresent(sdlrend); // framerate limiter based off SDL2_gfx - float framerate = 1000.0f / 60.0f; + float framerate; + if (nlines == 263) framerate = 1000.0f / 60.0f; + else framerate = 1000.0f / ((60.0f * nlines) / 263.0f); fpslimitcount++; u32 curtick = SDL_GetTicks();