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)));
25 static noinline void do_reset(void)
27 memset(gpu.regs, 0, sizeof(gpu.regs));
28 gpu.status.reg = 0x14802000;
31 gpu.screen.hres = gpu.screen.w = 256;
32 gpu.screen.vres = gpu.screen.h = 240;
35 static noinline void update_width(void)
37 int sw = gpu.screen.x2 - gpu.screen.x1;
38 if (sw <= 0 || sw >= 2560)
40 gpu.screen.w = gpu.screen.hres;
42 gpu.screen.w = sw * gpu.screen.hres / 2560;
45 static noinline void update_height(void)
47 int sh = gpu.screen.y2 - gpu.screen.y1;
48 if (gpu.status.dheight)
56 static noinline void decide_frameskip(void)
58 gpu.frameskip.frame_ready = !gpu.frameskip.active;
60 if (!gpu.frameskip.active && *gpu.frameskip.advice)
61 gpu.frameskip.active = 1;
63 gpu.frameskip.active = 0;
66 static noinline void get_gpu_info(uint32_t data)
68 switch (data & 0x0f) {
73 gpu.gp0 = gpu.ex_regs[data & 7] & 0xfffff;
76 gpu.gp0 = gpu.ex_regs[5] & 0xfffff;
89 int ret = vout_init();
91 gpu.lcf_hc = &gpu.zero;
95 long GPUshutdown(void)
100 void GPUwriteStatus(uint32_t data)
102 static const short hres[8] = { 256, 368, 320, 384, 512, 512, 640, 640 };
103 static const short vres[4] = { 240, 480, 256, 480 };
104 uint32_t cmd = data >> 24;
106 if (cmd < ARRAY_SIZE(gpu.regs)) {
107 if (cmd != 0 && gpu.regs[cmd] == data)
109 gpu.regs[cmd] = data;
112 gpu.state.fb_dirty = 1;
119 gpu.status.blanking = data & 1;
122 gpu.status.dma = data & 3;
125 gpu.screen.x = data & 0x3ff;
126 gpu.screen.y = (data >> 10) & 0x3ff;
127 if (gpu.frameskip.enabled)
131 gpu.screen.x1 = data & 0xfff;
132 gpu.screen.x2 = (data >> 12) & 0xfff;
136 gpu.screen.y1 = data & 0x3ff;
137 gpu.screen.y2 = (data >> 10) & 0x3ff;
141 gpu.status.reg = (gpu.status.reg & ~0x7f0000) | ((data & 0x3F) << 17) | ((data & 0x40) << 10);
142 gpu.screen.hres = hres[(gpu.status.reg >> 16) & 7];
143 gpu.screen.vres = vres[(gpu.status.reg >> 19) & 3];
153 const unsigned char cmd_lengths[256] =
155 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
156 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
157 3, 3, 3, 3, 6, 6, 6, 6, 4, 4, 4, 4, 8, 8, 8, 8, // 20
158 5, 5, 5, 5, 8, 8, 8, 8, 7, 7, 7, 7, 11, 11, 11, 11,
159 2, 2, 2, 2, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, // 40
160 3, 3, 3, 3, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4,
161 2, 2, 2, 2, 3, 3, 3, 3, 1, 1, 1, 1, 2, 2, 2, 2, // 60
162 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 2, 2, 2, 2,
163 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 80
164 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
165 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // a0
166 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
167 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // c0
168 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
169 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // e0
170 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
173 #define VRAM_MEM_XY(x, y) &gpu.vram[(y) * 1024 + (x)]
175 static inline void do_vram_line(int x, int y, uint16_t *mem, int l, int is_read)
177 uint16_t *vram = VRAM_MEM_XY(x, y);
179 memcpy(mem, vram, l * 2);
181 memcpy(vram, mem, l * 2);
184 static int do_vram_io(uint32_t *data, int count, int is_read)
186 int count_initial = count;
187 uint16_t *sdata = (uint16_t *)data;
188 int x = gpu.dma.x, y = gpu.dma.y;
189 int w = gpu.dma.w, h = gpu.dma.h;
190 int o = gpu.dma.offset;
192 count *= 2; // operate in 16bpp pixels
194 if (gpu.dma.offset) {
195 l = w - gpu.dma.offset;
199 do_vram_line(x + o, y, sdata, l, is_read);
212 for (; h > 0 && count >= w; sdata += w, count -= w, y++, h--) {
214 do_vram_line(x, y, sdata, w, is_read);
217 if (h > 0 && count > 0) {
219 do_vram_line(x, y, sdata, count, is_read);
227 return count_initial - count / 2;
230 static void start_vram_transfer(uint32_t pos_word, uint32_t size_word, int is_read)
233 log_anomaly("start_vram_transfer while old unfinished\n");
235 gpu.dma.x = pos_word & 1023;
236 gpu.dma.y = (pos_word >> 16) & 511;
237 gpu.dma.w = size_word & 0xffff; // ?
238 gpu.dma.h = size_word >> 16;
244 log_io("start_vram_transfer %c (%d, %d) %dx%d\n", is_read ? 'r' : 'w',
245 gpu.dma.x, gpu.dma.y, gpu.dma.w, gpu.dma.h);
248 static int check_cmd(uint32_t *data, int count)
250 int len, cmd, start, pos;
254 for (start = pos = 0; pos < count; )
260 pos += do_vram_io(data + pos, count - pos, 0);
266 // do look-ahead pass to detect SR changes and VRAM i/o
267 while (pos < count) {
268 uint32_t *list = data + pos;
270 len = 1 + cmd_lengths[cmd];
272 //printf(" %3d: %02x %d\n", pos, cmd, len);
273 if ((cmd & 0xf4) == 0x24) {
274 // flat textured prim
275 gpu.status.reg &= ~0x1ff;
276 gpu.status.reg |= list[4] & 0x1ff;
278 else if ((cmd & 0xf4) == 0x34) {
279 // shaded textured prim
280 gpu.status.reg &= ~0x1ff;
281 gpu.status.reg |= list[5] & 0x1ff;
286 gpu.status.reg &= ~0x7ff;
287 gpu.status.reg |= list[0] & 0x7ff;
290 gpu.status.reg &= ~0x1800;
291 gpu.status.reg |= (list[0] & 3) << 11;
294 if (2 <= cmd && cmd < 0xc0)
296 else if ((cmd & 0xf8) == 0xe0)
297 gpu.ex_regs[cmd & 7] = list[0];
299 if (pos + len > count) {
301 break; // incomplete cmd
303 if (cmd == 0xa0 || cmd == 0xc0)
308 if (pos - start > 0) {
309 if (!gpu.frameskip.active)
310 do_cmd_list(data + start, pos - start);
314 if (cmd == 0xa0 || cmd == 0xc0) {
315 // consume vram write/read cmd
316 start_vram_transfer(data[pos + 1], data[pos + 2], cmd == 0xc0);
324 gpu.state.fb_dirty |= vram_dirty;
329 static void flush_cmd_buffer(void)
331 int left = check_cmd(gpu.cmd_buffer, gpu.cmd_len);
333 memmove(gpu.cmd_buffer, gpu.cmd_buffer + gpu.cmd_len - left, left * 4);
337 void GPUwriteDataMem(uint32_t *mem, int count)
341 log_io("gpu_dma_write %p %d\n", mem, count);
343 if (unlikely(gpu.cmd_len > 0))
346 left = check_cmd(mem, count);
348 log_anomaly("GPUwriteDataMem: discarded %d/%d words\n", left, count);
351 void GPUwriteData(uint32_t data)
353 log_io("gpu_write %08x\n", data);
354 gpu.cmd_buffer[gpu.cmd_len++] = data;
355 if (gpu.cmd_len >= CMD_BUFFER_LEN)
359 long GPUdmaChain(uint32_t *rambase, uint32_t start_addr)
361 uint32_t addr, *list;
362 int len, left, count;
364 if (unlikely(gpu.cmd_len > 0))
367 log_io("gpu_dma_chain\n");
368 addr = start_addr & 0xffffff;
369 for (count = 0; addr != 0xffffff; count++)
371 log_io(".chain %08x\n", addr);
373 list = rambase + (addr & 0x1fffff) / 4;
375 addr = list[0] & 0xffffff;
377 // loop detection marker
378 // (bit23 set causes DMA error on real machine, so
379 // unlikely to be ever set by the game)
383 left = check_cmd(list + 1, len);
385 log_anomaly("GPUwriteDataMem: discarded %d/%d words\n", left, len);
392 // remove loop detection markers
393 addr = start_addr & 0x1fffff;
394 while (count-- > 0) {
395 list = rambase + addr / 4;
396 addr = list[0] & 0x1fffff;
397 list[0] &= ~0x800000;
403 void GPUreadDataMem(uint32_t *mem, int count)
405 log_io("gpu_dma_read %p %d\n", mem, count);
407 if (unlikely(gpu.cmd_len > 0))
411 do_vram_io(mem, count, 1);
414 uint32_t GPUreadData(void)
416 log_io("gpu_read\n");
418 if (unlikely(gpu.cmd_len > 0))
422 do_vram_io(&gpu.gp0, 1, 1);
427 uint32_t GPUreadStatus(void)
431 if (unlikely(gpu.cmd_len > 0))
434 ret = gpu.status.reg | (*gpu.lcf_hc << 31);
435 log_io("gpu_read_status %08x\n", ret);
439 typedef struct GPUFREEZETAG
441 uint32_t ulFreezeVersion; // should be always 1 for now (set by main emu)
442 uint32_t ulStatus; // current gpu status
443 uint32_t ulControl[256]; // latest control register values
444 unsigned char psxVRam[1024*1024*2]; // current VRam image (full 2 MB for ZN)
447 long GPUfreeze(uint32_t type, GPUFreeze_t *freeze)
455 memcpy(freeze->psxVRam, gpu.vram, sizeof(gpu.vram));
456 memcpy(freeze->ulControl, gpu.regs, sizeof(gpu.regs));
457 memcpy(freeze->ulControl + 0xe0, gpu.ex_regs, sizeof(gpu.ex_regs));
458 freeze->ulStatus = gpu.status.reg;
461 memcpy(gpu.vram, freeze->psxVRam, sizeof(gpu.vram));
462 memcpy(gpu.regs, freeze->ulControl, sizeof(gpu.regs));
463 memcpy(gpu.ex_regs, freeze->ulControl + 0xe0, sizeof(gpu.ex_regs));
464 gpu.status.reg = freeze->ulStatus;
465 for (i = 8; i > 0; i--) {
466 gpu.regs[i] ^= 1; // avoid reg change detection
467 GPUwriteStatus((i << 24) | (gpu.regs[i] ^ 1));
475 void GPUvBlank(int val, uint32_t *hcnt)
477 gpu.lcf_hc = &gpu.zero;
478 if (gpu.status.interlace) {
489 // vim:shiftwidth=2:expandtab