Merge pull request #369 from negativeExponent/switch
[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       // consume vram write/read cmd
532       start_vram_transfer(data[pos + 1], data[pos + 2], (cmd & 0xe0) == 0xc0);
533       pos += 3;
534       continue;
535     }
536
537     // 0xex cmds might affect frameskip.allow, so pass to do_cmd_list_skip
538     if (gpu.frameskip.active && (gpu.frameskip.allow || ((data[pos] >> 24) & 0xf0) == 0xe0))
539       pos += do_cmd_list_skip(data + pos, count - pos, &cmd);
540     else {
541       pos += do_cmd_list(data + pos, count - pos, &cmd);
542       vram_dirty = 1;
543     }
544
545     if (cmd == -1)
546       // incomplete cmd
547       break;
548   }
549
550   gpu.status.reg &= ~0x1fff;
551   gpu.status.reg |= gpu.ex_regs[1] & 0x7ff;
552   gpu.status.reg |= (gpu.ex_regs[6] & 3) << 11;
553
554   gpu.state.fb_dirty |= vram_dirty;
555
556   if (old_e3 != gpu.ex_regs[3])
557     decide_frameskip_allow(gpu.ex_regs[3]);
558
559   return count - pos;
560 }
561
562 static void flush_cmd_buffer(void)
563 {
564   int left = do_cmd_buffer(gpu.cmd_buffer, gpu.cmd_len);
565   if (left > 0)
566     memmove(gpu.cmd_buffer, gpu.cmd_buffer + gpu.cmd_len - left, left * 4);
567   gpu.cmd_len = left;
568 }
569
570 void GPUwriteDataMem(uint32_t *mem, int count)
571 {
572   int left;
573
574   log_io("gpu_dma_write %p %d\n", mem, count);
575
576   if (unlikely(gpu.cmd_len > 0))
577     flush_cmd_buffer();
578
579   left = do_cmd_buffer(mem, count);
580   if (left)
581     log_anomaly("GPUwriteDataMem: discarded %d/%d words\n", left, count);
582 }
583
584 void GPUwriteData(uint32_t data)
585 {
586   log_io("gpu_write %08x\n", data);
587   gpu.cmd_buffer[gpu.cmd_len++] = data;
588   if (gpu.cmd_len >= CMD_BUFFER_LEN)
589     flush_cmd_buffer();
590 }
591
592 long GPUdmaChain(uint32_t *rambase, uint32_t start_addr)
593 {
594   uint32_t addr, *list, ld_addr = 0;
595   int len, left, count;
596   long cpu_cycles = 0;
597
598   preload(rambase + (start_addr & 0x1fffff) / 4);
599
600   if (unlikely(gpu.cmd_len > 0))
601     flush_cmd_buffer();
602
603   log_io("gpu_dma_chain\n");
604   addr = start_addr & 0xffffff;
605   for (count = 0; (addr & 0x800000) == 0; count++)
606   {
607     list = rambase + (addr & 0x1fffff) / 4;
608     len = list[0] >> 24;
609     addr = list[0] & 0xffffff;
610     preload(rambase + (addr & 0x1fffff) / 4);
611
612     cpu_cycles += 10;
613     if (len > 0)
614       cpu_cycles += 5 + len;
615
616     log_io(".chain %08x #%d\n", (list - rambase) * 4, len);
617
618     if (len) {
619       left = do_cmd_buffer(list + 1, len);
620       if (left)
621         log_anomaly("GPUdmaChain: discarded %d/%d words\n", left, len);
622     }
623
624     #define LD_THRESHOLD (8*1024)
625     if (count >= LD_THRESHOLD) {
626       if (count == LD_THRESHOLD) {
627         ld_addr = addr;
628         continue;
629       }
630
631       // loop detection marker
632       // (bit23 set causes DMA error on real machine, so
633       //  unlikely to be ever set by the game)
634       list[0] |= 0x800000;
635     }
636   }
637
638   if (ld_addr != 0) {
639     // remove loop detection markers
640     count -= LD_THRESHOLD + 2;
641     addr = ld_addr & 0x1fffff;
642     while (count-- > 0) {
643       list = rambase + addr / 4;
644       addr = list[0] & 0x1fffff;
645       list[0] &= ~0x800000;
646     }
647   }
648
649   gpu.state.last_list.frame = *gpu.state.frame_count;
650   gpu.state.last_list.hcnt = *gpu.state.hcnt;
651   gpu.state.last_list.cycles = cpu_cycles;
652   gpu.state.last_list.addr = start_addr;
653
654   return cpu_cycles;
655 }
656
657 void GPUreadDataMem(uint32_t *mem, int count)
658 {
659   log_io("gpu_dma_read  %p %d\n", mem, count);
660
661   if (unlikely(gpu.cmd_len > 0))
662     flush_cmd_buffer();
663
664   if (gpu.dma.h)
665     do_vram_io(mem, count, 1);
666 }
667
668 uint32_t GPUreadData(void)
669 {
670   uint32_t ret;
671
672   if (unlikely(gpu.cmd_len > 0))
673     flush_cmd_buffer();
674
675   ret = gpu.gp0;
676   if (gpu.dma.h)
677     do_vram_io(&ret, 1, 1);
678
679   log_io("gpu_read %08x\n", ret);
680   return ret;
681 }
682
683 uint32_t GPUreadStatus(void)
684 {
685   uint32_t ret;
686
687   if (unlikely(gpu.cmd_len > 0))
688     flush_cmd_buffer();
689
690   ret = gpu.status.reg;
691   log_io("gpu_read_status %08x\n", ret);
692   return ret;
693 }
694
695 struct GPUFreeze
696 {
697   uint32_t ulFreezeVersion;      // should be always 1 for now (set by main emu)
698   uint32_t ulStatus;             // current gpu status
699   uint32_t ulControl[256];       // latest control register values
700   unsigned char psxVRam[1024*1024*2]; // current VRam image (full 2 MB for ZN)
701 };
702
703 long GPUfreeze(uint32_t type, struct GPUFreeze *freeze)
704 {
705   int i;
706
707   switch (type) {
708     case 1: // save
709       if (gpu.cmd_len > 0)
710         flush_cmd_buffer();
711       memcpy(freeze->psxVRam, gpu.vram, 1024 * 512 * 2);
712       memcpy(freeze->ulControl, gpu.regs, sizeof(gpu.regs));
713       memcpy(freeze->ulControl + 0xe0, gpu.ex_regs, sizeof(gpu.ex_regs));
714       freeze->ulStatus = gpu.status.reg;
715       break;
716     case 0: // load
717       memcpy(gpu.vram, freeze->psxVRam, 1024 * 512 * 2);
718       memcpy(gpu.regs, freeze->ulControl, sizeof(gpu.regs));
719       memcpy(gpu.ex_regs, freeze->ulControl + 0xe0, sizeof(gpu.ex_regs));
720       gpu.status.reg = freeze->ulStatus;
721       gpu.cmd_len = 0;
722       for (i = 8; i > 0; i--) {
723         gpu.regs[i] ^= 1; // avoid reg change detection
724         GPUwriteStatus((i << 24) | (gpu.regs[i] ^ 1));
725       }
726       renderer_sync_ecmds(gpu.ex_regs);
727       renderer_update_caches(0, 0, 1024, 512);
728       break;
729   }
730
731   return 1;
732 }
733
734 void GPUupdateLace(void)
735 {
736   if (gpu.cmd_len > 0)
737     flush_cmd_buffer();
738   renderer_flush_queues();
739
740   if (gpu.status.blanking) {
741     if (!gpu.state.blanked) {
742       vout_blank();
743       gpu.state.blanked = 1;
744       gpu.state.fb_dirty = 1;
745     }
746     return;
747   }
748
749   if (!gpu.state.fb_dirty)
750     return;
751
752   if (gpu.frameskip.set) {
753     if (!gpu.frameskip.frame_ready) {
754       if (*gpu.state.frame_count - gpu.frameskip.last_flip_frame < 9)
755         return;
756       gpu.frameskip.active = 0;
757     }
758     gpu.frameskip.frame_ready = 0;
759   }
760
761   vout_update();
762   gpu.state.fb_dirty = 0;
763   gpu.state.blanked = 0;
764 }
765
766 void GPUvBlank(int is_vblank, int lcf)
767 {
768   int interlace = gpu.state.allow_interlace
769     && gpu.status.interlace && gpu.status.dheight;
770   // interlace doesn't look nice on progressive displays,
771   // so we have this "auto" mode here for games that don't read vram
772   if (gpu.state.allow_interlace == 2
773       && *gpu.state.frame_count - gpu.state.last_vram_read_frame > 1)
774   {
775     interlace = 0;
776   }
777   if (interlace || interlace != gpu.state.old_interlace) {
778     gpu.state.old_interlace = interlace;
779
780     if (gpu.cmd_len > 0)
781       flush_cmd_buffer();
782     renderer_flush_queues();
783     renderer_set_interlace(interlace, !lcf);
784   }
785 }
786
787 #include "../../frontend/plugin_lib.h"
788
789 void GPUrearmedCallbacks(const struct rearmed_cbs *cbs)
790 {
791   gpu.frameskip.set = cbs->frameskip;
792   gpu.frameskip.advice = &cbs->fskip_advice;
793   gpu.frameskip.active = 0;
794   gpu.frameskip.frame_ready = 1;
795   gpu.state.hcnt = cbs->gpu_hcnt;
796   gpu.state.frame_count = cbs->gpu_frame_count;
797   gpu.state.allow_interlace = cbs->gpu_neon.allow_interlace;
798   gpu.state.enhancement_enable = cbs->gpu_neon.enhancement_enable;
799
800   gpu.useDithering = cbs->gpu_neon.allow_dithering;
801   gpu.mmap = cbs->mmap;
802   gpu.munmap = cbs->munmap;
803
804   // delayed vram mmap
805   if (gpu.vram == NULL)
806     map_vram();
807
808   if (cbs->pl_vout_set_raw_vram)
809     cbs->pl_vout_set_raw_vram(gpu.vram);
810   renderer_set_config(cbs);
811   vout_set_config(cbs);
812 }
813
814 // vim:shiftwidth=2:expandtab