dma.h

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 // A DMA class. This should prove extremely useful, and frees you from the
00015 // irritating task of having to count quad word counts which should mean your
00016 // crash count goes down a lot.
00017 
00018 // There are two types of DMA chains here. A static DMA chain, and a dynamic one.
00019 // The static one has data added to it at initialisation time and is never changed 
00020 // again. The dynamic one is built every frame and is double buffered so that the
00021 // next frame's DMA packet can be built while the current frame's one is being
00022 // processed. This means that we no longer need to send the packet, and wait right away.
00023 // We can instead send the packet, build the new one, and then wait.
00024 // This is exploiting parallelism and will make everything much faster, especially
00025 // when you start doing non-trivial things. A key to speed on the PS2 is to only add
00026 // what you need per frame, so any data that won't change per frame should go into
00027 // the static DMA chain, and then be called from the dynamic chain using a DMA call tag.
00028 
00029 #ifndef __DMA_H__
00030 #define __DMA_H__
00031 
00032 #include "sps2wrap.h"
00033 #include "singleton.h"
00034 #include "ps2matrix4x4.h"
00035 #include "ps2vector4.h"
00036 #include "PS2Defines.h"
00037 
00038 // These macros allow you to treat VIFStaticDMA and
00039 // VIFDynamicDMA as if they were global variables. Much easier!
00040 #define VIFStaticDMA CVIFStaticDMA::GetSingleton()
00041 #define VIFDynamicDMA CVIFDynamicDMA::GetSingleton()
00042 
00043 // A base class for the static and dynamic chains. This class is never
00044 // used alone, it just holds all of the behaviour that is common to both the other classes.
00045 class CDMAChain
00046 {
00047         public:
00048                 // Constructor
00049                 CDMAChain()
00050                 {
00051                         // Zero the array that will hold all of the SPS2 memory pages
00052                         m_pPages = 0;
00053                 }
00054 
00055                 // Destructor
00056                 virtual ~CDMAChain()
00057                 {
00058                         // Delete the array that holds the SPS2 memory pages (note we aren't
00059                         // freeing up the SPS2 memory, just this array)
00060                         if(m_pPages)
00061                         {
00062                                 delete [] m_pPages;
00063                                 m_pPages = 0;
00064                         }
00065                 }
00066 
00067                 void Begin();                           // Starts a new DMA chain
00068 
00069                 // This is the most important function of the whole class. It adds
00070                 // 32bits (an int worth) of data to the chain, and handles stitching
00071                 // automatically. All of the other add-data functions use this.
00072                 void Add32(uint32 data)
00073                 {
00074                         if(m_pPtr >= m_pEndPtr) // Would we be writing to the new page?
00075                         {
00076                                 Stitch();                       // If so then stitch
00077                         }
00078 
00079                         *m_pPtr++ = data;               // Write the data and then move the pointer on
00080                 }
00081 
00082                 // These are simply to make life easier, as you can see this function just calls
00083                 // Add32 twice.
00084                 void Add64(uint64 data)
00085                 { Add32((uint32)data);  Add32((uint32)(data >> 32)); }
00086 
00087                 void Add128(uint128 data)
00088                 { Add64((uint64)data);  Add64((uint64)(data >> 64)); }
00089 
00090                 void AddFloat(float data)
00091                 { Add32(*(uint32 *)&data); }
00092 
00093                 void AddVectorI(int x, int y, int z, int w);
00094                 void AddVectorF(float x, float y, float z, float w);
00095 
00096                 void AddVector(const Vector4 & Vector)
00097                 {
00098                         AddFloat(Vector.x);
00099                         AddFloat(Vector.y);
00100                         AddFloat(Vector.z);
00101                         AddFloat(Vector.w);
00102                 }
00103 
00104                 void AddMatrix(const Matrix4x4 & Matrix);
00105 
00106                 void Align(int iAlign, int iOffset = 0);        // Aligns the write pointer to a mulitple of iAlign * 32 bits
00107                                                                         // The value can be offset by iOffset * 32 bits
00108 
00109                 void PrepForDMATag();           // Makes sure that we are writing a multiple of 1 quadword (pad with No Operations
00110                                                                         // if we aren't) and also makes sure that the next tag isn't on a stitching boundary
00111 
00112                 // Functions to add different DMA Tags (note that the class handles all of the QWC stuff, so you
00113                 // don't need to worry!)
00114                 void DMAEnd();
00115                 // Use this function at the end of any static blocks to tell the DMAC to return to the dynamic buffer just
00116                 // after the DMACall tag.
00117                 void DMARet();
00118                 // Use this to call little chunks inside the static DMA buffer. Use the physical address
00119                 // returned by CVIFStaticDMA::GetPointer()
00120                 void DMACall(uint32 iAddr);
00121 
00122                 // Start a section of data that should be sent directly to the GS (i.e. GIF path 2)
00123                 // These functions can go over a stitching boundary, so there is no need to worry about that.
00124                 // They also handle calculating the QWC for the DIRECT VIFCode and will automatically align
00125                 // to the last word of a quad if needed too!
00126                 void StartDirect();
00127                 void EndDirect();
00128 
00129                 // Use these functions to add data that will used in A+D mode, and these functions
00130                 // will calculate the QWC for you. (You must be in DIRECT mode to use these)
00131                 void StartAD();
00132                 void EndAD(){};
00133                 void AddAD(uint64 iData, uint64 iAddr);
00134 
00135                 // Use these functions to transfer microcode across to VU1 via the DMAC
00136                 void StartMPG(uint32 iAddr);
00137                 void EndMPG();
00138                 void AddMPG(uint64 iInst);
00139 
00140                 // Add a VIF unpack code. You must know the number of quadwords you are adding for this function.
00141                 void AddUnpack(int format, int addr, int num, int usetops = 0, int nosign = 1, int masking = 0)
00142                 {
00143                         Add32((0x60 << 24) + (format << 24) + (masking << 28) + (usetops << 15) +
00144                                 (nosign << 14) + (num << 16) + addr);
00145                 }
00146 
00147                 // A nice function for debugging that will print the contents of the packet
00148                 // out via printf
00149                 virtual void PrintPacket();
00150                 
00151         protected:
00152                 virtual int NewPage() = 0;      // Starts writing to a new memory page
00153                 virtual void Stitch();          // Stitches across the page boundary with a next tag
00154                 virtual void NewTag();          // Reserves space in the chain for a DMA tag
00155                 
00156                 int m_iNumPages;                        // How many pages we have allocated for DMAing
00157                 CDMAMem * m_pPages;                     // All the pages that we have allocated
00158 
00159                 int * m_pPtr;                           // The data write pointer
00160                 int * m_pEndPtr;                        // The next page starts here
00161                 int m_iPhysAddr;                        // The physical address of the first page (for TADR)
00162 
00163                 uint64 * m_pDMATag;                     // The currently open DMA tag
00164                 int * m_pVIFCode;                       // The currently open VIF code
00165 
00166                 int m_iMPGCount;                        // How many MPG instructions we have written
00167                 int m_iMPGAddr;                         // The VU_MICRO_MEM address we are writing to
00168 
00169                 uint64 * m_pADGiftag;           // The currently open AD GIFtag.
00170                 
00171                 typedef enum
00172                 {
00173                         VIF_BASE,
00174                         VIF_DIRECT                              // Direct + TTE has alignment issues, MPG, and unpack do not
00175                 } VIF1_STATE;                           // The different states the DMA chain can be in
00176 
00177                 VIF1_STATE m_VifState;          // The VIF state at this point in the chain
00178 };
00179 
00180 // The static DMA chain. You should use this by first getting the physical address via GetPointer
00181 // You should then save this address to use with a DMACall tag in the dynamic chain. Then add your
00182 // data in the standard CDMAChain way. Finally add a DMARet tag to the static DMA chain so that
00183 // the DMAC will return control to the dynamic chain.
00184 class CVIFStaticDMA : public CDMAChain, public CSingleton<CVIFStaticDMA>
00185 {
00186         public:
00187                 CVIFStaticDMA();
00188                 virtual ~CVIFStaticDMA();
00189 
00190                 void Initialise(int iNumPages);
00191 
00192                 int GetPointer();                       // Retrieve the current pointer's physical address
00193 
00194         protected:
00195                 virtual int NewPage();
00196 
00197                 int m_iCurrPage;
00198                 int m_iPhysicalOffset;
00199 };
00200 
00201 inline int CVIFStaticDMA::GetPointer()
00202 {
00203         return (int)m_pDMATag + m_iPhysicalOffset;
00204 }
00205 
00206 // The dynamic DMA chain is the only DMA chain that you should fire. You should fire this at
00207 // the start of your main loop, then add all of your data to the class. That way while the previous
00208 // frame's data is being transferred, you can be building the next packet in parallel.
00209 // This DMA chain is double buffered to allow this, so you should remember that when you call
00210 // Initialise, this class will actually use double that, so make sure to allocate enough memory!
00211 class CVIFDynamicDMA : public CDMAChain, public CSingleton<CVIFDynamicDMA>
00212 {
00213         public:
00214                 CVIFDynamicDMA();
00215                 virtual ~CVIFDynamicDMA();
00216 
00217                 // See comment above
00218                 void Initialise(int iNumPages);
00219                 // This fires the packet via path 1. It takes two parameters, one that allows you
00220                 // to build but not actually fire the packet (good for debugging), and the second which
00221                 // will print the packet out via printf.
00222                 void Fire(bool bFire = true, bool bPrint = false);
00223 
00224                 // Prints the packet via printf
00225                 virtual void PrintPacket();
00226 
00227                 // Retrieve a temporary piece of SPS2 memory. Note that this returns the
00228                 // First page from the buffer that is currently being sent, so don't call this
00229                 // Directly after calling Fire or you could experience problems.
00230                 CDMAMem & getTempPage(int iPage = 0)
00231                 {
00232                         return m_pPages[((m_iCurrBuffer ^ 1) * m_iNumPages) + iPage];
00233                 }
00234 
00235         protected:
00236                 virtual int NewPage();
00237                 
00238                 int m_iCurrPage;        // The current page we are writing to.
00239                 int m_iCurrBuffer;      // The current buffer we are writing to (0 or 1)
00240 };
00241 
00242 #endif

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