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);
673 // 0xex cmds might affect frameskip.allow, so pass to do_cmd_list_skip
674 if (gpu.frameskip.active && (gpu.frameskip.allow || ((LE32TOH(data[pos]) >> 24) & 0xf0) == 0xe0))
675 pos += do_cmd_list_skip(data + pos, count - pos, &cmd);
677 pos += do_cmd_list(data + pos, count - pos, cpu_cycles, &cmd);
686 gpu.status &= ~0x1fff;
687 gpu.status |= gpu.ex_regs[1] & 0x7ff;
688 gpu.status |= (gpu.ex_regs[6] & 3) << 11;
690 gpu.state.fb_dirty |= vram_dirty;
692 if (old_e3 != gpu.ex_regs[3])
693 decide_frameskip_allow(gpu.ex_regs[3]);
698 static noinline void flush_cmd_buffer(void)
701 left = do_cmd_buffer(gpu.cmd_buffer, gpu.cmd_len, &dummy);
703 memmove(gpu.cmd_buffer, gpu.cmd_buffer + gpu.cmd_len - left, left * 4);
704 if (left != gpu.cmd_len) {
705 if (!gpu.dma.h && gpu.gpu_state_change)
706 gpu.gpu_state_change(PGS_PRIMITIVE_START);
711 void GPUwriteDataMem(uint32_t *mem, int count)
715 log_io("gpu_dma_write %p %d\n", mem, count);
717 if (unlikely(gpu.cmd_len > 0))
720 left = do_cmd_buffer(mem, count, &dummy);
722 log_anomaly("GPUwriteDataMem: discarded %d/%d words\n", left, count);
725 void GPUwriteData(uint32_t data)
727 log_io("gpu_write %08x\n", data);
728 gpu.cmd_buffer[gpu.cmd_len++] = HTOLE32(data);
729 if (gpu.cmd_len >= CMD_BUFFER_LEN)
733 long GPUdmaChain(uint32_t *rambase, uint32_t start_addr, uint32_t *progress_addr)
735 uint32_t addr, *list, ld_addr = 0;
736 int len, left, count;
739 preload(rambase + (start_addr & 0x1fffff) / 4);
741 if (unlikely(gpu.cmd_len > 0))
744 log_io("gpu_dma_chain\n");
745 addr = start_addr & 0xffffff;
746 for (count = 0; (addr & 0x800000) == 0; count++)
748 list = rambase + (addr & 0x1fffff) / 4;
749 len = LE32TOH(list[0]) >> 24;
750 addr = LE32TOH(list[0]) & 0xffffff;
751 preload(rambase + (addr & 0x1fffff) / 4);
755 cpu_cycles += 5 + len;
757 log_io(".chain %08lx #%d+%d\n",
758 (long)(list - rambase) * 4, len, gpu.cmd_len);
759 if (unlikely(gpu.cmd_len > 0)) {
760 if (gpu.cmd_len + len > ARRAY_SIZE(gpu.cmd_buffer)) {
761 log_anomaly("cmd_buffer overflow, likely garbage commands\n");
764 memcpy(gpu.cmd_buffer + gpu.cmd_len, list + 1, len * 4);
771 left = do_cmd_buffer(list + 1, len, &cpu_cycles);
773 memcpy(gpu.cmd_buffer, list + 1 + len - left, left * 4);
775 log_anomaly("GPUdmaChain: %d/%d words left\n", left, len);
780 *progress_addr = addr;
783 #define LD_THRESHOLD (8*1024)
784 if (count >= LD_THRESHOLD) {
785 if (count == LD_THRESHOLD) {
790 // loop detection marker
791 // (bit23 set causes DMA error on real machine, so
792 // unlikely to be ever set by the game)
793 list[0] |= HTOLE32(0x800000);
798 // remove loop detection markers
799 count -= LD_THRESHOLD + 2;
800 addr = ld_addr & 0x1fffff;
801 while (count-- > 0) {
802 list = rambase + addr / 4;
803 addr = LE32TOH(list[0]) & 0x1fffff;
804 list[0] &= HTOLE32(~0x800000);
808 gpu.state.last_list.frame = *gpu.state.frame_count;
809 gpu.state.last_list.hcnt = *gpu.state.hcnt;
810 gpu.state.last_list.cycles = cpu_cycles;
811 gpu.state.last_list.addr = start_addr;
816 void GPUreadDataMem(uint32_t *mem, int count)
818 log_io("gpu_dma_read %p %d\n", mem, count);
820 if (unlikely(gpu.cmd_len > 0))
824 do_vram_io(mem, count, 1);
827 uint32_t GPUreadData(void)
831 if (unlikely(gpu.cmd_len > 0))
837 do_vram_io(&ret, 1, 1);
841 log_io("gpu_read %08x\n", ret);
845 uint32_t GPUreadStatus(void)
849 if (unlikely(gpu.cmd_len > 0))
853 log_io("gpu_read_status %08x\n", ret);
859 uint32_t ulFreezeVersion; // should be always 1 for now (set by main emu)
860 uint32_t ulStatus; // current gpu status
861 uint32_t ulControl[256]; // latest control register values
862 unsigned char psxVRam[1024*1024*2]; // current VRam image (full 2 MB for ZN)
865 long GPUfreeze(uint32_t type, struct GPUFreeze *freeze)
875 memcpy(freeze->psxVRam, gpu.vram, 1024 * 512 * 2);
876 memcpy(freeze->ulControl, gpu.regs, sizeof(gpu.regs));
877 memcpy(freeze->ulControl + 0xe0, gpu.ex_regs, sizeof(gpu.ex_regs));
878 freeze->ulStatus = gpu.status;
882 memcpy(gpu.vram, freeze->psxVRam, 1024 * 512 * 2);
883 memcpy(gpu.regs, freeze->ulControl, sizeof(gpu.regs));
884 memcpy(gpu.ex_regs, freeze->ulControl + 0xe0, sizeof(gpu.ex_regs));
885 gpu.status = freeze->ulStatus;
887 for (i = 8; i > 0; i--) {
888 gpu.regs[i] ^= 1; // avoid reg change detection
889 GPUwriteStatus((i << 24) | (gpu.regs[i] ^ 1));
891 renderer_sync_ecmds(gpu.ex_regs);
892 renderer_update_caches(0, 0, 1024, 512, 0);
899 void GPUupdateLace(void)
903 renderer_flush_queues();
905 #ifndef RAW_FB_DISPLAY
906 if (gpu.status & PSX_GPU_STATUS_BLANKING) {
907 if (!gpu.state.blanked) {
909 gpu.state.blanked = 1;
910 gpu.state.fb_dirty = 1;
915 renderer_notify_update_lace(0);
917 if (!gpu.state.fb_dirty)
921 if (gpu.frameskip.set) {
922 if (!gpu.frameskip.frame_ready) {
923 if (*gpu.state.frame_count - gpu.frameskip.last_flip_frame < 9)
925 gpu.frameskip.active = 0;
927 gpu.frameskip.frame_ready = 0;
931 if (gpu.state.enhancement_active && !gpu.state.enhancement_was_active)
932 renderer_update_caches(0, 0, 1024, 512, 1);
933 gpu.state.enhancement_was_active = gpu.state.enhancement_active;
934 gpu.state.fb_dirty = 0;
935 gpu.state.blanked = 0;
936 renderer_notify_update_lace(1);
939 void GPUvBlank(int is_vblank, int lcf)
941 int interlace = gpu.state.allow_interlace
942 && (gpu.status & PSX_GPU_STATUS_INTERLACE)
943 && (gpu.status & PSX_GPU_STATUS_DHEIGHT);
944 // interlace doesn't look nice on progressive displays,
945 // so we have this "auto" mode here for games that don't read vram
946 if (gpu.state.allow_interlace == 2
947 && *gpu.state.frame_count - gpu.state.last_vram_read_frame > 1)
951 if (interlace || interlace != gpu.state.old_interlace) {
952 gpu.state.old_interlace = interlace;
956 renderer_flush_queues();
957 renderer_set_interlace(interlace, !lcf);
961 void GPUgetScreenInfo(int *y, int *base_hres)
964 *base_hres = gpu.screen.vres;
965 if (gpu.status & PSX_GPU_STATUS_DHEIGHT)
969 void GPUrearmedCallbacks(const struct rearmed_cbs *cbs)
971 gpu.frameskip.set = cbs->frameskip;
972 gpu.frameskip.advice = &cbs->fskip_advice;
973 gpu.frameskip.force = &cbs->fskip_force;
974 gpu.frameskip.dirty = (void *)&cbs->fskip_dirty;
975 gpu.frameskip.active = 0;
976 gpu.frameskip.frame_ready = 1;
977 gpu.state.hcnt = cbs->gpu_hcnt;
978 gpu.state.frame_count = cbs->gpu_frame_count;
979 gpu.state.allow_interlace = cbs->gpu_neon.allow_interlace;
980 gpu.state.enhancement_enable = cbs->gpu_neon.enhancement_enable;
981 gpu.state.screen_centering_type_default = cbs->screen_centering_type_default;
982 if (gpu.state.screen_centering_type != cbs->screen_centering_type
983 || gpu.state.screen_centering_x != cbs->screen_centering_x
984 || gpu.state.screen_centering_y != cbs->screen_centering_y) {
985 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;
992 gpu.mmap = cbs->mmap;
993 gpu.munmap = cbs->munmap;
994 gpu.gpu_state_change = cbs->gpu_state_change;
997 if (gpu.vram == NULL)
1000 if (cbs->pl_vout_set_raw_vram)
1001 cbs->pl_vout_set_raw_vram(gpu.vram);
1002 renderer_set_config(cbs);
1003 vout_set_config(cbs);
1006 // vim:shiftwidth=2:expandtab