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