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