frontend: Fix colorspace conversion routines on big-endian
[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 <string.h>
13#include <stdlib.h> /* for calloc */
14
15#include "gpu.h"
16
17#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
18#ifdef __GNUC__
19#define unlikely(x) __builtin_expect((x), 0)
20#define preload __builtin_prefetch
21#define noinline __attribute__((noinline))
22#else
23#define unlikely(x)
24#define preload(...)
25#define noinline
26#endif
27
28#define gpu_log(fmt, ...) \
29 printf("%d:%03d: " fmt, *gpu.state.frame_count, *gpu.state.hcnt, ##__VA_ARGS__)
30
31//#define log_io gpu_log
32#define log_io(...)
33//#define log_anomaly gpu_log
34#define log_anomaly(...)
35
36struct psx_gpu gpu;
37
38static noinline int do_cmd_buffer(uint32_t *data, int count);
39static void finish_vram_transfer(int is_read);
40
41static noinline void do_cmd_reset(void)
42{
43 renderer_sync();
44
45 if (unlikely(gpu.cmd_len > 0))
46 do_cmd_buffer(gpu.cmd_buffer, gpu.cmd_len);
47 gpu.cmd_len = 0;
48
49 if (unlikely(gpu.dma.h > 0))
50 finish_vram_transfer(gpu.dma_start.is_read);
51 gpu.dma.h = 0;
52}
53
54static noinline void do_reset(void)
55{
56 unsigned int i;
57 do_cmd_reset();
58
59 memset(gpu.regs, 0, sizeof(gpu.regs));
60 for (i = 0; i < sizeof(gpu.ex_regs) / sizeof(gpu.ex_regs[0]); i++)
61 gpu.ex_regs[i] = (0xe0 + i) << 24;
62 gpu.status = 0x14802000;
63 gpu.gp0 = 0;
64 gpu.regs[3] = 1;
65 gpu.screen.hres = gpu.screen.w = 256;
66 gpu.screen.vres = gpu.screen.h = 240;
67}
68
69static noinline void update_width(void)
70{
71 int sw = gpu.screen.x2 - gpu.screen.x1;
72 if (sw <= 0 || sw >= 2560)
73 // full width
74 gpu.screen.w = gpu.screen.hres;
75 else
76 gpu.screen.w = sw * gpu.screen.hres / 2560;
77}
78
79static noinline void update_height(void)
80{
81 // TODO: emulate this properly..
82 int sh = gpu.screen.y2 - gpu.screen.y1;
83 if (gpu.status & PSX_GPU_STATUS_DHEIGHT)
84 sh *= 2;
85 if (sh <= 0 || sh > gpu.screen.vres)
86 sh = gpu.screen.vres;
87
88 gpu.screen.h = sh;
89}
90
91static noinline void decide_frameskip(void)
92{
93 *gpu.frameskip.dirty = 1;
94
95 if (gpu.frameskip.active)
96 gpu.frameskip.cnt++;
97 else {
98 gpu.frameskip.cnt = 0;
99 gpu.frameskip.frame_ready = 1;
100 }
101
102 if (*gpu.frameskip.force)
103 gpu.frameskip.active = 1;
104 else if (!gpu.frameskip.active && *gpu.frameskip.advice)
105 gpu.frameskip.active = 1;
106 else if (gpu.frameskip.set > 0 && gpu.frameskip.cnt < gpu.frameskip.set)
107 gpu.frameskip.active = 1;
108 else
109 gpu.frameskip.active = 0;
110
111 if (!gpu.frameskip.active && gpu.frameskip.pending_fill[0] != 0) {
112 int dummy;
113 do_cmd_list(gpu.frameskip.pending_fill, 3, &dummy);
114 gpu.frameskip.pending_fill[0] = 0;
115 }
116}
117
118static noinline int decide_frameskip_allow(uint32_t cmd_e3)
119{
120 // no frameskip if it decides to draw to display area,
121 // but not for interlace since it'll most likely always do that
122 uint32_t x = cmd_e3 & 0x3ff;
123 uint32_t y = (cmd_e3 >> 10) & 0x3ff;
124 gpu.frameskip.allow = (gpu.status & PSX_GPU_STATUS_INTERLACE) ||
125 (uint32_t)(x - gpu.screen.x) >= (uint32_t)gpu.screen.w ||
126 (uint32_t)(y - gpu.screen.y) >= (uint32_t)gpu.screen.h;
127 return gpu.frameskip.allow;
128}
129
130static noinline void get_gpu_info(uint32_t data)
131{
132 switch (data & 0x0f) {
133 case 0x02:
134 case 0x03:
135 case 0x04:
136 gpu.gp0 = gpu.ex_regs[data & 7] & 0xfffff;
137 break;
138 case 0x05:
139 case 0x06:
140 gpu.gp0 = gpu.ex_regs[5] & 0x3fffff;
141 break;
142 case 0x07:
143 gpu.gp0 = 2;
144 break;
145 default:
146 gpu.gp0 = 0;
147 break;
148 }
149}
150
151// double, for overdraw guard
152#define VRAM_SIZE ((1024 * 512 * 2 * 2) + 4096)
153
154// Minimum 16-byte VRAM alignment needed by gpu_unai's pixel-skipping
155// renderer/downscaler it uses in high res modes:
156#ifdef GCW_ZERO
157 // On GCW platform (MIPS), align to 8192 bytes (1 TLB entry) to reduce # of
158 // fills. (Will change this value if it ever gets large page support)
159 #define VRAM_ALIGN 8192
160#else
161 #define VRAM_ALIGN 16
162#endif
163
164// vram ptr received from mmap/malloc/alloc (will deallocate using this)
165static uint16_t *vram_ptr_orig = NULL;
166
167#ifdef GPULIB_USE_MMAP
168static int map_vram(void)
169{
170 gpu.vram = vram_ptr_orig = gpu.mmap(VRAM_SIZE + (VRAM_ALIGN-1));
171 if (gpu.vram != NULL) {
172 // 4kb guard in front
173 gpu.vram += (4096 / 2);
174 // Align
175 gpu.vram = (uint16_t*)(((uintptr_t)gpu.vram + (VRAM_ALIGN-1)) & ~(VRAM_ALIGN-1));
176 return 0;
177 }
178 else {
179 fprintf(stderr, "could not map vram, expect crashes\n");
180 return -1;
181 }
182}
183#else
184static int map_vram(void)
185{
186 gpu.vram = vram_ptr_orig = (uint16_t*)calloc(VRAM_SIZE + (VRAM_ALIGN-1), 1);
187 if (gpu.vram != NULL) {
188 // 4kb guard in front
189 gpu.vram += (4096 / 2);
190 // Align
191 gpu.vram = (uint16_t*)(((uintptr_t)gpu.vram + (VRAM_ALIGN-1)) & ~(VRAM_ALIGN-1));
192 return 0;
193 } else {
194 fprintf(stderr, "could not allocate vram, expect crashes\n");
195 return -1;
196 }
197}
198
199static int allocate_vram(void)
200{
201 gpu.vram = vram_ptr_orig = (uint16_t*)calloc(VRAM_SIZE + (VRAM_ALIGN-1), 1);
202 if (gpu.vram != NULL) {
203 // 4kb guard in front
204 gpu.vram += (4096 / 2);
205 // Align
206 gpu.vram = (uint16_t*)(((uintptr_t)gpu.vram + (VRAM_ALIGN-1)) & ~(VRAM_ALIGN-1));
207 return 0;
208 } else {
209 fprintf(stderr, "could not allocate vram, expect crashes\n");
210 return -1;
211 }
212}
213#endif
214
215long GPUinit(void)
216{
217#ifndef GPULIB_USE_MMAP
218 if (gpu.vram == NULL) {
219 if (allocate_vram() != 0) {
220 printf("ERROR: could not allocate VRAM, exiting..\n");
221 exit(1);
222 }
223 }
224#endif
225
226 //extern uint32_t hSyncCount; // in psxcounters.cpp
227 //extern uint32_t frame_counter; // in psxcounters.cpp
228 //gpu.state.hcnt = &hSyncCount;
229 //gpu.state.frame_count = &frame_counter;
230
231 int ret;
232 ret = vout_init();
233 ret |= renderer_init();
234
235 gpu.state.frame_count = &gpu.zero;
236 gpu.state.hcnt = &gpu.zero;
237 gpu.frameskip.active = 0;
238 gpu.cmd_len = 0;
239 do_reset();
240
241 /*if (gpu.mmap != NULL) {
242 if (map_vram() != 0)
243 ret = -1;
244 }*/
245 return ret;
246}
247
248long GPUshutdown(void)
249{
250 long ret;
251
252 renderer_finish();
253 ret = vout_finish();
254
255 if (vram_ptr_orig != NULL) {
256#ifdef GPULIB_USE_MMAP
257 gpu.munmap(vram_ptr_orig, VRAM_SIZE);
258#else
259 free(vram_ptr_orig);
260#endif
261 }
262 vram_ptr_orig = gpu.vram = NULL;
263
264 return ret;
265}
266
267void GPUwriteStatus(uint32_t data)
268{
269 //senquack TODO: Would it be wise to add cmd buffer flush here, since
270 // status settings can affect commands already in buffer?
271
272 static const short hres[8] = { 256, 368, 320, 384, 512, 512, 640, 640 };
273 static const short vres[4] = { 240, 480, 256, 480 };
274 uint32_t cmd = data >> 24;
275
276 if (cmd < ARRAY_SIZE(gpu.regs)) {
277 if (cmd > 1 && cmd != 5 && gpu.regs[cmd] == data)
278 return;
279 gpu.regs[cmd] = data;
280 }
281
282 gpu.state.fb_dirty = 1;
283
284 switch (cmd) {
285 case 0x00:
286 do_reset();
287 break;
288 case 0x01:
289 do_cmd_reset();
290 break;
291 case 0x03:
292 if (data & 1)
293 gpu.status |= PSX_GPU_STATUS_BLANKING;
294 else
295 gpu.status &= ~PSX_GPU_STATUS_BLANKING;
296 break;
297 case 0x04:
298 gpu.status &= ~PSX_GPU_STATUS_DMA_MASK;
299 gpu.status |= PSX_GPU_STATUS_DMA(data & 3);
300 break;
301 case 0x05:
302 gpu.screen.x = data & 0x3ff;
303 gpu.screen.y = (data >> 10) & 0x1ff;
304 if (gpu.frameskip.set) {
305 decide_frameskip_allow(gpu.ex_regs[3]);
306 if (gpu.frameskip.last_flip_frame != *gpu.state.frame_count) {
307 decide_frameskip();
308 gpu.frameskip.last_flip_frame = *gpu.state.frame_count;
309 }
310 }
311 break;
312 case 0x06:
313 gpu.screen.x1 = data & 0xfff;
314 gpu.screen.x2 = (data >> 12) & 0xfff;
315 update_width();
316 break;
317 case 0x07:
318 gpu.screen.y1 = data & 0x3ff;
319 gpu.screen.y2 = (data >> 10) & 0x3ff;
320 update_height();
321 break;
322 case 0x08:
323 gpu.status = (gpu.status & ~0x7f0000) | ((data & 0x3F) << 17) | ((data & 0x40) << 10);
324 gpu.screen.hres = hres[(gpu.status >> 16) & 7];
325 gpu.screen.vres = vres[(gpu.status >> 19) & 3];
326 update_width();
327 update_height();
328 renderer_notify_res_change();
329 break;
330 default:
331 if ((cmd & 0xf0) == 0x10)
332 get_gpu_info(data);
333 break;
334 }
335
336#ifdef GPUwriteStatus_ext
337 GPUwriteStatus_ext(data);
338#endif
339}
340
341const unsigned char cmd_lengths[256] =
342{
343 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
344 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
345 3, 3, 3, 3, 6, 6, 6, 6, 4, 4, 4, 4, 8, 8, 8, 8, // 20
346 5, 5, 5, 5, 8, 8, 8, 8, 7, 7, 7, 7, 11, 11, 11, 11,
347 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, // 40
348 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4,
349 2, 2, 2, 2, 3, 3, 3, 3, 1, 1, 1, 1, 0, 0, 0, 0, // 60
350 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 2, 2, 2, 2,
351 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 80
352 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
353 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // a0
354 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
355 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // c0
356 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
357 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // e0
358 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
359};
360
361#define VRAM_MEM_XY(x, y) &gpu.vram[(y) * 1024 + (x)]
362
363static inline void do_vram_line(int x, int y, uint16_t *mem, int l, int is_read)
364{
365 uint16_t *vram = VRAM_MEM_XY(x, y);
366 if (is_read)
367 memcpy(mem, vram, l * 2);
368 else
369 memcpy(vram, mem, l * 2);
370}
371
372static int do_vram_io(uint32_t *data, int count, int is_read)
373{
374 int count_initial = count;
375 uint16_t *sdata = (uint16_t *)data;
376 int x = gpu.dma.x, y = gpu.dma.y;
377 int w = gpu.dma.w, h = gpu.dma.h;
378 int o = gpu.dma.offset;
379 int l;
380 count *= 2; // operate in 16bpp pixels
381
382 renderer_sync();
383
384 if (gpu.dma.offset) {
385 l = w - gpu.dma.offset;
386 if (count < l)
387 l = count;
388
389 do_vram_line(x + o, y, sdata, l, is_read);
390
391 if (o + l < w)
392 o += l;
393 else {
394 o = 0;
395 y++;
396 h--;
397 }
398 sdata += l;
399 count -= l;
400 }
401
402 for (; h > 0 && count >= w; sdata += w, count -= w, y++, h--) {
403 y &= 511;
404 do_vram_line(x, y, sdata, w, is_read);
405 }
406
407 if (h > 0) {
408 if (count > 0) {
409 y &= 511;
410 do_vram_line(x, y, sdata, count, is_read);
411 o = count;
412 count = 0;
413 }
414 }
415 else
416 finish_vram_transfer(is_read);
417 gpu.dma.y = y;
418 gpu.dma.h = h;
419 gpu.dma.offset = o;
420
421 return count_initial - count / 2;
422}
423
424static void start_vram_transfer(uint32_t pos_word, uint32_t size_word, int is_read)
425{
426 if (gpu.dma.h)
427 log_anomaly("start_vram_transfer while old unfinished\n");
428
429 gpu.dma.x = pos_word & 0x3ff;
430 gpu.dma.y = (pos_word >> 16) & 0x1ff;
431 gpu.dma.w = ((size_word - 1) & 0x3ff) + 1;
432 gpu.dma.h = (((size_word >> 16) - 1) & 0x1ff) + 1;
433 gpu.dma.offset = 0;
434 gpu.dma.is_read = is_read;
435 gpu.dma_start = gpu.dma;
436
437 renderer_flush_queues();
438 if (is_read) {
439 gpu.status |= PSX_GPU_STATUS_IMG;
440 // XXX: wrong for width 1
441 memcpy(&gpu.gp0, VRAM_MEM_XY(gpu.dma.x, gpu.dma.y), 4);
442 gpu.state.last_vram_read_frame = *gpu.state.frame_count;
443 }
444
445 log_io("start_vram_transfer %c (%d, %d) %dx%d\n", is_read ? 'r' : 'w',
446 gpu.dma.x, gpu.dma.y, gpu.dma.w, gpu.dma.h);
447}
448
449static void finish_vram_transfer(int is_read)
450{
451 if (is_read)
452 gpu.status &= ~PSX_GPU_STATUS_IMG;
453 else
454 renderer_update_caches(gpu.dma_start.x, gpu.dma_start.y,
455 gpu.dma_start.w, gpu.dma_start.h);
456}
457
458static noinline int do_cmd_list_skip(uint32_t *data, int count, int *last_cmd)
459{
460 int cmd = 0, pos = 0, len, dummy, v;
461 int skip = 1;
462
463 gpu.frameskip.pending_fill[0] = 0;
464
465 while (pos < count && skip) {
466 uint32_t *list = data + pos;
467 cmd = LE32TOH(list[0]) >> 24;
468 len = 1 + cmd_lengths[cmd];
469
470 switch (cmd) {
471 case 0x02:
472 if ((LE32TOH(list[2]) & 0x3ff) > gpu.screen.w || ((LE32TOH(list[2]) >> 16) & 0x1ff) > gpu.screen.h)
473 // clearing something large, don't skip
474 do_cmd_list(list, 3, &dummy);
475 else
476 memcpy(gpu.frameskip.pending_fill, list, 3 * 4);
477 break;
478 case 0x24 ... 0x27:
479 case 0x2c ... 0x2f:
480 case 0x34 ... 0x37:
481 case 0x3c ... 0x3f:
482 gpu.ex_regs[1] &= ~0x1ff;
483 gpu.ex_regs[1] |= LE32TOH(list[4 + ((cmd >> 4) & 1)]) & 0x1ff;
484 break;
485 case 0x48 ... 0x4F:
486 for (v = 3; pos + v < count; v++)
487 {
488 if ((list[v] & HTOLE32(0xf000f000)) == HTOLE32(0x50005000))
489 break;
490 }
491 len += v - 3;
492 break;
493 case 0x58 ... 0x5F:
494 for (v = 4; pos + v < count; v += 2)
495 {
496 if ((list[v] & HTOLE32(0xf000f000)) == HTOLE32(0x50005000))
497 break;
498 }
499 len += v - 4;
500 break;
501 default:
502 if (cmd == 0xe3)
503 skip = decide_frameskip_allow(LE32TOH(list[0]));
504 if ((cmd & 0xf8) == 0xe0)
505 gpu.ex_regs[cmd & 7] = LE32TOH(list[0]);
506 break;
507 }
508
509 if (pos + len > count) {
510 cmd = -1;
511 break; // incomplete cmd
512 }
513 if (0xa0 <= cmd && cmd <= 0xdf)
514 break; // image i/o
515
516 pos += len;
517 }
518
519 renderer_sync_ecmds(gpu.ex_regs);
520 *last_cmd = cmd;
521 return pos;
522}
523
524static noinline int do_cmd_buffer(uint32_t *data, int count)
525{
526 int cmd, pos;
527 uint32_t old_e3 = gpu.ex_regs[3];
528 int vram_dirty = 0;
529
530 // process buffer
531 for (pos = 0; pos < count; )
532 {
533 if (gpu.dma.h && !gpu.dma_start.is_read) { // XXX: need to verify
534 vram_dirty = 1;
535 pos += do_vram_io(data + pos, count - pos, 0);
536 if (pos == count)
537 break;
538 }
539
540 cmd = LE32TOH(data[pos]) >> 24;
541 if (0xa0 <= cmd && cmd <= 0xdf) {
542 if (unlikely((pos+2) >= count)) {
543 // incomplete vram write/read cmd, can't consume yet
544 cmd = -1;
545 break;
546 }
547
548 // consume vram write/read cmd
549 start_vram_transfer(LE32TOH(data[pos + 1]), LE32TOH(data[pos + 2]), (cmd & 0xe0) == 0xc0);
550 pos += 3;
551 continue;
552 }
553
554 // 0xex cmds might affect frameskip.allow, so pass to do_cmd_list_skip
555 if (gpu.frameskip.active && (gpu.frameskip.allow || ((LE32TOH(data[pos]) >> 24) & 0xf0) == 0xe0))
556 pos += do_cmd_list_skip(data + pos, count - pos, &cmd);
557 else {
558 pos += do_cmd_list(data + pos, count - pos, &cmd);
559 vram_dirty = 1;
560 }
561
562 if (cmd == -1)
563 // incomplete cmd
564 break;
565 }
566
567 gpu.status &= ~0x1fff;
568 gpu.status |= gpu.ex_regs[1] & 0x7ff;
569 gpu.status |= (gpu.ex_regs[6] & 3) << 11;
570
571 gpu.state.fb_dirty |= vram_dirty;
572
573 if (old_e3 != gpu.ex_regs[3])
574 decide_frameskip_allow(gpu.ex_regs[3]);
575
576 return count - pos;
577}
578
579static void flush_cmd_buffer(void)
580{
581 int left = do_cmd_buffer(gpu.cmd_buffer, gpu.cmd_len);
582 if (left > 0)
583 memmove(gpu.cmd_buffer, gpu.cmd_buffer + gpu.cmd_len - left, left * 4);
584 gpu.cmd_len = left;
585}
586
587void GPUwriteDataMem(uint32_t *mem, int count)
588{
589 int left;
590
591 log_io("gpu_dma_write %p %d\n", mem, count);
592
593 if (unlikely(gpu.cmd_len > 0))
594 flush_cmd_buffer();
595
596 left = do_cmd_buffer(mem, count);
597 if (left)
598 log_anomaly("GPUwriteDataMem: discarded %d/%d words\n", left, count);
599}
600
601void GPUwriteData(uint32_t data)
602{
603 log_io("gpu_write %08x\n", data);
604 gpu.cmd_buffer[gpu.cmd_len++] = HTOLE32(data);
605 if (gpu.cmd_len >= CMD_BUFFER_LEN)
606 flush_cmd_buffer();
607}
608
609long GPUdmaChain(uint32_t *rambase, uint32_t start_addr)
610{
611 uint32_t addr, *list, ld_addr = 0;
612 int len, left, count;
613 long cpu_cycles = 0;
614
615 preload(rambase + (start_addr & 0x1fffff) / 4);
616
617 if (unlikely(gpu.cmd_len > 0))
618 flush_cmd_buffer();
619
620 log_io("gpu_dma_chain\n");
621 addr = start_addr & 0xffffff;
622 for (count = 0; (addr & 0x800000) == 0; count++)
623 {
624 list = rambase + (addr & 0x1fffff) / 4;
625 len = LE32TOH(list[0]) >> 24;
626 addr = LE32TOH(list[0]) & 0xffffff;
627 preload(rambase + (addr & 0x1fffff) / 4);
628
629 cpu_cycles += 10;
630 if (len > 0)
631 cpu_cycles += 5 + len;
632
633 log_io(".chain %08x #%d\n", (list - rambase) * 4, len);
634
635 if (len) {
636 left = do_cmd_buffer(list + 1, len);
637 if (left)
638 log_anomaly("GPUdmaChain: discarded %d/%d words\n", left, len);
639 }
640
641 #define LD_THRESHOLD (8*1024)
642 if (count >= LD_THRESHOLD) {
643 if (count == LD_THRESHOLD) {
644 ld_addr = addr;
645 continue;
646 }
647
648 // loop detection marker
649 // (bit23 set causes DMA error on real machine, so
650 // unlikely to be ever set by the game)
651 list[0] |= HTOLE32(0x800000);
652 }
653 }
654
655 if (ld_addr != 0) {
656 // remove loop detection markers
657 count -= LD_THRESHOLD + 2;
658 addr = ld_addr & 0x1fffff;
659 while (count-- > 0) {
660 list = rambase + addr / 4;
661 addr = LE32TOH(list[0]) & 0x1fffff;
662 list[0] &= HTOLE32(~0x800000);
663 }
664 }
665
666 gpu.state.last_list.frame = *gpu.state.frame_count;
667 gpu.state.last_list.hcnt = *gpu.state.hcnt;
668 gpu.state.last_list.cycles = cpu_cycles;
669 gpu.state.last_list.addr = start_addr;
670
671 return cpu_cycles;
672}
673
674void GPUreadDataMem(uint32_t *mem, int count)
675{
676 log_io("gpu_dma_read %p %d\n", mem, count);
677
678 if (unlikely(gpu.cmd_len > 0))
679 flush_cmd_buffer();
680
681 if (gpu.dma.h)
682 do_vram_io(mem, count, 1);
683}
684
685uint32_t GPUreadData(void)
686{
687 uint32_t ret;
688
689 if (unlikely(gpu.cmd_len > 0))
690 flush_cmd_buffer();
691
692 ret = gpu.gp0;
693 if (gpu.dma.h)
694 do_vram_io(&ret, 1, 1);
695
696 log_io("gpu_read %08x\n", ret);
697 return ret;
698}
699
700uint32_t GPUreadStatus(void)
701{
702 uint32_t ret;
703
704 if (unlikely(gpu.cmd_len > 0))
705 flush_cmd_buffer();
706
707 ret = gpu.status;
708 log_io("gpu_read_status %08x\n", ret);
709 return ret;
710}
711
712struct GPUFreeze
713{
714 uint32_t ulFreezeVersion; // should be always 1 for now (set by main emu)
715 uint32_t ulStatus; // current gpu status
716 uint32_t ulControl[256]; // latest control register values
717 unsigned char psxVRam[1024*1024*2]; // current VRam image (full 2 MB for ZN)
718};
719
720long GPUfreeze(uint32_t type, struct GPUFreeze *freeze)
721{
722 int i;
723
724 switch (type) {
725 case 1: // save
726 if (gpu.cmd_len > 0)
727 flush_cmd_buffer();
728
729 renderer_sync();
730 memcpy(freeze->psxVRam, gpu.vram, 1024 * 512 * 2);
731 memcpy(freeze->ulControl, gpu.regs, sizeof(gpu.regs));
732 memcpy(freeze->ulControl + 0xe0, gpu.ex_regs, sizeof(gpu.ex_regs));
733 freeze->ulStatus = gpu.status;
734 break;
735 case 0: // load
736 renderer_sync();
737 memcpy(gpu.vram, freeze->psxVRam, 1024 * 512 * 2);
738 memcpy(gpu.regs, freeze->ulControl, sizeof(gpu.regs));
739 memcpy(gpu.ex_regs, freeze->ulControl + 0xe0, sizeof(gpu.ex_regs));
740 gpu.status = freeze->ulStatus;
741 gpu.cmd_len = 0;
742 for (i = 8; i > 0; i--) {
743 gpu.regs[i] ^= 1; // avoid reg change detection
744 GPUwriteStatus((i << 24) | (gpu.regs[i] ^ 1));
745 }
746 renderer_sync_ecmds(gpu.ex_regs);
747 renderer_update_caches(0, 0, 1024, 512);
748 break;
749 }
750
751 return 1;
752}
753
754void GPUupdateLace(void)
755{
756 if (gpu.cmd_len > 0)
757 flush_cmd_buffer();
758 renderer_flush_queues();
759
760 if (gpu.status & PSX_GPU_STATUS_BLANKING) {
761 if (!gpu.state.blanked) {
762 vout_blank();
763 gpu.state.blanked = 1;
764 gpu.state.fb_dirty = 1;
765 }
766 return;
767 }
768
769 renderer_notify_update_lace(0);
770
771 if (!gpu.state.fb_dirty)
772 return;
773
774 if (gpu.frameskip.set) {
775 if (!gpu.frameskip.frame_ready) {
776 if (*gpu.state.frame_count - gpu.frameskip.last_flip_frame < 9)
777 return;
778 gpu.frameskip.active = 0;
779 }
780 gpu.frameskip.frame_ready = 0;
781 }
782
783 vout_update();
784 gpu.state.fb_dirty = 0;
785 gpu.state.blanked = 0;
786 renderer_notify_update_lace(1);
787}
788
789void GPUvBlank(int is_vblank, int lcf)
790{
791 int interlace = gpu.state.allow_interlace
792 && (gpu.status & PSX_GPU_STATUS_INTERLACE)
793 && (gpu.status & PSX_GPU_STATUS_DHEIGHT);
794 // interlace doesn't look nice on progressive displays,
795 // so we have this "auto" mode here for games that don't read vram
796 if (gpu.state.allow_interlace == 2
797 && *gpu.state.frame_count - gpu.state.last_vram_read_frame > 1)
798 {
799 interlace = 0;
800 }
801 if (interlace || interlace != gpu.state.old_interlace) {
802 gpu.state.old_interlace = interlace;
803
804 if (gpu.cmd_len > 0)
805 flush_cmd_buffer();
806 renderer_flush_queues();
807 renderer_set_interlace(interlace, !lcf);
808 }
809}
810
811#include "../../frontend/plugin_lib.h"
812
813void GPUrearmedCallbacks(const struct rearmed_cbs *cbs)
814{
815 gpu.frameskip.set = cbs->frameskip;
816 gpu.frameskip.advice = &cbs->fskip_advice;
817 gpu.frameskip.force = &cbs->fskip_force;
818 gpu.frameskip.dirty = &cbs->fskip_dirty;
819 gpu.frameskip.active = 0;
820 gpu.frameskip.frame_ready = 1;
821 gpu.state.hcnt = cbs->gpu_hcnt;
822 gpu.state.frame_count = cbs->gpu_frame_count;
823 gpu.state.allow_interlace = cbs->gpu_neon.allow_interlace;
824 gpu.state.enhancement_enable = cbs->gpu_neon.enhancement_enable;
825
826 gpu.useDithering = cbs->gpu_neon.allow_dithering;
827 gpu.mmap = cbs->mmap;
828 gpu.munmap = cbs->munmap;
829
830 // delayed vram mmap
831 if (gpu.vram == NULL)
832 map_vram();
833
834 if (cbs->pl_vout_set_raw_vram)
835 cbs->pl_vout_set_raw_vram(gpu.vram);
836 renderer_set_config(cbs);
837 vout_set_config(cbs);
838}
839
840// vim:shiftwidth=2:expandtab