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