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