From 2150240cbd67711bcd1734bf8bcf0ddfe6acc708 Mon Sep 17 00:00:00 2001 From: StapleButter Date: Mon, 20 Mar 2017 22:18:35 +0100 Subject: [PATCH] implement some obscure DMA types --- src/DMA.cpp | 20 +++++++++++++++++--- src/DMA.h | 6 ++++++ src/GPU.cpp | 22 ++++++++++++++-------- src/GPU2D.cpp | 28 +++++++++++++++++++++++++--- src/GPU2D.h | 4 ++++ src/NDS.cpp | 9 +++++++++ src/NDS.h | 1 + 7 files changed, 76 insertions(+), 14 deletions(-) diff --git a/src/DMA.cpp b/src/DMA.cpp index 5a7ca1c1..14fe791d 100644 --- a/src/DMA.cpp +++ b/src/DMA.cpp @@ -20,7 +20,7 @@ #include "NDS.h" #include "DMA.h" #include "NDSCart.h" -#include "GPU3D.h" +#include "GPU.h" // NOTES ON DMA SHIT @@ -153,8 +153,8 @@ void DMA::WriteCnt(u32 val) else if (StartMode == 0x07) GPU3D::CheckFIFODMA(); - if (StartMode==0x04 || StartMode==0x06 || StartMode==0x13) - printf("UNIMPLEMENTED ARM%d DMA%d START MODE %02X\n", CPU?7:9, Num, StartMode); + if (StartMode==0x06 || StartMode==0x13) + printf("UNIMPLEMENTED ARM%d DMA%d START MODE %02X, %08X->%08X\n", CPU?7:9, Num, StartMode, SrcAddr, DstAddr); } } @@ -194,6 +194,20 @@ void DMA::Start() return; } + // 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; + } + // TODO eventually: not stop if we're running code in ITCM Running = true; diff --git a/src/DMA.h b/src/DMA.h index 59a7f036..1b8bbb22 100644 --- a/src/DMA.h +++ b/src/DMA.h @@ -40,6 +40,12 @@ public: Start(); } + void StopIfNeeded(u32 mode) + { + if (mode == StartMode) + Cnt &= ~0x80000000; + } + u32 SrcAddr; u32 DstAddr; u32 Cnt; diff --git a/src/GPU.cpp b/src/GPU.cpp index aaef43f9..5b9bf330 100644 --- a/src/GPU.cpp +++ b/src/GPU.cpp @@ -678,13 +678,23 @@ void StartScanline(u32 line) else DispStat[1] &= ~(1<<2); + if (line >= 2 && line < 194) + NDS::CheckDMAs(0, 0x03); + else if (line == 194) + NDS::StopDMAs(0, 0x03); + 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) + NDS::CheckDMAs(0, 0x04); + // draw GPU2D_A->DrawScanline(line); GPU2D_B->DrawScanline(line); - - //NDS::ScheduleEvent(LINE_CYCLES, StartScanline, line+1); } else if (line == 262) { @@ -701,6 +711,8 @@ void StartScanline(u32 line) DispStat[0] |= (1<<0); DispStat[1] |= (1<<0); + NDS::StopDMAs(0, 0x04); + NDS::CheckDMAs(0, 0x01); NDS::CheckDMAs(1, 0x11); @@ -711,14 +723,8 @@ void StartScanline(u32 line) GPU2D_B->VBlank(); GPU3D::VBlank(); } - - //NDS::ScheduleEvent(LINE_CYCLES, StartScanline, line+1); - //NDS::ScheduleEvent(NDS::Event_LCD, true, LINE_CYCLES, StartScanline, line+1); } - // checkme - if (line == 0) NDS::CheckDMAs(0, 0x03); - NDS::ScheduleEvent(NDS::Event_LCD, true, HBLANK_CYCLES, StartHBlank, line); } diff --git a/src/GPU2D.cpp b/src/GPU2D.cpp index cedfe1e1..3717cf56 100644 --- a/src/GPU2D.cpp +++ b/src/GPU2D.cpp @@ -91,6 +91,8 @@ void GPU2D::Reset() EVB = 0; EVY = 0; + memset(DispFIFOBuffer, 0, 256*2); + CaptureCnt = 0; MasterBrightness = 0; @@ -337,9 +339,17 @@ void GPU2D::DrawScanline(u32 line) } break; - case 3: // FIFO display + case 3: // FIFO display (grossly inaccurate) { - // TODO + for (int i = 0; i < 256; i++) + { + u16 color = DispFIFOBuffer[i]; + u8 r = (color & 0x001F) << 1; + u8 g = (color & 0x03E0) >> 4; + u8 b = (color & 0x7C00) >> 9; + + dst[i] = r | (g << 8) | (b << 16); + } } break; } @@ -445,7 +455,8 @@ void GPU2D::DoCapture(u32 line, u32 width, u32* src) if (CaptureCnt & (1<<25)) { - // TODO: FIFO mode + srcB = &DispFIFOBuffer[0]; + srcBaddr = 0; } else { @@ -570,6 +581,17 @@ void GPU2D::DoCapture(u32 line, u32 width, u32* src) } } +void GPU2D::FIFODMA(u32 addr) +{ + for (int i = 0; i < 256; i += 2) + { + u32 val = NDS::ARM9Read32(addr); + addr += 4; + DispFIFOBuffer[i] = val & 0xFFFF; + DispFIFOBuffer[i+1] = val >> 16; + } +} + void GPU2D::BGExtPalDirty(u32 base) { diff --git a/src/GPU2D.h b/src/GPU2D.h index 41364400..acbe967e 100644 --- a/src/GPU2D.h +++ b/src/GPU2D.h @@ -45,10 +45,14 @@ public: u16* GetBGExtPal(u32 slot, u32 pal); u16* GetOBJExtPal(u32 pal); + void FIFODMA(u32 addr); + private: u32 Num; u32* Framebuffer; + u16 DispFIFOBuffer[256]; + u32 DispCnt; u16 BGCnt[4]; diff --git a/src/NDS.cpp b/src/NDS.cpp index 3ab543e0..d2fa59cf 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -622,6 +622,15 @@ void CheckDMAs(u32 cpu, u32 mode) DMAs[cpu+3]->StartIfNeeded(mode); } +void StopDMAs(u32 cpu, u32 mode) +{ + cpu <<= 2; + DMAs[cpu+0]->StopIfNeeded(mode); + DMAs[cpu+1]->StopIfNeeded(mode); + DMAs[cpu+2]->StopIfNeeded(mode); + DMAs[cpu+3]->StopIfNeeded(mode); +} + diff --git a/src/NDS.h b/src/NDS.h index 28f777e6..9721c845 100644 --- a/src/NDS.h +++ b/src/NDS.h @@ -148,6 +148,7 @@ void StopCPU(u32 cpu, u32 mask); void ResumeCPU(u32 cpu, u32 mask); void CheckDMAs(u32 cpu, u32 mode); +void StopDMAs(u32 cpu, u32 mode); void RunTimingCriticalDevices(u32 cpu, s32 cycles);