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