diff options
author | StapleButter <thetotalworm@gmail.com> | 2017-06-26 11:02:10 +0200 |
---|---|---|
committer | StapleButter <thetotalworm@gmail.com> | 2017-06-26 11:02:10 +0200 |
commit | 4afac282630317ae84c2060e6e62359987894701 (patch) | |
tree | 8de623167a775320fdbbc94b76293abc75302199 | |
parent | 155609b6d91a81e70df7ee99de64b8a9d8d847e4 (diff) |
proper display FIFO emulation
-rw-r--r-- | src/DMA.cpp | 14 | ||||
-rw-r--r-- | src/DMA.h | 5 | ||||
-rw-r--r-- | src/GPU.cpp | 48 | ||||
-rw-r--r-- | src/GPU2D.cpp | 36 | ||||
-rw-r--r-- | src/GPU2D.h | 18 | ||||
-rw-r--r-- | src/NDS.cpp | 16 | ||||
-rw-r--r-- | src/NDS.h | 2 |
7 files changed, 105 insertions, 34 deletions
diff --git a/src/DMA.cpp b/src/DMA.cpp index edd6f8b..39dcb85 100644 --- a/src/DMA.cpp +++ b/src/DMA.cpp @@ -186,20 +186,6 @@ void DMA::Start() //printf("ARM%d DMA%d %08X %02X %08X->%08X %d bytes %dbit\n", CPU?7:9, Num, Cnt, StartMode, CurSrcAddr, CurDstAddr, RemCount*((Cnt&0x04000000)?4:2), (Cnt&0x04000000)?32:16); - // special path for the display FIFO. another gross hack. - // the display FIFO seems to be more like a circular buffer that holds 16 pixels - // from which the display controller reads. DMA is triggered every 8 pixels to fill it - // as it is being read from. emulating it properly would be resource intensive. - // proper emulation would only matter if something is trying to feed the FIFO manually - // instead of using the DMA. which is probably never happening. the only thing I know of - // that even uses the display FIFO is the aging cart. - if (StartMode == 0x04) - { - GPU::GPU2D_A->FIFODMA(CurSrcAddr); - CurSrcAddr += 256*2; - return; - } - IsGXFIFODMA = (CPU == 0 && (CurSrcAddr>>24) == 0x02 && CurDstAddr == 0x04000400 && DstAddrInc == 0); // TODO eventually: not stop if we're running code in ITCM @@ -34,6 +34,11 @@ public: s32 Run(s32 cycles); + bool IsInMode(u32 mode) + { + return ((mode == StartMode) && (Cnt & 0x80000000)); + } + void StartIfNeeded(u32 mode) { if ((mode == StartMode) && (Cnt & 0x80000000)) diff --git a/src/GPU.cpp b/src/GPU.cpp index 20cfadd..ac3684d 100644 --- a/src/GPU.cpp +++ b/src/GPU.cpp @@ -33,6 +33,8 @@ u16 VCount; u32 NextVCount; u16 TotalScanlines; +bool RunFIFO; + u16 DispStat[2], VMatch[2]; u8 Palette[2*1024]; @@ -606,8 +608,36 @@ void DisplaySwap(u32 val) } +void DisplayFIFO(u32 x) +{ + // sample the FIFO + // as this starts 16 cycles (~3 pixels) before display start, + // we aren't aligned to the 8-pixel grid + if (x > 0) + { + if (x == 8) + GPU2D_A->SampleFIFO(0, 5); + else + GPU2D_A->SampleFIFO(x-11, 8); + } + + if (x < 256) + { + // transfer the next 8 pixels + NDS::CheckDMAs(0, 0x04); + NDS::ScheduleEvent(NDS::Event_DisplayFIFO, true, 6*8, DisplayFIFO, x+8); + } + else + GPU2D_A->SampleFIFO(253, 3); // sample the remaining pixels +} + void StartFrame() { + // only run the display FIFO if needed: + // * if it is used for display or capture + // * if we have display FIFO DMA + RunFIFO = GPU2D_A->UsesFIFO() || NDS::DMAsInMode(0, 0x04); + TotalScanlines = 0; StartScanline(0); } @@ -619,6 +649,11 @@ void StartHBlank(u32 line) if (VCount < 192) { + // draw + // note: this should start 48 cycles after the scanline start + GPU2D_A->DrawScanline(line); + GPU2D_B->DrawScanline(line); + NDS::CheckDMAs(0, 0x02); } else if (VCount == 215) @@ -682,23 +717,14 @@ void StartScanline(u32 line) if (line < 192) { - // fill a line from the display FIFO if needed. - // this isn't how the real thing works, but emulating it - // properly would be too much trouble given barely anything - // uses FIFO display - // (TODO, eventually: emulate it properly) - if (VCount < 192) - NDS::CheckDMAs(0, 0x04); - if (line == 0) { GPU2D_A->VBlankEnd(); GPU2D_B->VBlankEnd(); } - // draw - GPU2D_A->DrawScanline(line); - GPU2D_B->DrawScanline(line); + if (RunFIFO) + NDS::ScheduleEvent(NDS::Event_DisplayFIFO, true, 32, DisplayFIFO, 0); } if (VCount == 262) diff --git a/src/GPU2D.cpp b/src/GPU2D.cpp index 6d92011..b0f1815 100644 --- a/src/GPU2D.cpp +++ b/src/GPU2D.cpp @@ -108,6 +108,10 @@ void GPU2D::Reset() EVB = 0; EVY = 0; + memset(DispFIFO, 0, 16*2); + DispFIFOReadPtr = 0; + DispFIFOWritePtr = 0; + memset(DispFIFOBuffer, 0, 256*2); CaptureCnt = 0; @@ -328,6 +332,15 @@ void GPU2D::Write16(u32 addr, u16 val) if (EVY > 16) EVY = 16; return; + case 0x068: + DispFIFO[DispFIFOWritePtr] = val; + return; + case 0x06A: + DispFIFO[DispFIFOWritePtr+1] = val; + DispFIFOWritePtr += 2; + DispFIFOWritePtr &= 0xF; + return; + case 0x06C: MasterBrightness = val; return; } @@ -369,6 +382,13 @@ void GPU2D::Write32(u32 addr, u32 val) // esp. if a capture is happening CaptureCnt = val & 0xEF3F1F1F; return; + + case 0x068: + DispFIFO[DispFIFOWritePtr] = val & 0xFFFF; + DispFIFO[DispFIFOWritePtr+1] = val >> 16; + DispFIFOWritePtr += 2; + DispFIFOWritePtr &= 0xF; + return; } Write16(addr, val&0xFFFF); @@ -532,6 +552,9 @@ void GPU2D::DrawScanline(u32 line) void GPU2D::VBlank() { CaptureCnt &= ~(1<<31); + + DispFIFOReadPtr = 0; + DispFIFOWritePtr = 0; } void GPU2D::VBlankEnd() @@ -694,14 +717,15 @@ void GPU2D::DoCapture(u32 line, u32 width, u32* src) } } -void GPU2D::FIFODMA(u32 addr) +void GPU2D::SampleFIFO(u32 offset, u32 num) { - for (int i = 0; i < 256; i += 2) + for (u32 i = 0; i < num; i++) { - u32 val = NDS::ARM9Read32(addr); - addr += 4; - DispFIFOBuffer[i] = val & 0xFFFF; - DispFIFOBuffer[i+1] = val >> 16; + u16 val = DispFIFO[DispFIFOReadPtr]; + DispFIFOReadPtr++; + DispFIFOReadPtr &= 0xF; + + DispFIFOBuffer[offset+i] = val; } } diff --git a/src/GPU2D.h b/src/GPU2D.h index 4bf698d..3deb42d 100644 --- a/src/GPU2D.h +++ b/src/GPU2D.h @@ -36,6 +36,18 @@ public: void Write16(u32 addr, u16 val); void Write32(u32 addr, u32 val); + bool UsesFIFO() + { + if (((DispCnt >> 16) & 0x3) == 3) + return true; + if ((CaptureCnt & (1<<25)) && ((CaptureCnt >> 29) & 0x3) != 0) + return true; + + return false; + } + + void SampleFIFO(u32 offset, u32 num); + void DrawScanline(u32 line); void VBlank(); void VBlankEnd(); @@ -48,12 +60,14 @@ public: u16* GetBGExtPal(u32 slot, u32 pal); u16* GetOBJExtPal(u32 pal); - void FIFODMA(u32 addr); - private: u32 Num; u32* Framebuffer; + u16 DispFIFO[16]; + u32 DispFIFOReadPtr; + u32 DispFIFOWritePtr; + u16 DispFIFOBuffer[256]; u32 DispCnt; diff --git a/src/NDS.cpp b/src/NDS.cpp index 66e5a47..5aa5f00 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -655,6 +655,16 @@ void RunTimingCriticalDevices(u32 cpu, s32 cycles) +bool DMAsInMode(u32 cpu, u32 mode) +{ + cpu <<= 2; + if (DMAs[cpu+0]->IsInMode(mode)) return true; + if (DMAs[cpu+1]->IsInMode(mode)) return true; + if (DMAs[cpu+2]->IsInMode(mode)) return true; + if (DMAs[cpu+3]->IsInMode(mode)) return true; + return false; +} + void CheckDMAs(u32 cpu, u32 mode) { cpu <<= 2; @@ -1672,6 +1682,9 @@ void ARM9IOWrite16(u32 addr, u16 val) case 0x04000060: GPU3D::Write16(addr, val); return; + case 0x04000068: + case 0x0400006A: GPU::GPU2D_A->Write16(addr, val); return; + case 0x040000B8: DMAs[0]->WriteCnt((DMAs[0]->Cnt & 0xFFFF0000) | val); return; case 0x040000BA: DMAs[0]->WriteCnt((DMAs[0]->Cnt & 0x0000FFFF) | (val << 16)); return; case 0x040000C4: DMAs[1]->WriteCnt((DMAs[1]->Cnt & 0xFFFF0000) | val); return; @@ -1823,7 +1836,8 @@ void ARM9IOWrite32(u32 addr, u32 val) switch (addr) { case 0x04000060: GPU3D::Write32(addr, val); return; - case 0x04000064: GPU::GPU2D_A->Write32(addr, val); return; + case 0x04000064: + case 0x04000068: GPU::GPU2D_A->Write32(addr, val); return; case 0x040000B0: DMAs[0]->SrcAddr = val; return; case 0x040000B4: DMAs[0]->DstAddr = val; return; @@ -30,6 +30,7 @@ enum Event_SPU, Event_Wifi, + Event_DisplayFIFO, Event_ROMTransfer, Event_MAX @@ -128,6 +129,7 @@ void ResumeCPU(u32 cpu, u32 mask); u32 GetPC(u32 cpu); +bool DMAsInMode(u32 cpu, u32 mode); void CheckDMAs(u32 cpu, u32 mode); void StopDMAs(u32 cpu, u32 mode); |