sps2wrap.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:12 $
00011 *
00012 */
00013 
00014 // A nice manager to wrap up all the SPS2 memory handling etc. into a nice easy to use
00015 // class.
00016 #include "sps2wrap.h"
00017 #include "PS2Defines.h"
00018 #include "dma.h"
00019 
00020 // We have to create this to use the singleton. (it calls the constructor
00021 // which sets up the ms_Singleton member).
00022 CSPS2Manager SPS2Manager_SingletonInit;
00023 
00024 static int iScreenClear = 0;
00025 
00026 // Constructor
00027 CSPS2Manager::CSPS2Manager()
00028 {
00029         // Write the disk cache to the HDD for safety
00030         printf("Writing cache to HDD\n");
00031         system("sync");
00032         
00033         // The array to keep track of how much memory is allocated in each page
00034         m_iaFreeMemInPage = 0;
00035 }
00036 
00037 // Destructor
00038 CSPS2Manager::~CSPS2Manager()
00039 {
00040         sps2UScreenShutdown();
00041         sps2Release(m_iSPS2Desc);
00042 
00043         // Free up all of the memory that was allocated in Initialise
00044         if(m_iaFreeMemInPage)
00045         {
00046                 delete [] m_iaFreeMemInPage;
00047                 m_iaFreeMemInPage = 0;
00048         }
00049 }
00050 
00051 // Set up SPS2, and allocate all of the SPS2 memory that will be
00052 // used
00053 void CSPS2Manager::Initialise(const int iAllocPages)
00054 {
00055         // Keep track of how many 4K pages will be allocated
00056         m_iNumPages = iAllocPages;
00057 
00058         // Initialise SPS2
00059         m_iSPS2Desc = sps2Init();
00060 
00061         if(m_iSPS2Desc < 0)
00062         {
00063                 fprintf(stderr, "Failed to initialise SPS2 library." \
00064                                 "Please check that the module is loaded.\n");
00065                 exit(-1);
00066         }
00067 
00068         // Open the vector units
00069         int iVPU0; iVPU0 = open("/dev/ps2vpu0", O_RDWR);
00070         int iVPU1; iVPU1 = open("/dev/ps2vpu1", O_RDWR);
00071 
00072         // Allocate the memory that we will be using, and get the cached and
00073         // cached pointers.
00074         m_pMemCached = sps2Allocate(iAllocPages * 4096,
00075                         SPS2_MAP_BLOCK_4K | SPS2_MAP_CACHED, m_iSPS2Desc);
00076         m_pMemUncached = sps2Remap(m_pMemCached, SPS2_MAP_UNCACHED, m_iSPS2Desc);
00077 
00078         // Set up an array to keep track of how much memory is free in each page
00079         m_iaFreeMemInPage = new int[m_iNumPages];
00080 
00081         for(int iPage = 0; iPage < m_iNumPages; iPage++)
00082         {
00083                 m_iaFreeMemInPage[iPage] = 256; // 4K == 256 Quad words
00084         }
00085 
00086         // Set up the screen
00087         sps2UScreenInit(0);
00088         
00089         // Enable EI DI instructions (this is needed for the screen shot code)
00090         _sps2SetEIDIEnabled(1, m_iSPS2Desc);
00091 }
00092 
00093 // Allocate some of the memory that we created in Initialise.
00094 void CSPS2Manager::Allocate(CDMAMem & Memory, int iQuadWords)
00095 {
00096         assert(iQuadWords <= 256 && "A CDMAMem class can only handle a maximum of 256 quadwords");
00097 
00098         for(int iPage = 0; iPage < m_iNumPages; iPage++)
00099         {
00100                 // Find the first page with enough free memory in it.
00101                 if(m_iaFreeMemInPage[iPage] >= iQuadWords)
00102                 {
00103                         // Set up the CDMAMem structure.
00104                         // Get the first free quad word in the page.
00105                         int FirstQW = 256 - m_iaFreeMemInPage[iPage];
00106                         m_iaFreeMemInPage[iPage] -= iQuadWords;
00107                         // Get the pointers.
00108                         void * pCached = &((char *)m_pMemCached->pvStart)[iPage * 4096 + (FirstQW * 16)];
00109                         void * pUncached = &((char *)m_pMemUncached->pvStart)[iPage * 4096 + (FirstQW * 16)];
00110                         // Set the CDMAMem's member variables
00111                         Memory.Set(iQuadWords, pCached, pUncached, sps2GetPhysicalAddress(pCached, m_pMemCached));
00112                         return; // No need to search anymore
00113                 }
00114         }
00115 
00116         assert(!"Not enough free memory!");
00117 }
00118 
00119 // Initialise the screen clear. With this class we clear the screen much faster than SPS2 would
00120 // usually. This puts all of the screen clear DMA data into our static DMA buffer. This means it is
00121 // only ever added once (very fast), and is then simply CALLed from the dynamic DMA buffer.
00122 // Notice that instead of just using one big sprite to clear the screen we use 20 long tall strips.
00123 // This is because the GS is optimised for polygons less than or equal to 32 pixels wide, and it
00124 // "chokes" on ones that are larger.
00125 void CSPS2Manager::InitScreenClear(int R, int G, int B)
00126 {
00127         int x0 = (2048 - (sps2UScreenGetWidth() >> 1)) << 4;
00128         int y0 = (2048 - (sps2UScreenGetHeight() >> 1)) << 4;
00129         int x1 = (2048 + (sps2UScreenGetWidth() >> 1)) << 4;
00130         int y1 = (2048 + (sps2UScreenGetHeight() >> 1)) << 4;
00131 
00132         // Get the address of the screen clear packet so we can CALL it
00133         // from the dynamic packet.
00134         iScreenClear = VIFStaticDMA.GetPointer();
00135 
00136         // Start the VIF direct mode.
00137         VIFStaticDMA.StartDirect();
00138 
00139         VIFStaticDMA.Add128(GS_GIFTAG_BATCH(4 + (20 * 2), 1, 0, 0, 
00140                                                 GIF_FLG_PACKED, GS_BATCH_1(GIF_REG_A_D)));
00141 
00142         VIFStaticDMA.Add64(TEST_SET(0, 0, 0, 0, 0, 0, 1, 1));
00143         VIFStaticDMA.Add64(TEST_1);
00144 
00145         VIFStaticDMA.Add64(PRIM_SET(0x6, 0, 0, 0, 0, 0, 0, 0, 0));
00146         VIFStaticDMA.Add64(PRIM);
00147 
00148         VIFStaticDMA.Add64(RGBAQ_SET(R, G, B, 0x80, 0x3f800000));
00149         VIFStaticDMA.Add64(RGBAQ);
00150 
00151         for(int i = 0; i < 20; i++)
00152         {
00153                 VIFStaticDMA.Add64(XYZ2_SET(x0, y0, 0));
00154                 VIFStaticDMA.Add64(XYZ2);
00155 
00156                 VIFStaticDMA.Add64(XYZ2_SET(x0 + (32 << 4), y1, 0));
00157                 VIFStaticDMA.Add64(XYZ2);
00158 
00159                 x0 += (32 << 4);
00160         }
00161 
00162         VIFStaticDMA.Add64(TEST_SET(0, 0, 0, 0, 0, 0, 1, 3));
00163         VIFStaticDMA.Add64(TEST_1);
00164 
00165         VIFStaticDMA.EndDirect();
00166 
00167         VIFStaticDMA.DMARet();
00168 }
00169 
00170 // This just DMACall's the screen clear packet that was set up in InitScreenClear
00171 // (I'm sure you can see why this is so much faster than the sps2 screen clear, as the
00172 //  CPU hardly has to do any work at all here).
00173 void CSPS2Manager::BeginScene()
00174 {
00175         VIFDynamicDMA.DMACall(iScreenClear);
00176 }
00177 
00178 
00179 int CSPS2Manager::FileExists(const char * const FileName)
00180 {
00181         FILE * f;
00182         f = fopen(FileName, "rb");      // Try to open the file
00183         if (f == NULL) return 0;        // File doesn't exist
00184         fclose (f);
00185         return 1;                                       // File exists
00186 }       
00187 
00188 void CSPS2Manager::ScreenShot(const int ImageType)
00189 {
00190         char name[256];
00191         unsigned char * data;
00192         unsigned char tmp;
00193         unsigned short Width, Height;
00194         int BytesPerPixel = 4;
00195         
00196         // Current Screen width and height
00197         Width = (unsigned short)GetScreenWidth();
00198         Height = (unsigned short)GetScreenHeight();
00199         
00200         // Get an unused filename
00201         do
00202         {
00203                 sprintf (name, "sshot_%02d.tga", m_SS_Number++);
00204         } while (FileExists(name));
00205         
00206         // Print the filename to the console with some stats
00207         if(ImageType == IMAGE_RGB)
00208                 printf("Dumping Screen to File %s. Dimensions %d x %d - RGB\n", name, Width, Height);
00209         else if(ImageType == IMAGE_RGBA)
00210                 printf("Dumping Screen to File %s. Dimensions %d x %d - RGBA\n", name, Width, Height);
00211         
00212         
00213         // Open the file for writing to 
00214         FILE * f = fopen(name, "wb");
00215 
00216         // Create a targa header for a 640x480, 32bit picture
00217         unsigned char header[18] = {
00218                 0,                              // Field 1 - Number of Bytes in Field 6
00219                 0,                              // Field 2 - Colour Map Type 0 = no colour map
00220                 2,                              // Field 3 - Image Type = Uncompressed true colour image
00221                 0, 0, 0, 0, 0,  // Field 4 - Colour map specification
00222                                                 // Field 5 - The remaining 10 bytes
00223                 0, 0,                   // X origin
00224                 0, 0,                   // Y origin
00225                 0x80, 2,                // Width 640
00226                 0xe0, 1,                // Height 480
00227                 32,                     // Pixel colour depth
00228                 8                               // Number of alpha channel bits
00229         };
00230         
00231         //for(int i =0; i< 18; i++)printf("%x ", header[i]);printf("\n");
00232                 
00233         //Change the width and height in the header to the correct dimensions
00234         unsigned char * pDimension = (unsigned char *)(&Width);
00235         //printf("%x %x\n", pDimension[0], pDimension[1]);
00236         header[12] = pDimension[0];
00237         header[13] = pDimension[1];
00238         
00239         pDimension = (unsigned char *)(&Height);
00240         header[14] = pDimension[0];
00241         header[15] = pDimension[1];
00242         
00243         // Change header and bytes per pixel if RGB
00244         if(ImageType == IMAGE_RGB)
00245         {
00246                 header[16] = 24;
00247                 header[17] = 0;
00248                 BytesPerPixel = 3;
00249         }
00250         
00251         
00252         //for(int i =0; i< 18; i++)printf("%x ", header[i]);printf("\n");
00253         
00254         // write the header to the file 
00255         fwrite(header, 18, 1, f);
00256         
00257         // Get two pages of memory to work with
00258         // one page for the DMA chain, 
00259         CDMAMem DMAChainMem = VIFDynamicDMA.getTempPage(0);
00260         // One page to hold the frame buffer data
00261         CDMAMem SShotMem = VIFDynamicDMA.getTempPage(1);
00262         
00263         // Now Download the Frame buffer, from the bottom up
00264         for(int y = 0; y < Height; y++)
00265         {
00266                 DownloadVram(DMAChainMem, SShotMem, 0, 10, 0, (Height - 1 - y), Width, 1);
00267                 
00268                 // convert RGBA to BGRA
00269                 data = (unsigned char *)SShotMem.GetUncached();
00270                 for(int x = 0; x < Width; x++)
00271                 {
00272                         tmp = data[0];
00273                         data[0] = data[2];
00274                         data[2] = tmp;
00275                         
00276                         // Write pixel to file
00277                         fwrite(data, BytesPerPixel, 1, f);
00278                         
00279                         data += 4;
00280                 }
00281         }
00282         
00283         // Close the file
00284         fclose(f);
00285 }
00286 
00287 void CSPS2Manager::DownloadVram(CDMAMem & DMAChainMem, CDMAMem & SShotMem, 
00288                                                                 int addr, int bufw, int x, int y, int w, int h)
00289 {
00290         // Pointer the memory for building the DMA chain
00291         uint32 * pDmaMem32 = (uint32 *)DMAChainMem.GetUncached();       
00292 
00293         uint32 uSrcPtr = addr;
00294         uint32 uQSize = (w*h*4)/16; // assuming 32bit framebuffer
00295 
00296         // Setup transfer texture back to memory
00297         pDmaMem32[0] = 0; //NOP;
00298         pDmaMem32[1] = 0x06008000; //MSKPATH3(0x8000);
00299         pDmaMem32[2] = 0x13000000; //FLUSHA;
00300         pDmaMem32[3] = 0x50000006; //DIRECT(6);
00301 
00302         // Add GifTag Data
00303         // Get pointer to GIFTag position
00304         uint128 * pGIFTag = (uint128 *)(pDmaMem32 + 4);
00305         
00306         // Store the GIFTag - The Giftag for A+D mode
00307         *pGIFTag = GS_GIFTAG_BATCH(     5,                                                      // NLOOP
00308                                                                 1,                                                      // EOP
00309                                                                 0,                                                      // PRE
00310                                                                 0,                                                      // PRIM                         
00311                                                                 GIF_FLG_PACKED,                         // FLG
00312                                                                 GS_BATCH_1(     GIF_REG_A_D));  // BATCH
00313          
00314         // Get uint64 pointer to write the A_D data
00315         // [0] and [1] are the GIFTag                                                                   
00316         uint64 * pDmaMem64 = (uint64 *) (pGIFTag);
00317 
00318         pDmaMem64[2] = (BITBLTBUF_SET( uSrcPtr, bufw, 0, 0, 0, 0 ));
00319         pDmaMem64[3] = (GIF_A_D_REG_BITBLTBUF);
00320 
00321         pDmaMem64[4] = (TRXPOS_SET(x, y, 0, 0, 0)); // SSAX, SSAY, DSAX, DSAY, DIR
00322         pDmaMem64[5] = (GIF_A_D_REG_TRXPOS);
00323 
00324         pDmaMem64[6] = (TRXREG_SET(w, h)); // RRW, RRh
00325         pDmaMem64[7] = (GIF_A_D_REG_TRXREG);
00326 
00327         pDmaMem64[8] = (0);
00328         pDmaMem64[9] = (GIF_A_D_REG_FINISH);
00329 
00330         pDmaMem64[10] = (TRXDIR_SET(1)); // XDIR
00331         pDmaMem64[11] = (GIF_A_D_REG_TRXDIR);
00332 
00333         uint32 prev_chcr = *EE_D1_CHCR;
00334         
00335         // *_GS_IMR fetches the current setting of interrupt mask register (imr)
00336         // DPUT_GS_IMR() sets the current value of imr
00337         // Mark interrupts from the FINISH Event (GS Page 154)
00338         uint32 prev_imr = *_GS_IMR;
00339         DPUT_GS_IMR( prev_imr | 0x0200 );
00340         
00341         // vif1 must be available
00342         if ((*EE_D1_CHCR & 0x0100) != 0)
00343         {
00344                 printf("VIF1 is not available for the Screen Shot\n");
00345                 exit(-1);
00346         }       
00347         
00348         // Enable generation of the FINISH Event (GS Page 145)
00349         DPUT_GS_CSR( 2 ); 
00350         
00351         // Disable interrupts so we don't lose our finish event
00352         asm __volatile__ ("di");
00353 
00354         
00355         // Make sure all cache is writen to main memory
00356         FlushCache();
00357         
00358         // Setup and start DMA transfer to VIF1
00359         *EE_D1_QWC = 0x7;
00360         *EE_D1_MADR = DMAChainMem.GetPhysicalAddr();
00361         //             DIR      MOD       ASP     TTE       TIE      STR
00362         *EE_D1_CHCR = (1<<0) | (0<<2) | (0<<4) | (0<<6) | (0<<7) | (1<<8);
00363 
00364         // Wait till memory load is complete
00365         asm __volatile__( " sync.l " );
00366         
00367         // Wait till DMA is complete (STR=0)
00368         while ( *EE_D1_CHCR & 0x0100 );
00369 
00370         // Wait for the GS to finish loading the registers (FINISH EVENT)
00371         //while ( ((*GS_CSR_OFF(SPS2_GS_REGISTERS_START)) & 2) == 0 ); 
00372         while ( (*_GS_CSR & 2) == 0 );
00373         
00374         // Re-enable interrupts
00375         asm __volatile__ ("ei");
00376         
00377         
00378         // START! The actual download to memory 
00379         // wait for VIF1-FIFO to empty and become idle 
00380         while(*EE_VIF1_STAT & 0x1f000003); 
00381         
00382         // Change the VIF1 direction to VIF1->Memory
00383         *((volatile uint32 *) EE_VIF1_STAT) = (1<<23);
00384         
00385         // Change direction of VIF1 FIF0 Local->Host
00386         DPUT_GS_BUSDIR( ( uint64 ) 0x00000001 );
00387         
00388         // Setup and start DMA transfer from VIF1
00389         *EE_D1_QWC = uQSize;
00390         *EE_D1_MADR = SShotMem.GetPhysicalAddr();
00391         //          DIR    MOD       ASP     TTE       TIE      STR
00392         *EE_D1_CHCR = 0 | (0<<2) | (0<<4) | (0<<6) | (0<<7) | (1<<8);
00393 
00394         // Wait till memory load is complete
00395         asm __volatile__( " sync.l " );
00396 
00397         // check if DMA is complete (STR=0)
00398         while ( *EE_D1_CHCR & 0x0100 );
00399         
00400         // Restore CHCR and wait for it to complete
00401         *EE_D1_CHCR = prev_chcr;
00402         asm __volatile__( " sync.l " );
00403         
00404         // wait for VIF1-FIFO to empty and become idle 
00405         while ( *EE_VIF1_STAT & 0x1f000003 ); 
00406 
00407         
00408         // Change the VIF1 and VIF1 FIFO Direction back to normal
00409         *((volatile uint32 *) EE_VIF1_STAT) = 0;
00410         DPUT_GS_BUSDIR( ( uint64 ) 0 );
00411         
00412         
00413         // restore the setting of IMR
00414         DPUT_GS_IMR( prev_imr);
00415         // set the FINISH event to default
00416         //dput_gs_csr( GS_EE_CSR_FINISH_M );
00417         DPUT_GS_CSR( 2 ); 
00418         
00419         
00420         
00421         // Re-enable path3 transfers
00422         static uint32 enable_path3[4] ALIGN_QW = {
00423         0x06000000, //MSKPATH3(0),
00424         0,
00425         0,
00426         0};
00427 
00428         DPUT_EE_VIF1_FIFO( *( uint128 * ) enable_path3 );
00429 
00430         // Make sure data is written to main memory
00431         FlushCache();
00432 }
00433 

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