dma.cpp

00001 /*
00002 * "PS2" Application Framework
00003 *
00004 * University of Abertay Dundee
00005 * May be used for educational purposed only
00006 *
00007 * Author - Dr Henry S Fortuna
00008 *
00009 * $Revision: 1.2 $
00010 * $Date: 2007/08/19 12:45:09 $
00011 *
00012 */
00013 
00014 #include "dma.h"
00015 
00016 // Singleton initialisers (note: Never make new copies of these classes anywhere else)
00017 CVIFStaticDMA VIFStaticDMA_SingletonInit;
00018 CVIFDynamicDMA VIFDynamicDMA_SingletonInit;
00019 
00020 // Function to print the packet via printf
00021 void CDMAChain::PrintPacket()
00022 {
00023         SPS2Manager.FlushCache();
00024 
00025         unsigned int * pMem = (unsigned int *)m_pPages[0].GetCached();
00026         
00027         printf("            ___________________________________________ \n");
00028         printf("  Address  |127     96|95      64|63      32|31       0| Virt Addr\n");
00029         printf(".----------+----------+----------+----------+----------|----------.\n");
00030 
00031         int iWritten = ((int)m_pPtr - (int)pMem + 0xF) >> 4;
00032 
00033         for(int i = 0; i < iWritten; i++)
00034         {
00035                 static int iPhysAddr = 0;
00036 
00037                 if((i % 256) == 0)
00038                 {
00039                         iPhysAddr = m_pPages[(i / 256)].GetPhysicalAddr();
00040                 }
00041 
00042                 printf("| %.8X | ", iPhysAddr + ((i % 256) << 4));
00043         
00044                 for(int j = 0; j < 4; j++)
00045                 {
00046                         printf("%.8X | ", pMem[3 - j]);
00047                 }
00048 
00049                 printf("%.8X | ", (int)pMem);
00050 
00051                 printf("\n");
00052                 pMem += 4;
00053         }
00054 
00055         printf("'-----------------------------------------------------------------'\n");
00056 }
00057 
00058 // Stitches a DMA packet over the 4k boundary
00059 void CDMAChain::Stitch()
00060 {
00061         VIF1_STATE VifState = m_VifState;
00062 
00063         // If we are in direct mode we will have to end it, stitch, and then start it again
00064         // in the new page
00065         if(VifState == VIF_DIRECT)
00066         {
00067                 EndDirect();
00068         }
00069 
00070         // Finish the previous DMA tag (i.e. set its QWC) and point it to the start of the next page
00071         *m_pDMATag = ((uint64)NewPage() << 32) + (2 << 28) + (m_pPtr - (int *)m_pDMATag) / 4 - 1;
00072         // And start a new one.
00073         NewTag();
00074 
00075         if(VifState == VIF_DIRECT)
00076                 StartDirect();
00077 }
00078 
00079 // Allocate space in the chain for the DMA tag that will be added there once we know how many
00080 // quad words it will be writing.
00081 void CDMAChain::NewTag()
00082 {
00083         m_pDMATag = (uint64 *)m_pPtr;
00084         Add64(0);
00085 }
00086 
00087 // Prepare for a new DMA tag
00088 void CDMAChain::PrepForDMATag()
00089 {
00090         // Make sure we aren't in DIRECT mode
00091         assert(m_VifState == VIF_BASE);
00092         // Make sure we are aligned on a quadword boundary.
00093         Align(4);
00094 
00095         // We can't add a new tag at the very end of a packet, so
00096         // lets just add a NOP and let the stitching handle the new tag.
00097         if(m_pPtr == m_pEndPtr)
00098                 Add64(0);
00099 }
00100 
00101 // Fire() will call this for you, so never call this yourself unless you know what you are doing.
00102 void CDMAChain::DMAEnd()
00103 {       
00104         // This function sets the previous open DMA tag to an end tag so that the DMA chain ends.
00105 
00106         assert(m_VifState == VIF_BASE);
00107         // Pad if we are trying to transfer less than a whole number of quadwords
00108         Align(4);
00109 
00110         // And finish the open DMA tag.
00111         *m_pDMATag = (7 << 28) + (m_pPtr - (int *)m_pDMATag) / 4 - 1;
00112 }
00113 
00114 // Call DMARet when you want to return from a DMA chain in the static buffer
00115 void CDMAChain::DMARet()
00116 {
00117         // Prepare for a new tag.
00118         PrepForDMATag();
00119         // Put the ret tag in as the most recent open DMA tag.
00120         *m_pDMATag = (6 << 28) + (m_pPtr - (int *)m_pDMATag) / 4 - 1;
00121         // And create a new tag.
00122         NewTag();
00123 }
00124 
00125 // The function will cause the DMAC to branch to the address at iAddr and run until it hits a DMARet tag,
00126 // when it will return control back to the tag after this one.
00127 void CDMAChain::DMACall(uint32 iAddr)
00128 {
00129         PrepForDMATag();
00130         *m_pDMATag = ((uint64)iAddr << 32) + (5 << 28) + (m_pPtr - (int *)m_pDMATag) / 4 - 1;
00131         NewTag();
00132 }
00133 
00134 // Start Direct (GS Path 2) mode.
00135 void CDMAChain::StartDirect()
00136 {
00137         assert(m_VifState == VIF_BASE);
00138         // Remember that the DIRECT vif code has to go into the very last (i.e. 3rd) word of a quadword.
00139         Align(4, 3);
00140         // Set the vif state so that the stitching knows if it has to stop and start direct mode.
00141         m_VifState = VIF_DIRECT;
00142         m_pVIFCode = m_pPtr;
00143         // Add the space for the DIRECT vif code (it won't be written until EndDirect() is called and we
00144         // know how many quad words were written)
00145         Add32(0);
00146 }
00147 
00148 // End Direct (GS Path 2) mode
00149 void CDMAChain::EndDirect()
00150 {
00151         assert(m_VifState == VIF_DIRECT);
00152         // We can only transfer an integer number of quadwords, so pad with NOPs if we don't haven't got that much.
00153         Align(4);
00154         int iSize = (m_pPtr - m_pVIFCode - 1) / 4;
00155         if(iSize != 0)  // If the size isn't 0, set write the DIRECT vifcode.
00156                 *m_pVIFCode = (0x50 << 24) + iSize;
00157         m_VifState = VIF_BASE;
00158 }
00159 
00160 // Start an A+D mode segment (you must be DIRECT mode to use this)
00161 void CDMAChain::StartAD()
00162 {
00163         assert(m_VifState == VIF_DIRECT);
00164 
00165         if(m_pPtr >= m_pEndPtr)
00166         {
00167                 EndDirect();
00168                 StartDirect();
00169         }
00170 
00171         // Store the location so we can add the A+D amount later.
00172         m_pADGiftag = (uint64 *)m_pPtr;
00173         // Add the A+D giftag
00174         Add64((1 << 15) | (1ULL << 60));
00175         Add64(0xE);
00176 }
00177 
00178 // Add an A+D mode piece, iData is the data to add, iAddr is the register number to send it to
00179 void CDMAChain::AddAD(uint64 iData, uint64 iAddr)
00180 {
00181         Add64(iData);
00182         Add64(iAddr);
00183         // Increment the NLOOP in the giftag
00184         (*m_pADGiftag)++;
00185 }
00186 
00187 // Start uploading MPG code to VU1_MEM address iAddr
00188 void CDMAChain::StartMPG(uint32 iAddr)
00189 {
00190         assert(m_VifState == VIF_BASE);
00191         // MPG vifcode must be in either word 1 or 3 of a quadword
00192         Align(2, 1);
00193         m_pVIFCode = m_pPtr;
00194         // Add the VIF code
00195         Add32((0x4a << 24) + iAddr);
00196         m_iMPGCount = 0;
00197         m_iMPGAddr = iAddr;
00198 }
00199 
00200 // Call this once you have finished adding the microcode chunks
00201 void CDMAChain::EndMPG()
00202 {
00203         // Write the number of doubleword chunks of VU code that were written, to the MPG vifcode.
00204         *m_pVIFCode += (m_iMPGCount & 0xFF) << 16;
00205 }
00206 
00207 // Add a 64bit microcode instruction to the MPG chain
00208 void CDMAChain::AddMPG(uint64 iInst)
00209 {
00210         // We are only allowed to transfer 256 microcode instructions at once, so if we are trying to send
00211         // more we will need to use a new MPG vifcode.
00212         if(m_iMPGCount >= 256)
00213         {
00214                 EndMPG();
00215                 StartMPG(m_iMPGAddr + m_iMPGCount);
00216         }
00217 
00218         // Add the instruction and increment the microcode count.
00219         Add64(iInst);
00220         m_iMPGCount++;
00221 }
00222 
00223 // Make the current write pointer aligned to a certain multiple of words with an offset, by padding
00224 // with NOPs if it isn't.
00225 void CDMAChain::Align(int iAlign, int iOffset = 0)
00226 {
00227         int p = (((int)m_pPtr >> 2) - iOffset) & (iAlign - 1);
00228         if(p == 0)
00229                 return;
00230         for(p = iAlign - p; p > 0; p--)
00231                 Add32(0);
00232 }
00233 
00234 // Add a floating point vector to the chain
00235 void CDMAChain::AddVectorF(float x, float y, float z, float w)
00236 {
00237         AddFloat(x);    
00238         AddFloat(y);
00239         AddFloat(z);
00240         AddFloat(w);
00241 }
00242 
00243 // Add an integer vector to the chain
00244 void CDMAChain::AddVectorI(int x, int y, int z, int w)
00245 {
00246         Add32(x);
00247         Add32(y);
00248         Add32(z);
00249         Add32(w);
00250 }
00251 
00252 // Add a 4x4 matrix to the chain
00253 void CDMAChain::AddMatrix(const Matrix4x4 & Matrix)
00254 {
00255         AddFloat(Matrix(0, 0));
00256         AddFloat(Matrix(0, 1));
00257         AddFloat(Matrix(0, 2));
00258         AddFloat(Matrix(0, 3));
00259 
00260         AddFloat(Matrix(1, 0));
00261         AddFloat(Matrix(1, 1));
00262         AddFloat(Matrix(1, 2));
00263         AddFloat(Matrix(1, 3));
00264 
00265         AddFloat(Matrix(2, 0));
00266         AddFloat(Matrix(2, 1));
00267         AddFloat(Matrix(2, 2));
00268         AddFloat(Matrix(2, 3));
00269 
00270         AddFloat(Matrix(3, 0));
00271         AddFloat(Matrix(3, 1));
00272         AddFloat(Matrix(3, 2));
00273         AddFloat(Matrix(3, 3));
00274 }
00275 
00276 // Begin a whole new chain
00277 void CDMAChain::Begin()
00278 {
00279         m_VifState = VIF_BASE;
00280         m_iPhysAddr = NewPage();
00281         NewTag(); // Add the first tag of the chain
00282 }
00283 
00284 CVIFDynamicDMA::CVIFDynamicDMA()
00285 {
00286         m_iCurrPage = 0;
00287         m_iCurrBuffer = 0;
00288 }
00289 
00290 CVIFDynamicDMA::~CVIFDynamicDMA()
00291 {
00292 
00293 }
00294 
00295 void CVIFDynamicDMA::Initialise(int iNumPages)
00296 {
00297         m_iNumPages = iNumPages;
00298 
00299         // We are using a double buffer so allocate 2x the memory
00300         m_pPages = new CDMAMem[m_iNumPages * 2];
00301 
00302         for(int iPage = 0; iPage < (m_iNumPages * 2); iPage++)
00303                 SPS2Manager.Allocate(m_pPages[iPage], 256);
00304 
00305         *EE_VIF1_ERR = 2; // Ignore DMA mismatch error
00306 
00307         Begin();
00308 }
00309 
00310 // Move onto a new 4K page
00311 int CVIFDynamicDMA::NewPage()
00312 {
00313         assert(m_iCurrPage < m_iNumPages);
00314 
00315         m_pPtr = (int *)m_pPages[m_iCurrPage + (m_iCurrBuffer * m_iNumPages)].GetCached();
00316         m_pEndPtr = m_pPtr + 1024;
00317         int iPhysAddr = (int)m_pPages[m_iCurrPage + (m_iCurrBuffer * m_iNumPages)].GetPhysicalAddr();
00318         ++m_iCurrPage;
00319 
00320         return iPhysAddr;
00321 }
00322 
00323 // Print the current VIF packet
00324 void CVIFDynamicDMA::PrintPacket()
00325 {
00326         SPS2Manager.FlushCache();
00327 
00328         unsigned int * pMem = (unsigned int *)m_pPages[(m_iCurrBuffer * m_iNumPages)].GetCached();
00329         
00330         printf("            ___________________________________________            \n");
00331         printf("  Address  |127     96|95      64|63      32|31       0| Virt Addr \n");
00332         printf(".----------+----------+----------+----------+----------|----------.\n");
00333 
00334         int iWritten = ((int)m_pPtr - (int)pMem + 0xF) >> 4;
00335 
00336         for(int i = 0; i < iWritten; i++)
00337         {
00338                 static int iPhysAddr = 0;
00339 
00340                 if((i % 256) == 0)
00341                 {
00342                         iPhysAddr = m_pPages[(i / 256) + (m_iCurrBuffer * m_iNumPages)].GetPhysicalAddr();
00343                 }
00344 
00345                 printf("| %.8X | ", iPhysAddr + ((i % 256) << 4));
00346         
00347                 for(int j = 0; j < 4; j++)
00348                 {
00349                         printf("%.8X | ", pMem[3 - j]);
00350                 }
00351 
00352                 printf("%.8X | ", (int)pMem);
00353 
00354                 printf("\n");
00355                 pMem += 4;
00356         }
00357 
00358         printf("'-----------------------------------------------------------------'\n");
00359 }
00360 
00361 void CVIFDynamicDMA::Fire(bool bFire = true, bool bPrint = false)
00362 {
00363         // End the chain before we send it.
00364         DMAEnd();
00365 
00366         if(bPrint)
00367                 PrintPacket();
00368 
00369         asm("sync.l");
00370         // Flush all the data through to memory.
00371         SPS2Manager.FlushCache();
00372         
00373         // Wait for channel 1 to finish the previous packet
00374         while(*EE_D1_CHCR & 256);
00375         *EE_D1_QWC = 0;
00376         *EE_D1_TADR = m_iPhysAddr & 0x8ffffff0;
00377 
00378         // Send the packet!
00379         if(bFire)
00380                 *EE_D1_CHCR = 0x145;
00381 
00382         asm("sync.l");
00383         
00384         // Make it so that now we write to the beginning of the other buffer.
00385         m_iCurrBuffer ^= 1;
00386         m_iCurrPage = 0;
00387 
00388         // Start the packet
00389         Begin();
00390 }
00391 
00392 CVIFStaticDMA::CVIFStaticDMA()
00393 {
00394         m_iCurrPage = 0;
00395 }
00396 
00397 CVIFStaticDMA::~CVIFStaticDMA()
00398 {
00399 
00400 }
00401 
00402 void CVIFStaticDMA::Initialise(int iNumPages)
00403 {
00404         m_iNumPages = iNumPages;
00405 
00406         m_pPages = new CDMAMem[iNumPages];
00407 
00408         for(int iPage = 0; iPage < iNumPages; iPage++)
00409                 SPS2Manager.Allocate(m_pPages[iPage], 256);
00410 
00411         *EE_VIF1_ERR = 2; // Ignore DMA mismatch error
00412 
00413         Begin();
00414 }
00415 
00416 int CVIFStaticDMA::NewPage()
00417 {
00418         assert(m_iCurrPage < m_iNumPages);
00419 
00420         m_pPtr = (int *)m_pPages[m_iCurrPage].GetCached();
00421 
00422         m_pEndPtr = m_pPtr + 1024;
00423         int iPhysAddr = (int)m_pPages[m_iCurrPage++].GetPhysicalAddr(); 
00424         m_iPhysicalOffset = iPhysAddr - (int)m_pPtr;
00425 
00426         return iPhysAddr;
00427 }

Generated on Sun May 18 21:45:08 2008 for PS2X by  doxygen 1.5.4