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