SPARC fixes : syscall fixes - added user register window exception support


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@494 c046a42c-6fe2-441c-8c8c-71466251a162
diff --git a/linux-user/main.c b/linux-user/main.c
index aa5b758..bc19c64 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -299,27 +299,127 @@
 
 #ifdef TARGET_SPARC
 
+//#define DEBUG_WIN
+
+/* WARNING: dealing with register windows _is_ complicated */
+static inline int get_reg_index(CPUSPARCState *env, int cwp, int index)
+{
+    index = (index + cwp * 16) & (16 * NWINDOWS - 1);
+    /* wrap handling : if cwp is on the last window, then we use the
+       registers 'after' the end */
+    if (index < 8 && env->cwp == (NWINDOWS - 1))
+        index += (16 * NWINDOWS);
+    return index;
+}
+
+static inline void save_window_offset(CPUSPARCState *env, int offset)
+{
+    unsigned int new_wim, i, cwp1;
+    uint32_t *sp_ptr;
+    
+    new_wim = ((env->wim >> 1) | (env->wim << (NWINDOWS - 1))) &
+        ((1LL << NWINDOWS) - 1);
+    /* save the window */
+    cwp1 = (env->cwp + offset) & (NWINDOWS - 1);
+    sp_ptr = (uint32_t *)(env->regbase[get_reg_index(env, cwp1, 6)]);
+#if defined(DEBUG_WIN)
+    printf("win_overflow: sp_ptr=0x%x save_cwp=%d\n", 
+           (int)sp_ptr, cwp1);
+#endif
+    for(i = 0; i < 16; i++)
+        stl_raw(sp_ptr + i, env->regbase[get_reg_index(env, cwp1, 8 + i)]);
+    env->wim = new_wim;
+}
+
+static void save_window(CPUSPARCState *env)
+{
+    save_window_offset(env, 2);
+}
+
+static void restore_window(CPUSPARCState *env)
+{
+    unsigned int new_wim, i, cwp1;
+    uint32_t *sp_ptr;
+    
+    new_wim = ((env->wim << 1) | (env->wim >> (NWINDOWS - 1))) &
+        ((1LL << NWINDOWS) - 1);
+    
+    /* restore the invalid window */
+    cwp1 = (env->cwp + 1) & (NWINDOWS - 1);
+    sp_ptr = (uint32_t *)(env->regbase[get_reg_index(env, cwp1, 6)]);
+#if defined(DEBUG_WIN)
+    printf("win_underflow: sp_ptr=0x%x load_cwp=%d\n", 
+           (int)sp_ptr, cwp1);
+#endif
+    for(i = 0; i < 16; i++)
+        env->regbase[get_reg_index(env, cwp1, 8 + i)] = ldl_raw(sp_ptr + i);
+    env->wim = new_wim;
+}
+
+static void flush_windows(CPUSPARCState *env)
+{
+    int offset, cwp1;
+#if defined(DEBUG_WIN)
+    printf("flush_windows:\n");
+#endif
+    offset = 2;
+    for(;;) {
+        /* if restore would invoke restore_window(), then we can stop */
+        cwp1 = (env->cwp + 1) & (NWINDOWS - 1);
+        if (env->wim & (1 << cwp1))
+            break;
+#if defined(DEBUG_WIN)
+        printf("offset=%d: ", offset);
+#endif
+        save_window_offset(env, offset);
+        offset++;
+    }
+}
+
 void cpu_loop (CPUSPARCState *env)
 {
-	int trapnr;
-
-	while (1) {
-		trapnr = cpu_sparc_exec (env);
-
-		switch (trapnr) {
-		  case 0x8: case 0x10:
-			env->regwptr[0] = do_syscall (env, env->gregs[1],
-				env->regwptr[0], env->regwptr[1], env->regwptr[2],
-				env->regwptr[3], env->regwptr[4], env->regwptr[13]);
-			if (env->regwptr[0] >= 0xffffffe0)
-				env->psr |= PSR_CARRY;
-			break;
-		  default:
-			printf ("Invalid trap: %d\n", trapnr);
-			exit (1);
-		}
-		process_pending_signals (env);
-	}
+    int trapnr, ret;
+    
+    while (1) {
+        trapnr = cpu_sparc_exec (env);
+        
+        switch (trapnr) {
+        case 0x88: 
+        case 0x90:
+            ret = do_syscall (env, env->gregs[1],
+                              env->regwptr[0], env->regwptr[1], 
+                              env->regwptr[2], env->regwptr[3], 
+                              env->regwptr[4], env->regwptr[5]);
+            if ((unsigned int)ret >= (unsigned int)(-515)) {
+                env->psr |= PSR_CARRY;
+                ret = -ret;
+            } else {
+                env->psr &= ~PSR_CARRY;
+            }
+            env->regwptr[0] = ret;
+            /* next instruction */
+            env->pc = env->npc;
+            env->npc = env->npc + 4;
+            break;
+        case 0x83: /* flush windows */
+            //            flush_windows(env);
+            /* next instruction */
+            env->pc = env->npc;
+            env->npc = env->npc + 4;
+            break;
+        case TT_WIN_OVF: /* window overflow */
+            save_window(env);
+            break;
+        case TT_WIN_UNF: /* window underflow */
+            restore_window(env);
+            break;
+        default:
+            printf ("Unhandled trap: 0x%x\n", trapnr);
+            cpu_sparc_dump_state(env, stderr, 0);
+            exit (1);
+        }
+        process_pending_signals (env);
+    }
 }
 
 #endif
@@ -636,8 +736,16 @@
         env->cpsr = regs->uregs[16];
     }
 #elif defined(TARGET_SPARC)
-	env->pc = regs->u_regs[0];
-	env->regwptr[6] = regs->u_regs[1]-0x40;
+    {
+        int i;
+	env->pc = regs->pc;
+	env->npc = regs->npc;
+        env->y = regs->y;
+        for(i = 0; i < 8; i++)
+            env->gregs[i] = regs->u_regs[i];
+        for(i = 0; i < 8; i++)
+            env->regwptr[i] = regs->u_regs[i + 8];
+    }
 #elif defined(TARGET_PPC)
     {
         int i;