Merge pull request #461 from negativeExponent/libchdr
[pcsx_rearmed.git] / plugins / gpu_unai / gpu.cpp
1 /***************************************************************************
2 *   Copyright (C) 2010 PCSX4ALL Team                                      *
3 *   Copyright (C) 2010 Unai                                               *
4 *   Copyright (C) 2016 Senquack (dansilsby <AT> gmail <DOT> com)          *
5 *                                                                         *
6 *   This program is free software; you can redistribute it and/or modify  *
7 *   it under the terms of the GNU General Public License as published by  *
8 *   the Free Software Foundation; either version 2 of the License, or     *
9 *   (at your option) any later version.                                   *
10 *                                                                         *
11 *   This program is distributed in the hope that it will be useful,       *
12 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
13 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
14 *   GNU General Public License for more details.                          *
15 *                                                                         *
16 *   You should have received a copy of the GNU General Public License     *
17 *   along with this program; if not, write to the                         *
18 *   Free Software Foundation, Inc.,                                       *
19 *   51 Franklin Street, Fifth Floor, Boston, MA 02111-1307 USA.           *
20 ***************************************************************************/
21
22 #include <stddef.h>
23 #include "plugins.h"
24 #include "psxcommon.h"
25 //#include "port.h"
26 #include "gpu_unai.h"
27
28 #define VIDEO_WIDTH 320
29
30 #ifdef TIME_IN_MSEC
31 #define TPS 1000
32 #else
33 #define TPS 1000000
34 #endif
35
36 #define IS_PAL (gpu_unai.GPU_GP1&(0x08<<17))
37
38 //senquack - Original 512KB of guard space seems not to be enough, as Xenogears
39 // accesses outside this range and crashes in town intro fight sequence.
40 // Increased to 2MB total (double PSX VRAM) and Xenogears no longer
41 // crashes, but some textures are still messed up. Also note that alignment min
42 // is 16 bytes, needed for pixel-skipping rendering/blitting in high horiz res.
43 // Extra 4KB is for guard room at beginning.
44 // TODO: Determine cause of out-of-bounds write/reads. <-- Note: this is largely
45 //  solved by adoption of PCSX Rearmed's 'gpulib' in gpulib_if.cpp, which
46 //  replaces this file (gpu.cpp)
47 //u16   GPU_FrameBuffer[(FRAME_BUFFER_SIZE+512*1024)/2] __attribute__((aligned(32)));
48 static u16 GPU_FrameBuffer[(FRAME_BUFFER_SIZE*2 + 4096)/2] __attribute__((aligned(32)));
49
50 ///////////////////////////////////////////////////////////////////////////////
51 // GPU fixed point math
52 #include "gpu_fixedpoint.h"
53
54 ///////////////////////////////////////////////////////////////////////////////
55 // Inner loop driver instantiation file
56 #include "gpu_inner.h"
57
58 ///////////////////////////////////////////////////////////////////////////////
59 // GPU internal image drawing functions
60 #include "gpu_raster_image.h"
61
62 ///////////////////////////////////////////////////////////////////////////////
63 // GPU internal line drawing functions
64 #include "gpu_raster_line.h"
65
66 ///////////////////////////////////////////////////////////////////////////////
67 // GPU internal polygon drawing functions
68 #include "gpu_raster_polygon.h"
69
70 ///////////////////////////////////////////////////////////////////////////////
71 // GPU internal sprite drawing functions
72 #include "gpu_raster_sprite.h"
73
74 ///////////////////////////////////////////////////////////////////////////////
75 // GPU command buffer execution/store
76 #include "gpu_command.h"
77
78 ///////////////////////////////////////////////////////////////////////////////
79 static void gpuReset(void)
80 {
81         memset((void*)&gpu_unai, 0, sizeof(gpu_unai));
82         gpu_unai.vram = (u16*)GPU_FrameBuffer + (4096/2); //4kb guard room in front
83         gpu_unai.GPU_GP1 = 0x14802000;
84         gpu_unai.DrawingArea[2] = 256;
85         gpu_unai.DrawingArea[3] = 240;
86         gpu_unai.DisplayArea[2] = 256;
87         gpu_unai.DisplayArea[3] = 240;
88         gpu_unai.DisplayArea[5] = 240;
89         gpu_unai.TextureWindow[0] = 0;
90         gpu_unai.TextureWindow[1] = 0;
91         gpu_unai.TextureWindow[2] = 255;
92         gpu_unai.TextureWindow[3] = 255;
93         //senquack - new vars must be updated whenever texture window is changed:
94         //           (used for polygon-drawing in gpu_inner.h, gpu_raster_polygon.h)
95         const u32 fb = FIXED_BITS;  // # of fractional fixed-pt bits of u4/v4
96         gpu_unai.u_msk = (((u32)gpu_unai.TextureWindow[2]) << fb) | ((1 << fb) - 1);
97         gpu_unai.v_msk = (((u32)gpu_unai.TextureWindow[3]) << fb) | ((1 << fb) - 1);
98
99         // Configuration options
100         gpu_unai.config = gpu_unai_config_ext;
101         gpu_unai.ilace_mask = gpu_unai.config.ilace_force;
102         gpu_unai.frameskip.skipCount = gpu_unai.config.frameskip_count;
103
104         SetupLightLUT();
105         SetupDitheringConstants();
106 }
107
108 ///////////////////////////////////////////////////////////////////////////////
109 long GPU_init(void)
110 {
111         gpuReset();
112
113 #ifdef GPU_UNAI_USE_INT_DIV_MULTINV
114         // s_invTable
115         for(unsigned int i=1;i<=(1<<TABLE_BITS);++i)
116         {
117                 s_invTable[i-1]=0x7fffffff/i;
118         }
119 #endif
120
121         gpu_unai.fb_dirty = true;
122         gpu_unai.dma.last_dma = NULL;
123         return (0);
124 }
125
126 ///////////////////////////////////////////////////////////////////////////////
127 long GPU_shutdown(void)
128 {
129         return 0;
130 }
131
132 ///////////////////////////////////////////////////////////////////////////////
133 long GPU_freeze(u32 bWrite, GPUFreeze_t* p2)
134 {
135         if (!p2) return (0);
136         if (p2->ulFreezeVersion != 1) return (0);
137
138         if (bWrite)
139         {
140                 p2->ulStatus = gpu_unai.GPU_GP1;
141                 memset(p2->ulControl, 0, sizeof(p2->ulControl));
142                 // save resolution and registers for P.E.Op.S. compatibility
143                 p2->ulControl[3] = (3 << 24) | ((gpu_unai.GPU_GP1 >> 23) & 1);
144                 p2->ulControl[4] = (4 << 24) | ((gpu_unai.GPU_GP1 >> 29) & 3);
145                 p2->ulControl[5] = (5 << 24) | (gpu_unai.DisplayArea[0] | (gpu_unai.DisplayArea[1] << 10));
146                 p2->ulControl[6] = (6 << 24) | (2560 << 12);
147                 p2->ulControl[7] = (7 << 24) | (gpu_unai.DisplayArea[4] | (gpu_unai.DisplayArea[5] << 10));
148                 p2->ulControl[8] = (8 << 24) | ((gpu_unai.GPU_GP1 >> 17) & 0x3f) | ((gpu_unai.GPU_GP1 >> 10) & 0x40);
149                 memcpy((void*)p2->psxVRam, (void*)gpu_unai.vram, FRAME_BUFFER_SIZE);
150                 return (1);
151         }
152         else
153         {
154                 extern void GPU_writeStatus(u32 data);
155                 gpu_unai.GPU_GP1 = p2->ulStatus;
156                 memcpy((void*)gpu_unai.vram, (void*)p2->psxVRam, FRAME_BUFFER_SIZE);
157                 GPU_writeStatus((5 << 24) | p2->ulControl[5]);
158                 GPU_writeStatus((7 << 24) | p2->ulControl[7]);
159                 GPU_writeStatus((8 << 24) | p2->ulControl[8]);
160                 gpuSetTexture(gpu_unai.GPU_GP1);
161                 return (1);
162         }
163         return (0);
164 }
165
166 ///////////////////////////////////////////////////////////////////////////////
167 //  GPU DMA comunication
168
169 ///////////////////////////////////////////////////////////////////////////////
170 u8 PacketSize[256] =
171 {
172         0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //              0-15
173         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //              16-31
174         3, 3, 3, 3, 6, 6, 6, 6, 4, 4, 4, 4, 8, 8, 8, 8, //              32-47
175         5, 5, 5, 5, 8, 8, 8, 8, 7, 7, 7, 7, 11, 11, 11, 11,     //      48-63
176         2, 2, 2, 2, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, //              64-79
177         3, 3, 3, 3, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, //              80-95
178         2, 2, 2, 2, 3, 3, 3, 3, 1, 1, 1, 1, 2, 2, 2, 2, //              96-111
179         1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 2, 2, 2, 2, //              112-127
180         3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //              128-
181         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //              144
182         2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //              160
183         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
184         2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
185         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
186         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
187         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0  //
188 };
189
190 ///////////////////////////////////////////////////////////////////////////////
191 INLINE void gpuSendPacket()
192 {
193         gpuSendPacketFunction(gpu_unai.PacketBuffer.U4[0]>>24);
194 }
195
196 ///////////////////////////////////////////////////////////////////////////////
197 INLINE void gpuCheckPacket(u32 uData)
198 {
199         if (gpu_unai.PacketCount)
200         {
201                 gpu_unai.PacketBuffer.U4[gpu_unai.PacketIndex++] = uData;
202                 --gpu_unai.PacketCount;
203         }
204         else
205         {
206                 gpu_unai.PacketBuffer.U4[0] = uData;
207                 gpu_unai.PacketCount = PacketSize[uData >> 24];
208                 gpu_unai.PacketIndex = 1;
209         }
210         if (!gpu_unai.PacketCount) gpuSendPacket();
211 }
212
213 ///////////////////////////////////////////////////////////////////////////////
214 void GPU_writeDataMem(u32* dmaAddress, int dmaCount)
215 {
216         #ifdef ENABLE_GPU_LOG_SUPPORT
217                 fprintf(stdout,"GPU_writeDataMem(%d)\n",dmaCount);
218         #endif
219         u32 data;
220         const u16 *VIDEO_END = (u16*)gpu_unai.vram+(FRAME_BUFFER_SIZE/2)-1;
221         gpu_unai.GPU_GP1 &= ~0x14000000;
222
223         while (dmaCount) 
224         {
225                 if (gpu_unai.dma.FrameToWrite)
226                 {
227                         while (dmaCount)
228                         {
229                                 dmaCount--;
230                                 data = *dmaAddress++;
231                                 if ((&gpu_unai.dma.pvram[gpu_unai.dma.px])>(VIDEO_END)) gpu_unai.dma.pvram-=512*1024;
232                                 gpu_unai.dma.pvram[gpu_unai.dma.px] = data;
233                                 if (++gpu_unai.dma.px >= gpu_unai.dma.x_end)
234                                 {
235                                         gpu_unai.dma.px = 0;
236                                         gpu_unai.dma.pvram += 1024;
237                                         if (++gpu_unai.dma.py >= gpu_unai.dma.y_end)
238                                         {
239                                                 gpu_unai.dma.FrameToWrite = false;
240                                                 gpu_unai.GPU_GP1 &= ~0x08000000;
241                                                 gpu_unai.fb_dirty = true;
242                                                 break;
243                                         }
244                                 }
245                                 if ((&gpu_unai.dma.pvram[gpu_unai.dma.px])>(VIDEO_END)) gpu_unai.dma.pvram-=512*1024;
246                                 gpu_unai.dma.pvram[gpu_unai.dma.px] = data>>16;
247                                 if (++gpu_unai.dma.px >= gpu_unai.dma.x_end)
248                                 {
249                                         gpu_unai.dma.px = 0;
250                                         gpu_unai.dma.pvram += 1024;
251                                         if (++gpu_unai.dma.py >= gpu_unai.dma.y_end)
252                                         {
253                                                 gpu_unai.dma.FrameToWrite = false;
254                                                 gpu_unai.GPU_GP1 &= ~0x08000000;
255                                                 gpu_unai.fb_dirty = true;
256                                                 break;
257                                         }
258                                 }
259                         }
260                 }
261                 else
262                 {
263                         data = *dmaAddress++;
264                         dmaCount--;
265                         gpuCheckPacket(data);
266                 }
267         }
268
269         gpu_unai.GPU_GP1 = (gpu_unai.GPU_GP1 | 0x14000000) & ~0x60000000;
270 }
271
272 long GPU_dmaChain(u32 *rambase, u32 start_addr)
273 {
274         #ifdef ENABLE_GPU_LOG_SUPPORT
275                 fprintf(stdout,"GPU_dmaChain(0x%x)\n",start_addr);
276         #endif
277
278         u32 addr, *list;
279         u32 len, count;
280         long dma_words = 0;
281
282         if (gpu_unai.dma.last_dma) *gpu_unai.dma.last_dma |= 0x800000;
283         
284         gpu_unai.GPU_GP1 &= ~0x14000000;
285         
286         addr = start_addr & 0xffffff;
287         for (count = 0; addr != 0xffffff; count++)
288         {
289                 list = rambase + (addr & 0x1fffff) / 4;
290                 len = list[0] >> 24;
291                 addr = list[0] & 0xffffff;
292
293                 dma_words += 1 + len;
294
295                 // add loop detection marker
296                 list[0] |= 0x800000;
297
298                 if (len) GPU_writeDataMem(list + 1, len);
299
300                 if (addr & 0x800000)
301                 {
302                         #ifdef ENABLE_GPU_LOG_SUPPORT
303                                 fprintf(stdout,"GPU_dmaChain(LOOP)\n");
304                         #endif
305                         break;
306                 }
307         }
308
309         // remove loop detection markers
310         addr = start_addr & 0x1fffff;
311         while (count-- > 0)
312         {
313                 list = rambase + addr / 4;
314                 addr = list[0] & 0x1fffff;
315                 list[0] &= ~0x800000;
316         }
317         
318         if (gpu_unai.dma.last_dma) *gpu_unai.dma.last_dma &= ~0x800000;
319         gpu_unai.dma.last_dma = rambase + (start_addr & 0x1fffff) / 4;
320
321         gpu_unai.GPU_GP1 = (gpu_unai.GPU_GP1 | 0x14000000) & ~0x60000000;
322
323         return dma_words;
324 }
325
326 ///////////////////////////////////////////////////////////////////////////////
327 void GPU_writeData(u32 data)
328 {
329         const u16 *VIDEO_END = (u16*)gpu_unai.vram+(FRAME_BUFFER_SIZE/2)-1;
330         #ifdef ENABLE_GPU_LOG_SUPPORT
331                 fprintf(stdout,"GPU_writeData()\n");
332         #endif
333         gpu_unai.GPU_GP1 &= ~0x14000000;
334
335         if (gpu_unai.dma.FrameToWrite)
336         {
337                 if ((&gpu_unai.dma.pvram[gpu_unai.dma.px])>(VIDEO_END)) gpu_unai.dma.pvram-=512*1024;
338                 gpu_unai.dma.pvram[gpu_unai.dma.px]=(u16)data;
339                 if (++gpu_unai.dma.px >= gpu_unai.dma.x_end)
340                 {
341                         gpu_unai.dma.px = 0;
342                         gpu_unai.dma.pvram += 1024;
343                         if (++gpu_unai.dma.py >= gpu_unai.dma.y_end)
344                         {
345                                 gpu_unai.dma.FrameToWrite = false;
346                                 gpu_unai.GPU_GP1 &= ~0x08000000;
347                                 gpu_unai.fb_dirty = true;
348                         }
349                 }
350                 if (gpu_unai.dma.FrameToWrite)
351                 {
352                         if ((&gpu_unai.dma.pvram[gpu_unai.dma.px])>(VIDEO_END)) gpu_unai.dma.pvram-=512*1024;
353                         gpu_unai.dma.pvram[gpu_unai.dma.px]=data>>16;
354                         if (++gpu_unai.dma.px >= gpu_unai.dma.x_end)
355                         {
356                                 gpu_unai.dma.px = 0;
357                                 gpu_unai.dma.pvram += 1024;
358                                 if (++gpu_unai.dma.py >= gpu_unai.dma.y_end)
359                                 {
360                                         gpu_unai.dma.FrameToWrite = false;
361                                         gpu_unai.GPU_GP1 &= ~0x08000000;
362                                         gpu_unai.fb_dirty = true;
363                                 }
364                         }
365                 }
366         }
367         else
368         {
369                 gpuCheckPacket(data);
370         }
371         gpu_unai.GPU_GP1 |= 0x14000000;
372 }
373
374
375 ///////////////////////////////////////////////////////////////////////////////
376 void GPU_readDataMem(u32* dmaAddress, int dmaCount)
377 {
378         const u16 *VIDEO_END = (u16*)gpu_unai.vram+(FRAME_BUFFER_SIZE/2)-1;
379         #ifdef ENABLE_GPU_LOG_SUPPORT
380                 fprintf(stdout,"GPU_readDataMem(%d)\n",dmaCount);
381         #endif
382         if(!gpu_unai.dma.FrameToRead) return;
383
384         gpu_unai.GPU_GP1 &= ~0x14000000;
385         do 
386         {
387                 if ((&gpu_unai.dma.pvram[gpu_unai.dma.px])>(VIDEO_END)) gpu_unai.dma.pvram-=512*1024;
388                 // lower 16 bit
389                 //senquack - 64-bit fix (from notaz)
390                 //u32 data = (unsigned long)gpu_unai.dma.pvram[gpu_unai.dma.px];
391                 u32 data = (u32)gpu_unai.dma.pvram[gpu_unai.dma.px];
392
393                 if (++gpu_unai.dma.px >= gpu_unai.dma.x_end)
394                 {
395                         gpu_unai.dma.px = 0;
396                         gpu_unai.dma.pvram += 1024;
397                 }
398
399                 if ((&gpu_unai.dma.pvram[gpu_unai.dma.px])>(VIDEO_END)) gpu_unai.dma.pvram-=512*1024;
400                 // higher 16 bit (always, even if it's an odd width)
401                 //senquack - 64-bit fix (from notaz)
402                 //data |= (unsigned long)(gpu_unai.dma.pvram[gpu_unai.dma.px])<<16;
403                 data |= (u32)(gpu_unai.dma.pvram[gpu_unai.dma.px])<<16;
404                 
405                 *dmaAddress++ = data;
406
407                 if (++gpu_unai.dma.px >= gpu_unai.dma.x_end)
408                 {
409                         gpu_unai.dma.px = 0;
410                         gpu_unai.dma.pvram += 1024;
411                         if (++gpu_unai.dma.py >= gpu_unai.dma.y_end)
412                         {
413                                 gpu_unai.dma.FrameToRead = false;
414                                 gpu_unai.GPU_GP1 &= ~0x08000000;
415                                 break;
416                         }
417                 }
418         } while (--dmaCount);
419
420         gpu_unai.GPU_GP1 = (gpu_unai.GPU_GP1 | 0x14000000) & ~0x60000000;
421 }
422
423
424
425 ///////////////////////////////////////////////////////////////////////////////
426 u32 GPU_readData(void)
427 {
428         const u16 *VIDEO_END = (u16*)gpu_unai.vram+(FRAME_BUFFER_SIZE/2)-1;
429         #ifdef ENABLE_GPU_LOG_SUPPORT
430                 fprintf(stdout,"GPU_readData()\n");
431         #endif
432         gpu_unai.GPU_GP1 &= ~0x14000000;
433         if (gpu_unai.dma.FrameToRead)
434         {
435                 if ((&gpu_unai.dma.pvram[gpu_unai.dma.px])>(VIDEO_END)) gpu_unai.dma.pvram-=512*1024;
436                 gpu_unai.GPU_GP0 = gpu_unai.dma.pvram[gpu_unai.dma.px];
437                 if (++gpu_unai.dma.px >= gpu_unai.dma.x_end)
438                 {
439                         gpu_unai.dma.px = 0;
440                         gpu_unai.dma.pvram += 1024;
441                         if (++gpu_unai.dma.py >= gpu_unai.dma.y_end)
442                         {
443                                 gpu_unai.dma.FrameToRead = false;
444                                 gpu_unai.GPU_GP1 &= ~0x08000000;
445                         }
446                 }
447                 if ((&gpu_unai.dma.pvram[gpu_unai.dma.px])>(VIDEO_END)) gpu_unai.dma.pvram-=512*1024;
448                 gpu_unai.GPU_GP0 |= gpu_unai.dma.pvram[gpu_unai.dma.px]<<16;
449                 if (++gpu_unai.dma.px >= gpu_unai.dma.x_end)
450                 {
451                         gpu_unai.dma.px = 0;
452                         gpu_unai.dma.pvram += 1024;
453                         if (++gpu_unai.dma.py >= gpu_unai.dma.y_end)
454                         {
455                                 gpu_unai.dma.FrameToRead = false;
456                                 gpu_unai.GPU_GP1 &= ~0x08000000;
457                         }
458                 }
459
460         }
461         gpu_unai.GPU_GP1 |= 0x14000000;
462
463         return (gpu_unai.GPU_GP0);
464 }
465
466 ///////////////////////////////////////////////////////////////////////////////
467 u32 GPU_readStatus(void)
468 {
469         return gpu_unai.GPU_GP1;
470 }
471
472 INLINE void GPU_NoSkip(void)
473 {
474         #ifdef ENABLE_GPU_LOG_SUPPORT
475                 fprintf(stdout,"GPU_NoSkip()\n");
476         #endif
477         gpu_unai.frameskip.wasSkip = gpu_unai.frameskip.isSkip;
478         if (gpu_unai.frameskip.isSkip)
479         {
480                 gpu_unai.frameskip.isSkip = false;
481                 gpu_unai.frameskip.skipGPU = false;
482         }
483         else
484         {
485                 gpu_unai.frameskip.isSkip = gpu_unai.frameskip.skipFrame;
486                 gpu_unai.frameskip.skipGPU = gpu_unai.frameskip.skipFrame;
487         }
488 }
489
490 ///////////////////////////////////////////////////////////////////////////////
491 void  GPU_writeStatus(u32 data)
492 {
493         #ifdef ENABLE_GPU_LOG_SUPPORT
494                 fprintf(stdout,"GPU_writeStatus(%d,%d)\n",data>>24,data & 0xff);
495         #endif
496         switch (data >> 24) {
497         case 0x00:
498                 gpuReset();
499                 break;
500         case 0x01:
501                 gpu_unai.GPU_GP1 &= ~0x08000000;
502                 gpu_unai.PacketCount = 0;
503                 gpu_unai.dma.FrameToRead = gpu_unai.dma.FrameToWrite = false;
504                 break;
505         case 0x02:
506                 gpu_unai.GPU_GP1 &= ~0x08000000;
507                 gpu_unai.PacketCount = 0;
508                 gpu_unai.dma.FrameToRead = gpu_unai.dma.FrameToWrite = false;
509                 break;
510         case 0x03:
511                 gpu_unai.GPU_GP1 = (gpu_unai.GPU_GP1 & ~0x00800000) | ((data & 1) << 23);
512                 break;
513         case 0x04:
514                 if (data == 0x04000000) gpu_unai.PacketCount = 0;
515                 gpu_unai.GPU_GP1 = (gpu_unai.GPU_GP1 & ~0x60000000) | ((data & 3) << 29);
516                 break;
517         case 0x05:
518                 // Start of Display Area in VRAM
519                 gpu_unai.DisplayArea[0] = data & 0x3ff;         // X (0..1023)
520                 gpu_unai.DisplayArea[1] = (data >> 10) & 0x1ff; // Y (0..511)
521                 GPU_NoSkip();
522                 break;
523         case 0x06:
524                 // GP1(06h) - Horizontal Display range (on Screen)
525                 // 0-11   X1 (260h+0)       ;12bit       ;\counted in 53.222400MHz units,
526                 // 12-23  X2 (260h+320*8)   ;12bit       ;/relative to HSYNC
527
528                 // senquack - gpu_unai completely ignores GP1(0x06) command and
529                 // lacks even a place in DisplayArea[] array to store the values.
530                 // It seems to have been concerned only with vertical display range
531                 // and centering top/bottom. I will not add support here, and
532                 // focus instead on the gpulib version (gpulib_if.cpp) which uses
533                 // gpulib for its PS1->host framebuffer blitting.
534                 break;
535         case 0x07:
536                 // GP1(07h) - Vertical Display range (on Screen)
537                 // 0-9   Y1 (NTSC=88h-(224/2), (PAL=A3h-(264/2))  ;\scanline numbers on screen,
538                 // 10-19 Y2 (NTSC=88h+(224/2), (PAL=A3h+(264/2))  ;/relative to VSYNC
539                 // 20-23 Not used (zero)
540                 {
541                         u32 v1=data & 0x000003FF; //(short)(data & 0x3ff);
542                         u32 v2=(data & 0x000FFC00) >> 10; //(short)((data>>10) & 0x3ff);
543                         if ((gpu_unai.DisplayArea[4]!=v1)||(gpu_unai.DisplayArea[5]!=v2))
544                         {
545                                 gpu_unai.DisplayArea[4] = v1;
546                                 gpu_unai.DisplayArea[5] = v2;
547                                 #ifdef ENABLE_GPU_LOG_SUPPORT
548                                         fprintf(stdout,"video_clear(CHANGE_Y)\n");
549                                 #endif
550                                 video_clear();
551                         }
552                 }
553                 break;
554         case 0x08:
555                 {
556                         static const u32 HorizontalResolution[8] = { 256, 368, 320, 384, 512, 512, 640, 640 };
557                         static const u32 VerticalResolution[4] = { 240, 480, 256, 480 };
558                         gpu_unai.GPU_GP1 = (gpu_unai.GPU_GP1 & ~0x007F0000) | ((data & 0x3F) << 17) | ((data & 0x40) << 10);
559                         #ifdef ENABLE_GPU_LOG_SUPPORT
560                                 fprintf(stdout,"GPU_writeStatus(RES=%dx%d,BITS=%d,PAL=%d)\n",HorizontalResolution[(gpu_unai.GPU_GP1 >> 16) & 7],
561                                                 VerticalResolution[(gpu_unai.GPU_GP1 >> 19) & 3],(gpu_unai.GPU_GP1&0x00200000?24:15),(IS_PAL?1:0));
562                         #endif
563                         // Video mode change
564                         u32 new_width = HorizontalResolution[(gpu_unai.GPU_GP1 >> 16) & 7];
565                         u32 new_height = VerticalResolution[(gpu_unai.GPU_GP1 >> 19) & 3];
566
567                         if (gpu_unai.DisplayArea[2] != new_width || gpu_unai.DisplayArea[3] != new_height)
568                         {
569                                 // Update width
570                                 gpu_unai.DisplayArea[2] = new_width;
571
572                                 if (PixelSkipEnabled()) {
573                                         // Set blit_mask for high horizontal resolutions. This allows skipping
574                                         //  rendering pixels that would never get displayed on low-resolution
575                                         //  platforms that use simple pixel-dropping scaler.
576                                         switch (gpu_unai.DisplayArea[2])
577                                         {
578                                                 case 512: gpu_unai.blit_mask = 0xa4; break; // GPU_BlitWWSWWSWS
579                                                 case 640: gpu_unai.blit_mask = 0xaa; break; // GPU_BlitWS
580                                                 default:  gpu_unai.blit_mask = 0;    break;
581                                         }
582                                 } else {
583                                         gpu_unai.blit_mask = 0;
584                                 }
585
586                                 // Update height
587                                 gpu_unai.DisplayArea[3] = new_height;
588
589                                 if (LineSkipEnabled()) {
590                                         // Set rendering line-skip (only render every other line in high-res
591                                         //  480 vertical mode, or, optionally, force it for all video modes)
592
593                                         if (gpu_unai.DisplayArea[3] == 480) {
594                                                 if (gpu_unai.config.ilace_force) {
595                                                         gpu_unai.ilace_mask = 3; // Only need 1/4 of lines
596                                                 } else {
597                                                         gpu_unai.ilace_mask = 1; // Only need 1/2 of lines
598                                                 }
599                                         } else {
600                                                 // Vert resolution changed from 480 to lower one
601                                                 gpu_unai.ilace_mask = gpu_unai.config.ilace_force;
602                                         }
603                                 } else {
604                                         gpu_unai.ilace_mask = 0;
605                                 }
606
607                                 #ifdef ENABLE_GPU_LOG_SUPPORT
608                                         fprintf(stdout,"video_clear(CHANGE_RES)\n");
609                                 #endif
610                                 video_clear();
611                         }
612
613                 }
614                 break;
615         case 0x10:
616                 switch (data & 0xff) {
617                         case 2: gpu_unai.GPU_GP0 = gpu_unai.tex_window; break;
618                         case 3: gpu_unai.GPU_GP0 = (gpu_unai.DrawingArea[1] << 10) | gpu_unai.DrawingArea[0]; break;
619                         case 4: gpu_unai.GPU_GP0 = ((gpu_unai.DrawingArea[3]-1) << 10) | (gpu_unai.DrawingArea[2]-1); break;
620                         case 5: case 6: gpu_unai.GPU_GP0 = (((u32)gpu_unai.DrawingOffset[1] & 0x7ff) << 11) | ((u32)gpu_unai.DrawingOffset[0] & 0x7ff); break;
621                         case 7: gpu_unai.GPU_GP0 = 2; break;
622                         case 8: case 15: gpu_unai.GPU_GP0 = 0xBFC03720; break;
623                 }
624                 break;
625         }
626 }
627
628 // Blitting functions
629 #include "gpu_blit.h"
630
631 static void gpuVideoOutput(void)
632 {
633         int h0, x0, y0, w0, h1;
634
635         x0 = gpu_unai.DisplayArea[0];
636         y0 = gpu_unai.DisplayArea[1];
637
638         w0 = gpu_unai.DisplayArea[2];
639         h0 = gpu_unai.DisplayArea[3];  // video mode
640
641         h1 = gpu_unai.DisplayArea[5] - gpu_unai.DisplayArea[4]; // display needed
642         if (h0 == 480) h1 = Min2(h1*2,480);
643
644         bool isRGB24 = (gpu_unai.GPU_GP1 & 0x00200000 ? true : false);
645         u16* dst16 = SCREEN;
646         u16* src16 = (u16*)gpu_unai.vram;
647
648         // PS1 fb read wraps around (fixes black screen in 'Tobal no. 1')
649         unsigned int src16_offs_msk = 1024*512-1;
650         unsigned int src16_offs = (x0 + y0*1024) & src16_offs_msk;
651
652         //  Height centering
653         int sizeShift = 1;
654         if (h0 == 256) {
655                 h0 = 240;
656         } else if (h0 == 480) {
657                 sizeShift = 2;
658         }
659         if (h1 > h0) {
660                 src16_offs = (src16_offs + (((h1-h0) / 2) * 1024)) & src16_offs_msk;
661                 h1 = h0;
662         } else if (h1<h0) {
663                 dst16 += ((h0-h1) >> sizeShift) * VIDEO_WIDTH;
664         }
665
666
667         /* Main blitter */
668         int incY = (h0==480) ? 2 : 1;
669         h0=(h0==480 ? 2048 : 1024);
670
671         {
672                 const int li=gpu_unai.ilace_mask;
673                 bool pi = ProgressiveInterlaceEnabled();
674                 bool pif = gpu_unai.prog_ilace_flag;
675                 switch ( w0 )
676                 {
677                         case 256:
678                                 for(int y1=y0+h1; y0<y1; y0+=incY)
679                                 {
680                                         if (( 0 == (y0&li) ) && ((!pi) || (pif=!pif)))
681                                                 GPU_BlitWWDWW(src16 + src16_offs, dst16, isRGB24);
682                                         dst16 += VIDEO_WIDTH;
683                                         src16_offs = (src16_offs + h0) & src16_offs_msk;
684                                 }
685                                 break;
686                         case 368:
687                                 for(int y1=y0+h1; y0<y1; y0+=incY)
688                                 {
689                                         if (( 0 == (y0&li) ) && ((!pi) || (pif=!pif)))
690                                                 GPU_BlitWWWWWWWWS(src16 + src16_offs, dst16, isRGB24, 4);
691                                         dst16 += VIDEO_WIDTH;
692                                         src16_offs = (src16_offs + h0) & src16_offs_msk;
693                                 }
694                                 break;
695                         case 320:
696                                 // Ensure 32-bit alignment for GPU_BlitWW() blitter:
697                                 src16_offs &= ~1;
698                                 for(int y1=y0+h1; y0<y1; y0+=incY)
699                                 {
700                                         if (( 0 == (y0&li) ) && ((!pi) || (pif=!pif)))
701                                                 GPU_BlitWW(src16 + src16_offs, dst16, isRGB24);
702                                         dst16 += VIDEO_WIDTH;
703                                         src16_offs = (src16_offs + h0) & src16_offs_msk;
704                                 }
705                                 break;
706                         case 384:
707                                 for(int y1=y0+h1; y0<y1; y0+=incY)
708                                 {
709                                         if (( 0 == (y0&li) ) && ((!pi) || (pif=!pif)))
710                                                 GPU_BlitWWWWWS(src16 + src16_offs, dst16, isRGB24);
711                                         dst16 += VIDEO_WIDTH;
712                                         src16_offs = (src16_offs + h0) & src16_offs_msk;
713                                 }
714                                 break;
715                         case 512:
716                                 for(int y1=y0+h1; y0<y1; y0+=incY)
717                                 {
718                                         if (( 0 == (y0&li) ) && ((!pi) || (pif=!pif)))
719                                                 GPU_BlitWWSWWSWS(src16 + src16_offs, dst16, isRGB24);
720                                         dst16 += VIDEO_WIDTH;
721                                         src16_offs = (src16_offs + h0) & src16_offs_msk;
722                                 }
723                                 break;
724                         case 640:
725                                 for(int y1=y0+h1; y0<y1; y0+=incY)
726                                 {
727                                         if (( 0 == (y0&li) ) && ((!pi) || (pif=!pif)))
728                                                 GPU_BlitWS(src16 + src16_offs, dst16, isRGB24);
729                                         dst16 += VIDEO_WIDTH;
730                                         src16_offs = (src16_offs + h0) & src16_offs_msk;
731                                 }
732                                 break;
733                 }
734                 gpu_unai.prog_ilace_flag = !gpu_unai.prog_ilace_flag;
735         }
736         video_flip();
737 }
738
739 // Update frames-skip each second>>3 (8 times per second)
740 #define GPU_FRAMESKIP_UPDATE 3
741
742 static void GPU_frameskip (bool show)
743 {
744         u32 now=get_ticks(); // current frame
745
746         // Update frameskip
747         if (gpu_unai.frameskip.skipCount==0) gpu_unai.frameskip.skipFrame=false; // frameskip off
748         else if (gpu_unai.frameskip.skipCount==7) { if (show) gpu_unai.frameskip.skipFrame=!gpu_unai.frameskip.skipFrame; } // frameskip medium
749         else if (gpu_unai.frameskip.skipCount==8) gpu_unai.frameskip.skipFrame=true; // frameskip maximum
750         else
751         {
752                 static u32 spd=100; // speed %
753                 static u32 frames=0; // frames counter
754                 static u32 prev=now; // previous fps calculation
755                 frames++;
756                 if ((now-prev)>=(TPS>>GPU_FRAMESKIP_UPDATE))
757                 {
758                         if (IS_PAL) spd=(frames<<1);
759                         else spd=((frames*1001)/600);
760                         spd<<=GPU_FRAMESKIP_UPDATE;
761                         frames=0;
762                         prev=now;
763                 }
764                 switch(gpu_unai.frameskip.skipCount)
765                 {
766                         case 1: if (spd<50) gpu_unai.frameskip.skipFrame=true; else gpu_unai.frameskip.skipFrame=false; break; // frameskip on (spd<50%)
767                         case 2: if (spd<60) gpu_unai.frameskip.skipFrame=true; else gpu_unai.frameskip.skipFrame=false; break; // frameskip on (spd<60%)
768                         case 3: if (spd<70) gpu_unai.frameskip.skipFrame=true; else gpu_unai.frameskip.skipFrame=false; break; // frameskip on (spd<70%)
769                         case 4: if (spd<80) gpu_unai.frameskip.skipFrame=true; else gpu_unai.frameskip.skipFrame=false; break; // frameskip on (spd<80%)
770                         case 5: if (spd<90) gpu_unai.frameskip.skipFrame=true; else gpu_unai.frameskip.skipFrame=false; break; // frameskip on (spd<90%)
771                 }
772         }
773 }
774
775 ///////////////////////////////////////////////////////////////////////////////
776 void GPU_updateLace(void)
777 {
778         // Interlace bit toggle
779         gpu_unai.GPU_GP1 ^= 0x80000000;
780
781         // Update display?
782         if ((gpu_unai.fb_dirty) && (!gpu_unai.frameskip.wasSkip) && (!(gpu_unai.GPU_GP1&0x00800000)))
783         {
784                 // Display updated
785                 gpuVideoOutput();
786                 GPU_frameskip(true);
787                 #ifdef ENABLE_GPU_LOG_SUPPORT
788                         fprintf(stdout,"GPU_updateLace(UPDATE)\n");
789                 #endif
790         } else {
791                 GPU_frameskip(false);
792                 #ifdef ENABLE_GPU_LOG_SUPPORT
793                         fprintf(stdout,"GPU_updateLace(SKIP)\n");
794                 #endif
795         }
796
797         if ((!gpu_unai.frameskip.skipCount) && (gpu_unai.DisplayArea[3] == 480)) gpu_unai.frameskip.skipGPU=true; // Tekken 3 hack
798
799         gpu_unai.fb_dirty=false;
800         gpu_unai.dma.last_dma = NULL;
801 }
802
803 // Allows frontend to signal plugin to redraw screen after returning to emu
804 void GPU_requestScreenRedraw()
805 {
806         gpu_unai.fb_dirty = true;
807 }
808
809 void GPU_getScreenInfo(GPUScreenInfo_t *sinfo)
810 {
811         bool depth24 = (gpu_unai.GPU_GP1 & 0x00200000 ? true : false);
812         int16_t hres = (uint16_t)gpu_unai.DisplayArea[2];
813         int16_t vres = (uint16_t)gpu_unai.DisplayArea[3];
814         int16_t w = hres; // Original gpu_unai doesn't support width < 100%
815         int16_t h = gpu_unai.DisplayArea[5] - gpu_unai.DisplayArea[4];
816         if (vres == 480)
817                 h *= 2;
818         if (h <= 0 || h > vres)
819                 h = vres;
820
821         sinfo->vram    = (uint8_t*)gpu_unai.vram;
822         sinfo->x       = (uint16_t)gpu_unai.DisplayArea[0];
823         sinfo->y       = (uint16_t)gpu_unai.DisplayArea[1];
824         sinfo->w       = w;
825         sinfo->h       = h;
826         sinfo->hres    = hres;
827         sinfo->vres    = vres;
828         sinfo->depth24 = depth24;
829         sinfo->pal     = IS_PAL;
830 }