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)
// vim:shiftwidth=2:expandtab
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
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)))
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;
}
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]))
--- /dev/null
+/* copy-paste from PD */
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <linux/fb.h>
+#include <linux/matroxfb.h>
+
+#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;
+}
--- /dev/null
+// vim:shiftwidth=2:expandtab
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <linux/input.h>
+
+#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;
+}
+
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
+#include <unistd.h>
#include "header.h"
#include "sys_cacheflush.h"
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;
}
#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 {
PATCH(open),
PATCH(mmap),
PATCH(mmap_), // mmap using mmap2 syscall
+ PATCH(read),
};
void do_patches(void *ptr, unsigned int size)