gpulib: new debug compile option for raw fb display
[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       if (gpu.cmd_len + len > ARRAY_SIZE(gpu.cmd_buffer)) {
694         log_anomaly("cmd_buffer overflow, likely garbage commands\n");
695         gpu.cmd_len = 0;
696       }
697       memcpy(gpu.cmd_buffer + gpu.cmd_len, list + 1, len * 4);
698       gpu.cmd_len += len;
699       flush_cmd_buffer();
700       continue;
701     }
702
703     if (len) {
704       left = do_cmd_buffer(list + 1, len);
705       if (left) {
706         memcpy(gpu.cmd_buffer, list + 1 + len - left, left * 4);
707         gpu.cmd_len = left;
708         log_anomaly("GPUdmaChain: %d/%d words left\n", left, len);
709       }
710     }
711
712     if (progress_addr) {
713       *progress_addr = addr;
714       break;
715     }
716     #define LD_THRESHOLD (8*1024)
717     if (count >= LD_THRESHOLD) {
718       if (count == LD_THRESHOLD) {
719         ld_addr = addr;
720         continue;
721       }
722
723       // loop detection marker
724       // (bit23 set causes DMA error on real machine, so
725       //  unlikely to be ever set by the game)
726       list[0] |= HTOLE32(0x800000);
727     }
728   }
729
730   if (ld_addr != 0) {
731     // remove loop detection markers
732     count -= LD_THRESHOLD + 2;
733     addr = ld_addr & 0x1fffff;
734     while (count-- > 0) {
735       list = rambase + addr / 4;
736       addr = LE32TOH(list[0]) & 0x1fffff;
737       list[0] &= HTOLE32(~0x800000);
738     }
739   }
740
741   gpu.state.last_list.frame = *gpu.state.frame_count;
742   gpu.state.last_list.hcnt = *gpu.state.hcnt;
743   gpu.state.last_list.cycles = cpu_cycles;
744   gpu.state.last_list.addr = start_addr;
745
746   return cpu_cycles;
747 }
748
749 void GPUreadDataMem(uint32_t *mem, int count)
750 {
751   log_io("gpu_dma_read  %p %d\n", mem, count);
752
753   if (unlikely(gpu.cmd_len > 0))
754     flush_cmd_buffer();
755
756   if (gpu.dma.h)
757     do_vram_io(mem, count, 1);
758 }
759
760 uint32_t GPUreadData(void)
761 {
762   uint32_t ret;
763
764   if (unlikely(gpu.cmd_len > 0))
765     flush_cmd_buffer();
766
767   ret = gpu.gp0;
768   if (gpu.dma.h) {
769     ret = HTOLE32(ret);
770     do_vram_io(&ret, 1, 1);
771     ret = LE32TOH(ret);
772   }
773
774   log_io("gpu_read %08x\n", ret);
775   return ret;
776 }
777
778 uint32_t GPUreadStatus(void)
779 {
780   uint32_t ret;
781
782   if (unlikely(gpu.cmd_len > 0))
783     flush_cmd_buffer();
784
785   ret = gpu.status;
786   log_io("gpu_read_status %08x\n", ret);
787   return ret;
788 }
789
790 struct GPUFreeze
791 {
792   uint32_t ulFreezeVersion;      // should be always 1 for now (set by main emu)
793   uint32_t ulStatus;             // current gpu status
794   uint32_t ulControl[256];       // latest control register values
795   unsigned char psxVRam[1024*1024*2]; // current VRam image (full 2 MB for ZN)
796 };
797
798 long GPUfreeze(uint32_t type, struct GPUFreeze *freeze)
799 {
800   int i;
801
802   switch (type) {
803     case 1: // save
804       if (gpu.cmd_len > 0)
805         flush_cmd_buffer();
806
807       renderer_sync();
808       memcpy(freeze->psxVRam, gpu.vram, 1024 * 512 * 2);
809       memcpy(freeze->ulControl, gpu.regs, sizeof(gpu.regs));
810       memcpy(freeze->ulControl + 0xe0, gpu.ex_regs, sizeof(gpu.ex_regs));
811       freeze->ulStatus = gpu.status;
812       break;
813     case 0: // load
814       renderer_sync();
815       memcpy(gpu.vram, freeze->psxVRam, 1024 * 512 * 2);
816       memcpy(gpu.regs, freeze->ulControl, sizeof(gpu.regs));
817       memcpy(gpu.ex_regs, freeze->ulControl + 0xe0, sizeof(gpu.ex_regs));
818       gpu.status = freeze->ulStatus;
819       gpu.cmd_len = 0;
820       for (i = 8; i > 0; i--) {
821         gpu.regs[i] ^= 1; // avoid reg change detection
822         GPUwriteStatus((i << 24) | (gpu.regs[i] ^ 1));
823       }
824       renderer_sync_ecmds(gpu.ex_regs);
825       renderer_update_caches(0, 0, 1024, 512, 1);
826       break;
827   }
828
829   return 1;
830 }
831
832 void GPUupdateLace(void)
833 {
834   if (gpu.cmd_len > 0)
835     flush_cmd_buffer();
836   renderer_flush_queues();
837
838 #ifndef RAW_FB_DISPLAY
839   if (gpu.status & PSX_GPU_STATUS_BLANKING) {
840     if (!gpu.state.blanked) {
841       vout_blank();
842       gpu.state.blanked = 1;
843       gpu.state.fb_dirty = 1;
844     }
845     return;
846   }
847
848   renderer_notify_update_lace(0);
849
850   if (!gpu.state.fb_dirty)
851     return;
852 #endif
853
854   if (gpu.frameskip.set) {
855     if (!gpu.frameskip.frame_ready) {
856       if (*gpu.state.frame_count - gpu.frameskip.last_flip_frame < 9)
857         return;
858       gpu.frameskip.active = 0;
859     }
860     gpu.frameskip.frame_ready = 0;
861   }
862
863   vout_update();
864   if (gpu.state.enhancement_active && !gpu.state.enhancement_was_active)
865     renderer_update_caches(0, 0, 1024, 512, 1);
866   gpu.state.enhancement_was_active = gpu.state.enhancement_active;
867   gpu.state.fb_dirty = 0;
868   gpu.state.blanked = 0;
869   renderer_notify_update_lace(1);
870 }
871
872 void GPUvBlank(int is_vblank, int lcf)
873 {
874   int interlace = gpu.state.allow_interlace
875     && (gpu.status & PSX_GPU_STATUS_INTERLACE)
876     && (gpu.status & PSX_GPU_STATUS_DHEIGHT);
877   // interlace doesn't look nice on progressive displays,
878   // so we have this "auto" mode here for games that don't read vram
879   if (gpu.state.allow_interlace == 2
880       && *gpu.state.frame_count - gpu.state.last_vram_read_frame > 1)
881   {
882     interlace = 0;
883   }
884   if (interlace || interlace != gpu.state.old_interlace) {
885     gpu.state.old_interlace = interlace;
886
887     if (gpu.cmd_len > 0)
888       flush_cmd_buffer();
889     renderer_flush_queues();
890     renderer_set_interlace(interlace, !lcf);
891   }
892 }
893
894 #include "../../frontend/plugin_lib.h"
895
896 void GPUrearmedCallbacks(const struct rearmed_cbs *cbs)
897 {
898   gpu.frameskip.set = cbs->frameskip;
899   gpu.frameskip.advice = &cbs->fskip_advice;
900   gpu.frameskip.force = &cbs->fskip_force;
901   gpu.frameskip.dirty = (void *)&cbs->fskip_dirty;
902   gpu.frameskip.active = 0;
903   gpu.frameskip.frame_ready = 1;
904   gpu.state.hcnt = cbs->gpu_hcnt;
905   gpu.state.frame_count = cbs->gpu_frame_count;
906   gpu.state.allow_interlace = cbs->gpu_neon.allow_interlace;
907   gpu.state.enhancement_enable = cbs->gpu_neon.enhancement_enable;
908   if (gpu.state.screen_centering_type != cbs->screen_centering_type
909       || gpu.state.screen_centering_x != cbs->screen_centering_x
910       || gpu.state.screen_centering_y != cbs->screen_centering_y) {
911     gpu.state.screen_centering_type = cbs->screen_centering_type;
912     gpu.state.screen_centering_x = cbs->screen_centering_x;
913     gpu.state.screen_centering_y = cbs->screen_centering_y;
914     update_width();
915     update_height();
916   }
917
918   gpu.mmap = cbs->mmap;
919   gpu.munmap = cbs->munmap;
920
921   // delayed vram mmap
922   if (gpu.vram == NULL)
923     map_vram();
924
925   if (cbs->pl_vout_set_raw_vram)
926     cbs->pl_vout_set_raw_vram(gpu.vram);
927   renderer_set_config(cbs);
928   vout_set_config(cbs);
929 }
930
931 // vim:shiftwidth=2:expandtab