aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStapleButter <thetotalworm@gmail.com>2017-06-26 11:02:10 +0200
committerStapleButter <thetotalworm@gmail.com>2017-06-26 11:02:10 +0200
commit4afac282630317ae84c2060e6e62359987894701 (patch)
tree8de623167a775320fdbbc94b76293abc75302199
parent155609b6d91a81e70df7ee99de64b8a9d8d847e4 (diff)
proper display FIFO emulation
-rw-r--r--src/DMA.cpp14
-rw-r--r--src/DMA.h5
-rw-r--r--src/GPU.cpp48
-rw-r--r--src/GPU2D.cpp36
-rw-r--r--src/GPU2D.h18
-rw-r--r--src/NDS.cpp16
-rw-r--r--src/NDS.h2
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
diff --git a/src/DMA.h b/src/DMA.h
index 17c075e..11ee1ad 100644
--- a/src/DMA.h
+++ b/src/DMA.h
@@ -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;
diff --git a/src/NDS.h b/src/NDS.h
index bb2d210..c6cd84b 100644
--- a/src/NDS.h
+++ b/src/NDS.h
@@ -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);