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