/******************************Module*Header*******************************\ * * Module Name: displist.c * Author: Goran Devic, Mark Einkauf * Purpose: General output to Laguna3D * * Copyright (c) 1997 Cirrus Logic, Inc. * \**************************************************************************/ #define OPENGL_MCD #include "precomp.h" #include "mcdhw.h" #include "mcdutil.h" /********************************************************************* * Defines **********************************************************************/ #define DL_MIN_SIZE (8 * KB) // Minimum size of the d-list #define DL_MAX_SIZE (4096 * KB) // Maximum size of the d-list #define DL_SAFETY_MARGIN (1 * KB) // Margin when building d-list (b) //WARNING!!! Any change to DL_START_OFFSET must be made in polys.c also!!! #define DL_START_OFFSET 20 // 5 dwords offset for dlist //WARNING!!! Any change to DL_START_OFFSET must be made in polys.c also!!! /********************************************************************* * Include Files **********************************************************************/ /********************************************************************* * Local Functions **********************************************************************/ /********************************************************************* * * DWORD _InitDisplayList( LL_DeviceState * DC ) * * Allocates memory and initializes display list structure. * Two display lists are created. * * Where: * * DC is the device context structure to be initialized * DC->dwDisplayListLen is the size of the display list * to be allocated (in bytes). * * Returns: * * LL_OK (0) if initialization succeeds * error_code if initialization fails * **********************************************************************/ DWORD _InitDisplayList( PDEV *ppdev, DWORD dwListLen ) { int i; #if 0 // The user requested DC->dwDisplayListLen bytes of system memory // to be used for device display list. That memory will be // allocated and stay locked. But first, we subdivide it at // several chunks for display list multibuffering. // // Sanity check // if( dwListLen < DL_MIN_SIZE || dwListLen > DL_MAX_SIZE ) return( LLE_INI_DL_LEN ); // Subdivide display list and allocate each chunk // chunk_size = (dwListLen / NUM_DL) & ~3; for( i=0; iLL_State.DL[0].pdwLinPtr = ppdev->temp_DL_chunk; // Set the length and the parametarization pointer to point to the // offset of 20: 16 bytes are reserved for jump table, additional 4 bytes // for a semaphore. // ppdev->LL_State.DL[0].dwLen = dwListLen; ppdev->LL_State.DL[0].pdwNext = ppdev->LL_State.DL[0].pdwLinPtr + DL_START_OFFSET/4; ppdev->LL_State.DL[0].pdwStartOutPtr = ppdev->LL_State.DL[0].pdwNext;//only used in coproc mode ppdev->LL_State.pDL = &ppdev->LL_State.DL[ 0 ]; ppdev->LL_State.pDL->pdwNext = ppdev->LL_State.pDL->pdwLinPtr + DL_START_OFFSET/4; ppdev->LL_State.pDL->pdwStartOutPtr = ppdev->LL_State.pDL->pdwNext;//only used in coproc mode #endif return( LL_OK ); } #ifndef OPENGL_MCD /********************************************************************* * * void _CloseDisplayList() * * Cleans up the memory allocations for display list(s). * This function is to be called at closing the library. * **********************************************************************/ void _CloseDisplayList() { int i; // Loop through and free each chunk of the display list // for( i=0; ibOp = LL_IDLE; * **********************************************************************/ DWORD * fnIdle( DWORD * pdwNext, LL_Batch * pBatch ) { if( !LL_State.fIndirectMode ) *pdwNext++ = IDLE; return( pdwNext ); } /********************************************************************* * * Batch cell instruction: LL_NOP * * Does nothing. * **********************************************************************/ DWORD * fnNop( DWORD * pdwNext, LL_Batch * pBatch ) { return( pdwNext ); } /********************************************************************* * * Batch cell instruction: LL_RAW_DATA * * Copies raw data to the display list. The pointer to data (DWORD *) * is pVert, number of dwords to be copied is in wCount. * * Note: All the data must fit into a display list. No explicit checking * is done. Keep your data stream short! * * Example: * * pBatch->wCount = 2; // 2 integers of data * pBatch->pVert = (DWORD *) &MyData[0]; // Starting data address * **********************************************************************/ DWORD * fnRawData( DWORD * pdwNext, LL_Batch * pBatch ) { register int count; register DWORD * dwPtr; dwPtr = (DWORD *)pBatch->pVert; for( count = pBatch->wCount; count>0; count-- ) { *pdwNext++ = *dwPtr++; } return( pdwNext ); } #endif // ndef OPENGL_MCD /********************************************************************* * * DWORD * _RunLaguna( DWORD *pdwNext ) * * Spins off Laguna 3D and resets the parametarization pointers * to the next available d-list * * The function will take into account the rendering mode that * was set to either processor or coprocessor indirect. * In the later case it will sense the instructions that can * not be executed in that mode and it will do them directly. * * Where: * * pdwNext is the pointer to a current display list to the next * available space. * * Returns: * * Offset in the new d-list to start building * **********************************************************************/ void _RunLaguna( PDEV *ppdev, DWORD *pdwNext ) { DWORD instr; int len; int address, offset, update_offset; int event; volatile int status; // Two rendering modes are supported: processor direct mode and // coprocessor indirect mode. // // MCD_TEMP - RunLaguna only supporting coprocessor mode now //if( ppdev->LL_State.fIndirectMode ) { DWORD *pSrc, *pDest; // Co-processor indirect mode: use host data port to program // the hardware // pSrc = ppdev->LL_State.pDL->pdwStartOutPtr; pDest = ppdev->LL_State.pRegs + HOST_3D_DATA_PORT; // Assumption: On the entry of this loop pSrc points to the valid // instruction. // while( pdwNext != pSrc ) { // The display list is examined for the instructions that // can not be executed in coprocessor mode. That involves // partially disassembling each instruction and tracing the // number of parameters. // // All this work is done so that WRITE_DEV_REGS and READ_DEV_REGS // may be detected and simulated. Other instructions such as // branches, idle, return, waits and interrupts are ignored. // /* Get the next instruction */ instr = *pSrc; update_offset = 0; /* Switch on the instruction opcode */ #if 1 // 1 here for good RM sound { int i; status = *(volatile *)(ppdev->LL_State.pRegs + PF_STATUS_3D); while (status & 0x200) { //i=10; //while (i--) { /* don't read in tight loop*/ } status = *(volatile *)(ppdev->LL_State.pRegs + PF_STATUS_3D); } } #endif USB_TIMEOUT_FIX(ppdev) switch( instr >> 27 ) { case 0x00: /* Draw point opcode */ case 0x01: /* Draw line opcode */ case 0x02: /* Draw poly opcode */ case 0x03: /* Write register opcode */ /* Get the amount of data for this opcode */ len = (instr & 0x3F) + 1; /* Send data to the host data area */ while( len-- ) *pDest = *pSrc++; break; case 0x05: /* Write device opcode (Simulated) */ /* Step past the instruction opcode */ pSrc++; /* Get the amount of data for this opcode */ len = instr & 0x3F; /* Switch on the module selected */ switch( (instr >> 21) & 0x1F ) { case 0x00: /* VGA register set */ /* Setup the device address and offset */ address = (int) (((BYTE *) ppdev->LL_State.pRegs) + 0x0000); offset = (instr >> 6) & 0x0FF; break; case 0x01: /* VGA frame buffer */ /* Setup the device address and offset */ address = 0xA0000; offset = (instr >> 6) & 0x7FF; break; case 0x02: /* Video port */ /* Setup the device address and offset */ address = (int) (((BYTE *) ppdev->LL_State.pRegs) + 0x0100); offset = (instr >> 6) & 0x07F; break; case 0x03: /* Local peripheral bus */ /* Setup the device address and offset */ address = (int) (((BYTE *) ppdev->LL_State.pRegs) + 0x0180); offset = (instr >> 6) & 0x07F; break; case 0x04: /* Miscellaneous */ /* Setup the device address and offset */ address = (int) (((BYTE *) ppdev->LL_State.pRegs) + 0x0200); offset = (instr >> 6) & 0x0FF; break; case 0x05: /* 2D engine registers */ /* Setup the device address and offset */ address = (int) (((BYTE *) ppdev->LL_State.pRegs) + 0x0400); offset = (instr >> 6) & 0x3FF; break; case 0x06: /* 2D host data */ /* Setup the device address and offset */ address = (int) (((BYTE *) ppdev->LL_State.pRegs) + 0x0800); offset = (instr >> 6) & 0x7FF; break; case 0x07: /* Frame buffer */ /* Setup the device address and offset */ address = (int) ppdev->LL_State.pFrame; offset = (instr >> 6) & 0x7FF; break; case 0x08: /* ROM memory */ /* Setup the device address and offset */ address = 0xC0000; offset = (instr >> 6) & 0x7FF; break; case 0x09: /* 3D engine registers */ /* Setup the device address and offset */ address = (int) (((BYTE *) ppdev->LL_State.pRegs) + 0x4000); offset = (instr >> 6) & 0x1FF; break; case 0x0A: /* 3D host XY registers */ /* Setup the device address and offset */ address = (int) (((BYTE *) ppdev->LL_State.pRegs) + 0x4200); offset = (instr >> 6) & 0x0FF; update_offset = 4; break; case 0x0B: /* 3D host data */ /* Setup the device address and offset */ address = (int) (((BYTE *) ppdev->LL_State.pRegs) + 0x4800); offset = (instr >> 6) & 0x3FF; break; default: /* Unknown module */ /* Setup the device address and offset */ //printf("RL:WriteDev, unknown mod instr=%x\n",instr); address = (int)NULL; offset = (int)NULL; break; } /* If this device supported, send the data */ if ( address ) { /* Send data to the device */ while( len-- ) { *(DWORD *)( address + offset ) = *pSrc++; offset += update_offset; } } else pSrc += len; break; case 0x0D: /* Control opcode */ /* Step past the instruction opcode */ pSrc++; /* Switch on the sub-opcode value */ switch( (instr >> 22) & 0x0F ) { case 0: /* Idle sub-opcode */ /* Force exit from display list */ pSrc = pdwNext; break; case 5: /* Clear sub-opcode */ *pDest = instr; break; } break; case 0x0E: /* Wait opcode */ { int wait_time_out; /* Step past the instruction opcode */ pSrc++; /* Get the wait event mask */ event = instr & 0x3FF; if (event == EV_BUFFER_SWITCH) wait_time_out=100000; else wait_time_out=5000000; /* Switch on the wait opcode sub-type (AND/OR/NAND/NOR) */ switch( (instr >> 24) & 0x03 ) { case 0: /* Wait OR sub-opcode */ /* Wait for requested event to happen */ do { status = (*(volatile *)(ppdev->LL_State.pRegs + PF_STATUS_3D) & 0x3FF) ^ 0x3E0; } while((!(status & event)) && wait_time_out--); break; case 1: /* Wait NOR sub-code */ /* Wait for requested event to happen */ do { status = (*(volatile *)(ppdev->LL_State.pRegs + PF_STATUS_3D) & 0x3FF) ^ 0x01F; } while((!(status & event)) && wait_time_out--); break; case 2: /* Wait AND sub-opcode */ /* Wait for requested events to happen */ do { status = (*(volatile *)(ppdev->LL_State.pRegs + PF_STATUS_3D) & 0x3FF) ^ 0x3E0; } while(((status & event) != event) && wait_time_out--); break; case 3: /* Wait NAND sub-opcode */ /* Wait for requested events to happen */ do { status = (*(volatile *)(ppdev->LL_State.pRegs + PF_STATUS_3D) & 0x3FF) ^ 0x01F; } while(((status & event) != event) && wait_time_out--); break; } //if (wait_time_out <= 0) printf("WAIT TIMED OUT, instr = %x, ev=%x stat=%x\n",instr,event,status); } break; default: /* Unknown/unhandled opcode (Skip) */ //printf("RL:WriteDev, unknown opcode, instr=%x\n",instr); /* Step past the instruction opcode */ pSrc++; break; } } } #if 0 // MCD_TEMP - no processor mode support yet else { // Check for valid address to put IDLE // if( ((int)pdwNext < (int)ppdev->LL_State.pDL->pdwLinPtr) || ((int)pdwNext - (int)ppdev->LL_State.pDL->pdwLinPtr >= chunk_size - 16) ) { //printf("DISPLAY LIST OVERRUN\n"); goto proceed; } // Stuff IDLE since this must be the end of current display list // *pdwNext = IDLE; // If Laguna is still busy with the previous d-list, poll // it until it is idle. // DEB("Entering 3D Engine Busy wait state...\n"); LL_Wait(); // We have to set the base address of the display list // This address is used as a base address when // fetching display list instructions // *(ppdev->LL_State.pRegs + PF_BASE_ADDR_3D) = ppdev->LL_State.pDL->dwPhyPtr; inp(0x80); inp(0x80); DEB2("New base: PF_BASE_ADDR_3D: %08X\n", *(ppdev->LL_State.pRegs + PF_BASE_ADDR_3D) ); // Start the execution of the display list from the offset 0 // // *(ppdev->LL_State.pRegs + PF_INST_3D) = BRANCH + DL_START_OFFSET; // No! Temporary fix the prefetch bug and jump to the // top of the display list where the real branch(0) // instruction was stored during the display list initialization // DEB4("Issuing BRANCH %08X (->%08X ->%08X)\n", BRANCH + chunk_size - 16, *(DWORD *)((int)ppdev->LL_State.pDL->pdwLinPtr + chunk_size - 16), *(ppdev->LL_State.pDL->pdwLinPtr + 5) ); if( *(DWORD *)((int)ppdev->LL_State.pDL->pdwLinPtr + chunk_size - 16) != BRANCH + DL_START_OFFSET ) { //printf("BRANCH LOCATION CONTAINS INVALID DATA!\n"); goto proceed; } *(ppdev->LL_State.pRegs + PF_INST_3D) = BRANCH + chunk_size - 16; inp(0x80); inp(0x80); } proceed: #endif // 0 - no processor mode support yet #if 0 // Set the active display list index to the next in an array // if( ++ppdev->LL_State.dwCdl >= NUM_DL ) ppdev->LL_State.dwCdl = 0; // Reset the parametarization pointer to the beginning // of the next display list // ppdev->LL_State.pDL = &ppdev->LL_State.DL[ ppdev->LL_State.dwCdl ]; ppdev->LL_State.pDL->pdwNext = ppdev->LL_State.pDL->pdwLinPtr + DL_START_OFFSET/4; ppdev->LL_State.pDL->pdwStartOutPtr = ppdev->LL_State.pDL->pdwNext;//only used in coproc mode return( ppdev->LL_State.pDL->pdwNext ); #else // 0 // MCD_TEMP - ppdev->LL_State.pDL remains same always since only 1 DList ppdev->LL_State.pDL->pdwNext = ppdev->LL_State.pDL->pdwLinPtr + DL_START_OFFSET/4; ppdev->LL_State.pDL->pdwStartOutPtr = ppdev->LL_State.pDL->pdwNext;//only used in coproc mode #endif // 0 } #ifndef OPENGL_MCD /********************************************************************* * * void LL_Execute( LL_Batch * pBatch ) * * Builds the display list from the batch array and executes it. * This function is the main executing function. If the batch * array cannot be completely fit into the available display * list, it will be processed in multiple pieces. * **********************************************************************/ void LL_Execute( LL_Batch * pBatch ) { register DWORD *pdwNext; // First we build the current display list // pdwNext = LL_State.pDL->pdwNext; do { // Build the current display list until IDLE instruction // //printf("bOp=%x\n",pBatch->bOp); pdwNext = (fnList[ pBatch->bOp ])( pdwNext, pBatch ); } while( (pBatch++)->bOp != LL_IDLE ); // Spin off the d-list that was just built and prepare to render // to the next available d-list // (void)_RunLaguna( pdwNext ); } /********************************************************************* * * void LL_QueueOp( LL_Batch *pBatch ) * * Queues the operation. This function supports programming the * l3d without using the arrays of batch cells and vertex cells. * Instead of building a batch command, all the parameters that * are to be set in a batch are passed in this function. * * Where: * * pBatch->bOp is the operation (eg. LL_LINE) * pBatch->wCount is the generic count * pBatch->dwFlags are the operation flags * pBatch->wBuf is the buffer/texture designator * pBatch->pVert is the pointer to a vertex array defining the operation * **********************************************************************/ void LL_QueueOp( LL_Batch *pBatch ) { // We continue building the current display list // If instruction was IDLE, spin-off the current d-list // if( pBatch->bOp == LL_IDLE ) (void)_RunLaguna( LL_State.pDL->pdwNext ); else LL_State.pDL->pdwNext = (fnList[ pBatch->bOp ])( LL_State.pDL->pdwNext, pBatch ); } /********************************************************************* * * void LL_SetRenderingMode( DWORD dwMode ) * * Sets the rendering mode: coprocessor indirect or processor * mode. By default, library will use processor mode and build * display list to execute it. * * Where: * * dwMode is one of LL_PROCESSOR_MODE * LL_COPROCESSOR_MODE * * **********************************************************************/ void LL_SetRenderingMode( DWORD dwMode ) { LL_State.fIndirectMode = dwMode; DEB2("Rendering mode set to: %sPROCESSOR\n", dwMode?"CO":"" ); LL_Wait(); } /********************************************************************* * * void DumpDisplayList( DWORD *pPtr, DWORD dwLen ) * * Dumps the content of the display list to the file. This function * can be used by a software developer for debugging. * * The display list disassembler (sldis.exe) may be used to * disassemble the display list: * * sldis -i9 < list00.pci > list00.out * * Where: * * pPtr is the starting address to dump * dwLen is the number of dwords to dump * **********************************************************************/ void DumpDisplayList( DWORD *pPtr, DWORD dwLen ) { static int count = 0; static char *sName = "List00.pci"; FILE *fp; DWORD i; #ifndef CGL // if( count == 5 ) // count = 4; sprintf(&sName[4], "%02X.pci", count++ ); printf("Temp Name: %s\nSize:%d dwords", sName, dwLen ); if( (fp=fopen(sName, "w" ))==NULL ) return; for( i=0; i