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