try some overscan display option
[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>
308c6e67 12#include <stdlib.h>
1ab64c54 13#include <string.h>
56f08d83 14#include "gpu.h"
90ac6fed 15#include "gpu_timing.h"
abf09485 16#include "../../libpcsxcore/gpu.h" // meh
44e76f8a 17#include "../../frontend/plugin_lib.h"
1ab64c54 18
44e76f8a 19#ifndef ARRAY_SIZE
1ab64c54 20#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
44e76f8a 21#endif
8f5f2dd5 22#ifdef __GNUC__
d30279e2 23#define unlikely(x) __builtin_expect((x), 0)
8f5f2dd5 24#define preload __builtin_prefetch
8dd855cd 25#define noinline __attribute__((noinline))
8f5f2dd5 26#else
27#define unlikely(x)
28#define preload(...)
29#define noinline
8f5f2dd5 30#endif
1ab64c54 31
deb18d24 32//#define log_io gpu_log
56f08d83 33#define log_io(...)
56f08d83 34
9ee0fd5b 35struct psx_gpu gpu;
1ab64c54 36
8412166f 37static noinline int do_cmd_buffer(uint32_t *data, int count,
38 int *cycles_sum, int *cycles_last);
05740673 39static void finish_vram_transfer(int is_read);
48f3d210 40
41static noinline void do_cmd_reset(void)
42{
90ac6fed 43 int dummy = 0;
48f3d210 44 if (unlikely(gpu.cmd_len > 0))
8412166f 45 do_cmd_buffer(gpu.cmd_buffer, gpu.cmd_len, &dummy, &dummy);
48f3d210 46 gpu.cmd_len = 0;
05740673 47
48 if (unlikely(gpu.dma.h > 0))
49 finish_vram_transfer(gpu.dma_start.is_read);
48f3d210 50 gpu.dma.h = 0;
51}
52
6e9bdaef 53static noinline void do_reset(void)
1ab64c54 54{
7841712d 55 unsigned int i;
48f3d210 56
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;
f23b103c 62 gpu.status = 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;
308c6e67 67 gpu.screen.x = gpu.screen.y = 0;
5fe1a2b1 68 renderer_sync_ecmds(gpu.ex_regs);
0b4038f8 69 renderer_notify_res_change();
1ab64c54
GI
70}
71
8dd855cd 72static noinline void update_width(void)
73{
308c6e67 74 static const short hres_all[8] = { 256, 368, 320, 368, 512, 368, 640, 368 };
75 static const uint8_t hdivs[8] = { 10, 7, 8, 7, 5, 7, 4, 7 };
76 uint8_t hdiv = hdivs[(gpu.status >> 16) & 7];
77 int hres = hres_all[(gpu.status >> 16) & 7];
78 int pal = gpu.status & PSX_GPU_STATUS_PAL;
8dd855cd 79 int sw = gpu.screen.x2 - gpu.screen.x1;
9ed80467 80 int type = gpu.state.screen_centering_type;
308c6e67 81 int x = 0, x_auto;
9ed80467 82 if (type == C_AUTO)
83 type = gpu.state.screen_centering_type_default;
308c6e67 84 if (sw <= 0)
85 /* nothing displayed? */;
86 else {
87 int s = pal ? 656 : 608; // or 600? pal is just a guess
88 x = (gpu.screen.x1 - s) / hdiv;
89 x = (x + 1) & ~1; // blitter limitation
90 sw /= hdiv;
91 sw = (sw + 2) & ~3; // according to nocash
f9ffa42c 92
93 if (gpu.state.show_overscan == 2) // widescreen hack
94 sw = (sw + 63) & ~63;
95 if (gpu.state.show_overscan && sw >= hres)
96 x = 0, hres = sw;
9ed80467 97 switch (type) {
44e76f8a 98 case C_INGAME:
308c6e67 99 break;
44e76f8a 100 case C_MANUAL:
308c6e67 101 x = gpu.state.screen_centering_x;
102 break;
103 default:
104 // correct if slightly miscentered
105 x_auto = (hres - sw) / 2 & ~3;
106 if ((uint32_t)x_auto <= 8u && abs(x) < 24)
107 x = x_auto;
108 }
109 if (x + sw > hres)
110 sw = hres - x;
111 // .x range check is done in vout_update()
112 }
113 // reduce the unpleasant right border that a few games have
114 if (gpu.state.screen_centering_type == 0
115 && x <= 4 && hres - (x + sw) >= 4)
116 hres -= 4;
117 gpu.screen.x = x;
118 gpu.screen.w = sw;
119 gpu.screen.hres = hres;
120 gpu.state.dims_changed = 1;
f9ffa42c 121 //printf("xx %d %d (%d) -> %2d, %d / %d\n", gpu.screen.x1,
122 // gpu.screen.x2, gpu.screen.x2 - gpu.screen.x1, x, sw, hres);
8dd855cd 123}
124
125static noinline void update_height(void)
126{
308c6e67 127 int pal = gpu.status & PSX_GPU_STATUS_PAL;
128 int dheight = gpu.status & PSX_GPU_STATUS_DHEIGHT;
129 int y = gpu.screen.y1 - (pal ? 39 : 16); // 39 for spyro
8dd855cd 130 int sh = gpu.screen.y2 - gpu.screen.y1;
308c6e67 131 int center_tol = 16;
132 int vres = 240;
133
134 if (pal && (sh > 240 || gpu.screen.vres == 256))
135 vres = 256;
136 if (dheight)
137 y *= 2, sh *= 2, vres *= 2, center_tol *= 2;
138 if (sh <= 0)
139 /* nothing displayed? */;
140 else {
141 switch (gpu.state.screen_centering_type) {
44e76f8a 142 case C_INGAME:
143 break;
144 case C_BORDERLESS:
145 y = 0;
308c6e67 146 break;
44e76f8a 147 case C_MANUAL:
308c6e67 148 y = gpu.state.screen_centering_y;
149 break;
150 default:
151 // correct if slightly miscentered
152 if ((uint32_t)(vres - sh) <= 1 && abs(y) <= center_tol)
153 y = 0;
154 }
155 if (y + sh > vres)
156 sh = vres - y;
157 }
158 gpu.screen.y = y;
8dd855cd 159 gpu.screen.h = sh;
308c6e67 160 gpu.screen.vres = vres;
161 gpu.state.dims_changed = 1;
162 //printf("yy %d %d -> %d, %d / %d\n",
163 // gpu.screen.y1, gpu.screen.y2, y, sh, vres);
8dd855cd 164}
165
fc84f618 166static noinline void decide_frameskip(void)
167{
9fe27e25 168 if (gpu.frameskip.active)
169 gpu.frameskip.cnt++;
170 else {
171 gpu.frameskip.cnt = 0;
172 gpu.frameskip.frame_ready = 1;
173 }
fc84f618 174
9fe27e25 175 if (!gpu.frameskip.active && *gpu.frameskip.advice)
176 gpu.frameskip.active = 1;
177 else if (gpu.frameskip.set > 0 && gpu.frameskip.cnt < gpu.frameskip.set)
fc84f618 178 gpu.frameskip.active = 1;
179 else
180 gpu.frameskip.active = 0;
fbb4bfff 181
182 if (!gpu.frameskip.active && gpu.frameskip.pending_fill[0] != 0) {
90ac6fed 183 int dummy = 0;
8412166f 184 do_cmd_list(gpu.frameskip.pending_fill, 3, &dummy, &dummy, &dummy);
fbb4bfff 185 gpu.frameskip.pending_fill[0] = 0;
186 }
fc84f618 187}
188
b243416b 189static noinline int decide_frameskip_allow(uint32_t cmd_e3)
9fe27e25 190{
191 // no frameskip if it decides to draw to display area,
192 // but not for interlace since it'll most likely always do that
193 uint32_t x = cmd_e3 & 0x3ff;
194 uint32_t y = (cmd_e3 >> 10) & 0x3ff;
f23b103c 195 gpu.frameskip.allow = (gpu.status & PSX_GPU_STATUS_INTERLACE) ||
308c6e67 196 (uint32_t)(x - gpu.screen.src_x) >= (uint32_t)gpu.screen.w ||
197 (uint32_t)(y - gpu.screen.src_y) >= (uint32_t)gpu.screen.h;
b243416b 198 return gpu.frameskip.allow;
9fe27e25 199}
200
5fe1a2b1 201static void flush_cmd_buffer(void);
202
6e9bdaef 203static noinline void get_gpu_info(uint32_t data)
204{
5fe1a2b1 205 if (unlikely(gpu.cmd_len > 0))
206 flush_cmd_buffer();
6e9bdaef 207 switch (data & 0x0f) {
208 case 0x02:
209 case 0x03:
210 case 0x04:
6e9bdaef 211 gpu.gp0 = gpu.ex_regs[data & 7] & 0xfffff;
212 break;
d04b8924 213 case 0x05:
214 gpu.gp0 = gpu.ex_regs[5] & 0x3fffff;
6e9bdaef 215 break;
216 case 0x07:
217 gpu.gp0 = 2;
218 break;
219 default:
d04b8924 220 // gpu.gp0 unchanged
6e9bdaef 221 break;
222 }
223}
224
9ee0fd5b 225// double, for overdraw guard
226#define VRAM_SIZE (1024 * 512 * 2 * 2)
227
228static int map_vram(void)
229{
230 gpu.vram = gpu.mmap(VRAM_SIZE);
231 if (gpu.vram != NULL) {
232 gpu.vram += 4096 / 2;
233 return 0;
234 }
235 else {
236 fprintf(stderr, "could not map vram, expect crashes\n");
237 return -1;
238 }
239}
240
6e9bdaef 241long GPUinit(void)
242{
9394ada5 243 int ret;
244 ret = vout_init();
245 ret |= renderer_init();
246
0b4038f8 247 memset(&gpu.state, 0, sizeof(gpu.state));
248 memset(&gpu.frameskip, 0, sizeof(gpu.frameskip));
249 gpu.zero = 0;
3ece2f0c 250 gpu.state.frame_count = &gpu.zero;
deb18d24 251 gpu.state.hcnt = &gpu.zero;
48f3d210 252 gpu.cmd_len = 0;
9394ada5 253 do_reset();
48f3d210 254
9ee0fd5b 255 if (gpu.mmap != NULL) {
256 if (map_vram() != 0)
257 ret = -1;
258 }
6e9bdaef 259 return ret;
260}
261
262long GPUshutdown(void)
263{
9ee0fd5b 264 long ret;
265
e929dec5 266 renderer_finish();
9ee0fd5b 267 ret = vout_finish();
268 if (gpu.vram != NULL) {
269 gpu.vram -= 4096 / 2;
270 gpu.munmap(gpu.vram, VRAM_SIZE);
271 }
272 gpu.vram = NULL;
273
274 return ret;
6e9bdaef 275}
276
1ab64c54
GI
277void GPUwriteStatus(uint32_t data)
278{
1ab64c54 279 uint32_t cmd = data >> 24;
2da2fc76 280 int src_x, src_y;
1ab64c54 281
fc84f618 282 if (cmd < ARRAY_SIZE(gpu.regs)) {
48f3d210 283 if (cmd > 1 && cmd != 5 && gpu.regs[cmd] == data)
fc84f618 284 return;
8dd855cd 285 gpu.regs[cmd] = data;
fc84f618 286 }
287
288 gpu.state.fb_dirty = 1;
8dd855cd 289
290 switch (cmd) {
1ab64c54 291 case 0x00:
6e9bdaef 292 do_reset();
1ab64c54 293 break;
48f3d210 294 case 0x01:
295 do_cmd_reset();
296 break;
1ab64c54 297 case 0x03:
308c6e67 298 if (data & 1) {
f23b103c 299 gpu.status |= PSX_GPU_STATUS_BLANKING;
308c6e67 300 gpu.state.dims_changed = 1; // for hud clearing
301 }
f23b103c
PC
302 else
303 gpu.status &= ~PSX_GPU_STATUS_BLANKING;
1ab64c54
GI
304 break;
305 case 0x04:
f23b103c
PC
306 gpu.status &= ~PSX_GPU_STATUS_DMA_MASK;
307 gpu.status |= PSX_GPU_STATUS_DMA(data & 3);
1ab64c54
GI
308 break;
309 case 0x05:
2da2fc76 310 src_x = data & 0x3ff; src_y = (data >> 10) & 0x1ff;
311 if (src_x != gpu.screen.src_x || src_y != gpu.screen.src_y) {
312 gpu.screen.src_x = src_x;
313 gpu.screen.src_y = src_y;
314 renderer_notify_scanout_change(src_x, src_y);
315 if (gpu.frameskip.set) {
316 decide_frameskip_allow(gpu.ex_regs[3]);
317 if (gpu.frameskip.last_flip_frame != *gpu.state.frame_count) {
318 decide_frameskip();
319 gpu.frameskip.last_flip_frame = *gpu.state.frame_count;
320 }
9fe27e25 321 }
fb4c6fba 322 }
1ab64c54 323 break;
8dd855cd 324 case 0x06:
325 gpu.screen.x1 = data & 0xfff;
326 gpu.screen.x2 = (data >> 12) & 0xfff;
327 update_width();
328 break;
1ab64c54
GI
329 case 0x07:
330 gpu.screen.y1 = data & 0x3ff;
331 gpu.screen.y2 = (data >> 10) & 0x3ff;
8dd855cd 332 update_height();
1ab64c54
GI
333 break;
334 case 0x08:
f23b103c 335 gpu.status = (gpu.status & ~0x7f0000) | ((data & 0x3F) << 17) | ((data & 0x40) << 10);
8dd855cd 336 update_width();
337 update_height();
e929dec5 338 renderer_notify_res_change();
1ab64c54 339 break;
deb18d24 340 default:
341 if ((cmd & 0xf0) == 0x10)
342 get_gpu_info(data);
6e9bdaef 343 break;
1ab64c54 344 }
7890a708 345
346#ifdef GPUwriteStatus_ext
347 GPUwriteStatus_ext(data);
348#endif
1ab64c54
GI
349}
350
56f08d83 351const unsigned char cmd_lengths[256] =
1ab64c54 352{
d30279e2
GI
353 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
354 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
355 3, 3, 3, 3, 6, 6, 6, 6, 4, 4, 4, 4, 8, 8, 8, 8, // 20
356 5, 5, 5, 5, 8, 8, 8, 8, 7, 7, 7, 7, 11, 11, 11, 11,
652c6b8b 357 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, // 40
358 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4,
359 2, 2, 2, 2, 3, 3, 3, 3, 1, 1, 1, 1, 0, 0, 0, 0, // 60
d30279e2 360 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 2, 2, 2, 2,
72583812 361 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 80
362 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
363 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // a0
364 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
365 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // c0
366 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
d30279e2
GI
367 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // e0
368 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
369};
370
d30279e2
GI
371#define VRAM_MEM_XY(x, y) &gpu.vram[(y) * 1024 + (x)]
372
72583812 373static void cpy_msb(uint16_t *dst, const uint16_t *src, int l, uint16_t msb)
374{
375 int i;
376 for (i = 0; i < l; i++)
377 dst[i] = src[i] | msb;
378}
379
380static inline void do_vram_line(int x, int y, uint16_t *mem, int l,
381 int is_read, uint16_t msb)
1ab64c54 382{
d30279e2 383 uint16_t *vram = VRAM_MEM_XY(x, y);
72583812 384 if (unlikely(is_read))
d30279e2 385 memcpy(mem, vram, l * 2);
72583812 386 else if (unlikely(msb))
387 cpy_msb(vram, mem, l, msb);
d30279e2
GI
388 else
389 memcpy(vram, mem, l * 2);
390}
391
392static int do_vram_io(uint32_t *data, int count, int is_read)
393{
394 int count_initial = count;
72583812 395 uint16_t msb = gpu.ex_regs[6] << 15;
d30279e2
GI
396 uint16_t *sdata = (uint16_t *)data;
397 int x = gpu.dma.x, y = gpu.dma.y;
398 int w = gpu.dma.w, h = gpu.dma.h;
ddd56f6e 399 int o = gpu.dma.offset;
d30279e2
GI
400 int l;
401 count *= 2; // operate in 16bpp pixels
402
403 if (gpu.dma.offset) {
404 l = w - gpu.dma.offset;
ddd56f6e 405 if (count < l)
d30279e2 406 l = count;
ddd56f6e 407
72583812 408 do_vram_line(x + o, y, sdata, l, is_read, msb);
ddd56f6e 409
410 if (o + l < w)
411 o += l;
412 else {
413 o = 0;
414 y++;
415 h--;
416 }
d30279e2
GI
417 sdata += l;
418 count -= l;
d30279e2
GI
419 }
420
421 for (; h > 0 && count >= w; sdata += w, count -= w, y++, h--) {
422 y &= 511;
72583812 423 do_vram_line(x, y, sdata, w, is_read, msb);
d30279e2
GI
424 }
425
05740673 426 if (h > 0) {
427 if (count > 0) {
428 y &= 511;
72583812 429 do_vram_line(x, y, sdata, count, is_read, msb);
05740673 430 o = count;
431 count = 0;
432 }
d30279e2 433 }
05740673 434 else
435 finish_vram_transfer(is_read);
d30279e2
GI
436 gpu.dma.y = y;
437 gpu.dma.h = h;
ddd56f6e 438 gpu.dma.offset = o;
d30279e2 439
6e9bdaef 440 return count_initial - count / 2;
d30279e2
GI
441}
442
443static void start_vram_transfer(uint32_t pos_word, uint32_t size_word, int is_read)
444{
ddd56f6e 445 if (gpu.dma.h)
446 log_anomaly("start_vram_transfer while old unfinished\n");
447
5440b88e 448 gpu.dma.x = pos_word & 0x3ff;
449 gpu.dma.y = (pos_word >> 16) & 0x1ff;
48f3d210 450 gpu.dma.w = ((size_word - 1) & 0x3ff) + 1;
451 gpu.dma.h = (((size_word >> 16) - 1) & 0x1ff) + 1;
d30279e2 452 gpu.dma.offset = 0;
05740673 453 gpu.dma.is_read = is_read;
454 gpu.dma_start = gpu.dma;
d30279e2 455
9e146206 456 renderer_flush_queues();
457 if (is_read) {
f23b103c 458 gpu.status |= PSX_GPU_STATUS_IMG;
9e146206 459 // XXX: wrong for width 1
ae097dfb 460 gpu.gp0 = LE32TOH(*(uint32_t *) VRAM_MEM_XY(gpu.dma.x, gpu.dma.y));
5440b88e 461 gpu.state.last_vram_read_frame = *gpu.state.frame_count;
9e146206 462 }
d30279e2 463
6e9bdaef 464 log_io("start_vram_transfer %c (%d, %d) %dx%d\n", is_read ? 'r' : 'w',
465 gpu.dma.x, gpu.dma.y, gpu.dma.w, gpu.dma.h);
abf09485 466 if (gpu.gpu_state_change)
467 gpu.gpu_state_change(PGS_VRAM_TRANSFER_START);
d30279e2
GI
468}
469
05740673 470static void finish_vram_transfer(int is_read)
471{
472 if (is_read)
f23b103c 473 gpu.status &= ~PSX_GPU_STATUS_IMG;
893f780e 474 else {
475 gpu.state.fb_dirty = 1;
05740673 476 renderer_update_caches(gpu.dma_start.x, gpu.dma_start.y,
0b4038f8 477 gpu.dma_start.w, gpu.dma_start.h, 0);
893f780e 478 }
abf09485 479 if (gpu.gpu_state_change)
480 gpu.gpu_state_change(PGS_VRAM_TRANSFER_END);
05740673 481}
482
90ac6fed 483static void do_vram_copy(const uint32_t *params, int *cpu_cycles)
72583812 484{
485 const uint32_t sx = LE32TOH(params[0]) & 0x3FF;
486 const uint32_t sy = (LE32TOH(params[0]) >> 16) & 0x1FF;
487 const uint32_t dx = LE32TOH(params[1]) & 0x3FF;
488 const uint32_t dy = (LE32TOH(params[1]) >> 16) & 0x1FF;
489 uint32_t w = ((LE32TOH(params[2]) - 1) & 0x3FF) + 1;
490 uint32_t h = (((LE32TOH(params[2]) >> 16) - 1) & 0x1FF) + 1;
491 uint16_t msb = gpu.ex_regs[6] << 15;
492 uint16_t lbuf[128];
493 uint32_t x, y;
494
90ac6fed 495 *cpu_cycles += gput_copy(w, h);
72583812 496 if (sx == dx && sy == dy && msb == 0)
497 return;
498
499 renderer_flush_queues();
500
501 if (unlikely((sx < dx && dx < sx + w) || sx + w > 1024 || dx + w > 1024 || msb))
502 {
503 for (y = 0; y < h; y++)
504 {
505 const uint16_t *src = VRAM_MEM_XY(0, (sy + y) & 0x1ff);
506 uint16_t *dst = VRAM_MEM_XY(0, (dy + y) & 0x1ff);
507 for (x = 0; x < w; x += ARRAY_SIZE(lbuf))
508 {
509 uint32_t x1, w1 = w - x;
510 if (w1 > ARRAY_SIZE(lbuf))
511 w1 = ARRAY_SIZE(lbuf);
512 for (x1 = 0; x1 < w1; x1++)
513 lbuf[x1] = src[(sx + x + x1) & 0x3ff];
514 for (x1 = 0; x1 < w1; x1++)
515 dst[(dx + x + x1) & 0x3ff] = lbuf[x1] | msb;
516 }
517 }
518 }
519 else
520 {
521 uint32_t sy1 = sy, dy1 = dy;
522 for (y = 0; y < h; y++, sy1++, dy1++)
523 memcpy(VRAM_MEM_XY(dx, dy1 & 0x1ff), VRAM_MEM_XY(sx, sy1 & 0x1ff), w * 2);
524 }
525
526 renderer_update_caches(dx, dy, w, h, 0);
527}
528
b243416b 529static noinline int do_cmd_list_skip(uint32_t *data, int count, int *last_cmd)
530{
90ac6fed 531 int cmd = 0, pos = 0, len, dummy = 0, v;
b243416b 532 int skip = 1;
533
fbb4bfff 534 gpu.frameskip.pending_fill[0] = 0;
535
b243416b 536 while (pos < count && skip) {
537 uint32_t *list = data + pos;
89df80c6 538 cmd = LE32TOH(list[0]) >> 24;
b243416b 539 len = 1 + cmd_lengths[cmd];
8295d332 540 if (pos + len > count) {
541 cmd = -1;
542 break; // incomplete cmd
543 }
b243416b 544
97e07db9 545 switch (cmd) {
546 case 0x02:
89df80c6 547 if ((LE32TOH(list[2]) & 0x3ff) > gpu.screen.w || ((LE32TOH(list[2]) >> 16) & 0x1ff) > gpu.screen.h)
97e07db9 548 // clearing something large, don't skip
8412166f 549 do_cmd_list(list, 3, &dummy, &dummy, &dummy);
97e07db9 550 else
551 memcpy(gpu.frameskip.pending_fill, list, 3 * 4);
552 break;
553 case 0x24 ... 0x27:
554 case 0x2c ... 0x2f:
555 case 0x34 ... 0x37:
556 case 0x3c ... 0x3f:
557 gpu.ex_regs[1] &= ~0x1ff;
89df80c6 558 gpu.ex_regs[1] |= LE32TOH(list[4 + ((cmd >> 4) & 1)]) & 0x1ff;
97e07db9 559 break;
560 case 0x48 ... 0x4F:
561 for (v = 3; pos + v < count; v++)
562 {
89df80c6 563 if ((list[v] & HTOLE32(0xf000f000)) == HTOLE32(0x50005000))
97e07db9 564 break;
565 }
566 len += v - 3;
567 break;
568 case 0x58 ... 0x5F:
569 for (v = 4; pos + v < count; v += 2)
570 {
89df80c6 571 if ((list[v] & HTOLE32(0xf000f000)) == HTOLE32(0x50005000))
97e07db9 572 break;
573 }
574 len += v - 4;
575 break;
576 default:
577 if (cmd == 0xe3)
89df80c6 578 skip = decide_frameskip_allow(LE32TOH(list[0]));
97e07db9 579 if ((cmd & 0xf8) == 0xe0)
89df80c6 580 gpu.ex_regs[cmd & 7] = LE32TOH(list[0]);
97e07db9 581 break;
b243416b 582 }
72583812 583 if (0x80 <= cmd && cmd <= 0xdf)
b243416b 584 break; // image i/o
97e07db9 585
b243416b 586 pos += len;
587 }
588
589 renderer_sync_ecmds(gpu.ex_regs);
590 *last_cmd = cmd;
591 return pos;
592}
593
8412166f 594static noinline int do_cmd_buffer(uint32_t *data, int count,
595 int *cycles_sum, int *cycles_last)
d30279e2 596{
b243416b 597 int cmd, pos;
598 uint32_t old_e3 = gpu.ex_regs[3];
fc84f618 599 int vram_dirty = 0;
d30279e2 600
d30279e2 601 // process buffer
b243416b 602 for (pos = 0; pos < count; )
d30279e2 603 {
b243416b 604 if (gpu.dma.h && !gpu.dma_start.is_read) { // XXX: need to verify
605 vram_dirty = 1;
d30279e2 606 pos += do_vram_io(data + pos, count - pos, 0);
ddd56f6e 607 if (pos == count)
608 break;
d30279e2
GI
609 }
610
89df80c6 611 cmd = LE32TOH(data[pos]) >> 24;
97e07db9 612 if (0xa0 <= cmd && cmd <= 0xdf) {
7194a46a
DS
613 if (unlikely((pos+2) >= count)) {
614 // incomplete vram write/read cmd, can't consume yet
615 cmd = -1;
616 break;
617 }
618
d30279e2 619 // consume vram write/read cmd
89df80c6 620 start_vram_transfer(LE32TOH(data[pos + 1]), LE32TOH(data[pos + 2]), (cmd & 0xe0) == 0xc0);
b243416b 621 pos += 3;
622 continue;
d30279e2 623 }
72583812 624 else if ((cmd & 0xe0) == 0x80) {
625 if (unlikely((pos+3) >= count)) {
626 cmd = -1; // incomplete cmd, can't consume yet
627 break;
628 }
8412166f 629 *cycles_sum += *cycles_last;
630 *cycles_last = 0;
631 do_vram_copy(data + pos + 1, cycles_last);
893f780e 632 vram_dirty = 1;
72583812 633 pos += 4;
634 continue;
635 }
1cec4719 636 else if (cmd == 0x1f) {
637 log_anomaly("irq1?\n");
638 pos++;
639 continue;
640 }
b243416b 641
1e07f71d 642 // 0xex cmds might affect frameskip.allow, so pass to do_cmd_list_skip
89df80c6 643 if (gpu.frameskip.active && (gpu.frameskip.allow || ((LE32TOH(data[pos]) >> 24) & 0xf0) == 0xe0))
b243416b 644 pos += do_cmd_list_skip(data + pos, count - pos, &cmd);
645 else {
8412166f 646 pos += do_cmd_list(data + pos, count - pos, cycles_sum, cycles_last, &cmd);
b243416b 647 vram_dirty = 1;
648 }
649
650 if (cmd == -1)
651 // incomplete cmd
ddd56f6e 652 break;
d30279e2 653 }
ddd56f6e 654
f23b103c
PC
655 gpu.status &= ~0x1fff;
656 gpu.status |= gpu.ex_regs[1] & 0x7ff;
657 gpu.status |= (gpu.ex_regs[6] & 3) << 11;
a3a9f519 658
fc84f618 659 gpu.state.fb_dirty |= vram_dirty;
660
b243416b 661 if (old_e3 != gpu.ex_regs[3])
662 decide_frameskip_allow(gpu.ex_regs[3]);
663
ddd56f6e 664 return count - pos;
d30279e2
GI
665}
666
abf09485 667static noinline void flush_cmd_buffer(void)
d30279e2 668{
90ac6fed 669 int dummy = 0, left;
8412166f 670 left = do_cmd_buffer(gpu.cmd_buffer, gpu.cmd_len, &dummy, &dummy);
d30279e2
GI
671 if (left > 0)
672 memmove(gpu.cmd_buffer, gpu.cmd_buffer + gpu.cmd_len - left, left * 4);
abf09485 673 if (left != gpu.cmd_len) {
674 if (!gpu.dma.h && gpu.gpu_state_change)
675 gpu.gpu_state_change(PGS_PRIMITIVE_START);
676 gpu.cmd_len = left;
677 }
1ab64c54
GI
678}
679
680void GPUwriteDataMem(uint32_t *mem, int count)
681{
90ac6fed 682 int dummy = 0, left;
d30279e2 683
56f08d83 684 log_io("gpu_dma_write %p %d\n", mem, count);
685
d30279e2
GI
686 if (unlikely(gpu.cmd_len > 0))
687 flush_cmd_buffer();
56f08d83 688
8412166f 689 left = do_cmd_buffer(mem, count, &dummy, &dummy);
d30279e2 690 if (left)
56f08d83 691 log_anomaly("GPUwriteDataMem: discarded %d/%d words\n", left, count);
1ab64c54
GI
692}
693
d30279e2 694void GPUwriteData(uint32_t data)
1ab64c54 695{
56f08d83 696 log_io("gpu_write %08x\n", data);
89df80c6 697 gpu.cmd_buffer[gpu.cmd_len++] = HTOLE32(data);
d30279e2
GI
698 if (gpu.cmd_len >= CMD_BUFFER_LEN)
699 flush_cmd_buffer();
1ab64c54
GI
700}
701
8412166f 702long GPUdmaChain(uint32_t *rambase, uint32_t start_addr,
703 uint32_t *progress_addr, int32_t *cycles_last_cmd)
1ab64c54 704{
24a7af90 705 uint32_t addr, *list, ld_addr;
706 int len, left, count, ld_count = 32;
8412166f 707 int cpu_cycles_sum = 0;
708 int cpu_cycles_last = 0;
d30279e2 709
8f5f2dd5 710 preload(rambase + (start_addr & 0x1fffff) / 4);
711
d30279e2
GI
712 if (unlikely(gpu.cmd_len > 0))
713 flush_cmd_buffer();
714
56f08d83 715 log_io("gpu_dma_chain\n");
24a7af90 716 addr = ld_addr = start_addr & 0xffffff;
09159d99 717 for (count = 0; (addr & 0x800000) == 0; count++)
ddd56f6e 718 {
ddd56f6e 719 list = rambase + (addr & 0x1fffff) / 4;
89df80c6
PC
720 len = LE32TOH(list[0]) >> 24;
721 addr = LE32TOH(list[0]) & 0xffffff;
8f5f2dd5 722 preload(rambase + (addr & 0x1fffff) / 4);
723
8412166f 724 cpu_cycles_sum += 10;
1c72b1c2 725 if (len > 0)
8412166f 726 cpu_cycles_sum += 5 + len;
deb18d24 727
8412166f 728 log_io(".chain %08lx #%d+%d %u+%u\n",
729 (long)(list - rambase) * 4, len, gpu.cmd_len, cpu_cycles_sum, cpu_cycles_last);
ae36bb28 730 if (unlikely(gpu.cmd_len > 0)) {
23948df3 731 if (gpu.cmd_len + len > ARRAY_SIZE(gpu.cmd_buffer)) {
732 log_anomaly("cmd_buffer overflow, likely garbage commands\n");
733 gpu.cmd_len = 0;
734 }
ae36bb28 735 memcpy(gpu.cmd_buffer + gpu.cmd_len, list + 1, len * 4);
736 gpu.cmd_len += len;
737 flush_cmd_buffer();
738 continue;
739 }
ddd56f6e 740
56f08d83 741 if (len) {
8412166f 742 left = do_cmd_buffer(list + 1, len, &cpu_cycles_sum, &cpu_cycles_last);
ae36bb28 743 if (left) {
744 memcpy(gpu.cmd_buffer, list + 1 + len - left, left * 4);
745 gpu.cmd_len = left;
746 log_anomaly("GPUdmaChain: %d/%d words left\n", left, len);
747 }
56f08d83 748 }
ddd56f6e 749
8c84ba5f 750 if (progress_addr) {
751 *progress_addr = addr;
752 break;
753 }
24a7af90 754 if (addr == ld_addr) {
755 log_anomaly("GPUdmaChain: loop @ %08x, cnt=%u\n", addr, count);
756 break;
09159d99 757 }
24a7af90 758 if (count == ld_count) {
759 ld_addr = addr;
760 ld_count *= 2;
09159d99 761 }
d30279e2 762 }
09159d99 763
8412166f 764 //printf(" -> %d %d\n", cpu_cycles_sum, cpu_cycles_last);
3ece2f0c 765 gpu.state.last_list.frame = *gpu.state.frame_count;
deb18d24 766 gpu.state.last_list.hcnt = *gpu.state.hcnt;
8412166f 767 gpu.state.last_list.cycles = cpu_cycles_sum + cpu_cycles_last;
deb18d24 768 gpu.state.last_list.addr = start_addr;
769
8412166f 770 *cycles_last_cmd = cpu_cycles_last;
771 return cpu_cycles_sum;
1ab64c54
GI
772}
773
d30279e2
GI
774void GPUreadDataMem(uint32_t *mem, int count)
775{
56f08d83 776 log_io("gpu_dma_read %p %d\n", mem, count);
777
d30279e2
GI
778 if (unlikely(gpu.cmd_len > 0))
779 flush_cmd_buffer();
56f08d83 780
d30279e2
GI
781 if (gpu.dma.h)
782 do_vram_io(mem, count, 1);
783}
784
785uint32_t GPUreadData(void)
786{
9e146206 787 uint32_t ret;
56f08d83 788
789 if (unlikely(gpu.cmd_len > 0))
790 flush_cmd_buffer();
791
9e146206 792 ret = gpu.gp0;
ae097dfb
PC
793 if (gpu.dma.h) {
794 ret = HTOLE32(ret);
9e146206 795 do_vram_io(&ret, 1, 1);
ae097dfb
PC
796 ret = LE32TOH(ret);
797 }
56f08d83 798
9e146206 799 log_io("gpu_read %08x\n", ret);
800 return ret;
d30279e2
GI
801}
802
803uint32_t GPUreadStatus(void)
804{
ddd56f6e 805 uint32_t ret;
56f08d83 806
d30279e2
GI
807 if (unlikely(gpu.cmd_len > 0))
808 flush_cmd_buffer();
809
f23b103c 810 ret = gpu.status;
ddd56f6e 811 log_io("gpu_read_status %08x\n", ret);
812 return ret;
d30279e2
GI
813}
814
096ec49b 815struct GPUFreeze
1ab64c54
GI
816{
817 uint32_t ulFreezeVersion; // should be always 1 for now (set by main emu)
818 uint32_t ulStatus; // current gpu status
819 uint32_t ulControl[256]; // latest control register values
820 unsigned char psxVRam[1024*1024*2]; // current VRam image (full 2 MB for ZN)
096ec49b 821};
1ab64c54 822
096ec49b 823long GPUfreeze(uint32_t type, struct GPUFreeze *freeze)
1ab64c54 824{
fc84f618 825 int i;
826
1ab64c54
GI
827 switch (type) {
828 case 1: // save
d30279e2
GI
829 if (gpu.cmd_len > 0)
830 flush_cmd_buffer();
9ee0fd5b 831 memcpy(freeze->psxVRam, gpu.vram, 1024 * 512 * 2);
1ab64c54 832 memcpy(freeze->ulControl, gpu.regs, sizeof(gpu.regs));
6e9bdaef 833 memcpy(freeze->ulControl + 0xe0, gpu.ex_regs, sizeof(gpu.ex_regs));
f23b103c 834 freeze->ulStatus = gpu.status;
1ab64c54
GI
835 break;
836 case 0: // load
9ee0fd5b 837 memcpy(gpu.vram, freeze->psxVRam, 1024 * 512 * 2);
1ab64c54 838 memcpy(gpu.regs, freeze->ulControl, sizeof(gpu.regs));
6e9bdaef 839 memcpy(gpu.ex_regs, freeze->ulControl + 0xe0, sizeof(gpu.ex_regs));
f23b103c 840 gpu.status = freeze->ulStatus;
3d47ef17 841 gpu.cmd_len = 0;
fc84f618 842 for (i = 8; i > 0; i--) {
843 gpu.regs[i] ^= 1; // avoid reg change detection
844 GPUwriteStatus((i << 24) | (gpu.regs[i] ^ 1));
845 }
5b745e5b 846 renderer_sync_ecmds(gpu.ex_regs);
2da2fc76 847 renderer_update_caches(0, 0, 1024, 512, 0);
1ab64c54
GI
848 break;
849 }
850
851 return 1;
852}
853
5440b88e 854void GPUupdateLace(void)
855{
856 if (gpu.cmd_len > 0)
857 flush_cmd_buffer();
858 renderer_flush_queues();
859
cb245e56 860#ifndef RAW_FB_DISPLAY
f23b103c 861 if (gpu.status & PSX_GPU_STATUS_BLANKING) {
aafcb4dd 862 if (!gpu.state.blanked) {
863 vout_blank();
864 gpu.state.blanked = 1;
865 gpu.state.fb_dirty = 1;
866 }
867 return;
868 }
869
870 if (!gpu.state.fb_dirty)
5440b88e 871 return;
cb245e56 872#endif
5440b88e 873
874 if (gpu.frameskip.set) {
875 if (!gpu.frameskip.frame_ready) {
876 if (*gpu.state.frame_count - gpu.frameskip.last_flip_frame < 9)
877 return;
878 gpu.frameskip.active = 0;
879 }
880 gpu.frameskip.frame_ready = 0;
881 }
882
883 vout_update();
0b4038f8 884 if (gpu.state.enhancement_active && !gpu.state.enhancement_was_active)
885 renderer_update_caches(0, 0, 1024, 512, 1);
886 gpu.state.enhancement_was_active = gpu.state.enhancement_active;
5440b88e 887 gpu.state.fb_dirty = 0;
aafcb4dd 888 gpu.state.blanked = 0;
5440b88e 889}
890
72e5023f 891void GPUvBlank(int is_vblank, int lcf)
892{
5440b88e 893 int interlace = gpu.state.allow_interlace
f23b103c
PC
894 && (gpu.status & PSX_GPU_STATUS_INTERLACE)
895 && (gpu.status & PSX_GPU_STATUS_DHEIGHT);
5440b88e 896 // interlace doesn't look nice on progressive displays,
897 // so we have this "auto" mode here for games that don't read vram
898 if (gpu.state.allow_interlace == 2
899 && *gpu.state.frame_count - gpu.state.last_vram_read_frame > 1)
900 {
901 interlace = 0;
902 }
903 if (interlace || interlace != gpu.state.old_interlace) {
904 gpu.state.old_interlace = interlace;
905
906 if (gpu.cmd_len > 0)
907 flush_cmd_buffer();
908 renderer_flush_queues();
909 renderer_set_interlace(interlace, !lcf);
910 }
911}
912
ab88daca 913void GPUgetScreenInfo(int *y, int *base_hres)
914{
915 *y = gpu.screen.y;
916 *base_hres = gpu.screen.vres;
917 if (gpu.status & PSX_GPU_STATUS_DHEIGHT)
918 *base_hres >>= 1;
919}
920
5440b88e 921void GPUrearmedCallbacks(const struct rearmed_cbs *cbs)
922{
923 gpu.frameskip.set = cbs->frameskip;
924 gpu.frameskip.advice = &cbs->fskip_advice;
925 gpu.frameskip.active = 0;
926 gpu.frameskip.frame_ready = 1;
49f5a273
PC
927 gpu.state.hcnt = (uint32_t *)cbs->gpu_hcnt;
928 gpu.state.frame_count = (uint32_t *)cbs->gpu_frame_count;
5440b88e 929 gpu.state.allow_interlace = cbs->gpu_neon.allow_interlace;
0b02eb77 930 gpu.state.enhancement_enable = cbs->gpu_neon.enhancement_enable;
9ed80467 931 gpu.state.screen_centering_type_default = cbs->screen_centering_type_default;
308c6e67 932 if (gpu.state.screen_centering_type != cbs->screen_centering_type
933 || gpu.state.screen_centering_x != cbs->screen_centering_x
f9ffa42c 934 || gpu.state.screen_centering_y != cbs->screen_centering_y
935 || gpu.state.show_overscan != cbs->show_overscan) {
308c6e67 936 gpu.state.screen_centering_type = cbs->screen_centering_type;
937 gpu.state.screen_centering_x = cbs->screen_centering_x;
938 gpu.state.screen_centering_y = cbs->screen_centering_y;
f9ffa42c 939 gpu.state.show_overscan = cbs->show_overscan;
308c6e67 940 update_width();
941 update_height();
942 }
5440b88e 943
9ee0fd5b 944 gpu.mmap = cbs->mmap;
945 gpu.munmap = cbs->munmap;
abf09485 946 gpu.gpu_state_change = cbs->gpu_state_change;
9ee0fd5b 947
948 // delayed vram mmap
949 if (gpu.vram == NULL)
950 map_vram();
951
5440b88e 952 if (cbs->pl_vout_set_raw_vram)
953 cbs->pl_vout_set_raw_vram(gpu.vram);
954 renderer_set_config(cbs);
955 vout_set_config(cbs);
72e5023f 956}
957
1ab64c54 958// vim:shiftwidth=2:expandtab