mirror of
https://github.com/melonDS-emu/melonDS.git
synced 2025-07-24 14:49:53 -06:00
proper display FIFO emulation
This commit is contained in:
14
src/DMA.cpp
14
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);
|
//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);
|
IsGXFIFODMA = (CPU == 0 && (CurSrcAddr>>24) == 0x02 && CurDstAddr == 0x04000400 && DstAddrInc == 0);
|
||||||
|
|
||||||
// TODO eventually: not stop if we're running code in ITCM
|
// TODO eventually: not stop if we're running code in ITCM
|
||||||
|
@ -34,6 +34,11 @@ public:
|
|||||||
|
|
||||||
s32 Run(s32 cycles);
|
s32 Run(s32 cycles);
|
||||||
|
|
||||||
|
bool IsInMode(u32 mode)
|
||||||
|
{
|
||||||
|
return ((mode == StartMode) && (Cnt & 0x80000000));
|
||||||
|
}
|
||||||
|
|
||||||
void StartIfNeeded(u32 mode)
|
void StartIfNeeded(u32 mode)
|
||||||
{
|
{
|
||||||
if ((mode == StartMode) && (Cnt & 0x80000000))
|
if ((mode == StartMode) && (Cnt & 0x80000000))
|
||||||
|
48
src/GPU.cpp
48
src/GPU.cpp
@ -33,6 +33,8 @@ u16 VCount;
|
|||||||
u32 NextVCount;
|
u32 NextVCount;
|
||||||
u16 TotalScanlines;
|
u16 TotalScanlines;
|
||||||
|
|
||||||
|
bool RunFIFO;
|
||||||
|
|
||||||
u16 DispStat[2], VMatch[2];
|
u16 DispStat[2], VMatch[2];
|
||||||
|
|
||||||
u8 Palette[2*1024];
|
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()
|
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;
|
TotalScanlines = 0;
|
||||||
StartScanline(0);
|
StartScanline(0);
|
||||||
}
|
}
|
||||||
@ -619,6 +649,11 @@ void StartHBlank(u32 line)
|
|||||||
|
|
||||||
if (VCount < 192)
|
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);
|
NDS::CheckDMAs(0, 0x02);
|
||||||
}
|
}
|
||||||
else if (VCount == 215)
|
else if (VCount == 215)
|
||||||
@ -682,23 +717,14 @@ void StartScanline(u32 line)
|
|||||||
|
|
||||||
if (line < 192)
|
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)
|
if (line == 0)
|
||||||
{
|
{
|
||||||
GPU2D_A->VBlankEnd();
|
GPU2D_A->VBlankEnd();
|
||||||
GPU2D_B->VBlankEnd();
|
GPU2D_B->VBlankEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
// draw
|
if (RunFIFO)
|
||||||
GPU2D_A->DrawScanline(line);
|
NDS::ScheduleEvent(NDS::Event_DisplayFIFO, true, 32, DisplayFIFO, 0);
|
||||||
GPU2D_B->DrawScanline(line);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (VCount == 262)
|
if (VCount == 262)
|
||||||
|
@ -108,6 +108,10 @@ void GPU2D::Reset()
|
|||||||
EVB = 0;
|
EVB = 0;
|
||||||
EVY = 0;
|
EVY = 0;
|
||||||
|
|
||||||
|
memset(DispFIFO, 0, 16*2);
|
||||||
|
DispFIFOReadPtr = 0;
|
||||||
|
DispFIFOWritePtr = 0;
|
||||||
|
|
||||||
memset(DispFIFOBuffer, 0, 256*2);
|
memset(DispFIFOBuffer, 0, 256*2);
|
||||||
|
|
||||||
CaptureCnt = 0;
|
CaptureCnt = 0;
|
||||||
@ -328,6 +332,15 @@ void GPU2D::Write16(u32 addr, u16 val)
|
|||||||
if (EVY > 16) EVY = 16;
|
if (EVY > 16) EVY = 16;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
case 0x068:
|
||||||
|
DispFIFO[DispFIFOWritePtr] = val;
|
||||||
|
return;
|
||||||
|
case 0x06A:
|
||||||
|
DispFIFO[DispFIFOWritePtr+1] = val;
|
||||||
|
DispFIFOWritePtr += 2;
|
||||||
|
DispFIFOWritePtr &= 0xF;
|
||||||
|
return;
|
||||||
|
|
||||||
case 0x06C: MasterBrightness = val; return;
|
case 0x06C: MasterBrightness = val; return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -369,6 +382,13 @@ void GPU2D::Write32(u32 addr, u32 val)
|
|||||||
// esp. if a capture is happening
|
// esp. if a capture is happening
|
||||||
CaptureCnt = val & 0xEF3F1F1F;
|
CaptureCnt = val & 0xEF3F1F1F;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
case 0x068:
|
||||||
|
DispFIFO[DispFIFOWritePtr] = val & 0xFFFF;
|
||||||
|
DispFIFO[DispFIFOWritePtr+1] = val >> 16;
|
||||||
|
DispFIFOWritePtr += 2;
|
||||||
|
DispFIFOWritePtr &= 0xF;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Write16(addr, val&0xFFFF);
|
Write16(addr, val&0xFFFF);
|
||||||
@ -532,6 +552,9 @@ void GPU2D::DrawScanline(u32 line)
|
|||||||
void GPU2D::VBlank()
|
void GPU2D::VBlank()
|
||||||
{
|
{
|
||||||
CaptureCnt &= ~(1<<31);
|
CaptureCnt &= ~(1<<31);
|
||||||
|
|
||||||
|
DispFIFOReadPtr = 0;
|
||||||
|
DispFIFOWritePtr = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPU2D::VBlankEnd()
|
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);
|
u16 val = DispFIFO[DispFIFOReadPtr];
|
||||||
addr += 4;
|
DispFIFOReadPtr++;
|
||||||
DispFIFOBuffer[i] = val & 0xFFFF;
|
DispFIFOReadPtr &= 0xF;
|
||||||
DispFIFOBuffer[i+1] = val >> 16;
|
|
||||||
|
DispFIFOBuffer[offset+i] = val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
18
src/GPU2D.h
18
src/GPU2D.h
@ -36,6 +36,18 @@ public:
|
|||||||
void Write16(u32 addr, u16 val);
|
void Write16(u32 addr, u16 val);
|
||||||
void Write32(u32 addr, u32 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 DrawScanline(u32 line);
|
||||||
void VBlank();
|
void VBlank();
|
||||||
void VBlankEnd();
|
void VBlankEnd();
|
||||||
@ -48,12 +60,14 @@ public:
|
|||||||
u16* GetBGExtPal(u32 slot, u32 pal);
|
u16* GetBGExtPal(u32 slot, u32 pal);
|
||||||
u16* GetOBJExtPal(u32 pal);
|
u16* GetOBJExtPal(u32 pal);
|
||||||
|
|
||||||
void FIFODMA(u32 addr);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
u32 Num;
|
u32 Num;
|
||||||
u32* Framebuffer;
|
u32* Framebuffer;
|
||||||
|
|
||||||
|
u16 DispFIFO[16];
|
||||||
|
u32 DispFIFOReadPtr;
|
||||||
|
u32 DispFIFOWritePtr;
|
||||||
|
|
||||||
u16 DispFIFOBuffer[256];
|
u16 DispFIFOBuffer[256];
|
||||||
|
|
||||||
u32 DispCnt;
|
u32 DispCnt;
|
||||||
|
16
src/NDS.cpp
16
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)
|
void CheckDMAs(u32 cpu, u32 mode)
|
||||||
{
|
{
|
||||||
cpu <<= 2;
|
cpu <<= 2;
|
||||||
@ -1672,6 +1682,9 @@ void ARM9IOWrite16(u32 addr, u16 val)
|
|||||||
|
|
||||||
case 0x04000060: GPU3D::Write16(addr, val); return;
|
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 0x040000B8: DMAs[0]->WriteCnt((DMAs[0]->Cnt & 0xFFFF0000) | val); return;
|
||||||
case 0x040000BA: DMAs[0]->WriteCnt((DMAs[0]->Cnt & 0x0000FFFF) | (val << 16)); return;
|
case 0x040000BA: DMAs[0]->WriteCnt((DMAs[0]->Cnt & 0x0000FFFF) | (val << 16)); return;
|
||||||
case 0x040000C4: DMAs[1]->WriteCnt((DMAs[1]->Cnt & 0xFFFF0000) | val); return;
|
case 0x040000C4: DMAs[1]->WriteCnt((DMAs[1]->Cnt & 0xFFFF0000) | val); return;
|
||||||
@ -1823,7 +1836,8 @@ void ARM9IOWrite32(u32 addr, u32 val)
|
|||||||
switch (addr)
|
switch (addr)
|
||||||
{
|
{
|
||||||
case 0x04000060: GPU3D::Write32(addr, val); return;
|
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 0x040000B0: DMAs[0]->SrcAddr = val; return;
|
||||||
case 0x040000B4: DMAs[0]->DstAddr = val; return;
|
case 0x040000B4: DMAs[0]->DstAddr = val; return;
|
||||||
|
@ -30,6 +30,7 @@ enum
|
|||||||
Event_SPU,
|
Event_SPU,
|
||||||
Event_Wifi,
|
Event_Wifi,
|
||||||
|
|
||||||
|
Event_DisplayFIFO,
|
||||||
Event_ROMTransfer,
|
Event_ROMTransfer,
|
||||||
|
|
||||||
Event_MAX
|
Event_MAX
|
||||||
@ -128,6 +129,7 @@ void ResumeCPU(u32 cpu, u32 mask);
|
|||||||
|
|
||||||
u32 GetPC(u32 cpu);
|
u32 GetPC(u32 cpu);
|
||||||
|
|
||||||
|
bool DMAsInMode(u32 cpu, u32 mode);
|
||||||
void CheckDMAs(u32 cpu, u32 mode);
|
void CheckDMAs(u32 cpu, u32 mode);
|
||||||
void StopDMAs(u32 cpu, u32 mode);
|
void StopDMAs(u32 cpu, u32 mode);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user