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,
40 int *cycles_sum, int *cycles_last);
41 static void finish_vram_transfer(int is_read);
43 static noinline void do_cmd_reset(void)
47 if (unlikely(gpu.cmd_len > 0))
48 do_cmd_buffer(gpu.cmd_buffer, gpu.cmd_len, &dummy, &dummy);
51 if (unlikely(gpu.dma.h > 0))
52 finish_vram_transfer(gpu.dma_start.is_read);
56 static noinline void do_reset(void)
62 memset(gpu.regs, 0, sizeof(gpu.regs));
63 for (i = 0; i < sizeof(gpu.ex_regs) / sizeof(gpu.ex_regs[0]); i++)
64 gpu.ex_regs[i] = (0xe0 + i) << 24;
65 gpu.status = 0x14802000;
68 gpu.screen.hres = gpu.screen.w = 256;
69 gpu.screen.vres = gpu.screen.h = 240;
70 gpu.screen.x = gpu.screen.y = 0;
71 renderer_sync_ecmds(gpu.ex_regs);
72 renderer_notify_res_change();
75 static noinline void update_width(void)
77 static const short hres_all[8] = { 256, 368, 320, 368, 512, 368, 640, 368 };
78 static const uint8_t hdivs[8] = { 10, 7, 8, 7, 5, 7, 4, 7 };
79 uint8_t hdiv = hdivs[(gpu.status >> 16) & 7];
80 int hres = hres_all[(gpu.status >> 16) & 7];
81 int pal = gpu.status & PSX_GPU_STATUS_PAL;
82 int sw = gpu.screen.x2 - gpu.screen.x1;
83 int type = gpu.state.screen_centering_type;
86 type = gpu.state.screen_centering_type_default;
88 /* nothing displayed? */;
90 int s = pal ? 656 : 608; // or 600? pal is just a guess
91 x = (gpu.screen.x1 - s) / hdiv;
92 x = (x + 1) & ~1; // blitter limitation
94 sw = (sw + 2) & ~3; // according to nocash
96 if (gpu.state.show_overscan == 2) // widescreen hack
98 if (gpu.state.show_overscan && sw >= hres)
104 x = gpu.state.screen_centering_x;
107 // correct if slightly miscentered
108 x_auto = (hres - sw) / 2 & ~3;
109 if ((uint32_t)x_auto <= 8u && abs(x) < 24)
114 // .x range check is done in vout_update()
116 // reduce the unpleasant right border that a few games have
117 if (gpu.state.screen_centering_type == 0
118 && x <= 4 && hres - (x + sw) >= 4)
122 gpu.screen.hres = hres;
123 gpu.state.dims_changed = 1;
124 //printf("xx %d %d (%d) -> %2d, %d / %d\n", gpu.screen.x1,
125 // gpu.screen.x2, gpu.screen.x2 - gpu.screen.x1, x, sw, hres);
128 static noinline void update_height(void)
130 int pal = gpu.status & PSX_GPU_STATUS_PAL;
131 int dheight = gpu.status & PSX_GPU_STATUS_DHEIGHT;
132 int y = gpu.screen.y1 - (pal ? 39 : 16); // 39 for spyro
133 int sh = gpu.screen.y2 - gpu.screen.y1;
137 if (pal && (sh > 240 || gpu.screen.vres == 256))
140 y *= 2, sh *= 2, vres *= 2, center_tol *= 2;
142 /* nothing displayed? */;
144 switch (gpu.state.screen_centering_type) {
151 y = gpu.state.screen_centering_y;
154 // correct if slightly miscentered
155 if ((uint32_t)(vres - sh) <= 1 && abs(y) <= center_tol)
163 gpu.screen.vres = vres;
164 gpu.state.dims_changed = 1;
165 //printf("yy %d %d -> %d, %d / %d\n",
166 // gpu.screen.y1, gpu.screen.y2, y, sh, vres);
169 static noinline void decide_frameskip(void)
171 *gpu.frameskip.dirty = 1;
173 if (gpu.frameskip.active)
176 gpu.frameskip.cnt = 0;
177 gpu.frameskip.frame_ready = 1;
180 if (*gpu.frameskip.force)
181 gpu.frameskip.active = 1;
182 else if (!gpu.frameskip.active && *gpu.frameskip.advice)
183 gpu.frameskip.active = 1;
184 else if (gpu.frameskip.set > 0 && gpu.frameskip.cnt < gpu.frameskip.set)
185 gpu.frameskip.active = 1;
187 gpu.frameskip.active = 0;
189 if (!gpu.frameskip.active && gpu.frameskip.pending_fill[0] != 0) {
191 do_cmd_list(gpu.frameskip.pending_fill, 3, &dummy, &dummy, &dummy);
192 gpu.frameskip.pending_fill[0] = 0;
196 static noinline int decide_frameskip_allow(uint32_t cmd_e3)
198 // no frameskip if it decides to draw to display area,
199 // but not for interlace since it'll most likely always do that
200 uint32_t x = cmd_e3 & 0x3ff;
201 uint32_t y = (cmd_e3 >> 10) & 0x3ff;
202 gpu.frameskip.allow = (gpu.status & PSX_GPU_STATUS_INTERLACE) ||
203 (uint32_t)(x - gpu.screen.src_x) >= (uint32_t)gpu.screen.w ||
204 (uint32_t)(y - gpu.screen.src_y) >= (uint32_t)gpu.screen.h;
205 return gpu.frameskip.allow;
208 static void flush_cmd_buffer(void);
210 static noinline void get_gpu_info(uint32_t data)
212 if (unlikely(gpu.cmd_len > 0))
214 switch (data & 0x0f) {
218 gpu.gp0 = gpu.ex_regs[data & 7] & 0xfffff;
221 gpu.gp0 = gpu.ex_regs[5] & 0x3fffff;
233 #define max(a, b) (((a) > (b)) ? (a) : (b))
236 // Minimum 16-byte VRAM alignment needed by gpu_unai's pixel-skipping
237 // renderer/downscaler it uses in high res modes:
239 // On GCW platform (MIPS), align to 8192 bytes (1 TLB entry) to reduce # of
240 // fills. (Will change this value if it ever gets large page support)
241 #define VRAM_ALIGN 8192
243 #define VRAM_ALIGN 16
246 // double, for overdraw guard + at least 1 page before
247 #define VRAM_SIZE ((1024 * 512 * 2 * 2) + max(VRAM_ALIGN, 4096))
249 // vram ptr received from mmap/malloc/alloc (will deallocate using this)
250 static uint16_t *vram_ptr_orig = NULL;
252 #ifndef GPULIB_USE_MMAP
254 # define GPULIB_USE_MMAP 1
256 # define GPULIB_USE_MMAP 0
259 static int map_vram(void)
262 gpu.vram = vram_ptr_orig = gpu.mmap(VRAM_SIZE);
264 gpu.vram = vram_ptr_orig = calloc(VRAM_SIZE, 1);
266 if (gpu.vram != NULL && gpu.vram != (void *)(intptr_t)-1) {
267 // 4kb guard in front
268 gpu.vram += (4096 / 2);
270 gpu.vram = (uint16_t*)(((uintptr_t)gpu.vram + (VRAM_ALIGN-1)) & ~(VRAM_ALIGN-1));
274 fprintf(stderr, "could not map vram, expect crashes\n");
283 ret |= renderer_init();
285 memset(&gpu.state, 0, sizeof(gpu.state));
286 memset(&gpu.frameskip, 0, sizeof(gpu.frameskip));
288 gpu.state.frame_count = &gpu.zero;
289 gpu.state.hcnt = &gpu.zero;
293 /*if (gpu.mmap != NULL) {
300 long GPUshutdown(void)
307 if (vram_ptr_orig != NULL) {
309 gpu.munmap(vram_ptr_orig, VRAM_SIZE);
314 vram_ptr_orig = gpu.vram = NULL;
319 void GPUwriteStatus(uint32_t data)
321 uint32_t cmd = data >> 24;
324 if (cmd < ARRAY_SIZE(gpu.regs)) {
325 if (cmd > 1 && cmd != 5 && gpu.regs[cmd] == data)
327 gpu.regs[cmd] = data;
330 gpu.state.fb_dirty = 1;
341 gpu.status |= PSX_GPU_STATUS_BLANKING;
342 gpu.state.dims_changed = 1; // for hud clearing
345 gpu.status &= ~PSX_GPU_STATUS_BLANKING;
348 gpu.status &= ~PSX_GPU_STATUS_DMA_MASK;
349 gpu.status |= PSX_GPU_STATUS_DMA(data & 3);
352 src_x = data & 0x3ff; src_y = (data >> 10) & 0x1ff;
353 if (src_x != gpu.screen.src_x || src_y != gpu.screen.src_y) {
354 gpu.screen.src_x = src_x;
355 gpu.screen.src_y = src_y;
356 renderer_notify_scanout_change(src_x, src_y);
357 if (gpu.frameskip.set) {
358 decide_frameskip_allow(gpu.ex_regs[3]);
359 if (gpu.frameskip.last_flip_frame != *gpu.state.frame_count) {
361 gpu.frameskip.last_flip_frame = *gpu.state.frame_count;
367 gpu.screen.x1 = data & 0xfff;
368 gpu.screen.x2 = (data >> 12) & 0xfff;
372 gpu.screen.y1 = data & 0x3ff;
373 gpu.screen.y2 = (data >> 10) & 0x3ff;
377 gpu.status = (gpu.status & ~0x7f0000) | ((data & 0x3F) << 17) | ((data & 0x40) << 10);
380 renderer_notify_res_change();
383 if ((cmd & 0xf0) == 0x10)
388 #ifdef GPUwriteStatus_ext
389 GPUwriteStatus_ext(data);
393 const unsigned char cmd_lengths[256] =
395 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
396 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
397 3, 3, 3, 3, 6, 6, 6, 6, 4, 4, 4, 4, 8, 8, 8, 8, // 20
398 5, 5, 5, 5, 8, 8, 8, 8, 7, 7, 7, 7, 11, 11, 11, 11,
399 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, // 40
400 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4,
401 2, 2, 2, 2, 3, 3, 3, 3, 1, 1, 1, 1, 0, 0, 0, 0, // 60
402 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 2, 2, 2, 2,
403 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 80
404 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
405 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // a0
406 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
407 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // c0
408 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
409 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // e0
410 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
413 #define VRAM_MEM_XY(x, y) &gpu.vram[(y) * 1024 + (x)]
415 static void cpy_msb(uint16_t *dst, const uint16_t *src, int l, uint16_t msb)
418 for (i = 0; i < l; i++)
419 dst[i] = src[i] | msb;
422 static inline void do_vram_line(int x, int y, uint16_t *mem, int l,
423 int is_read, uint16_t msb)
425 uint16_t *vram = VRAM_MEM_XY(x, y);
426 if (unlikely(is_read))
427 memcpy(mem, vram, l * 2);
428 else if (unlikely(msb))
429 cpy_msb(vram, mem, l, msb);
431 memcpy(vram, mem, l * 2);
434 static int do_vram_io(uint32_t *data, int count, int is_read)
436 int count_initial = count;
437 uint16_t msb = gpu.ex_regs[6] << 15;
438 uint16_t *sdata = (uint16_t *)data;
439 int x = gpu.dma.x, y = gpu.dma.y;
440 int w = gpu.dma.w, h = gpu.dma.h;
441 int o = gpu.dma.offset;
443 count *= 2; // operate in 16bpp pixels
447 if (gpu.dma.offset) {
448 l = w - gpu.dma.offset;
452 do_vram_line(x + o, y, sdata, l, is_read, msb);
465 for (; h > 0 && count >= w; sdata += w, count -= w, y++, h--) {
467 do_vram_line(x, y, sdata, w, is_read, msb);
473 do_vram_line(x, y, sdata, count, is_read, msb);
479 finish_vram_transfer(is_read);
484 return count_initial - count / 2;
487 static void start_vram_transfer(uint32_t pos_word, uint32_t size_word, int is_read)
490 log_anomaly("start_vram_transfer while old unfinished\n");
492 gpu.dma.x = pos_word & 0x3ff;
493 gpu.dma.y = (pos_word >> 16) & 0x1ff;
494 gpu.dma.w = ((size_word - 1) & 0x3ff) + 1;
495 gpu.dma.h = (((size_word >> 16) - 1) & 0x1ff) + 1;
497 gpu.dma.is_read = is_read;
498 gpu.dma_start = gpu.dma;
500 renderer_flush_queues();
502 gpu.status |= PSX_GPU_STATUS_IMG;
503 // XXX: wrong for width 1
504 gpu.gp0 = LE32TOH(*(uint32_t *) VRAM_MEM_XY(gpu.dma.x, gpu.dma.y));
505 gpu.state.last_vram_read_frame = *gpu.state.frame_count;
508 log_io("start_vram_transfer %c (%d, %d) %dx%d\n", is_read ? 'r' : 'w',
509 gpu.dma.x, gpu.dma.y, gpu.dma.w, gpu.dma.h);
510 if (gpu.gpu_state_change)
511 gpu.gpu_state_change(PGS_VRAM_TRANSFER_START);
514 static void finish_vram_transfer(int is_read)
517 gpu.status &= ~PSX_GPU_STATUS_IMG;
519 gpu.state.fb_dirty = 1;
520 renderer_update_caches(gpu.dma_start.x, gpu.dma_start.y,
521 gpu.dma_start.w, gpu.dma_start.h, 0);
523 if (gpu.gpu_state_change)
524 gpu.gpu_state_change(PGS_VRAM_TRANSFER_END);
527 static void do_vram_copy(const uint32_t *params, int *cpu_cycles)
529 const uint32_t sx = LE32TOH(params[0]) & 0x3FF;
530 const uint32_t sy = (LE32TOH(params[0]) >> 16) & 0x1FF;
531 const uint32_t dx = LE32TOH(params[1]) & 0x3FF;
532 const uint32_t dy = (LE32TOH(params[1]) >> 16) & 0x1FF;
533 uint32_t w = ((LE32TOH(params[2]) - 1) & 0x3FF) + 1;
534 uint32_t h = (((LE32TOH(params[2]) >> 16) - 1) & 0x1FF) + 1;
535 uint16_t msb = gpu.ex_regs[6] << 15;
539 *cpu_cycles += gput_copy(w, h);
540 if (sx == dx && sy == dy && msb == 0)
543 renderer_flush_queues();
545 if (unlikely((sx < dx && dx < sx + w) || sx + w > 1024 || dx + w > 1024 || msb))
547 for (y = 0; y < h; y++)
549 const uint16_t *src = VRAM_MEM_XY(0, (sy + y) & 0x1ff);
550 uint16_t *dst = VRAM_MEM_XY(0, (dy + y) & 0x1ff);
551 for (x = 0; x < w; x += ARRAY_SIZE(lbuf))
553 uint32_t x1, w1 = w - x;
554 if (w1 > ARRAY_SIZE(lbuf))
555 w1 = ARRAY_SIZE(lbuf);
556 for (x1 = 0; x1 < w1; x1++)
557 lbuf[x1] = src[(sx + x + x1) & 0x3ff];
558 for (x1 = 0; x1 < w1; x1++)
559 dst[(dx + x + x1) & 0x3ff] = lbuf[x1] | msb;
565 uint32_t sy1 = sy, dy1 = dy;
566 for (y = 0; y < h; y++, sy1++, dy1++)
567 memcpy(VRAM_MEM_XY(dx, dy1 & 0x1ff), VRAM_MEM_XY(sx, sy1 & 0x1ff), w * 2);
570 renderer_update_caches(dx, dy, w, h, 0);
573 static noinline int do_cmd_list_skip(uint32_t *data, int count, int *last_cmd)
575 int cmd = 0, pos = 0, len, dummy = 0, v;
578 gpu.frameskip.pending_fill[0] = 0;
580 while (pos < count && skip) {
581 uint32_t *list = data + pos;
582 cmd = LE32TOH(list[0]) >> 24;
583 len = 1 + cmd_lengths[cmd];
584 if (pos + len > count) {
586 break; // incomplete cmd
591 if ((LE32TOH(list[2]) & 0x3ff) > gpu.screen.w || ((LE32TOH(list[2]) >> 16) & 0x1ff) > gpu.screen.h)
592 // clearing something large, don't skip
593 do_cmd_list(list, 3, &dummy, &dummy, &dummy);
595 memcpy(gpu.frameskip.pending_fill, list, 3 * 4);
601 gpu.ex_regs[1] &= ~0x1ff;
602 gpu.ex_regs[1] |= LE32TOH(list[4 + ((cmd >> 4) & 1)]) & 0x1ff;
605 for (v = 3; pos + v < count; v++)
607 if ((list[v] & HTOLE32(0xf000f000)) == HTOLE32(0x50005000))
613 for (v = 4; pos + v < count; v += 2)
615 if ((list[v] & HTOLE32(0xf000f000)) == HTOLE32(0x50005000))
622 skip = decide_frameskip_allow(LE32TOH(list[0]));
623 if ((cmd & 0xf8) == 0xe0)
624 gpu.ex_regs[cmd & 7] = LE32TOH(list[0]);
627 if (0x80 <= cmd && cmd <= 0xdf)
633 renderer_sync_ecmds(gpu.ex_regs);
638 static noinline int do_cmd_buffer(uint32_t *data, int count,
639 int *cycles_sum, int *cycles_last)
642 uint32_t old_e3 = gpu.ex_regs[3];
646 for (pos = 0; pos < count; )
648 if (gpu.dma.h && !gpu.dma_start.is_read) { // XXX: need to verify
650 pos += do_vram_io(data + pos, count - pos, 0);
655 cmd = LE32TOH(data[pos]) >> 24;
656 if (0xa0 <= cmd && cmd <= 0xdf) {
657 if (unlikely((pos+2) >= count)) {
658 // incomplete vram write/read cmd, can't consume yet
663 // consume vram write/read cmd
664 start_vram_transfer(LE32TOH(data[pos + 1]), LE32TOH(data[pos + 2]), (cmd & 0xe0) == 0xc0);
668 else if ((cmd & 0xe0) == 0x80) {
669 if (unlikely((pos+3) >= count)) {
670 cmd = -1; // incomplete cmd, can't consume yet
674 *cycles_sum += *cycles_last;
676 do_vram_copy(data + pos + 1, cycles_last);
681 else if (cmd == 0x1f) {
682 log_anomaly("irq1?\n");
687 // 0xex cmds might affect frameskip.allow, so pass to do_cmd_list_skip
688 if (gpu.frameskip.active && (gpu.frameskip.allow || ((LE32TOH(data[pos]) >> 24) & 0xf0) == 0xe0))
689 pos += do_cmd_list_skip(data + pos, count - pos, &cmd);
691 pos += do_cmd_list(data + pos, count - pos, cycles_sum, cycles_last, &cmd);
700 gpu.status &= ~0x1fff;
701 gpu.status |= gpu.ex_regs[1] & 0x7ff;
702 gpu.status |= (gpu.ex_regs[6] & 3) << 11;
704 gpu.state.fb_dirty |= vram_dirty;
706 if (old_e3 != gpu.ex_regs[3])
707 decide_frameskip_allow(gpu.ex_regs[3]);
712 static noinline void flush_cmd_buffer(void)
715 left = do_cmd_buffer(gpu.cmd_buffer, gpu.cmd_len, &dummy, &dummy);
717 memmove(gpu.cmd_buffer, gpu.cmd_buffer + gpu.cmd_len - left, left * 4);
718 if (left != gpu.cmd_len) {
719 if (!gpu.dma.h && gpu.gpu_state_change)
720 gpu.gpu_state_change(PGS_PRIMITIVE_START);
725 void GPUwriteDataMem(uint32_t *mem, int count)
729 log_io("gpu_dma_write %p %d\n", mem, count);
731 if (unlikely(gpu.cmd_len > 0))
734 left = do_cmd_buffer(mem, count, &dummy, &dummy);
736 log_anomaly("GPUwriteDataMem: discarded %d/%d words\n", left, count);
739 void GPUwriteData(uint32_t data)
741 log_io("gpu_write %08x\n", data);
742 gpu.cmd_buffer[gpu.cmd_len++] = HTOLE32(data);
743 if (gpu.cmd_len >= CMD_BUFFER_LEN)
747 long GPUdmaChain(uint32_t *rambase, uint32_t start_addr,
748 uint32_t *progress_addr, int32_t *cycles_last_cmd)
750 uint32_t addr, *list, ld_addr;
751 int len, left, count, ld_count = 32;
752 int cpu_cycles_sum = 0;
753 int cpu_cycles_last = 0;
755 preload(rambase + (start_addr & 0x1fffff) / 4);
757 if (unlikely(gpu.cmd_len > 0))
760 log_io("gpu_dma_chain\n");
761 addr = ld_addr = start_addr & 0xffffff;
762 for (count = 0; (addr & 0x800000) == 0; count++)
764 list = rambase + (addr & 0x1fffff) / 4;
765 len = LE32TOH(list[0]) >> 24;
766 addr = LE32TOH(list[0]) & 0xffffff;
767 preload(rambase + (addr & 0x1fffff) / 4);
769 cpu_cycles_sum += 10;
771 cpu_cycles_sum += 5 + len;
773 log_io(".chain %08lx #%d+%d %u+%u\n",
774 (long)(list - rambase) * 4, len, gpu.cmd_len, cpu_cycles_sum, cpu_cycles_last);
775 if (unlikely(gpu.cmd_len > 0)) {
776 if (gpu.cmd_len + len > ARRAY_SIZE(gpu.cmd_buffer)) {
777 log_anomaly("cmd_buffer overflow, likely garbage commands\n");
780 memcpy(gpu.cmd_buffer + gpu.cmd_len, list + 1, len * 4);
787 left = do_cmd_buffer(list + 1, len, &cpu_cycles_sum, &cpu_cycles_last);
789 memcpy(gpu.cmd_buffer, list + 1 + len - left, left * 4);
791 log_anomaly("GPUdmaChain: %d/%d words left\n", left, len);
796 *progress_addr = addr;
799 if (addr == ld_addr) {
800 log_anomaly("GPUdmaChain: loop @ %08x, cnt=%u\n", addr, count);
803 if (count == ld_count) {
809 //printf(" -> %d %d\n", cpu_cycles_sum, cpu_cycles_last);
810 gpu.state.last_list.frame = *gpu.state.frame_count;
811 gpu.state.last_list.hcnt = *gpu.state.hcnt;
812 gpu.state.last_list.cycles = cpu_cycles_sum + cpu_cycles_last;
813 gpu.state.last_list.addr = start_addr;
815 *cycles_last_cmd = cpu_cycles_last;
816 return cpu_cycles_sum;
819 void GPUreadDataMem(uint32_t *mem, int count)
821 log_io("gpu_dma_read %p %d\n", mem, count);
823 if (unlikely(gpu.cmd_len > 0))
827 do_vram_io(mem, count, 1);
830 uint32_t GPUreadData(void)
834 if (unlikely(gpu.cmd_len > 0))
840 do_vram_io(&ret, 1, 1);
844 log_io("gpu_read %08x\n", ret);
848 uint32_t GPUreadStatus(void)
852 if (unlikely(gpu.cmd_len > 0))
856 log_io("gpu_read_status %08x\n", ret);
862 uint32_t ulFreezeVersion; // should be always 1 for now (set by main emu)
863 uint32_t ulStatus; // current gpu status
864 uint32_t ulControl[256]; // latest control register values
865 unsigned char psxVRam[1024*1024*2]; // current VRam image (full 2 MB for ZN)
868 long GPUfreeze(uint32_t type, struct GPUFreeze *freeze)
878 memcpy(freeze->psxVRam, gpu.vram, 1024 * 512 * 2);
879 memcpy(freeze->ulControl, gpu.regs, sizeof(gpu.regs));
880 memcpy(freeze->ulControl + 0xe0, gpu.ex_regs, sizeof(gpu.ex_regs));
881 freeze->ulStatus = gpu.status;
885 memcpy(gpu.vram, freeze->psxVRam, 1024 * 512 * 2);
886 memcpy(gpu.regs, freeze->ulControl, sizeof(gpu.regs));
887 memcpy(gpu.ex_regs, freeze->ulControl + 0xe0, sizeof(gpu.ex_regs));
888 gpu.status = freeze->ulStatus;
890 for (i = 8; i > 0; i--) {
891 gpu.regs[i] ^= 1; // avoid reg change detection
892 GPUwriteStatus((i << 24) | (gpu.regs[i] ^ 1));
894 renderer_sync_ecmds(gpu.ex_regs);
895 renderer_update_caches(0, 0, 1024, 512, 0);
902 void GPUupdateLace(void)
906 renderer_flush_queues();
908 #ifndef RAW_FB_DISPLAY
909 if (gpu.status & PSX_GPU_STATUS_BLANKING) {
910 if (!gpu.state.blanked) {
912 gpu.state.blanked = 1;
913 gpu.state.fb_dirty = 1;
918 renderer_notify_update_lace(0);
920 if (!gpu.state.fb_dirty)
924 if (gpu.frameskip.set) {
925 if (!gpu.frameskip.frame_ready) {
926 if (*gpu.state.frame_count - gpu.frameskip.last_flip_frame < 9)
928 gpu.frameskip.active = 0;
930 gpu.frameskip.frame_ready = 0;
934 if (gpu.state.enhancement_active && !gpu.state.enhancement_was_active)
935 renderer_update_caches(0, 0, 1024, 512, 1);
936 gpu.state.enhancement_was_active = gpu.state.enhancement_active;
937 gpu.state.fb_dirty = 0;
938 gpu.state.blanked = 0;
939 renderer_notify_update_lace(1);
942 void GPUvBlank(int is_vblank, int lcf)
944 int interlace = gpu.state.allow_interlace
945 && (gpu.status & PSX_GPU_STATUS_INTERLACE)
946 && (gpu.status & PSX_GPU_STATUS_DHEIGHT);
947 // interlace doesn't look nice on progressive displays,
948 // so we have this "auto" mode here for games that don't read vram
949 if (gpu.state.allow_interlace == 2
950 && *gpu.state.frame_count - gpu.state.last_vram_read_frame > 1)
954 if (interlace || interlace != gpu.state.old_interlace) {
955 gpu.state.old_interlace = interlace;
959 renderer_flush_queues();
960 renderer_set_interlace(interlace, !lcf);
964 void GPUgetScreenInfo(int *y, int *base_hres)
967 *base_hres = gpu.screen.vres;
968 if (gpu.status & PSX_GPU_STATUS_DHEIGHT)
972 void GPUrearmedCallbacks(const struct rearmed_cbs *cbs)
974 gpu.frameskip.set = cbs->frameskip;
975 gpu.frameskip.advice = &cbs->fskip_advice;
976 gpu.frameskip.force = &cbs->fskip_force;
977 gpu.frameskip.dirty = (void *)&cbs->fskip_dirty;
978 gpu.frameskip.active = 0;
979 gpu.frameskip.frame_ready = 1;
980 gpu.state.hcnt = (uint32_t *)cbs->gpu_hcnt;
981 gpu.state.frame_count = (uint32_t *)cbs->gpu_frame_count;
982 gpu.state.allow_interlace = cbs->gpu_neon.allow_interlace;
983 gpu.state.enhancement_enable = cbs->gpu_neon.enhancement_enable;
984 gpu.state.screen_centering_type_default = cbs->screen_centering_type_default;
985 if (gpu.state.screen_centering_type != cbs->screen_centering_type
986 || gpu.state.screen_centering_x != cbs->screen_centering_x
987 || gpu.state.screen_centering_y != cbs->screen_centering_y
988 || gpu.state.show_overscan != cbs->show_overscan) {
989 gpu.state.screen_centering_type = cbs->screen_centering_type;
990 gpu.state.screen_centering_x = cbs->screen_centering_x;
991 gpu.state.screen_centering_y = cbs->screen_centering_y;
992 gpu.state.show_overscan = cbs->show_overscan;
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