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