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