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 CMD_BUFFER_LEN 1024
20 static struct __attribute__((aligned(64))) {
21 uint16_t vram[1024 * 512];
22 uint16_t guard[1024 * 512]; // overdraw guard
23 uint32_t cmd_buffer[CMD_BUFFER_LEN];
28 uint32_t tx:4; // 0 texture page
31 uint32_t tp:2; // 7 t.p. mode (4,8,15bpp)
32 uint32_t dtd:1; // 9 dither
34 uint32_t md:1; // 11 set mask bit when drawing
35 uint32_t me:1; // 12 no draw on mask
37 uint32_t width1:1; // 16
39 uint32_t dheight:1; // 19 double height
40 uint32_t video:1; // 20 NTSC,PAL
42 uint32_t interlace:1; // 22 interlace on
43 uint32_t blanking:1; // 23 display not enabled
45 uint32_t busy:1; // 26 !busy drawing
46 uint32_t img:1; // 27 ready to DMA image data
47 uint32_t com:1; // 28 ready for commands
48 uint32_t dma:2; // 29 off, ?, to vram, from vram
61 const uint32_t *lcf_hc;
67 gpu.status.reg = 0x14802000;
71 long GPUshutdown(void)
76 void GPUwriteStatus(uint32_t data)
78 static const short hres[8] = { 256, 368, 320, 384, 512, 512, 640, 640 };
79 static const short vres[4] = { 240, 480, 256, 480 };
80 uint32_t cmd = data >> 24;
84 gpu.status.reg = 0x14802000;
87 gpu.status.blanking = data & 1;
90 gpu.status.dma = data & 3;
93 gpu.screen.x = data & 0x3ff;
94 gpu.screen.y = (data >> 10) & 0x3ff;
97 gpu.screen.y1 = data & 0x3ff;
98 gpu.screen.y2 = (data >> 10) & 0x3ff;
101 gpu.status.reg = (gpu.status.reg & ~0x7f0000) | ((data & 0x3F) << 17) | ((data & 0x40) << 10);
102 gpu.screen.w = hres[(gpu.status.reg >> 16) & 7];
103 gpu.screen.h = vres[(gpu.status.reg >> 19) & 3];
107 if (cmd < ARRAY_SIZE(gpu.regs))
108 gpu.regs[cmd] = data;
111 static const unsigned char cmd_lengths[256] =
113 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
114 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
115 3, 3, 3, 3, 6, 6, 6, 6, 4, 4, 4, 4, 8, 8, 8, 8, // 20
116 5, 5, 5, 5, 8, 8, 8, 8, 7, 7, 7, 7, 11, 11, 11, 11,
117 2, 2, 2, 2, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, // 40
118 3, 3, 3, 3, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4,
119 2, 2, 2, 2, 3, 3, 3, 3, 1, 1, 1, 1, 2, 2, 2, 2, // 60
120 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 2, 2, 2, 2,
121 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 80
122 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
123 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // a0
124 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
125 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // c0
126 0, 0, 0, 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, // e0
128 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
131 void do_cmd(uint32_t *list, int count)
133 uint32_t *list_end = list + count;
135 //printf("do_cmd %p, %d\n", data, count);
137 for (; list < list_end; list += 1 + cmd_lengths[cmd])
143 gpu.status.reg &= ~0x7ff;
144 gpu.status.reg |= list[0] & 0x7ff;
147 gpu.status.reg &= ~0x1800;
148 gpu.status.reg |= (list[0] & 3) << 11;
151 if ((cmd & 0xf4) == 0x24) {
152 // flat textured prim
153 gpu.status.reg &= ~0x1ff;
154 gpu.status.reg |= list[4] & 0x1ff;
156 else if ((cmd & 0xf4) == 0x34) {
157 // shaded textured prim
158 gpu.status.reg &= ~0x1ff;
159 gpu.status.reg |= list[5] & 0x1ff;
164 #define VRAM_MEM_XY(x, y) &gpu.vram[(y) * 1024 + (x)]
166 static inline void do_vram_line(int x, int y, uint16_t *mem, int l, int is_read)
168 uint16_t *vram = VRAM_MEM_XY(x, y);
170 memcpy(mem, vram, l * 2);
172 memcpy(vram, mem, l * 2);
175 static int do_vram_io(uint32_t *data, int count, int is_read)
177 int count_initial = count;
178 uint16_t *sdata = (uint16_t *)data;
179 int x = gpu.dma.x, y = gpu.dma.y;
180 int w = gpu.dma.w, h = gpu.dma.h;
182 count *= 2; // operate in 16bpp pixels
184 if (gpu.dma.offset) {
185 l = w - gpu.dma.offset;
188 do_vram_line(x + gpu.dma.offset, y, sdata, l, is_read);
195 for (; h > 0 && count >= w; sdata += w, count -= w, y++, h--) {
197 do_vram_line(x, y, sdata, w, is_read);
200 if (h > 0 && count > 0) {
202 do_vram_line(x, y, sdata, count, is_read);
203 gpu.dma.offset = count;
211 return count_initial - (count + 1) / 2;
214 static void start_vram_transfer(uint32_t pos_word, uint32_t size_word, int is_read)
216 gpu.dma.x = pos_word & 1023;
217 gpu.dma.y = (pos_word >> 16) & 511;
218 gpu.dma.w = size_word & 0xffff; // ?
219 gpu.dma.h = size_word >> 16;
225 //printf("start_vram_transfer %c (%d, %d) %dx%d\n", is_read ? 'r' : 'w',
226 // gpu.dma.x, gpu.dma.y, gpu.dma.w, gpu.dma.h);
229 static int check_cmd(uint32_t *data, int count)
231 int len, cmd, start, pos;
233 //printf("check_cmd %p, %d\n", data, count);
236 for (start = pos = 0;; )
242 pos += do_vram_io(data + pos, count - pos, 0);
246 while (pos < count) {
247 cmd = data[pos] >> 24;
248 len = 1 + cmd_lengths[cmd];
249 //printf(" %3d: %02x %d\n", pos, cmd, len);
250 if (pos + len > count) {
252 break; // incomplete cmd
254 if (cmd == 0xa0 || cmd == 0xc0)
259 if (pos - start > 0) {
260 do_cmd(data + start, pos - start);
264 if (cmd == 0xa0 || cmd == 0xc0) {
265 // consume vram write/read cmd
266 start_vram_transfer(data[pos + 1], data[pos + 2], cmd == 0xc0);
273 if (pos + len > count) {
274 //printf("discarding %d words\n", pos + len - count);
275 return pos + len - count;
280 static void flush_cmd_buffer(void)
282 int left = check_cmd(gpu.cmd_buffer, gpu.cmd_len);
284 memmove(gpu.cmd_buffer, gpu.cmd_buffer + gpu.cmd_len - left, left * 4);
288 void GPUwriteDataMem(uint32_t *mem, int count)
292 if (unlikely(gpu.cmd_len > 0))
294 left = check_cmd(mem, count);
296 printf("GPUwriteDataMem: discarded %d/%d words\n", left, count);
299 void GPUwriteData(uint32_t data)
301 gpu.cmd_buffer[gpu.cmd_len++] = data;
302 if (gpu.cmd_len >= CMD_BUFFER_LEN)
306 long GPUdmaChain(uint32_t *base, uint32_t addr)
311 if (unlikely(gpu.cmd_len > 0))
314 while (addr != 0xffffff) {
315 list = base + (addr & 0x1fffff) / 4;
317 addr = list[0] & 0xffffff;
319 GPUwriteDataMem(list + 1, len);
325 void GPUreadDataMem(uint32_t *mem, int count)
327 if (unlikely(gpu.cmd_len > 0))
330 do_vram_io(mem, count, 1);
333 uint32_t GPUreadData(void)
336 GPUreadDataMem(&v, 1);
340 uint32_t GPUreadStatus(void)
342 if (unlikely(gpu.cmd_len > 0))
345 return gpu.status.reg | (*gpu.lcf_hc << 31);
348 typedef struct GPUFREEZETAG
350 uint32_t ulFreezeVersion; // should be always 1 for now (set by main emu)
351 uint32_t ulStatus; // current gpu status
352 uint32_t ulControl[256]; // latest control register values
353 unsigned char psxVRam[1024*1024*2]; // current VRam image (full 2 MB for ZN)
356 long GPUfreeze(uint32_t type, GPUFreeze_t *freeze)
362 memcpy(freeze->psxVRam, gpu.vram, sizeof(gpu.vram));
363 memcpy(freeze->ulControl, gpu.regs, sizeof(gpu.regs));
364 freeze->ulStatus = gpu.status.reg;
367 memcpy(gpu.vram, freeze->psxVRam, sizeof(gpu.vram));
368 memcpy(gpu.regs, freeze->ulControl, sizeof(gpu.regs));
369 gpu.status.reg = freeze->ulStatus;
370 GPUwriteStatus((5 << 24) | gpu.regs[5]);
371 GPUwriteStatus((7 << 24) | gpu.regs[7]);
372 GPUwriteStatus((8 << 24) | gpu.regs[8]);
379 void GPUvBlank(int val, uint32_t *hcnt)
381 gpu.lcf_hc = &gpu.zero;
382 if (gpu.status.interlace) {
395 #include "../../frontend/plugin_lib.h"
396 #include "../../frontend/arm_utils.h"
398 static const struct rearmed_cbs *cbs;
399 static void *screen_buf;
401 static void blit(void)
403 static uint32_t old_status, old_h;
404 int x = gpu.screen.x & ~3; // alignment needed by blitter
405 int y = gpu.screen.y;
406 int w = gpu.screen.w;
411 srcs = &gpu.vram[y * 1024 + x];
413 h = gpu.screen.y2 - gpu.screen.y1;
414 if (gpu.status.dheight)
420 if ((gpu.status.reg ^ old_status) & ((7<<16)|(1<<21)) || h != old_h) // width|rgb24 change?
422 old_status = gpu.status.reg;
424 screen_buf = cbs->pl_fbdev_set_mode(w, h, gpu.status.rgb24 ? 24 : 16);
428 if (gpu.status.rgb24)
431 for (; h-- > 0; dest += w * 3, srcs += 1024)
433 bgr888_to_rgb888(dest, srcs, w * 3);
436 for (; h-- > 0; dest += w * 2, srcs += 1024)
438 bgr888_to_rgb565(dest, srcs, w * 3);
444 for (; h-- > 0; dest += w * 2, srcs += 1024)
446 bgr555_to_rgb565(dest, srcs, w * 2);
450 screen_buf = cbs->pl_fbdev_flip();
453 void GPUupdateLace(void)
455 if (!gpu.status.blanking)
461 cbs->pl_fbdev_open();
462 screen_buf = cbs->pl_fbdev_flip();
468 cbs->pl_fbdev_close();
472 void GPUrearmedCallbacks(const struct rearmed_cbs *cbs_)
477 // vim:shiftwidth=2:expandtab