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