gpu: rework dma vs busy timing
[pcsx_rearmed.git] / plugins / gpu_unai / gpu.cpp
CommitLineData
86aad47b 1/***************************************************************************
2* Copyright (C) 2010 PCSX4ALL Team *
3* Copyright (C) 2010 Unai *
030d1121 4* Copyright (C) 2016 Senquack (dansilsby <AT> gmail <DOT> com) *
86aad47b 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
030d1121 22#include <stddef.h>
23#include "plugins.h"
24#include "psxcommon.h"
25//#include "port.h"
26#include "gpu_unai.h"
86aad47b 27
030d1121 28#define VIDEO_WIDTH 320
86aad47b 29
030d1121 30#ifdef TIME_IN_MSEC
31#define TPS 1000
32#else
33#define TPS 1000000
34#endif
86aad47b 35
030d1121 36#define IS_PAL (gpu_unai.GPU_GP1&(0x08<<17))
86aad47b 37
030d1121 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)));
48static u16 GPU_FrameBuffer[(FRAME_BUFFER_SIZE*2 + 4096)/2] __attribute__((aligned(32)));
86aad47b 49
50///////////////////////////////////////////////////////////////////////////////
030d1121 51// GPU fixed point math
52#include "gpu_fixedpoint.h"
86aad47b 53
54///////////////////////////////////////////////////////////////////////////////
030d1121 55// Inner loop driver instantiation file
86aad47b 56#include "gpu_inner.h"
57
86aad47b 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///////////////////////////////////////////////////////////////////////////////
030d1121 79static void gpuReset(void)
86aad47b 80{
030d1121 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();
86aad47b 106}
107
108///////////////////////////////////////////////////////////////////////////////
030d1121 109long GPU_init(void)
86aad47b 110{
111 gpuReset();
030d1121 112
113#ifdef GPU_UNAI_USE_INT_DIV_MULTINV
86aad47b 114 // s_invTable
030d1121 115 for(unsigned int i=1;i<=(1<<TABLE_BITS);++i)
86aad47b 116 {
030d1121 117 s_invTable[i-1]=0x7fffffff/i;
86aad47b 118 }
030d1121 119#endif
120
121 gpu_unai.fb_dirty = true;
122 gpu_unai.dma.last_dma = NULL;
86aad47b 123 return (0);
124}
125
126///////////////////////////////////////////////////////////////////////////////
030d1121 127long GPU_shutdown(void)
86aad47b 128{
030d1121 129 return 0;
86aad47b 130}
131
132///////////////////////////////////////////////////////////////////////////////
030d1121 133long GPU_freeze(u32 bWrite, GPUFreeze_t* p2)
86aad47b 134{
135 if (!p2) return (0);
030d1121 136 if (p2->ulFreezeVersion != 1) return (0);
86aad47b 137
138 if (bWrite)
139 {
030d1121 140 p2->ulStatus = gpu_unai.GPU_GP1;
141 memset(p2->ulControl, 0, sizeof(p2->ulControl));
799a9f26 142 // save resolution and registers for P.E.Op.S. compatibility
030d1121 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);
86aad47b 150 return (1);
151 }
152 else
153 {
030d1121 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);
86aad47b 161 return (1);
162 }
163 return (0);
164}
165
166///////////////////////////////////////////////////////////////////////////////
167// GPU DMA comunication
168
169///////////////////////////////////////////////////////////////////////////////
170u8 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///////////////////////////////////////////////////////////////////////////////
191INLINE void gpuSendPacket()
192{
030d1121 193 gpuSendPacketFunction(gpu_unai.PacketBuffer.U4[0]>>24);
86aad47b 194}
195
196///////////////////////////////////////////////////////////////////////////////
197INLINE void gpuCheckPacket(u32 uData)
198{
030d1121 199 if (gpu_unai.PacketCount)
86aad47b 200 {
030d1121 201 gpu_unai.PacketBuffer.U4[gpu_unai.PacketIndex++] = uData;
202 --gpu_unai.PacketCount;
86aad47b 203 }
204 else
205 {
030d1121 206 gpu_unai.PacketBuffer.U4[0] = uData;
207 gpu_unai.PacketCount = PacketSize[uData >> 24];
208 gpu_unai.PacketIndex = 1;
86aad47b 209 }
030d1121 210 if (!gpu_unai.PacketCount) gpuSendPacket();
86aad47b 211}
212
213///////////////////////////////////////////////////////////////////////////////
030d1121 214void GPU_writeDataMem(u32* dmaAddress, int dmaCount)
86aad47b 215{
030d1121 216 #ifdef ENABLE_GPU_LOG_SUPPORT
217 fprintf(stdout,"GPU_writeDataMem(%d)\n",dmaCount);
218 #endif
86aad47b 219 u32 data;
030d1121 220 const u16 *VIDEO_END = (u16*)gpu_unai.vram+(FRAME_BUFFER_SIZE/2)-1;
221 gpu_unai.GPU_GP1 &= ~0x14000000;
86aad47b 222
223 while (dmaCount)
224 {
030d1121 225 if (gpu_unai.dma.FrameToWrite)
86aad47b 226 {
adc3fd64 227 while (dmaCount)
86aad47b 228 {
adc3fd64 229 dmaCount--;
86aad47b 230 data = *dmaAddress++;
030d1121 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)
86aad47b 234 {
030d1121 235 gpu_unai.dma.px = 0;
236 gpu_unai.dma.pvram += 1024;
237 if (++gpu_unai.dma.py >= gpu_unai.dma.y_end)
86aad47b 238 {
030d1121 239 gpu_unai.dma.FrameToWrite = false;
240 gpu_unai.GPU_GP1 &= ~0x08000000;
241 gpu_unai.fb_dirty = true;
86aad47b 242 break;
243 }
244 }
030d1121 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)
86aad47b 248 {
030d1121 249 gpu_unai.dma.px = 0;
250 gpu_unai.dma.pvram += 1024;
251 if (++gpu_unai.dma.py >= gpu_unai.dma.y_end)
86aad47b 252 {
030d1121 253 gpu_unai.dma.FrameToWrite = false;
254 gpu_unai.GPU_GP1 &= ~0x08000000;
255 gpu_unai.fb_dirty = true;
86aad47b 256 break;
257 }
258 }
259 }
260 }
261 else
262 {
263 data = *dmaAddress++;
264 dmaCount--;
265 gpuCheckPacket(data);
266 }
267 }
268
030d1121 269 gpu_unai.GPU_GP1 = (gpu_unai.GPU_GP1 | 0x14000000) & ~0x60000000;
86aad47b 270}
271
030d1121 272long GPU_dmaChain(u32 *rambase, u32 start_addr)
86aad47b 273{
030d1121 274 #ifdef ENABLE_GPU_LOG_SUPPORT
275 fprintf(stdout,"GPU_dmaChain(0x%x)\n",start_addr);
276 #endif
86aad47b 277
030d1121 278 u32 addr, *list;
279 u32 len, count;
b03e0caf 280 long dma_words = 0;
86aad47b 281
030d1121 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++)
86aad47b 288 {
030d1121 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 }
86aad47b 307 }
030d1121 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;
b03e0caf 322
323 return dma_words;
86aad47b 324}
325
326///////////////////////////////////////////////////////////////////////////////
030d1121 327void GPU_writeData(u32 data)
86aad47b 328{
030d1121 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;
86aad47b 334
030d1121 335 if (gpu_unai.dma.FrameToWrite)
86aad47b 336 {
030d1121 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)
86aad47b 340 {
030d1121 341 gpu_unai.dma.px = 0;
342 gpu_unai.dma.pvram += 1024;
343 if (++gpu_unai.dma.py >= gpu_unai.dma.y_end)
86aad47b 344 {
030d1121 345 gpu_unai.dma.FrameToWrite = false;
346 gpu_unai.GPU_GP1 &= ~0x08000000;
347 gpu_unai.fb_dirty = true;
86aad47b 348 }
349 }
030d1121 350 if (gpu_unai.dma.FrameToWrite)
86aad47b 351 {
030d1121 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)
86aad47b 355 {
030d1121 356 gpu_unai.dma.px = 0;
357 gpu_unai.dma.pvram += 1024;
358 if (++gpu_unai.dma.py >= gpu_unai.dma.y_end)
86aad47b 359 {
030d1121 360 gpu_unai.dma.FrameToWrite = false;
361 gpu_unai.GPU_GP1 &= ~0x08000000;
362 gpu_unai.fb_dirty = true;
86aad47b 363 }
364 }
365 }
366 }
367 else
368 {
369 gpuCheckPacket(data);
370 }
030d1121 371 gpu_unai.GPU_GP1 |= 0x14000000;
86aad47b 372}
373
374
375///////////////////////////////////////////////////////////////////////////////
030d1121 376void GPU_readDataMem(u32* dmaAddress, int dmaCount)
86aad47b 377{
030d1121 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;
86aad47b 383
030d1121 384 gpu_unai.GPU_GP1 &= ~0x14000000;
86aad47b 385 do
386 {
030d1121 387 if ((&gpu_unai.dma.pvram[gpu_unai.dma.px])>(VIDEO_END)) gpu_unai.dma.pvram-=512*1024;
86aad47b 388 // lower 16 bit
030d1121 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];
86aad47b 392
030d1121 393 if (++gpu_unai.dma.px >= gpu_unai.dma.x_end)
86aad47b 394 {
030d1121 395 gpu_unai.dma.px = 0;
396 gpu_unai.dma.pvram += 1024;
86aad47b 397 }
398
030d1121 399 if ((&gpu_unai.dma.pvram[gpu_unai.dma.px])>(VIDEO_END)) gpu_unai.dma.pvram-=512*1024;
86aad47b 400 // higher 16 bit (always, even if it's an odd width)
030d1121 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;
86aad47b 404
405 *dmaAddress++ = data;
406
030d1121 407 if (++gpu_unai.dma.px >= gpu_unai.dma.x_end)
86aad47b 408 {
030d1121 409 gpu_unai.dma.px = 0;
410 gpu_unai.dma.pvram += 1024;
411 if (++gpu_unai.dma.py >= gpu_unai.dma.y_end)
86aad47b 412 {
030d1121 413 gpu_unai.dma.FrameToRead = false;
414 gpu_unai.GPU_GP1 &= ~0x08000000;
86aad47b 415 break;
416 }
417 }
418 } while (--dmaCount);
419
030d1121 420 gpu_unai.GPU_GP1 = (gpu_unai.GPU_GP1 | 0x14000000) & ~0x60000000;
86aad47b 421}
422
423
424
425///////////////////////////////////////////////////////////////////////////////
030d1121 426u32 GPU_readData(void)
86aad47b 427{
030d1121 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)
86aad47b 434 {
030d1121 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)
86aad47b 438 {
030d1121 439 gpu_unai.dma.px = 0;
440 gpu_unai.dma.pvram += 1024;
441 if (++gpu_unai.dma.py >= gpu_unai.dma.y_end)
86aad47b 442 {
030d1121 443 gpu_unai.dma.FrameToRead = false;
444 gpu_unai.GPU_GP1 &= ~0x08000000;
86aad47b 445 }
446 }
030d1121 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)
86aad47b 450 {
030d1121 451 gpu_unai.dma.px = 0;
452 gpu_unai.dma.pvram += 1024;
453 if (++gpu_unai.dma.py >= gpu_unai.dma.y_end)
86aad47b 454 {
030d1121 455 gpu_unai.dma.FrameToRead = false;
456 gpu_unai.GPU_GP1 &= ~0x08000000;
86aad47b 457 }
458 }
459
460 }
030d1121 461 gpu_unai.GPU_GP1 |= 0x14000000;
86aad47b 462
030d1121 463 return (gpu_unai.GPU_GP0);
86aad47b 464}
465
466///////////////////////////////////////////////////////////////////////////////
030d1121 467u32 GPU_readStatus(void)
86aad47b 468{
030d1121 469 return gpu_unai.GPU_GP1;
470}
471
472INLINE 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 }
86aad47b 488}
489
490///////////////////////////////////////////////////////////////////////////////
491void GPU_writeStatus(u32 data)
492{
030d1121 493 #ifdef ENABLE_GPU_LOG_SUPPORT
494 fprintf(stdout,"GPU_writeStatus(%d,%d)\n",data>>24,data & 0xff);
495 #endif
86aad47b 496 switch (data >> 24) {
497 case 0x00:
498 gpuReset();
499 break;
500 case 0x01:
030d1121 501 gpu_unai.GPU_GP1 &= ~0x08000000;
502 gpu_unai.PacketCount = 0;
503 gpu_unai.dma.FrameToRead = gpu_unai.dma.FrameToWrite = false;
86aad47b 504 break;
505 case 0x02:
030d1121 506 gpu_unai.GPU_GP1 &= ~0x08000000;
507 gpu_unai.PacketCount = 0;
508 gpu_unai.dma.FrameToRead = gpu_unai.dma.FrameToWrite = false;
86aad47b 509 break;
510 case 0x03:
030d1121 511 gpu_unai.GPU_GP1 = (gpu_unai.GPU_GP1 & ~0x00800000) | ((data & 1) << 23);
86aad47b 512 break;
513 case 0x04:
030d1121 514 if (data == 0x04000000) gpu_unai.PacketCount = 0;
515 gpu_unai.GPU_GP1 = (gpu_unai.GPU_GP1 & ~0x60000000) | ((data & 3) << 29);
86aad47b 516 break;
517 case 0x05:
030d1121 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.
86aad47b 534 break;
535 case 0x07:
030d1121 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 }
86aad47b 553 break;
554 case 0x08:
555 {
030d1121 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
86aad47b 613 }
614 break;
615 case 0x10:
030d1121 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;
86aad47b 623 }
624 break;
625 }
86aad47b 626}
627
628// Blitting functions
629#include "gpu_blit.h"
630
030d1121 631static void gpuVideoOutput(void)
86aad47b 632{
030d1121 633 int h0, x0, y0, w0, h1;
86aad47b 634
030d1121 635 x0 = gpu_unai.DisplayArea[0];
636 y0 = gpu_unai.DisplayArea[1];
86aad47b 637
030d1121 638 w0 = gpu_unai.DisplayArea[2];
639 h0 = gpu_unai.DisplayArea[3]; // video mode
86aad47b 640
030d1121 641 h1 = gpu_unai.DisplayArea[5] - gpu_unai.DisplayArea[4]; // display needed
86aad47b 642 if (h0 == 480) h1 = Min2(h1*2,480);
643
030d1121 644 bool isRGB24 = (gpu_unai.GPU_GP1 & 0x00200000 ? true : false);
645 u16* dst16 = SCREEN;
646 u16* src16 = (u16*)gpu_unai.vram;
86aad47b 647
030d1121 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;
86aad47b 651
652 // Height centering
653 int sizeShift = 1;
030d1121 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
86aad47b 666
667 /* Main blitter */
668 int incY = (h0==480) ? 2 : 1;
669 h0=(h0==480 ? 2048 : 1024);
670
86aad47b 671 {
030d1121 672 const int li=gpu_unai.ilace_mask;
673 bool pi = ProgressiveInterlaceEnabled();
674 bool pif = gpu_unai.prog_ilace_flag;
86aad47b 675 switch ( w0 )
676 {
677 case 256:
678 for(int y1=y0+h1; y0<y1; y0+=incY)
679 {
030d1121 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;
86aad47b 684 }
685 break;
686 case 368:
687 for(int y1=y0+h1; y0<y1; y0+=incY)
688 {
030d1121 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;
86aad47b 693 }
694 break;
695 case 320:
030d1121 696 // Ensure 32-bit alignment for GPU_BlitWW() blitter:
697 src16_offs &= ~1;
86aad47b 698 for(int y1=y0+h1; y0<y1; y0+=incY)
699 {
030d1121 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;
86aad47b 704 }
705 break;
706 case 384:
707 for(int y1=y0+h1; y0<y1; y0+=incY)
708 {
030d1121 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;
86aad47b 713 }
714 break;
715 case 512:
716 for(int y1=y0+h1; y0<y1; y0+=incY)
717 {
030d1121 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;
86aad47b 722 }
723 break;
724 case 640:
725 for(int y1=y0+h1; y0<y1; y0+=incY)
726 {
030d1121 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;
86aad47b 731 }
732 break;
733 }
030d1121 734 gpu_unai.prog_ilace_flag = !gpu_unai.prog_ilace_flag;
86aad47b 735 }
736 video_flip();
737}
738
030d1121 739// Update frames-skip each second>>3 (8 times per second)
740#define GPU_FRAMESKIP_UPDATE 3
86aad47b 741
030d1121 742static void GPU_frameskip (bool show)
743{
744 u32 now=get_ticks(); // current frame
86aad47b 745
030d1121 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
86aad47b 751 {
030d1121 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))
86aad47b 757 {
030d1121 758 if (IS_PAL) spd=(frames<<1);
759 else spd=((frames*1001)/600);
760 spd<<=GPU_FRAMESKIP_UPDATE;
761 frames=0;
86aad47b 762 prev=now;
86aad47b 763 }
030d1121 764 switch(gpu_unai.frameskip.skipCount)
86aad47b 765 {
030d1121 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%)
86aad47b 771 }
772 }
55b0eeea 773}
774
030d1121 775///////////////////////////////////////////////////////////////////////////////
7c49c8a2 776void GPU_updateLace(void)
777{
778 // Interlace bit toggle
030d1121 779 gpu_unai.GPU_GP1 ^= 0x80000000;
7c49c8a2 780
030d1121 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
c006d9e3 795 }
796
030d1121 797 if ((!gpu_unai.frameskip.skipCount) && (gpu_unai.DisplayArea[3] == 480)) gpu_unai.frameskip.skipGPU=true; // Tekken 3 hack
7c49c8a2 798
030d1121 799 gpu_unai.fb_dirty=false;
800 gpu_unai.dma.last_dma = NULL;
7c49c8a2 801}
802
030d1121 803// Allows frontend to signal plugin to redraw screen after returning to emu
804void GPU_requestScreenRedraw()
7c49c8a2 805{
030d1121 806 gpu_unai.fb_dirty = true;
7c49c8a2 807}
808
030d1121 809void GPU_getScreenInfo(GPUScreenInfo_t *sinfo)
7c49c8a2 810{
030d1121 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;
7c49c8a2 830}