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