gpulib: forgot to mark fb dirty
[pcsx_rearmed.git] / plugins / gpulib / gpu.c
... / ...
CommitLineData
1/*
2 * (C) GraÅžvydas "notaz" Ignotas, 2011-2012
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
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14#include "gpu.h"
15
16#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
17#ifdef __GNUC__
18#define unlikely(x) __builtin_expect((x), 0)
19#define preload __builtin_prefetch
20#define noinline __attribute__((noinline))
21#else
22#define unlikely(x)
23#define preload(...)
24#define noinline
25#endif
26
27//#define log_io gpu_log
28#define log_io(...)
29
30struct psx_gpu gpu;
31
32static noinline int do_cmd_buffer(uint32_t *data, int count);
33static void finish_vram_transfer(int is_read);
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);
39 gpu.cmd_len = 0;
40
41 if (unlikely(gpu.dma.h > 0))
42 finish_vram_transfer(gpu.dma_start.is_read);
43 gpu.dma.h = 0;
44}
45
46static noinline void do_reset(void)
47{
48 unsigned int i;
49
50 do_cmd_reset();
51
52 memset(gpu.regs, 0, sizeof(gpu.regs));
53 for (i = 0; i < sizeof(gpu.ex_regs) / sizeof(gpu.ex_regs[0]); i++)
54 gpu.ex_regs[i] = (0xe0 + i) << 24;
55 gpu.status = 0x14802000;
56 gpu.gp0 = 0;
57 gpu.regs[3] = 1;
58 gpu.screen.hres = gpu.screen.w = 256;
59 gpu.screen.vres = gpu.screen.h = 240;
60 gpu.screen.x = gpu.screen.y = 0;
61 renderer_sync_ecmds(gpu.ex_regs);
62 renderer_notify_res_change();
63}
64
65static noinline void update_width(void)
66{
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;
72 int sw = gpu.screen.x2 - gpu.screen.x1;
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);
108}
109
110static noinline void update_height(void)
111{
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
115 int sh = gpu.screen.y2 - gpu.screen.y1;
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;
141 gpu.screen.h = sh;
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);
146}
147
148static noinline void decide_frameskip(void)
149{
150 if (gpu.frameskip.active)
151 gpu.frameskip.cnt++;
152 else {
153 gpu.frameskip.cnt = 0;
154 gpu.frameskip.frame_ready = 1;
155 }
156
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)
160 gpu.frameskip.active = 1;
161 else
162 gpu.frameskip.active = 0;
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 }
169}
170
171static noinline int decide_frameskip_allow(uint32_t cmd_e3)
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;
177 gpu.frameskip.allow = (gpu.status & PSX_GPU_STATUS_INTERLACE) ||
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;
180 return gpu.frameskip.allow;
181}
182
183static void flush_cmd_buffer(void);
184
185static noinline void get_gpu_info(uint32_t data)
186{
187 if (unlikely(gpu.cmd_len > 0))
188 flush_cmd_buffer();
189 switch (data & 0x0f) {
190 case 0x02:
191 case 0x03:
192 case 0x04:
193 gpu.gp0 = gpu.ex_regs[data & 7] & 0xfffff;
194 break;
195 case 0x05:
196 gpu.gp0 = gpu.ex_regs[5] & 0x3fffff;
197 break;
198 case 0x07:
199 gpu.gp0 = 2;
200 break;
201 default:
202 // gpu.gp0 unchanged
203 break;
204 }
205}
206
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
223long GPUinit(void)
224{
225 int ret;
226 ret = vout_init();
227 ret |= renderer_init();
228
229 memset(&gpu.state, 0, sizeof(gpu.state));
230 memset(&gpu.frameskip, 0, sizeof(gpu.frameskip));
231 gpu.zero = 0;
232 gpu.state.frame_count = &gpu.zero;
233 gpu.state.hcnt = &gpu.zero;
234 gpu.cmd_len = 0;
235 do_reset();
236
237 if (gpu.mmap != NULL) {
238 if (map_vram() != 0)
239 ret = -1;
240 }
241 return ret;
242}
243
244long GPUshutdown(void)
245{
246 long ret;
247
248 renderer_finish();
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;
257}
258
259void GPUwriteStatus(uint32_t data)
260{
261 uint32_t cmd = data >> 24;
262
263 if (cmd < ARRAY_SIZE(gpu.regs)) {
264 if (cmd > 1 && cmd != 5 && gpu.regs[cmd] == data)
265 return;
266 gpu.regs[cmd] = data;
267 }
268
269 gpu.state.fb_dirty = 1;
270
271 switch (cmd) {
272 case 0x00:
273 do_reset();
274 break;
275 case 0x01:
276 do_cmd_reset();
277 break;
278 case 0x03:
279 if (data & 1) {
280 gpu.status |= PSX_GPU_STATUS_BLANKING;
281 gpu.state.dims_changed = 1; // for hud clearing
282 }
283 else
284 gpu.status &= ~PSX_GPU_STATUS_BLANKING;
285 break;
286 case 0x04:
287 gpu.status &= ~PSX_GPU_STATUS_DMA_MASK;
288 gpu.status |= PSX_GPU_STATUS_DMA(data & 3);
289 break;
290 case 0x05:
291 gpu.screen.src_x = data & 0x3ff;
292 gpu.screen.src_y = (data >> 10) & 0x1ff;
293 renderer_notify_scanout_x_change(gpu.screen.src_x, gpu.screen.hres);
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 }
300 }
301 break;
302 case 0x06:
303 gpu.screen.x1 = data & 0xfff;
304 gpu.screen.x2 = (data >> 12) & 0xfff;
305 update_width();
306 break;
307 case 0x07:
308 gpu.screen.y1 = data & 0x3ff;
309 gpu.screen.y2 = (data >> 10) & 0x3ff;
310 update_height();
311 break;
312 case 0x08:
313 gpu.status = (gpu.status & ~0x7f0000) | ((data & 0x3F) << 17) | ((data & 0x40) << 10);
314 update_width();
315 update_height();
316 renderer_notify_res_change();
317 break;
318 default:
319 if ((cmd & 0xf0) == 0x10)
320 get_gpu_info(data);
321 break;
322 }
323
324#ifdef GPUwriteStatus_ext
325 GPUwriteStatus_ext(data);
326#endif
327}
328
329const unsigned char cmd_lengths[256] =
330{
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,
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
338 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 2, 2, 2, 2,
339 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 80
340 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
341 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // a0
342 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
343 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // c0
344 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
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
349#define VRAM_MEM_XY(x, y) &gpu.vram[(y) * 1024 + (x)]
350
351static void cpy_msb(uint16_t *dst, const uint16_t *src, int l, uint16_t msb)
352{
353 int i;
354 for (i = 0; i < l; i++)
355 dst[i] = src[i] | msb;
356}
357
358static inline void do_vram_line(int x, int y, uint16_t *mem, int l,
359 int is_read, uint16_t msb)
360{
361 uint16_t *vram = VRAM_MEM_XY(x, y);
362 if (unlikely(is_read))
363 memcpy(mem, vram, l * 2);
364 else if (unlikely(msb))
365 cpy_msb(vram, mem, l, msb);
366 else
367 memcpy(vram, mem, l * 2);
368}
369
370static int do_vram_io(uint32_t *data, int count, int is_read)
371{
372 int count_initial = count;
373 uint16_t msb = gpu.ex_regs[6] << 15;
374 uint16_t *sdata = (uint16_t *)data;
375 int x = gpu.dma.x, y = gpu.dma.y;
376 int w = gpu.dma.w, h = gpu.dma.h;
377 int o = gpu.dma.offset;
378 int l;
379 count *= 2; // operate in 16bpp pixels
380
381 if (gpu.dma.offset) {
382 l = w - gpu.dma.offset;
383 if (count < l)
384 l = count;
385
386 do_vram_line(x + o, y, sdata, l, is_read, msb);
387
388 if (o + l < w)
389 o += l;
390 else {
391 o = 0;
392 y++;
393 h--;
394 }
395 sdata += l;
396 count -= l;
397 }
398
399 for (; h > 0 && count >= w; sdata += w, count -= w, y++, h--) {
400 y &= 511;
401 do_vram_line(x, y, sdata, w, is_read, msb);
402 }
403
404 if (h > 0) {
405 if (count > 0) {
406 y &= 511;
407 do_vram_line(x, y, sdata, count, is_read, msb);
408 o = count;
409 count = 0;
410 }
411 }
412 else
413 finish_vram_transfer(is_read);
414 gpu.dma.y = y;
415 gpu.dma.h = h;
416 gpu.dma.offset = o;
417
418 return count_initial - count / 2;
419}
420
421static void start_vram_transfer(uint32_t pos_word, uint32_t size_word, int is_read)
422{
423 if (gpu.dma.h)
424 log_anomaly("start_vram_transfer while old unfinished\n");
425
426 gpu.dma.x = pos_word & 0x3ff;
427 gpu.dma.y = (pos_word >> 16) & 0x1ff;
428 gpu.dma.w = ((size_word - 1) & 0x3ff) + 1;
429 gpu.dma.h = (((size_word >> 16) - 1) & 0x1ff) + 1;
430 gpu.dma.offset = 0;
431 gpu.dma.is_read = is_read;
432 gpu.dma_start = gpu.dma;
433
434 renderer_flush_queues();
435 if (is_read) {
436 gpu.status |= PSX_GPU_STATUS_IMG;
437 // XXX: wrong for width 1
438 gpu.gp0 = LE32TOH(*(uint32_t *) VRAM_MEM_XY(gpu.dma.x, gpu.dma.y));
439 gpu.state.last_vram_read_frame = *gpu.state.frame_count;
440 }
441
442 log_io("start_vram_transfer %c (%d, %d) %dx%d\n", is_read ? 'r' : 'w',
443 gpu.dma.x, gpu.dma.y, gpu.dma.w, gpu.dma.h);
444}
445
446static void finish_vram_transfer(int is_read)
447{
448 if (is_read)
449 gpu.status &= ~PSX_GPU_STATUS_IMG;
450 else {
451 gpu.state.fb_dirty = 1;
452 renderer_update_caches(gpu.dma_start.x, gpu.dma_start.y,
453 gpu.dma_start.w, gpu.dma_start.h, 0);
454 }
455}
456
457static void do_vram_copy(const uint32_t *params)
458{
459 const uint32_t sx = LE32TOH(params[0]) & 0x3FF;
460 const uint32_t sy = (LE32TOH(params[0]) >> 16) & 0x1FF;
461 const uint32_t dx = LE32TOH(params[1]) & 0x3FF;
462 const uint32_t dy = (LE32TOH(params[1]) >> 16) & 0x1FF;
463 uint32_t w = ((LE32TOH(params[2]) - 1) & 0x3FF) + 1;
464 uint32_t h = (((LE32TOH(params[2]) >> 16) - 1) & 0x1FF) + 1;
465 uint16_t msb = gpu.ex_regs[6] << 15;
466 uint16_t lbuf[128];
467 uint32_t x, y;
468
469 if (sx == dx && sy == dy && msb == 0)
470 return;
471
472 renderer_flush_queues();
473
474 if (unlikely((sx < dx && dx < sx + w) || sx + w > 1024 || dx + w > 1024 || msb))
475 {
476 for (y = 0; y < h; y++)
477 {
478 const uint16_t *src = VRAM_MEM_XY(0, (sy + y) & 0x1ff);
479 uint16_t *dst = VRAM_MEM_XY(0, (dy + y) & 0x1ff);
480 for (x = 0; x < w; x += ARRAY_SIZE(lbuf))
481 {
482 uint32_t x1, w1 = w - x;
483 if (w1 > ARRAY_SIZE(lbuf))
484 w1 = ARRAY_SIZE(lbuf);
485 for (x1 = 0; x1 < w1; x1++)
486 lbuf[x1] = src[(sx + x + x1) & 0x3ff];
487 for (x1 = 0; x1 < w1; x1++)
488 dst[(dx + x + x1) & 0x3ff] = lbuf[x1] | msb;
489 }
490 }
491 }
492 else
493 {
494 uint32_t sy1 = sy, dy1 = dy;
495 for (y = 0; y < h; y++, sy1++, dy1++)
496 memcpy(VRAM_MEM_XY(dx, dy1 & 0x1ff), VRAM_MEM_XY(sx, sy1 & 0x1ff), w * 2);
497 }
498
499 renderer_update_caches(dx, dy, w, h, 0);
500}
501
502static noinline int do_cmd_list_skip(uint32_t *data, int count, int *last_cmd)
503{
504 int cmd = 0, pos = 0, len, dummy, v;
505 int skip = 1;
506
507 gpu.frameskip.pending_fill[0] = 0;
508
509 while (pos < count && skip) {
510 uint32_t *list = data + pos;
511 cmd = LE32TOH(list[0]) >> 24;
512 len = 1 + cmd_lengths[cmd];
513
514 switch (cmd) {
515 case 0x02:
516 if ((LE32TOH(list[2]) & 0x3ff) > gpu.screen.w || ((LE32TOH(list[2]) >> 16) & 0x1ff) > gpu.screen.h)
517 // clearing something large, don't skip
518 do_cmd_list(list, 3, &dummy);
519 else
520 memcpy(gpu.frameskip.pending_fill, list, 3 * 4);
521 break;
522 case 0x24 ... 0x27:
523 case 0x2c ... 0x2f:
524 case 0x34 ... 0x37:
525 case 0x3c ... 0x3f:
526 gpu.ex_regs[1] &= ~0x1ff;
527 gpu.ex_regs[1] |= LE32TOH(list[4 + ((cmd >> 4) & 1)]) & 0x1ff;
528 break;
529 case 0x48 ... 0x4F:
530 for (v = 3; pos + v < count; v++)
531 {
532 if ((list[v] & HTOLE32(0xf000f000)) == HTOLE32(0x50005000))
533 break;
534 }
535 len += v - 3;
536 break;
537 case 0x58 ... 0x5F:
538 for (v = 4; pos + v < count; v += 2)
539 {
540 if ((list[v] & HTOLE32(0xf000f000)) == HTOLE32(0x50005000))
541 break;
542 }
543 len += v - 4;
544 break;
545 default:
546 if (cmd == 0xe3)
547 skip = decide_frameskip_allow(LE32TOH(list[0]));
548 if ((cmd & 0xf8) == 0xe0)
549 gpu.ex_regs[cmd & 7] = LE32TOH(list[0]);
550 break;
551 }
552
553 if (pos + len > count) {
554 cmd = -1;
555 break; // incomplete cmd
556 }
557 if (0x80 <= cmd && cmd <= 0xdf)
558 break; // image i/o
559
560 pos += len;
561 }
562
563 renderer_sync_ecmds(gpu.ex_regs);
564 *last_cmd = cmd;
565 return pos;
566}
567
568static noinline int do_cmd_buffer(uint32_t *data, int count)
569{
570 int cmd, pos;
571 uint32_t old_e3 = gpu.ex_regs[3];
572 int vram_dirty = 0;
573
574 // process buffer
575 for (pos = 0; pos < count; )
576 {
577 if (gpu.dma.h && !gpu.dma_start.is_read) { // XXX: need to verify
578 vram_dirty = 1;
579 pos += do_vram_io(data + pos, count - pos, 0);
580 if (pos == count)
581 break;
582 }
583
584 cmd = LE32TOH(data[pos]) >> 24;
585 if (0xa0 <= cmd && cmd <= 0xdf) {
586 if (unlikely((pos+2) >= count)) {
587 // incomplete vram write/read cmd, can't consume yet
588 cmd = -1;
589 break;
590 }
591
592 // consume vram write/read cmd
593 start_vram_transfer(LE32TOH(data[pos + 1]), LE32TOH(data[pos + 2]), (cmd & 0xe0) == 0xc0);
594 pos += 3;
595 continue;
596 }
597 else if ((cmd & 0xe0) == 0x80) {
598 if (unlikely((pos+3) >= count)) {
599 cmd = -1; // incomplete cmd, can't consume yet
600 break;
601 }
602 do_vram_copy(data + pos + 1);
603 vram_dirty = 1;
604 pos += 4;
605 continue;
606 }
607
608 // 0xex cmds might affect frameskip.allow, so pass to do_cmd_list_skip
609 if (gpu.frameskip.active && (gpu.frameskip.allow || ((LE32TOH(data[pos]) >> 24) & 0xf0) == 0xe0))
610 pos += do_cmd_list_skip(data + pos, count - pos, &cmd);
611 else {
612 pos += do_cmd_list(data + pos, count - pos, &cmd);
613 vram_dirty = 1;
614 }
615
616 if (cmd == -1)
617 // incomplete cmd
618 break;
619 }
620
621 gpu.status &= ~0x1fff;
622 gpu.status |= gpu.ex_regs[1] & 0x7ff;
623 gpu.status |= (gpu.ex_regs[6] & 3) << 11;
624
625 gpu.state.fb_dirty |= vram_dirty;
626
627 if (old_e3 != gpu.ex_regs[3])
628 decide_frameskip_allow(gpu.ex_regs[3]);
629
630 return count - pos;
631}
632
633static void flush_cmd_buffer(void)
634{
635 int left = do_cmd_buffer(gpu.cmd_buffer, gpu.cmd_len);
636 if (left > 0)
637 memmove(gpu.cmd_buffer, gpu.cmd_buffer + gpu.cmd_len - left, left * 4);
638 gpu.cmd_len = left;
639}
640
641void GPUwriteDataMem(uint32_t *mem, int count)
642{
643 int left;
644
645 log_io("gpu_dma_write %p %d\n", mem, count);
646
647 if (unlikely(gpu.cmd_len > 0))
648 flush_cmd_buffer();
649
650 left = do_cmd_buffer(mem, count);
651 if (left)
652 log_anomaly("GPUwriteDataMem: discarded %d/%d words\n", left, count);
653}
654
655void GPUwriteData(uint32_t data)
656{
657 log_io("gpu_write %08x\n", data);
658 gpu.cmd_buffer[gpu.cmd_len++] = HTOLE32(data);
659 if (gpu.cmd_len >= CMD_BUFFER_LEN)
660 flush_cmd_buffer();
661}
662
663long GPUdmaChain(uint32_t *rambase, uint32_t start_addr, uint32_t *progress_addr)
664{
665 uint32_t addr, *list, ld_addr = 0;
666 int len, left, count;
667 long cpu_cycles = 0;
668
669 preload(rambase + (start_addr & 0x1fffff) / 4);
670
671 if (unlikely(gpu.cmd_len > 0))
672 flush_cmd_buffer();
673
674 log_io("gpu_dma_chain\n");
675 addr = start_addr & 0xffffff;
676 for (count = 0; (addr & 0x800000) == 0; count++)
677 {
678 list = rambase + (addr & 0x1fffff) / 4;
679 len = LE32TOH(list[0]) >> 24;
680 addr = LE32TOH(list[0]) & 0xffffff;
681 preload(rambase + (addr & 0x1fffff) / 4);
682
683 cpu_cycles += 10;
684 if (len > 0)
685 cpu_cycles += 5 + len;
686
687 log_io(".chain %08lx #%d+%d\n",
688 (long)(list - rambase) * 4, len, gpu.cmd_len);
689 if (unlikely(gpu.cmd_len > 0)) {
690 if (gpu.cmd_len + len > ARRAY_SIZE(gpu.cmd_buffer)) {
691 log_anomaly("cmd_buffer overflow, likely garbage commands\n");
692 gpu.cmd_len = 0;
693 }
694 memcpy(gpu.cmd_buffer + gpu.cmd_len, list + 1, len * 4);
695 gpu.cmd_len += len;
696 flush_cmd_buffer();
697 continue;
698 }
699
700 if (len) {
701 left = do_cmd_buffer(list + 1, len);
702 if (left) {
703 memcpy(gpu.cmd_buffer, list + 1 + len - left, left * 4);
704 gpu.cmd_len = left;
705 log_anomaly("GPUdmaChain: %d/%d words left\n", left, len);
706 }
707 }
708
709 if (progress_addr) {
710 *progress_addr = addr;
711 break;
712 }
713 #define LD_THRESHOLD (8*1024)
714 if (count >= LD_THRESHOLD) {
715 if (count == LD_THRESHOLD) {
716 ld_addr = addr;
717 continue;
718 }
719
720 // loop detection marker
721 // (bit23 set causes DMA error on real machine, so
722 // unlikely to be ever set by the game)
723 list[0] |= HTOLE32(0x800000);
724 }
725 }
726
727 if (ld_addr != 0) {
728 // remove loop detection markers
729 count -= LD_THRESHOLD + 2;
730 addr = ld_addr & 0x1fffff;
731 while (count-- > 0) {
732 list = rambase + addr / 4;
733 addr = LE32TOH(list[0]) & 0x1fffff;
734 list[0] &= HTOLE32(~0x800000);
735 }
736 }
737
738 gpu.state.last_list.frame = *gpu.state.frame_count;
739 gpu.state.last_list.hcnt = *gpu.state.hcnt;
740 gpu.state.last_list.cycles = cpu_cycles;
741 gpu.state.last_list.addr = start_addr;
742
743 return cpu_cycles;
744}
745
746void GPUreadDataMem(uint32_t *mem, int count)
747{
748 log_io("gpu_dma_read %p %d\n", mem, count);
749
750 if (unlikely(gpu.cmd_len > 0))
751 flush_cmd_buffer();
752
753 if (gpu.dma.h)
754 do_vram_io(mem, count, 1);
755}
756
757uint32_t GPUreadData(void)
758{
759 uint32_t ret;
760
761 if (unlikely(gpu.cmd_len > 0))
762 flush_cmd_buffer();
763
764 ret = gpu.gp0;
765 if (gpu.dma.h) {
766 ret = HTOLE32(ret);
767 do_vram_io(&ret, 1, 1);
768 ret = LE32TOH(ret);
769 }
770
771 log_io("gpu_read %08x\n", ret);
772 return ret;
773}
774
775uint32_t GPUreadStatus(void)
776{
777 uint32_t ret;
778
779 if (unlikely(gpu.cmd_len > 0))
780 flush_cmd_buffer();
781
782 ret = gpu.status;
783 log_io("gpu_read_status %08x\n", ret);
784 return ret;
785}
786
787struct GPUFreeze
788{
789 uint32_t ulFreezeVersion; // should be always 1 for now (set by main emu)
790 uint32_t ulStatus; // current gpu status
791 uint32_t ulControl[256]; // latest control register values
792 unsigned char psxVRam[1024*1024*2]; // current VRam image (full 2 MB for ZN)
793};
794
795long GPUfreeze(uint32_t type, struct GPUFreeze *freeze)
796{
797 int i;
798
799 switch (type) {
800 case 1: // save
801 if (gpu.cmd_len > 0)
802 flush_cmd_buffer();
803 memcpy(freeze->psxVRam, gpu.vram, 1024 * 512 * 2);
804 memcpy(freeze->ulControl, gpu.regs, sizeof(gpu.regs));
805 memcpy(freeze->ulControl + 0xe0, gpu.ex_regs, sizeof(gpu.ex_regs));
806 freeze->ulStatus = gpu.status;
807 break;
808 case 0: // load
809 memcpy(gpu.vram, freeze->psxVRam, 1024 * 512 * 2);
810 memcpy(gpu.regs, freeze->ulControl, sizeof(gpu.regs));
811 memcpy(gpu.ex_regs, freeze->ulControl + 0xe0, sizeof(gpu.ex_regs));
812 gpu.status = freeze->ulStatus;
813 gpu.cmd_len = 0;
814 for (i = 8; i > 0; i--) {
815 gpu.regs[i] ^= 1; // avoid reg change detection
816 GPUwriteStatus((i << 24) | (gpu.regs[i] ^ 1));
817 }
818 renderer_sync_ecmds(gpu.ex_regs);
819 renderer_update_caches(0, 0, 1024, 512, 1);
820 break;
821 }
822
823 return 1;
824}
825
826void GPUupdateLace(void)
827{
828 if (gpu.cmd_len > 0)
829 flush_cmd_buffer();
830 renderer_flush_queues();
831
832#ifndef RAW_FB_DISPLAY
833 if (gpu.status & PSX_GPU_STATUS_BLANKING) {
834 if (!gpu.state.blanked) {
835 vout_blank();
836 gpu.state.blanked = 1;
837 gpu.state.fb_dirty = 1;
838 }
839 return;
840 }
841
842 if (!gpu.state.fb_dirty)
843 return;
844#endif
845
846 if (gpu.frameskip.set) {
847 if (!gpu.frameskip.frame_ready) {
848 if (*gpu.state.frame_count - gpu.frameskip.last_flip_frame < 9)
849 return;
850 gpu.frameskip.active = 0;
851 }
852 gpu.frameskip.frame_ready = 0;
853 }
854
855 vout_update();
856 if (gpu.state.enhancement_active && !gpu.state.enhancement_was_active)
857 renderer_update_caches(0, 0, 1024, 512, 1);
858 gpu.state.enhancement_was_active = gpu.state.enhancement_active;
859 gpu.state.fb_dirty = 0;
860 gpu.state.blanked = 0;
861}
862
863void GPUvBlank(int is_vblank, int lcf)
864{
865 int interlace = gpu.state.allow_interlace
866 && (gpu.status & PSX_GPU_STATUS_INTERLACE)
867 && (gpu.status & PSX_GPU_STATUS_DHEIGHT);
868 // interlace doesn't look nice on progressive displays,
869 // so we have this "auto" mode here for games that don't read vram
870 if (gpu.state.allow_interlace == 2
871 && *gpu.state.frame_count - gpu.state.last_vram_read_frame > 1)
872 {
873 interlace = 0;
874 }
875 if (interlace || interlace != gpu.state.old_interlace) {
876 gpu.state.old_interlace = interlace;
877
878 if (gpu.cmd_len > 0)
879 flush_cmd_buffer();
880 renderer_flush_queues();
881 renderer_set_interlace(interlace, !lcf);
882 }
883}
884
885void GPUgetScreenInfo(int *y, int *base_hres)
886{
887 *y = gpu.screen.y;
888 *base_hres = gpu.screen.vres;
889 if (gpu.status & PSX_GPU_STATUS_DHEIGHT)
890 *base_hres >>= 1;
891}
892
893#include "../../frontend/plugin_lib.h"
894
895void GPUrearmedCallbacks(const struct rearmed_cbs *cbs)
896{
897 gpu.frameskip.set = cbs->frameskip;
898 gpu.frameskip.advice = &cbs->fskip_advice;
899 gpu.frameskip.active = 0;
900 gpu.frameskip.frame_ready = 1;
901 gpu.state.hcnt = cbs->gpu_hcnt;
902 gpu.state.frame_count = cbs->gpu_frame_count;
903 gpu.state.allow_interlace = cbs->gpu_neon.allow_interlace;
904 gpu.state.enhancement_enable = cbs->gpu_neon.enhancement_enable;
905 if (gpu.state.screen_centering_type != cbs->screen_centering_type
906 || gpu.state.screen_centering_x != cbs->screen_centering_x
907 || gpu.state.screen_centering_y != cbs->screen_centering_y) {
908 gpu.state.screen_centering_type = cbs->screen_centering_type;
909 gpu.state.screen_centering_x = cbs->screen_centering_x;
910 gpu.state.screen_centering_y = cbs->screen_centering_y;
911 update_width();
912 update_height();
913 }
914
915 gpu.mmap = cbs->mmap;
916 gpu.munmap = cbs->munmap;
917
918 // delayed vram mmap
919 if (gpu.vram == NULL)
920 map_vram();
921
922 if (cbs->pl_vout_set_raw_vram)
923 cbs->pl_vout_set_raw_vram(gpu.vram);
924 renderer_set_config(cbs);
925 vout_set_config(cbs);
926}
927
928// vim:shiftwidth=2:expandtab