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