Rework ASI instructions (Aurelien Jarno)
authorblueswir1 <blueswir1@c046a42c-6fe2-441c-8c8c-71466251a162>
Fri, 21 Sep 2007 19:10:53 +0000 (19:10 +0000)
committerblueswir1 <blueswir1@c046a42c-6fe2-441c-8c8c-71466251a162>
Fri, 21 Sep 2007 19:10:53 +0000 (19:10 +0000)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3205 c046a42c-6fe2-441c-8c8c-71466251a162

target-sparc/exec.h
target-sparc/op.c
target-sparc/op_helper.c
target-sparc/op_mem.h
target-sparc/translate.c

index 0305a04..35f4b5f 100644 (file)
@@ -50,7 +50,7 @@ void cpu_unlock(void);
 void cpu_loop_exit(void);
 void helper_flush(target_ulong addr);
 void helper_ld_asi(int asi, int size, int sign);
-void helper_st_asi(int asi, int size, int sign);
+void helper_st_asi(int asi, int size);
 void helper_rett(void);
 void helper_ldfsr(void);
 void set_cwp(int new_cwp);
index f2168af..bb084ee 100644 (file)
@@ -1862,10 +1862,126 @@ void OPPROTO op_ld_asi_reg()
 void OPPROTO op_st_asi_reg()
 {
     T0 += PARAM1;
-    helper_st_asi(env->asi, PARAM2, PARAM3);
+    helper_st_asi(env->asi, PARAM2);
+}
+
+void OPPROTO op_ldstub_asi_reg()             /* XXX: should be atomically */
+{
+    target_ulong tmp;
+
+    T0 += PARAM1;
+    helper_ld_asi(env->asi, 1, 0);
+    tmp = T1;
+    T1 = 0xff;
+    helper_st_asi(env->asi, 1);
+    T1 = tmp;
+}
+
+void OPPROTO op_swap_asi_reg()               /* XXX: should be atomically */
+{
+    target_ulong tmp1, tmp2;
+
+    T0 += PARAM1;
+    tmp1 = T1;
+    helper_ld_asi(env->asi, 4, 0);
+    tmp2 = T1;
+    T1 = tmp1;
+    helper_st_asi(env->asi, 4);
+    T1 = tmp2;
+}
+
+void OPPROTO op_ldda_asi()
+{
+    helper_ld_asi(PARAM1, 8, 0);
+    T0 = T1 & 0xffffffffUL;
+    T1 >>= 32;
+}
+
+void OPPROTO op_ldda_asi_reg()
+{
+    T0 += PARAM1;
+    helper_ld_asi(env->asi, 8, 0);
+    T0 = T1 & 0xffffffffUL;
+    T1 >>= 32;
+}
+
+void OPPROTO op_stda_asi()
+{
+    T1 <<= 32;
+    T1 += T2 & 0xffffffffUL;
+    helper_st_asi(PARAM1, 8);
+}
+
+void OPPROTO op_stda_asi_reg()
+{
+    T0 += PARAM1;
+    T1 <<= 32;
+    T1 += T2 & 0xffffffffUL;
+    helper_st_asi(env->asi, 8);
+}
+
+void OPPROTO op_cas_asi()                    /* XXX: should be atomically */
+{
+    target_ulong tmp;
+
+    tmp = T1 & 0xffffffffUL;
+    helper_ld_asi(PARAM1, 4, 0);
+    if (tmp == T1) {
+        tmp = T1;
+        T1 = T2 & 0xffffffffUL;
+        helper_st_asi(PARAM1, 4);
+        T1 = tmp;
+    }
+    T1 &= 0xffffffffUL;
+}
+
+void OPPROTO op_cas_asi_reg()                /* XXX: should be atomically */
+{
+    target_ulong tmp;
+
+    T0 += PARAM1;
+    tmp = T1 & 0xffffffffUL;
+    helper_ld_asi(env->asi, 4, 0);
+    if (tmp == T1) {
+        tmp = T1;
+        T1 = T2 & 0xffffffffUL;
+        helper_st_asi(env->asi, 4);
+        T1 = tmp;
+    }
+    T1 &= 0xffffffffUL;
+}
+
+void OPPROTO op_casx_asi()                   /* XXX: should be atomically */
+{
+    target_ulong tmp;
+
+    tmp = T1;
+    helper_ld_asi(PARAM1, 8, 0);
+    if (tmp == T1) {
+        tmp = T1;
+        T1 = T2;
+        helper_st_asi(PARAM1, 8);
+        T1 = tmp;
+    }
+}
+
+void OPPROTO op_casx_asi_reg()               /* XXX: should be atomically */
+{
+    target_ulong tmp;
+
+    T0 += PARAM1;
+    tmp = T1;
+    helper_ld_asi(env->asi, 8, 0);
+    if (tmp == T1) {
+        tmp = T1;
+        T1 = T2;
+        helper_st_asi(env->asi, 8);
+        T1 = tmp;
+    }
 }
 #endif
 
+#if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64)
 void OPPROTO op_ld_asi()
 {
     helper_ld_asi(PARAM1, PARAM2, PARAM3);
@@ -1873,9 +1989,33 @@ void OPPROTO op_ld_asi()
 
 void OPPROTO op_st_asi()
 {
-    helper_st_asi(PARAM1, PARAM2, PARAM3);
+    helper_st_asi(PARAM1, PARAM2);
 }
 
+void OPPROTO op_ldstub_asi()                 /* XXX: should be atomically */
+{
+    target_ulong tmp;
+
+    helper_ld_asi(PARAM1, 1, 0);
+    tmp = T1;
+    T1 = 0xff;
+    helper_st_asi(PARAM1, 1);
+    T1 = tmp;
+}
+
+void OPPROTO op_swap_asi()                   /* XXX: should be atomically */
+{
+    target_ulong tmp1, tmp2;
+
+    tmp1 = T1;
+    helper_ld_asi(PARAM1, 4, 0);
+    tmp2 = T1;
+    T1 = tmp1;
+    helper_st_asi(PARAM1, 4);
+    T1 = tmp2;
+}
+#endif
+
 #ifdef TARGET_SPARC64
 // This function uses non-native bit order
 #define GET_FIELD(X, FROM, TO)                                  \
index 98c4a1b..c168252 100644 (file)
@@ -137,16 +137,8 @@ GEN_FCMP(fcmpes_fcc3, float32, FT0, FT1, 26, 1);
 GEN_FCMP(fcmped_fcc3, float64, DT0, DT1, 26, 1);
 #endif
 
-#if defined(CONFIG_USER_ONLY)
-void helper_ld_asi(int asi, int size, int sign)
-{
-}
-
-void helper_st_asi(int asi, int size, int sign)
-{
-}
-#else
 #ifndef TARGET_SPARC64
+#ifndef CONFIG_USER_ONLY
 void helper_ld_asi(int asi, int size, int sign)
 {
     uint32_t ret = 0;
@@ -200,6 +192,42 @@ void helper_ld_asi(int asi, int size, int sign)
             break;
         }
         break;
+    case 0xa: /* User data access */
+        switch(size) {
+        case 1:
+            ret = ldub_user(T0);
+            break;
+        case 2:
+            ret = lduw_user(T0 & ~1);
+            break;
+        default:
+        case 4:
+            ret = ldl_user(T0 & ~3);
+            break;
+        case 8:
+            ret = ldl_user(T0 & ~3);
+            T0 = ldl_user((T0 + 4) & ~3);
+            break;
+        }
+        break;
+    case 0xb: /* Supervisor data access */
+        switch(size) {
+        case 1:
+            ret = ldub_kernel(T0);
+            break;
+        case 2:
+            ret = lduw_kernel(T0 & ~1);
+            break;
+        default:
+        case 4:
+            ret = ldl_kernel(T0 & ~3);
+            break;
+        case 8:
+            ret = ldl_kernel(T0 & ~3);
+            T0 = ldl_kernel((T0 + 4) & ~3);
+            break;
+        }
+        break;
     case 0xc: /* I-cache tag */
     case 0xd: /* I-cache data */
     case 0xe: /* D-cache tag */
@@ -253,10 +281,22 @@ void helper_ld_asi(int asi, int size, int sign)
         ret = 0;
         break;
     }
-    T1 = ret;
+    if (sign) {
+        switch(size) {
+        case 1:
+            T1 = (int8_t) ret;
+        case 2:
+            T1 = (int16_t) ret;
+        default:
+            T1 = ret;
+            break;
+        }
+    }
+    else
+        T1 = ret;
 }
 
-void helper_st_asi(int asi, int size, int sign)
+void helper_st_asi(int asi, int size)
 {
     switch(asi) {
     case 2: /* SuperSparc MXCC registers */
@@ -325,6 +365,42 @@ void helper_st_asi(int asi, int size, int sign)
 #endif
             return;
         }
+    case 0xa: /* User data access */
+        switch(size) {
+        case 1:
+            stb_user(T0, T1);
+            break;
+        case 2:
+            stw_user(T0 & ~1, T1);
+            break;
+        default:
+        case 4:
+            stl_user(T0 & ~3, T1);
+            break;
+        case 8:
+            stl_user(T0 & ~3, T1);
+            stl_user((T0 + 4) & ~3, T2);
+            break;
+        }
+        break;
+    case 0xb: /* Supervisor data access */
+        switch(size) {
+        case 1:
+            stb_kernel(T0, T1);
+            break;
+        case 2:
+            stw_kernel(T0 & ~1, T1);
+            break;
+        default:
+        case 4:
+            stl_kernel(T0 & ~3, T1);
+            break;
+        case 8:
+            stl_kernel(T0 & ~3, T1);
+            stl_kernel((T0 + 4) & ~3, T2);
+            break;
+        }
+        break;
     case 0xc: /* I-cache tag */
     case 0xd: /* I-cache data */
     case 0xe: /* D-cache tag */
@@ -422,7 +498,146 @@ void helper_st_asi(int asi, int size, int sign)
     }
 }
 
-#else
+#endif /* CONFIG_USER_ONLY */
+#else /* TARGET_SPARC64 */
+
+#ifdef CONFIG_USER_ONLY
+void helper_ld_asi(int asi, int size, int sign)
+{
+    uint64_t ret = 0;
+
+    if (asi < 0x80)
+        raise_exception(TT_PRIV_ACT);
+
+    switch (asi) {
+    case 0x80: // Primary
+    case 0x82: // Primary no-fault
+    case 0x88: // Primary LE
+    case 0x8a: // Primary no-fault LE
+        {
+            switch(size) {
+            case 1:
+                ret = ldub_raw(T0);
+                break;
+            case 2:
+                ret = lduw_raw(T0 & ~1);
+                break;
+            case 4:
+                ret = ldl_raw(T0 & ~3);
+                break;
+            default:
+            case 8:
+                ret = ldq_raw(T0 & ~7);
+                break;
+            }
+        }
+        break;
+    case 0x81: // Secondary
+    case 0x83: // Secondary no-fault
+    case 0x89: // Secondary LE
+    case 0x8b: // Secondary no-fault LE
+        // XXX
+        break;
+    default:
+        break;
+    }
+
+    /* Convert from little endian */
+    switch (asi) {
+    case 0x88: // Primary LE
+    case 0x89: // Secondary LE
+    case 0x8a: // Primary no-fault LE
+    case 0x8b: // Secondary no-fault LE
+        switch(size) {
+        case 2:
+            ret = bswap16(ret);
+        case 4:
+            ret = bswap32(ret);
+        case 8:
+            ret = bswap64(ret);
+        default:
+            break;
+        }
+    default:
+        break;
+    }
+
+    /* Convert to signed number */
+    if (sign) {
+        switch(size) {
+        case 1:
+            ret = (int8_t) ret;
+        case 2:
+            ret = (int16_t) ret;
+        case 4:
+            ret = (int32_t) ret;
+        default:
+            break;
+        }
+    }
+    T1 = ret;
+}
+
+void helper_st_asi(int asi, int size)
+{
+    if (asi < 0x80)
+        raise_exception(TT_PRIV_ACT);
+
+    /* Convert to little endian */
+    switch (asi) {
+    case 0x88: // Primary LE
+    case 0x89: // Secondary LE
+        switch(size) {
+        case 2:
+            T0 = bswap16(T0);
+        case 4:
+            T0 = bswap32(T0);
+        case 8:
+            T0 = bswap64(T0);
+        default:
+            break;
+        }
+    default:
+        break;
+    }
+
+    switch(asi) {
+    case 0x80: // Primary
+    case 0x88: // Primary LE
+        {
+            switch(size) {
+            case 1:
+                stb_raw(T0, T1);
+                break;
+            case 2:
+                stw_raw(T0 & ~1, T1);
+                break;
+            case 4:
+                stl_raw(T0 & ~3, T1);
+                break;
+            case 8:
+            default:
+                stq_raw(T0 & ~7, T1);
+                break;
+            }
+        }
+        break;
+    case 0x81: // Secondary
+    case 0x89: // Secondary LE
+        // XXX
+        return;
+
+    case 0x82: // Primary no-fault, RO
+    case 0x83: // Secondary no-fault, RO
+    case 0x8a: // Primary no-fault LE, RO
+    case 0x8b: // Secondary no-fault LE, RO
+    default:
+        do_unassigned_access(T0, 1, 0, 1);
+        return;
+    }
+}
+
+#else /* CONFIG_USER_ONLY */
 
 void helper_ld_asi(int asi, int size, int sign)
 {
@@ -432,8 +647,50 @@ void helper_ld_asi(int asi, int size, int sign)
         raise_exception(TT_PRIV_ACT);
 
     switch (asi) {
+    case 0x10: // As if user primary
+    case 0x18: // As if user primary LE
+    case 0x80: // Primary
+    case 0x82: // Primary no-fault
+    case 0x88: // Primary LE
+    case 0x8a: // Primary no-fault LE
+        if ((asi & 0x80) && (env->pstate & PS_PRIV)) {
+            switch(size) {
+            case 1:
+                ret = ldub_kernel(T0);
+                break;
+            case 2:
+                ret = lduw_kernel(T0 & ~1);
+                break;
+            case 4:
+                ret = ldl_kernel(T0 & ~3);
+                break;
+            default:
+            case 8:
+                ret = ldq_kernel(T0 & ~7);
+                break;
+            }
+        } else {
+            switch(size) {
+            case 1:
+                ret = ldub_user(T0);
+                break;
+            case 2:
+                ret = lduw_user(T0 & ~1);
+                break;
+            case 4:
+                ret = ldl_user(T0 & ~3);
+                break;
+            default:
+            case 8:
+                ret = ldq_user(T0 & ~7);
+                break;
+            }
+        }
+        break;
     case 0x14: // Bypass
     case 0x15: // Bypass, non-cacheable
+    case 0x1c: // Bypass LE
+    case 0x1d: // Bypass, non-cacheable LE
         {
             switch(size) {
             case 1:
@@ -454,20 +711,14 @@ void helper_ld_asi(int asi, int size, int sign)
         }
     case 0x04: // Nucleus
     case 0x0c: // Nucleus Little Endian (LE)
-    case 0x10: // As if user primary
     case 0x11: // As if user secondary
-    case 0x18: // As if user primary LE
     case 0x19: // As if user secondary LE
-    case 0x1c: // Bypass LE
-    case 0x1d: // Bypass, non-cacheable LE
     case 0x24: // Nucleus quad LDD 128 bit atomic
     case 0x2c: // Nucleus quad LDD 128 bit atomic
     case 0x4a: // UPA config
-    case 0x82: // Primary no-fault
+    case 0x81: // Secondary
     case 0x83: // Secondary no-fault
-    case 0x88: // Primary LE
     case 0x89: // Secondary LE
-    case 0x8a: // Primary no-fault LE
     case 0x8b: // Secondary no-fault LE
         // XXX
         break;
@@ -540,17 +791,120 @@ void helper_ld_asi(int asi, int size, int sign)
         ret = 0;
         break;
     }
+
+    /* Convert from little endian */
+    switch (asi) {
+    case 0x0c: // Nucleus Little Endian (LE)
+    case 0x18: // As if user primary LE
+    case 0x19: // As if user secondary LE
+    case 0x1c: // Bypass LE
+    case 0x1d: // Bypass, non-cacheable LE
+    case 0x88: // Primary LE
+    case 0x89: // Secondary LE
+    case 0x8a: // Primary no-fault LE
+    case 0x8b: // Secondary no-fault LE
+        switch(size) {
+        case 2:
+            ret = bswap16(ret);
+        case 4:
+            ret = bswap32(ret);
+        case 8:
+            ret = bswap64(ret);
+        default:
+            break;
+        }
+    default:
+        break;
+    }
+
+    /* Convert to signed number */
+    if (sign) {
+        switch(size) {
+        case 1:
+            ret = (int8_t) ret;
+        case 2:
+            ret = (int16_t) ret;
+        case 4:
+            ret = (int32_t) ret;
+        default:
+            break;
+        }
+    }
     T1 = ret;
 }
 
-void helper_st_asi(int asi, int size, int sign)
+void helper_st_asi(int asi, int size)
 {
     if (asi < 0x80 && (env->pstate & PS_PRIV) == 0)
         raise_exception(TT_PRIV_ACT);
 
+    /* Convert to little endian */
+    switch (asi) {
+    case 0x0c: // Nucleus Little Endian (LE)
+    case 0x18: // As if user primary LE
+    case 0x19: // As if user secondary LE
+    case 0x1c: // Bypass LE
+    case 0x1d: // Bypass, non-cacheable LE
+    case 0x81: // Secondary
+    case 0x88: // Primary LE
+    case 0x89: // Secondary LE
+        switch(size) {
+        case 2:
+            T0 = bswap16(T0);
+        case 4:
+            T0 = bswap32(T0);
+        case 8:
+            T0 = bswap64(T0);
+        default:
+            break;
+        }
+    default:
+        break;
+    }
+
     switch(asi) {
+    case 0x10: // As if user primary
+    case 0x18: // As if user primary LE
+    case 0x80: // Primary
+    case 0x88: // Primary LE
+        if ((asi & 0x80) && (env->pstate & PS_PRIV)) {
+            switch(size) {
+            case 1:
+                stb_kernel(T0, T1);
+                break;
+            case 2:
+                stw_kernel(T0 & ~1, T1);
+                break;
+            case 4:
+                stl_kernel(T0 & ~3, T1);
+                break;
+            case 8:
+            default:
+                stq_kernel(T0 & ~7, T1);
+                break;
+            }
+        } else {
+            switch(size) {
+            case 1:
+                stb_user(T0, T1);
+                break;
+            case 2:
+                stw_user(T0 & ~1, T1);
+                break;
+            case 4:
+                stl_user(T0 & ~3, T1);
+                break;
+            case 8:
+            default:
+                stq_user(T0 & ~7, T1);
+                break;
+            }
+        }
+        break;
     case 0x14: // Bypass
     case 0x15: // Bypass, non-cacheable
+    case 0x1c: // Bypass LE
+    case 0x1d: // Bypass, non-cacheable LE
         {
             switch(size) {
             case 1:
@@ -571,16 +925,11 @@ void helper_st_asi(int asi, int size, int sign)
         return;
     case 0x04: // Nucleus
     case 0x0c: // Nucleus Little Endian (LE)
-    case 0x10: // As if user primary
     case 0x11: // As if user secondary
-    case 0x18: // As if user primary LE
     case 0x19: // As if user secondary LE
-    case 0x1c: // Bypass LE
-    case 0x1d: // Bypass, non-cacheable LE
     case 0x24: // Nucleus quad LDD 128 bit atomic
     case 0x2c: // Nucleus quad LDD 128 bit atomic
     case 0x4a: // UPA config
-    case 0x88: // Primary LE
     case 0x89: // Secondary LE
         // XXX
         return;
@@ -756,8 +1105,8 @@ void helper_st_asi(int asi, int size, int sign)
         return;
     }
 }
-#endif
-#endif /* !CONFIG_USER_ONLY */
+#endif /* CONFIG_USER_ONLY */
+#endif /* TARGET_SPARC64 */
 
 #ifndef TARGET_SPARC64
 void helper_rett()
index ae63181..c0cf043 100644 (file)
@@ -76,33 +76,6 @@ void OPPROTO glue(op_lddf, MEMSUFFIX) (void)
 }
 
 #ifdef TARGET_SPARC64
-/* XXX: Should be Atomically */
-/* XXX: There are no cas[x] instructions, only cas[x]a */
-void OPPROTO glue(op_cas, MEMSUFFIX)(void)
-{
-    uint32_t tmp;
-
-    tmp = glue(ldl, MEMSUFFIX)(T0);
-    T2 &=  0xffffffffULL;
-    if (tmp == (T1 & 0xffffffffULL)) {
-        glue(stl, MEMSUFFIX)(T0, T2);
-    }
-    T2 = tmp;
-}
-
-void OPPROTO glue(op_casx, MEMSUFFIX)(void)
-{
-    uint64_t tmp;
-
-    // XXX
-    tmp = (uint64_t)glue(ldl, MEMSUFFIX)(T0) << 32;
-    tmp |= glue(ldl, MEMSUFFIX)(T0);
-    if (tmp == T1) {
-        glue(stq, MEMSUFFIX)(T0, T2);
-    }
-    T2 = tmp;
-}
-
 void OPPROTO glue(op_lduw, MEMSUFFIX)(void)
 {
     T1 = (uint64_t)(glue(ldl, MEMSUFFIX)(T0) & 0xffffffff);
index a52d671..d617b91 100644 (file)
@@ -353,112 +353,27 @@ GEN32(gen_op_store_DT1_fpr, gen_op_store_DT1_fpr_fprf);
 #endif
 #endif
 
-#ifdef TARGET_SPARC64
-// 'a' versions allowed to user depending on asi
-#if defined(CONFIG_USER_ONLY)
+/* moves */
+#ifdef CONFIG_USER_ONLY
 #define supervisor(dc) 0
+#ifdef TARGET_SPARC64
 #define hypervisor(dc) 0
+#endif
 #define gen_op_ldst(name)        gen_op_##name##_raw()
-#define OP_LD_TABLE(width)                                              \
-    static void gen_op_##width##a(int insn, int is_ld, int size, int sign) \
-    {                                                                   \
-        int asi, offset;                                                \
-                                                                        \
-        if (IS_IMM) {                                                   \
-            offset = GET_FIELD(insn, 25, 31);                           \
-            if (is_ld)                                                  \
-                gen_op_ld_asi_reg(offset, size, sign);                  \
-            else                                                        \
-                gen_op_st_asi_reg(offset, size, sign);                  \
-            return;                                                     \
-        }                                                               \
-        asi = GET_FIELD(insn, 19, 26);                                  \
-        switch (asi) {                                                  \
-        case 0x80: /* Primary address space */                          \
-            gen_op_##width##_raw();                                     \
-            break;                                                      \
-        case 0x82: /* Primary address space, non-faulting load */       \
-            gen_op_##width##_raw();                                     \
-            break;                                                      \
-        default:                                                        \
-            break;                                                      \
-        }                                                               \
-    }
-
 #else
+#define supervisor(dc) (dc->mem_idx == 1)
+#ifdef TARGET_SPARC64
+#define hypervisor(dc) (dc->mem_idx == 2)
+#endif
 #define gen_op_ldst(name)        (*gen_op_##name[dc->mem_idx])()
 #define OP_LD_TABLE(width)                                              \
     static GenOpFunc * const gen_op_##width[] = {                       \
         &gen_op_##width##_user,                                         \
         &gen_op_##width##_kernel,                                       \
-    };                                                                  \
-                                                                        \
-    static void gen_op_##width##a(int insn, int is_ld, int size, int sign) \
-    {                                                                   \
-        int asi, offset;                                                \
-                                                                        \
-        if (IS_IMM) {                                                   \
-            offset = GET_FIELD(insn, 25, 31);                           \
-            if (is_ld)                                                  \
-                gen_op_ld_asi_reg(offset, size, sign);                  \
-            else                                                        \
-                gen_op_st_asi_reg(offset, size, sign);                  \
-            return;                                                     \
-        }                                                               \
-        asi = GET_FIELD(insn, 19, 26);                                  \
-        if (is_ld)                                                      \
-            gen_op_ld_asi(asi, size, sign);                             \
-        else                                                            \
-            gen_op_st_asi(asi, size, sign);                             \
-    }
-
-#define supervisor(dc) (dc->mem_idx == 1)
-#define hypervisor(dc) (dc->mem_idx == 2)
-#endif
-#else
-#if defined(CONFIG_USER_ONLY)
-#define gen_op_ldst(name)        gen_op_##name##_raw()
-#define OP_LD_TABLE(width)
-#define supervisor(dc) 0
-#else
-#define gen_op_ldst(name)        (*gen_op_##name[dc->mem_idx])()
-#define OP_LD_TABLE(width)                                                    \
-static GenOpFunc * const gen_op_##width[] = {                                 \
-    &gen_op_##width##_user,                                                   \
-    &gen_op_##width##_kernel,                                                 \
-};                                                                            \
-                                                                              \
-static void gen_op_##width##a(int insn, int is_ld, int size, int sign)        \
-{                                                                             \
-    int asi;                                                                  \
-                                                                              \
-    asi = GET_FIELD(insn, 19, 26);                                            \
-    switch (asi) {                                                            \
-        case 10: /* User data access */                                       \
-            gen_op_##width##_user();                                          \
-            break;                                                            \
-        case 11: /* Supervisor data access */                                 \
-            gen_op_##width##_kernel();                                        \
-            break;                                                            \
-        case 0x20 ... 0x2f: /* MMU passthrough */                             \
-            if (is_ld)                                                        \
-                gen_op_ld_asi(asi, size, sign);                               \
-            else                                                              \
-                gen_op_st_asi(asi, size, sign);                               \
-            break;                                                            \
-        default:                                                              \
-            if (is_ld)                                                        \
-                gen_op_ld_asi(asi, size, sign);                               \
-            else                                                              \
-                gen_op_st_asi(asi, size, sign);                               \
-            break;                                                            \
-    }                                                                         \
-}
-
-#define supervisor(dc) (dc->mem_idx == 1)
-#endif
+    };
 #endif
 
+#ifndef CONFIG_USER_ONLY
 OP_LD_TABLE(ld);
 OP_LD_TABLE(st);
 OP_LD_TABLE(ldub);
@@ -481,8 +396,164 @@ OP_LD_TABLE(lduw);
 OP_LD_TABLE(ldsw);
 OP_LD_TABLE(ldx);
 OP_LD_TABLE(stx);
-OP_LD_TABLE(cas);
-OP_LD_TABLE(casx);
+#endif
+#endif
+
+/* asi moves */
+#ifdef TARGET_SPARC64
+static inline void gen_ld_asi(int insn, int size, int sign)
+{
+    int asi, offset;
+
+    if (IS_IMM) {
+        offset = GET_FIELD(insn, 25, 31);
+        gen_op_ld_asi_reg(offset, size, sign);
+    } else {
+        asi = GET_FIELD(insn, 19, 26);
+        gen_op_ld_asi(asi, size, sign);
+    }
+}
+
+static inline void gen_st_asi(int insn, int size)
+{
+    int asi, offset;
+
+    if (IS_IMM) {
+        offset = GET_FIELD(insn, 25, 31);
+        gen_op_st_asi_reg(offset, size);
+    } else {
+        asi = GET_FIELD(insn, 19, 26);
+        gen_op_st_asi(asi, size);
+    }
+}
+
+static inline void gen_swap_asi(int insn)
+{
+    int asi, offset;
+
+    if (IS_IMM) {
+        offset = GET_FIELD(insn, 25, 31);
+        gen_op_swap_asi_reg(offset);
+    } else {
+        asi = GET_FIELD(insn, 19, 26);
+        gen_op_swap_asi(asi);
+    }
+}
+
+static inline void gen_ldstub_asi(int insn)
+{
+    int asi, offset;
+
+    if (IS_IMM) {
+        offset = GET_FIELD(insn, 25, 31);
+        gen_op_ldstub_asi_reg(offset);
+    } else {
+        asi = GET_FIELD(insn, 19, 26);
+        gen_op_ldstub_asi(asi);
+    }
+}
+
+static inline void gen_ldda_asi(int insn)
+{
+    int asi, offset;
+
+    if (IS_IMM) {
+        offset = GET_FIELD(insn, 25, 31);
+        gen_op_ldda_asi_reg(offset);
+    } else {
+        asi = GET_FIELD(insn, 19, 26);
+        gen_op_ldda_asi(asi);
+    }
+}
+
+static inline void gen_stda_asi(int insn)
+{
+    int asi, offset;
+
+    if (IS_IMM) {
+        offset = GET_FIELD(insn, 25, 31);
+        gen_op_stda_asi_reg(offset);
+    } else {
+        asi = GET_FIELD(insn, 19, 26);
+        gen_op_stda_asi(asi);
+    }
+}
+
+static inline void gen_cas_asi(int insn)
+{
+    int asi, offset;
+
+    if (IS_IMM) {
+        offset = GET_FIELD(insn, 25, 31);
+        gen_op_cas_asi_reg(offset);
+    } else {
+        asi = GET_FIELD(insn, 19, 26);
+        gen_op_cas_asi(asi);
+    }
+}
+
+static inline void gen_casx_asi(int insn)
+{
+    int asi, offset;
+
+    if (IS_IMM) {
+        offset = GET_FIELD(insn, 25, 31);
+        gen_op_casx_asi_reg(offset);
+    } else {
+        asi = GET_FIELD(insn, 19, 26);
+        gen_op_casx_asi(asi);
+    }
+}
+
+#elif !defined(CONFIG_USER_ONLY)
+
+static inline void gen_ld_asi(int insn, int size, int sign)
+{
+    int asi;
+
+    asi = GET_FIELD(insn, 19, 26);
+    gen_op_ld_asi(asi, size, sign);
+}
+
+static inline void gen_st_asi(int insn, int size)
+{
+    int asi;
+
+    asi = GET_FIELD(insn, 19, 26);
+    gen_op_st_asi(asi, size);
+}
+
+static inline void gen_ldstub_asi(int insn)
+{
+    int asi;
+
+    asi = GET_FIELD(insn, 19, 26);
+    gen_op_ldstub_asi(asi);
+}
+
+static inline void gen_swap_asi(int insn)
+{
+    int asi;
+
+    asi = GET_FIELD(insn, 19, 26);
+    gen_op_swap_asi(asi);
+}
+
+static inline void gen_ldda_asi(int insn)
+{
+    int asi;
+
+    asi = GET_FIELD(insn, 19, 26);
+    gen_op_ld_asi(asi, 8, 0);
+}
+
+static inline void gen_stda_asi(int insn)
+{
+    int asi;
+
+    asi = GET_FIELD(insn, 19, 26);
+    gen_op_st_asi(asi, 8);
+}
 #endif
 
 static inline void gen_movl_imm_TN(int reg, uint32_t imm)
@@ -2796,7 +2867,12 @@ static void disas_sparc_insn(DisasContext * dc)
             rs1 = GET_FIELD(insn, 13, 17);
             save_state(dc);
             gen_movl_reg_T0(rs1);
-            if (IS_IMM) {       /* immediate */
+            if (xop == 0x3c || xop == 0x3e)
+            {
+                rs2 = GET_FIELD(insn, 27, 31);
+                gen_movl_reg_T1(rs2);
+            }
+            else if (IS_IMM) {       /* immediate */
                 rs2 = GET_FIELDs(insn, 19, 31);
 #if defined(OPTIM)
                 if (rs2 != 0) {
@@ -2873,16 +2949,10 @@ static void disas_sparc_insn(DisasContext * dc)
                         goto illegal_insn;
                     if (!supervisor(dc))
                         goto priv_insn;
-#ifdef CONFIG_USER_ONLY
+#elif CONFIG_USER_ONLY
                     gen_op_check_align_T0_3();
 #endif
-                    gen_op_lda(insn, 1, 4, 0);
-#else
-#ifdef CONFIG_USER_ONLY
-                    gen_op_check_align_T0_3();
-#endif
-                    gen_op_lduwa(insn, 1, 4, 0);
-#endif
+                    gen_ld_asi(insn, 4, 0);
                     break;
                 case 0x11:      /* load unsigned byte alternate */
 #ifndef TARGET_SPARC64
@@ -2891,7 +2961,7 @@ static void disas_sparc_insn(DisasContext * dc)
                     if (!supervisor(dc))
                         goto priv_insn;
 #endif
-                    gen_op_lduba(insn, 1, 1, 0);
+                    gen_ld_asi(insn, 1, 0);
                     break;
                 case 0x12:      /* load unsigned halfword alternate */
 #ifndef TARGET_SPARC64
@@ -2899,11 +2969,10 @@ static void disas_sparc_insn(DisasContext * dc)
                         goto illegal_insn;
                     if (!supervisor(dc))
                         goto priv_insn;
-#endif
-#ifdef CONFIG_USER_ONLY
+#elif CONFIG_USER_ONLY
                     gen_op_check_align_T0_1();
 #endif
-                    gen_op_lduha(insn, 1, 2, 0);
+                    gen_ld_asi(insn, 2, 0);
                     break;
                 case 0x13:      /* load double word alternate */
 #ifndef TARGET_SPARC64
@@ -2915,7 +2984,7 @@ static void disas_sparc_insn(DisasContext * dc)
                     if (rd & 1)
                         goto illegal_insn;
                     gen_op_check_align_T0_7();
-                    gen_op_ldda(insn, 1, 8, 0);
+                    gen_ldda_asi(insn);
                     gen_movl_T0_reg(rd + 1);
                     break;
                 case 0x19:      /* load signed byte alternate */
@@ -2925,7 +2994,7 @@ static void disas_sparc_insn(DisasContext * dc)
                     if (!supervisor(dc))
                         goto priv_insn;
 #endif
-                    gen_op_ldsba(insn, 1, 1, 1);
+                    gen_ld_asi(insn, 1, 1);
                     break;
                 case 0x1a:      /* load signed halfword alternate */
 #ifndef TARGET_SPARC64
@@ -2933,11 +3002,10 @@ static void disas_sparc_insn(DisasContext * dc)
                         goto illegal_insn;
                     if (!supervisor(dc))
                         goto priv_insn;
-#endif
-#ifdef CONFIG_USER_ONLY
+#elif CONFIG_USER_ONLY
                     gen_op_check_align_T0_1();
 #endif
-                    gen_op_ldsha(insn, 1, 2 ,1);
+                    gen_ld_asi(insn, 2, 1);
                     break;
                 case 0x1d:      /* ldstuba -- XXX: should be atomically */
 #ifndef TARGET_SPARC64
@@ -2946,7 +3014,7 @@ static void disas_sparc_insn(DisasContext * dc)
                     if (!supervisor(dc))
                         goto priv_insn;
 #endif
-                    gen_op_ldstuba(insn, 1, 1, 0);
+                    gen_ldstub_asi(insn);
                     break;
                 case 0x1f:      /* swap reg with alt. memory. Also atomically */
 #ifndef TARGET_SPARC64
@@ -2954,12 +3022,11 @@ static void disas_sparc_insn(DisasContext * dc)
                         goto illegal_insn;
                     if (!supervisor(dc))
                         goto priv_insn;
-#endif
-                    gen_movl_reg_T1(rd);
-#ifdef CONFIG_USER_ONLY
+#elif CONFIG_USER_ONLY
                     gen_op_check_align_T0_3();
 #endif
-                    gen_op_swapa(insn, 1, 4, 0);
+                    gen_movl_reg_T1(rd);
+                    gen_swap_asi(insn);
                     break;
 
 #ifndef TARGET_SPARC64
@@ -2967,17 +3034,6 @@ static void disas_sparc_insn(DisasContext * dc)
                 case 0x31: /* ldcsr */
                 case 0x33: /* lddc */
                     goto ncp_insn;
-                    /* avoid warnings */
-                    (void) &gen_op_stfa;
-                    (void) &gen_op_stdfa;
-                    (void) &gen_op_ldfa;
-                    (void) &gen_op_lddfa;
-#else
-                    (void) &gen_op_lda;
-#if !defined(CONFIG_USER_ONLY)
-                    (void) &gen_op_cas;
-                    (void) &gen_op_casx;
-#endif
 #endif
 #endif
 #ifdef TARGET_SPARC64
@@ -2995,11 +3051,11 @@ static void disas_sparc_insn(DisasContext * dc)
 #ifdef CONFIG_USER_ONLY
                     gen_op_check_align_T0_3();
 #endif
-                    gen_op_ldswa(insn, 1, 4, 1);
+                    gen_ld_asi(insn, 4, 1);
                     break;
                 case 0x1b: /* V9 ldxa */
                     gen_op_check_align_T0_7();
-                    gen_op_ldxa(insn, 1, 8, 0);
+                    gen_ld_asi(insn, 8, 0);
                     break;
                 case 0x2d: /* V9 prefetch, no effect */
                     goto skip_move;
@@ -3007,13 +3063,12 @@ static void disas_sparc_insn(DisasContext * dc)
 #ifdef CONFIG_USER_ONLY
                     gen_op_check_align_T0_3();
 #endif
-                    gen_op_ldfa(insn, 1, 8, 0); // XXX
-                    break;
+                    gen_ld_asi(insn, 8, 0); // XXX
+                    goto skip_move;
                 case 0x33: /* V9 lddfa */
                     gen_op_check_align_T0_7();
-                    gen_op_lddfa(insn, 1, 8, 0); // XXX
-
-                    break;
+                    gen_ld_asi(insn, 8, 0); // XXX
+                    goto skip_move;
                 case 0x3d: /* V9 prefetcha, no effect */
                     goto skip_move;
                 case 0x32: /* V9 ldqfa */
@@ -3092,7 +3147,7 @@ static void disas_sparc_insn(DisasContext * dc)
 #ifdef CONFIG_USER_ONLY
                     gen_op_check_align_T0_3();
 #endif
-                    gen_op_sta(insn, 0, 4, 0);
+                    gen_st_asi(insn, 4);
                     break;
                 case 0x15:
 #ifndef TARGET_SPARC64
@@ -3101,7 +3156,7 @@ static void disas_sparc_insn(DisasContext * dc)
                     if (!supervisor(dc))
                         goto priv_insn;
 #endif
-                    gen_op_stba(insn, 0, 1, 0);
+                    gen_st_asi(insn, 1);
                     break;
                 case 0x16:
 #ifndef TARGET_SPARC64
@@ -3113,7 +3168,7 @@ static void disas_sparc_insn(DisasContext * dc)
 #ifdef CONFIG_USER_ONLY
                     gen_op_check_align_T0_1();
 #endif
-                    gen_op_stha(insn, 0, 2, 0);
+                    gen_st_asi(insn, 2);
                     break;
                 case 0x17:
 #ifndef TARGET_SPARC64
@@ -3127,7 +3182,7 @@ static void disas_sparc_insn(DisasContext * dc)
                     gen_op_check_align_T0_7();
                     flush_T2(dc);
                     gen_movl_reg_T2(rd + 1);
-                    gen_op_stda(insn, 0, 8, 0);
+                    gen_stda_asi(insn);
                     break;
 #endif
 #ifdef TARGET_SPARC64
@@ -3137,7 +3192,7 @@ static void disas_sparc_insn(DisasContext * dc)
                     break;
                 case 0x1e: /* V9 stxa */
                     gen_op_check_align_T0_7();
-                    gen_op_stxa(insn, 0, 8, 0); // XXX
+                    gen_st_asi(insn, 8);
                     break;
 #endif
                 default:
@@ -3184,21 +3239,27 @@ static void disas_sparc_insn(DisasContext * dc)
 #ifdef CONFIG_USER_ONLY
                     gen_op_check_align_T0_3();
 #endif
-                    gen_op_stfa(insn, 0, 0, 0); // XXX
+                    gen_st_asi(insn, 0); // XXX
                     break;
                 case 0x37: /* V9 stdfa */
                     gen_op_check_align_T0_7();
-                    gen_op_stdfa(insn, 0, 0, 0); // XXX
+                    gen_st_asi(insn, 0); // XXX
                     break;
                 case 0x3c: /* V9 casa */
 #ifdef CONFIG_USER_ONLY
                     gen_op_check_align_T0_3();
 #endif
-                    gen_op_casa(insn, 0, 4, 0); // XXX
+                    flush_T2(dc);
+                    gen_movl_reg_T2(rd);
+                    gen_cas_asi(insn);
+                    gen_movl_T1_reg(rd);
                     break;
                 case 0x3e: /* V9 casxa */
                     gen_op_check_align_T0_7();
-                    gen_op_casxa(insn, 0, 8, 0); // XXX
+                    flush_T2(dc);
+                    gen_movl_reg_T2(rd);
+                    gen_casx_asi(insn);
+                    gen_movl_T1_reg(rd);
                     break;
                 case 0x36: /* V9 stqfa */
                     goto nfpu_insn;