gpu_neon: split output code, some refactoring
[pcsx_rearmed.git] / plugins / gpu_neon / gpu.c
CommitLineData
1ab64c54
GI
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
d30279e2 11#include <stdio.h>
1ab64c54 12#include <string.h>
56f08d83 13#include "gpu.h"
1ab64c54
GI
14
15#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
d30279e2 16#define unlikely(x) __builtin_expect((x), 0)
1ab64c54 17
56f08d83 18//#define log_io printf
19#define log_io(...)
20#define log_anomaly printf
21
22struct psx_gpu gpu __attribute__((aligned(64)));
1ab64c54
GI
23
24long GPUinit(void)
25{
56f08d83 26 int ret = vout_init();
d30279e2 27 gpu.status.reg = 0x14802000;
56f08d83 28 gpu.lcf_hc = &gpu.zero;
29 return ret;
1ab64c54
GI
30}
31
32long GPUshutdown(void)
33{
56f08d83 34 return vout_finish();
1ab64c54
GI
35}
36
1ab64c54
GI
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:
d30279e2 45 gpu.status.reg = 0x14802000;
1ab64c54
GI
46 break;
47 case 0x03:
d30279e2 48 gpu.status.blanking = data & 1;
1ab64c54
GI
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
56f08d83 72const unsigned char cmd_lengths[256] =
1ab64c54 73{
d30279e2
GI
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
d30279e2
GI
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)
1ab64c54 95{
d30279e2
GI
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
d30279e2
GI
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) {
56f08d83 173 uint32_t *list = data + pos;
174 cmd = list[0] >> 24;
d30279e2 175 len = 1 + cmd_lengths[cmd];
56f08d83 176
d30279e2 177 //printf(" %3d: %02x %d\n", pos, cmd, len);
56f08d83 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
d30279e2
GI
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) {
56f08d83 210 do_cmd_list(data + start, pos - start);
d30279e2
GI
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
56f08d83 220 if (pos >= count)
d30279e2
GI
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;
1ab64c54
GI
236}
237
238void GPUwriteDataMem(uint32_t *mem, int count)
239{
d30279e2
GI
240 int left;
241
56f08d83 242 log_io("gpu_dma_write %p %d\n", mem, count);
243
d30279e2
GI
244 if (unlikely(gpu.cmd_len > 0))
245 flush_cmd_buffer();
56f08d83 246
d30279e2
GI
247 left = check_cmd(mem, count);
248 if (left)
56f08d83 249 log_anomaly("GPUwriteDataMem: discarded %d/%d words\n", left, count);
1ab64c54
GI
250}
251
d30279e2 252void GPUwriteData(uint32_t data)
1ab64c54 253{
56f08d83 254 log_io("gpu_write %08x\n", data);
d30279e2
GI
255 gpu.cmd_buffer[gpu.cmd_len++] = data;
256 if (gpu.cmd_len >= CMD_BUFFER_LEN)
257 flush_cmd_buffer();
1ab64c54
GI
258}
259
260long GPUdmaChain(uint32_t *base, uint32_t addr)
261{
d30279e2 262 uint32_t *list;
56f08d83 263 int len, left;
d30279e2
GI
264
265 if (unlikely(gpu.cmd_len > 0))
266 flush_cmd_buffer();
267
56f08d83 268 log_io("gpu_dma_chain\n");
d30279e2 269 while (addr != 0xffffff) {
56f08d83 270 log_io(".chain %08x\n", addr);
271
d30279e2
GI
272 list = base + (addr & 0x1fffff) / 4;
273 len = list[0] >> 24;
274 addr = list[0] & 0xffffff;
56f08d83 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 }
d30279e2
GI
280 }
281
1ab64c54
GI
282 return 0;
283}
284
d30279e2
GI
285void GPUreadDataMem(uint32_t *mem, int count)
286{
56f08d83 287 log_io("gpu_dma_read %p %d\n", mem, count);
288
d30279e2
GI
289 if (unlikely(gpu.cmd_len > 0))
290 flush_cmd_buffer();
56f08d83 291
d30279e2
GI
292 if (gpu.dma.h)
293 do_vram_io(mem, count, 1);
294}
295
296uint32_t GPUreadData(void)
297{
298 uint32_t v = 0;
56f08d83 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
d30279e2
GI
308 return v;
309}
310
311uint32_t GPUreadStatus(void)
312{
56f08d83 313 log_io("gpu_read_status\n");
314
d30279e2
GI
315 if (unlikely(gpu.cmd_len > 0))
316 flush_cmd_buffer();
317
318 return gpu.status.reg | (*gpu.lcf_hc << 31);
319}
320
1ab64c54
GI
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
d30279e2
GI
333 if (gpu.cmd_len > 0)
334 flush_cmd_buffer();
1ab64c54
GI
335 memcpy(freeze->psxVRam, gpu.vram, sizeof(gpu.vram));
336 memcpy(freeze->ulControl, gpu.regs, sizeof(gpu.regs));
337 freeze->ulStatus = gpu.status.reg;
1ab64c54
GI
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;
1ab64c54
GI
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
d30279e2 352void GPUvBlank(int val, uint32_t *hcnt)
1ab64c54 353{
d30279e2
GI
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 }
1ab64c54
GI
364}
365
1ab64c54 366// vim:shiftwidth=2:expandtab