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)
17 #define noinline __attribute__((noinline))
19 //#define log_io printf
21 #define log_anomaly printf
23 struct psx_gpu gpu __attribute__((aligned(64)));
27 int ret = vout_init();
28 gpu.status.reg = 0x14802000;
29 gpu.status.blanking = 1;
31 gpu.screen.hres = gpu.screen.w = 320;
32 gpu.screen.vres = gpu.screen.h = 240;
33 gpu.lcf_hc = &gpu.zero;
37 long GPUshutdown(void)
42 static noinline void update_width(void)
44 int sw = gpu.screen.x2 - gpu.screen.x1;
45 if (sw <= 0 || sw >= 2560)
47 gpu.screen.w = gpu.screen.hres;
49 gpu.screen.w = sw * gpu.screen.hres / 2560;
52 static noinline void update_height(void)
54 int sh = gpu.screen.y2 - gpu.screen.y1;
55 if (gpu.status.dheight)
63 static noinline void decide_frameskip(void)
65 gpu.frameskip.frame_ready = !gpu.frameskip.active;
67 if (!gpu.frameskip.active && *gpu.frameskip.advice)
68 gpu.frameskip.active = 1;
70 gpu.frameskip.active = 0;
73 void GPUwriteStatus(uint32_t data)
75 static const short hres[8] = { 256, 368, 320, 384, 512, 512, 640, 640 };
76 static const short vres[4] = { 240, 480, 256, 480 };
77 uint32_t cmd = data >> 24;
79 if (cmd < ARRAY_SIZE(gpu.regs)) {
80 if (cmd != 0 && gpu.regs[cmd] == data)
85 gpu.state.fb_dirty = 1;
89 gpu.status.reg = 0x14802000;
90 gpu.status.blanking = 1;
93 gpu.status.blanking = data & 1;
96 gpu.status.dma = data & 3;
99 gpu.screen.x = data & 0x3ff;
100 gpu.screen.y = (data >> 10) & 0x3ff;
101 if (gpu.frameskip.enabled)
105 gpu.screen.x1 = data & 0xfff;
106 gpu.screen.x2 = (data >> 12) & 0xfff;
110 gpu.screen.y1 = data & 0x3ff;
111 gpu.screen.y2 = (data >> 10) & 0x3ff;
115 gpu.status.reg = (gpu.status.reg & ~0x7f0000) | ((data & 0x3F) << 17) | ((data & 0x40) << 10);
116 gpu.screen.hres = hres[(gpu.status.reg >> 16) & 7];
117 gpu.screen.vres = vres[(gpu.status.reg >> 19) & 3];
124 const unsigned char cmd_lengths[256] =
126 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
127 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
128 3, 3, 3, 3, 6, 6, 6, 6, 4, 4, 4, 4, 8, 8, 8, 8, // 20
129 5, 5, 5, 5, 8, 8, 8, 8, 7, 7, 7, 7, 11, 11, 11, 11,
130 2, 2, 2, 2, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, // 40
131 3, 3, 3, 3, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4,
132 2, 2, 2, 2, 3, 3, 3, 3, 1, 1, 1, 1, 2, 2, 2, 2, // 60
133 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 2, 2, 2, 2,
134 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 80
135 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
136 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // a0
137 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
138 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // c0
139 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
140 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // e0
141 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
144 #define VRAM_MEM_XY(x, y) &gpu.vram[(y) * 1024 + (x)]
146 static inline void do_vram_line(int x, int y, uint16_t *mem, int l, int is_read)
148 uint16_t *vram = VRAM_MEM_XY(x, y);
150 memcpy(mem, vram, l * 2);
152 memcpy(vram, mem, l * 2);
155 static int do_vram_io(uint32_t *data, int count, int is_read)
157 int count_initial = count;
158 uint16_t *sdata = (uint16_t *)data;
159 int x = gpu.dma.x, y = gpu.dma.y;
160 int w = gpu.dma.w, h = gpu.dma.h;
161 int o = gpu.dma.offset;
163 count *= 2; // operate in 16bpp pixels
165 if (gpu.dma.offset) {
166 l = w - gpu.dma.offset;
170 do_vram_line(x + o, y, sdata, l, is_read);
183 for (; h > 0 && count >= w; sdata += w, count -= w, y++, h--) {
185 do_vram_line(x, y, sdata, w, is_read);
188 if (h > 0 && count > 0) {
190 do_vram_line(x, y, sdata, count, is_read);
198 return count_initial - (count + 1) / 2;
201 static void start_vram_transfer(uint32_t pos_word, uint32_t size_word, int is_read)
204 log_anomaly("start_vram_transfer while old unfinished\n");
206 gpu.dma.x = pos_word & 1023;
207 gpu.dma.y = (pos_word >> 16) & 511;
208 gpu.dma.w = size_word & 0xffff; // ?
209 gpu.dma.h = size_word >> 16;
215 //printf("start_vram_transfer %c (%d, %d) %dx%d\n", is_read ? 'r' : 'w',
216 // gpu.dma.x, gpu.dma.y, gpu.dma.w, gpu.dma.h);
219 static int check_cmd(uint32_t *data, int count)
221 int len, cmd, start, pos;
225 for (start = pos = 0; pos < count; )
231 pos += do_vram_io(data + pos, count - pos, 0);
237 // do look-ahead pass to detect SR changes and VRAM i/o
238 while (pos < count) {
239 uint32_t *list = data + pos;
241 len = 1 + cmd_lengths[cmd];
243 //printf(" %3d: %02x %d\n", pos, cmd, len);
244 if ((cmd & 0xf4) == 0x24) {
245 // flat textured prim
246 gpu.status.reg &= ~0x1ff;
247 gpu.status.reg |= list[4] & 0x1ff;
249 else if ((cmd & 0xf4) == 0x34) {
250 // shaded textured prim
251 gpu.status.reg &= ~0x1ff;
252 gpu.status.reg |= list[5] & 0x1ff;
257 gpu.status.reg &= ~0x7ff;
258 gpu.status.reg |= list[0] & 0x7ff;
261 gpu.status.reg &= ~0x1800;
262 gpu.status.reg |= (list[0] & 3) << 11;
265 if (2 <= cmd && cmd < 0xc0)
268 if (pos + len > count) {
270 break; // incomplete cmd
272 if (cmd == 0xa0 || cmd == 0xc0)
277 if (pos - start > 0) {
278 if (!gpu.frameskip.active)
279 do_cmd_list(data + start, pos - start);
283 if (cmd == 0xa0 || cmd == 0xc0) {
284 // consume vram write/read cmd
285 start_vram_transfer(data[pos + 1], data[pos + 2], cmd == 0xc0);
293 gpu.state.fb_dirty |= vram_dirty;
298 static void flush_cmd_buffer(void)
300 int left = check_cmd(gpu.cmd_buffer, gpu.cmd_len);
302 memmove(gpu.cmd_buffer, gpu.cmd_buffer + gpu.cmd_len - left, left * 4);
306 void GPUwriteDataMem(uint32_t *mem, int count)
310 log_io("gpu_dma_write %p %d\n", mem, count);
312 if (unlikely(gpu.cmd_len > 0))
315 left = check_cmd(mem, count);
317 log_anomaly("GPUwriteDataMem: discarded %d/%d words\n", left, count);
320 void GPUwriteData(uint32_t data)
322 log_io("gpu_write %08x\n", data);
323 gpu.cmd_buffer[gpu.cmd_len++] = data;
324 if (gpu.cmd_len >= CMD_BUFFER_LEN)
328 long GPUdmaChain(uint32_t *rambase, uint32_t start_addr)
330 uint32_t addr, *list;
331 int len, left, count;
333 if (unlikely(gpu.cmd_len > 0))
336 log_io("gpu_dma_chain\n");
337 addr = start_addr & 0xffffff;
338 for (count = 0; addr != 0xffffff; count++)
340 log_io(".chain %08x\n", addr);
342 list = rambase + (addr & 0x1fffff) / 4;
344 addr = list[0] & 0xffffff;
346 // loop detection marker
347 // (bit23 set causes DMA error on real machine, so
348 // unlikely to be ever set by the game)
352 left = check_cmd(list + 1, len);
354 log_anomaly("GPUwriteDataMem: discarded %d/%d words\n", left, len);
361 // remove loop detection markers
362 addr = start_addr & 0x1fffff;
363 while (count-- > 0) {
364 list = rambase + addr / 4;
365 addr = list[0] & 0x1fffff;
366 list[0] &= ~0x800000;
372 void GPUreadDataMem(uint32_t *mem, int count)
374 log_io("gpu_dma_read %p %d\n", mem, count);
376 if (unlikely(gpu.cmd_len > 0))
380 do_vram_io(mem, count, 1);
383 uint32_t GPUreadData(void)
387 log_io("gpu_read\n");
389 if (unlikely(gpu.cmd_len > 0))
393 do_vram_io(&v, 1, 1);
398 uint32_t GPUreadStatus(void)
402 if (unlikely(gpu.cmd_len > 0))
405 ret = gpu.status.reg | (*gpu.lcf_hc << 31);
406 log_io("gpu_read_status %08x\n", ret);
410 typedef struct GPUFREEZETAG
412 uint32_t ulFreezeVersion; // should be always 1 for now (set by main emu)
413 uint32_t ulStatus; // current gpu status
414 uint32_t ulControl[256]; // latest control register values
415 unsigned char psxVRam[1024*1024*2]; // current VRam image (full 2 MB for ZN)
418 long GPUfreeze(uint32_t type, GPUFreeze_t *freeze)
426 memcpy(freeze->psxVRam, gpu.vram, sizeof(gpu.vram));
427 memcpy(freeze->ulControl, gpu.regs, sizeof(gpu.regs));
428 freeze->ulStatus = gpu.status.reg;
431 memcpy(gpu.vram, freeze->psxVRam, sizeof(gpu.vram));
432 memcpy(gpu.regs, freeze->ulControl, sizeof(gpu.regs));
433 gpu.status.reg = freeze->ulStatus;
434 for (i = 8; i > 0; i--) {
435 gpu.regs[i] ^= 1; // avoid reg change detection
436 GPUwriteStatus((i << 24) | (gpu.regs[i] ^ 1));
444 void GPUvBlank(int val, uint32_t *hcnt)
446 gpu.lcf_hc = &gpu.zero;
447 if (gpu.status.interlace) {
458 // vim:shiftwidth=2:expandtab