psxbios: unbreak bcopy
[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)) {
617 memcpy(gpu.cmd_buffer + gpu.cmd_len, list + 1, len * 4);
618 gpu.cmd_len += len;
619 flush_cmd_buffer();
620 continue;
621 }
ddd56f6e 622
56f08d83 623 if (len) {
48f3d210 624 left = do_cmd_buffer(list + 1, len);
ae36bb28 625 if (left) {
626 memcpy(gpu.cmd_buffer, list + 1 + len - left, left * 4);
627 gpu.cmd_len = left;
628 log_anomaly("GPUdmaChain: %d/%d words left\n", left, len);
629 }
56f08d83 630 }
ddd56f6e 631
8c84ba5f 632 if (progress_addr) {
633 *progress_addr = addr;
634 break;
635 }
09159d99 636 #define LD_THRESHOLD (8*1024)
637 if (count >= LD_THRESHOLD) {
638 if (count == LD_THRESHOLD) {
639 ld_addr = addr;
640 continue;
641 }
642
643 // loop detection marker
644 // (bit23 set causes DMA error on real machine, so
645 // unlikely to be ever set by the game)
89df80c6 646 list[0] |= HTOLE32(0x800000);
09159d99 647 }
ddd56f6e 648 }
649
09159d99 650 if (ld_addr != 0) {
651 // remove loop detection markers
652 count -= LD_THRESHOLD + 2;
653 addr = ld_addr & 0x1fffff;
654 while (count-- > 0) {
655 list = rambase + addr / 4;
89df80c6
PC
656 addr = LE32TOH(list[0]) & 0x1fffff;
657 list[0] &= HTOLE32(~0x800000);
09159d99 658 }
d30279e2 659 }
09159d99 660
3ece2f0c 661 gpu.state.last_list.frame = *gpu.state.frame_count;
deb18d24 662 gpu.state.last_list.hcnt = *gpu.state.hcnt;
1c72b1c2 663 gpu.state.last_list.cycles = cpu_cycles;
deb18d24 664 gpu.state.last_list.addr = start_addr;
665
1c72b1c2 666 return cpu_cycles;
1ab64c54
GI
667}
668
d30279e2
GI
669void GPUreadDataMem(uint32_t *mem, int count)
670{
56f08d83 671 log_io("gpu_dma_read %p %d\n", mem, count);
672
d30279e2
GI
673 if (unlikely(gpu.cmd_len > 0))
674 flush_cmd_buffer();
56f08d83 675
d30279e2
GI
676 if (gpu.dma.h)
677 do_vram_io(mem, count, 1);
678}
679
680uint32_t GPUreadData(void)
681{
9e146206 682 uint32_t ret;
56f08d83 683
684 if (unlikely(gpu.cmd_len > 0))
685 flush_cmd_buffer();
686
9e146206 687 ret = gpu.gp0;
ae097dfb
PC
688 if (gpu.dma.h) {
689 ret = HTOLE32(ret);
9e146206 690 do_vram_io(&ret, 1, 1);
ae097dfb
PC
691 ret = LE32TOH(ret);
692 }
56f08d83 693
9e146206 694 log_io("gpu_read %08x\n", ret);
695 return ret;
d30279e2
GI
696}
697
698uint32_t GPUreadStatus(void)
699{
ddd56f6e 700 uint32_t ret;
56f08d83 701
d30279e2
GI
702 if (unlikely(gpu.cmd_len > 0))
703 flush_cmd_buffer();
704
f23b103c 705 ret = gpu.status;
ddd56f6e 706 log_io("gpu_read_status %08x\n", ret);
707 return ret;
d30279e2
GI
708}
709
096ec49b 710struct GPUFreeze
1ab64c54
GI
711{
712 uint32_t ulFreezeVersion; // should be always 1 for now (set by main emu)
713 uint32_t ulStatus; // current gpu status
714 uint32_t ulControl[256]; // latest control register values
715 unsigned char psxVRam[1024*1024*2]; // current VRam image (full 2 MB for ZN)
096ec49b 716};
1ab64c54 717
096ec49b 718long GPUfreeze(uint32_t type, struct GPUFreeze *freeze)
1ab64c54 719{
fc84f618 720 int i;
721
1ab64c54
GI
722 switch (type) {
723 case 1: // save
d30279e2
GI
724 if (gpu.cmd_len > 0)
725 flush_cmd_buffer();
9ee0fd5b 726 memcpy(freeze->psxVRam, gpu.vram, 1024 * 512 * 2);
1ab64c54 727 memcpy(freeze->ulControl, gpu.regs, sizeof(gpu.regs));
6e9bdaef 728 memcpy(freeze->ulControl + 0xe0, gpu.ex_regs, sizeof(gpu.ex_regs));
f23b103c 729 freeze->ulStatus = gpu.status;
1ab64c54
GI
730 break;
731 case 0: // load
9ee0fd5b 732 memcpy(gpu.vram, freeze->psxVRam, 1024 * 512 * 2);
1ab64c54 733 memcpy(gpu.regs, freeze->ulControl, sizeof(gpu.regs));
6e9bdaef 734 memcpy(gpu.ex_regs, freeze->ulControl + 0xe0, sizeof(gpu.ex_regs));
f23b103c 735 gpu.status = freeze->ulStatus;
3d47ef17 736 gpu.cmd_len = 0;
fc84f618 737 for (i = 8; i > 0; i--) {
738 gpu.regs[i] ^= 1; // avoid reg change detection
739 GPUwriteStatus((i << 24) | (gpu.regs[i] ^ 1));
740 }
5b745e5b 741 renderer_sync_ecmds(gpu.ex_regs);
0b4038f8 742 renderer_update_caches(0, 0, 1024, 512, 1);
1ab64c54
GI
743 break;
744 }
745
746 return 1;
747}
748
5440b88e 749void GPUupdateLace(void)
750{
751 if (gpu.cmd_len > 0)
752 flush_cmd_buffer();
753 renderer_flush_queues();
754
f23b103c 755 if (gpu.status & PSX_GPU_STATUS_BLANKING) {
aafcb4dd 756 if (!gpu.state.blanked) {
757 vout_blank();
758 gpu.state.blanked = 1;
759 gpu.state.fb_dirty = 1;
760 }
761 return;
762 }
763
764 if (!gpu.state.fb_dirty)
5440b88e 765 return;
766
767 if (gpu.frameskip.set) {
768 if (!gpu.frameskip.frame_ready) {
769 if (*gpu.state.frame_count - gpu.frameskip.last_flip_frame < 9)
770 return;
771 gpu.frameskip.active = 0;
772 }
773 gpu.frameskip.frame_ready = 0;
774 }
775
776 vout_update();
0b4038f8 777 if (gpu.state.enhancement_active && !gpu.state.enhancement_was_active)
778 renderer_update_caches(0, 0, 1024, 512, 1);
779 gpu.state.enhancement_was_active = gpu.state.enhancement_active;
5440b88e 780 gpu.state.fb_dirty = 0;
aafcb4dd 781 gpu.state.blanked = 0;
5440b88e 782}
783
72e5023f 784void GPUvBlank(int is_vblank, int lcf)
785{
5440b88e 786 int interlace = gpu.state.allow_interlace
f23b103c
PC
787 && (gpu.status & PSX_GPU_STATUS_INTERLACE)
788 && (gpu.status & PSX_GPU_STATUS_DHEIGHT);
5440b88e 789 // interlace doesn't look nice on progressive displays,
790 // so we have this "auto" mode here for games that don't read vram
791 if (gpu.state.allow_interlace == 2
792 && *gpu.state.frame_count - gpu.state.last_vram_read_frame > 1)
793 {
794 interlace = 0;
795 }
796 if (interlace || interlace != gpu.state.old_interlace) {
797 gpu.state.old_interlace = interlace;
798
799 if (gpu.cmd_len > 0)
800 flush_cmd_buffer();
801 renderer_flush_queues();
802 renderer_set_interlace(interlace, !lcf);
803 }
804}
805
806#include "../../frontend/plugin_lib.h"
807
808void GPUrearmedCallbacks(const struct rearmed_cbs *cbs)
809{
810 gpu.frameskip.set = cbs->frameskip;
811 gpu.frameskip.advice = &cbs->fskip_advice;
812 gpu.frameskip.active = 0;
813 gpu.frameskip.frame_ready = 1;
814 gpu.state.hcnt = cbs->gpu_hcnt;
815 gpu.state.frame_count = cbs->gpu_frame_count;
816 gpu.state.allow_interlace = cbs->gpu_neon.allow_interlace;
0b02eb77 817 gpu.state.enhancement_enable = cbs->gpu_neon.enhancement_enable;
308c6e67 818 if (gpu.state.screen_centering_type != cbs->screen_centering_type
819 || gpu.state.screen_centering_x != cbs->screen_centering_x
820 || gpu.state.screen_centering_y != cbs->screen_centering_y) {
821 gpu.state.screen_centering_type = cbs->screen_centering_type;
822 gpu.state.screen_centering_x = cbs->screen_centering_x;
823 gpu.state.screen_centering_y = cbs->screen_centering_y;
824 update_width();
825 update_height();
826 }
5440b88e 827
9ee0fd5b 828 gpu.mmap = cbs->mmap;
829 gpu.munmap = cbs->munmap;
830
831 // delayed vram mmap
832 if (gpu.vram == NULL)
833 map_vram();
834
5440b88e 835 if (cbs->pl_vout_set_raw_vram)
836 cbs->pl_vout_set_raw_vram(gpu.vram);
837 renderer_set_config(cbs);
838 vout_set_config(cbs);
72e5023f 839}
840
1ab64c54 841// vim:shiftwidth=2:expandtab