proper display FIFO emulation

This commit is contained in:
StapleButter 2017-06-26 11:02:10 +02:00
parent 155609b6d9
commit 4afac28263
7 changed files with 105 additions and 34 deletions

View File

@ -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

View File

@ -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))

View File

@ -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)

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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);