da6317ecd7e9cd0a9095a1e23693dbf1b09b7176
[pcsx_rearmed.git] / plugins / gpu_neon / gpu.c
1 /*
2  * (C) GraÅžvydas "notaz" Ignotas, 2011
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 "gpu.h"
14
15 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
16 #define unlikely(x) __builtin_expect((x), 0)
17
18 //#define log_io printf
19 #define log_io(...)
20 #define log_anomaly printf
21
22 struct psx_gpu gpu __attribute__((aligned(64)));
23
24 long GPUinit(void)
25 {
26   int ret = vout_init();
27   gpu.status.reg = 0x14802000;
28   gpu.lcf_hc = &gpu.zero;
29   return ret;
30 }
31
32 long GPUshutdown(void)
33 {
34   return vout_finish();
35 }
36
37 void GPUwriteStatus(uint32_t data)
38 {
39   static const short hres[8] = { 256, 368, 320, 384, 512, 512, 640, 640 };
40   static const short vres[4] = { 240, 480, 256, 480 };
41   uint32_t cmd = data >> 24;
42
43   switch (data >> 24) {
44     case 0x00:
45       gpu.status.reg = 0x14802000;
46       break;
47     case 0x03:
48       gpu.status.blanking = data & 1;
49       break;
50     case 0x04:
51       gpu.status.dma = data & 3;
52       break;
53     case 0x05:
54       gpu.screen.x = data & 0x3ff;
55       gpu.screen.y = (data >> 10) & 0x3ff;
56       break;
57     case 0x07:
58       gpu.screen.y1 = data & 0x3ff;
59       gpu.screen.y2 = (data >> 10) & 0x3ff;
60       break;
61     case 0x08:
62       gpu.status.reg = (gpu.status.reg & ~0x7f0000) | ((data & 0x3F) << 17) | ((data & 0x40) << 10);
63       gpu.screen.w = hres[(gpu.status.reg >> 16) & 7];
64       gpu.screen.h = vres[(gpu.status.reg >> 19) & 3];
65       break;
66   }
67
68   if (cmd < ARRAY_SIZE(gpu.regs))
69     gpu.regs[cmd] = data;
70 }
71
72 const unsigned char cmd_lengths[256] =
73 {
74         0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
75         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
76         3, 3, 3, 3, 6, 6, 6, 6, 4, 4, 4, 4, 8, 8, 8, 8, // 20
77         5, 5, 5, 5, 8, 8, 8, 8, 7, 7, 7, 7, 11, 11, 11, 11,
78         2, 2, 2, 2, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, // 40
79         3, 3, 3, 3, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4,
80         2, 2, 2, 2, 3, 3, 3, 3, 1, 1, 1, 1, 2, 2, 2, 2, // 60
81         1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 2, 2, 2, 2,
82         3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 80
83         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
84         2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // a0
85         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
86         2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // c0
87         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
88         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // e0
89         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
90 };
91
92 #define VRAM_MEM_XY(x, y) &gpu.vram[(y) * 1024 + (x)]
93
94 static inline void do_vram_line(int x, int y, uint16_t *mem, int l, int is_read)
95 {
96   uint16_t *vram = VRAM_MEM_XY(x, y);
97   if (is_read)
98     memcpy(mem, vram, l * 2);
99   else
100     memcpy(vram, mem, l * 2);
101 }
102
103 static int do_vram_io(uint32_t *data, int count, int is_read)
104 {
105   int count_initial = count;
106   uint16_t *sdata = (uint16_t *)data;
107   int x = gpu.dma.x, y = gpu.dma.y;
108   int w = gpu.dma.w, h = gpu.dma.h;
109   int l;
110   count *= 2; // operate in 16bpp pixels
111
112   if (gpu.dma.offset) {
113     l = w - gpu.dma.offset;
114     if (l > count)
115       l = count;
116     do_vram_line(x + gpu.dma.offset, y, sdata, l, is_read);
117     sdata += l;
118     count -= l;
119     y++;
120     h--;
121   }
122
123   for (; h > 0 && count >= w; sdata += w, count -= w, y++, h--) {
124     y &= 511;
125     do_vram_line(x, y, sdata, w, is_read);
126   }
127
128   if (h > 0 && count > 0) {
129     y &= 511;
130     do_vram_line(x, y, sdata, count, is_read);
131     gpu.dma.offset = count;
132     count = 0;
133   }
134   else
135     gpu.dma.offset = 0;
136   gpu.dma.y = y;
137   gpu.dma.h = h;
138
139   return count_initial - (count + 1) / 2;
140 }
141
142 static void start_vram_transfer(uint32_t pos_word, uint32_t size_word, int is_read)
143 {
144   gpu.dma.x = pos_word & 1023;
145   gpu.dma.y = (pos_word >> 16) & 511;
146   gpu.dma.w = size_word & 0xffff; // ?
147   gpu.dma.h = size_word >> 16;
148   gpu.dma.offset = 0;
149
150   if (is_read)
151     gpu.status.img = 1;
152
153   //printf("start_vram_transfer %c (%d, %d) %dx%d\n", is_read ? 'r' : 'w',
154   //  gpu.dma.x, gpu.dma.y, gpu.dma.w, gpu.dma.h);
155 }
156
157 static int check_cmd(uint32_t *data, int count)
158 {
159   int len, cmd, start, pos;
160
161   // process buffer
162   for (start = pos = 0;; )
163   {
164     cmd = -1;
165     len = 0;
166
167     if (gpu.dma.h) {
168       pos += do_vram_io(data + pos, count - pos, 0);
169       start = pos;
170     }
171
172     while (pos < count) {
173       uint32_t *list = data + pos;
174       cmd = list[0] >> 24;
175       len = 1 + cmd_lengths[cmd];
176
177       //printf("  %3d: %02x %d\n", pos, cmd, len);
178       if ((cmd & 0xf4) == 0x24) {
179         // flat textured prim
180         gpu.status.reg &= ~0x1ff;
181         gpu.status.reg |= list[4] & 0x1ff;
182       }
183       else if ((cmd & 0xf4) == 0x34) {
184         // shaded textured prim
185         gpu.status.reg &= ~0x1ff;
186         gpu.status.reg |= list[5] & 0x1ff;
187       }
188       else switch (cmd)
189       {
190         case 0xe1:
191           gpu.status.reg &= ~0x7ff;
192           gpu.status.reg |= list[0] & 0x7ff;
193           break;
194         case 0xe6:
195           gpu.status.reg &= ~0x1800;
196           gpu.status.reg |= (list[0] & 3) << 11;
197           break;
198       }
199
200       if (pos + len > count) {
201         cmd = -1;
202         break; // incomplete cmd
203       }
204       if (cmd == 0xa0 || cmd == 0xc0)
205         break; // image i/o
206       pos += len;
207     }
208
209     if (pos - start > 0) {
210       do_cmd_list(data + start, pos - start);
211       start = pos;
212     }
213
214     if (cmd == 0xa0 || cmd == 0xc0) {
215       // consume vram write/read cmd
216       start_vram_transfer(data[pos + 1], data[pos + 2], cmd == 0xc0);
217       pos += len;
218     }
219
220     if (pos >= count)
221       return 0;
222
223     if (pos + len > count) {
224       //printf("discarding %d words\n", pos + len - count);
225       return pos + len - count;
226     }
227   }
228 }
229
230 static void flush_cmd_buffer(void)
231 {
232   int left = check_cmd(gpu.cmd_buffer, gpu.cmd_len);
233   if (left > 0)
234     memmove(gpu.cmd_buffer, gpu.cmd_buffer + gpu.cmd_len - left, left * 4);
235   gpu.cmd_len = left;
236 }
237
238 void GPUwriteDataMem(uint32_t *mem, int count)
239 {
240   int left;
241
242   log_io("gpu_dma_write %p %d\n", mem, count);
243
244   if (unlikely(gpu.cmd_len > 0))
245     flush_cmd_buffer();
246
247   left = check_cmd(mem, count);
248   if (left)
249     log_anomaly("GPUwriteDataMem: discarded %d/%d words\n", left, count);
250 }
251
252 void GPUwriteData(uint32_t data)
253 {
254   log_io("gpu_write %08x\n", data);
255   gpu.cmd_buffer[gpu.cmd_len++] = data;
256   if (gpu.cmd_len >= CMD_BUFFER_LEN)
257     flush_cmd_buffer();
258 }
259
260 long GPUdmaChain(uint32_t *base, uint32_t addr)
261 {
262   uint32_t *list;
263   int len, left;
264
265   if (unlikely(gpu.cmd_len > 0))
266     flush_cmd_buffer();
267
268   log_io("gpu_dma_chain\n");
269   while (addr != 0xffffff) {
270     log_io(".chain %08x\n", addr);
271
272     list = base + (addr & 0x1fffff) / 4;
273     len = list[0] >> 24;
274     addr = list[0] & 0xffffff;
275     if (len) {
276       left = check_cmd(list + 1, len);
277       if (left)
278         log_anomaly("GPUwriteDataMem: discarded %d/%d words\n", left, len);
279     }
280   }
281
282   return 0;
283 }
284
285 void GPUreadDataMem(uint32_t *mem, int count)
286 {
287   log_io("gpu_dma_read  %p %d\n", mem, count);
288
289   if (unlikely(gpu.cmd_len > 0))
290     flush_cmd_buffer();
291
292   if (gpu.dma.h)
293     do_vram_io(mem, count, 1);
294 }
295
296 uint32_t GPUreadData(void)
297 {
298   uint32_t v = 0;
299
300   log_io("gpu_read\n");
301
302   if (unlikely(gpu.cmd_len > 0))
303     flush_cmd_buffer();
304
305   if (gpu.dma.h)
306     do_vram_io(&v, 1, 1);
307
308   return v;
309 }
310
311 uint32_t GPUreadStatus(void)
312 {
313   log_io("gpu_read_status\n");
314
315   if (unlikely(gpu.cmd_len > 0))
316     flush_cmd_buffer();
317
318   return gpu.status.reg | (*gpu.lcf_hc << 31);
319 }
320
321 typedef struct GPUFREEZETAG
322 {
323   uint32_t ulFreezeVersion;      // should be always 1 for now (set by main emu)
324   uint32_t ulStatus;             // current gpu status
325   uint32_t ulControl[256];       // latest control register values
326   unsigned char psxVRam[1024*1024*2]; // current VRam image (full 2 MB for ZN)
327 } GPUFreeze_t;
328
329 long GPUfreeze(uint32_t type, GPUFreeze_t *freeze)
330 {
331   switch (type) {
332     case 1: // save
333       if (gpu.cmd_len > 0)
334         flush_cmd_buffer();
335       memcpy(freeze->psxVRam, gpu.vram, sizeof(gpu.vram));
336       memcpy(freeze->ulControl, gpu.regs, sizeof(gpu.regs));
337       freeze->ulStatus = gpu.status.reg;
338       break;
339     case 0: // load
340       memcpy(gpu.vram, freeze->psxVRam, sizeof(gpu.vram));
341       memcpy(gpu.regs, freeze->ulControl, sizeof(gpu.regs));
342       gpu.status.reg = freeze->ulStatus;
343       GPUwriteStatus((5 << 24) | gpu.regs[5]);
344       GPUwriteStatus((7 << 24) | gpu.regs[7]);
345       GPUwriteStatus((8 << 24) | gpu.regs[8]);
346       break;
347   }
348
349   return 1;
350 }
351
352 void GPUvBlank(int val, uint32_t *hcnt)
353 {
354   gpu.lcf_hc = &gpu.zero;
355   if (gpu.status.interlace) {
356     if (val)
357       gpu.status.lcf ^= 1;
358   }
359   else {
360     gpu.status.lcf = 0;
361     if (!val)
362       gpu.lcf_hc = hcnt;
363   }
364 }
365
366 // vim:shiftwidth=2:expandtab