gpulib: add some missed sync and flush
[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, 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
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)
352{
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;
366 int o = gpu.dma.offset;
367 int l;
368 count *= 2; // operate in 16bpp pixels
369
370 if (gpu.dma.offset) {
371 l = w - gpu.dma.offset;
372 if (count < l)
373 l = count;
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 }
384 sdata += l;
385 count -= l;
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
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 }
400 }
401 else
402 finish_vram_transfer(is_read);
403 gpu.dma.y = y;
404 gpu.dma.h = h;
405 gpu.dma.offset = o;
406
407 return count_initial - count / 2;
408}
409
410static void start_vram_transfer(uint32_t pos_word, uint32_t size_word, int is_read)
411{
412 if (gpu.dma.h)
413 log_anomaly("start_vram_transfer while old unfinished\n");
414
415 gpu.dma.x = pos_word & 0x3ff;
416 gpu.dma.y = (pos_word >> 16) & 0x1ff;
417 gpu.dma.w = ((size_word - 1) & 0x3ff) + 1;
418 gpu.dma.h = (((size_word >> 16) - 1) & 0x1ff) + 1;
419 gpu.dma.offset = 0;
420 gpu.dma.is_read = is_read;
421 gpu.dma_start = gpu.dma;
422
423 renderer_flush_queues();
424 if (is_read) {
425 gpu.status |= PSX_GPU_STATUS_IMG;
426 // XXX: wrong for width 1
427 gpu.gp0 = LE32TOH(*(uint32_t *) VRAM_MEM_XY(gpu.dma.x, gpu.dma.y));
428 gpu.state.last_vram_read_frame = *gpu.state.frame_count;
429 }
430
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);
433}
434
435static void finish_vram_transfer(int is_read)
436{
437 if (is_read)
438 gpu.status &= ~PSX_GPU_STATUS_IMG;
439 else
440 renderer_update_caches(gpu.dma_start.x, gpu.dma_start.y,
441 gpu.dma_start.w, gpu.dma_start.h, 0);
442}
443
444static noinline int do_cmd_list_skip(uint32_t *data, int count, int *last_cmd)
445{
446 int cmd = 0, pos = 0, len, dummy, v;
447 int skip = 1;
448
449 gpu.frameskip.pending_fill[0] = 0;
450
451 while (pos < count && skip) {
452 uint32_t *list = data + pos;
453 cmd = LE32TOH(list[0]) >> 24;
454 len = 1 + cmd_lengths[cmd];
455
456 switch (cmd) {
457 case 0x02:
458 if ((LE32TOH(list[2]) & 0x3ff) > gpu.screen.w || ((LE32TOH(list[2]) >> 16) & 0x1ff) > gpu.screen.h)
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;
469 gpu.ex_regs[1] |= LE32TOH(list[4 + ((cmd >> 4) & 1)]) & 0x1ff;
470 break;
471 case 0x48 ... 0x4F:
472 for (v = 3; pos + v < count; v++)
473 {
474 if ((list[v] & HTOLE32(0xf000f000)) == HTOLE32(0x50005000))
475 break;
476 }
477 len += v - 3;
478 break;
479 case 0x58 ... 0x5F:
480 for (v = 4; pos + v < count; v += 2)
481 {
482 if ((list[v] & HTOLE32(0xf000f000)) == HTOLE32(0x50005000))
483 break;
484 }
485 len += v - 4;
486 break;
487 default:
488 if (cmd == 0xe3)
489 skip = decide_frameskip_allow(LE32TOH(list[0]));
490 if ((cmd & 0xf8) == 0xe0)
491 gpu.ex_regs[cmd & 7] = LE32TOH(list[0]);
492 break;
493 }
494
495 if (pos + len > count) {
496 cmd = -1;
497 break; // incomplete cmd
498 }
499 if (0xa0 <= cmd && cmd <= 0xdf)
500 break; // image i/o
501
502 pos += len;
503 }
504
505 renderer_sync_ecmds(gpu.ex_regs);
506 *last_cmd = cmd;
507 return pos;
508}
509
510static noinline int do_cmd_buffer(uint32_t *data, int count)
511{
512 int cmd, pos;
513 uint32_t old_e3 = gpu.ex_regs[3];
514 int vram_dirty = 0;
515
516 // process buffer
517 for (pos = 0; pos < count; )
518 {
519 if (gpu.dma.h && !gpu.dma_start.is_read) { // XXX: need to verify
520 vram_dirty = 1;
521 pos += do_vram_io(data + pos, count - pos, 0);
522 if (pos == count)
523 break;
524 }
525
526 cmd = LE32TOH(data[pos]) >> 24;
527 if (0xa0 <= cmd && cmd <= 0xdf) {
528 if (unlikely((pos+2) >= count)) {
529 // incomplete vram write/read cmd, can't consume yet
530 cmd = -1;
531 break;
532 }
533
534 // consume vram write/read cmd
535 start_vram_transfer(LE32TOH(data[pos + 1]), LE32TOH(data[pos + 2]), (cmd & 0xe0) == 0xc0);
536 pos += 3;
537 continue;
538 }
539
540 // 0xex cmds might affect frameskip.allow, so pass to do_cmd_list_skip
541 if (gpu.frameskip.active && (gpu.frameskip.allow || ((LE32TOH(data[pos]) >> 24) & 0xf0) == 0xe0))
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
550 break;
551 }
552
553 gpu.status &= ~0x1fff;
554 gpu.status |= gpu.ex_regs[1] & 0x7ff;
555 gpu.status |= (gpu.ex_regs[6] & 3) << 11;
556
557 gpu.state.fb_dirty |= vram_dirty;
558
559 if (old_e3 != gpu.ex_regs[3])
560 decide_frameskip_allow(gpu.ex_regs[3]);
561
562 return count - pos;
563}
564
565static void flush_cmd_buffer(void)
566{
567 int left = do_cmd_buffer(gpu.cmd_buffer, gpu.cmd_len);
568 if (left > 0)
569 memmove(gpu.cmd_buffer, gpu.cmd_buffer + gpu.cmd_len - left, left * 4);
570 gpu.cmd_len = left;
571}
572
573void GPUwriteDataMem(uint32_t *mem, int count)
574{
575 int left;
576
577 log_io("gpu_dma_write %p %d\n", mem, count);
578
579 if (unlikely(gpu.cmd_len > 0))
580 flush_cmd_buffer();
581
582 left = do_cmd_buffer(mem, count);
583 if (left)
584 log_anomaly("GPUwriteDataMem: discarded %d/%d words\n", left, count);
585}
586
587void GPUwriteData(uint32_t data)
588{
589 log_io("gpu_write %08x\n", data);
590 gpu.cmd_buffer[gpu.cmd_len++] = HTOLE32(data);
591 if (gpu.cmd_len >= CMD_BUFFER_LEN)
592 flush_cmd_buffer();
593}
594
595long GPUdmaChain(uint32_t *rambase, uint32_t start_addr, uint32_t *progress_addr)
596{
597 uint32_t addr, *list, ld_addr = 0;
598 int len, left, count;
599 long cpu_cycles = 0;
600
601 preload(rambase + (start_addr & 0x1fffff) / 4);
602
603 if (unlikely(gpu.cmd_len > 0))
604 flush_cmd_buffer();
605
606 log_io("gpu_dma_chain\n");
607 addr = start_addr & 0xffffff;
608 for (count = 0; (addr & 0x800000) == 0; count++)
609 {
610 list = rambase + (addr & 0x1fffff) / 4;
611 len = LE32TOH(list[0]) >> 24;
612 addr = LE32TOH(list[0]) & 0xffffff;
613 preload(rambase + (addr & 0x1fffff) / 4);
614
615 cpu_cycles += 10;
616 if (len > 0)
617 cpu_cycles += 5 + len;
618
619 log_io(".chain %08lx #%d+%d\n",
620 (long)(list - rambase) * 4, len, gpu.cmd_len);
621 if (unlikely(gpu.cmd_len > 0)) {
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 }
626 memcpy(gpu.cmd_buffer + gpu.cmd_len, list + 1, len * 4);
627 gpu.cmd_len += len;
628 flush_cmd_buffer();
629 continue;
630 }
631
632 if (len) {
633 left = do_cmd_buffer(list + 1, len);
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 }
639 }
640
641 if (progress_addr) {
642 *progress_addr = addr;
643 break;
644 }
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)
655 list[0] |= HTOLE32(0x800000);
656 }
657 }
658
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;
665 addr = LE32TOH(list[0]) & 0x1fffff;
666 list[0] &= HTOLE32(~0x800000);
667 }
668 }
669
670 gpu.state.last_list.frame = *gpu.state.frame_count;
671 gpu.state.last_list.hcnt = *gpu.state.hcnt;
672 gpu.state.last_list.cycles = cpu_cycles;
673 gpu.state.last_list.addr = start_addr;
674
675 return cpu_cycles;
676}
677
678void GPUreadDataMem(uint32_t *mem, int count)
679{
680 log_io("gpu_dma_read %p %d\n", mem, count);
681
682 if (unlikely(gpu.cmd_len > 0))
683 flush_cmd_buffer();
684
685 if (gpu.dma.h)
686 do_vram_io(mem, count, 1);
687}
688
689uint32_t GPUreadData(void)
690{
691 uint32_t ret;
692
693 if (unlikely(gpu.cmd_len > 0))
694 flush_cmd_buffer();
695
696 ret = gpu.gp0;
697 if (gpu.dma.h) {
698 ret = HTOLE32(ret);
699 do_vram_io(&ret, 1, 1);
700 ret = LE32TOH(ret);
701 }
702
703 log_io("gpu_read %08x\n", ret);
704 return ret;
705}
706
707uint32_t GPUreadStatus(void)
708{
709 uint32_t ret;
710
711 if (unlikely(gpu.cmd_len > 0))
712 flush_cmd_buffer();
713
714 ret = gpu.status;
715 log_io("gpu_read_status %08x\n", ret);
716 return ret;
717}
718
719struct GPUFreeze
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)
725};
726
727long GPUfreeze(uint32_t type, struct GPUFreeze *freeze)
728{
729 int i;
730
731 switch (type) {
732 case 1: // save
733 if (gpu.cmd_len > 0)
734 flush_cmd_buffer();
735 memcpy(freeze->psxVRam, gpu.vram, 1024 * 512 * 2);
736 memcpy(freeze->ulControl, gpu.regs, sizeof(gpu.regs));
737 memcpy(freeze->ulControl + 0xe0, gpu.ex_regs, sizeof(gpu.ex_regs));
738 freeze->ulStatus = gpu.status;
739 break;
740 case 0: // load
741 memcpy(gpu.vram, freeze->psxVRam, 1024 * 512 * 2);
742 memcpy(gpu.regs, freeze->ulControl, sizeof(gpu.regs));
743 memcpy(gpu.ex_regs, freeze->ulControl + 0xe0, sizeof(gpu.ex_regs));
744 gpu.status = freeze->ulStatus;
745 gpu.cmd_len = 0;
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 }
750 renderer_sync_ecmds(gpu.ex_regs);
751 renderer_update_caches(0, 0, 1024, 512, 1);
752 break;
753 }
754
755 return 1;
756}
757
758void GPUupdateLace(void)
759{
760 if (gpu.cmd_len > 0)
761 flush_cmd_buffer();
762 renderer_flush_queues();
763
764#ifndef RAW_FB_DISPLAY
765 if (gpu.status & PSX_GPU_STATUS_BLANKING) {
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)
775 return;
776#endif
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();
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;
791 gpu.state.fb_dirty = 0;
792 gpu.state.blanked = 0;
793}
794
795void GPUvBlank(int is_vblank, int lcf)
796{
797 int interlace = gpu.state.allow_interlace
798 && (gpu.status & PSX_GPU_STATUS_INTERLACE)
799 && (gpu.status & PSX_GPU_STATUS_DHEIGHT);
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;
828 gpu.state.enhancement_enable = cbs->gpu_neon.enhancement_enable;
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 }
838
839 gpu.mmap = cbs->mmap;
840 gpu.munmap = cbs->munmap;
841
842 // delayed vram mmap
843 if (gpu.vram == NULL)
844 map_vram();
845
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);
850}
851
852// vim:shiftwidth=2:expandtab