2 * (C) GraÅžvydas "notaz" Ignotas, 2011
4 * This work is licensed under the terms of any of these licenses
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.
15 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
16 #define unlikely(x) __builtin_expect((x), 0)
18 //#define log_io printf
20 #define log_anomaly printf
22 struct psx_gpu gpu __attribute__((aligned(64)));
26 int ret = vout_init();
27 gpu.status.reg = 0x14802000;
28 gpu.lcf_hc = &gpu.zero;
32 long GPUshutdown(void)
37 void GPUwriteStatus(uint32_t data)
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;
45 gpu.status.reg = 0x14802000;
48 gpu.status.blanking = data & 1;
51 gpu.status.dma = data & 3;
54 gpu.screen.x = data & 0x3ff;
55 gpu.screen.y = (data >> 10) & 0x3ff;
58 gpu.screen.y1 = data & 0x3ff;
59 gpu.screen.y2 = (data >> 10) & 0x3ff;
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];
68 if (cmd < ARRAY_SIZE(gpu.regs))
72 const unsigned char cmd_lengths[256] =
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
92 #define VRAM_MEM_XY(x, y) &gpu.vram[(y) * 1024 + (x)]
94 static inline void do_vram_line(int x, int y, uint16_t *mem, int l, int is_read)
96 uint16_t *vram = VRAM_MEM_XY(x, y);
98 memcpy(mem, vram, l * 2);
100 memcpy(vram, mem, l * 2);
103 static int do_vram_io(uint32_t *data, int count, int is_read)
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;
110 count *= 2; // operate in 16bpp pixels
112 if (gpu.dma.offset) {
113 l = w - gpu.dma.offset;
116 do_vram_line(x + gpu.dma.offset, y, sdata, l, is_read);
123 for (; h > 0 && count >= w; sdata += w, count -= w, y++, h--) {
125 do_vram_line(x, y, sdata, w, is_read);
128 if (h > 0 && count > 0) {
130 do_vram_line(x, y, sdata, count, is_read);
131 gpu.dma.offset = count;
139 return count_initial - (count + 1) / 2;
142 static void start_vram_transfer(uint32_t pos_word, uint32_t size_word, int is_read)
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;
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);
157 static int check_cmd(uint32_t *data, int count)
159 int len, cmd, start, pos;
162 for (start = pos = 0;; )
168 pos += do_vram_io(data + pos, count - pos, 0);
172 while (pos < count) {
173 uint32_t *list = data + pos;
175 len = 1 + cmd_lengths[cmd];
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;
183 else if ((cmd & 0xf4) == 0x34) {
184 // shaded textured prim
185 gpu.status.reg &= ~0x1ff;
186 gpu.status.reg |= list[5] & 0x1ff;
191 gpu.status.reg &= ~0x7ff;
192 gpu.status.reg |= list[0] & 0x7ff;
195 gpu.status.reg &= ~0x1800;
196 gpu.status.reg |= (list[0] & 3) << 11;
200 if (pos + len > count) {
202 break; // incomplete cmd
204 if (cmd == 0xa0 || cmd == 0xc0)
209 if (pos - start > 0) {
210 do_cmd_list(data + start, pos - start);
214 if (cmd == 0xa0 || cmd == 0xc0) {
215 // consume vram write/read cmd
216 start_vram_transfer(data[pos + 1], data[pos + 2], cmd == 0xc0);
223 if (pos + len > count) {
224 //printf("discarding %d words\n", pos + len - count);
225 return pos + len - count;
230 static void flush_cmd_buffer(void)
232 int left = check_cmd(gpu.cmd_buffer, gpu.cmd_len);
234 memmove(gpu.cmd_buffer, gpu.cmd_buffer + gpu.cmd_len - left, left * 4);
238 void GPUwriteDataMem(uint32_t *mem, int count)
242 log_io("gpu_dma_write %p %d\n", mem, count);
244 if (unlikely(gpu.cmd_len > 0))
247 left = check_cmd(mem, count);
249 log_anomaly("GPUwriteDataMem: discarded %d/%d words\n", left, count);
252 void GPUwriteData(uint32_t data)
254 log_io("gpu_write %08x\n", data);
255 gpu.cmd_buffer[gpu.cmd_len++] = data;
256 if (gpu.cmd_len >= CMD_BUFFER_LEN)
260 long GPUdmaChain(uint32_t *base, uint32_t addr)
265 if (unlikely(gpu.cmd_len > 0))
268 log_io("gpu_dma_chain\n");
269 while (addr != 0xffffff) {
270 log_io(".chain %08x\n", addr);
272 list = base + (addr & 0x1fffff) / 4;
274 addr = list[0] & 0xffffff;
276 left = check_cmd(list + 1, len);
278 log_anomaly("GPUwriteDataMem: discarded %d/%d words\n", left, len);
285 void GPUreadDataMem(uint32_t *mem, int count)
287 log_io("gpu_dma_read %p %d\n", mem, count);
289 if (unlikely(gpu.cmd_len > 0))
293 do_vram_io(mem, count, 1);
296 uint32_t GPUreadData(void)
300 log_io("gpu_read\n");
302 if (unlikely(gpu.cmd_len > 0))
306 do_vram_io(&v, 1, 1);
311 uint32_t GPUreadStatus(void)
313 log_io("gpu_read_status\n");
315 if (unlikely(gpu.cmd_len > 0))
318 return gpu.status.reg | (*gpu.lcf_hc << 31);
321 typedef struct GPUFREEZETAG
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)
329 long GPUfreeze(uint32_t type, GPUFreeze_t *freeze)
335 memcpy(freeze->psxVRam, gpu.vram, sizeof(gpu.vram));
336 memcpy(freeze->ulControl, gpu.regs, sizeof(gpu.regs));
337 freeze->ulStatus = gpu.status.reg;
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]);
352 void GPUvBlank(int val, uint32_t *hcnt)
354 gpu.lcf_hc = &gpu.zero;
355 if (gpu.status.interlace) {
366 // vim:shiftwidth=2:expandtab