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