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