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