gpulib: update gpuinfo
[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
ddd56f6e 530long GPUdmaChain(uint32_t *rambase, uint32_t start_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
09159d99 562 #define LD_THRESHOLD (8*1024)
563 if (count >= LD_THRESHOLD) {
564 if (count == LD_THRESHOLD) {
565 ld_addr = addr;
566 continue;
567 }
568
569 // loop detection marker
570 // (bit23 set causes DMA error on real machine, so
571 // unlikely to be ever set by the game)
89df80c6 572 list[0] |= HTOLE32(0x800000);
09159d99 573 }
ddd56f6e 574 }
575
09159d99 576 if (ld_addr != 0) {
577 // remove loop detection markers
578 count -= LD_THRESHOLD + 2;
579 addr = ld_addr & 0x1fffff;
580 while (count-- > 0) {
581 list = rambase + addr / 4;
89df80c6
PC
582 addr = LE32TOH(list[0]) & 0x1fffff;
583 list[0] &= HTOLE32(~0x800000);
09159d99 584 }
d30279e2 585 }
09159d99 586
3ece2f0c 587 gpu.state.last_list.frame = *gpu.state.frame_count;
deb18d24 588 gpu.state.last_list.hcnt = *gpu.state.hcnt;
1c72b1c2 589 gpu.state.last_list.cycles = cpu_cycles;
deb18d24 590 gpu.state.last_list.addr = start_addr;
591
1c72b1c2 592 return cpu_cycles;
1ab64c54
GI
593}
594
d30279e2
GI
595void GPUreadDataMem(uint32_t *mem, int count)
596{
56f08d83 597 log_io("gpu_dma_read %p %d\n", mem, count);
598
d30279e2
GI
599 if (unlikely(gpu.cmd_len > 0))
600 flush_cmd_buffer();
56f08d83 601
d30279e2
GI
602 if (gpu.dma.h)
603 do_vram_io(mem, count, 1);
604}
605
606uint32_t GPUreadData(void)
607{
9e146206 608 uint32_t ret;
56f08d83 609
610 if (unlikely(gpu.cmd_len > 0))
611 flush_cmd_buffer();
612
9e146206 613 ret = gpu.gp0;
ae097dfb
PC
614 if (gpu.dma.h) {
615 ret = HTOLE32(ret);
9e146206 616 do_vram_io(&ret, 1, 1);
ae097dfb
PC
617 ret = LE32TOH(ret);
618 }
56f08d83 619
9e146206 620 log_io("gpu_read %08x\n", ret);
621 return ret;
d30279e2
GI
622}
623
624uint32_t GPUreadStatus(void)
625{
ddd56f6e 626 uint32_t ret;
56f08d83 627
d30279e2
GI
628 if (unlikely(gpu.cmd_len > 0))
629 flush_cmd_buffer();
630
f23b103c 631 ret = gpu.status;
ddd56f6e 632 log_io("gpu_read_status %08x\n", ret);
633 return ret;
d30279e2
GI
634}
635
096ec49b 636struct GPUFreeze
1ab64c54
GI
637{
638 uint32_t ulFreezeVersion; // should be always 1 for now (set by main emu)
639 uint32_t ulStatus; // current gpu status
640 uint32_t ulControl[256]; // latest control register values
641 unsigned char psxVRam[1024*1024*2]; // current VRam image (full 2 MB for ZN)
096ec49b 642};
1ab64c54 643
096ec49b 644long GPUfreeze(uint32_t type, struct GPUFreeze *freeze)
1ab64c54 645{
fc84f618 646 int i;
647
1ab64c54
GI
648 switch (type) {
649 case 1: // save
d30279e2
GI
650 if (gpu.cmd_len > 0)
651 flush_cmd_buffer();
9ee0fd5b 652 memcpy(freeze->psxVRam, gpu.vram, 1024 * 512 * 2);
1ab64c54 653 memcpy(freeze->ulControl, gpu.regs, sizeof(gpu.regs));
6e9bdaef 654 memcpy(freeze->ulControl + 0xe0, gpu.ex_regs, sizeof(gpu.ex_regs));
f23b103c 655 freeze->ulStatus = gpu.status;
1ab64c54
GI
656 break;
657 case 0: // load
9ee0fd5b 658 memcpy(gpu.vram, freeze->psxVRam, 1024 * 512 * 2);
1ab64c54 659 memcpy(gpu.regs, freeze->ulControl, sizeof(gpu.regs));
6e9bdaef 660 memcpy(gpu.ex_regs, freeze->ulControl + 0xe0, sizeof(gpu.ex_regs));
f23b103c 661 gpu.status = freeze->ulStatus;
3d47ef17 662 gpu.cmd_len = 0;
fc84f618 663 for (i = 8; i > 0; i--) {
664 gpu.regs[i] ^= 1; // avoid reg change detection
665 GPUwriteStatus((i << 24) | (gpu.regs[i] ^ 1));
666 }
5b745e5b 667 renderer_sync_ecmds(gpu.ex_regs);
05740673 668 renderer_update_caches(0, 0, 1024, 512);
1ab64c54
GI
669 break;
670 }
671
672 return 1;
673}
674
5440b88e 675void GPUupdateLace(void)
676{
677 if (gpu.cmd_len > 0)
678 flush_cmd_buffer();
679 renderer_flush_queues();
680
f23b103c 681 if (gpu.status & PSX_GPU_STATUS_BLANKING) {
aafcb4dd 682 if (!gpu.state.blanked) {
683 vout_blank();
684 gpu.state.blanked = 1;
685 gpu.state.fb_dirty = 1;
686 }
687 return;
688 }
689
690 if (!gpu.state.fb_dirty)
5440b88e 691 return;
692
693 if (gpu.frameskip.set) {
694 if (!gpu.frameskip.frame_ready) {
695 if (*gpu.state.frame_count - gpu.frameskip.last_flip_frame < 9)
696 return;
697 gpu.frameskip.active = 0;
698 }
699 gpu.frameskip.frame_ready = 0;
700 }
701
702 vout_update();
703 gpu.state.fb_dirty = 0;
aafcb4dd 704 gpu.state.blanked = 0;
5440b88e 705}
706
72e5023f 707void GPUvBlank(int is_vblank, int lcf)
708{
5440b88e 709 int interlace = gpu.state.allow_interlace
f23b103c
PC
710 && (gpu.status & PSX_GPU_STATUS_INTERLACE)
711 && (gpu.status & PSX_GPU_STATUS_DHEIGHT);
5440b88e 712 // interlace doesn't look nice on progressive displays,
713 // so we have this "auto" mode here for games that don't read vram
714 if (gpu.state.allow_interlace == 2
715 && *gpu.state.frame_count - gpu.state.last_vram_read_frame > 1)
716 {
717 interlace = 0;
718 }
719 if (interlace || interlace != gpu.state.old_interlace) {
720 gpu.state.old_interlace = interlace;
721
722 if (gpu.cmd_len > 0)
723 flush_cmd_buffer();
724 renderer_flush_queues();
725 renderer_set_interlace(interlace, !lcf);
726 }
727}
728
729#include "../../frontend/plugin_lib.h"
730
731void GPUrearmedCallbacks(const struct rearmed_cbs *cbs)
732{
733 gpu.frameskip.set = cbs->frameskip;
734 gpu.frameskip.advice = &cbs->fskip_advice;
735 gpu.frameskip.active = 0;
736 gpu.frameskip.frame_ready = 1;
737 gpu.state.hcnt = cbs->gpu_hcnt;
738 gpu.state.frame_count = cbs->gpu_frame_count;
739 gpu.state.allow_interlace = cbs->gpu_neon.allow_interlace;
0b02eb77 740 gpu.state.enhancement_enable = cbs->gpu_neon.enhancement_enable;
5440b88e 741
9ee0fd5b 742 gpu.mmap = cbs->mmap;
743 gpu.munmap = cbs->munmap;
744
745 // delayed vram mmap
746 if (gpu.vram == NULL)
747 map_vram();
748
5440b88e 749 if (cbs->pl_vout_set_raw_vram)
750 cbs->pl_vout_set_raw_vram(gpu.vram);
751 renderer_set_config(cbs);
752 vout_set_config(cbs);
72e5023f 753}
754
1ab64c54 755// vim:shiftwidth=2:expandtab