Merge pull request #461 from negativeExponent/libchdr
[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) {
79573c20
DS
531 if (unlikely((pos+2) >= count)) {
532 // incomplete vram write/read cmd, can't consume yet
533 cmd = -1;
534 break;
535 }
536
d30279e2 537 // consume vram write/read cmd
97e07db9 538 start_vram_transfer(data[pos + 1], data[pos + 2], (cmd & 0xe0) == 0xc0);
b243416b 539 pos += 3;
540 continue;
d30279e2 541 }
b243416b 542
1e07f71d 543 // 0xex cmds might affect frameskip.allow, so pass to do_cmd_list_skip
544 if (gpu.frameskip.active && (gpu.frameskip.allow || ((data[pos] >> 24) & 0xf0) == 0xe0))
b243416b 545 pos += do_cmd_list_skip(data + pos, count - pos, &cmd);
546 else {
547 pos += do_cmd_list(data + pos, count - pos, &cmd);
548 vram_dirty = 1;
549 }
550
551 if (cmd == -1)
552 // incomplete cmd
ddd56f6e 553 break;
d30279e2 554 }
ddd56f6e 555
a3a9f519 556 gpu.status.reg &= ~0x1fff;
557 gpu.status.reg |= gpu.ex_regs[1] & 0x7ff;
558 gpu.status.reg |= (gpu.ex_regs[6] & 3) << 11;
559
fc84f618 560 gpu.state.fb_dirty |= vram_dirty;
561
b243416b 562 if (old_e3 != gpu.ex_regs[3])
563 decide_frameskip_allow(gpu.ex_regs[3]);
564
ddd56f6e 565 return count - pos;
d30279e2
GI
566}
567
5440b88e 568static void flush_cmd_buffer(void)
d30279e2 569{
48f3d210 570 int left = do_cmd_buffer(gpu.cmd_buffer, gpu.cmd_len);
d30279e2
GI
571 if (left > 0)
572 memmove(gpu.cmd_buffer, gpu.cmd_buffer + gpu.cmd_len - left, left * 4);
573 gpu.cmd_len = left;
1ab64c54
GI
574}
575
576void GPUwriteDataMem(uint32_t *mem, int count)
577{
d30279e2
GI
578 int left;
579
56f08d83 580 log_io("gpu_dma_write %p %d\n", mem, count);
581
d30279e2
GI
582 if (unlikely(gpu.cmd_len > 0))
583 flush_cmd_buffer();
56f08d83 584
48f3d210 585 left = do_cmd_buffer(mem, count);
d30279e2 586 if (left)
56f08d83 587 log_anomaly("GPUwriteDataMem: discarded %d/%d words\n", left, count);
1ab64c54
GI
588}
589
d30279e2 590void GPUwriteData(uint32_t data)
1ab64c54 591{
56f08d83 592 log_io("gpu_write %08x\n", data);
d30279e2
GI
593 gpu.cmd_buffer[gpu.cmd_len++] = data;
594 if (gpu.cmd_len >= CMD_BUFFER_LEN)
595 flush_cmd_buffer();
1ab64c54
GI
596}
597
ddd56f6e 598long GPUdmaChain(uint32_t *rambase, uint32_t start_addr)
1ab64c54 599{
09159d99 600 uint32_t addr, *list, ld_addr = 0;
ddd56f6e 601 int len, left, count;
1c72b1c2 602 long cpu_cycles = 0;
d30279e2 603
8f5f2dd5 604 preload(rambase + (start_addr & 0x1fffff) / 4);
605
d30279e2
GI
606 if (unlikely(gpu.cmd_len > 0))
607 flush_cmd_buffer();
608
56f08d83 609 log_io("gpu_dma_chain\n");
ddd56f6e 610 addr = start_addr & 0xffffff;
09159d99 611 for (count = 0; (addr & 0x800000) == 0; count++)
ddd56f6e 612 {
ddd56f6e 613 list = rambase + (addr & 0x1fffff) / 4;
d30279e2
GI
614 len = list[0] >> 24;
615 addr = list[0] & 0xffffff;
8f5f2dd5 616 preload(rambase + (addr & 0x1fffff) / 4);
617
1c72b1c2 618 cpu_cycles += 10;
619 if (len > 0)
620 cpu_cycles += 5 + len;
deb18d24 621
622 log_io(".chain %08x #%d\n", (list - rambase) * 4, len);
ddd56f6e 623
56f08d83 624 if (len) {
48f3d210 625 left = do_cmd_buffer(list + 1, len);
56f08d83 626 if (left)
deb18d24 627 log_anomaly("GPUdmaChain: discarded %d/%d words\n", left, len);
56f08d83 628 }
ddd56f6e 629
09159d99 630 #define LD_THRESHOLD (8*1024)
631 if (count >= LD_THRESHOLD) {
632 if (count == LD_THRESHOLD) {
633 ld_addr = addr;
634 continue;
635 }
636
637 // loop detection marker
638 // (bit23 set causes DMA error on real machine, so
639 // unlikely to be ever set by the game)
640 list[0] |= 0x800000;
641 }
ddd56f6e 642 }
643
09159d99 644 if (ld_addr != 0) {
645 // remove loop detection markers
646 count -= LD_THRESHOLD + 2;
647 addr = ld_addr & 0x1fffff;
648 while (count-- > 0) {
649 list = rambase + addr / 4;
650 addr = list[0] & 0x1fffff;
651 list[0] &= ~0x800000;
652 }
d30279e2 653 }
09159d99 654
3ece2f0c 655 gpu.state.last_list.frame = *gpu.state.frame_count;
deb18d24 656 gpu.state.last_list.hcnt = *gpu.state.hcnt;
1c72b1c2 657 gpu.state.last_list.cycles = cpu_cycles;
deb18d24 658 gpu.state.last_list.addr = start_addr;
659
1c72b1c2 660 return cpu_cycles;
1ab64c54
GI
661}
662
d30279e2
GI
663void GPUreadDataMem(uint32_t *mem, int count)
664{
56f08d83 665 log_io("gpu_dma_read %p %d\n", mem, count);
666
d30279e2
GI
667 if (unlikely(gpu.cmd_len > 0))
668 flush_cmd_buffer();
56f08d83 669
d30279e2
GI
670 if (gpu.dma.h)
671 do_vram_io(mem, count, 1);
672}
673
674uint32_t GPUreadData(void)
675{
9e146206 676 uint32_t ret;
56f08d83 677
678 if (unlikely(gpu.cmd_len > 0))
679 flush_cmd_buffer();
680
9e146206 681 ret = gpu.gp0;
56f08d83 682 if (gpu.dma.h)
9e146206 683 do_vram_io(&ret, 1, 1);
56f08d83 684
9e146206 685 log_io("gpu_read %08x\n", ret);
686 return ret;
d30279e2
GI
687}
688
689uint32_t GPUreadStatus(void)
690{
ddd56f6e 691 uint32_t ret;
56f08d83 692
d30279e2
GI
693 if (unlikely(gpu.cmd_len > 0))
694 flush_cmd_buffer();
695
24de2dd4 696 ret = gpu.status.reg;
ddd56f6e 697 log_io("gpu_read_status %08x\n", ret);
698 return ret;
d30279e2
GI
699}
700
096ec49b 701struct GPUFreeze
1ab64c54
GI
702{
703 uint32_t ulFreezeVersion; // should be always 1 for now (set by main emu)
704 uint32_t ulStatus; // current gpu status
705 uint32_t ulControl[256]; // latest control register values
706 unsigned char psxVRam[1024*1024*2]; // current VRam image (full 2 MB for ZN)
096ec49b 707};
1ab64c54 708
096ec49b 709long GPUfreeze(uint32_t type, struct GPUFreeze *freeze)
1ab64c54 710{
fc84f618 711 int i;
712
1ab64c54
GI
713 switch (type) {
714 case 1: // save
d30279e2
GI
715 if (gpu.cmd_len > 0)
716 flush_cmd_buffer();
9ee0fd5b 717 memcpy(freeze->psxVRam, gpu.vram, 1024 * 512 * 2);
1ab64c54 718 memcpy(freeze->ulControl, gpu.regs, sizeof(gpu.regs));
6e9bdaef 719 memcpy(freeze->ulControl + 0xe0, gpu.ex_regs, sizeof(gpu.ex_regs));
1ab64c54 720 freeze->ulStatus = gpu.status.reg;
1ab64c54
GI
721 break;
722 case 0: // load
9ee0fd5b 723 memcpy(gpu.vram, freeze->psxVRam, 1024 * 512 * 2);
1ab64c54 724 memcpy(gpu.regs, freeze->ulControl, sizeof(gpu.regs));
6e9bdaef 725 memcpy(gpu.ex_regs, freeze->ulControl + 0xe0, sizeof(gpu.ex_regs));
1ab64c54 726 gpu.status.reg = freeze->ulStatus;
3d47ef17 727 gpu.cmd_len = 0;
fc84f618 728 for (i = 8; i > 0; i--) {
729 gpu.regs[i] ^= 1; // avoid reg change detection
730 GPUwriteStatus((i << 24) | (gpu.regs[i] ^ 1));
731 }
5b745e5b 732 renderer_sync_ecmds(gpu.ex_regs);
05740673 733 renderer_update_caches(0, 0, 1024, 512);
1ab64c54
GI
734 break;
735 }
736
737 return 1;
738}
739
5440b88e 740void GPUupdateLace(void)
741{
742 if (gpu.cmd_len > 0)
743 flush_cmd_buffer();
744 renderer_flush_queues();
745
aafcb4dd 746 if (gpu.status.blanking) {
747 if (!gpu.state.blanked) {
748 vout_blank();
749 gpu.state.blanked = 1;
750 gpu.state.fb_dirty = 1;
751 }
752 return;
753 }
754
755 if (!gpu.state.fb_dirty)
5440b88e 756 return;
757
758 if (gpu.frameskip.set) {
759 if (!gpu.frameskip.frame_ready) {
760 if (*gpu.state.frame_count - gpu.frameskip.last_flip_frame < 9)
761 return;
762 gpu.frameskip.active = 0;
763 }
764 gpu.frameskip.frame_ready = 0;
765 }
766
767 vout_update();
768 gpu.state.fb_dirty = 0;
aafcb4dd 769 gpu.state.blanked = 0;
5440b88e 770}
771
72e5023f 772void GPUvBlank(int is_vblank, int lcf)
773{
5440b88e 774 int interlace = gpu.state.allow_interlace
775 && gpu.status.interlace && gpu.status.dheight;
776 // interlace doesn't look nice on progressive displays,
777 // so we have this "auto" mode here for games that don't read vram
778 if (gpu.state.allow_interlace == 2
779 && *gpu.state.frame_count - gpu.state.last_vram_read_frame > 1)
780 {
781 interlace = 0;
782 }
783 if (interlace || interlace != gpu.state.old_interlace) {
784 gpu.state.old_interlace = interlace;
785
786 if (gpu.cmd_len > 0)
787 flush_cmd_buffer();
788 renderer_flush_queues();
789 renderer_set_interlace(interlace, !lcf);
790 }
791}
792
793#include "../../frontend/plugin_lib.h"
794
795void GPUrearmedCallbacks(const struct rearmed_cbs *cbs)
796{
797 gpu.frameskip.set = cbs->frameskip;
798 gpu.frameskip.advice = &cbs->fskip_advice;
799 gpu.frameskip.active = 0;
800 gpu.frameskip.frame_ready = 1;
801 gpu.state.hcnt = cbs->gpu_hcnt;
802 gpu.state.frame_count = cbs->gpu_frame_count;
803 gpu.state.allow_interlace = cbs->gpu_neon.allow_interlace;
0b02eb77 804 gpu.state.enhancement_enable = cbs->gpu_neon.enhancement_enable;
5440b88e 805
5c1cbedc 806 gpu.useDithering = cbs->gpu_neon.allow_dithering;
9ee0fd5b 807 gpu.mmap = cbs->mmap;
808 gpu.munmap = cbs->munmap;
809
810 // delayed vram mmap
811 if (gpu.vram == NULL)
812 map_vram();
813
5440b88e 814 if (cbs->pl_vout_set_raw_vram)
815 cbs->pl_vout_set_raw_vram(gpu.vram);
816 renderer_set_config(cbs);
817 vout_set_config(cbs);
72e5023f 818}
819
1ab64c54 820// vim:shiftwidth=2:expandtab