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;
109 int o = gpu.dma.offset;
111 count *= 2; // operate in 16bpp pixels
113 if (gpu.dma.offset) {
114 l = w - gpu.dma.offset;
118 do_vram_line(x + o, y, sdata, l, is_read);
131 for (; h > 0 && count >= w; sdata += w, count -= w, y++, h--) {
133 do_vram_line(x, y, sdata, w, is_read);
136 if (h > 0 && count > 0) {
138 do_vram_line(x, y, sdata, count, is_read);
146 return count_initial - (count + 1) / 2;
149 static void start_vram_transfer(uint32_t pos_word, uint32_t size_word, int is_read)
152 log_anomaly("start_vram_transfer while old unfinished\n");
154 gpu.dma.x = pos_word & 1023;
155 gpu.dma.y = (pos_word >> 16) & 511;
156 gpu.dma.w = size_word & 0xffff; // ?
157 gpu.dma.h = size_word >> 16;
163 //printf("start_vram_transfer %c (%d, %d) %dx%d\n", is_read ? 'r' : 'w',
164 // gpu.dma.x, gpu.dma.y, gpu.dma.w, gpu.dma.h);
167 static int check_cmd(uint32_t *data, int count)
169 int len, cmd, start, pos;
172 for (start = pos = 0; pos < count; )
178 pos += do_vram_io(data + pos, count - pos, 0);
184 // do look-ahead pass to detect SR changes and VRAM i/o
185 while (pos < count) {
186 uint32_t *list = data + pos;
188 len = 1 + cmd_lengths[cmd];
190 //printf(" %3d: %02x %d\n", pos, cmd, len);
191 if ((cmd & 0xf4) == 0x24) {
192 // flat textured prim
193 gpu.status.reg &= ~0x1ff;
194 gpu.status.reg |= list[4] & 0x1ff;
196 else if ((cmd & 0xf4) == 0x34) {
197 // shaded textured prim
198 gpu.status.reg &= ~0x1ff;
199 gpu.status.reg |= list[5] & 0x1ff;
204 gpu.status.reg &= ~0x7ff;
205 gpu.status.reg |= list[0] & 0x7ff;
208 gpu.status.reg &= ~0x1800;
209 gpu.status.reg |= (list[0] & 3) << 11;
213 if (pos + len > count) {
215 break; // incomplete cmd
217 if (cmd == 0xa0 || cmd == 0xc0)
222 if (pos - start > 0) {
223 do_cmd_list(data + start, pos - start);
227 if (cmd == 0xa0 || cmd == 0xc0) {
228 // consume vram write/read cmd
229 start_vram_transfer(data[pos + 1], data[pos + 2], cmd == 0xc0);
240 static void flush_cmd_buffer(void)
242 int left = check_cmd(gpu.cmd_buffer, gpu.cmd_len);
244 memmove(gpu.cmd_buffer, gpu.cmd_buffer + gpu.cmd_len - left, left * 4);
248 void GPUwriteDataMem(uint32_t *mem, int count)
252 log_io("gpu_dma_write %p %d\n", mem, count);
254 if (unlikely(gpu.cmd_len > 0))
257 left = check_cmd(mem, count);
259 log_anomaly("GPUwriteDataMem: discarded %d/%d words\n", left, count);
262 void GPUwriteData(uint32_t data)
264 log_io("gpu_write %08x\n", data);
265 gpu.cmd_buffer[gpu.cmd_len++] = data;
266 if (gpu.cmd_len >= CMD_BUFFER_LEN)
270 long GPUdmaChain(uint32_t *rambase, uint32_t start_addr)
272 uint32_t addr, *list;
273 int len, left, count;
275 if (unlikely(gpu.cmd_len > 0))
278 log_io("gpu_dma_chain\n");
279 addr = start_addr & 0xffffff;
280 for (count = 0; addr != 0xffffff; count++)
282 log_io(".chain %08x\n", addr);
284 list = rambase + (addr & 0x1fffff) / 4;
286 addr = list[0] & 0xffffff;
288 // loop detection marker
289 // (bit23 set causes DMA error on real machine, so
290 // unlikely to be ever set by the game)
294 left = check_cmd(list + 1, len);
296 log_anomaly("GPUwriteDataMem: discarded %d/%d words\n", left, len);
303 // remove loop detection markers
304 addr = start_addr & 0x1fffff;
305 while (count-- > 0) {
306 list = rambase + addr / 4;
307 addr = list[0] & 0x1fffff;
308 list[0] &= ~0x800000;
314 void GPUreadDataMem(uint32_t *mem, int count)
316 log_io("gpu_dma_read %p %d\n", mem, count);
318 if (unlikely(gpu.cmd_len > 0))
322 do_vram_io(mem, count, 1);
325 uint32_t GPUreadData(void)
329 log_io("gpu_read\n");
331 if (unlikely(gpu.cmd_len > 0))
335 do_vram_io(&v, 1, 1);
340 uint32_t GPUreadStatus(void)
344 if (unlikely(gpu.cmd_len > 0))
347 ret = gpu.status.reg | (*gpu.lcf_hc << 31);
348 log_io("gpu_read_status %08x\n", ret);
352 typedef struct GPUFREEZETAG
354 uint32_t ulFreezeVersion; // should be always 1 for now (set by main emu)
355 uint32_t ulStatus; // current gpu status
356 uint32_t ulControl[256]; // latest control register values
357 unsigned char psxVRam[1024*1024*2]; // current VRam image (full 2 MB for ZN)
360 long GPUfreeze(uint32_t type, GPUFreeze_t *freeze)
366 memcpy(freeze->psxVRam, gpu.vram, sizeof(gpu.vram));
367 memcpy(freeze->ulControl, gpu.regs, sizeof(gpu.regs));
368 freeze->ulStatus = gpu.status.reg;
371 memcpy(gpu.vram, freeze->psxVRam, sizeof(gpu.vram));
372 memcpy(gpu.regs, freeze->ulControl, sizeof(gpu.regs));
373 gpu.status.reg = freeze->ulStatus;
374 GPUwriteStatus((5 << 24) | gpu.regs[5]);
375 GPUwriteStatus((7 << 24) | gpu.regs[7]);
376 GPUwriteStatus((8 << 24) | gpu.regs[8]);
383 void GPUvBlank(int val, uint32_t *hcnt)
385 gpu.lcf_hc = &gpu.zero;
386 if (gpu.status.interlace) {
397 // vim:shiftwidth=2:expandtab