/*************************************************************************** * Copyright (C) 2010 PCSX4ALL Team * * Copyright (C) 2010 Unai * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1307 USA. * ***************************************************************************/ #include "port.h" #include "gpu.h" #include "profiler.h" #include "debug.h" int skipCount = 2; /* frame skip (0,1,2,3...) */ int skCount=0; /* internal frame skip */ int linesInterlace = 0; /* internal lines interlace */ int linesInterlace_user = 0; /* Lines interlace */ bool isSkip=false; /* skip frame (info coming from GPU) */ bool skipFrame=false; /* skip frame (according to frame skip) */ bool alt_fps = false; /* Alternative FPS algorithm */ bool show_fps = false; /* Show FPS statistics */ bool isPAL=false; /* PAL video timing */ bool progressInterlace_flag = false; /* Progressive interlace flag */ bool progressInterlace = false; /* Progressive interlace option*/ bool frameLimit = false; /* frames to wait */ bool light = true; /* lighting */ bool blend = true; /* blending */ bool fb_dirty = false; bool enableAbbeyHack = false; /* Abe's Odyssey hack */ u8 BLEND_MODE; u8 TEXT_MODE; u8 Masking; u16 PixelMSB; u16 PixelData; /////////////////////////////////////////////////////////////////////////////// // GPU Global data /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // Dma Transfers info s32 px,py; s32 x_end,y_end; u16* pvram; u32 FrameToRead; u32 FrameToWrite; u32 GP0; s32 PacketCount; s32 PacketIndex; /////////////////////////////////////////////////////////////////////////////// // Display status u32 DisplayArea [6]; /////////////////////////////////////////////////////////////////////////////// // Rasterizer status u32 TextureWindow [4]; u32 DrawingArea [4]; u32 DrawingOffset [2]; /////////////////////////////////////////////////////////////////////////////// // Rasterizer status u16* TBA; u16* CBA; /////////////////////////////////////////////////////////////////////////////// // Inner Loops s32 u4, du4; s32 v4, dv4; s32 r4, dr4; s32 g4, dg4; s32 b4, db4; u32 lInc; u32 tInc, tMsk; GPUPacket PacketBuffer; // FRAME_BUFFER_SIZE is defined in bytes; 512K is guard memory for out of range reads u16 GPU_FrameBuffer[(FRAME_BUFFER_SIZE+512*1024)/2] __attribute__((aligned(16))); u32 GPU_GP1; /////////////////////////////////////////////////////////////////////////////// // Inner loop driver instanciation file #include "gpu_inner.h" /////////////////////////////////////////////////////////////////////////////// // GPU Raster Macros #define GPU_RGB16(rgb) ((((rgb)&0xF80000)>>9)|(((rgb)&0xF800)>>6)|(((rgb)&0xF8)>>3)) #define GPU_EXPANDSIGN_POLY(x) (((s32)(x)<<20)>>20) //#define GPU_EXPANDSIGN_POLY(x) (((s32)(x)<<21)>>21) #define GPU_EXPANDSIGN_SPRT(x) (((s32)(x)<<21)>>21) //#define GPU_TESTRANGE(x) { if((u32)(x+1024) > 2047) return; } #define GPU_TESTRANGE(x) { if ((x<-1023) || (x>1023)) return; } #define GPU_SWAP(a,b,t) {(t)=(a);(a)=(b);(b)=(t);} /////////////////////////////////////////////////////////////////////////////// // GPU internal image drawing functions #include "gpu_raster_image.h" /////////////////////////////////////////////////////////////////////////////// // GPU internal line drawing functions #include "gpu_raster_line.h" /////////////////////////////////////////////////////////////////////////////// // GPU internal polygon drawing functions #include "gpu_raster_polygon.h" /////////////////////////////////////////////////////////////////////////////// // GPU internal sprite drawing functions #include "gpu_raster_sprite.h" /////////////////////////////////////////////////////////////////////////////// // GPU command buffer execution/store #include "gpu_command.h" /////////////////////////////////////////////////////////////////////////////// INLINE void gpuReset(void) { GPU_GP1 = 0x14802000; TextureWindow[0] = 0; TextureWindow[1] = 0; TextureWindow[2] = 255; TextureWindow[3] = 255; DrawingArea[2] = 256; DrawingArea[3] = 240; DisplayArea[2] = 256; DisplayArea[3] = 240; DisplayArea[5] = 240; } /////////////////////////////////////////////////////////////////////////////// bool GPU_init(void) { gpuReset(); // s_invTable for(int i=1;i<=(1<>1); #else v *= double(0x80000000); #endif s_invTable[i-1]=s32(v); } return (0); } /////////////////////////////////////////////////////////////////////////////// void GPU_shutdown(void) { } /////////////////////////////////////////////////////////////////////////////// long GPU_freeze(unsigned int bWrite, GPUFreeze_t* p2) { if (!p2) return (0); if (p2->Version != 1) return (0); if (bWrite) { p2->GPU_gp1 = GPU_GP1; memset(p2->Control, 0, sizeof(p2->Control)); p2->Control[5] = DisplayArea[0] | (DisplayArea[1] << 10); p2->Control[7] = DisplayArea[4] | (DisplayArea[5] << 10); p2->Control[8] = ((GPU_GP1 >> 17) & 0x3f) | ((GPU_GP1 >> 10) & 0x40); memcpy(p2->FrameBuffer, (u16*)GPU_FrameBuffer, FRAME_BUFFER_SIZE); return (1); } else { GPU_GP1 = p2->GPU_gp1; memcpy((u16*)GPU_FrameBuffer, p2->FrameBuffer, FRAME_BUFFER_SIZE); GPU_writeStatus((5 << 24) | p2->Control[5]); GPU_writeStatus((7 << 24) | p2->Control[7]); GPU_writeStatus((8 << 24) | p2->Control[8]); gpuSetTexture(GPU_GP1); return (1); } return (0); } /////////////////////////////////////////////////////////////////////////////// // GPU DMA comunication /////////////////////////////////////////////////////////////////////////////// u8 PacketSize[256] = { 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0-15 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16-31 3, 3, 3, 3, 6, 6, 6, 6, 4, 4, 4, 4, 8, 8, 8, 8, // 32-47 5, 5, 5, 5, 8, 8, 8, 8, 7, 7, 7, 7, 11, 11, 11, 11, // 48-63 2, 2, 2, 2, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, // 64-79 3, 3, 3, 3, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, // 80-95 2, 2, 2, 2, 3, 3, 3, 3, 1, 1, 1, 1, 2, 2, 2, 2, // 96-111 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 2, 2, 2, 2, // 112-127 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 128- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 144 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 160 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // }; /////////////////////////////////////////////////////////////////////////////// INLINE void gpuSendPacket() { #ifdef DEBUG_ANALYSIS dbg_anacnt_GPU_sendPacket++; #endif gpuSendPacketFunction(PacketBuffer.U4[0]>>24); } /////////////////////////////////////////////////////////////////////////////// INLINE void gpuCheckPacket(u32 uData) { if (PacketCount) { PacketBuffer.U4[PacketIndex++] = uData; --PacketCount; } else { PacketBuffer.U4[0] = uData; PacketCount = PacketSize[uData >> 24]; PacketIndex = 1; } if (!PacketCount) gpuSendPacket(); } /////////////////////////////////////////////////////////////////////////////// void GPU_writeDataMem(u32* dmaAddress, s32 dmaCount) { #ifdef DEBUG_ANALYSIS dbg_anacnt_GPU_writeDataMem++; #endif pcsx4all_prof_pause(PCSX4ALL_PROF_CPU); pcsx4all_prof_start_with_pause(PCSX4ALL_PROF_GPU,PCSX4ALL_PROF_HW_WRITE); u32 data; const u16 *VIDEO_END=(GPU_FrameBuffer+(FRAME_BUFFER_SIZE/2)-1); GPU_GP1 &= ~0x14000000; while (dmaCount) { if (FrameToWrite) { while (dmaCount--) { data = *dmaAddress++; if ((&pvram[px])>(VIDEO_END)) pvram-=512*1024; pvram[px] = data; if (++px>=x_end) { px = 0; pvram += 1024; if (++py>=y_end) { FrameToWrite = 0; GPU_GP1 &= ~0x08000000; break; } } if ((&pvram[px])>(VIDEO_END)) pvram-=512*1024; pvram[px] = data>>16; if (++px>=x_end) { px = 0; pvram += 1024; if (++py>=y_end) { FrameToWrite = 0; GPU_GP1 &= ~0x08000000; break; } } } } else { data = *dmaAddress++; dmaCount--; gpuCheckPacket(data); } } GPU_GP1 = (GPU_GP1 | 0x14000000) & ~0x60000000; fb_dirty = true; pcsx4all_prof_end_with_resume(PCSX4ALL_PROF_GPU,PCSX4ALL_PROF_HW_WRITE); pcsx4all_prof_resume(PCSX4ALL_PROF_CPU); } u32 *lUsedAddr[3]; INLINE int CheckForEndlessLoop(u32 *laddr) { if(laddr==lUsedAddr[1]) return 1; if(laddr==lUsedAddr[2]) return 1; if(laddr> 2)); if(DMACommandCounter++ > 2000000) break; if(CheckForEndlessLoop(address)) break; data = *address++; count = (data >> 24); offset = data & 0x001FFFFF; if (dmaVAddr != offset) dmaVAddr = offset; else dmaVAddr = 0x1FFFFF; if(count>0) GPU_writeDataMem(address,count); } GPU_GP1 = (GPU_GP1 | 0x14000000) & ~0x60000000; pcsx4all_prof_end_with_resume(PCSX4ALL_PROF_GPU,PCSX4ALL_PROF_HW_WRITE); } /////////////////////////////////////////////////////////////////////////////// void GPU_writeData(u32 data) { const u16 *VIDEO_END=(GPU_FrameBuffer+(FRAME_BUFFER_SIZE/2)-1); #ifdef DEBUG_ANALYSIS dbg_anacnt_GPU_writeData++; #endif pcsx4all_prof_pause(PCSX4ALL_PROF_CPU); pcsx4all_prof_start_with_pause(PCSX4ALL_PROF_GPU,PCSX4ALL_PROF_HW_WRITE); GPU_GP1 &= ~0x14000000; if (FrameToWrite) { if ((&pvram[px])>(VIDEO_END)) pvram-=512*1024; pvram[px]=(u16)data; if (++px>=x_end) { px = 0; pvram += 1024; if (++py>=y_end) { FrameToWrite = 0; GPU_GP1 &= ~0x08000000; } } if (FrameToWrite) { if ((&pvram[px])>(VIDEO_END)) pvram-=512*1024; pvram[px]=data>>16; if (++px>=x_end) { px = 0; pvram += 1024; if (++py>=y_end) { FrameToWrite = 0; GPU_GP1 &= ~0x08000000; } } } } else { gpuCheckPacket(data); } GPU_GP1 |= 0x14000000; fb_dirty = true; pcsx4all_prof_end_with_resume(PCSX4ALL_PROF_GPU,PCSX4ALL_PROF_HW_WRITE); pcsx4all_prof_resume(PCSX4ALL_PROF_CPU); } /////////////////////////////////////////////////////////////////////////////// void GPU_readDataMem(u32* dmaAddress, s32 dmaCount) { const u16 *VIDEO_END=(GPU_FrameBuffer+(FRAME_BUFFER_SIZE/2)-1); #ifdef DEBUG_ANALYSIS dbg_anacnt_GPU_readDataMem++; #endif if(!FrameToRead) return; pcsx4all_prof_start_with_pause(PCSX4ALL_PROF_GPU,PCSX4ALL_PROF_HW_WRITE); GPU_GP1 &= ~0x14000000; do { if ((&pvram[px])>(VIDEO_END)) pvram-=512*1024; // lower 16 bit u32 data = (unsigned long)pvram[px]; if (++px>=x_end) { px = 0; pvram += 1024; } if ((&pvram[px])>(VIDEO_END)) pvram-=512*1024; // higher 16 bit (always, even if it's an odd width) data |= (unsigned long)(pvram[px])<<16; *dmaAddress++ = data; if (++px>=x_end) { px = 0; pvram += 1024; if (++py>=y_end) { FrameToRead = 0; GPU_GP1 &= ~0x08000000; break; } } } while (--dmaCount); GPU_GP1 = (GPU_GP1 | 0x14000000) & ~0x60000000; pcsx4all_prof_end_with_resume(PCSX4ALL_PROF_GPU,PCSX4ALL_PROF_HW_WRITE); } /////////////////////////////////////////////////////////////////////////////// u32 GPU_readData(void) { const u16 *VIDEO_END=(GPU_FrameBuffer+(FRAME_BUFFER_SIZE/2)-1); #ifdef DEBUG_ANALYSIS dbg_anacnt_GPU_readData++; #endif pcsx4all_prof_pause(PCSX4ALL_PROF_CPU); pcsx4all_prof_start_with_pause(PCSX4ALL_PROF_GPU,PCSX4ALL_PROF_HW_READ); GPU_GP1 &= ~0x14000000; if (FrameToRead) { if ((&pvram[px])>(VIDEO_END)) pvram-=512*1024; GP0 = pvram[px]; if (++px>=x_end) { px = 0; pvram += 1024; if (++py>=y_end) { FrameToRead = 0; GPU_GP1 &= ~0x08000000; } } if ((&pvram[px])>(VIDEO_END)) pvram-=512*1024; GP0 |= pvram[px]<<16; if (++px>=x_end) { px = 0; pvram +=1024; if (++py>=y_end) { FrameToRead = 0; GPU_GP1 &= ~0x08000000; } } } GPU_GP1 |= 0x14000000; pcsx4all_prof_end_with_resume(PCSX4ALL_PROF_GPU,PCSX4ALL_PROF_HW_READ); pcsx4all_prof_resume(PCSX4ALL_PROF_CPU); return (GP0); } /////////////////////////////////////////////////////////////////////////////// u32 GPU_readStatus(void) { #ifdef DEBUG_ANALYSIS dbg_anacnt_GPU_readStatus++; #endif return GPU_GP1; } /////////////////////////////////////////////////////////////////////////////// void GPU_writeStatus(u32 data) { #ifdef DEBUG_ANALYSIS dbg_anacnt_GPU_writeStatus++; #endif pcsx4all_prof_pause(PCSX4ALL_PROF_CPU); pcsx4all_prof_start_with_pause(PCSX4ALL_PROF_GPU,PCSX4ALL_PROF_HW_WRITE); switch (data >> 24) { case 0x00: gpuReset(); break; case 0x01: GPU_GP1 &= ~0x08000000; PacketCount = FrameToRead = FrameToWrite = 0; break; case 0x02: GPU_GP1 &= ~0x08000000; PacketCount = FrameToRead = FrameToWrite = 0; break; case 0x03: GPU_GP1 = (GPU_GP1 & ~0x00800000) | ((data & 1) << 23); break; case 0x04: if (data == 0x04000000) PacketCount = 0; GPU_GP1 = (GPU_GP1 & ~0x60000000) | ((data & 3) << 29); break; case 0x05: DisplayArea[0] = (data & 0x000003FF); //(short)(data & 0x3ff); DisplayArea[1] = ((data & 0x0007FC00)>>10); //(data & 0x000FFC00) >> 10; //(short)((data>>10)&0x1ff); fb_dirty = true; break; case 0x07: DisplayArea[4] = data & 0x000003FF; //(short)(data & 0x3ff); DisplayArea[5] = (data & 0x000FFC00) >> 10; //(short)((data>>10) & 0x3ff); fb_dirty = true; break; case 0x08: { GPU_GP1 = (GPU_GP1 & ~0x007F0000) | ((data & 0x3F) << 17) | ((data & 0x40) << 10); static u32 HorizontalResolution[8] = { 256, 368, 320, 384, 512, 512, 640, 640 }; DisplayArea[2] = HorizontalResolution[(GPU_GP1 >> 16) & 7]; static u32 VerticalResolution[4] = { 240, 480, 256, 480 }; DisplayArea[3] = VerticalResolution[(GPU_GP1 >> 19) & 3]; isPAL = (data & 0x08) ? true : false; // if 1 - PAL mode, else NTSC } fb_dirty = true; break; case 0x10: switch (data & 0xffff) { case 0: case 1: case 3: GP0 = (DrawingArea[1] << 10) | DrawingArea[0]; break; case 4: GP0 = ((DrawingArea[3]-1) << 10) | (DrawingArea[2]-1); break; case 6: case 5: GP0 = (DrawingOffset[1] << 11) | DrawingOffset[0]; break; case 7: GP0 = 2; break; default: GP0 = 0; } break; } pcsx4all_prof_end_with_resume(PCSX4ALL_PROF_GPU,PCSX4ALL_PROF_HW_WRITE); pcsx4all_prof_resume(PCSX4ALL_PROF_CPU); } #ifndef REARMED // Blitting functions #include "gpu_blit.h" INLINE void gpuVideoOutput(void) { static s16 old_res_horz, old_res_vert, old_rgb24; s16 h0, x0, y0, w0, h1; x0 = DisplayArea[0]; y0 = DisplayArea[1]; w0 = DisplayArea[2]; h0 = DisplayArea[3]; // video mode h1 = DisplayArea[5] - DisplayArea[4]; // display needed if (h0 == 480) h1 = Min2(h1*2,480); u16* dest_screen16 = SCREEN; u16* src_screen16 = &((u16*)GPU_FrameBuffer)[FRAME_OFFSET(x0,y0)]; u32 isRGB24 = (GPU_GP1 & 0x00200000 ? 32 : 0); /* Clear the screen if resolution changed to prevent interlacing and clipping to clash */ if( (w0 != old_res_horz || h1 != old_res_vert || (s16)isRGB24 != old_rgb24) ) { // Update old resolution old_res_horz = w0; old_res_vert = h1; old_rgb24 = (s16)isRGB24; // Finally, clear the screen for this special case video_clear(); } // Height centering int sizeShift = 1; if(h0==256) h0 = 240; else if(h0==480) sizeShift = 2; if(h1>h0) { src_screen16 += ((h1-h0)>>sizeShift)*1024; h1 = h0; } else if(h1>sizeShift)*VIDEO_WIDTH; /* Main blitter */ int incY = (h0==480) ? 2 : 1; h0=(h0==480 ? 2048 : 1024); if (!progressInterlace) { const int li=linesInterlace; switch ( w0 ) { case 256: for(int y1=y0+h1; y0=1000000) { u32 expected_fps=(isPAL?50:60); sprintf(msg,"FPS=%3d/%2d SPD=%3d%%",((real_fps*(12-skipCount))/12),((expected_fps*(12-skipCount))/12),((real_fps*100)/expected_fps)); prev=now; real_fps=0; } port_printf(5,5,msg); } // Update frame-skip if (!alt_fps) { // Video frame-skip skipFrame=skipTable[skipCount][skCount]; skCount--; if (skCount<0) skCount=11; isSkip=skipFrame; } else { // Game frame-skip if (!isSkip) { skipFrame=skipTable[skipCount][skCount]; skCount--; if (skCount<0) skCount=11; isSkip=true; } } pcsx4all_prof_end_with_resume(PCSX4ALL_PROF_GPU,PCSX4ALL_PROF_COUNTERS); } #else #include "../../frontend/plugin_lib.h" extern "C" { extern void bgr555_to_rgb565(void *dst, void *src, int bytes); extern void bgr888_to_rgb888(void *dst, void *src, int bytes); static const struct rearmed_cbs *cbs; static void *screen_buf; static void blit(void) { static s16 old_res_horz, old_res_vert, old_rgb24; s16 isRGB24 = (GPU_GP1 & 0x00200000) ? 1 : 0; s16 h0, x0, y0, w0, h1; u16 *srcs; u8 *dest; x0 = DisplayArea[0] & ~3; // alignment needed by blitter y0 = DisplayArea[1]; srcs = &((u16*)GPU_FrameBuffer)[FRAME_OFFSET(x0,y0)]; w0 = DisplayArea[2]; h0 = DisplayArea[3]; // video mode h1 = DisplayArea[5] - DisplayArea[4]; // display needed if (h0 == 480) h1 = Min2(h1*2,480); if (h1 <= 0) return; if (w0 != old_res_horz || h1 != old_res_vert || isRGB24 != old_rgb24) { old_res_horz = w0; old_res_vert = h1; old_rgb24 = (s16)isRGB24; screen_buf = cbs->pl_fbdev_set_mode(w0, h1, isRGB24 ? 24 : 16); } dest = (u8 *)screen_buf; if (isRGB24) { for (; h1-- > 0; dest += w0 * 3, srcs += 1024) { bgr888_to_rgb888(dest, srcs, w0 * 3); } } else { for (; h1-- > 0; dest += w0 * 2, srcs += 1024) { bgr555_to_rgb565(dest, srcs, w0 * 2); } } screen_buf = cbs->pl_fbdev_flip(); } void GPU_updateLace(void) { // Interlace bit toggle GPU_GP1 ^= 0x80000000; if (!fb_dirty || (GPU_GP1&0x08800000)) return; if (!isSkip) { blit(); fb_dirty = false; if (*cbs->fskip_option) isSkip = true; } else isSkip = false; } long GPUopen(unsigned long *, char *, char *) { cbs->pl_fbdev_open(); screen_buf = cbs->pl_fbdev_flip(); return 0; } long GPUclose(void) { cbs->pl_fbdev_close(); return 0; } long GPUfreeze(unsigned int ulGetFreezeData, GPUFreeze_t* p2) { if (ulGetFreezeData > 1) return 0; return GPU_freeze(ulGetFreezeData, p2); } void GPUrearmedCallbacks(const struct rearmed_cbs *cbs_) { cbs = cbs_; } } /* extern "C" */ #endif