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