some lightgun selection mechanism
[pcsx_rearmed.git] / plugins / gpulib / gpu.c
CommitLineData
1ab64c54 1/*
05740673 2 * (C) GraÅžvydas "notaz" Ignotas, 2011-2012
1ab64c54
GI
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
d30279e2 11#include <stdio.h>
5bbe183f 12#include <stdlib.h>
1ab64c54 13#include <string.h>
12367ad0 14#include <stdlib.h> /* for calloc */
15
56f08d83 16#include "gpu.h"
1ab64c54
GI
17
18#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
8f5f2dd5 19#ifdef __GNUC__
d30279e2 20#define unlikely(x) __builtin_expect((x), 0)
8f5f2dd5 21#define preload __builtin_prefetch
8dd855cd 22#define noinline __attribute__((noinline))
8f5f2dd5 23#else
24#define unlikely(x)
25#define preload(...)
26#define noinline
8f5f2dd5 27#endif
1ab64c54 28
deb18d24 29//#define log_io gpu_log
56f08d83 30#define log_io(...)
56f08d83 31
9ee0fd5b 32struct psx_gpu gpu;
1ab64c54 33
48f3d210 34static noinline int do_cmd_buffer(uint32_t *data, int count);
05740673 35static void finish_vram_transfer(int is_read);
48f3d210 36
37static noinline void do_cmd_reset(void)
38{
c765eb86
JW
39 renderer_sync();
40
48f3d210 41 if (unlikely(gpu.cmd_len > 0))
42 do_cmd_buffer(gpu.cmd_buffer, gpu.cmd_len);
48f3d210 43 gpu.cmd_len = 0;
05740673 44
45 if (unlikely(gpu.dma.h > 0))
46 finish_vram_transfer(gpu.dma_start.is_read);
48f3d210 47 gpu.dma.h = 0;
48}
49
6e9bdaef 50static noinline void do_reset(void)
1ab64c54 51{
7841712d 52 unsigned int i;
5b568098 53
48f3d210 54 do_cmd_reset();
55
6e9bdaef 56 memset(gpu.regs, 0, sizeof(gpu.regs));
48f3d210 57 for (i = 0; i < sizeof(gpu.ex_regs) / sizeof(gpu.ex_regs[0]); i++)
58 gpu.ex_regs[i] = (0xe0 + i) << 24;
61124a6d 59 gpu.status = 0x14802000;
6e9bdaef 60 gpu.gp0 = 0;
fc84f618 61 gpu.regs[3] = 1;
6e9bdaef 62 gpu.screen.hres = gpu.screen.w = 256;
fc84f618 63 gpu.screen.vres = gpu.screen.h = 240;
5bbe183f 64 gpu.screen.x = gpu.screen.y = 0;
01ff3105 65 renderer_sync_ecmds(gpu.ex_regs);
3b7b0065 66 renderer_notify_res_change();
1ab64c54
GI
67}
68
8dd855cd 69static noinline void update_width(void)
70{
5bbe183f 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;
8dd855cd 76 int sw = gpu.screen.x2 - gpu.screen.x1;
5bbe183f 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);
8dd855cd 112}
113
114static noinline void update_height(void)
115{
5bbe183f 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
8dd855cd 119 int sh = gpu.screen.y2 - gpu.screen.y1;
5bbe183f 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;
8dd855cd 145 gpu.screen.h = sh;
5bbe183f 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);
8dd855cd 150}
151
fc84f618 152static noinline void decide_frameskip(void)
153{
5eaa13f1
A
154 *gpu.frameskip.dirty = 1;
155
9fe27e25 156 if (gpu.frameskip.active)
157 gpu.frameskip.cnt++;
158 else {
159 gpu.frameskip.cnt = 0;
160 gpu.frameskip.frame_ready = 1;
161 }
fc84f618 162
5eaa13f1
A
163 if (*gpu.frameskip.force)
164 gpu.frameskip.active = 1;
165 else if (!gpu.frameskip.active && *gpu.frameskip.advice)
9fe27e25 166 gpu.frameskip.active = 1;
167 else if (gpu.frameskip.set > 0 && gpu.frameskip.cnt < gpu.frameskip.set)
fc84f618 168 gpu.frameskip.active = 1;
169 else
170 gpu.frameskip.active = 0;
fbb4bfff 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 }
fc84f618 177}
178
b243416b 179static noinline int decide_frameskip_allow(uint32_t cmd_e3)
9fe27e25 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;
61124a6d 185 gpu.frameskip.allow = (gpu.status & PSX_GPU_STATUS_INTERLACE) ||
5bbe183f 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;
b243416b 188 return gpu.frameskip.allow;
9fe27e25 189}
190
01ff3105 191static void flush_cmd_buffer(void);
192
6e9bdaef 193static noinline void get_gpu_info(uint32_t data)
194{
01ff3105 195 if (unlikely(gpu.cmd_len > 0))
196 flush_cmd_buffer();
6e9bdaef 197 switch (data & 0x0f) {
198 case 0x02:
199 case 0x03:
200 case 0x04:
6e9bdaef 201 gpu.gp0 = gpu.ex_regs[data & 7] & 0xfffff;
202 break;
08b33377 203 case 0x05:
204 gpu.gp0 = gpu.ex_regs[5] & 0x3fffff;
6e9bdaef 205 break;
206 case 0x07:
207 gpu.gp0 = 2;
208 break;
209 default:
08b33377 210 // gpu.gp0 unchanged
6e9bdaef 211 break;
212 }
213}
214
9ee0fd5b 215// double, for overdraw guard
12367ad0 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;
9ee0fd5b 230
12367ad0 231#ifdef GPULIB_USE_MMAP
9ee0fd5b 232static int map_vram(void)
233{
12367ad0 234 gpu.vram = vram_ptr_orig = gpu.mmap(VRAM_SIZE + (VRAM_ALIGN-1));
9ee0fd5b 235 if (gpu.vram != NULL) {
12367ad0 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));
9ee0fd5b 240 return 0;
241 }
242 else {
243 fprintf(stderr, "could not map vram, expect crashes\n");
244 return -1;
245 }
246}
12367ad0 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
9ee0fd5b 278
6e9bdaef 279long GPUinit(void)
280{
12367ad0 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
9394ada5 295 int ret;
296 ret = vout_init();
297 ret |= renderer_init();
298
3b7b0065 299 memset(&gpu.state, 0, sizeof(gpu.state));
300 memset(&gpu.frameskip, 0, sizeof(gpu.frameskip));
301 gpu.zero = 0;
3ece2f0c 302 gpu.state.frame_count = &gpu.zero;
deb18d24 303 gpu.state.hcnt = &gpu.zero;
48f3d210 304 gpu.cmd_len = 0;
9394ada5 305 do_reset();
48f3d210 306
12367ad0 307 /*if (gpu.mmap != NULL) {
9ee0fd5b 308 if (map_vram() != 0)
309 ret = -1;
12367ad0 310 }*/
6e9bdaef 311 return ret;
312}
313
314long GPUshutdown(void)
315{
9ee0fd5b 316 long ret;
317
e929dec5 318 renderer_finish();
9ee0fd5b 319 ret = vout_finish();
12367ad0 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
9ee0fd5b 327 }
12367ad0 328 vram_ptr_orig = gpu.vram = NULL;
9ee0fd5b 329
330 return ret;
6e9bdaef 331}
332
1ab64c54
GI
333void GPUwriteStatus(uint32_t data)
334{
1ab64c54
GI
335 uint32_t cmd = data >> 24;
336
fc84f618 337 if (cmd < ARRAY_SIZE(gpu.regs)) {
48f3d210 338 if (cmd > 1 && cmd != 5 && gpu.regs[cmd] == data)
fc84f618 339 return;
8dd855cd 340 gpu.regs[cmd] = data;
fc84f618 341 }
342
343 gpu.state.fb_dirty = 1;
8dd855cd 344
345 switch (cmd) {
1ab64c54 346 case 0x00:
6e9bdaef 347 do_reset();
1ab64c54 348 break;
48f3d210 349 case 0x01:
350 do_cmd_reset();
351 break;
1ab64c54 352 case 0x03:
5bbe183f 353 if (data & 1) {
61124a6d 354 gpu.status |= PSX_GPU_STATUS_BLANKING;
5bbe183f 355 gpu.state.dims_changed = 1; // for hud clearing
356 }
61124a6d
PC
357 else
358 gpu.status &= ~PSX_GPU_STATUS_BLANKING;
1ab64c54
GI
359 break;
360 case 0x04:
61124a6d
PC
361 gpu.status &= ~PSX_GPU_STATUS_DMA_MASK;
362 gpu.status |= PSX_GPU_STATUS_DMA(data & 3);
1ab64c54
GI
363 break;
364 case 0x05:
5bbe183f 365 gpu.screen.src_x = data & 0x3ff;
366 gpu.screen.src_y = (data >> 10) & 0x1ff;
3b7b0065 367 renderer_notify_scanout_x_change(gpu.screen.src_x, gpu.screen.hres);
9fe27e25 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 }
fb4c6fba 374 }
1ab64c54 375 break;
8dd855cd 376 case 0x06:
377 gpu.screen.x1 = data & 0xfff;
378 gpu.screen.x2 = (data >> 12) & 0xfff;
379 update_width();
380 break;
1ab64c54
GI
381 case 0x07:
382 gpu.screen.y1 = data & 0x3ff;
383 gpu.screen.y2 = (data >> 10) & 0x3ff;
8dd855cd 384 update_height();
1ab64c54
GI
385 break;
386 case 0x08:
61124a6d 387 gpu.status = (gpu.status & ~0x7f0000) | ((data & 0x3F) << 17) | ((data & 0x40) << 10);
8dd855cd 388 update_width();
389 update_height();
e929dec5 390 renderer_notify_res_change();
1ab64c54 391 break;
deb18d24 392 default:
393 if ((cmd & 0xf0) == 0x10)
394 get_gpu_info(data);
6e9bdaef 395 break;
1ab64c54 396 }
7890a708 397
398#ifdef GPUwriteStatus_ext
399 GPUwriteStatus_ext(data);
400#endif
1ab64c54
GI
401}
402
56f08d83 403const unsigned char cmd_lengths[256] =
1ab64c54 404{
d30279e2
GI
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,
652c6b8b 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
d30279e2
GI
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
d30279e2
GI
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)
1ab64c54 426{
d30279e2
GI
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;
ddd56f6e 440 int o = gpu.dma.offset;
d30279e2
GI
441 int l;
442 count *= 2; // operate in 16bpp pixels
443
c765eb86
JW
444 renderer_sync();
445
d30279e2
GI
446 if (gpu.dma.offset) {
447 l = w - gpu.dma.offset;
ddd56f6e 448 if (count < l)
d30279e2 449 l = count;
ddd56f6e 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 }
d30279e2
GI
460 sdata += l;
461 count -= l;
d30279e2
GI
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
05740673 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 }
d30279e2 476 }
05740673 477 else
478 finish_vram_transfer(is_read);
d30279e2
GI
479 gpu.dma.y = y;
480 gpu.dma.h = h;
ddd56f6e 481 gpu.dma.offset = o;
d30279e2 482
6e9bdaef 483 return count_initial - count / 2;
d30279e2
GI
484}
485
486static void start_vram_transfer(uint32_t pos_word, uint32_t size_word, int is_read)
487{
ddd56f6e 488 if (gpu.dma.h)
489 log_anomaly("start_vram_transfer while old unfinished\n");
490
5440b88e 491 gpu.dma.x = pos_word & 0x3ff;
492 gpu.dma.y = (pos_word >> 16) & 0x1ff;
48f3d210 493 gpu.dma.w = ((size_word - 1) & 0x3ff) + 1;
494 gpu.dma.h = (((size_word >> 16) - 1) & 0x1ff) + 1;
d30279e2 495 gpu.dma.offset = 0;
05740673 496 gpu.dma.is_read = is_read;
497 gpu.dma_start = gpu.dma;
d30279e2 498
9e146206 499 renderer_flush_queues();
500 if (is_read) {
61124a6d 501 gpu.status |= PSX_GPU_STATUS_IMG;
9e146206 502 // XXX: wrong for width 1
495d603c 503 gpu.gp0 = LE32TOH(*(uint32_t *) VRAM_MEM_XY(gpu.dma.x, gpu.dma.y));
5440b88e 504 gpu.state.last_vram_read_frame = *gpu.state.frame_count;
9e146206 505 }
d30279e2 506
6e9bdaef 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);
d30279e2
GI
509}
510
05740673 511static void finish_vram_transfer(int is_read)
512{
513 if (is_read)
61124a6d 514 gpu.status &= ~PSX_GPU_STATUS_IMG;
05740673 515 else
516 renderer_update_caches(gpu.dma_start.x, gpu.dma_start.y,
3b7b0065 517 gpu.dma_start.w, gpu.dma_start.h, 0);
05740673 518}
519
b243416b 520static noinline int do_cmd_list_skip(uint32_t *data, int count, int *last_cmd)
521{
97e07db9 522 int cmd = 0, pos = 0, len, dummy, v;
b243416b 523 int skip = 1;
524
fbb4bfff 525 gpu.frameskip.pending_fill[0] = 0;
526
b243416b 527 while (pos < count && skip) {
528 uint32_t *list = data + pos;
db215a72 529 cmd = LE32TOH(list[0]) >> 24;
b243416b 530 len = 1 + cmd_lengths[cmd];
531
97e07db9 532 switch (cmd) {
533 case 0x02:
db215a72 534 if ((LE32TOH(list[2]) & 0x3ff) > gpu.screen.w || ((LE32TOH(list[2]) >> 16) & 0x1ff) > gpu.screen.h)
97e07db9 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;
db215a72 545 gpu.ex_regs[1] |= LE32TOH(list[4 + ((cmd >> 4) & 1)]) & 0x1ff;
97e07db9 546 break;
547 case 0x48 ... 0x4F:
548 for (v = 3; pos + v < count; v++)
549 {
db215a72 550 if ((list[v] & HTOLE32(0xf000f000)) == HTOLE32(0x50005000))
97e07db9 551 break;
552 }
553 len += v - 3;
554 break;
555 case 0x58 ... 0x5F:
556 for (v = 4; pos + v < count; v += 2)
557 {
db215a72 558 if ((list[v] & HTOLE32(0xf000f000)) == HTOLE32(0x50005000))
97e07db9 559 break;
560 }
561 len += v - 4;
562 break;
563 default:
564 if (cmd == 0xe3)
db215a72 565 skip = decide_frameskip_allow(LE32TOH(list[0]));
97e07db9 566 if ((cmd & 0xf8) == 0xe0)
db215a72 567 gpu.ex_regs[cmd & 7] = LE32TOH(list[0]);
97e07db9 568 break;
b243416b 569 }
b243416b 570
571 if (pos + len > count) {
572 cmd = -1;
573 break; // incomplete cmd
574 }
97e07db9 575 if (0xa0 <= cmd && cmd <= 0xdf)
b243416b 576 break; // image i/o
97e07db9 577
b243416b 578 pos += len;
579 }
580
581 renderer_sync_ecmds(gpu.ex_regs);
582 *last_cmd = cmd;
583 return pos;
584}
585
48f3d210 586static noinline int do_cmd_buffer(uint32_t *data, int count)
d30279e2 587{
b243416b 588 int cmd, pos;
589 uint32_t old_e3 = gpu.ex_regs[3];
fc84f618 590 int vram_dirty = 0;
d30279e2 591
d30279e2 592 // process buffer
b243416b 593 for (pos = 0; pos < count; )
d30279e2 594 {
b243416b 595 if (gpu.dma.h && !gpu.dma_start.is_read) { // XXX: need to verify
596 vram_dirty = 1;
d30279e2 597 pos += do_vram_io(data + pos, count - pos, 0);
ddd56f6e 598 if (pos == count)
599 break;
d30279e2
GI
600 }
601
db215a72 602 cmd = LE32TOH(data[pos]) >> 24;
97e07db9 603 if (0xa0 <= cmd && cmd <= 0xdf) {
79573c20
DS
604 if (unlikely((pos+2) >= count)) {
605 // incomplete vram write/read cmd, can't consume yet
606 cmd = -1;
607 break;
608 }
609
d30279e2 610 // consume vram write/read cmd
db215a72 611 start_vram_transfer(LE32TOH(data[pos + 1]), LE32TOH(data[pos + 2]), (cmd & 0xe0) == 0xc0);
b243416b 612 pos += 3;
613 continue;
d30279e2 614 }
b243416b 615
1e07f71d 616 // 0xex cmds might affect frameskip.allow, so pass to do_cmd_list_skip
db215a72 617 if (gpu.frameskip.active && (gpu.frameskip.allow || ((LE32TOH(data[pos]) >> 24) & 0xf0) == 0xe0))
b243416b 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
ddd56f6e 626 break;
d30279e2 627 }
ddd56f6e 628
61124a6d
PC
629 gpu.status &= ~0x1fff;
630 gpu.status |= gpu.ex_regs[1] & 0x7ff;
631 gpu.status |= (gpu.ex_regs[6] & 3) << 11;
a3a9f519 632
fc84f618 633 gpu.state.fb_dirty |= vram_dirty;
634
b243416b 635 if (old_e3 != gpu.ex_regs[3])
636 decide_frameskip_allow(gpu.ex_regs[3]);
637
ddd56f6e 638 return count - pos;
d30279e2
GI
639}
640
5440b88e 641static void flush_cmd_buffer(void)
d30279e2 642{
48f3d210 643 int left = do_cmd_buffer(gpu.cmd_buffer, gpu.cmd_len);
d30279e2
GI
644 if (left > 0)
645 memmove(gpu.cmd_buffer, gpu.cmd_buffer + gpu.cmd_len - left, left * 4);
646 gpu.cmd_len = left;
1ab64c54
GI
647}
648
649void GPUwriteDataMem(uint32_t *mem, int count)
650{
d30279e2
GI
651 int left;
652
56f08d83 653 log_io("gpu_dma_write %p %d\n", mem, count);
654
d30279e2
GI
655 if (unlikely(gpu.cmd_len > 0))
656 flush_cmd_buffer();
56f08d83 657
48f3d210 658 left = do_cmd_buffer(mem, count);
d30279e2 659 if (left)
56f08d83 660 log_anomaly("GPUwriteDataMem: discarded %d/%d words\n", left, count);
1ab64c54
GI
661}
662
d30279e2 663void GPUwriteData(uint32_t data)
1ab64c54 664{
56f08d83 665 log_io("gpu_write %08x\n", data);
db215a72 666 gpu.cmd_buffer[gpu.cmd_len++] = HTOLE32(data);
d30279e2
GI
667 if (gpu.cmd_len >= CMD_BUFFER_LEN)
668 flush_cmd_buffer();
1ab64c54
GI
669}
670
fae38d7a 671long GPUdmaChain(uint32_t *rambase, uint32_t start_addr, uint32_t *progress_addr)
1ab64c54 672{
09159d99 673 uint32_t addr, *list, ld_addr = 0;
ddd56f6e 674 int len, left, count;
1c72b1c2 675 long cpu_cycles = 0;
d30279e2 676
8f5f2dd5 677 preload(rambase + (start_addr & 0x1fffff) / 4);
678
d30279e2
GI
679 if (unlikely(gpu.cmd_len > 0))
680 flush_cmd_buffer();
681
56f08d83 682 log_io("gpu_dma_chain\n");
ddd56f6e 683 addr = start_addr & 0xffffff;
09159d99 684 for (count = 0; (addr & 0x800000) == 0; count++)
ddd56f6e 685 {
ddd56f6e 686 list = rambase + (addr & 0x1fffff) / 4;
db215a72
PC
687 len = LE32TOH(list[0]) >> 24;
688 addr = LE32TOH(list[0]) & 0xffffff;
8f5f2dd5 689 preload(rambase + (addr & 0x1fffff) / 4);
690
1c72b1c2 691 cpu_cycles += 10;
692 if (len > 0)
693 cpu_cycles += 5 + len;
deb18d24 694
a4e249a1 695 log_io(".chain %08lx #%d+%d\n",
696 (long)(list - rambase) * 4, len, gpu.cmd_len);
697 if (unlikely(gpu.cmd_len > 0)) {
81ff42e1 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 }
a4e249a1 702 memcpy(gpu.cmd_buffer + gpu.cmd_len, list + 1, len * 4);
703 gpu.cmd_len += len;
704 flush_cmd_buffer();
705 continue;
706 }
ddd56f6e 707
56f08d83 708 if (len) {
48f3d210 709 left = do_cmd_buffer(list + 1, len);
a4e249a1 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 }
56f08d83 715 }
ddd56f6e 716
fae38d7a 717 if (progress_addr) {
718 *progress_addr = addr;
719 break;
720 }
09159d99 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)
db215a72 731 list[0] |= HTOLE32(0x800000);
09159d99 732 }
ddd56f6e 733 }
734
09159d99 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;
db215a72
PC
741 addr = LE32TOH(list[0]) & 0x1fffff;
742 list[0] &= HTOLE32(~0x800000);
09159d99 743 }
d30279e2 744 }
09159d99 745
3ece2f0c 746 gpu.state.last_list.frame = *gpu.state.frame_count;
deb18d24 747 gpu.state.last_list.hcnt = *gpu.state.hcnt;
1c72b1c2 748 gpu.state.last_list.cycles = cpu_cycles;
deb18d24 749 gpu.state.last_list.addr = start_addr;
750
1c72b1c2 751 return cpu_cycles;
1ab64c54
GI
752}
753
d30279e2
GI
754void GPUreadDataMem(uint32_t *mem, int count)
755{
56f08d83 756 log_io("gpu_dma_read %p %d\n", mem, count);
757
d30279e2
GI
758 if (unlikely(gpu.cmd_len > 0))
759 flush_cmd_buffer();
56f08d83 760
d30279e2
GI
761 if (gpu.dma.h)
762 do_vram_io(mem, count, 1);
763}
764
765uint32_t GPUreadData(void)
766{
9e146206 767 uint32_t ret;
56f08d83 768
769 if (unlikely(gpu.cmd_len > 0))
770 flush_cmd_buffer();
771
9e146206 772 ret = gpu.gp0;
495d603c
PC
773 if (gpu.dma.h) {
774 ret = HTOLE32(ret);
9e146206 775 do_vram_io(&ret, 1, 1);
495d603c
PC
776 ret = LE32TOH(ret);
777 }
56f08d83 778
9e146206 779 log_io("gpu_read %08x\n", ret);
780 return ret;
d30279e2
GI
781}
782
783uint32_t GPUreadStatus(void)
784{
ddd56f6e 785 uint32_t ret;
56f08d83 786
d30279e2
GI
787 if (unlikely(gpu.cmd_len > 0))
788 flush_cmd_buffer();
789
61124a6d 790 ret = gpu.status;
ddd56f6e 791 log_io("gpu_read_status %08x\n", ret);
792 return ret;
d30279e2
GI
793}
794
096ec49b 795struct GPUFreeze
1ab64c54
GI
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)
096ec49b 801};
1ab64c54 802
096ec49b 803long GPUfreeze(uint32_t type, struct GPUFreeze *freeze)
1ab64c54 804{
fc84f618 805 int i;
806
1ab64c54
GI
807 switch (type) {
808 case 1: // save
d30279e2
GI
809 if (gpu.cmd_len > 0)
810 flush_cmd_buffer();
c765eb86
JW
811
812 renderer_sync();
9ee0fd5b 813 memcpy(freeze->psxVRam, gpu.vram, 1024 * 512 * 2);
1ab64c54 814 memcpy(freeze->ulControl, gpu.regs, sizeof(gpu.regs));
6e9bdaef 815 memcpy(freeze->ulControl + 0xe0, gpu.ex_regs, sizeof(gpu.ex_regs));
61124a6d 816 freeze->ulStatus = gpu.status;
1ab64c54
GI
817 break;
818 case 0: // load
c765eb86 819 renderer_sync();
9ee0fd5b 820 memcpy(gpu.vram, freeze->psxVRam, 1024 * 512 * 2);
1ab64c54 821 memcpy(gpu.regs, freeze->ulControl, sizeof(gpu.regs));
6e9bdaef 822 memcpy(gpu.ex_regs, freeze->ulControl + 0xe0, sizeof(gpu.ex_regs));
61124a6d 823 gpu.status = freeze->ulStatus;
3d47ef17 824 gpu.cmd_len = 0;
fc84f618 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 }
5b745e5b 829 renderer_sync_ecmds(gpu.ex_regs);
3b7b0065 830 renderer_update_caches(0, 0, 1024, 512, 1);
1ab64c54
GI
831 break;
832 }
833
834 return 1;
835}
836
5440b88e 837void GPUupdateLace(void)
838{
839 if (gpu.cmd_len > 0)
840 flush_cmd_buffer();
841 renderer_flush_queues();
842
7a20a6d0 843#ifndef RAW_FB_DISPLAY
61124a6d 844 if (gpu.status & PSX_GPU_STATUS_BLANKING) {
aafcb4dd 845 if (!gpu.state.blanked) {
846 vout_blank();
847 gpu.state.blanked = 1;
848 gpu.state.fb_dirty = 1;
849 }
850 return;
851 }
852
c765eb86
JW
853 renderer_notify_update_lace(0);
854
aafcb4dd 855 if (!gpu.state.fb_dirty)
5440b88e 856 return;
7a20a6d0 857#endif
5440b88e 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();
3b7b0065 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;
5440b88e 872 gpu.state.fb_dirty = 0;
aafcb4dd 873 gpu.state.blanked = 0;
c765eb86 874 renderer_notify_update_lace(1);
5440b88e 875}
876
72e5023f 877void GPUvBlank(int is_vblank, int lcf)
878{
5440b88e 879 int interlace = gpu.state.allow_interlace
61124a6d
PC
880 && (gpu.status & PSX_GPU_STATUS_INTERLACE)
881 && (gpu.status & PSX_GPU_STATUS_DHEIGHT);
5440b88e 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;
5eaa13f1 905 gpu.frameskip.force = &cbs->fskip_force;
5bbe183f 906 gpu.frameskip.dirty = (void *)&cbs->fskip_dirty;
5440b88e 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;
0b02eb77 912 gpu.state.enhancement_enable = cbs->gpu_neon.enhancement_enable;
5bbe183f 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 }
5440b88e 922
9ee0fd5b 923 gpu.mmap = cbs->mmap;
924 gpu.munmap = cbs->munmap;
925
926 // delayed vram mmap
927 if (gpu.vram == NULL)
928 map_vram();
929
5440b88e 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);
72e5023f 934}
935
1ab64c54 936// vim:shiftwidth=2:expandtab