pureikyubu/Docs/RE/gxfifo.txt
ogamespec 646a643ec3 More Docs
- DVD controller Matsushita MN102-series user manual
- Some Macronix ETH / DSP manuals, similar to GC
- YAGCD
- Duddie DSP Infos
- tmbinc's article on GC DVD protection
- Nintendo US patents as PDFs
2015-09-11 18:20:28 +03:00

728 lines
16 KiB
Text

volatile void *__cpReg = (u16 *)0xCC000000; // 16-bit only
volatile void *__piReg = (u32 *)0xCC003000; // 32-bit only
typedef struct __GXFifoObj
{
u32 base;
u32 top;
u32 size;
u32 hiWatermark;
u32 loWatermark;
void *rdPtr;
void *wrPtr;
u32 count;
u8 bind_cpu,
bind_gp;
} GXFifoObj;
GXFifoObj *CPUFifo, *GPFifo;
GXBool CPGPLinked;
static GXBreakPtCallback BreakPointCB;
static void * __GXCurrentBP;
static BOOL GXOverflowSuspendInProgress;
static u32 __GXOverflowCount;
static BOOL IsWGPipeRedirected;
// ----------------------------------------------------------------------------
void GXOverflowHandler(OSContext *context)
{
if(__gxVerif[4] > 1)
{
OSReport("[GXOverflowHandler]");
}
ASSERT(!GXOverflowSuspendInProgress);
__GXOverflowCount++;
__GXWriteFifoIntEnable(FALSE, TRUE);
__GXWriteFifoIntReset(TRUE, FALSE);
GXOverflowSuspendInProgress = TRUE;
if(__gxVerif[4] > 1)
{
OSReport("[GXOverflowHandler Sleeping]");
}
OSSuspendThread(__GXCurrentThread);
}
void GXUnderflowHandler(OSContext *context)
{
if(__gxVerif[4] > 1)
{
OSReport("[GXUnderflowHandler]");
}
ASSERT(GXOverflowSuspendInProgress);
OSResumeThread(__GXCurrentThread);
GXOverflowSuspendInProgress = FALSE;
__GXWriteFifoIntReset(TRUE, TRUE);
__GXWriteFifoIntEnable(TRUE, FALSE);
}
void GXBreakPointHandler(OSContext *context)
{
OSContext exceptionContext;
// disable breakpoint checking
gx.cpEnable &= ~0x0020;
__cpReg[0x02] = gx.cpEnable;
// execute breakpoint callback
if(BreakPointCB)
{
OSClearContext(&exceptionContext);
OSSetCurrentContext(exceptionContext);
BreakPointCB();
}
OSClearContext(&exceptionContext);
OSSetCurrentContext(context);
}
void GXCPInterruptHandler(__OSInterrupt interrupt, OSContext *context)
{
gx.cpStatus = __cpReg[0x00];
// low watermark interrupt
if((gx.cpEnable >> 3) & 1) // low watermark enabled ?
{
if((gx.cpStatus >> 1) & 1) // interrupt active ?
{
GXUnderflowHandler();
}
}
// high watermark interrupt // high watermark enabled ?
if((gx.cpEnable >> 2) & 1)
{
if((gx.cpStatus >> 0) & 1) // interrupt active ?
{
GXOverflowHandler();
}
}
// break interrupt
if((gx.cpEnable >> 5) & 1) // breakpoint enabled ?
{
if((gx.cpStatus >> 4) & 1) // interrupt active ?
{
GXBreakPointHandler();
}
}
}
// ----------------------------------------------------------------------------
void GXInitFifoBase(GXFifoObj *fifo, void *base, u32 size)
{
if(fifo == CPUFifo)
{
OSPanic("GXInitFifoBase: fifo is attached to CPU");
}
if(fifo == GPFifo)
{
OSPanic("GXInitFifoBase: fifo is attached to GP");
}
if(base & 0x1f)
{
OSPanic("GXInitFifoBase: base must be 32B aligned");
}
if(base == NULL)
{
OSPanic("GXInitFifoBase: base pointer is NULL");
}
if(size & 0x1f)
{
OSPanic("GXInitFifoBase: size must be 32B aligned");
}
if(size < GX_FIFO_MINSIZE)
{
OSPanic("GXInitFifoBase: fifo is not large enough");
}
fifo->base = base;
fifo->top = base + size - 4;
fifo->size = size;
fifo->count = 0;
GXInitFifoLimits(fifo, size - GX_FIFO_HI_WATERMARK_BUFFER, (size / 2) & ~0x1f);
GXInitFifoPtrs(fifo, base, base);
}
void GXInitFifoLimits(GXFifoObj *fifo, u32 hiWaterMark, u32 loWaterMark)
{
if(fifo == GPFifo)
{
OSPanic("GXInitFifoLimits: fifo is attached to GP");
}
if(hiWaterMark & 0x1f)
{
OSPanic("GXInitFifoLimits: hiWatermark not 32B aligned");
}
if(loWaterMark & 0x1f)
{
OSPanic("GXInitFifoLimits: loWatermark not 32B aligned");
}
if((base - top) <= hiWaterMark)
{
OSPanic("GXInitFifoLimits: hiWatermark too large");
}
if(hiWaterMark < loWaterMark)
{
OSPanic("GXInitFifoLimits: hiWatermark below lo watermark");
}
fifo->hiWatermark = hiWaterMark;
fifo->loWatermark = loWaterMark;
}
void GXInitFifoPtrs(GXFifoObj *fifo, void *readPtr, void *writePtr)
{
BOOL level;
if(fifo == CPUFifo)
{
OSPanic("GXInitFifoPtrs: fifo is attached to CPU");
}
if(fifo == GPFifo)
{
OSPanic("GXInitFifoPtrs: fifo is attached to GP");
}
if(readPtr & 0x1f)
{
OSPanic("GXInitFifoPtrs: readPtr not 32B aligned");
}
if(writePtr & 0x1f)
{
OSPanic("GXInitFifoPtrs: writePtr not 32B aligned");
}
if((readPtr < fifo->base) || (readPtr >= fifo->top))
{
OSPanic("GXInitFifoPtrs: readPtr not in fifo range");
}
if((writePtr < fifo->base) || (writePtr >= fifo->top))
{
OSPanic("GXInitFifoPtrs: writePtr not in fifo range");
}
level = OSDisableInterrupts();
fifo->rdPtr = readPtr;
fifo->wrPtr = writePtr;
if((fifo->count = (readPtr - writePtr)) < 0)
{
fifo->count += fifo->size;
}
OSRestoreInterrupts(level);
}
// ----------------------------------------------------------------------------
void GXSetCPUFifo(GXFifoObj *fifo)
{
BOOL enabled = OSDisableInterrupts();
CPUFifo = fifo;
// immediate mode
if(CPUFifo == GPFifo)
{
__piReg[0x0C] = fifo->base;
__piReg[0x10] = fifo->top;
__piReg[0x14] = fifo->wrPtr;
CPGPLinked = 1;
__GXWriteFifoIntReset(TRUE, TRUE);
__GXWriteFifoIntEnable(TRUE, FALSE);
__GXFifoLink(TRUE);
}
// multi-buffer mode
else
{
if(CPGPLinked)
{
__GXFifoLink(FALSE);
CPGPLinked = 0;
}
__GXWriteFifoIntEnable(FALSE, FALSE);
__piReg[0x0C] = fifo->base;
__piReg[0x10] = fifo->top;
__piReg[0x14] = fifo->wrPtr;
}
PPC_SYNC();
OSRestoreInterrupts(enabled);
}
void GXSetGPFifo(GXFifoObj *fifo)
{
BOOL enabled = OSDisableInterrupts();
__GXFifoReadDisable();
__GXWriteFifoIntEnable(FALSE, FALSE);
GPFifo = fifo;
__cpReg[0x20] = (u16)fifo->base;
__cpReg[0x22] = (u16)(fifo->base >> 16);
__cpReg[0x24] = (u16)fifo->top;
__cpReg[0x26] = (u16)(fifo->top >> 16);
__cpReg[0x30] = (u16)fifo->count;
__cpReg[0x32] = (u16)(fifo->count >> 16);
__cpReg[0x34] = (u16)fifo->wrPtr;
__cpReg[0x36] = (u16)(fifo->wrPtr >> 16);
__cpReg[0x38] = (u16)fifo->rdPtr;
__cpReg[0x3A] = (u16)(fifo->rdPtr >> 16);
__cpReg[0x28] = (u16)fifo->hiWatermark;
__cpReg[0x2A] = (u16)(fifo->hiWatermark >> 16);
__cpReg[0x2C] = (u16)fifo->loWatermark;
__cpReg[0x2E] = (u16)(fifo->loWatermark >> 16);
PPC_SYNC();
if(CPUFifo == GPFifo)
{
CPGPLinked = 1;
__GXWriteFifoIntEnable(TRUE, FALSE);
__GXFifoLink(TRUE);
}
else
{
CPGPLinked = 0;
__GXWriteFifoIntEnable(FALSE, FALSE);
__GXFifoLink(FALSE);
__GXWriteFifoIntReset(TRUE, TRUE);
}
__GXFifoReadEnable();
OSRestoreInterrupts(enabled);
}
void GXSaveCPUFifo(GXFifoObj *fifo)
{
if(CPUFifo != fifo)
{
OSPanic("GXSaveCPUFifo: fifo is not attached to CPU");
}
__GXSaveCPUFifoAux(fifo);
}
static void __GXSaveCPUFifoAux(GXFifoObj *fifo)
{
BOOL enabled = OSDisableInterrupts();
GXFlush();
fifo->base = OSPhysicalToCached(__piReg[0x0C]);
fifo->top = OSPhysicalToCached(__piReg[0x10]);
fifo->wrPtr= OSPhysicalToCached(__piReg[0x14]);
if(CPGPLinked)
{
// save also GP fifo
fifo->rdPtr = OSPhysicalToCached((__cpReg[0x3A] << 16) | __cpReg[0x38]);
fifo->count = (__cpReg[0x32] << 16) | __cpReg[0x30];
}
else
{
// dont understand :( adjust fifo ?
fifo->wrPtr = fifo->rdPtr - fifo->count;
if(fifo->wrPtr < 0)
{
fifo->wrPtr += fifo->size;
}
}
}
void GXSaveGPFifo(GXFifoObj *fifo)
{
if(fifo != GPFifo)
{
OSPanic("GXSaveGPFifo: fifo is not attached to GP");
}
if((__cpReg[0x00] & 0x0002) == 0)
{
OSPanic("GXSaveGPFifo: GP is not idle");
}
fifo->rdPtr = OSPhysicalToCached((__cpReg[0x3A] << 16) | __cpReg[0x38]);
fifo->count = (__cpReg[0x32] << 16) | __cpReg[0x30];
}
void GXGetGPStatus(
GXBool *overhi,
GXBool *underlow,
GXBool *readIdle,
GXBool *cmdIdle,
GXBool *brkpt)
{
gx.cpStatus = __cpReg[0x00];
*overhi = (gx.cpStatus >> 0) & 1;
*underlow = (gx.cpStatus >> 1) & 1;
*readIdle = (gx.cpStatus >> 2) & 1;
*cmdIdle = (gx.cpStatus >> 3) & 1;
*brkpt = (gx.cpStatus >> 4) & 1;
}
void GXGetFifoStatus(
GXFifoObj *fifo,
GXBool *overhi,
GXBool *underlow,
u32 *fifoCount,
GXBool *cpu_write,
GXBool *gp_read,
GXBool *fifowrap)
{
...
}
void GXGetFifoPtrs(
GXFifoObj *fifo,
void **readPtr,
void **writePtr)
{
if(fifo == CPUFifo)
{
fifo->wrPtr = OSPhysicalToCached(__piReg[0x14]);
}
if(fifo == GPFifo)
{
fifo->rdPtr = OSPhysicalToCached((__cpReg[0x3A] << 16) | __cpReg[0x38]);
fifo->count = (__cpReg[0x32] << 16) | __cpReg[0x30];
}
else
{
fifo->wrPtr = fifo->rdPtr - fifo->count;
if(fifo->wrPtr < 0)
{
fifo->wrPtr += fifo->size;
}
}
*readPtr = fifo->rdPtr;
*writePtr= fifo->wrPtr;
}
void *GXGetFifoBase(GXFifoObj *fifo)
{
return fifo->rdPtr;
}
u32 GXGetFifoSize(GXFifoObj *fifo)
{
return fifo->size;
}
void GXGetFifoLimits(GXFifoObj *fifo, u32 *hi, u32 *lo)
{
*hi = fifo->hiWatermark;
*lo = fifo->loWatermark;
}
// ----------------------------------------------------------------------------
// breakpoint api
GXBreakPtCallback GXSetBreakPtCallback(GXBreakPtCallback cb)
{
GXBreakPtCallback oldcb = BreakPointCB;
BOOL enabled = OSDisableInterrupts();
BreakPointCB = cb;
OSRestoreInterrupts(enabled);
return oldcb;
}
void GXEnableBreakPt(void *breakPtr)
{
BOOL enabled = OSDisableInterrupts();
__GXFifoReadDisable();
__cpReg[0x3C] = (u16)breakPtr;
__cpReg[0x3E] = (u16)((u32)breakPtr >> 16);
gx.bpSentNot |= 0x0002; // clear bp int ?
gx.bpSentNot |= 0x0020;
__cpReg[2] = gx.bpSentNot;
__GXCurrentBP = breakPtr;
__GXFifoReadEnable();
OSRestoreInterrupts(enabled);
}
void GXDisableBreakPt(void)
{
BOOL enabled = OSDisableInterrupts();
gx.bpSentNot &= ~0x0022;
__cpReg[2] = gx.bpSentNot;
__GXCurrentBP = 0;
OSRestoreInterrupts(enabled);
}
// ----------------------------------------------------------------------------
void __GXFifoInit(void)
{
__OSSetInterruptHandler(
__OS_INTERRUPT_PI_CP,
GXCPInterruptHandler
);
__OSUnmaskInterrupts(OS_INTERRUPTMASK_PI_CP);
__GXCurrentThread = OSGetCurrentThread();
GXOverflowSuspendInProgress = FALSE;
CPUFifo = NULL;
GPFifo = NULL;
}
// ----------------------------------------------------------------------------
void __GXFifoReadEnable(void)
{
gx.cpEnable |= 0x0001;
__cpReg[0x02] = gx.cpEnable;
}
void __GXFifoReadDisable(void)
{
gx.cpEnable &= ~0x0001;
__cpReg[0x02] = gx.cpEnable;
}
void __GXFifoLink(u8 en)
{
u16 old = gx.cpEnable & ~0x0010;
gx.cpEnable |= (en << 4);
__cpReg[0x02] = gx.cpEnable;
}
void __GXWriteFifoIntEnable(BOOL hiWatermarkEn, BOOL loWatermarkEn)
{
u16 old = gx.cpEnable & ~0x000C;
gx.cpEnable |= (hiWatermarkEn << 2);
gx.cpEnable |= (loWatermarkEn << 3);
__cpReg[0x02] = gx.cpEnable;
}
void __GXWriteFifoIntReset(BOOL hiWatermarkClr, BOOL loWatermarkClr)
{
u16 old = gx.cpClr & ~0x0003;
gx.cpClr |= (hiWatermarkClr << 0);
gx.cpClr |= (loWatermarkClr << 1);
__cpReg[0x04] = gx.cpClr;
}
// for extensive testing of watermark triggers
void __GXInsaneWatermark(void)
{
GXFifoObj *realFifo = GPFifo;
// too slow, cough ..
realFifo->hiWatermark = realFifo->loWatermark + 512;
__cpReg[0x28] = (u16)realFifo->hiWatermark;
__cpReg[0x2A] = (u16)(realFifo->hiWatermark >> 16);
}
// ----------------------------------------------------------------------------
// strange ..
void __GXCleanGPFifo(void)
{
GXFifoObj dummyFifo,
*gpFifo = GXGetGPFifo(),
*cpuFifo = GXGetCPUFifo();
void *base;
base = GXGetFifoBase(gpFifo);
memset(&dummyFifo, 0, sizeof(GXFifoObj));
GXInitFifoPtrs(&dummyFifo, base, base);
GXSetGPFifo(&dummyFifo);
if(gpFifo == cpuFifo) GXSetCPUFifo(&dummyFifo);
GXInitFifoPtrs(gpFifo, base, base);
GXSetGPFifo(gpFifo);
if(gpFifo == cpuFifo) GXSetCPUFifo(cpuFifo);
}
OSThread *GXSetCurrentGXThread(void)
{
BOOL enabled = OSDisableInterrupts();
OSThread old = __GXCurrentThread;
if(GXOverflowSuspendInProgress)
{
OSPanic(
"GXSetCurrentGXThread: Two threads cannot generate"
"GX commands at the same time!"
);
}
__GXCurrentThread = OSGetCurrentThread();
OSRestoreInterrupts(enabled);
return old;
}
OSThread *GXGetCurrentGXThread(void)
{
return __GXCurrentThread;
}
// ----------------------------------------------------------------------------
GXFifoObj *GXGetCPUFifo(void)
{
return CPUFifo;
}
GXFifoObj *GXGetGPFifo(void)
{
return GPFifo;
}
// ----------------------------------------------------------------------------
u32 GXGetOverflowCount(void)
{
return __GXOverflowCount;
}
u32 GXResetOverflowCount(void)
{
__GXOverflowCount = 0;
}
// ----------------------------------------------------------------------------
// write gather pipe redirecting api
volatile void * GXRedirectWriteGatherPipe(void *ptr)
{
BOOL enabled = OSDisableInterrupts();
if(__GXinBegin)
{
OSPanic(
"\'GXRedirectWriteGatherPipe\' is not"
"allowed between GXBegin/GXEnd"
);
}
ASSERT(OFFSET(ptr, 32) == 0);
ASSERT(!IsWGPipeRedirected);
IsWGPipeRedirected = TRUE;
GXFlush();
// wait until pipe is not empty
while(PCMfwpar() & WPAR_BNE);
PPCMtwpar(OSUncachedToPhysical(0xCC008000));
if(CPGPLinked)
{
__GXFifoLink(0);
__GXWriteFifoIntEnable(0, 0);
}
CPUFifo->wrPtr = OSPhysicalToCached(__piReg[0x14]);
__piReg[0x0C] = 0; // base
__piReg[0x10] = 0x04000000; // top
__piReg[0x14] = OSCachedToHardwired(ptr);
PPC_SYNC();
OSRestoreInterrupts(enabled);
return 0xCC008000;
}
void GXRestoreWriteGatherPipe(void)
{
PPCWGPipe wgpipe : 0xCC008000;
BOOL enabled;
int i;
ASSERT(IsWGPipeRedirected);
IsWGPipeRedirected = FALSE;
enabled = OSDisableInterrupts();
// flush fifo
for(i=0; i<31; i++)
{
wgpipe.u8 = 0;
}
PPCSync();
// wait until pipe is not empty
while(PCMfwpar() & WPAR_BNE);
PPCMtwpar(OSUncachedToPhysical(0xCC008000));
__piReg[0x0C] = CPUFifo->base;
__piReg[0x10] = CPUFifo->top;
__piReg[0x14] = OSCachedToHardwired(CPUFifo->wrPtr);
if(CPGPLinked)
{
__GXWriteFifoIntReset(TRUE, TRUE);
__GXWriteFifoIntEnable(TRUE, FALSE);
__GXFifoLink(TRUE);
}
PPC_SYNC();
OSRestoreInterrupts(enabled);
}