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