proper display FIFO emulation
This commit is contained in:
parent
155609b6d9
commit
4afac28263
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);
|
||||
|
||||
// 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))
|
||||
|
|
48
src/GPU.cpp
48
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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
18
src/GPU2D.h
18
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;
|
||||
|
|
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)
|
||||
{
|
||||
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;
|
||||
|
|
Loading…
Reference in New Issue