gpu_neon: rework buffer selection
[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_notify_res_change();
62}
63
64static noinline void update_width(void)
65{
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;
71 int sw = gpu.screen.x2 - gpu.screen.x1;
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);
107}
108
109static noinline void update_height(void)
110{
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
114 int sh = gpu.screen.y2 - gpu.screen.y1;
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;
140 gpu.screen.h = sh;
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);
145}
146
147static noinline void decide_frameskip(void)
148{
149 if (gpu.frameskip.active)
150 gpu.frameskip.cnt++;
151 else {
152 gpu.frameskip.cnt = 0;
153 gpu.frameskip.frame_ready = 1;
154 }
155
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)
159 gpu.frameskip.active = 1;
160 else
161 gpu.frameskip.active = 0;
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 }
168}
169
170static noinline int decide_frameskip_allow(uint32_t cmd_e3)
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;
176 gpu.frameskip.allow = (gpu.status & PSX_GPU_STATUS_INTERLACE) ||
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;
179 return gpu.frameskip.allow;
180}
181
182static noinline void get_gpu_info(uint32_t data)
183{
184 switch (data & 0x0f) {
185 case 0x02:
186 case 0x03:
187 case 0x04:
188 gpu.gp0 = gpu.ex_regs[data & 7] & 0xfffff;
189 break;
190 case 0x05:
191 gpu.gp0 = gpu.ex_regs[5] & 0x3fffff;
192 break;
193 case 0x07:
194 gpu.gp0 = 2;
195 break;
196 default:
197 // gpu.gp0 unchanged
198 break;
199 }
200}
201
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
218long GPUinit(void)
219{
220 int ret;
221 ret = vout_init();
222 ret |= renderer_init();
223
224 memset(&gpu.state, 0, sizeof(gpu.state));
225 memset(&gpu.frameskip, 0, sizeof(gpu.frameskip));
226 gpu.zero = 0;
227 gpu.state.frame_count = &gpu.zero;
228 gpu.state.hcnt = &gpu.zero;
229 gpu.cmd_len = 0;
230 do_reset();
231
232 if (gpu.mmap != NULL) {
233 if (map_vram() != 0)
234 ret = -1;
235 }
236 return ret;
237}
238
239long GPUshutdown(void)
240{
241 long ret;
242
243 renderer_finish();
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;
252}
253
254void GPUwriteStatus(uint32_t data)
255{
256 uint32_t cmd = data >> 24;
257
258 if (cmd < ARRAY_SIZE(gpu.regs)) {
259 if (cmd > 1 && cmd != 5 && gpu.regs[cmd] == data)
260 return;
261 gpu.regs[cmd] = data;
262 }
263
264 gpu.state.fb_dirty = 1;
265
266 switch (cmd) {
267 case 0x00:
268 do_reset();
269 break;
270 case 0x01:
271 do_cmd_reset();
272 break;
273 case 0x03:
274 if (data & 1) {
275 gpu.status |= PSX_GPU_STATUS_BLANKING;
276 gpu.state.dims_changed = 1; // for hud clearing
277 }
278 else
279 gpu.status &= ~PSX_GPU_STATUS_BLANKING;
280 break;
281 case 0x04:
282 gpu.status &= ~PSX_GPU_STATUS_DMA_MASK;
283 gpu.status |= PSX_GPU_STATUS_DMA(data & 3);
284 break;
285 case 0x05:
286 gpu.screen.src_x = data & 0x3ff;
287 gpu.screen.src_y = (data >> 10) & 0x1ff;
288 renderer_notify_scanout_x_change(gpu.screen.src_x, gpu.screen.hres);
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 }
295 }
296 break;
297 case 0x06:
298 gpu.screen.x1 = data & 0xfff;
299 gpu.screen.x2 = (data >> 12) & 0xfff;
300 update_width();
301 break;
302 case 0x07:
303 gpu.screen.y1 = data & 0x3ff;
304 gpu.screen.y2 = (data >> 10) & 0x3ff;
305 update_height();
306 break;
307 case 0x08:
308 gpu.status = (gpu.status & ~0x7f0000) | ((data & 0x3F) << 17) | ((data & 0x40) << 10);
309 update_width();
310 update_height();
311 renderer_notify_res_change();
312 break;
313 default:
314 if ((cmd & 0xf0) == 0x10)
315 get_gpu_info(data);
316 break;
317 }
318
319#ifdef GPUwriteStatus_ext
320 GPUwriteStatus_ext(data);
321#endif
322}
323
324const unsigned char cmd_lengths[256] =
325{
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,
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
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
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)
347{
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;
361 int o = gpu.dma.offset;
362 int l;
363 count *= 2; // operate in 16bpp pixels
364
365 if (gpu.dma.offset) {
366 l = w - gpu.dma.offset;
367 if (count < l)
368 l = count;
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 }
379 sdata += l;
380 count -= l;
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
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 }
395 }
396 else
397 finish_vram_transfer(is_read);
398 gpu.dma.y = y;
399 gpu.dma.h = h;
400 gpu.dma.offset = o;
401
402 return count_initial - count / 2;
403}
404
405static void start_vram_transfer(uint32_t pos_word, uint32_t size_word, int is_read)
406{
407 if (gpu.dma.h)
408 log_anomaly("start_vram_transfer while old unfinished\n");
409
410 gpu.dma.x = pos_word & 0x3ff;
411 gpu.dma.y = (pos_word >> 16) & 0x1ff;
412 gpu.dma.w = ((size_word - 1) & 0x3ff) + 1;
413 gpu.dma.h = (((size_word >> 16) - 1) & 0x1ff) + 1;
414 gpu.dma.offset = 0;
415 gpu.dma.is_read = is_read;
416 gpu.dma_start = gpu.dma;
417
418 renderer_flush_queues();
419 if (is_read) {
420 gpu.status |= PSX_GPU_STATUS_IMG;
421 // XXX: wrong for width 1
422 gpu.gp0 = LE32TOH(*(uint32_t *) VRAM_MEM_XY(gpu.dma.x, gpu.dma.y));
423 gpu.state.last_vram_read_frame = *gpu.state.frame_count;
424 }
425
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);
428}
429
430static void finish_vram_transfer(int is_read)
431{
432 if (is_read)
433 gpu.status &= ~PSX_GPU_STATUS_IMG;
434 else
435 renderer_update_caches(gpu.dma_start.x, gpu.dma_start.y,
436 gpu.dma_start.w, gpu.dma_start.h, 0);
437}
438
439static noinline int do_cmd_list_skip(uint32_t *data, int count, int *last_cmd)
440{
441 int cmd = 0, pos = 0, len, dummy, v;
442 int skip = 1;
443
444 gpu.frameskip.pending_fill[0] = 0;
445
446 while (pos < count && skip) {
447 uint32_t *list = data + pos;
448 cmd = LE32TOH(list[0]) >> 24;
449 len = 1 + cmd_lengths[cmd];
450
451 switch (cmd) {
452 case 0x02:
453 if ((LE32TOH(list[2]) & 0x3ff) > gpu.screen.w || ((LE32TOH(list[2]) >> 16) & 0x1ff) > gpu.screen.h)
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;
464 gpu.ex_regs[1] |= LE32TOH(list[4 + ((cmd >> 4) & 1)]) & 0x1ff;
465 break;
466 case 0x48 ... 0x4F:
467 for (v = 3; pos + v < count; v++)
468 {
469 if ((list[v] & HTOLE32(0xf000f000)) == HTOLE32(0x50005000))
470 break;
471 }
472 len += v - 3;
473 break;
474 case 0x58 ... 0x5F:
475 for (v = 4; pos + v < count; v += 2)
476 {
477 if ((list[v] & HTOLE32(0xf000f000)) == HTOLE32(0x50005000))
478 break;
479 }
480 len += v - 4;
481 break;
482 default:
483 if (cmd == 0xe3)
484 skip = decide_frameskip_allow(LE32TOH(list[0]));
485 if ((cmd & 0xf8) == 0xe0)
486 gpu.ex_regs[cmd & 7] = LE32TOH(list[0]);
487 break;
488 }
489
490 if (pos + len > count) {
491 cmd = -1;
492 break; // incomplete cmd
493 }
494 if (0xa0 <= cmd && cmd <= 0xdf)
495 break; // image i/o
496
497 pos += len;
498 }
499
500 renderer_sync_ecmds(gpu.ex_regs);
501 *last_cmd = cmd;
502 return pos;
503}
504
505static noinline int do_cmd_buffer(uint32_t *data, int count)
506{
507 int cmd, pos;
508 uint32_t old_e3 = gpu.ex_regs[3];
509 int vram_dirty = 0;
510
511 // process buffer
512 for (pos = 0; pos < count; )
513 {
514 if (gpu.dma.h && !gpu.dma_start.is_read) { // XXX: need to verify
515 vram_dirty = 1;
516 pos += do_vram_io(data + pos, count - pos, 0);
517 if (pos == count)
518 break;
519 }
520
521 cmd = LE32TOH(data[pos]) >> 24;
522 if (0xa0 <= cmd && cmd <= 0xdf) {
523 if (unlikely((pos+2) >= count)) {
524 // incomplete vram write/read cmd, can't consume yet
525 cmd = -1;
526 break;
527 }
528
529 // consume vram write/read cmd
530 start_vram_transfer(LE32TOH(data[pos + 1]), LE32TOH(data[pos + 2]), (cmd & 0xe0) == 0xc0);
531 pos += 3;
532 continue;
533 }
534
535 // 0xex cmds might affect frameskip.allow, so pass to do_cmd_list_skip
536 if (gpu.frameskip.active && (gpu.frameskip.allow || ((LE32TOH(data[pos]) >> 24) & 0xf0) == 0xe0))
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
545 break;
546 }
547
548 gpu.status &= ~0x1fff;
549 gpu.status |= gpu.ex_regs[1] & 0x7ff;
550 gpu.status |= (gpu.ex_regs[6] & 3) << 11;
551
552 gpu.state.fb_dirty |= vram_dirty;
553
554 if (old_e3 != gpu.ex_regs[3])
555 decide_frameskip_allow(gpu.ex_regs[3]);
556
557 return count - pos;
558}
559
560static void flush_cmd_buffer(void)
561{
562 int left = do_cmd_buffer(gpu.cmd_buffer, gpu.cmd_len);
563 if (left > 0)
564 memmove(gpu.cmd_buffer, gpu.cmd_buffer + gpu.cmd_len - left, left * 4);
565 gpu.cmd_len = left;
566}
567
568void GPUwriteDataMem(uint32_t *mem, int count)
569{
570 int left;
571
572 log_io("gpu_dma_write %p %d\n", mem, count);
573
574 if (unlikely(gpu.cmd_len > 0))
575 flush_cmd_buffer();
576
577 left = do_cmd_buffer(mem, count);
578 if (left)
579 log_anomaly("GPUwriteDataMem: discarded %d/%d words\n", left, count);
580}
581
582void GPUwriteData(uint32_t data)
583{
584 log_io("gpu_write %08x\n", data);
585 gpu.cmd_buffer[gpu.cmd_len++] = HTOLE32(data);
586 if (gpu.cmd_len >= CMD_BUFFER_LEN)
587 flush_cmd_buffer();
588}
589
590long GPUdmaChain(uint32_t *rambase, uint32_t start_addr, uint32_t *progress_addr)
591{
592 uint32_t addr, *list, ld_addr = 0;
593 int len, left, count;
594 long cpu_cycles = 0;
595
596 preload(rambase + (start_addr & 0x1fffff) / 4);
597
598 if (unlikely(gpu.cmd_len > 0))
599 flush_cmd_buffer();
600
601 log_io("gpu_dma_chain\n");
602 addr = start_addr & 0xffffff;
603 for (count = 0; (addr & 0x800000) == 0; count++)
604 {
605 list = rambase + (addr & 0x1fffff) / 4;
606 len = LE32TOH(list[0]) >> 24;
607 addr = LE32TOH(list[0]) & 0xffffff;
608 preload(rambase + (addr & 0x1fffff) / 4);
609
610 cpu_cycles += 10;
611 if (len > 0)
612 cpu_cycles += 5 + len;
613
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 }
622
623 if (len) {
624 left = do_cmd_buffer(list + 1, len);
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 }
630 }
631
632 if (progress_addr) {
633 *progress_addr = addr;
634 break;
635 }
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)
646 list[0] |= HTOLE32(0x800000);
647 }
648 }
649
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;
656 addr = LE32TOH(list[0]) & 0x1fffff;
657 list[0] &= HTOLE32(~0x800000);
658 }
659 }
660
661 gpu.state.last_list.frame = *gpu.state.frame_count;
662 gpu.state.last_list.hcnt = *gpu.state.hcnt;
663 gpu.state.last_list.cycles = cpu_cycles;
664 gpu.state.last_list.addr = start_addr;
665
666 return cpu_cycles;
667}
668
669void GPUreadDataMem(uint32_t *mem, int count)
670{
671 log_io("gpu_dma_read %p %d\n", mem, count);
672
673 if (unlikely(gpu.cmd_len > 0))
674 flush_cmd_buffer();
675
676 if (gpu.dma.h)
677 do_vram_io(mem, count, 1);
678}
679
680uint32_t GPUreadData(void)
681{
682 uint32_t ret;
683
684 if (unlikely(gpu.cmd_len > 0))
685 flush_cmd_buffer();
686
687 ret = gpu.gp0;
688 if (gpu.dma.h) {
689 ret = HTOLE32(ret);
690 do_vram_io(&ret, 1, 1);
691 ret = LE32TOH(ret);
692 }
693
694 log_io("gpu_read %08x\n", ret);
695 return ret;
696}
697
698uint32_t GPUreadStatus(void)
699{
700 uint32_t ret;
701
702 if (unlikely(gpu.cmd_len > 0))
703 flush_cmd_buffer();
704
705 ret = gpu.status;
706 log_io("gpu_read_status %08x\n", ret);
707 return ret;
708}
709
710struct GPUFreeze
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)
716};
717
718long GPUfreeze(uint32_t type, struct GPUFreeze *freeze)
719{
720 int i;
721
722 switch (type) {
723 case 1: // save
724 if (gpu.cmd_len > 0)
725 flush_cmd_buffer();
726 memcpy(freeze->psxVRam, gpu.vram, 1024 * 512 * 2);
727 memcpy(freeze->ulControl, gpu.regs, sizeof(gpu.regs));
728 memcpy(freeze->ulControl + 0xe0, gpu.ex_regs, sizeof(gpu.ex_regs));
729 freeze->ulStatus = gpu.status;
730 break;
731 case 0: // load
732 memcpy(gpu.vram, freeze->psxVRam, 1024 * 512 * 2);
733 memcpy(gpu.regs, freeze->ulControl, sizeof(gpu.regs));
734 memcpy(gpu.ex_regs, freeze->ulControl + 0xe0, sizeof(gpu.ex_regs));
735 gpu.status = freeze->ulStatus;
736 gpu.cmd_len = 0;
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 }
741 renderer_sync_ecmds(gpu.ex_regs);
742 renderer_update_caches(0, 0, 1024, 512, 1);
743 break;
744 }
745
746 return 1;
747}
748
749void GPUupdateLace(void)
750{
751 if (gpu.cmd_len > 0)
752 flush_cmd_buffer();
753 renderer_flush_queues();
754
755 if (gpu.status & PSX_GPU_STATUS_BLANKING) {
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)
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();
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;
780 gpu.state.fb_dirty = 0;
781 gpu.state.blanked = 0;
782}
783
784void GPUvBlank(int is_vblank, int lcf)
785{
786 int interlace = gpu.state.allow_interlace
787 && (gpu.status & PSX_GPU_STATUS_INTERLACE)
788 && (gpu.status & PSX_GPU_STATUS_DHEIGHT);
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;
817 gpu.state.enhancement_enable = cbs->gpu_neon.enhancement_enable;
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 }
827
828 gpu.mmap = cbs->mmap;
829 gpu.munmap = cbs->munmap;
830
831 // delayed vram mmap
832 if (gpu.vram == NULL)
833 map_vram();
834
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);
839}
840
841// vim:shiftwidth=2:expandtab