cdrom: forget old sector on cd change
[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
58 do_cmd_reset();
59
60 memset(gpu.regs, 0, sizeof(gpu.regs));
61 for (i = 0; i < sizeof(gpu.ex_regs) / sizeof(gpu.ex_regs[0]); i++)
62 gpu.ex_regs[i] = (0xe0 + i) << 24;
63 gpu.status = 0x14802000;
64 gpu.gp0 = 0;
65 gpu.regs[3] = 1;
66 gpu.screen.hres = gpu.screen.w = 256;
67 gpu.screen.vres = gpu.screen.h = 240;
68}
69
70static noinline void update_width(void)
71{
72 int sw = gpu.screen.x2 - gpu.screen.x1;
73 if (sw <= 0 || sw >= 2560)
74 // full width
75 gpu.screen.w = gpu.screen.hres;
76 else
77 gpu.screen.w = sw * gpu.screen.hres / 2560;
78}
79
80static noinline void update_height(void)
81{
82 // TODO: emulate this properly..
83 int sh = gpu.screen.y2 - gpu.screen.y1;
84 if (gpu.status & PSX_GPU_STATUS_DHEIGHT)
85 sh *= 2;
86 if (sh <= 0 || sh > gpu.screen.vres)
87 sh = gpu.screen.vres;
88
89 gpu.screen.h = sh;
90}
91
92static noinline void decide_frameskip(void)
93{
94 *gpu.frameskip.dirty = 1;
95
96 if (gpu.frameskip.active)
97 gpu.frameskip.cnt++;
98 else {
99 gpu.frameskip.cnt = 0;
100 gpu.frameskip.frame_ready = 1;
101 }
102
103 if (*gpu.frameskip.force)
104 gpu.frameskip.active = 1;
105 else if (!gpu.frameskip.active && *gpu.frameskip.advice)
106 gpu.frameskip.active = 1;
107 else if (gpu.frameskip.set > 0 && gpu.frameskip.cnt < gpu.frameskip.set)
108 gpu.frameskip.active = 1;
109 else
110 gpu.frameskip.active = 0;
111
112 if (!gpu.frameskip.active && gpu.frameskip.pending_fill[0] != 0) {
113 int dummy;
114 do_cmd_list(gpu.frameskip.pending_fill, 3, &dummy);
115 gpu.frameskip.pending_fill[0] = 0;
116 }
117}
118
119static noinline int decide_frameskip_allow(uint32_t cmd_e3)
120{
121 // no frameskip if it decides to draw to display area,
122 // but not for interlace since it'll most likely always do that
123 uint32_t x = cmd_e3 & 0x3ff;
124 uint32_t y = (cmd_e3 >> 10) & 0x3ff;
125 gpu.frameskip.allow = (gpu.status & PSX_GPU_STATUS_INTERLACE) ||
126 (uint32_t)(x - gpu.screen.x) >= (uint32_t)gpu.screen.w ||
127 (uint32_t)(y - gpu.screen.y) >= (uint32_t)gpu.screen.h;
128 return gpu.frameskip.allow;
129}
130
131static noinline void get_gpu_info(uint32_t data)
132{
133 switch (data & 0x0f) {
134 case 0x02:
135 case 0x03:
136 case 0x04:
137 gpu.gp0 = gpu.ex_regs[data & 7] & 0xfffff;
138 break;
139 case 0x05:
140 gpu.gp0 = gpu.ex_regs[5] & 0x3fffff;
141 break;
142 case 0x07:
143 gpu.gp0 = 2;
144 break;
145 default:
146 // gpu.gp0 unchanged
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 gpu.gp0 = LE32TOH(*(uint32_t *) VRAM_MEM_XY(gpu.dma.x, gpu.dma.y));
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, uint32_t *progress_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 if (progress_addr) {
642 *progress_addr = addr;
643 break;
644 }
645 #define LD_THRESHOLD (8*1024)
646 if (count >= LD_THRESHOLD) {
647 if (count == LD_THRESHOLD) {
648 ld_addr = addr;
649 continue;
650 }
651
652 // loop detection marker
653 // (bit23 set causes DMA error on real machine, so
654 // unlikely to be ever set by the game)
655 list[0] |= HTOLE32(0x800000);
656 }
657 }
658
659 if (ld_addr != 0) {
660 // remove loop detection markers
661 count -= LD_THRESHOLD + 2;
662 addr = ld_addr & 0x1fffff;
663 while (count-- > 0) {
664 list = rambase + addr / 4;
665 addr = LE32TOH(list[0]) & 0x1fffff;
666 list[0] &= HTOLE32(~0x800000);
667 }
668 }
669
670 gpu.state.last_list.frame = *gpu.state.frame_count;
671 gpu.state.last_list.hcnt = *gpu.state.hcnt;
672 gpu.state.last_list.cycles = cpu_cycles;
673 gpu.state.last_list.addr = start_addr;
674
675 return cpu_cycles;
676}
677
678void GPUreadDataMem(uint32_t *mem, int count)
679{
680 log_io("gpu_dma_read %p %d\n", mem, count);
681
682 if (unlikely(gpu.cmd_len > 0))
683 flush_cmd_buffer();
684
685 if (gpu.dma.h)
686 do_vram_io(mem, count, 1);
687}
688
689uint32_t GPUreadData(void)
690{
691 uint32_t ret;
692
693 if (unlikely(gpu.cmd_len > 0))
694 flush_cmd_buffer();
695
696 ret = gpu.gp0;
697 if (gpu.dma.h) {
698 ret = HTOLE32(ret);
699 do_vram_io(&ret, 1, 1);
700 ret = LE32TOH(ret);
701 }
702
703 log_io("gpu_read %08x\n", ret);
704 return ret;
705}
706
707uint32_t GPUreadStatus(void)
708{
709 uint32_t ret;
710
711 if (unlikely(gpu.cmd_len > 0))
712 flush_cmd_buffer();
713
714 ret = gpu.status;
715 log_io("gpu_read_status %08x\n", ret);
716 return ret;
717}
718
719struct GPUFreeze
720{
721 uint32_t ulFreezeVersion; // should be always 1 for now (set by main emu)
722 uint32_t ulStatus; // current gpu status
723 uint32_t ulControl[256]; // latest control register values
724 unsigned char psxVRam[1024*1024*2]; // current VRam image (full 2 MB for ZN)
725};
726
727long GPUfreeze(uint32_t type, struct GPUFreeze *freeze)
728{
729 int i;
730
731 switch (type) {
732 case 1: // save
733 if (gpu.cmd_len > 0)
734 flush_cmd_buffer();
735
736 renderer_sync();
737 memcpy(freeze->psxVRam, gpu.vram, 1024 * 512 * 2);
738 memcpy(freeze->ulControl, gpu.regs, sizeof(gpu.regs));
739 memcpy(freeze->ulControl + 0xe0, gpu.ex_regs, sizeof(gpu.ex_regs));
740 freeze->ulStatus = gpu.status;
741 break;
742 case 0: // load
743 renderer_sync();
744 memcpy(gpu.vram, freeze->psxVRam, 1024 * 512 * 2);
745 memcpy(gpu.regs, freeze->ulControl, sizeof(gpu.regs));
746 memcpy(gpu.ex_regs, freeze->ulControl + 0xe0, sizeof(gpu.ex_regs));
747 gpu.status = freeze->ulStatus;
748 gpu.cmd_len = 0;
749 for (i = 8; i > 0; i--) {
750 gpu.regs[i] ^= 1; // avoid reg change detection
751 GPUwriteStatus((i << 24) | (gpu.regs[i] ^ 1));
752 }
753 renderer_sync_ecmds(gpu.ex_regs);
754 renderer_update_caches(0, 0, 1024, 512);
755 break;
756 }
757
758 return 1;
759}
760
761void GPUupdateLace(void)
762{
763 if (gpu.cmd_len > 0)
764 flush_cmd_buffer();
765 renderer_flush_queues();
766
767 if (gpu.status & PSX_GPU_STATUS_BLANKING) {
768 if (!gpu.state.blanked) {
769 vout_blank();
770 gpu.state.blanked = 1;
771 gpu.state.fb_dirty = 1;
772 }
773 return;
774 }
775
776 renderer_notify_update_lace(0);
777
778 if (!gpu.state.fb_dirty)
779 return;
780
781 if (gpu.frameskip.set) {
782 if (!gpu.frameskip.frame_ready) {
783 if (*gpu.state.frame_count - gpu.frameskip.last_flip_frame < 9)
784 return;
785 gpu.frameskip.active = 0;
786 }
787 gpu.frameskip.frame_ready = 0;
788 }
789
790 vout_update();
791 gpu.state.fb_dirty = 0;
792 gpu.state.blanked = 0;
793 renderer_notify_update_lace(1);
794}
795
796void GPUvBlank(int is_vblank, int lcf)
797{
798 int interlace = gpu.state.allow_interlace
799 && (gpu.status & PSX_GPU_STATUS_INTERLACE)
800 && (gpu.status & PSX_GPU_STATUS_DHEIGHT);
801 // interlace doesn't look nice on progressive displays,
802 // so we have this "auto" mode here for games that don't read vram
803 if (gpu.state.allow_interlace == 2
804 && *gpu.state.frame_count - gpu.state.last_vram_read_frame > 1)
805 {
806 interlace = 0;
807 }
808 if (interlace || interlace != gpu.state.old_interlace) {
809 gpu.state.old_interlace = interlace;
810
811 if (gpu.cmd_len > 0)
812 flush_cmd_buffer();
813 renderer_flush_queues();
814 renderer_set_interlace(interlace, !lcf);
815 }
816}
817
818#include "../../frontend/plugin_lib.h"
819
820void GPUrearmedCallbacks(const struct rearmed_cbs *cbs)
821{
822 gpu.frameskip.set = cbs->frameskip;
823 gpu.frameskip.advice = &cbs->fskip_advice;
824 gpu.frameskip.force = &cbs->fskip_force;
825 gpu.frameskip.dirty = &cbs->fskip_dirty;
826 gpu.frameskip.active = 0;
827 gpu.frameskip.frame_ready = 1;
828 gpu.state.hcnt = cbs->gpu_hcnt;
829 gpu.state.frame_count = cbs->gpu_frame_count;
830 gpu.state.allow_interlace = cbs->gpu_neon.allow_interlace;
831 gpu.state.enhancement_enable = cbs->gpu_neon.enhancement_enable;
832
833 gpu.useDithering = cbs->gpu_neon.allow_dithering;
834 gpu.mmap = cbs->mmap;
835 gpu.munmap = cbs->munmap;
836
837 // delayed vram mmap
838 if (gpu.vram == NULL)
839 map_vram();
840
841 if (cbs->pl_vout_set_raw_vram)
842 cbs->pl_vout_set_raw_vram(gpu.vram);
843 renderer_set_config(cbs);
844 vout_set_config(cbs);
845}
846
847// vim:shiftwidth=2:expandtab