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