rename gpu_unai to gpu_unai_old
[pcsx_rearmed.git] / plugins / gpu_unai_old / gpu.cpp
diff --git a/plugins/gpu_unai_old/gpu.cpp b/plugins/gpu_unai_old/gpu.cpp
new file mode 100644 (file)
index 0000000..1552bed
--- /dev/null
@@ -0,0 +1,915 @@
+/***************************************************************************
+*   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 wasSkip = false;
+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 FrameToRead = false; /* load image in progress */
+bool FrameToWrite = false; /* store image in progress */
+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 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(2048)));
+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(x)  (((s32)(x)<<21)>>21)
+
+#define CHKMAX_X 1024
+#define CHKMAX_Y 512
+
+#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<<TABLE_BITS);++i)
+       {
+               double v = 1.0 / double(i);
+               #ifdef GPU_TABLE_10_BITS
+               v *= double(0xffffffff>>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));
+               // save resolution and registers for P.E.Op.S. compatibility
+               p2->Control[3] = (3 << 24) | ((GPU_GP1 >> 23) & 1);
+               p2->Control[4] = (4 << 24) | ((GPU_GP1 >> 29) & 3);
+               p2->Control[5] = (5 << 24) | (DisplayArea[0] | (DisplayArea[1] << 10));
+               p2->Control[6] = (6 << 24) | (2560 << 12);
+               p2->Control[7] = (7 << 24) | (DisplayArea[4] | (DisplayArea[5] << 10));
+               p2->Control[8] = (8 << 24) | ((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)
+                       {
+                               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 = false;
+                                               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 = false;
+                                               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<lUsedAddr[0]) lUsedAddr[1]=laddr;
+       else                   lUsedAddr[2]=laddr;
+       lUsedAddr[0]=laddr;
+       return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+long GPU_dmaChain(u32* baseAddr, u32 dmaVAddr)
+{
+#ifdef DEBUG_ANALYSIS
+       dbg_anacnt_GPU_dmaChain++;
+#endif
+       pcsx4all_prof_start_with_pause(PCSX4ALL_PROF_GPU,PCSX4ALL_PROF_HW_WRITE);
+       u32 data, *address, count, offset;
+       unsigned int DMACommandCounter = 0;
+       long dma_words = 0;
+
+       GPU_GP1 &= ~0x14000000;
+       lUsedAddr[0]=lUsedAddr[1]=lUsedAddr[2]=(u32*)0x1fffff;
+       dmaVAddr &= 0x001FFFFF;
+       while (dmaVAddr != 0x1FFFFF)
+       {
+               address = (baseAddr + (dmaVAddr >> 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);
+               dma_words += 1 + count;
+       }
+       GPU_GP1 = (GPU_GP1 | 0x14000000) & ~0x60000000;
+       pcsx4all_prof_end_with_resume(PCSX4ALL_PROF_GPU,PCSX4ALL_PROF_HW_WRITE);
+
+       return dma_words;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+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 = false;
+                               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 = false;
+                                       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 = 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 |= (u32)(pvram[px])<<16;
+               
+               *dmaAddress++ = data;
+
+               if (++px>=x_end) 
+               {
+                       px = 0;
+                       pvram += 1024;
+                       if (++py>=y_end) 
+                       {
+                               FrameToRead = false;
+                               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 = false;
+                               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 = false;
+                               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 = 0; FrameToRead = FrameToWrite = false;
+               break;
+       case 0x02:
+               GPU_GP1 &= ~0x08000000;
+               PacketCount = 0; FrameToRead = FrameToWrite = false;
+               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;
+               wasSkip = isSkip;
+               if (isSkip)
+                       isSkip = false;
+               else
+                       isSkip = skipFrame;
+               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<h0) dest_screen16 += ((h0-h1)>>sizeShift)*VIDEO_WIDTH;
+
+       /* Main blitter */
+       int incY = (h0==480) ? 2 : 1;
+       h0=(h0==480 ? 2048 : 1024);
+
+       {
+               const int li=linesInterlace;
+               bool pi=progressInterlace;
+               bool pif=progressInterlace_flag;
+               switch ( w0 )
+               {
+                       case 256:
+                               for(int y1=y0+h1; y0<y1; y0+=incY)
+                               {
+                                       if(( 0 == (y0&li) ) && ((!pi) || (pif=!pif))) GPU_BlitWWDWW(    src_screen16,   dest_screen16, isRGB24);
+                                       dest_screen16 += VIDEO_WIDTH;
+                                       src_screen16  += h0;
+                               }
+                               break;
+                       case 368:
+                               for(int y1=y0+h1; y0<y1; y0+=incY)
+                               {
+                                       if(( 0 == (y0&li) ) && ((!pi) || (pif=!pif))) GPU_BlitWWWWWWWWS(        src_screen16,   dest_screen16, isRGB24, 4);
+                                       dest_screen16 += VIDEO_WIDTH;
+                                       src_screen16  += h0;
+                               }
+                               break;
+                       case 320:
+                               for(int y1=y0+h1; y0<y1; y0+=incY)
+                               {
+                                       if(( 0 == (y0&li) ) && ((!pi) || (pif=!pif))) GPU_BlitWW(       src_screen16,   dest_screen16, isRGB24);
+                                       dest_screen16 += VIDEO_WIDTH;
+                                       src_screen16  += h0;
+                               }
+                               break;
+                       case 384:
+                               for(int y1=y0+h1; y0<y1; y0+=incY)
+                               {
+                                       if(( 0 == (y0&li) ) && ((!pi) || (pif=!pif))) GPU_BlitWWWWWS(   src_screen16,   dest_screen16, isRGB24);
+                                       dest_screen16 += VIDEO_WIDTH;
+                                       src_screen16  += h0;
+                               }
+                               break;
+                       case 512:
+                               for(int y1=y0+h1; y0<y1; y0+=incY)
+                               {
+                                       if(( 0 == (y0&li) ) && ((!pi) || (pif=!pif))) GPU_BlitWWSWWSWS( src_screen16, dest_screen16, isRGB24);
+                                       dest_screen16 += VIDEO_WIDTH;
+                                       src_screen16  += h0;
+                               }
+                               break;
+                       case 640:
+                               for(int y1=y0+h1; y0<y1; y0+=incY)
+                               {
+                                       if(( 0 == (y0&li) ) && ((!pi) || (pif=!pif))) GPU_BlitWS(       src_screen16, dest_screen16, isRGB24);
+                                       dest_screen16 += VIDEO_WIDTH;
+                                       src_screen16  += h0;
+                               }
+                               break;
+               }
+               progressInterlace_flag=!progressInterlace_flag;
+       }
+       video_flip();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void  GPU_updateLace(void)
+{
+#ifdef  ENABLE_GPU_LOG_SUPPORT
+       fprintf(stdout,"GPU_updateLace()\n");
+#endif
+#ifdef DEBUG_ANALYSIS
+       dbg_anacnt_GPU_updateLace++;
+#endif
+       pcsx4all_prof_start_with_pause(PCSX4ALL_PROF_GPU,PCSX4ALL_PROF_COUNTERS);
+#ifdef PROFILER_PCSX4ALL
+       pcsx4all_prof_frames++;
+#endif
+#ifdef DEBUG_FRAME
+       if(isdbg_frame())
+       {
+               static int passed=0;
+               if (!passed) dbg_enable();
+               else pcsx4all_exit();
+               passed++;
+       }
+#endif
+
+       // Frame skip table
+       static const unsigned char skipTable[12][12] =
+       {
+               { 0,0,0,0,0,0,0,0,0,0,0,0 },
+               { 0,0,0,0,0,0,0,0,0,0,0,1 },
+               { 0,0,0,0,0,1,0,0,0,0,0,1 },
+               { 0,0,0,1,0,0,0,1,0,0,0,1 },
+               { 0,0,1,0,0,1,0,0,1,0,0,1 },
+               { 0,1,0,0,1,0,1,0,0,1,0,1 },
+               { 0,1,0,1,0,1,0,1,0,1,0,1 },
+               { 0,1,0,1,1,0,1,0,1,1,0,1 },
+               { 0,1,1,0,1,1,0,1,1,0,1,1 },
+               { 0,1,1,1,0,1,1,1,0,1,1,1 },
+               { 0,1,1,1,1,1,0,1,1,1,1,1 },
+               { 0,1,1,1,1,1,1,1,1,1,1,1 }
+       };
+       
+       // Interlace bit toggle
+       GPU_GP1 ^= 0x80000000;
+
+       // Update display
+       if ((!skipFrame) && (!isSkip) && (fb_dirty) && (!(((GPU_GP1&0x08000000))||((GPU_GP1&0x00800000)))))
+       {
+               gpuVideoOutput(); // Display updated
+
+               if (DisplayArea[3] == 480)
+               {
+                       if (linesInterlace_user) linesInterlace = 3; // 1/4 of lines
+                       else linesInterlace = 1; // if 480 we only need half of lines
+               }
+               else if (linesInterlace != linesInterlace_user)
+               {
+                       linesInterlace = linesInterlace_user; // resolution changed from 480 to lower one
+                       video_clear();
+               }
+       }
+
+       // Limit FPS
+       if (frameLimit)
+       {
+               static unsigned next=get_ticks();
+               if (!skipFrame)
+               {
+                       unsigned now=get_ticks();
+                       if (now<next) wait_ticks(next-now);
+               }
+               next+=(isPAL?(1000000/50):((unsigned)(1000000.0/59.94)));
+       }
+
+       // Show FPS statistics
+       if (show_fps)
+       {
+               static u32 real_fps=0;
+               static u32 prev=get_ticks();
+               static char msg[32]="FPS=000/00 SPD=000%";
+               u32 now=get_ticks();
+               real_fps++;
+               if ((now-prev)>=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;
+               }
+       }
+       fb_dirty=false;
+
+       pcsx4all_prof_end_with_resume(PCSX4ALL_PROF_GPU,PCSX4ALL_PROF_COUNTERS);
+}
+
+#else
+
+#include "../../frontend/plugin_lib.h"
+
+extern "C" {
+
+static const struct rearmed_cbs *cbs;
+static s16 old_res_horz, old_res_vert, old_rgb24;
+
+static void blit(void)
+{
+       u16 *base = (u16 *)GPU_FrameBuffer;
+       s16 isRGB24 = (GPU_GP1 & 0x00200000) ? 1 : 0;
+       s16 h0, x0, y0, w0, h1;
+
+       x0 = DisplayArea[0] & ~1; // alignment needed by blitter
+       y0 = DisplayArea[1];
+       base += 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;
+               cbs->pl_vout_set_mode(w0, h1, w0, h1, isRGB24 ? 24 : 16);
+       }
+
+       cbs->pl_vout_flip(base, 1024, isRGB24, w0, h1);
+}
+
+void GPU_updateLace(void)
+{
+       // Interlace bit toggle
+       GPU_GP1 ^= 0x80000000;
+
+       if (!fb_dirty || (GPU_GP1&0x08800000))
+               return;
+
+       if (!wasSkip) {
+               blit();
+               fb_dirty = false;
+               skCount = 0;
+       }
+       else {
+               skCount++;
+               if (skCount >= 8)
+                       wasSkip = isSkip = 0;
+       }
+
+       skipFrame = cbs->fskip_advice || cbs->frameskip == 1;
+}
+
+long GPUopen(unsigned long *, char *, char *)
+{
+       cbs->pl_vout_open();
+       return 0;
+}
+
+long GPUclose(void)
+{
+       cbs->pl_vout_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_)
+{
+       enableAbbeyHack = cbs_->gpu_unai.abe_hack;
+       light = !cbs_->gpu_unai.no_light;
+       blend = !cbs_->gpu_unai.no_blend;
+       if (cbs_->pl_vout_set_raw_vram)
+               cbs_->pl_vout_set_raw_vram((void *)GPU_FrameBuffer);
+
+       cbs = cbs_;
+       if (cbs->pl_set_gpu_caps)
+               cbs->pl_set_gpu_caps(0);
+}
+
+} /* extern "C" */
+
+#endif