2 * (C) GraÅžvydas "notaz" Ignotas, 2011-2012
4 * This work is licensed under the terms of any of these licenses
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.
14 #include <stdlib.h> /* for calloc */
17 #include "gpu_timing.h"
18 #include "../../libpcsxcore/gpu.h" // meh
19 #include "../../frontend/plugin_lib.h"
22 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
25 #define unlikely(x) __builtin_expect((x), 0)
26 #define preload __builtin_prefetch
27 #define noinline __attribute__((noinline))
34 //#define log_io gpu_log
39 static noinline int do_cmd_buffer(uint32_t *data, int count, int *cpu_cycles);
40 static void finish_vram_transfer(int is_read);
42 static noinline void do_cmd_reset(void)
46 if (unlikely(gpu.cmd_len > 0))
47 do_cmd_buffer(gpu.cmd_buffer, gpu.cmd_len, &dummy);
50 if (unlikely(gpu.dma.h > 0))
51 finish_vram_transfer(gpu.dma_start.is_read);
55 static noinline void do_reset(void)
61 memset(gpu.regs, 0, sizeof(gpu.regs));
62 for (i = 0; i < sizeof(gpu.ex_regs) / sizeof(gpu.ex_regs[0]); i++)
63 gpu.ex_regs[i] = (0xe0 + i) << 24;
64 gpu.status = 0x14802000;
67 gpu.screen.hres = gpu.screen.w = 256;
68 gpu.screen.vres = gpu.screen.h = 240;
69 gpu.screen.x = gpu.screen.y = 0;
70 renderer_sync_ecmds(gpu.ex_regs);
71 renderer_notify_res_change();
74 static noinline void update_width(void)
76 static const short hres_all[8] = { 256, 368, 320, 368, 512, 368, 640, 368 };
77 static const uint8_t hdivs[8] = { 10, 7, 8, 7, 5, 7, 4, 7 };
78 uint8_t hdiv = hdivs[(gpu.status >> 16) & 7];
79 int hres = hres_all[(gpu.status >> 16) & 7];
80 int pal = gpu.status & PSX_GPU_STATUS_PAL;
81 int sw = gpu.screen.x2 - gpu.screen.x1;
82 int type = gpu.state.screen_centering_type;
85 type = gpu.state.screen_centering_type_default;
87 /* nothing displayed? */;
89 int s = pal ? 656 : 608; // or 600? pal is just a guess
90 x = (gpu.screen.x1 - s) / hdiv;
91 x = (x + 1) & ~1; // blitter limitation
93 sw = (sw + 2) & ~3; // according to nocash
98 x = gpu.state.screen_centering_x;
101 // correct if slightly miscentered
102 x_auto = (hres - sw) / 2 & ~3;
103 if ((uint32_t)x_auto <= 8u && abs(x) < 24)
108 // .x range check is done in vout_update()
110 // reduce the unpleasant right border that a few games have
111 if (gpu.state.screen_centering_type == 0
112 && x <= 4 && hres - (x + sw) >= 4)
116 gpu.screen.hres = hres;
117 gpu.state.dims_changed = 1;
118 //printf("xx %d %d -> %2d, %d / %d\n",
119 // gpu.screen.x1, gpu.screen.x2, x, sw, hres);
122 static noinline void update_height(void)
124 int pal = gpu.status & PSX_GPU_STATUS_PAL;
125 int dheight = gpu.status & PSX_GPU_STATUS_DHEIGHT;
126 int y = gpu.screen.y1 - (pal ? 39 : 16); // 39 for spyro
127 int sh = gpu.screen.y2 - gpu.screen.y1;
131 if (pal && (sh > 240 || gpu.screen.vres == 256))
134 y *= 2, sh *= 2, vres *= 2, center_tol *= 2;
136 /* nothing displayed? */;
138 switch (gpu.state.screen_centering_type) {
145 y = gpu.state.screen_centering_y;
148 // correct if slightly miscentered
149 if ((uint32_t)(vres - sh) <= 1 && abs(y) <= center_tol)
157 gpu.screen.vres = vres;
158 gpu.state.dims_changed = 1;
159 //printf("yy %d %d -> %d, %d / %d\n",
160 // gpu.screen.y1, gpu.screen.y2, y, sh, vres);
163 static noinline void decide_frameskip(void)
165 *gpu.frameskip.dirty = 1;
167 if (gpu.frameskip.active)
170 gpu.frameskip.cnt = 0;
171 gpu.frameskip.frame_ready = 1;
174 if (*gpu.frameskip.force)
175 gpu.frameskip.active = 1;
176 else if (!gpu.frameskip.active && *gpu.frameskip.advice)
177 gpu.frameskip.active = 1;
178 else if (gpu.frameskip.set > 0 && gpu.frameskip.cnt < gpu.frameskip.set)
179 gpu.frameskip.active = 1;
181 gpu.frameskip.active = 0;
183 if (!gpu.frameskip.active && gpu.frameskip.pending_fill[0] != 0) {
185 do_cmd_list(gpu.frameskip.pending_fill, 3, &dummy, &dummy);
186 gpu.frameskip.pending_fill[0] = 0;
190 static noinline int decide_frameskip_allow(uint32_t cmd_e3)
192 // no frameskip if it decides to draw to display area,
193 // but not for interlace since it'll most likely always do that
194 uint32_t x = cmd_e3 & 0x3ff;
195 uint32_t y = (cmd_e3 >> 10) & 0x3ff;
196 gpu.frameskip.allow = (gpu.status & PSX_GPU_STATUS_INTERLACE) ||
197 (uint32_t)(x - gpu.screen.src_x) >= (uint32_t)gpu.screen.w ||
198 (uint32_t)(y - gpu.screen.src_y) >= (uint32_t)gpu.screen.h;
199 return gpu.frameskip.allow;
202 static void flush_cmd_buffer(void);
204 static noinline void get_gpu_info(uint32_t data)
206 if (unlikely(gpu.cmd_len > 0))
208 switch (data & 0x0f) {
212 gpu.gp0 = gpu.ex_regs[data & 7] & 0xfffff;
215 gpu.gp0 = gpu.ex_regs[5] & 0x3fffff;
227 #define max(a, b) (((a) > (b)) ? (a) : (b))
230 // Minimum 16-byte VRAM alignment needed by gpu_unai's pixel-skipping
231 // renderer/downscaler it uses in high res modes:
233 // On GCW platform (MIPS), align to 8192 bytes (1 TLB entry) to reduce # of
234 // fills. (Will change this value if it ever gets large page support)
235 #define VRAM_ALIGN 8192
237 #define VRAM_ALIGN 16
240 // double, for overdraw guard + at least 1 page before
241 #define VRAM_SIZE ((1024 * 512 * 2 * 2) + max(VRAM_ALIGN, 4096))
243 // vram ptr received from mmap/malloc/alloc (will deallocate using this)
244 static uint16_t *vram_ptr_orig = NULL;
246 #ifndef GPULIB_USE_MMAP
248 # define GPULIB_USE_MMAP 1
250 # define GPULIB_USE_MMAP 0
253 static int map_vram(void)
256 gpu.vram = vram_ptr_orig = gpu.mmap(VRAM_SIZE);
258 gpu.vram = vram_ptr_orig = calloc(VRAM_SIZE, 1);
260 if (gpu.vram != NULL && gpu.vram != (void *)(intptr_t)-1) {
261 // 4kb guard in front
262 gpu.vram += (4096 / 2);
264 gpu.vram = (uint16_t*)(((uintptr_t)gpu.vram + (VRAM_ALIGN-1)) & ~(VRAM_ALIGN-1));
268 fprintf(stderr, "could not map vram, expect crashes\n");
277 ret |= renderer_init();
279 memset(&gpu.state, 0, sizeof(gpu.state));
280 memset(&gpu.frameskip, 0, sizeof(gpu.frameskip));
282 gpu.state.frame_count = &gpu.zero;
283 gpu.state.hcnt = &gpu.zero;
287 /*if (gpu.mmap != NULL) {
294 long GPUshutdown(void)
301 if (vram_ptr_orig != NULL) {
303 gpu.munmap(vram_ptr_orig, VRAM_SIZE);
308 vram_ptr_orig = gpu.vram = NULL;
313 void GPUwriteStatus(uint32_t data)
315 uint32_t cmd = data >> 24;
318 if (cmd < ARRAY_SIZE(gpu.regs)) {
319 if (cmd > 1 && cmd != 5 && gpu.regs[cmd] == data)
321 gpu.regs[cmd] = data;
324 gpu.state.fb_dirty = 1;
335 gpu.status |= PSX_GPU_STATUS_BLANKING;
336 gpu.state.dims_changed = 1; // for hud clearing
339 gpu.status &= ~PSX_GPU_STATUS_BLANKING;
342 gpu.status &= ~PSX_GPU_STATUS_DMA_MASK;
343 gpu.status |= PSX_GPU_STATUS_DMA(data & 3);
346 src_x = data & 0x3ff; src_y = (data >> 10) & 0x1ff;
347 if (src_x != gpu.screen.src_x || src_y != gpu.screen.src_y) {
348 gpu.screen.src_x = src_x;
349 gpu.screen.src_y = src_y;
350 renderer_notify_scanout_change(src_x, src_y);
351 if (gpu.frameskip.set) {
352 decide_frameskip_allow(gpu.ex_regs[3]);
353 if (gpu.frameskip.last_flip_frame != *gpu.state.frame_count) {
355 gpu.frameskip.last_flip_frame = *gpu.state.frame_count;
361 gpu.screen.x1 = data & 0xfff;
362 gpu.screen.x2 = (data >> 12) & 0xfff;
366 gpu.screen.y1 = data & 0x3ff;
367 gpu.screen.y2 = (data >> 10) & 0x3ff;
371 gpu.status = (gpu.status & ~0x7f0000) | ((data & 0x3F) << 17) | ((data & 0x40) << 10);
374 renderer_notify_res_change();
377 if ((cmd & 0xf0) == 0x10)
382 #ifdef GPUwriteStatus_ext
383 GPUwriteStatus_ext(data);
387 const unsigned char cmd_lengths[256] =
389 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
390 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
391 3, 3, 3, 3, 6, 6, 6, 6, 4, 4, 4, 4, 8, 8, 8, 8, // 20
392 5, 5, 5, 5, 8, 8, 8, 8, 7, 7, 7, 7, 11, 11, 11, 11,
393 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, // 40
394 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4,
395 2, 2, 2, 2, 3, 3, 3, 3, 1, 1, 1, 1, 0, 0, 0, 0, // 60
396 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 2, 2, 2, 2,
397 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 80
398 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
399 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // a0
400 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
401 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // c0
402 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
403 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // e0
404 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
407 #define VRAM_MEM_XY(x, y) &gpu.vram[(y) * 1024 + (x)]
409 static void cpy_msb(uint16_t *dst, const uint16_t *src, int l, uint16_t msb)
412 for (i = 0; i < l; i++)
413 dst[i] = src[i] | msb;
416 static inline void do_vram_line(int x, int y, uint16_t *mem, int l,
417 int is_read, uint16_t msb)
419 uint16_t *vram = VRAM_MEM_XY(x, y);
420 if (unlikely(is_read))
421 memcpy(mem, vram, l * 2);
422 else if (unlikely(msb))
423 cpy_msb(vram, mem, l, msb);
425 memcpy(vram, mem, l * 2);
428 static int do_vram_io(uint32_t *data, int count, int is_read)
430 int count_initial = count;
431 uint16_t msb = gpu.ex_regs[6] << 15;
432 uint16_t *sdata = (uint16_t *)data;
433 int x = gpu.dma.x, y = gpu.dma.y;
434 int w = gpu.dma.w, h = gpu.dma.h;
435 int o = gpu.dma.offset;
437 count *= 2; // operate in 16bpp pixels
441 if (gpu.dma.offset) {
442 l = w - gpu.dma.offset;
446 do_vram_line(x + o, y, sdata, l, is_read, msb);
459 for (; h > 0 && count >= w; sdata += w, count -= w, y++, h--) {
461 do_vram_line(x, y, sdata, w, is_read, msb);
467 do_vram_line(x, y, sdata, count, is_read, msb);
473 finish_vram_transfer(is_read);
478 return count_initial - count / 2;
481 static void start_vram_transfer(uint32_t pos_word, uint32_t size_word, int is_read)
484 log_anomaly("start_vram_transfer while old unfinished\n");
486 gpu.dma.x = pos_word & 0x3ff;
487 gpu.dma.y = (pos_word >> 16) & 0x1ff;
488 gpu.dma.w = ((size_word - 1) & 0x3ff) + 1;
489 gpu.dma.h = (((size_word >> 16) - 1) & 0x1ff) + 1;
491 gpu.dma.is_read = is_read;
492 gpu.dma_start = gpu.dma;
494 renderer_flush_queues();
496 gpu.status |= PSX_GPU_STATUS_IMG;
497 // XXX: wrong for width 1
498 gpu.gp0 = LE32TOH(*(uint32_t *) VRAM_MEM_XY(gpu.dma.x, gpu.dma.y));
499 gpu.state.last_vram_read_frame = *gpu.state.frame_count;
502 log_io("start_vram_transfer %c (%d, %d) %dx%d\n", is_read ? 'r' : 'w',
503 gpu.dma.x, gpu.dma.y, gpu.dma.w, gpu.dma.h);
504 if (gpu.gpu_state_change)
505 gpu.gpu_state_change(PGS_VRAM_TRANSFER_START);
508 static void finish_vram_transfer(int is_read)
511 gpu.status &= ~PSX_GPU_STATUS_IMG;
513 gpu.state.fb_dirty = 1;
514 renderer_update_caches(gpu.dma_start.x, gpu.dma_start.y,
515 gpu.dma_start.w, gpu.dma_start.h, 0);
517 if (gpu.gpu_state_change)
518 gpu.gpu_state_change(PGS_VRAM_TRANSFER_END);
521 static void do_vram_copy(const uint32_t *params, int *cpu_cycles)
523 const uint32_t sx = LE32TOH(params[0]) & 0x3FF;
524 const uint32_t sy = (LE32TOH(params[0]) >> 16) & 0x1FF;
525 const uint32_t dx = LE32TOH(params[1]) & 0x3FF;
526 const uint32_t dy = (LE32TOH(params[1]) >> 16) & 0x1FF;
527 uint32_t w = ((LE32TOH(params[2]) - 1) & 0x3FF) + 1;
528 uint32_t h = (((LE32TOH(params[2]) >> 16) - 1) & 0x1FF) + 1;
529 uint16_t msb = gpu.ex_regs[6] << 15;
533 *cpu_cycles += gput_copy(w, h);
534 if (sx == dx && sy == dy && msb == 0)
537 renderer_flush_queues();
539 if (unlikely((sx < dx && dx < sx + w) || sx + w > 1024 || dx + w > 1024 || msb))
541 for (y = 0; y < h; y++)
543 const uint16_t *src = VRAM_MEM_XY(0, (sy + y) & 0x1ff);
544 uint16_t *dst = VRAM_MEM_XY(0, (dy + y) & 0x1ff);
545 for (x = 0; x < w; x += ARRAY_SIZE(lbuf))
547 uint32_t x1, w1 = w - x;
548 if (w1 > ARRAY_SIZE(lbuf))
549 w1 = ARRAY_SIZE(lbuf);
550 for (x1 = 0; x1 < w1; x1++)
551 lbuf[x1] = src[(sx + x + x1) & 0x3ff];
552 for (x1 = 0; x1 < w1; x1++)
553 dst[(dx + x + x1) & 0x3ff] = lbuf[x1] | msb;
559 uint32_t sy1 = sy, dy1 = dy;
560 for (y = 0; y < h; y++, sy1++, dy1++)
561 memcpy(VRAM_MEM_XY(dx, dy1 & 0x1ff), VRAM_MEM_XY(sx, sy1 & 0x1ff), w * 2);
564 renderer_update_caches(dx, dy, w, h, 0);
567 static noinline int do_cmd_list_skip(uint32_t *data, int count, int *last_cmd)
569 int cmd = 0, pos = 0, len, dummy = 0, v;
572 gpu.frameskip.pending_fill[0] = 0;
574 while (pos < count && skip) {
575 uint32_t *list = data + pos;
576 cmd = LE32TOH(list[0]) >> 24;
577 len = 1 + cmd_lengths[cmd];
581 if ((LE32TOH(list[2]) & 0x3ff) > gpu.screen.w || ((LE32TOH(list[2]) >> 16) & 0x1ff) > gpu.screen.h)
582 // clearing something large, don't skip
583 do_cmd_list(list, 3, &dummy, &dummy);
585 memcpy(gpu.frameskip.pending_fill, list, 3 * 4);
591 gpu.ex_regs[1] &= ~0x1ff;
592 gpu.ex_regs[1] |= LE32TOH(list[4 + ((cmd >> 4) & 1)]) & 0x1ff;
595 for (v = 3; pos + v < count; v++)
597 if ((list[v] & HTOLE32(0xf000f000)) == HTOLE32(0x50005000))
603 for (v = 4; pos + v < count; v += 2)
605 if ((list[v] & HTOLE32(0xf000f000)) == HTOLE32(0x50005000))
612 skip = decide_frameskip_allow(LE32TOH(list[0]));
613 if ((cmd & 0xf8) == 0xe0)
614 gpu.ex_regs[cmd & 7] = LE32TOH(list[0]);
618 if (pos + len > count) {
620 break; // incomplete cmd
622 if (0x80 <= cmd && cmd <= 0xdf)
628 renderer_sync_ecmds(gpu.ex_regs);
633 static noinline int do_cmd_buffer(uint32_t *data, int count, int *cpu_cycles)
636 uint32_t old_e3 = gpu.ex_regs[3];
640 for (pos = 0; pos < count; )
642 if (gpu.dma.h && !gpu.dma_start.is_read) { // XXX: need to verify
644 pos += do_vram_io(data + pos, count - pos, 0);
649 cmd = LE32TOH(data[pos]) >> 24;
650 if (0xa0 <= cmd && cmd <= 0xdf) {
651 if (unlikely((pos+2) >= count)) {
652 // incomplete vram write/read cmd, can't consume yet
657 // consume vram write/read cmd
658 start_vram_transfer(LE32TOH(data[pos + 1]), LE32TOH(data[pos + 2]), (cmd & 0xe0) == 0xc0);
662 else if ((cmd & 0xe0) == 0x80) {
663 if (unlikely((pos+3) >= count)) {
664 cmd = -1; // incomplete cmd, can't consume yet
667 do_vram_copy(data + pos + 1, cpu_cycles);
672 else if (cmd == 0x1f) {
673 log_anomaly("irq1?\n");
678 // 0xex cmds might affect frameskip.allow, so pass to do_cmd_list_skip
679 if (gpu.frameskip.active && (gpu.frameskip.allow || ((LE32TOH(data[pos]) >> 24) & 0xf0) == 0xe0))
680 pos += do_cmd_list_skip(data + pos, count - pos, &cmd);
682 pos += do_cmd_list(data + pos, count - pos, cpu_cycles, &cmd);
691 gpu.status &= ~0x1fff;
692 gpu.status |= gpu.ex_regs[1] & 0x7ff;
693 gpu.status |= (gpu.ex_regs[6] & 3) << 11;
695 gpu.state.fb_dirty |= vram_dirty;
697 if (old_e3 != gpu.ex_regs[3])
698 decide_frameskip_allow(gpu.ex_regs[3]);
703 static noinline void flush_cmd_buffer(void)
706 left = do_cmd_buffer(gpu.cmd_buffer, gpu.cmd_len, &dummy);
708 memmove(gpu.cmd_buffer, gpu.cmd_buffer + gpu.cmd_len - left, left * 4);
709 if (left != gpu.cmd_len) {
710 if (!gpu.dma.h && gpu.gpu_state_change)
711 gpu.gpu_state_change(PGS_PRIMITIVE_START);
716 void GPUwriteDataMem(uint32_t *mem, int count)
720 log_io("gpu_dma_write %p %d\n", mem, count);
722 if (unlikely(gpu.cmd_len > 0))
725 left = do_cmd_buffer(mem, count, &dummy);
727 log_anomaly("GPUwriteDataMem: discarded %d/%d words\n", left, count);
730 void GPUwriteData(uint32_t data)
732 log_io("gpu_write %08x\n", data);
733 gpu.cmd_buffer[gpu.cmd_len++] = HTOLE32(data);
734 if (gpu.cmd_len >= CMD_BUFFER_LEN)
738 long GPUdmaChain(uint32_t *rambase, uint32_t start_addr, uint32_t *progress_addr)
740 uint32_t addr, *list, ld_addr = 0;
741 int len, left, count;
744 preload(rambase + (start_addr & 0x1fffff) / 4);
746 if (unlikely(gpu.cmd_len > 0))
749 log_io("gpu_dma_chain\n");
750 addr = start_addr & 0xffffff;
751 for (count = 0; (addr & 0x800000) == 0; count++)
753 list = rambase + (addr & 0x1fffff) / 4;
754 len = LE32TOH(list[0]) >> 24;
755 addr = LE32TOH(list[0]) & 0xffffff;
756 preload(rambase + (addr & 0x1fffff) / 4);
760 cpu_cycles += 5 + len;
762 log_io(".chain %08lx #%d+%d %u\n",
763 (long)(list - rambase) * 4, len, gpu.cmd_len, cpu_cycles);
764 if (unlikely(gpu.cmd_len > 0)) {
765 if (gpu.cmd_len + len > ARRAY_SIZE(gpu.cmd_buffer)) {
766 log_anomaly("cmd_buffer overflow, likely garbage commands\n");
769 memcpy(gpu.cmd_buffer + gpu.cmd_len, list + 1, len * 4);
776 left = do_cmd_buffer(list + 1, len, &cpu_cycles);
778 memcpy(gpu.cmd_buffer, list + 1 + len - left, left * 4);
780 log_anomaly("GPUdmaChain: %d/%d words left\n", left, len);
785 *progress_addr = addr;
788 #define LD_THRESHOLD (8*1024)
789 if (count >= LD_THRESHOLD) {
790 if (count == LD_THRESHOLD) {
795 // loop detection marker
796 // (bit23 set causes DMA error on real machine, so
797 // unlikely to be ever set by the game)
798 list[0] |= HTOLE32(0x800000);
803 // remove loop detection markers
804 count -= LD_THRESHOLD + 2;
805 addr = ld_addr & 0x1fffff;
806 while (count-- > 0) {
807 list = rambase + addr / 4;
808 addr = LE32TOH(list[0]) & 0x1fffff;
809 list[0] &= HTOLE32(~0x800000);
813 gpu.state.last_list.frame = *gpu.state.frame_count;
814 gpu.state.last_list.hcnt = *gpu.state.hcnt;
815 gpu.state.last_list.cycles = cpu_cycles;
816 gpu.state.last_list.addr = start_addr;
821 void GPUreadDataMem(uint32_t *mem, int count)
823 log_io("gpu_dma_read %p %d\n", mem, count);
825 if (unlikely(gpu.cmd_len > 0))
829 do_vram_io(mem, count, 1);
832 uint32_t GPUreadData(void)
836 if (unlikely(gpu.cmd_len > 0))
842 do_vram_io(&ret, 1, 1);
846 log_io("gpu_read %08x\n", ret);
850 uint32_t GPUreadStatus(void)
854 if (unlikely(gpu.cmd_len > 0))
858 log_io("gpu_read_status %08x\n", ret);
864 uint32_t ulFreezeVersion; // should be always 1 for now (set by main emu)
865 uint32_t ulStatus; // current gpu status
866 uint32_t ulControl[256]; // latest control register values
867 unsigned char psxVRam[1024*1024*2]; // current VRam image (full 2 MB for ZN)
870 long GPUfreeze(uint32_t type, struct GPUFreeze *freeze)
880 memcpy(freeze->psxVRam, gpu.vram, 1024 * 512 * 2);
881 memcpy(freeze->ulControl, gpu.regs, sizeof(gpu.regs));
882 memcpy(freeze->ulControl + 0xe0, gpu.ex_regs, sizeof(gpu.ex_regs));
883 freeze->ulStatus = gpu.status;
887 memcpy(gpu.vram, freeze->psxVRam, 1024 * 512 * 2);
888 memcpy(gpu.regs, freeze->ulControl, sizeof(gpu.regs));
889 memcpy(gpu.ex_regs, freeze->ulControl + 0xe0, sizeof(gpu.ex_regs));
890 gpu.status = freeze->ulStatus;
892 for (i = 8; i > 0; i--) {
893 gpu.regs[i] ^= 1; // avoid reg change detection
894 GPUwriteStatus((i << 24) | (gpu.regs[i] ^ 1));
896 renderer_sync_ecmds(gpu.ex_regs);
897 renderer_update_caches(0, 0, 1024, 512, 0);
904 void GPUupdateLace(void)
908 renderer_flush_queues();
910 #ifndef RAW_FB_DISPLAY
911 if (gpu.status & PSX_GPU_STATUS_BLANKING) {
912 if (!gpu.state.blanked) {
914 gpu.state.blanked = 1;
915 gpu.state.fb_dirty = 1;
920 renderer_notify_update_lace(0);
922 if (!gpu.state.fb_dirty)
926 if (gpu.frameskip.set) {
927 if (!gpu.frameskip.frame_ready) {
928 if (*gpu.state.frame_count - gpu.frameskip.last_flip_frame < 9)
930 gpu.frameskip.active = 0;
932 gpu.frameskip.frame_ready = 0;
936 if (gpu.state.enhancement_active && !gpu.state.enhancement_was_active)
937 renderer_update_caches(0, 0, 1024, 512, 1);
938 gpu.state.enhancement_was_active = gpu.state.enhancement_active;
939 gpu.state.fb_dirty = 0;
940 gpu.state.blanked = 0;
941 renderer_notify_update_lace(1);
944 void GPUvBlank(int is_vblank, int lcf)
946 int interlace = gpu.state.allow_interlace
947 && (gpu.status & PSX_GPU_STATUS_INTERLACE)
948 && (gpu.status & PSX_GPU_STATUS_DHEIGHT);
949 // interlace doesn't look nice on progressive displays,
950 // so we have this "auto" mode here for games that don't read vram
951 if (gpu.state.allow_interlace == 2
952 && *gpu.state.frame_count - gpu.state.last_vram_read_frame > 1)
956 if (interlace || interlace != gpu.state.old_interlace) {
957 gpu.state.old_interlace = interlace;
961 renderer_flush_queues();
962 renderer_set_interlace(interlace, !lcf);
966 void GPUgetScreenInfo(int *y, int *base_hres)
969 *base_hres = gpu.screen.vres;
970 if (gpu.status & PSX_GPU_STATUS_DHEIGHT)
974 void GPUrearmedCallbacks(const struct rearmed_cbs *cbs)
976 gpu.frameskip.set = cbs->frameskip;
977 gpu.frameskip.advice = &cbs->fskip_advice;
978 gpu.frameskip.force = &cbs->fskip_force;
979 gpu.frameskip.dirty = (void *)&cbs->fskip_dirty;
980 gpu.frameskip.active = 0;
981 gpu.frameskip.frame_ready = 1;
982 gpu.state.hcnt = cbs->gpu_hcnt;
983 gpu.state.frame_count = cbs->gpu_frame_count;
984 gpu.state.allow_interlace = cbs->gpu_neon.allow_interlace;
985 gpu.state.enhancement_enable = cbs->gpu_neon.enhancement_enable;
986 gpu.state.screen_centering_type_default = cbs->screen_centering_type_default;
987 if (gpu.state.screen_centering_type != cbs->screen_centering_type
988 || gpu.state.screen_centering_x != cbs->screen_centering_x
989 || gpu.state.screen_centering_y != cbs->screen_centering_y) {
990 gpu.state.screen_centering_type = cbs->screen_centering_type;
991 gpu.state.screen_centering_x = cbs->screen_centering_x;
992 gpu.state.screen_centering_y = cbs->screen_centering_y;
997 gpu.mmap = cbs->mmap;
998 gpu.munmap = cbs->munmap;
999 gpu.gpu_state_change = cbs->gpu_state_change;
1001 // delayed vram mmap
1002 if (gpu.vram == NULL)
1005 if (cbs->pl_vout_set_raw_vram)
1006 cbs->pl_vout_set_raw_vram(gpu.vram);
1007 renderer_set_config(cbs);
1008 vout_set_config(cbs);
1011 // vim:shiftwidth=2:expandtab