gpu_neon: split output code, some refactoring
[pcsx_rearmed.git] / plugins / gpu_neon / gpu.c
... / ...
CommitLineData
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
22struct psx_gpu gpu __attribute__((aligned(64)));
23
24long GPUinit(void)
25{
26 int ret = vout_init();
27 gpu.status.reg = 0x14802000;
28 gpu.lcf_hc = &gpu.zero;
29 return ret;
30}
31
32long GPUshutdown(void)
33{
34 return vout_finish();
35}
36
37void 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
72const 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
94static 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
103static 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
142static 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
157static 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
230static 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
238void 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
252void 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
260long 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
285void 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
296uint32_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
311uint32_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
321typedef 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
329long 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
352void 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