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