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
99 x = gpu.state.screen_centering_x;
102 // correct if slightly miscentered
103 x_auto = (hres - sw) / 2 & ~3;
104 if ((uint32_t)x_auto <= 8u && abs(x) < 24)
109 // .x range check is done in vout_update()
111 // reduce the unpleasant right border that a few games have
112 if (gpu.state.screen_centering_type == 0
113 && x <= 4 && hres - (x + sw) >= 4)
117 gpu.screen.hres = hres;
118 gpu.state.dims_changed = 1;
119 //printf("xx %d %d -> %2d, %d / %d\n",
120 // gpu.screen.x1, gpu.screen.x2, x, sw, hres);
123 static noinline void update_height(void)
125 int pal = gpu.status & PSX_GPU_STATUS_PAL;
126 int dheight = gpu.status & PSX_GPU_STATUS_DHEIGHT;
127 int y = gpu.screen.y1 - (pal ? 39 : 16); // 39 for spyro
128 int sh = gpu.screen.y2 - gpu.screen.y1;
132 if (pal && (sh > 240 || gpu.screen.vres == 256))
135 y *= 2, sh *= 2, vres *= 2, center_tol *= 2;
137 /* nothing displayed? */;
139 switch (gpu.state.screen_centering_type) {
146 y = gpu.state.screen_centering_y;
149 // correct if slightly miscentered
150 if ((uint32_t)(vres - sh) <= 1 && abs(y) <= center_tol)
158 gpu.screen.vres = vres;
159 gpu.state.dims_changed = 1;
160 //printf("yy %d %d -> %d, %d / %d\n",
161 // gpu.screen.y1, gpu.screen.y2, y, sh, vres);
164 static noinline void decide_frameskip(void)
166 *gpu.frameskip.dirty = 1;
168 if (gpu.frameskip.active)
171 gpu.frameskip.cnt = 0;
172 gpu.frameskip.frame_ready = 1;
175 if (*gpu.frameskip.force)
176 gpu.frameskip.active = 1;
177 else if (!gpu.frameskip.active && *gpu.frameskip.advice)
178 gpu.frameskip.active = 1;
179 else if (gpu.frameskip.set > 0 && gpu.frameskip.cnt < gpu.frameskip.set)
180 gpu.frameskip.active = 1;
182 gpu.frameskip.active = 0;
184 if (!gpu.frameskip.active && gpu.frameskip.pending_fill[0] != 0) {
186 do_cmd_list(gpu.frameskip.pending_fill, 3, &dummy, &dummy, &dummy);
187 gpu.frameskip.pending_fill[0] = 0;
191 static noinline int decide_frameskip_allow(uint32_t cmd_e3)
193 // no frameskip if it decides to draw to display area,
194 // but not for interlace since it'll most likely always do that
195 uint32_t x = cmd_e3 & 0x3ff;
196 uint32_t y = (cmd_e3 >> 10) & 0x3ff;
197 gpu.frameskip.allow = (gpu.status & PSX_GPU_STATUS_INTERLACE) ||
198 (uint32_t)(x - gpu.screen.src_x) >= (uint32_t)gpu.screen.w ||
199 (uint32_t)(y - gpu.screen.src_y) >= (uint32_t)gpu.screen.h;
200 return gpu.frameskip.allow;
203 static void flush_cmd_buffer(void);
205 static noinline void get_gpu_info(uint32_t data)
207 if (unlikely(gpu.cmd_len > 0))
209 switch (data & 0x0f) {
213 gpu.gp0 = gpu.ex_regs[data & 7] & 0xfffff;
216 gpu.gp0 = gpu.ex_regs[5] & 0x3fffff;
228 #define max(a, b) (((a) > (b)) ? (a) : (b))
231 // Minimum 16-byte VRAM alignment needed by gpu_unai's pixel-skipping
232 // renderer/downscaler it uses in high res modes:
234 // On GCW platform (MIPS), align to 8192 bytes (1 TLB entry) to reduce # of
235 // fills. (Will change this value if it ever gets large page support)
236 #define VRAM_ALIGN 8192
238 #define VRAM_ALIGN 16
241 // double, for overdraw guard + at least 1 page before
242 #define VRAM_SIZE ((1024 * 512 * 2 * 2) + max(VRAM_ALIGN, 4096))
244 // vram ptr received from mmap/malloc/alloc (will deallocate using this)
245 static uint16_t *vram_ptr_orig = NULL;
247 #ifndef GPULIB_USE_MMAP
249 # define GPULIB_USE_MMAP 1
251 # define GPULIB_USE_MMAP 0
254 static int map_vram(void)
257 gpu.vram = vram_ptr_orig = gpu.mmap(VRAM_SIZE);
259 gpu.vram = vram_ptr_orig = calloc(VRAM_SIZE, 1);
261 if (gpu.vram != NULL && gpu.vram != (void *)(intptr_t)-1) {
262 // 4kb guard in front
263 gpu.vram += (4096 / 2);
265 gpu.vram = (uint16_t*)(((uintptr_t)gpu.vram + (VRAM_ALIGN-1)) & ~(VRAM_ALIGN-1));
269 fprintf(stderr, "could not map vram, expect crashes\n");
278 ret |= renderer_init();
280 memset(&gpu.state, 0, sizeof(gpu.state));
281 memset(&gpu.frameskip, 0, sizeof(gpu.frameskip));
283 gpu.state.frame_count = &gpu.zero;
284 gpu.state.hcnt = &gpu.zero;
288 /*if (gpu.mmap != NULL) {
295 long GPUshutdown(void)
302 if (vram_ptr_orig != NULL) {
304 gpu.munmap(vram_ptr_orig, VRAM_SIZE);
309 vram_ptr_orig = gpu.vram = NULL;
314 void GPUwriteStatus(uint32_t data)
316 uint32_t cmd = data >> 24;
319 if (cmd < ARRAY_SIZE(gpu.regs)) {
320 if (cmd > 1 && cmd != 5 && gpu.regs[cmd] == data)
322 gpu.regs[cmd] = data;
325 gpu.state.fb_dirty = 1;
336 gpu.status |= PSX_GPU_STATUS_BLANKING;
337 gpu.state.dims_changed = 1; // for hud clearing
340 gpu.status &= ~PSX_GPU_STATUS_BLANKING;
343 gpu.status &= ~PSX_GPU_STATUS_DMA_MASK;
344 gpu.status |= PSX_GPU_STATUS_DMA(data & 3);
347 src_x = data & 0x3ff; src_y = (data >> 10) & 0x1ff;
348 if (src_x != gpu.screen.src_x || src_y != gpu.screen.src_y) {
349 gpu.screen.src_x = src_x;
350 gpu.screen.src_y = src_y;
351 renderer_notify_scanout_change(src_x, src_y);
352 if (gpu.frameskip.set) {
353 decide_frameskip_allow(gpu.ex_regs[3]);
354 if (gpu.frameskip.last_flip_frame != *gpu.state.frame_count) {
356 gpu.frameskip.last_flip_frame = *gpu.state.frame_count;
362 gpu.screen.x1 = data & 0xfff;
363 gpu.screen.x2 = (data >> 12) & 0xfff;
367 gpu.screen.y1 = data & 0x3ff;
368 gpu.screen.y2 = (data >> 10) & 0x3ff;
372 gpu.status = (gpu.status & ~0x7f0000) | ((data & 0x3F) << 17) | ((data & 0x40) << 10);
375 renderer_notify_res_change();
378 if ((cmd & 0xf0) == 0x10)
383 #ifdef GPUwriteStatus_ext
384 GPUwriteStatus_ext(data);
388 const unsigned char cmd_lengths[256] =
390 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
391 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
392 3, 3, 3, 3, 6, 6, 6, 6, 4, 4, 4, 4, 8, 8, 8, 8, // 20
393 5, 5, 5, 5, 8, 8, 8, 8, 7, 7, 7, 7, 11, 11, 11, 11,
394 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, // 40
395 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4,
396 2, 2, 2, 2, 3, 3, 3, 3, 1, 1, 1, 1, 0, 0, 0, 0, // 60
397 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 2, 2, 2, 2,
398 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 80
399 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
400 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // a0
401 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
402 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // c0
403 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
404 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // e0
405 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
408 #define VRAM_MEM_XY(x, y) &gpu.vram[(y) * 1024 + (x)]
410 static void cpy_msb(uint16_t *dst, const uint16_t *src, int l, uint16_t msb)
413 for (i = 0; i < l; i++)
414 dst[i] = src[i] | msb;
417 static inline void do_vram_line(int x, int y, uint16_t *mem, int l,
418 int is_read, uint16_t msb)
420 uint16_t *vram = VRAM_MEM_XY(x, y);
421 if (unlikely(is_read))
422 memcpy(mem, vram, l * 2);
423 else if (unlikely(msb))
424 cpy_msb(vram, mem, l, msb);
426 memcpy(vram, mem, l * 2);
429 static int do_vram_io(uint32_t *data, int count, int is_read)
431 int count_initial = count;
432 uint16_t msb = gpu.ex_regs[6] << 15;
433 uint16_t *sdata = (uint16_t *)data;
434 int x = gpu.dma.x, y = gpu.dma.y;
435 int w = gpu.dma.w, h = gpu.dma.h;
436 int o = gpu.dma.offset;
438 count *= 2; // operate in 16bpp pixels
442 if (gpu.dma.offset) {
443 l = w - gpu.dma.offset;
447 do_vram_line(x + o, y, sdata, l, is_read, msb);
460 for (; h > 0 && count >= w; sdata += w, count -= w, y++, h--) {
462 do_vram_line(x, y, sdata, w, is_read, msb);
468 do_vram_line(x, y, sdata, count, is_read, msb);
474 finish_vram_transfer(is_read);
479 return count_initial - count / 2;
482 static void start_vram_transfer(uint32_t pos_word, uint32_t size_word, int is_read)
485 log_anomaly("start_vram_transfer while old unfinished\n");
487 gpu.dma.x = pos_word & 0x3ff;
488 gpu.dma.y = (pos_word >> 16) & 0x1ff;
489 gpu.dma.w = ((size_word - 1) & 0x3ff) + 1;
490 gpu.dma.h = (((size_word >> 16) - 1) & 0x1ff) + 1;
492 gpu.dma.is_read = is_read;
493 gpu.dma_start = gpu.dma;
495 renderer_flush_queues();
497 gpu.status |= PSX_GPU_STATUS_IMG;
498 // XXX: wrong for width 1
499 gpu.gp0 = LE32TOH(*(uint32_t *) VRAM_MEM_XY(gpu.dma.x, gpu.dma.y));
500 gpu.state.last_vram_read_frame = *gpu.state.frame_count;
503 log_io("start_vram_transfer %c (%d, %d) %dx%d\n", is_read ? 'r' : 'w',
504 gpu.dma.x, gpu.dma.y, gpu.dma.w, gpu.dma.h);
505 if (gpu.gpu_state_change)
506 gpu.gpu_state_change(PGS_VRAM_TRANSFER_START);
509 static void finish_vram_transfer(int is_read)
512 gpu.status &= ~PSX_GPU_STATUS_IMG;
514 gpu.state.fb_dirty = 1;
515 renderer_update_caches(gpu.dma_start.x, gpu.dma_start.y,
516 gpu.dma_start.w, gpu.dma_start.h, 0);
518 if (gpu.gpu_state_change)
519 gpu.gpu_state_change(PGS_VRAM_TRANSFER_END);
522 static void do_vram_copy(const uint32_t *params, int *cpu_cycles)
524 const uint32_t sx = LE32TOH(params[0]) & 0x3FF;
525 const uint32_t sy = (LE32TOH(params[0]) >> 16) & 0x1FF;
526 const uint32_t dx = LE32TOH(params[1]) & 0x3FF;
527 const uint32_t dy = (LE32TOH(params[1]) >> 16) & 0x1FF;
528 uint32_t w = ((LE32TOH(params[2]) - 1) & 0x3FF) + 1;
529 uint32_t h = (((LE32TOH(params[2]) >> 16) - 1) & 0x1FF) + 1;
530 uint16_t msb = gpu.ex_regs[6] << 15;
534 *cpu_cycles += gput_copy(w, h);
535 if (sx == dx && sy == dy && msb == 0)
538 renderer_flush_queues();
540 if (unlikely((sx < dx && dx < sx + w) || sx + w > 1024 || dx + w > 1024 || msb))
542 for (y = 0; y < h; y++)
544 const uint16_t *src = VRAM_MEM_XY(0, (sy + y) & 0x1ff);
545 uint16_t *dst = VRAM_MEM_XY(0, (dy + y) & 0x1ff);
546 for (x = 0; x < w; x += ARRAY_SIZE(lbuf))
548 uint32_t x1, w1 = w - x;
549 if (w1 > ARRAY_SIZE(lbuf))
550 w1 = ARRAY_SIZE(lbuf);
551 for (x1 = 0; x1 < w1; x1++)
552 lbuf[x1] = src[(sx + x + x1) & 0x3ff];
553 for (x1 = 0; x1 < w1; x1++)
554 dst[(dx + x + x1) & 0x3ff] = lbuf[x1] | msb;
560 uint32_t sy1 = sy, dy1 = dy;
561 for (y = 0; y < h; y++, sy1++, dy1++)
562 memcpy(VRAM_MEM_XY(dx, dy1 & 0x1ff), VRAM_MEM_XY(sx, sy1 & 0x1ff), w * 2);
565 renderer_update_caches(dx, dy, w, h, 0);
568 static noinline int do_cmd_list_skip(uint32_t *data, int count, int *last_cmd)
570 int cmd = 0, pos = 0, len, dummy = 0, v;
573 gpu.frameskip.pending_fill[0] = 0;
575 while (pos < count && skip) {
576 uint32_t *list = data + pos;
577 cmd = LE32TOH(list[0]) >> 24;
578 len = 1 + cmd_lengths[cmd];
582 if ((LE32TOH(list[2]) & 0x3ff) > gpu.screen.w || ((LE32TOH(list[2]) >> 16) & 0x1ff) > gpu.screen.h)
583 // clearing something large, don't skip
584 do_cmd_list(list, 3, &dummy, &dummy, &dummy);
586 memcpy(gpu.frameskip.pending_fill, list, 3 * 4);
592 gpu.ex_regs[1] &= ~0x1ff;
593 gpu.ex_regs[1] |= LE32TOH(list[4 + ((cmd >> 4) & 1)]) & 0x1ff;
596 for (v = 3; pos + v < count; v++)
598 if ((list[v] & HTOLE32(0xf000f000)) == HTOLE32(0x50005000))
604 for (v = 4; pos + v < count; v += 2)
606 if ((list[v] & HTOLE32(0xf000f000)) == HTOLE32(0x50005000))
613 skip = decide_frameskip_allow(LE32TOH(list[0]));
614 if ((cmd & 0xf8) == 0xe0)
615 gpu.ex_regs[cmd & 7] = LE32TOH(list[0]);
619 if (pos + len > count) {
621 break; // incomplete cmd
623 if (0x80 <= cmd && cmd <= 0xdf)
629 renderer_sync_ecmds(gpu.ex_regs);
634 static noinline int do_cmd_buffer(uint32_t *data, int count,
635 int *cycles_sum, int *cycles_last)
638 uint32_t old_e3 = gpu.ex_regs[3];
642 for (pos = 0; pos < count; )
644 if (gpu.dma.h && !gpu.dma_start.is_read) { // XXX: need to verify
646 pos += do_vram_io(data + pos, count - pos, 0);
651 cmd = LE32TOH(data[pos]) >> 24;
652 if (0xa0 <= cmd && cmd <= 0xdf) {
653 if (unlikely((pos+2) >= count)) {
654 // incomplete vram write/read cmd, can't consume yet
659 // consume vram write/read cmd
660 start_vram_transfer(LE32TOH(data[pos + 1]), LE32TOH(data[pos + 2]), (cmd & 0xe0) == 0xc0);
664 else if ((cmd & 0xe0) == 0x80) {
665 if (unlikely((pos+3) >= count)) {
666 cmd = -1; // incomplete cmd, can't consume yet
670 *cycles_sum += *cycles_last;
672 do_vram_copy(data + pos + 1, cycles_last);
677 else if (cmd == 0x1f) {
678 log_anomaly("irq1?\n");
683 // 0xex cmds might affect frameskip.allow, so pass to do_cmd_list_skip
684 if (gpu.frameskip.active && (gpu.frameskip.allow || ((LE32TOH(data[pos]) >> 24) & 0xf0) == 0xe0))
685 pos += do_cmd_list_skip(data + pos, count - pos, &cmd);
687 pos += do_cmd_list(data + pos, count - pos, cycles_sum, cycles_last, &cmd);
696 gpu.status &= ~0x1fff;
697 gpu.status |= gpu.ex_regs[1] & 0x7ff;
698 gpu.status |= (gpu.ex_regs[6] & 3) << 11;
700 gpu.state.fb_dirty |= vram_dirty;
702 if (old_e3 != gpu.ex_regs[3])
703 decide_frameskip_allow(gpu.ex_regs[3]);
708 static noinline void flush_cmd_buffer(void)
711 left = do_cmd_buffer(gpu.cmd_buffer, gpu.cmd_len, &dummy, &dummy);
713 memmove(gpu.cmd_buffer, gpu.cmd_buffer + gpu.cmd_len - left, left * 4);
714 if (left != gpu.cmd_len) {
715 if (!gpu.dma.h && gpu.gpu_state_change)
716 gpu.gpu_state_change(PGS_PRIMITIVE_START);
721 void GPUwriteDataMem(uint32_t *mem, int count)
725 log_io("gpu_dma_write %p %d\n", mem, count);
727 if (unlikely(gpu.cmd_len > 0))
730 left = do_cmd_buffer(mem, count, &dummy, &dummy);
732 log_anomaly("GPUwriteDataMem: discarded %d/%d words\n", left, count);
735 void GPUwriteData(uint32_t data)
737 log_io("gpu_write %08x\n", data);
738 gpu.cmd_buffer[gpu.cmd_len++] = HTOLE32(data);
739 if (gpu.cmd_len >= CMD_BUFFER_LEN)
743 long GPUdmaChain(uint32_t *rambase, uint32_t start_addr,
744 uint32_t *progress_addr, int32_t *cycles_last_cmd)
746 uint32_t addr, *list, ld_addr;
747 int len, left, count, ld_count = 32;
748 int cpu_cycles_sum = 0;
749 int cpu_cycles_last = 0;
751 preload(rambase + (start_addr & 0x1fffff) / 4);
753 if (unlikely(gpu.cmd_len > 0))
756 log_io("gpu_dma_chain\n");
757 addr = ld_addr = start_addr & 0xffffff;
758 for (count = 0; (addr & 0x800000) == 0; count++)
760 list = rambase + (addr & 0x1fffff) / 4;
761 len = LE32TOH(list[0]) >> 24;
762 addr = LE32TOH(list[0]) & 0xffffff;
763 preload(rambase + (addr & 0x1fffff) / 4);
765 cpu_cycles_sum += 10;
767 cpu_cycles_sum += 5 + len;
769 log_io(".chain %08lx #%d+%d %u+%u\n",
770 (long)(list - rambase) * 4, len, gpu.cmd_len, cpu_cycles_sum, cpu_cycles_last);
771 if (unlikely(gpu.cmd_len > 0)) {
772 if (gpu.cmd_len + len > ARRAY_SIZE(gpu.cmd_buffer)) {
773 log_anomaly("cmd_buffer overflow, likely garbage commands\n");
776 memcpy(gpu.cmd_buffer + gpu.cmd_len, list + 1, len * 4);
783 left = do_cmd_buffer(list + 1, len, &cpu_cycles_sum, &cpu_cycles_last);
785 memcpy(gpu.cmd_buffer, list + 1 + len - left, left * 4);
787 log_anomaly("GPUdmaChain: %d/%d words left\n", left, len);
792 *progress_addr = addr;
795 if (addr == ld_addr) {
796 log_anomaly("GPUdmaChain: loop @ %08x, cnt=%u\n", addr, count);
799 if (count == ld_count) {
805 //printf(" -> %d %d\n", cpu_cycles_sum, cpu_cycles_last);
806 gpu.state.last_list.frame = *gpu.state.frame_count;
807 gpu.state.last_list.hcnt = *gpu.state.hcnt;
808 gpu.state.last_list.cycles = cpu_cycles_sum + cpu_cycles_last;
809 gpu.state.last_list.addr = start_addr;
811 *cycles_last_cmd = cpu_cycles_last;
812 return cpu_cycles_sum;
815 void GPUreadDataMem(uint32_t *mem, int count)
817 log_io("gpu_dma_read %p %d\n", mem, count);
819 if (unlikely(gpu.cmd_len > 0))
823 do_vram_io(mem, count, 1);
826 uint32_t GPUreadData(void)
830 if (unlikely(gpu.cmd_len > 0))
836 do_vram_io(&ret, 1, 1);
840 log_io("gpu_read %08x\n", ret);
844 uint32_t GPUreadStatus(void)
848 if (unlikely(gpu.cmd_len > 0))
852 log_io("gpu_read_status %08x\n", ret);
858 uint32_t ulFreezeVersion; // should be always 1 for now (set by main emu)
859 uint32_t ulStatus; // current gpu status
860 uint32_t ulControl[256]; // latest control register values
861 unsigned char psxVRam[1024*1024*2]; // current VRam image (full 2 MB for ZN)
864 long GPUfreeze(uint32_t type, struct GPUFreeze *freeze)
874 memcpy(freeze->psxVRam, gpu.vram, 1024 * 512 * 2);
875 memcpy(freeze->ulControl, gpu.regs, sizeof(gpu.regs));
876 memcpy(freeze->ulControl + 0xe0, gpu.ex_regs, sizeof(gpu.ex_regs));
877 freeze->ulStatus = gpu.status;
881 memcpy(gpu.vram, freeze->psxVRam, 1024 * 512 * 2);
882 memcpy(gpu.regs, freeze->ulControl, sizeof(gpu.regs));
883 memcpy(gpu.ex_regs, freeze->ulControl + 0xe0, sizeof(gpu.ex_regs));
884 gpu.status = freeze->ulStatus;
886 for (i = 8; i > 0; i--) {
887 gpu.regs[i] ^= 1; // avoid reg change detection
888 GPUwriteStatus((i << 24) | (gpu.regs[i] ^ 1));
890 renderer_sync_ecmds(gpu.ex_regs);
891 renderer_update_caches(0, 0, 1024, 512, 0);
898 void GPUupdateLace(void)
902 renderer_flush_queues();
904 #ifndef RAW_FB_DISPLAY
905 if (gpu.status & PSX_GPU_STATUS_BLANKING) {
906 if (!gpu.state.blanked) {
908 gpu.state.blanked = 1;
909 gpu.state.fb_dirty = 1;
914 renderer_notify_update_lace(0);
916 if (!gpu.state.fb_dirty)
920 if (gpu.frameskip.set) {
921 if (!gpu.frameskip.frame_ready) {
922 if (*gpu.state.frame_count - gpu.frameskip.last_flip_frame < 9)
924 gpu.frameskip.active = 0;
926 gpu.frameskip.frame_ready = 0;
930 if (gpu.state.enhancement_active && !gpu.state.enhancement_was_active)
931 renderer_update_caches(0, 0, 1024, 512, 1);
932 gpu.state.enhancement_was_active = gpu.state.enhancement_active;
933 gpu.state.fb_dirty = 0;
934 gpu.state.blanked = 0;
935 renderer_notify_update_lace(1);
938 void GPUvBlank(int is_vblank, int lcf)
940 int interlace = gpu.state.allow_interlace
941 && (gpu.status & PSX_GPU_STATUS_INTERLACE)
942 && (gpu.status & PSX_GPU_STATUS_DHEIGHT);
943 // interlace doesn't look nice on progressive displays,
944 // so we have this "auto" mode here for games that don't read vram
945 if (gpu.state.allow_interlace == 2
946 && *gpu.state.frame_count - gpu.state.last_vram_read_frame > 1)
950 if (interlace || interlace != gpu.state.old_interlace) {
951 gpu.state.old_interlace = interlace;
955 renderer_flush_queues();
956 renderer_set_interlace(interlace, !lcf);
960 void GPUgetScreenInfo(int *y, int *base_hres)
963 *base_hres = gpu.screen.vres;
964 if (gpu.status & PSX_GPU_STATUS_DHEIGHT)
968 void GPUrearmedCallbacks(const struct rearmed_cbs *cbs)
970 gpu.frameskip.set = cbs->frameskip;
971 gpu.frameskip.advice = &cbs->fskip_advice;
972 gpu.frameskip.force = &cbs->fskip_force;
973 gpu.frameskip.dirty = (void *)&cbs->fskip_dirty;
974 gpu.frameskip.active = 0;
975 gpu.frameskip.frame_ready = 1;
976 gpu.state.hcnt = cbs->gpu_hcnt;
977 gpu.state.frame_count = cbs->gpu_frame_count;
978 gpu.state.allow_interlace = cbs->gpu_neon.allow_interlace;
979 gpu.state.enhancement_enable = cbs->gpu_neon.enhancement_enable;
980 gpu.state.screen_centering_type_default = cbs->screen_centering_type_default;
981 if (gpu.state.screen_centering_type != cbs->screen_centering_type
982 || gpu.state.screen_centering_x != cbs->screen_centering_x
983 || gpu.state.screen_centering_y != cbs->screen_centering_y) {
984 gpu.state.screen_centering_type = cbs->screen_centering_type;
985 gpu.state.screen_centering_x = cbs->screen_centering_x;
986 gpu.state.screen_centering_y = cbs->screen_centering_y;
991 gpu.mmap = cbs->mmap;
992 gpu.munmap = cbs->munmap;
993 gpu.gpu_state_change = cbs->gpu_state_change;
996 if (gpu.vram == NULL)
999 if (cbs->pl_vout_set_raw_vram)
1000 cbs->pl_vout_set_raw_vram(gpu.vram);
1001 renderer_set_config(cbs);
1002 vout_set_config(cbs);
1005 // vim:shiftwidth=2:expandtab