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