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