gpu_neon: rework buffer selection
[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       memcpy(gpu.cmd_buffer + gpu.cmd_len, list + 1, len * 4);
618       gpu.cmd_len += len;
619       flush_cmd_buffer();
620       continue;
621     }
622
623     if (len) {
624       left = do_cmd_buffer(list + 1, len);
625       if (left) {
626         memcpy(gpu.cmd_buffer, list + 1 + len - left, left * 4);
627         gpu.cmd_len = left;
628         log_anomaly("GPUdmaChain: %d/%d words left\n", left, len);
629       }
630     }
631
632     if (progress_addr) {
633       *progress_addr = addr;
634       break;
635     }
636     #define LD_THRESHOLD (8*1024)
637     if (count >= LD_THRESHOLD) {
638       if (count == LD_THRESHOLD) {
639         ld_addr = addr;
640         continue;
641       }
642
643       // loop detection marker
644       // (bit23 set causes DMA error on real machine, so
645       //  unlikely to be ever set by the game)
646       list[0] |= HTOLE32(0x800000);
647     }
648   }
649
650   if (ld_addr != 0) {
651     // remove loop detection markers
652     count -= LD_THRESHOLD + 2;
653     addr = ld_addr & 0x1fffff;
654     while (count-- > 0) {
655       list = rambase + addr / 4;
656       addr = LE32TOH(list[0]) & 0x1fffff;
657       list[0] &= HTOLE32(~0x800000);
658     }
659   }
660
661   gpu.state.last_list.frame = *gpu.state.frame_count;
662   gpu.state.last_list.hcnt = *gpu.state.hcnt;
663   gpu.state.last_list.cycles = cpu_cycles;
664   gpu.state.last_list.addr = start_addr;
665
666   return cpu_cycles;
667 }
668
669 void GPUreadDataMem(uint32_t *mem, int count)
670 {
671   log_io("gpu_dma_read  %p %d\n", mem, count);
672
673   if (unlikely(gpu.cmd_len > 0))
674     flush_cmd_buffer();
675
676   if (gpu.dma.h)
677     do_vram_io(mem, count, 1);
678 }
679
680 uint32_t GPUreadData(void)
681 {
682   uint32_t ret;
683
684   if (unlikely(gpu.cmd_len > 0))
685     flush_cmd_buffer();
686
687   ret = gpu.gp0;
688   if (gpu.dma.h) {
689     ret = HTOLE32(ret);
690     do_vram_io(&ret, 1, 1);
691     ret = LE32TOH(ret);
692   }
693
694   log_io("gpu_read %08x\n", ret);
695   return ret;
696 }
697
698 uint32_t GPUreadStatus(void)
699 {
700   uint32_t ret;
701
702   if (unlikely(gpu.cmd_len > 0))
703     flush_cmd_buffer();
704
705   ret = gpu.status;
706   log_io("gpu_read_status %08x\n", ret);
707   return ret;
708 }
709
710 struct GPUFreeze
711 {
712   uint32_t ulFreezeVersion;      // should be always 1 for now (set by main emu)
713   uint32_t ulStatus;             // current gpu status
714   uint32_t ulControl[256];       // latest control register values
715   unsigned char psxVRam[1024*1024*2]; // current VRam image (full 2 MB for ZN)
716 };
717
718 long GPUfreeze(uint32_t type, struct GPUFreeze *freeze)
719 {
720   int i;
721
722   switch (type) {
723     case 1: // save
724       if (gpu.cmd_len > 0)
725         flush_cmd_buffer();
726       memcpy(freeze->psxVRam, gpu.vram, 1024 * 512 * 2);
727       memcpy(freeze->ulControl, gpu.regs, sizeof(gpu.regs));
728       memcpy(freeze->ulControl + 0xe0, gpu.ex_regs, sizeof(gpu.ex_regs));
729       freeze->ulStatus = gpu.status;
730       break;
731     case 0: // load
732       memcpy(gpu.vram, freeze->psxVRam, 1024 * 512 * 2);
733       memcpy(gpu.regs, freeze->ulControl, sizeof(gpu.regs));
734       memcpy(gpu.ex_regs, freeze->ulControl + 0xe0, sizeof(gpu.ex_regs));
735       gpu.status = freeze->ulStatus;
736       gpu.cmd_len = 0;
737       for (i = 8; i > 0; i--) {
738         gpu.regs[i] ^= 1; // avoid reg change detection
739         GPUwriteStatus((i << 24) | (gpu.regs[i] ^ 1));
740       }
741       renderer_sync_ecmds(gpu.ex_regs);
742       renderer_update_caches(0, 0, 1024, 512, 1);
743       break;
744   }
745
746   return 1;
747 }
748
749 void GPUupdateLace(void)
750 {
751   if (gpu.cmd_len > 0)
752     flush_cmd_buffer();
753   renderer_flush_queues();
754
755   if (gpu.status & PSX_GPU_STATUS_BLANKING) {
756     if (!gpu.state.blanked) {
757       vout_blank();
758       gpu.state.blanked = 1;
759       gpu.state.fb_dirty = 1;
760     }
761     return;
762   }
763
764   if (!gpu.state.fb_dirty)
765     return;
766
767   if (gpu.frameskip.set) {
768     if (!gpu.frameskip.frame_ready) {
769       if (*gpu.state.frame_count - gpu.frameskip.last_flip_frame < 9)
770         return;
771       gpu.frameskip.active = 0;
772     }
773     gpu.frameskip.frame_ready = 0;
774   }
775
776   vout_update();
777   if (gpu.state.enhancement_active && !gpu.state.enhancement_was_active)
778     renderer_update_caches(0, 0, 1024, 512, 1);
779   gpu.state.enhancement_was_active = gpu.state.enhancement_active;
780   gpu.state.fb_dirty = 0;
781   gpu.state.blanked = 0;
782 }
783
784 void GPUvBlank(int is_vblank, int lcf)
785 {
786   int interlace = gpu.state.allow_interlace
787     && (gpu.status & PSX_GPU_STATUS_INTERLACE)
788     && (gpu.status & PSX_GPU_STATUS_DHEIGHT);
789   // interlace doesn't look nice on progressive displays,
790   // so we have this "auto" mode here for games that don't read vram
791   if (gpu.state.allow_interlace == 2
792       && *gpu.state.frame_count - gpu.state.last_vram_read_frame > 1)
793   {
794     interlace = 0;
795   }
796   if (interlace || interlace != gpu.state.old_interlace) {
797     gpu.state.old_interlace = interlace;
798
799     if (gpu.cmd_len > 0)
800       flush_cmd_buffer();
801     renderer_flush_queues();
802     renderer_set_interlace(interlace, !lcf);
803   }
804 }
805
806 #include "../../frontend/plugin_lib.h"
807
808 void GPUrearmedCallbacks(const struct rearmed_cbs *cbs)
809 {
810   gpu.frameskip.set = cbs->frameskip;
811   gpu.frameskip.advice = &cbs->fskip_advice;
812   gpu.frameskip.active = 0;
813   gpu.frameskip.frame_ready = 1;
814   gpu.state.hcnt = cbs->gpu_hcnt;
815   gpu.state.frame_count = cbs->gpu_frame_count;
816   gpu.state.allow_interlace = cbs->gpu_neon.allow_interlace;
817   gpu.state.enhancement_enable = cbs->gpu_neon.enhancement_enable;
818   if (gpu.state.screen_centering_type != cbs->screen_centering_type
819       || gpu.state.screen_centering_x != cbs->screen_centering_x
820       || gpu.state.screen_centering_y != cbs->screen_centering_y) {
821     gpu.state.screen_centering_type = cbs->screen_centering_type;
822     gpu.state.screen_centering_x = cbs->screen_centering_x;
823     gpu.state.screen_centering_y = cbs->screen_centering_y;
824     update_width();
825     update_height();
826   }
827
828   gpu.mmap = cbs->mmap;
829   gpu.munmap = cbs->munmap;
830
831   // delayed vram mmap
832   if (gpu.vram == NULL)
833     map_vram();
834
835   if (cbs->pl_vout_set_raw_vram)
836     cbs->pl_vout_set_raw_vram(gpu.vram);
837   renderer_set_config(cbs);
838   vout_set_config(cbs);
839 }
840
841 // vim:shiftwidth=2:expandtab