some conditional op emulation
[ginge.git] / loader / emu.c
index dd507ff..8dc6d51 100644 (file)
@@ -1,8 +1,18 @@
-// vim:shiftwidth=2:expandtab
+/*
+ * GINGE - GINGE Is Not Gp2x Emulator
+ * (C) notaz, 2010-2011,2016
+ *
+ * This work is licensed under the MAME license, see COPYING file for details.
+ */
+// a "gentle" reminder
+#ifdef __ARM_EABI__
+#error loader is meant to be OABI!
+#endif
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <alloca.h>
+#include <ctype.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
@@ -438,7 +448,8 @@ static u32 xread32_io_cmn(u32 a, u32 *handled)
   //  ???? ???? YXBA DURiLe ???? VdVuMS LR?? ????
   // |     GPIOC[31:16]    |    GPIOB[31:16]     |
   case 0xa058: // GPIOBPAD
-    d =   pollux.btn_state & 0x0300;
+    d =  (pollux.btn_state >> 1) & 0x0100;
+    d |= (pollux.btn_state << 1) & 0x0200;
     d |= (pollux.btn_state >> 3) & 0x0080;
     d |= (pollux.btn_state >> 5) & 0x0040;
     d |= (pollux.btn_state >> 6) & 0x0c00;
@@ -720,18 +731,46 @@ static struct op_linkpage *g_linkpage;
 static u32 *g_code_ptr;
 static int g_linkpage_count;
 
+enum opcond {
+  C_EQ, C_NE, C_CS, C_CC, C_MI, C_PL, C_VS, C_VC,
+  C_HI, C_LS, C_GE, C_LT, C_GT, C_LE, C_AL,
+};
+enum cpsr_cond {
+  CPSR_N = (1u << 31),
+  CPSR_Z = (1u << 30),
+  CPSR_C = (1u << 29),
+  CPSR_V = (1u << 28),
+};
+
 #define BIT_SET(v, b) (v & (1 << (b)))
 
 void emu_handle_op(struct op_context *op_ctx, struct op_stackframe *sframe)
 {
   u32 *regs = sframe->saved_regs;
+  u32 cpsr = sframe->cpsr;
   u32 op = op_ctx->op;
   u32 t, shift, ret, addr;
-  int rn, rd;
+  int i, rn, rd, cond;
 
+  cond = (op & 0xf0000000) >> 28;
   rd = (op & 0x0000f000) >> 12;
   rn = (op & 0x000f0000) >> 16;
 
+  if (cond != 0x0e) {
+    switch (cond) {
+    case C_EQ: if ( (cpsr & CPSR_Z)) break; return;
+    case C_NE: if (!(cpsr & CPSR_Z)) break; return;
+    case C_CS: if ( (cpsr & CPSR_C)) break; return;
+    case C_CC: if (!(cpsr & CPSR_C)) break; return;
+    case C_MI: if ( (cpsr & CPSR_N)) break; return;
+    case C_PL: if (!(cpsr & CPSR_N)) break; return;
+    case C_VS: if ( (cpsr & CPSR_V)) break; return;
+    case C_VC: if (!(cpsr & CPSR_V)) break; return;
+    default:
+      goto unhandled;
+    }
+  }
+
   if ((op & 0x0f200090) == 0x01000090) { // AM3: LDRH, STRH
     if (!BIT_SET(op, 5)) // !H
       goto unhandled;
@@ -758,10 +797,12 @@ void emu_handle_op(struct op_context *op_ctx, struct op_stackframe *sframe)
     else
       xwrite16(addr, regs[rd]);
   }
-  else if ((op & 0x0d200000) == 0x05000000) { // AM2: LDR[B], STR[B]
+  else if ((op & 0x0c000000) == 0x04000000) { // load/store word/byte
+    if (BIT_SET(op, 21))
+      goto unhandled;                   // unprivileged
     if (BIT_SET(op, 25)) {              // reg offs
       if (BIT_SET(op, 4))
-        goto unhandled;
+        goto unhandled;                 // nah it's media
 
       t = regs[op & 0x000f];
       shift = (op & 0x0f80) >> 7;
@@ -777,7 +818,12 @@ void emu_handle_op(struct op_context *op_ctx, struct op_stackframe *sframe)
 
     if (!BIT_SET(op, 23))
       t = -t;
-    addr = regs[rn] + t;
+
+    addr = regs[rn];
+    if (BIT_SET(op, 24))   // pre-indexed
+      addr += t;
+    if (!BIT_SET(op, 24) || BIT_SET(op, 21))
+      regs[rn] += t;       // writeback
 
     if (BIT_SET(op, 20)) { // Load
       if (BIT_SET(op, 22)) // Byte
@@ -806,6 +852,10 @@ void emu_handle_op(struct op_context *op_ctx, struct op_stackframe *sframe)
 
 unhandled:
   err("unhandled IO op %08x @ %08x\n", op, op_ctx->pc);
+  for (i = 0; i < 8-1; i++)
+    err(" r%d=%08x  r%-2d=%08x\n", i, regs[i], i+8, regs[i+8]);
+  err(" r%d=%08x cpsr=%08x\n", i, regs[i], cpsr);
+  abort();
 }
 
 static u32 make_offset12(u32 *pc, u32 *target)
@@ -870,7 +920,7 @@ static void segv_sigaction(int num, siginfo_t *info, void *ctx)
     // real crash - time to die
     err("segv %d %p @ %08x\n", info->si_code, info->si_addr, regs[15]);
     for (i = 0; i < 8; i++)
-      dbg(" r%d=%08x r%2d=%08x\n", i, regs[i], i+8, regs[i+8]);
+      dbg(" r%d=%08x r%-2d=%08x\n", i, regs[i], i+8, regs[i+8]);
     signal(num, SIG_DFL);
     raise(num);
     return;
@@ -925,6 +975,14 @@ void emu_init(void *map_bottom)
   void *pret;
   int ret;
 
+#ifdef PND
+  if (geteuid() == 0) {
+    fprintf(stderr, "don't try to run as root, device registers or memory "
+                    "might get trashed crashing the OS or even damaging the device.\n");
+    exit(1);
+  }
+#endif
+
   g_linkpage = (void *)(((u32)map_bottom - LINKPAGE_ALLOC) & ~0xfff);
   pret = mmap(g_linkpage, LINKPAGE_ALLOC, PROT_READ|PROT_WRITE,
               MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0);
@@ -948,13 +1006,15 @@ void emu_init(void *map_bottom)
     exit(1);
   }
 
-#ifdef WIZ
-  // we are short on memmory on Wiz, need special handling
-  extern void *host_mmap_upper(void);
-  mmsp2.umem = host_mmap_upper();
-#else
+  // TODO: check if this really fails on Wiz..
   mmsp2.umem = mmap(NULL, 0x2000000, PROT_READ|PROT_WRITE|PROT_EXEC,
                     MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
+#ifdef WIZ
+  if (mmsp2.umem == MAP_FAILED) {
+    // we are short on memmory on Wiz, need special handling
+    extern void *host_mmap_upper(void);
+    mmsp2.umem = host_mmap_upper();
+  }
 #endif
   if (mmsp2.umem == MAP_FAILED) {
     perror(PFX "mmap upper mem");
@@ -1195,24 +1255,27 @@ static const struct {
   const char *from;
   const char *to;
 } path_map[] = {
-  { "/mnt/tmp/", "/tmp/" },
+  { "/mnt/tmp", "./tmp" },
 };
 
 static const char *wrap_path(const char *path)
 {
-  char *buff;
+  char *buff, *p;
   size_t size;
   int i, len;
 
   // do only path mapping for now
   for (i = 0; i < ARRAY_SIZE(path_map); i++) {
-    len = strlen(path_map[i].from);
-    if (strncmp(path, path_map[i].from, len) == 0) {
+    p = strstr(path, path_map[i].from);
+    if (p != NULL) {
       size = strlen(path) + strlen(path_map[i].to) + 1;
       buff = malloc(size);
       if (buff == NULL)
         break;
-      snprintf(buff, size, "%s%s", path_map[i].to, path + len);
+      len = p - path;
+      strncpy(buff, path, len);
+      snprintf(buff + len, size - len, "%s%s", path_map[i].to,
+        path + len + strlen(path_map[i].from));
       dbg("mapped path \"%s\" -> \"%s\"\n", path, buff);
       return buff;
     }
@@ -1243,6 +1306,7 @@ void *emu_do_fopen(const char *path, const char *mode)
 int emu_do_system(const char *command)
 {
   static char tmp_path[512];
+  int need_ginge = 0;
   const char *p2;
   char *p;
   int ret;
@@ -1250,17 +1314,25 @@ int emu_do_system(const char *command)
   if (command == NULL)
     return -1;
 
-  // pass through stuff in PATH
-  p = strchr(command, ' ');
-  p2 = strchr(command, '/');
-  if (p2 == NULL || (p != NULL && p2 > p))
-    return system(command);
+  for (p2 = command; *p2 && isspace(*p2); p2++)
+    ;
 
-  make_local_path(tmp_path, sizeof(tmp_path), "ginge_prep");
-  p = tmp_path + strlen(tmp_path);
+  if (*p2 == '.') // relative path?
+    need_ginge = 1;
+  else if (*p2 == '/' && strncmp(p2, "/bin", 4) && strncmp(p2, "/lib", 4)
+           && strncmp(p2, "/sbin", 4) && strncmp(p2, "/usr", 4))
+    // absolute path, but not a system command
+    need_ginge = 1;
 
   p2 = wrap_path(command);
-  snprintf(p, sizeof(tmp_path) - (p - tmp_path), " --nomenu %s", p2);
+  if (need_ginge) {
+    make_local_path(tmp_path, sizeof(tmp_path), "ginge_prep");
+    p = tmp_path + strlen(tmp_path);
+
+    snprintf(p, sizeof(tmp_path) - (p - tmp_path), " --nomenu %s", p2);
+  }
+  else
+    snprintf(tmp_path, sizeof(tmp_path), "%s", p2);
   wrap_path_free(p2, command);
 
   dbg("system: \"%s\"\n", tmp_path);
@@ -1311,3 +1383,4 @@ int emu_do_execve(const char *filename, char *const argv[], char *const envp[])
   return ret;
 }
 
+// vim:shiftwidth=2:expandtab