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