gpulib: trust game's centering based on a database
[pcsx_rearmed.git] / plugins / gpulib / gpu.c
CommitLineData
1ab64c54 1/*
05740673 2 * (C) GraÅžvydas "notaz" Ignotas, 2011-2012
1ab64c54
GI
3 *
4 * This work is licensed under the terms of any of these licenses
5 * (at your option):
6 * - GNU GPL, version 2 or later.
7 * - GNU LGPL, version 2.1 or later.
8 * See the COPYING file in the top-level directory.
9 */
10
d30279e2 11#include <stdio.h>
308c6e67 12#include <stdlib.h>
1ab64c54 13#include <string.h>
56f08d83 14#include "gpu.h"
abf09485 15#include "../../libpcsxcore/gpu.h" // meh
44e76f8a 16#include "../../frontend/plugin_lib.h"
1ab64c54 17
44e76f8a 18#ifndef ARRAY_SIZE
1ab64c54 19#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
44e76f8a 20#endif
8f5f2dd5 21#ifdef __GNUC__
d30279e2 22#define unlikely(x) __builtin_expect((x), 0)
8f5f2dd5 23#define preload __builtin_prefetch
8dd855cd 24#define noinline __attribute__((noinline))
8f5f2dd5 25#else
26#define unlikely(x)
27#define preload(...)
28#define noinline
8f5f2dd5 29#endif
1ab64c54 30
deb18d24 31//#define log_io gpu_log
56f08d83 32#define log_io(...)
56f08d83 33
9ee0fd5b 34struct psx_gpu gpu;
1ab64c54 35
48f3d210 36static noinline int do_cmd_buffer(uint32_t *data, int count);
05740673 37static void finish_vram_transfer(int is_read);
48f3d210 38
39static noinline void do_cmd_reset(void)
40{
41 if (unlikely(gpu.cmd_len > 0))
42 do_cmd_buffer(gpu.cmd_buffer, gpu.cmd_len);
48f3d210 43 gpu.cmd_len = 0;
05740673 44
45 if (unlikely(gpu.dma.h > 0))
46 finish_vram_transfer(gpu.dma_start.is_read);
48f3d210 47 gpu.dma.h = 0;
48}
49
6e9bdaef 50static noinline void do_reset(void)
1ab64c54 51{
7841712d 52 unsigned int i;
48f3d210 53
54 do_cmd_reset();
55
6e9bdaef 56 memset(gpu.regs, 0, sizeof(gpu.regs));
48f3d210 57 for (i = 0; i < sizeof(gpu.ex_regs) / sizeof(gpu.ex_regs[0]); i++)
58 gpu.ex_regs[i] = (0xe0 + i) << 24;
f23b103c 59 gpu.status = 0x14802000;
6e9bdaef 60 gpu.gp0 = 0;
fc84f618 61 gpu.regs[3] = 1;
6e9bdaef 62 gpu.screen.hres = gpu.screen.w = 256;
fc84f618 63 gpu.screen.vres = gpu.screen.h = 240;
308c6e67 64 gpu.screen.x = gpu.screen.y = 0;
5fe1a2b1 65 renderer_sync_ecmds(gpu.ex_regs);
0b4038f8 66 renderer_notify_res_change();
1ab64c54
GI
67}
68
8dd855cd 69static noinline void update_width(void)
70{
308c6e67 71 static const short hres_all[8] = { 256, 368, 320, 368, 512, 368, 640, 368 };
72 static const uint8_t hdivs[8] = { 10, 7, 8, 7, 5, 7, 4, 7 };
73 uint8_t hdiv = hdivs[(gpu.status >> 16) & 7];
74 int hres = hres_all[(gpu.status >> 16) & 7];
75 int pal = gpu.status & PSX_GPU_STATUS_PAL;
8dd855cd 76 int sw = gpu.screen.x2 - gpu.screen.x1;
9ed80467 77 int type = gpu.state.screen_centering_type;
308c6e67 78 int x = 0, x_auto;
9ed80467 79 if (type == C_AUTO)
80 type = gpu.state.screen_centering_type_default;
308c6e67 81 if (sw <= 0)
82 /* nothing displayed? */;
83 else {
84 int s = pal ? 656 : 608; // or 600? pal is just a guess
85 x = (gpu.screen.x1 - s) / hdiv;
86 x = (x + 1) & ~1; // blitter limitation
87 sw /= hdiv;
88 sw = (sw + 2) & ~3; // according to nocash
9ed80467 89 switch (type) {
44e76f8a 90 case C_INGAME:
308c6e67 91 break;
44e76f8a 92 case C_MANUAL:
308c6e67 93 x = gpu.state.screen_centering_x;
94 break;
95 default:
96 // correct if slightly miscentered
97 x_auto = (hres - sw) / 2 & ~3;
98 if ((uint32_t)x_auto <= 8u && abs(x) < 24)
99 x = x_auto;
100 }
101 if (x + sw > hres)
102 sw = hres - x;
103 // .x range check is done in vout_update()
104 }
105 // reduce the unpleasant right border that a few games have
106 if (gpu.state.screen_centering_type == 0
107 && x <= 4 && hres - (x + sw) >= 4)
108 hres -= 4;
109 gpu.screen.x = x;
110 gpu.screen.w = sw;
111 gpu.screen.hres = hres;
112 gpu.state.dims_changed = 1;
113 //printf("xx %d %d -> %2d, %d / %d\n",
114 // gpu.screen.x1, gpu.screen.x2, x, sw, hres);
8dd855cd 115}
116
117static noinline void update_height(void)
118{
308c6e67 119 int pal = gpu.status & PSX_GPU_STATUS_PAL;
120 int dheight = gpu.status & PSX_GPU_STATUS_DHEIGHT;
121 int y = gpu.screen.y1 - (pal ? 39 : 16); // 39 for spyro
8dd855cd 122 int sh = gpu.screen.y2 - gpu.screen.y1;
308c6e67 123 int center_tol = 16;
124 int vres = 240;
125
126 if (pal && (sh > 240 || gpu.screen.vres == 256))
127 vres = 256;
128 if (dheight)
129 y *= 2, sh *= 2, vres *= 2, center_tol *= 2;
130 if (sh <= 0)
131 /* nothing displayed? */;
132 else {
133 switch (gpu.state.screen_centering_type) {
44e76f8a 134 case C_INGAME:
135 break;
136 case C_BORDERLESS:
137 y = 0;
308c6e67 138 break;
44e76f8a 139 case C_MANUAL:
308c6e67 140 y = gpu.state.screen_centering_y;
141 break;
142 default:
143 // correct if slightly miscentered
144 if ((uint32_t)(vres - sh) <= 1 && abs(y) <= center_tol)
145 y = 0;
146 }
147 if (y + sh > vres)
148 sh = vres - y;
149 }
150 gpu.screen.y = y;
8dd855cd 151 gpu.screen.h = sh;
308c6e67 152 gpu.screen.vres = vres;
153 gpu.state.dims_changed = 1;
154 //printf("yy %d %d -> %d, %d / %d\n",
155 // gpu.screen.y1, gpu.screen.y2, y, sh, vres);
8dd855cd 156}
157
fc84f618 158static noinline void decide_frameskip(void)
159{
9fe27e25 160 if (gpu.frameskip.active)
161 gpu.frameskip.cnt++;
162 else {
163 gpu.frameskip.cnt = 0;
164 gpu.frameskip.frame_ready = 1;
165 }
fc84f618 166
9fe27e25 167 if (!gpu.frameskip.active && *gpu.frameskip.advice)
168 gpu.frameskip.active = 1;
169 else if (gpu.frameskip.set > 0 && gpu.frameskip.cnt < gpu.frameskip.set)
fc84f618 170 gpu.frameskip.active = 1;
171 else
172 gpu.frameskip.active = 0;
fbb4bfff 173
174 if (!gpu.frameskip.active && gpu.frameskip.pending_fill[0] != 0) {
175 int dummy;
176 do_cmd_list(gpu.frameskip.pending_fill, 3, &dummy);
177 gpu.frameskip.pending_fill[0] = 0;
178 }
fc84f618 179}
180
b243416b 181static noinline int decide_frameskip_allow(uint32_t cmd_e3)
9fe27e25 182{
183 // no frameskip if it decides to draw to display area,
184 // but not for interlace since it'll most likely always do that
185 uint32_t x = cmd_e3 & 0x3ff;
186 uint32_t y = (cmd_e3 >> 10) & 0x3ff;
f23b103c 187 gpu.frameskip.allow = (gpu.status & PSX_GPU_STATUS_INTERLACE) ||
308c6e67 188 (uint32_t)(x - gpu.screen.src_x) >= (uint32_t)gpu.screen.w ||
189 (uint32_t)(y - gpu.screen.src_y) >= (uint32_t)gpu.screen.h;
b243416b 190 return gpu.frameskip.allow;
9fe27e25 191}
192
5fe1a2b1 193static void flush_cmd_buffer(void);
194
6e9bdaef 195static noinline void get_gpu_info(uint32_t data)
196{
5fe1a2b1 197 if (unlikely(gpu.cmd_len > 0))
198 flush_cmd_buffer();
6e9bdaef 199 switch (data & 0x0f) {
200 case 0x02:
201 case 0x03:
202 case 0x04:
6e9bdaef 203 gpu.gp0 = gpu.ex_regs[data & 7] & 0xfffff;
204 break;
d04b8924 205 case 0x05:
206 gpu.gp0 = gpu.ex_regs[5] & 0x3fffff;
6e9bdaef 207 break;
208 case 0x07:
209 gpu.gp0 = 2;
210 break;
211 default:
d04b8924 212 // gpu.gp0 unchanged
6e9bdaef 213 break;
214 }
215}
216
9ee0fd5b 217// double, for overdraw guard
218#define VRAM_SIZE (1024 * 512 * 2 * 2)
219
220static int map_vram(void)
221{
222 gpu.vram = gpu.mmap(VRAM_SIZE);
223 if (gpu.vram != NULL) {
224 gpu.vram += 4096 / 2;
225 return 0;
226 }
227 else {
228 fprintf(stderr, "could not map vram, expect crashes\n");
229 return -1;
230 }
231}
232
6e9bdaef 233long GPUinit(void)
234{
9394ada5 235 int ret;
236 ret = vout_init();
237 ret |= renderer_init();
238
0b4038f8 239 memset(&gpu.state, 0, sizeof(gpu.state));
240 memset(&gpu.frameskip, 0, sizeof(gpu.frameskip));
241 gpu.zero = 0;
3ece2f0c 242 gpu.state.frame_count = &gpu.zero;
deb18d24 243 gpu.state.hcnt = &gpu.zero;
48f3d210 244 gpu.cmd_len = 0;
9394ada5 245 do_reset();
48f3d210 246
9ee0fd5b 247 if (gpu.mmap != NULL) {
248 if (map_vram() != 0)
249 ret = -1;
250 }
6e9bdaef 251 return ret;
252}
253
254long GPUshutdown(void)
255{
9ee0fd5b 256 long ret;
257
e929dec5 258 renderer_finish();
9ee0fd5b 259 ret = vout_finish();
260 if (gpu.vram != NULL) {
261 gpu.vram -= 4096 / 2;
262 gpu.munmap(gpu.vram, VRAM_SIZE);
263 }
264 gpu.vram = NULL;
265
266 return ret;
6e9bdaef 267}
268
1ab64c54
GI
269void GPUwriteStatus(uint32_t data)
270{
1ab64c54 271 uint32_t cmd = data >> 24;
2da2fc76 272 int src_x, src_y;
1ab64c54 273
fc84f618 274 if (cmd < ARRAY_SIZE(gpu.regs)) {
48f3d210 275 if (cmd > 1 && cmd != 5 && gpu.regs[cmd] == data)
fc84f618 276 return;
8dd855cd 277 gpu.regs[cmd] = data;
fc84f618 278 }
279
280 gpu.state.fb_dirty = 1;
8dd855cd 281
282 switch (cmd) {
1ab64c54 283 case 0x00:
6e9bdaef 284 do_reset();
1ab64c54 285 break;
48f3d210 286 case 0x01:
287 do_cmd_reset();
288 break;
1ab64c54 289 case 0x03:
308c6e67 290 if (data & 1) {
f23b103c 291 gpu.status |= PSX_GPU_STATUS_BLANKING;
308c6e67 292 gpu.state.dims_changed = 1; // for hud clearing
293 }
f23b103c
PC
294 else
295 gpu.status &= ~PSX_GPU_STATUS_BLANKING;
1ab64c54
GI
296 break;
297 case 0x04:
f23b103c
PC
298 gpu.status &= ~PSX_GPU_STATUS_DMA_MASK;
299 gpu.status |= PSX_GPU_STATUS_DMA(data & 3);
1ab64c54
GI
300 break;
301 case 0x05:
2da2fc76 302 src_x = data & 0x3ff; src_y = (data >> 10) & 0x1ff;
303 if (src_x != gpu.screen.src_x || src_y != gpu.screen.src_y) {
304 gpu.screen.src_x = src_x;
305 gpu.screen.src_y = src_y;
306 renderer_notify_scanout_change(src_x, src_y);
307 if (gpu.frameskip.set) {
308 decide_frameskip_allow(gpu.ex_regs[3]);
309 if (gpu.frameskip.last_flip_frame != *gpu.state.frame_count) {
310 decide_frameskip();
311 gpu.frameskip.last_flip_frame = *gpu.state.frame_count;
312 }
9fe27e25 313 }
fb4c6fba 314 }
1ab64c54 315 break;
8dd855cd 316 case 0x06:
317 gpu.screen.x1 = data & 0xfff;
318 gpu.screen.x2 = (data >> 12) & 0xfff;
319 update_width();
320 break;
1ab64c54
GI
321 case 0x07:
322 gpu.screen.y1 = data & 0x3ff;
323 gpu.screen.y2 = (data >> 10) & 0x3ff;
8dd855cd 324 update_height();
1ab64c54
GI
325 break;
326 case 0x08:
f23b103c 327 gpu.status = (gpu.status & ~0x7f0000) | ((data & 0x3F) << 17) | ((data & 0x40) << 10);
8dd855cd 328 update_width();
329 update_height();
e929dec5 330 renderer_notify_res_change();
1ab64c54 331 break;
deb18d24 332 default:
333 if ((cmd & 0xf0) == 0x10)
334 get_gpu_info(data);
6e9bdaef 335 break;
1ab64c54 336 }
7890a708 337
338#ifdef GPUwriteStatus_ext
339 GPUwriteStatus_ext(data);
340#endif
1ab64c54
GI
341}
342
56f08d83 343const unsigned char cmd_lengths[256] =
1ab64c54 344{
d30279e2
GI
345 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
346 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
347 3, 3, 3, 3, 6, 6, 6, 6, 4, 4, 4, 4, 8, 8, 8, 8, // 20
348 5, 5, 5, 5, 8, 8, 8, 8, 7, 7, 7, 7, 11, 11, 11, 11,
652c6b8b 349 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, // 40
350 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4,
351 2, 2, 2, 2, 3, 3, 3, 3, 1, 1, 1, 1, 0, 0, 0, 0, // 60
d30279e2 352 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 2, 2, 2, 2,
72583812 353 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 80
354 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
355 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // a0
356 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
357 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // c0
358 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
d30279e2
GI
359 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // e0
360 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
361};
362
d30279e2
GI
363#define VRAM_MEM_XY(x, y) &gpu.vram[(y) * 1024 + (x)]
364
72583812 365static void cpy_msb(uint16_t *dst, const uint16_t *src, int l, uint16_t msb)
366{
367 int i;
368 for (i = 0; i < l; i++)
369 dst[i] = src[i] | msb;
370}
371
372static inline void do_vram_line(int x, int y, uint16_t *mem, int l,
373 int is_read, uint16_t msb)
1ab64c54 374{
d30279e2 375 uint16_t *vram = VRAM_MEM_XY(x, y);
72583812 376 if (unlikely(is_read))
d30279e2 377 memcpy(mem, vram, l * 2);
72583812 378 else if (unlikely(msb))
379 cpy_msb(vram, mem, l, msb);
d30279e2
GI
380 else
381 memcpy(vram, mem, l * 2);
382}
383
384static int do_vram_io(uint32_t *data, int count, int is_read)
385{
386 int count_initial = count;
72583812 387 uint16_t msb = gpu.ex_regs[6] << 15;
d30279e2
GI
388 uint16_t *sdata = (uint16_t *)data;
389 int x = gpu.dma.x, y = gpu.dma.y;
390 int w = gpu.dma.w, h = gpu.dma.h;
ddd56f6e 391 int o = gpu.dma.offset;
d30279e2
GI
392 int l;
393 count *= 2; // operate in 16bpp pixels
394
395 if (gpu.dma.offset) {
396 l = w - gpu.dma.offset;
ddd56f6e 397 if (count < l)
d30279e2 398 l = count;
ddd56f6e 399
72583812 400 do_vram_line(x + o, y, sdata, l, is_read, msb);
ddd56f6e 401
402 if (o + l < w)
403 o += l;
404 else {
405 o = 0;
406 y++;
407 h--;
408 }
d30279e2
GI
409 sdata += l;
410 count -= l;
d30279e2
GI
411 }
412
413 for (; h > 0 && count >= w; sdata += w, count -= w, y++, h--) {
414 y &= 511;
72583812 415 do_vram_line(x, y, sdata, w, is_read, msb);
d30279e2
GI
416 }
417
05740673 418 if (h > 0) {
419 if (count > 0) {
420 y &= 511;
72583812 421 do_vram_line(x, y, sdata, count, is_read, msb);
05740673 422 o = count;
423 count = 0;
424 }
d30279e2 425 }
05740673 426 else
427 finish_vram_transfer(is_read);
d30279e2
GI
428 gpu.dma.y = y;
429 gpu.dma.h = h;
ddd56f6e 430 gpu.dma.offset = o;
d30279e2 431
6e9bdaef 432 return count_initial - count / 2;
d30279e2
GI
433}
434
435static void start_vram_transfer(uint32_t pos_word, uint32_t size_word, int is_read)
436{
ddd56f6e 437 if (gpu.dma.h)
438 log_anomaly("start_vram_transfer while old unfinished\n");
439
5440b88e 440 gpu.dma.x = pos_word & 0x3ff;
441 gpu.dma.y = (pos_word >> 16) & 0x1ff;
48f3d210 442 gpu.dma.w = ((size_word - 1) & 0x3ff) + 1;
443 gpu.dma.h = (((size_word >> 16) - 1) & 0x1ff) + 1;
d30279e2 444 gpu.dma.offset = 0;
05740673 445 gpu.dma.is_read = is_read;
446 gpu.dma_start = gpu.dma;
d30279e2 447
9e146206 448 renderer_flush_queues();
449 if (is_read) {
f23b103c 450 gpu.status |= PSX_GPU_STATUS_IMG;
9e146206 451 // XXX: wrong for width 1
ae097dfb 452 gpu.gp0 = LE32TOH(*(uint32_t *) VRAM_MEM_XY(gpu.dma.x, gpu.dma.y));
5440b88e 453 gpu.state.last_vram_read_frame = *gpu.state.frame_count;
9e146206 454 }
d30279e2 455
6e9bdaef 456 log_io("start_vram_transfer %c (%d, %d) %dx%d\n", is_read ? 'r' : 'w',
457 gpu.dma.x, gpu.dma.y, gpu.dma.w, gpu.dma.h);
abf09485 458 if (gpu.gpu_state_change)
459 gpu.gpu_state_change(PGS_VRAM_TRANSFER_START);
d30279e2
GI
460}
461
05740673 462static void finish_vram_transfer(int is_read)
463{
464 if (is_read)
f23b103c 465 gpu.status &= ~PSX_GPU_STATUS_IMG;
893f780e 466 else {
467 gpu.state.fb_dirty = 1;
05740673 468 renderer_update_caches(gpu.dma_start.x, gpu.dma_start.y,
0b4038f8 469 gpu.dma_start.w, gpu.dma_start.h, 0);
893f780e 470 }
abf09485 471 if (gpu.gpu_state_change)
472 gpu.gpu_state_change(PGS_VRAM_TRANSFER_END);
05740673 473}
474
72583812 475static void do_vram_copy(const uint32_t *params)
476{
477 const uint32_t sx = LE32TOH(params[0]) & 0x3FF;
478 const uint32_t sy = (LE32TOH(params[0]) >> 16) & 0x1FF;
479 const uint32_t dx = LE32TOH(params[1]) & 0x3FF;
480 const uint32_t dy = (LE32TOH(params[1]) >> 16) & 0x1FF;
481 uint32_t w = ((LE32TOH(params[2]) - 1) & 0x3FF) + 1;
482 uint32_t h = (((LE32TOH(params[2]) >> 16) - 1) & 0x1FF) + 1;
483 uint16_t msb = gpu.ex_regs[6] << 15;
484 uint16_t lbuf[128];
485 uint32_t x, y;
486
487 if (sx == dx && sy == dy && msb == 0)
488 return;
489
490 renderer_flush_queues();
491
492 if (unlikely((sx < dx && dx < sx + w) || sx + w > 1024 || dx + w > 1024 || msb))
493 {
494 for (y = 0; y < h; y++)
495 {
496 const uint16_t *src = VRAM_MEM_XY(0, (sy + y) & 0x1ff);
497 uint16_t *dst = VRAM_MEM_XY(0, (dy + y) & 0x1ff);
498 for (x = 0; x < w; x += ARRAY_SIZE(lbuf))
499 {
500 uint32_t x1, w1 = w - x;
501 if (w1 > ARRAY_SIZE(lbuf))
502 w1 = ARRAY_SIZE(lbuf);
503 for (x1 = 0; x1 < w1; x1++)
504 lbuf[x1] = src[(sx + x + x1) & 0x3ff];
505 for (x1 = 0; x1 < w1; x1++)
506 dst[(dx + x + x1) & 0x3ff] = lbuf[x1] | msb;
507 }
508 }
509 }
510 else
511 {
512 uint32_t sy1 = sy, dy1 = dy;
513 for (y = 0; y < h; y++, sy1++, dy1++)
514 memcpy(VRAM_MEM_XY(dx, dy1 & 0x1ff), VRAM_MEM_XY(sx, sy1 & 0x1ff), w * 2);
515 }
516
517 renderer_update_caches(dx, dy, w, h, 0);
518}
519
b243416b 520static noinline int do_cmd_list_skip(uint32_t *data, int count, int *last_cmd)
521{
97e07db9 522 int cmd = 0, pos = 0, len, dummy, v;
b243416b 523 int skip = 1;
524
fbb4bfff 525 gpu.frameskip.pending_fill[0] = 0;
526
b243416b 527 while (pos < count && skip) {
528 uint32_t *list = data + pos;
89df80c6 529 cmd = LE32TOH(list[0]) >> 24;
b243416b 530 len = 1 + cmd_lengths[cmd];
531
97e07db9 532 switch (cmd) {
533 case 0x02:
89df80c6 534 if ((LE32TOH(list[2]) & 0x3ff) > gpu.screen.w || ((LE32TOH(list[2]) >> 16) & 0x1ff) > gpu.screen.h)
97e07db9 535 // clearing something large, don't skip
536 do_cmd_list(list, 3, &dummy);
537 else
538 memcpy(gpu.frameskip.pending_fill, list, 3 * 4);
539 break;
540 case 0x24 ... 0x27:
541 case 0x2c ... 0x2f:
542 case 0x34 ... 0x37:
543 case 0x3c ... 0x3f:
544 gpu.ex_regs[1] &= ~0x1ff;
89df80c6 545 gpu.ex_regs[1] |= LE32TOH(list[4 + ((cmd >> 4) & 1)]) & 0x1ff;
97e07db9 546 break;
547 case 0x48 ... 0x4F:
548 for (v = 3; pos + v < count; v++)
549 {
89df80c6 550 if ((list[v] & HTOLE32(0xf000f000)) == HTOLE32(0x50005000))
97e07db9 551 break;
552 }
553 len += v - 3;
554 break;
555 case 0x58 ... 0x5F:
556 for (v = 4; pos + v < count; v += 2)
557 {
89df80c6 558 if ((list[v] & HTOLE32(0xf000f000)) == HTOLE32(0x50005000))
97e07db9 559 break;
560 }
561 len += v - 4;
562 break;
563 default:
564 if (cmd == 0xe3)
89df80c6 565 skip = decide_frameskip_allow(LE32TOH(list[0]));
97e07db9 566 if ((cmd & 0xf8) == 0xe0)
89df80c6 567 gpu.ex_regs[cmd & 7] = LE32TOH(list[0]);
97e07db9 568 break;
b243416b 569 }
b243416b 570
571 if (pos + len > count) {
572 cmd = -1;
573 break; // incomplete cmd
574 }
72583812 575 if (0x80 <= cmd && cmd <= 0xdf)
b243416b 576 break; // image i/o
97e07db9 577
b243416b 578 pos += len;
579 }
580
581 renderer_sync_ecmds(gpu.ex_regs);
582 *last_cmd = cmd;
583 return pos;
584}
585
48f3d210 586static noinline int do_cmd_buffer(uint32_t *data, int count)
d30279e2 587{
b243416b 588 int cmd, pos;
589 uint32_t old_e3 = gpu.ex_regs[3];
fc84f618 590 int vram_dirty = 0;
d30279e2 591
d30279e2 592 // process buffer
b243416b 593 for (pos = 0; pos < count; )
d30279e2 594 {
b243416b 595 if (gpu.dma.h && !gpu.dma_start.is_read) { // XXX: need to verify
596 vram_dirty = 1;
d30279e2 597 pos += do_vram_io(data + pos, count - pos, 0);
ddd56f6e 598 if (pos == count)
599 break;
d30279e2
GI
600 }
601
89df80c6 602 cmd = LE32TOH(data[pos]) >> 24;
97e07db9 603 if (0xa0 <= cmd && cmd <= 0xdf) {
7194a46a
DS
604 if (unlikely((pos+2) >= count)) {
605 // incomplete vram write/read cmd, can't consume yet
606 cmd = -1;
607 break;
608 }
609
d30279e2 610 // consume vram write/read cmd
89df80c6 611 start_vram_transfer(LE32TOH(data[pos + 1]), LE32TOH(data[pos + 2]), (cmd & 0xe0) == 0xc0);
b243416b 612 pos += 3;
613 continue;
d30279e2 614 }
72583812 615 else if ((cmd & 0xe0) == 0x80) {
616 if (unlikely((pos+3) >= count)) {
617 cmd = -1; // incomplete cmd, can't consume yet
618 break;
619 }
620 do_vram_copy(data + pos + 1);
893f780e 621 vram_dirty = 1;
72583812 622 pos += 4;
623 continue;
624 }
b243416b 625
1e07f71d 626 // 0xex cmds might affect frameskip.allow, so pass to do_cmd_list_skip
89df80c6 627 if (gpu.frameskip.active && (gpu.frameskip.allow || ((LE32TOH(data[pos]) >> 24) & 0xf0) == 0xe0))
b243416b 628 pos += do_cmd_list_skip(data + pos, count - pos, &cmd);
629 else {
630 pos += do_cmd_list(data + pos, count - pos, &cmd);
631 vram_dirty = 1;
632 }
633
634 if (cmd == -1)
635 // incomplete cmd
ddd56f6e 636 break;
d30279e2 637 }
ddd56f6e 638
f23b103c
PC
639 gpu.status &= ~0x1fff;
640 gpu.status |= gpu.ex_regs[1] & 0x7ff;
641 gpu.status |= (gpu.ex_regs[6] & 3) << 11;
a3a9f519 642
fc84f618 643 gpu.state.fb_dirty |= vram_dirty;
644
b243416b 645 if (old_e3 != gpu.ex_regs[3])
646 decide_frameskip_allow(gpu.ex_regs[3]);
647
ddd56f6e 648 return count - pos;
d30279e2
GI
649}
650
abf09485 651static noinline void flush_cmd_buffer(void)
d30279e2 652{
48f3d210 653 int left = do_cmd_buffer(gpu.cmd_buffer, gpu.cmd_len);
d30279e2
GI
654 if (left > 0)
655 memmove(gpu.cmd_buffer, gpu.cmd_buffer + gpu.cmd_len - left, left * 4);
abf09485 656 if (left != gpu.cmd_len) {
657 if (!gpu.dma.h && gpu.gpu_state_change)
658 gpu.gpu_state_change(PGS_PRIMITIVE_START);
659 gpu.cmd_len = left;
660 }
1ab64c54
GI
661}
662
663void GPUwriteDataMem(uint32_t *mem, int count)
664{
d30279e2
GI
665 int left;
666
56f08d83 667 log_io("gpu_dma_write %p %d\n", mem, count);
668
d30279e2
GI
669 if (unlikely(gpu.cmd_len > 0))
670 flush_cmd_buffer();
56f08d83 671
48f3d210 672 left = do_cmd_buffer(mem, count);
d30279e2 673 if (left)
56f08d83 674 log_anomaly("GPUwriteDataMem: discarded %d/%d words\n", left, count);
1ab64c54
GI
675}
676
d30279e2 677void GPUwriteData(uint32_t data)
1ab64c54 678{
56f08d83 679 log_io("gpu_write %08x\n", data);
89df80c6 680 gpu.cmd_buffer[gpu.cmd_len++] = HTOLE32(data);
d30279e2
GI
681 if (gpu.cmd_len >= CMD_BUFFER_LEN)
682 flush_cmd_buffer();
1ab64c54
GI
683}
684
8c84ba5f 685long GPUdmaChain(uint32_t *rambase, uint32_t start_addr, uint32_t *progress_addr)
1ab64c54 686{
09159d99 687 uint32_t addr, *list, ld_addr = 0;
ddd56f6e 688 int len, left, count;
1c72b1c2 689 long cpu_cycles = 0;
d30279e2 690
8f5f2dd5 691 preload(rambase + (start_addr & 0x1fffff) / 4);
692
d30279e2
GI
693 if (unlikely(gpu.cmd_len > 0))
694 flush_cmd_buffer();
695
56f08d83 696 log_io("gpu_dma_chain\n");
ddd56f6e 697 addr = start_addr & 0xffffff;
09159d99 698 for (count = 0; (addr & 0x800000) == 0; count++)
ddd56f6e 699 {
ddd56f6e 700 list = rambase + (addr & 0x1fffff) / 4;
89df80c6
PC
701 len = LE32TOH(list[0]) >> 24;
702 addr = LE32TOH(list[0]) & 0xffffff;
8f5f2dd5 703 preload(rambase + (addr & 0x1fffff) / 4);
704
1c72b1c2 705 cpu_cycles += 10;
706 if (len > 0)
707 cpu_cycles += 5 + len;
deb18d24 708
ae36bb28 709 log_io(".chain %08lx #%d+%d\n",
710 (long)(list - rambase) * 4, len, gpu.cmd_len);
711 if (unlikely(gpu.cmd_len > 0)) {
23948df3 712 if (gpu.cmd_len + len > ARRAY_SIZE(gpu.cmd_buffer)) {
713 log_anomaly("cmd_buffer overflow, likely garbage commands\n");
714 gpu.cmd_len = 0;
715 }
ae36bb28 716 memcpy(gpu.cmd_buffer + gpu.cmd_len, list + 1, len * 4);
717 gpu.cmd_len += len;
718 flush_cmd_buffer();
719 continue;
720 }
ddd56f6e 721
56f08d83 722 if (len) {
48f3d210 723 left = do_cmd_buffer(list + 1, len);
ae36bb28 724 if (left) {
725 memcpy(gpu.cmd_buffer, list + 1 + len - left, left * 4);
726 gpu.cmd_len = left;
727 log_anomaly("GPUdmaChain: %d/%d words left\n", left, len);
728 }
56f08d83 729 }
ddd56f6e 730
8c84ba5f 731 if (progress_addr) {
732 *progress_addr = addr;
733 break;
734 }
09159d99 735 #define LD_THRESHOLD (8*1024)
736 if (count >= LD_THRESHOLD) {
737 if (count == LD_THRESHOLD) {
738 ld_addr = addr;
739 continue;
740 }
741
742 // loop detection marker
743 // (bit23 set causes DMA error on real machine, so
744 // unlikely to be ever set by the game)
89df80c6 745 list[0] |= HTOLE32(0x800000);
09159d99 746 }
ddd56f6e 747 }
748
09159d99 749 if (ld_addr != 0) {
750 // remove loop detection markers
751 count -= LD_THRESHOLD + 2;
752 addr = ld_addr & 0x1fffff;
753 while (count-- > 0) {
754 list = rambase + addr / 4;
89df80c6
PC
755 addr = LE32TOH(list[0]) & 0x1fffff;
756 list[0] &= HTOLE32(~0x800000);
09159d99 757 }
d30279e2 758 }
09159d99 759
3ece2f0c 760 gpu.state.last_list.frame = *gpu.state.frame_count;
deb18d24 761 gpu.state.last_list.hcnt = *gpu.state.hcnt;
1c72b1c2 762 gpu.state.last_list.cycles = cpu_cycles;
deb18d24 763 gpu.state.last_list.addr = start_addr;
764
1c72b1c2 765 return cpu_cycles;
1ab64c54
GI
766}
767
d30279e2
GI
768void GPUreadDataMem(uint32_t *mem, int count)
769{
56f08d83 770 log_io("gpu_dma_read %p %d\n", mem, count);
771
d30279e2
GI
772 if (unlikely(gpu.cmd_len > 0))
773 flush_cmd_buffer();
56f08d83 774
d30279e2
GI
775 if (gpu.dma.h)
776 do_vram_io(mem, count, 1);
777}
778
779uint32_t GPUreadData(void)
780{
9e146206 781 uint32_t ret;
56f08d83 782
783 if (unlikely(gpu.cmd_len > 0))
784 flush_cmd_buffer();
785
9e146206 786 ret = gpu.gp0;
ae097dfb
PC
787 if (gpu.dma.h) {
788 ret = HTOLE32(ret);
9e146206 789 do_vram_io(&ret, 1, 1);
ae097dfb
PC
790 ret = LE32TOH(ret);
791 }
56f08d83 792
9e146206 793 log_io("gpu_read %08x\n", ret);
794 return ret;
d30279e2
GI
795}
796
797uint32_t GPUreadStatus(void)
798{
ddd56f6e 799 uint32_t ret;
56f08d83 800
d30279e2
GI
801 if (unlikely(gpu.cmd_len > 0))
802 flush_cmd_buffer();
803
f23b103c 804 ret = gpu.status;
ddd56f6e 805 log_io("gpu_read_status %08x\n", ret);
806 return ret;
d30279e2
GI
807}
808
096ec49b 809struct GPUFreeze
1ab64c54
GI
810{
811 uint32_t ulFreezeVersion; // should be always 1 for now (set by main emu)
812 uint32_t ulStatus; // current gpu status
813 uint32_t ulControl[256]; // latest control register values
814 unsigned char psxVRam[1024*1024*2]; // current VRam image (full 2 MB for ZN)
096ec49b 815};
1ab64c54 816
096ec49b 817long GPUfreeze(uint32_t type, struct GPUFreeze *freeze)
1ab64c54 818{
fc84f618 819 int i;
820
1ab64c54
GI
821 switch (type) {
822 case 1: // save
d30279e2
GI
823 if (gpu.cmd_len > 0)
824 flush_cmd_buffer();
9ee0fd5b 825 memcpy(freeze->psxVRam, gpu.vram, 1024 * 512 * 2);
1ab64c54 826 memcpy(freeze->ulControl, gpu.regs, sizeof(gpu.regs));
6e9bdaef 827 memcpy(freeze->ulControl + 0xe0, gpu.ex_regs, sizeof(gpu.ex_regs));
f23b103c 828 freeze->ulStatus = gpu.status;
1ab64c54
GI
829 break;
830 case 0: // load
9ee0fd5b 831 memcpy(gpu.vram, freeze->psxVRam, 1024 * 512 * 2);
1ab64c54 832 memcpy(gpu.regs, freeze->ulControl, sizeof(gpu.regs));
6e9bdaef 833 memcpy(gpu.ex_regs, freeze->ulControl + 0xe0, sizeof(gpu.ex_regs));
f23b103c 834 gpu.status = freeze->ulStatus;
3d47ef17 835 gpu.cmd_len = 0;
fc84f618 836 for (i = 8; i > 0; i--) {
837 gpu.regs[i] ^= 1; // avoid reg change detection
838 GPUwriteStatus((i << 24) | (gpu.regs[i] ^ 1));
839 }
5b745e5b 840 renderer_sync_ecmds(gpu.ex_regs);
2da2fc76 841 renderer_update_caches(0, 0, 1024, 512, 0);
1ab64c54
GI
842 break;
843 }
844
845 return 1;
846}
847
5440b88e 848void GPUupdateLace(void)
849{
850 if (gpu.cmd_len > 0)
851 flush_cmd_buffer();
852 renderer_flush_queues();
853
cb245e56 854#ifndef RAW_FB_DISPLAY
f23b103c 855 if (gpu.status & PSX_GPU_STATUS_BLANKING) {
aafcb4dd 856 if (!gpu.state.blanked) {
857 vout_blank();
858 gpu.state.blanked = 1;
859 gpu.state.fb_dirty = 1;
860 }
861 return;
862 }
863
864 if (!gpu.state.fb_dirty)
5440b88e 865 return;
cb245e56 866#endif
5440b88e 867
868 if (gpu.frameskip.set) {
869 if (!gpu.frameskip.frame_ready) {
870 if (*gpu.state.frame_count - gpu.frameskip.last_flip_frame < 9)
871 return;
872 gpu.frameskip.active = 0;
873 }
874 gpu.frameskip.frame_ready = 0;
875 }
876
877 vout_update();
0b4038f8 878 if (gpu.state.enhancement_active && !gpu.state.enhancement_was_active)
879 renderer_update_caches(0, 0, 1024, 512, 1);
880 gpu.state.enhancement_was_active = gpu.state.enhancement_active;
5440b88e 881 gpu.state.fb_dirty = 0;
aafcb4dd 882 gpu.state.blanked = 0;
5440b88e 883}
884
72e5023f 885void GPUvBlank(int is_vblank, int lcf)
886{
5440b88e 887 int interlace = gpu.state.allow_interlace
f23b103c
PC
888 && (gpu.status & PSX_GPU_STATUS_INTERLACE)
889 && (gpu.status & PSX_GPU_STATUS_DHEIGHT);
5440b88e 890 // interlace doesn't look nice on progressive displays,
891 // so we have this "auto" mode here for games that don't read vram
892 if (gpu.state.allow_interlace == 2
893 && *gpu.state.frame_count - gpu.state.last_vram_read_frame > 1)
894 {
895 interlace = 0;
896 }
897 if (interlace || interlace != gpu.state.old_interlace) {
898 gpu.state.old_interlace = interlace;
899
900 if (gpu.cmd_len > 0)
901 flush_cmd_buffer();
902 renderer_flush_queues();
903 renderer_set_interlace(interlace, !lcf);
904 }
905}
906
ab88daca 907void GPUgetScreenInfo(int *y, int *base_hres)
908{
909 *y = gpu.screen.y;
910 *base_hres = gpu.screen.vres;
911 if (gpu.status & PSX_GPU_STATUS_DHEIGHT)
912 *base_hres >>= 1;
913}
914
5440b88e 915void GPUrearmedCallbacks(const struct rearmed_cbs *cbs)
916{
917 gpu.frameskip.set = cbs->frameskip;
918 gpu.frameskip.advice = &cbs->fskip_advice;
919 gpu.frameskip.active = 0;
920 gpu.frameskip.frame_ready = 1;
921 gpu.state.hcnt = cbs->gpu_hcnt;
922 gpu.state.frame_count = cbs->gpu_frame_count;
923 gpu.state.allow_interlace = cbs->gpu_neon.allow_interlace;
0b02eb77 924 gpu.state.enhancement_enable = cbs->gpu_neon.enhancement_enable;
9ed80467 925 gpu.state.screen_centering_type_default = cbs->screen_centering_type_default;
308c6e67 926 if (gpu.state.screen_centering_type != cbs->screen_centering_type
927 || gpu.state.screen_centering_x != cbs->screen_centering_x
928 || gpu.state.screen_centering_y != cbs->screen_centering_y) {
929 gpu.state.screen_centering_type = cbs->screen_centering_type;
930 gpu.state.screen_centering_x = cbs->screen_centering_x;
931 gpu.state.screen_centering_y = cbs->screen_centering_y;
932 update_width();
933 update_height();
934 }
5440b88e 935
9ee0fd5b 936 gpu.mmap = cbs->mmap;
937 gpu.munmap = cbs->munmap;
abf09485 938 gpu.gpu_state_change = cbs->gpu_state_change;
9ee0fd5b 939
940 // delayed vram mmap
941 if (gpu.vram == NULL)
942 map_vram();
943
5440b88e 944 if (cbs->pl_vout_set_raw_vram)
945 cbs->pl_vout_set_raw_vram(gpu.vram);
946 renderer_set_config(cbs);
947 vout_set_config(cbs);
72e5023f 948}
949
1ab64c54 950// vim:shiftwidth=2:expandtab