From: notaz Date: Sun, 14 Mar 2010 23:08:53 +0000 (+0200) Subject: add preliminary blitter; fb, input handling X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3d295a9fe6e27f6103701e8d8ae23257b43ff2dd;p=ginge.git add preliminary blitter; fb, input handling somewhat playable doukutsu on pnd (gfx glitches due to unfinished blitter). --- diff --git a/loader/.gitignore b/loader/.gitignore new file mode 100644 index 0000000..5761abc --- /dev/null +++ b/loader/.gitignore @@ -0,0 +1 @@ +*.o diff --git a/loader/Makefile b/loader/Makefile index 67dd24c..5055ac7 100644 --- a/loader/Makefile +++ b/loader/Makefile @@ -14,7 +14,7 @@ ASFLAGS += -mfloat-abi=soft OBJ += sys_cacheflush.o endif -OBJ += loader.o loader_$(ARCH).o patches.o emu.o +OBJ += loader.o loader_$(ARCH).o patches.o emu.o host_fb.o host_pnd.o loader: $(OBJ) diff --git a/loader/emu.c b/loader/emu.c index 7e3ca5e..97649ff 100644 --- a/loader/emu.c +++ b/loader/emu.c @@ -1,6 +1,7 @@ // vim:shiftwidth=2:expandtab #include #include +#include #include #include #include @@ -20,84 +21,170 @@ typedef unsigned int u32; typedef unsigned short u16; typedef unsigned char u8; -static int memdev; -static volatile u16 *memregs, *blitter; +struct uppermem_block { + u32 addr; // physical + u32 size; + void *mem; + struct uppermem_block *next; +}; + +static struct uppermem_block *upper_mem; + +static struct { + u32 dstctrl; + u32 dstaddr; + u32 dststride; + u32 srcctrl; + u32 srcaddr; // + u32 srcstride; + u32 srcforcolor; + u32 srcbackcolor; + u32 patctrl; // + u32 patforcolor; + u32 patbackcolor; + u32 size; + u32 ctrl; // + u32 run; + u32 intc; + u32 srcfifo; +} blitter; + +static struct { + union { + u32 mlc_stl_eadr; + struct { + u16 mlc_stl_eadrl; + u16 mlc_stl_eadrh; + }; + }; +} mmsp2; + +static u16 *host_screen; +static int host_stride; -static volatile void *translate_addr(u32 a, u32 *mask) +static void *upper_lookup(u32 addr, u8 **mem_end, int *stride_override) { - if ((a & 0xfff00000) == 0x7f000000) { - *mask = 0xffff; - return memregs; + struct uppermem_block *ub; + + // maybe the screen? + if (mmsp2.mlc_stl_eadr <= addr && addr < mmsp2.mlc_stl_eadr + 320*240*2) { + host_screen = host_video_flip(); // HACK + *mem_end = (u8 *)host_screen + host_stride * 240; + *stride_override = host_stride; + return (u8 *)host_screen + addr - mmsp2.mlc_stl_eadr; } - if ((a & 0xfff00000) == 0x7f100000) { - *mask = 0xff; - return blitter; + + for (ub = upper_mem; ub != NULL; ub = ub->next) { + if (ub->addr <= addr && addr < ub->addr + ub->size) { + *mem_end = (u8 *)ub->mem + ub->size; + return (u8 *)ub->mem + addr - ub->addr; + } } - fprintf(stderr, "bad IO @ %08x\n", a); - abort(); + + return NULL; } -static u32 xread8(u32 a) +static void blitter_do(void) { - volatile u8 *mem; - u32 mask; + u8 *dst, *dste, *src, *srce; + int w, h, sstrd, dstrd; + + if (blitter.srcaddr == blitter.dstaddr) + return; // dummy blit? + + w = blitter.size & 0x7ff; + h = (blitter.size >> 16) & 0x7ff; + sstrd = blitter.srcstride; + dstrd = blitter.dststride; + + dst = upper_lookup(blitter.dstaddr, &dste, &dstrd); + src = upper_lookup(blitter.srcaddr, &srce, &sstrd); + if (dst == NULL || src == NULL) { + printf("blit %08x->%08x %dx%d translated to %p->%p\n", + blitter.srcaddr, blitter.dstaddr, w, h, src, dst); + return; + } + + if (dst + dstrd * h > dste) { + printf("blit %08x->%08x %dx%d did not fit dst\n", + blitter.srcaddr, blitter.dstaddr, w, h); + h = (dste - dst) / dstrd; + } + + if (src + sstrd * h > srce) { + printf("blit %08x->%08x %dx%d did not fit src\n", + blitter.srcaddr, blitter.dstaddr, w, h); + h = (srce - src) / sstrd; + } + + for (; h > 0; h--, dst += dstrd, src += sstrd) + memcpy(dst, src, w * 2); +} + +static u32 xread8(u32 a) +{ iolog("r8 %08x\n", a); - mem = translate_addr(a, &mask); - return mem[a & mask]; + return 0; } static u32 xread16(u32 a) { - volatile u16 *mem; - u32 mask; - - //if ((a & 0xfff00000) == 0x7f100000) { static int a; a ^= ~1; return a & 0xffff; } +// if ((a & 0xfff00000) == 0x7f100000) { static int a; a ^= ~1; return a & 0xffff; } iolog("r16 %08x\n", a); - mem = translate_addr(a, &mask); - return mem[(a & mask) / 2]; + return 0; } static u32 xread32(u32 a) { - volatile u32 *mem; - u32 mask; - - //if ((a & 0xfff00000) == 0x7f100000) { static int a; a ^= ~1; return a; } + u32 d = 0; + if ((a & 0xfff00000) == 0x7f100000) { + u32 *bl = &blitter.dstctrl; + a &= 0xfff; + if (a < 0x40) + d = bl[a / 4]; + if (a == 0x34) + d = 0; // not busy + } iolog("r32 %08x\n", a); - mem = translate_addr(a, &mask); - return mem[(a & mask) / 4]; + return d; } static void xwrite8(u32 a, u32 d) { - volatile u8 *mem; - u32 mask; - iolog("w8 %08x %08x\n", a, d); - mem = translate_addr(a, &mask); - mem[a & mask] = d; } static void xwrite16(u32 a, u32 d) { - volatile u16 *mem; - u32 mask; - iolog("w16 %08x %08x\n", a, d); - mem = translate_addr(a, &mask); - mem[(a & mask) / 2] = d; + if ((a & 0xfff00000) == 0x7f000000) { + a &= 0xffff; + switch (a) { + case 0x2912: mmsp2.mlc_stl_eadrl = d; break; + case 0x2914: mmsp2.mlc_stl_eadrh = d; break; + } + printf("w16 %08x %08x\n", a, d); + } } static void xwrite32(u32 a, u32 d) { - volatile u32 *mem; - u32 mask; - iolog("w32 %08x %08x\n", a, d); - mem = translate_addr(a, &mask); - mem[(a & mask) / 4] = d; + if ((a & 0xfff00000) == 0x7f000000) { + printf("w32 %08x %08x\n", a, d); + return; + } + if ((a & 0xfff00000) == 0x7f100000) { + u32 *bl = &blitter.dstctrl; + a &= 0xfff; + if (a < 0x40) + bl[a / 4] = d; + if (a == 0x34 && (d & 1)) + blitter_do(); + return; + } } #define BIT_SET(v, b) (v & (1 << (b))) @@ -331,36 +418,81 @@ void emu_init(void *map_bottom) printf("linkpages @ %p\n", g_linkpage); init_linkpage(); - memdev = open("/dev/mem", O_RDWR); - memregs = mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, memdev, 0xc0000000); - blitter = mmap(NULL, 0x100, PROT_READ|PROT_WRITE, MAP_SHARED, memdev, 0xe0020000); - //blitter = mmap(NULL, 0x100, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); - printf("mapped %d %p %p\n", memdev, memregs, blitter); + // host stuff + host_screen = host_video_init(&host_stride); + if (host_screen == NULL) { + printf("can't alloc screen\n"); + exit(1); + } +} + +int emu_read_gpiodev(void *buf, int count) +{ + unsigned int btns; + + if (count < 4) { + printf("gpiodev read %d?\n", count); + return -1; + } + + btns = host_read_btns(); + memcpy(buf, &btns, 4); + return 4; } void *emu_mmap_dev(unsigned int length, int prot, int flags, unsigned int offset) { + struct uppermem_block *umem; char name[32]; int fd; + // SoC regs if ((offset & ~0xffff) == 0xc0000000) { return mmap((void *)0x7f000000, length, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED|MAP_NORESERVE, -1, 0); } + // blitter if ((offset & ~0xffff) == 0xe0020000) { return mmap((void *)0x7f100000, length, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED|MAP_NORESERVE, -1, 0); } - // pass through - if ((offset & 0xfe000000) == 0x02000000) - return mmap(NULL, length, prot, flags, memdev, offset); + // upper mem + if ((offset & 0xfe000000) != 0x02000000) + printf("unexpected devmem mmap @ %08x\n", offset); + // return mmap(NULL, length, prot, flags, memdev, offset); + + umem = calloc(1, sizeof(*umem)); + if (umem == NULL) { + printf("OOM\n"); + return MAP_FAILED; + } + + umem->addr = offset; + umem->size = length; + umem->mem = mmap(NULL, length, prot, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + if (umem->mem != MAP_FAILED) + goto done; + + printf("upper mem @ %08x %d mmap fail, trying backing file\n", offset, length); sprintf(name, "m%08x", offset); fd = open(name, O_CREAT|O_RDWR, 0644); lseek(fd, length - 1, SEEK_SET); name[0] = 0; write(fd, name, 1); - return mmap(NULL, length, prot, MAP_SHARED, fd, 0); + umem->mem = mmap(NULL, length, prot, MAP_SHARED, fd, 0); + if (umem->mem == MAP_FAILED) { + printf("failed, giving up\n"); + close(fd); + free(umem); + return MAP_FAILED; + } + +done: + printf("upper mem @ %08x %d\n", offset, length); + umem->next = upper_mem; + upper_mem = umem; + return umem->mem; } diff --git a/loader/header.h b/loader/header.h index ddb948d..8e245c8 100644 --- a/loader/header.h +++ b/loader/header.h @@ -5,6 +5,16 @@ void do_patches(void *ptr, unsigned int size); void emu_init(void *map_bottom); void *emu_mmap_dev(unsigned int length, int prot, int flags, unsigned int offset); +int emu_read_gpiodev(void *buf, int count); + +void *host_video_init(int *stride); +void *host_video_flip(void); +int host_read_btns(void); + +enum { GP2X_UP = 0, GP2X_LEFT = 2, GP2X_DOWN = 4, GP2X_RIGHT = 6, + GP2X_START = 8, GP2X_SELECT = 9, GP2X_L = 10, GP2X_R = 11, + GP2X_A = 12, GP2X_B = 13, GP2X_X = 14, GP2X_Y = 15, + GP2X_VOL_UP = 16, GP2X_VOL_DOWN = 17, GP2X_PUSH = 18 }; #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) diff --git a/loader/host_fb.c b/loader/host_fb.c new file mode 100644 index 0000000..8529f36 --- /dev/null +++ b/loader/host_fb.c @@ -0,0 +1,164 @@ +/* copy-paste from PD */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FBDEV_MAX_BUFFERS 3 + +static int fbdev = -1; +static void *fbdev_mem = MAP_FAILED; +static int fbdev_mem_size; +static struct fb_var_screeninfo fbvar_old; +static struct fb_var_screeninfo fbvar_new; +static int fbdev_buffer_write; + +static void *fbdev_buffers[FBDEV_MAX_BUFFERS]; +static int fbdev_buffer_count; +static void *g_screen_ptr; + +void plat_video_flip(void) +{ + int draw_buf; + + if (fbdev_buffer_count < 2) + return; + + draw_buf = fbdev_buffer_write; + fbdev_buffer_write++; + if (fbdev_buffer_write >= fbdev_buffer_count) + fbdev_buffer_write = 0; + + fbvar_new.yoffset = fbvar_old.yres * draw_buf; + g_screen_ptr = fbdev_buffers[fbdev_buffer_write]; + + ioctl(fbdev, FBIOPAN_DISPLAY, &fbvar_new); +} + +void plat_video_wait_vsync(void) +{ + int arg = 0; + ioctl(fbdev, FBIO_WAITFORVSYNC, &arg); +} + +int vout_fbdev_init(int *w, int *h) +{ + static const char *fbdev_name = "/dev/fb0"; + int i, ret; + + fbdev = open(fbdev_name, O_RDWR); + if (fbdev == -1) { + fprintf(stderr, "%s: ", fbdev_name); + perror("open"); + return -1; + } + + ret = ioctl(fbdev, FBIOGET_VSCREENINFO, &fbvar_old); + if (ret == -1) { + perror("FBIOGET_VSCREENINFO ioctl"); + goto fail; + } + + fbvar_new = fbvar_old; + printf("%s: %ix%i@%d\n", fbdev_name, fbvar_old.xres, fbvar_old.yres, fbvar_old.bits_per_pixel); + *w = fbvar_old.xres; + *h = fbvar_old.yres; + fbdev_buffer_count = FBDEV_MAX_BUFFERS; // be optimistic + + if (fbvar_new.bits_per_pixel != 16) { + printf(" switching to 16bpp\n"); + fbvar_new.bits_per_pixel = 16; + ret = ioctl(fbdev, FBIOPUT_VSCREENINFO, &fbvar_new); + if (ret == -1) { + perror("FBIOPUT_VSCREENINFO ioctl"); + goto fail; + } + } + + if (fbvar_new.yres_virtual < fbvar_old.yres * fbdev_buffer_count) { + fbvar_new.yres_virtual = fbvar_old.yres * fbdev_buffer_count; + ret = ioctl(fbdev, FBIOPUT_VSCREENINFO, &fbvar_new); + if (ret == -1) { + fbdev_buffer_count = 1; + fprintf(stderr, "Warning: failed to increase virtual resolution, " + "doublebuffering disabled\n"); + } + } + + fbdev_mem_size = *w * *h * 2 * fbdev_buffer_count; + fbdev_mem = mmap(0, fbdev_mem_size, PROT_WRITE|PROT_READ, MAP_SHARED, fbdev, 0); + if (fbdev_mem == MAP_FAILED && fbdev_buffer_count > 1) { + fbdev_mem_size = *w * *h * 2; + fbdev_buffer_count = 1; + fprintf(stderr, "Warning: can't map %d bytes, doublebuffering disabled\n", fbdev_mem_size); + fbdev_mem = mmap(0, fbdev_mem_size, PROT_WRITE|PROT_READ, MAP_SHARED, fbdev, 0); + } + if (fbdev_mem == MAP_FAILED) { + perror("mmap framebuffer"); + goto fail; + } + memset(fbdev_mem, 0, fbdev_mem_size); + for (i = 0; i < fbdev_buffer_count; i++) + fbdev_buffers[i] = (char *)fbdev_mem + i * *w * *h * 2; + g_screen_ptr = fbdev_buffers[0]; + + // some checks + ret = 0; + ret = ioctl(fbdev, FBIO_WAITFORVSYNC, &ret); + if (ret != 0) + fprintf(stderr, "Warning: vsync doesn't seem to be supported\n"); + + if (fbdev_buffer_count > 1) { + fbdev_buffer_write = 0; + fbvar_new.yoffset = fbvar_old.yres * (fbdev_buffer_count - 1); + ret = ioctl(fbdev, FBIOPAN_DISPLAY, &fbvar_new); + if (ret != 0) { + fbdev_buffer_count = 1; + fprintf(stderr, "Warning: can't pan display, doublebuffering disabled\n"); + } + } + + printf("fbdev initialized.\n"); + return 0; + +fail: + close(fbdev); + return -1; +} + +void vout_fbdev_finish(void) +{ + ioctl(fbdev, FBIOPUT_VSCREENINFO, &fbvar_old); + if (fbdev_mem != MAP_FAILED) + munmap(fbdev_mem, fbdev_mem_size); + if (fbdev >= 0) + close(fbdev); + fbdev_mem = NULL; + fbdev = -1; +} + +#include "header.h" + +void *host_video_init(int *stride) +{ + int ret, w, h; + + ret = vout_fbdev_init(&w, &h); + if (ret != 0) + return NULL; + + *stride = w * 2; + return g_screen_ptr; +} + +void *host_video_flip(void) +{ + plat_video_flip(); + return g_screen_ptr; +} diff --git a/loader/host_pnd.c b/loader/host_pnd.c new file mode 100644 index 0000000..773a087 --- /dev/null +++ b/loader/host_pnd.c @@ -0,0 +1,102 @@ +// vim:shiftwidth=2:expandtab +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "header.h" + +static int ifd = -1; +static int keystate; + +static void init(void) +{ + char buff[64]; + int i; + + for (ifd = -1, i = 0; ; i++) { + snprintf(buff, sizeof(buff), "/dev/input/event%i", i); + ifd = open(buff, O_RDONLY | O_NONBLOCK); + if (ifd == -1) + break; + + ioctl(ifd, EVIOCGNAME(sizeof(buff)), buff); + + if (strcasestr(buff, "gpio") != NULL) + break; + close(ifd); + } + + if (ifd < 0) + printf("no input device\n"); +} + +static const struct { + unsigned short key; + unsigned short btn; +} key_map[] = { + { KEY_LEFT, GP2X_LEFT }, + { KEY_RIGHT, GP2X_RIGHT }, + { KEY_UP, GP2X_UP }, + { KEY_DOWN, GP2X_DOWN }, + { KEY_PAGEUP, GP2X_Y }, + { BTN_BASE, GP2X_Y }, + { KEY_END, GP2X_B }, + { BTN_BASE2, GP2X_B }, + { KEY_PAGEDOWN, GP2X_X }, + { BTN_BASE3, GP2X_X }, + { KEY_HOME, GP2X_A }, + { BTN_BASE4, GP2X_A }, + { KEY_RIGHTSHIFT, GP2X_L }, + { BTN_TL, GP2X_L }, + { KEY_RIGHTCTRL, GP2X_R }, + { BTN_TR, GP2X_R }, + { KEY_LEFTALT, GP2X_START }, + { BTN_START, GP2X_START }, + { KEY_LEFTCTRL, GP2X_SELECT }, + { BTN_SELECT, GP2X_SELECT }, +}; + +int host_read_btns(void) +{ + struct input_event ev; + int i, ret; + + if (ifd < 0) + init(); + if (ifd < 0) + return keystate; + + while (1) + { + ret = read(ifd, &ev, sizeof(ev)); + if (ret < (int) sizeof(ev)) { + if (errno != EAGAIN && errno != EWOULDBLOCK) + perror("evtest: read error"); + + return keystate; + } + + if (ev.type != EV_KEY) + continue; + + for (i = 0; i < sizeof(key_map) / sizeof(key_map[0]); i++) { + if (key_map[i].key != ev.code) + continue; + if (ev.value) + keystate |= (1 << key_map[i].btn); + else + keystate &= ~(1 << key_map[i].btn); + break; + } + } + + return keystate; +} + diff --git a/loader/patches.c b/loader/patches.c index 9354d2f..dc9edf8 100644 --- a/loader/patches.c +++ b/loader/patches.c @@ -5,6 +5,7 @@ #include #include #include +#include #include "header.h" #include "sys_cacheflush.h" @@ -22,15 +23,22 @@ static const unsigned int sig_mmap_[] = { 0xe1b0ca05, 0x1a000006, 0xe1a05625, 0xef9000c0 }; -#define FAKE_DEVMEM_DEVICE 10001 +static const unsigned int sig_read[] = { + 0xe59fc080, 0xe59cc000, 0xe33c0000, 0x1a000003, 0xef900003 +}; + +#define FAKE_DEVMEM_DEVICE 10001 +#define FAKE_DEVGPIO_DEVICE 10002 static int w_open(const char *pathname, int flags, mode_t mode) { int ret; - if (strcmp(pathname, "/dev/mem") != 0) - ret = open(pathname, flags, mode); - else + if (strcmp(pathname, "/dev/mem") == 0) ret = FAKE_DEVMEM_DEVICE; + else if (strcmp(pathname, "/dev/GPIO") == 0) + ret = FAKE_DEVGPIO_DEVICE; + else + ret = open(pathname, flags, mode); printf("open(%s) = %d\n", pathname, ret); return ret; @@ -49,6 +57,17 @@ static void *w_mmap(void *addr, size_t length, int prot, int flags, int fd, off_ } #define w_mmap_ w_mmap +ssize_t w_read(int fd, void *buf, size_t count) +{ + ssize_t ret; + if (fd != FAKE_DEVGPIO_DEVICE) + return read(fd, buf, count); + + ret = emu_read_gpiodev(buf, count); + //printf("read(%d, %p, %d) = %d\n", fd, buf, count, ret); + return ret; +} + #define PATCH(f) { sig_##f, ARRAY_SIZE(sig_##f), w_##f } static const struct { @@ -59,6 +78,7 @@ static const struct { PATCH(open), PATCH(mmap), PATCH(mmap_), // mmap using mmap2 syscall + PATCH(read), }; void do_patches(void *ptr, unsigned int size)