]> git.proxmox.com Git - qemu.git/blobdiff - target-microblaze/translate.c
slirp: Replace m_freem with m_free
[qemu.git] / target-microblaze / translate.c
index efa5d56e195e2742de195cd6d7c16b9c7ef3a82f..31e8306ef30cf7b972b0ea3fd8d414d2a6dcee4d 100644 (file)
@@ -25,7 +25,6 @@
 #include <assert.h>
 
 #include "cpu.h"
-#include "exec-all.h"
 #include "disas.h"
 #include "tcg-op.h"
 #include "helper.h"
@@ -63,8 +62,7 @@ static TCGv env_iflags;
 /* This is the state at translation time.  */
 typedef struct DisasContext {
     CPUState *env;
-    target_ulong pc, ppc;
-    target_ulong cache_pc;
+    target_ulong pc;
 
     /* Decoder.  */
     int type_b;
@@ -79,9 +77,10 @@ typedef struct DisasContext {
     unsigned int clear_imm;
     int is_jmp;
 
-#define JMP_NOJMP    0
-#define JMP_DIRECT   1
-#define JMP_INDIRECT 2
+#define JMP_NOJMP     0
+#define JMP_DIRECT    1
+#define JMP_DIRECT_CC 2
+#define JMP_INDIRECT  3
     unsigned int jmp;
     uint32_t jmp_pc;
 
@@ -146,13 +145,38 @@ static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest)
     if ((tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK)) {
         tcg_gen_goto_tb(n);
         tcg_gen_movi_tl(cpu_SR[SR_PC], dest);
-        tcg_gen_exit_tb((long)tb + n);
+        tcg_gen_exit_tb((tcg_target_long)tb + n);
     } else {
         tcg_gen_movi_tl(cpu_SR[SR_PC], dest);
         tcg_gen_exit_tb(0);
     }
 }
 
+static void read_carry(DisasContext *dc, TCGv d)
+{
+    tcg_gen_shri_tl(d, cpu_SR[SR_MSR], 31);
+}
+
+static void write_carry(DisasContext *dc, TCGv v)
+{
+    TCGv t0 = tcg_temp_new();
+    tcg_gen_shli_tl(t0, v, 31);
+    tcg_gen_sari_tl(t0, t0, 31);
+    tcg_gen_andi_tl(t0, t0, (MSR_C | MSR_CC));
+    tcg_gen_andi_tl(cpu_SR[SR_MSR], cpu_SR[SR_MSR],
+                    ~(MSR_C | MSR_CC));
+    tcg_gen_or_tl(cpu_SR[SR_MSR], cpu_SR[SR_MSR], t0);
+    tcg_temp_free(t0);
+}
+
+/* True if ALU operand b is a small immediate that may deserve
+   faster treatment.  */
+static inline int dec_alu_op_b_is_small_imm(DisasContext *dc)
+{
+    /* Immediate insn without the imm prefix ?  */
+    return dc->type_b && !(dc->tb_flags & IMM_FLAG);
+}
+
 static inline TCGv *dec_alu_op_b(DisasContext *dc)
 {
     if (dc->type_b) {
@@ -168,6 +192,7 @@ static inline TCGv *dec_alu_op_b(DisasContext *dc)
 static void dec_add(DisasContext *dc)
 {
     unsigned int k, c;
+    TCGv cf;
 
     k = dc->opcode & 4;
     c = dc->opcode & 2;
@@ -176,22 +201,52 @@ static void dec_add(DisasContext *dc)
             dc->type_b ? "i" : "", k ? "k" : "", c ? "c" : "",
             dc->rd, dc->ra, dc->rb);
 
-    if (k && !c && dc->rd)
+    /* Take care of the easy cases first.  */
+    if (k) {
+        /* k - keep carry, no need to update MSR.  */
+        /* If rd == r0, it's a nop.  */
+        if (dc->rd) {
+            tcg_gen_add_tl(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc)));
+
+            if (c) {
+                /* c - Add carry into the result.  */
+                cf = tcg_temp_new();
+
+                read_carry(dc, cf);
+                tcg_gen_add_tl(cpu_R[dc->rd], cpu_R[dc->rd], cf);
+                tcg_temp_free(cf);
+            }
+        }
+        return;
+    }
+
+    /* From now on, we can assume k is zero.  So we need to update MSR.  */
+    /* Extract carry.  */
+    cf = tcg_temp_new();
+    if (c) {
+        read_carry(dc, cf);
+    } else {
+        tcg_gen_movi_tl(cf, 0);
+    }
+
+    if (dc->rd) {
+        TCGv ncf = tcg_temp_new();
+        gen_helper_carry(ncf, cpu_R[dc->ra], *(dec_alu_op_b(dc)), cf);
         tcg_gen_add_tl(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc)));
-    else if (dc->rd)
-        gen_helper_addkc(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc)),
-                         tcg_const_tl(k), tcg_const_tl(c));
-    else {
-        TCGv d = tcg_temp_new();
-        gen_helper_addkc(d, cpu_R[dc->ra], *(dec_alu_op_b(dc)),
-                         tcg_const_tl(k), tcg_const_tl(c));
-        tcg_temp_free(d);
+        tcg_gen_add_tl(cpu_R[dc->rd], cpu_R[dc->rd], cf);
+        write_carry(dc, ncf);
+        tcg_temp_free(ncf);
+    } else {
+        gen_helper_carry(cf, cpu_R[dc->ra], *(dec_alu_op_b(dc)), cf);
+        write_carry(dc, cf);
     }
+    tcg_temp_free(cf);
 }
 
 static void dec_sub(DisasContext *dc)
 {
     unsigned int u, cmp, k, c;
+    TCGv cf, na;
 
     u = dc->imm & 2;
     k = dc->opcode & 4;
@@ -206,24 +261,57 @@ static void dec_sub(DisasContext *dc)
             else
                 gen_helper_cmp(cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]);
         }
-    } else {
-        LOG_DIS("sub%s%s r%d, r%d r%d\n",
-                 k ? "k" : "",  c ? "c" : "", dc->rd, dc->ra, dc->rb);
+        return;
+    }
 
-        if (!k || c) {
-            TCGv t;
-            t = tcg_temp_new();
-            if (dc->rd)
-                gen_helper_subkc(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc)),
-                                 tcg_const_tl(k), tcg_const_tl(c));
-            else
-                gen_helper_subkc(t, cpu_R[dc->ra], *(dec_alu_op_b(dc)),
-                                 tcg_const_tl(k), tcg_const_tl(c));
-            tcg_temp_free(t);
-        }
-        else if (dc->rd)
+    LOG_DIS("sub%s%s r%d, r%d r%d\n",
+             k ? "k" : "",  c ? "c" : "", dc->rd, dc->ra, dc->rb);
+
+    /* Take care of the easy cases first.  */
+    if (k) {
+        /* k - keep carry, no need to update MSR.  */
+        /* If rd == r0, it's a nop.  */
+        if (dc->rd) {
             tcg_gen_sub_tl(cpu_R[dc->rd], *(dec_alu_op_b(dc)), cpu_R[dc->ra]);
+
+            if (c) {
+                /* c - Add carry into the result.  */
+                cf = tcg_temp_new();
+
+                read_carry(dc, cf);
+                tcg_gen_add_tl(cpu_R[dc->rd], cpu_R[dc->rd], cf);
+                tcg_temp_free(cf);
+            }
+        }
+        return;
+    }
+
+    /* From now on, we can assume k is zero.  So we need to update MSR.  */
+    /* Extract carry. And complement a into na.  */
+    cf = tcg_temp_new();
+    na = tcg_temp_new();
+    if (c) {
+        read_carry(dc, cf);
+    } else {
+        tcg_gen_movi_tl(cf, 1);
+    }
+
+    /* d = b + ~a + c. carry defaults to 1.  */
+    tcg_gen_not_tl(na, cpu_R[dc->ra]);
+
+    if (dc->rd) {
+        TCGv ncf = tcg_temp_new();
+        gen_helper_carry(ncf, na, *(dec_alu_op_b(dc)), cf);
+        tcg_gen_add_tl(cpu_R[dc->rd], na, *(dec_alu_op_b(dc)));
+        tcg_gen_add_tl(cpu_R[dc->rd], cpu_R[dc->rd], cf);
+        write_carry(dc, ncf);
+        tcg_temp_free(ncf);
+    } else {
+        gen_helper_carry(cf, na, *(dec_alu_op_b(dc)), cf);
+        write_carry(dc, cf);
     }
+    tcg_temp_free(cf);
+    tcg_temp_free(na);
 }
 
 static void dec_pattern(DisasContext *dc)
@@ -329,25 +417,6 @@ static void dec_xor(DisasContext *dc)
         tcg_gen_xor_tl(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc)));
 }
 
-static void read_carry(DisasContext *dc, TCGv d)
-{
-    tcg_gen_shri_tl(d, cpu_SR[SR_MSR], 31);
-}
-
-static void write_carry(DisasContext *dc, TCGv v)
-{
-    TCGv t0 = tcg_temp_new();
-    tcg_gen_shli_tl(t0, v, 31);
-    tcg_gen_sari_tl(t0, t0, 31);
-    tcg_gen_mov_tl(env_debug, t0);
-    tcg_gen_andi_tl(t0, t0, (MSR_C | MSR_CC));
-    tcg_gen_andi_tl(cpu_SR[SR_MSR], cpu_SR[SR_MSR],
-                    ~(MSR_C | MSR_CC));
-    tcg_gen_or_tl(cpu_SR[SR_MSR], cpu_SR[SR_MSR], t0);
-    tcg_temp_free(t0);
-}
-
-
 static inline void msr_read(DisasContext *dc, TCGv d)
 {
     tcg_gen_mov_tl(d, cpu_SR[SR_MSR]);
@@ -450,7 +519,7 @@ static void dec_msr(DisasContext *dc)
                 tcg_gen_mov_tl(cpu_SR[SR_ESR], cpu_R[dc->ra]);
                 break;
             case 0x7:
-                /* Ignored at the moment.  */
+                tcg_gen_andi_tl(cpu_SR[SR_FSR], cpu_R[dc->ra], 31);
                 break;
             default:
                 cpu_abort(dc->env, "unknown mts reg %x\n", sr);
@@ -473,7 +542,7 @@ static void dec_msr(DisasContext *dc)
                 tcg_gen_mov_tl(cpu_R[dc->rd], cpu_SR[SR_ESR]);
                 break;
              case 0x7:
-                tcg_gen_movi_tl(cpu_R[dc->rd], 0);
+                tcg_gen_mov_tl(cpu_R[dc->rd], cpu_SR[SR_FSR]);
                 break;
             case 0xb:
                 tcg_gen_mov_tl(cpu_R[dc->rd], cpu_SR[SR_BTR]);
@@ -744,10 +813,12 @@ static void dec_bit(DisasContext *dc)
 
 static inline void sync_jmpstate(DisasContext *dc)
 {
-    if (dc->jmp == JMP_DIRECT) {
-            dc->jmp = JMP_INDIRECT;
+    if (dc->jmp == JMP_DIRECT || dc->jmp == JMP_DIRECT_CC) {
+        if (dc->jmp == JMP_DIRECT) {
             tcg_gen_movi_tl(env_btaken, 1);
-            tcg_gen_movi_tl(env_btarget, dc->jmp_pc);
+        }
+        dc->jmp = JMP_INDIRECT;
+        tcg_gen_movi_tl(env_btarget, dc->jmp_pc);
     }
 }
 
@@ -778,8 +849,15 @@ static inline TCGv *compute_ldst_addr(DisasContext *dc, TCGv *t)
 {
     unsigned int extimm = dc->tb_flags & IMM_FLAG;
 
-    /* Treat the fast cases first.  */
+    /* Treat the common cases first.  */
     if (!dc->type_b) {
+        /* If any of the regs is r0, return a ptr to the other.  */
+        if (dc->ra == 0) {
+            return &cpu_R[dc->rb];
+        } else if (dc->rb == 0) {
+            return &cpu_R[dc->ra];
+        }
+
         *t = tcg_temp_new();
         tcg_gen_add_tl(*t, cpu_R[dc->ra], cpu_R[dc->rb]);
         return t;
@@ -800,12 +878,35 @@ static inline TCGv *compute_ldst_addr(DisasContext *dc, TCGv *t)
     return t;
 }
 
+static inline void dec_byteswap(DisasContext *dc, TCGv dst, TCGv src, int size)
+{
+    if (size == 4) {
+        tcg_gen_bswap32_tl(dst, src);
+    } else if (size == 2) {
+        TCGv t = tcg_temp_new();
+
+        /* bswap16 assumes the high bits are zero.  */
+        tcg_gen_andi_tl(t, src, 0xffff);
+        tcg_gen_bswap16_tl(dst, t);
+        tcg_temp_free(t);
+    } else {
+        /* Ignore.
+        cpu_abort(dc->env, "Invalid ldst byteswap size %d\n", size);
+        */
+    }
+}
+
 static void dec_load(DisasContext *dc)
 {
     TCGv t, *addr;
-    unsigned int size;
+    unsigned int size, rev = 0;
 
     size = 1 << (dc->opcode & 3);
+
+    if (!dc->type_b) {
+        rev = (dc->ir >> 9) & 1;
+    }
+
     if (size > 4 && (dc->tb_flags & MSR_EE_FLAG)
           && (dc->env->pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK)) {
         tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP);
@@ -813,10 +914,62 @@ static void dec_load(DisasContext *dc)
         return;
     }
 
-    LOG_DIS("l %x %d\n", dc->opcode, size);
+    LOG_DIS("l%d%s%s\n", size, dc->type_b ? "i" : "", rev ? "r" : "");
+
     t_sync_flags(dc);
     addr = compute_ldst_addr(dc, &t);
 
+    /*
+     * When doing reverse accesses we need to do two things.
+     *
+     * 1. Reverse the address wrt endianness.
+     * 2. Byteswap the data lanes on the way back into the CPU core.
+     */
+    if (rev && size != 4) {
+        /* Endian reverse the address. t is addr.  */
+        switch (size) {
+            case 1:
+            {
+                /* 00 -> 11
+                   01 -> 10
+                   10 -> 10
+                   11 -> 00 */
+                TCGv low = tcg_temp_new();
+
+                /* Force addr into the temp.  */
+                if (addr != &t) {
+                    t = tcg_temp_new();
+                    tcg_gen_mov_tl(t, *addr);
+                    addr = &t;
+                }
+
+                tcg_gen_andi_tl(low, t, 3);
+                tcg_gen_sub_tl(low, tcg_const_tl(3), low);
+                tcg_gen_andi_tl(t, t, ~3);
+                tcg_gen_or_tl(t, t, low);
+                tcg_gen_mov_tl(env_imm, t);
+                tcg_temp_free(low);
+                break;
+            }
+
+            case 2:
+                /* 00 -> 10
+                   10 -> 00.  */
+                /* Force addr into the temp.  */
+                if (addr != &t) {
+                    t = tcg_temp_new();
+                    tcg_gen_xori_tl(t, *addr, 2);
+                    addr = &t;
+                } else {
+                    tcg_gen_xori_tl(t, t, 2);
+                }
+                break;
+            default:
+                cpu_abort(dc->env, "Invalid reverse size\n");
+                break;
+        }
+    }
+
     /* If we get a fault on a dslot, the jmpstate better be in sync.  */
     sync_jmpstate(dc);
 
@@ -835,13 +988,22 @@ static void dec_load(DisasContext *dc)
         tcg_gen_movi_tl(cpu_SR[SR_PC], dc->pc);
         gen_helper_memalign(*addr, tcg_const_tl(dc->rd),
                             tcg_const_tl(0), tcg_const_tl(size - 1));
-        if (dc->rd)
-            tcg_gen_mov_tl(cpu_R[dc->rd], v);
+        if (dc->rd) {
+            if (rev) {
+                dec_byteswap(dc, cpu_R[dc->rd], v, size);
+            } else {
+                tcg_gen_mov_tl(cpu_R[dc->rd], v);
+            }
+        }
         tcg_temp_free(v);
     } else {
         if (dc->rd) {
             gen_load(dc, cpu_R[dc->rd], *addr, size);
+            if (rev) {
+                dec_byteswap(dc, cpu_R[dc->rd], cpu_R[dc->rd], size);
+            }
         } else {
+            /* We are loading into r0, no need to reverse.  */
             gen_load(dc, env_imm, *addr, size);
         }
     }
@@ -868,9 +1030,12 @@ static void gen_store(DisasContext *dc, TCGv addr, TCGv val,
 static void dec_store(DisasContext *dc)
 {
     TCGv t, *addr;
-    unsigned int size;
+    unsigned int size, rev = 0;
 
     size = 1 << (dc->opcode & 3);
+    if (!dc->type_b) {
+        rev = (dc->ir >> 9) & 1;
+    }
 
     if (size > 4 && (dc->tb_flags & MSR_EE_FLAG)
           && (dc->env->pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK)) {
@@ -879,19 +1044,83 @@ static void dec_store(DisasContext *dc)
         return;
     }
 
-    LOG_DIS("s%d%s\n", size, dc->type_b ? "i" : "");
+    LOG_DIS("s%d%s%s\n", size, dc->type_b ? "i" : "", rev ? "r" : "");
     t_sync_flags(dc);
     /* If we get a fault on a dslot, the jmpstate better be in sync.  */
     sync_jmpstate(dc);
     addr = compute_ldst_addr(dc, &t);
 
-    gen_store(dc, *addr, cpu_R[dc->rd], size);
+    if (rev && size != 4) {
+        /* Endian reverse the address. t is addr.  */
+        switch (size) {
+            case 1:
+            {
+                /* 00 -> 11
+                   01 -> 10
+                   10 -> 10
+                   11 -> 00 */
+                TCGv low = tcg_temp_new();
+
+                /* Force addr into the temp.  */
+                if (addr != &t) {
+                    t = tcg_temp_new();
+                    tcg_gen_mov_tl(t, *addr);
+                    addr = &t;
+                }
+
+                tcg_gen_andi_tl(low, t, 3);
+                tcg_gen_sub_tl(low, tcg_const_tl(3), low);
+                tcg_gen_andi_tl(t, t, ~3);
+                tcg_gen_or_tl(t, t, low);
+                tcg_gen_mov_tl(env_imm, t);
+                tcg_temp_free(low);
+                break;
+            }
+
+            case 2:
+                /* 00 -> 10
+                   10 -> 00.  */
+                /* Force addr into the temp.  */
+                if (addr != &t) {
+                    t = tcg_temp_new();
+                    tcg_gen_xori_tl(t, *addr, 2);
+                    addr = &t;
+                } else {
+                    tcg_gen_xori_tl(t, t, 2);
+                }
+                break;
+            default:
+                cpu_abort(dc->env, "Invalid reverse size\n");
+                break;
+        }
+
+        if (size != 1) {
+            TCGv bs_data = tcg_temp_new();
+            dec_byteswap(dc, bs_data, cpu_R[dc->rd], size);
+            gen_store(dc, *addr, bs_data, size);
+            tcg_temp_free(bs_data);
+        } else {
+            gen_store(dc, *addr, cpu_R[dc->rd], size);
+        }
+    } else {
+        if (rev) {
+            TCGv bs_data = tcg_temp_new();
+            dec_byteswap(dc, bs_data, cpu_R[dc->rd], size);
+            gen_store(dc, *addr, bs_data, size);
+            tcg_temp_free(bs_data);
+        } else {
+            gen_store(dc, *addr, cpu_R[dc->rd], size);
+        }
+    }
 
     /* Verify alignment if needed.  */
     if ((dc->env->pvr.regs[2] & PVR2_UNALIGNED_EXC_MASK) && size > 1) {
         tcg_gen_movi_tl(cpu_SR[SR_PC], dc->pc);
         /* FIXME: if the alignment is wrong, we should restore the value
-         *        in memory.
+         *        in memory. One possible way to acheive this is to probe
+         *        the MMU prior to the memaccess, thay way we could put
+         *        the alignment checks in between the probe and the mem
+         *        access.
          */
         gen_helper_memalign(*addr, tcg_const_tl(dc->rd),
                             tcg_const_tl(1), tcg_const_tl(size - 1));
@@ -904,50 +1133,24 @@ static void dec_store(DisasContext *dc)
 static inline void eval_cc(DisasContext *dc, unsigned int cc,
                            TCGv d, TCGv a, TCGv b)
 {
-    int l1;
-
     switch (cc) {
         case CC_EQ:
-            l1 = gen_new_label();
-            tcg_gen_movi_tl(env_btaken, 1);
-            tcg_gen_brcond_tl(TCG_COND_EQ, a, b, l1);
-            tcg_gen_movi_tl(env_btaken, 0);
-            gen_set_label(l1);
+            tcg_gen_setcond_tl(TCG_COND_EQ, d, a, b);
             break;
         case CC_NE:
-            l1 = gen_new_label();
-            tcg_gen_movi_tl(env_btaken, 1);
-            tcg_gen_brcond_tl(TCG_COND_NE, a, b, l1);
-            tcg_gen_movi_tl(env_btaken, 0);
-            gen_set_label(l1);
+            tcg_gen_setcond_tl(TCG_COND_NE, d, a, b);
             break;
         case CC_LT:
-            l1 = gen_new_label();
-            tcg_gen_movi_tl(env_btaken, 1);
-            tcg_gen_brcond_tl(TCG_COND_LT, a, b, l1);
-            tcg_gen_movi_tl(env_btaken, 0);
-            gen_set_label(l1);
+            tcg_gen_setcond_tl(TCG_COND_LT, d, a, b);
             break;
         case CC_LE:
-            l1 = gen_new_label();
-            tcg_gen_movi_tl(env_btaken, 1);
-            tcg_gen_brcond_tl(TCG_COND_LE, a, b, l1);
-            tcg_gen_movi_tl(env_btaken, 0);
-            gen_set_label(l1);
+            tcg_gen_setcond_tl(TCG_COND_LE, d, a, b);
             break;
         case CC_GE:
-            l1 = gen_new_label();
-            tcg_gen_movi_tl(env_btaken, 1);
-            tcg_gen_brcond_tl(TCG_COND_GE, a, b, l1);
-            tcg_gen_movi_tl(env_btaken, 0);
-            gen_set_label(l1);
+            tcg_gen_setcond_tl(TCG_COND_GE, d, a, b);
             break;
         case CC_GT:
-            l1 = gen_new_label();
-            tcg_gen_movi_tl(env_btaken, 1);
-            tcg_gen_brcond_tl(TCG_COND_GT, a, b, l1);
-            tcg_gen_movi_tl(env_btaken, 0);
-            gen_set_label(l1);
+            tcg_gen_setcond_tl(TCG_COND_GT, d, a, b);
             break;
         default:
             cpu_abort(dc->env, "Unknown condition code %x.\n", cc);
@@ -984,15 +1187,24 @@ static void dec_bcc(DisasContext *dc)
                       cpu_env, offsetof(CPUState, bimm));
     }
 
-    tcg_gen_movi_tl(env_btarget, dc->pc);
-    tcg_gen_add_tl(env_btarget, env_btarget, *(dec_alu_op_b(dc)));
+    if (dec_alu_op_b_is_small_imm(dc)) {
+        int32_t offset = (int32_t)((int16_t)dc->imm); /* sign-extend.  */
+
+        tcg_gen_movi_tl(env_btarget, dc->pc + offset);
+        dc->jmp = JMP_DIRECT_CC;
+        dc->jmp_pc = dc->pc + offset;
+    } else {
+        dc->jmp = JMP_INDIRECT;
+        tcg_gen_movi_tl(env_btarget, dc->pc);
+        tcg_gen_add_tl(env_btarget, env_btarget, *(dec_alu_op_b(dc)));
+    }
     eval_cc(dc, cc, env_btaken, cpu_R[dc->ra], tcg_const_tl(0));
-    dc->jmp = JMP_INDIRECT;
 }
 
 static void dec_br(DisasContext *dc)
 {
     unsigned int dslot, link, abs;
+    int mem_index = cpu_mmu_index(dc->env);
 
     dslot = dc->ir & (1 << 20);
     abs = dc->ir & (1 << 19);
@@ -1016,19 +1228,27 @@ static void dec_br(DisasContext *dc)
     if (abs) {
         tcg_gen_movi_tl(env_btaken, 1);
         tcg_gen_mov_tl(env_btarget, *(dec_alu_op_b(dc)));
-        if (link && !(dc->tb_flags & IMM_FLAG)
-            && (dc->imm == 8 || dc->imm == 0x18))
-            t_gen_raise_exception(dc, EXCP_BREAK);
-        if (dc->imm == 0)
-            t_gen_raise_exception(dc, EXCP_DEBUG);
+        if (link && !dslot) {
+            if (!(dc->tb_flags & IMM_FLAG) && (dc->imm == 8 || dc->imm == 0x18))
+                t_gen_raise_exception(dc, EXCP_BREAK);
+            if (dc->imm == 0) {
+                if ((dc->tb_flags & MSR_EE_FLAG) && mem_index == MMU_USER_IDX) {
+                    tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_PRIVINSN);
+                    t_gen_raise_exception(dc, EXCP_HW_EXCP);
+                    return;
+                }
+
+                t_gen_raise_exception(dc, EXCP_DEBUG);
+            }
+        }
     } else {
-        if (!dc->type_b || (dc->tb_flags & IMM_FLAG)) {
+        if (dec_alu_op_b_is_small_imm(dc)) {
+            dc->jmp = JMP_DIRECT;
+            dc->jmp_pc = dc->pc + (int32_t)((int16_t)dc->imm);
+        } else {
             tcg_gen_movi_tl(env_btaken, 1);
             tcg_gen_movi_tl(env_btarget, dc->pc);
             tcg_gen_add_tl(env_btarget, env_btarget, *(dec_alu_op_b(dc)));
-        } else {
-            dc->jmp = JMP_DIRECT;
-            dc->jmp_pc = dc->pc + (int32_t)((int16_t)dc->imm);
         }
     }
 }
@@ -1127,22 +1347,120 @@ static void dec_rts(DisasContext *dc)
     } else
         LOG_DIS("rts ir=%x\n", dc->ir);
 
+    dc->jmp = JMP_INDIRECT;
     tcg_gen_movi_tl(env_btaken, 1);
     tcg_gen_add_tl(env_btarget, cpu_R[dc->ra], *(dec_alu_op_b(dc)));
 }
 
+static int dec_check_fpuv2(DisasContext *dc)
+{
+    int r;
+
+    r = dc->env->pvr.regs[2] & PVR2_USE_FPU2_MASK;
+
+    if (!r && (dc->tb_flags & MSR_EE_FLAG)) {
+        tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_FPU);
+        t_gen_raise_exception(dc, EXCP_HW_EXCP);
+    }
+    return r;
+}
+
 static void dec_fpu(DisasContext *dc)
 {
+    unsigned int fpu_insn;
+
     if ((dc->tb_flags & MSR_EE_FLAG)
           && (dc->env->pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK)
           && !((dc->env->pvr.regs[2] & PVR2_USE_FPU_MASK))) {
-        tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_FPU);
+        tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP);
         t_gen_raise_exception(dc, EXCP_HW_EXCP);
         return;
     }
 
-    qemu_log ("unimplemented FPU insn pc=%x opc=%x\n", dc->pc, dc->opcode);
-    dc->abort_at_next_insn = 1;
+    fpu_insn = (dc->ir >> 7) & 7;
+
+    switch (fpu_insn) {
+        case 0:
+            gen_helper_fadd(cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]);
+            break;
+
+        case 1:
+            gen_helper_frsub(cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]);
+            break;
+
+        case 2:
+            gen_helper_fmul(cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]);
+            break;
+
+        case 3:
+            gen_helper_fdiv(cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]);
+            break;
+
+        case 4:
+            switch ((dc->ir >> 4) & 7) {
+                case 0:
+                    gen_helper_fcmp_un(cpu_R[dc->rd],
+                                       cpu_R[dc->ra], cpu_R[dc->rb]);
+                    break;
+                case 1:
+                    gen_helper_fcmp_lt(cpu_R[dc->rd],
+                                       cpu_R[dc->ra], cpu_R[dc->rb]);
+                    break;
+                case 2:
+                    gen_helper_fcmp_eq(cpu_R[dc->rd],
+                                       cpu_R[dc->ra], cpu_R[dc->rb]);
+                    break;
+                case 3:
+                    gen_helper_fcmp_le(cpu_R[dc->rd],
+                                       cpu_R[dc->ra], cpu_R[dc->rb]);
+                    break;
+                case 4:
+                    gen_helper_fcmp_gt(cpu_R[dc->rd],
+                                       cpu_R[dc->ra], cpu_R[dc->rb]);
+                    break;
+                case 5:
+                    gen_helper_fcmp_ne(cpu_R[dc->rd],
+                                       cpu_R[dc->ra], cpu_R[dc->rb]);
+                    break;
+                case 6:
+                    gen_helper_fcmp_ge(cpu_R[dc->rd],
+                                       cpu_R[dc->ra], cpu_R[dc->rb]);
+                    break;
+                default:
+                    qemu_log ("unimplemented fcmp fpu_insn=%x pc=%x opc=%x\n",
+                              fpu_insn, dc->pc, dc->opcode);
+                    dc->abort_at_next_insn = 1;
+                    break;
+            }
+            break;
+
+        case 5:
+            if (!dec_check_fpuv2(dc)) {
+                return;
+            }
+            gen_helper_flt(cpu_R[dc->rd], cpu_R[dc->ra]);
+            break;
+
+        case 6:
+            if (!dec_check_fpuv2(dc)) {
+                return;
+            }
+            gen_helper_fint(cpu_R[dc->rd], cpu_R[dc->ra]);
+            break;
+
+        case 7:
+            if (!dec_check_fpuv2(dc)) {
+                return;
+            }
+            gen_helper_fsqrt(cpu_R[dc->rd], cpu_R[dc->ra]);
+            break;
+
+        default:
+            qemu_log ("unimplemented FPU insn fpu_insn=%x pc=%x opc=%x\n",
+                      fpu_insn, dc->pc, dc->opcode);
+            dc->abort_at_next_insn = 1;
+            break;
+    }
 }
 
 static void dec_null(DisasContext *dc)
@@ -1157,6 +1475,42 @@ static void dec_null(DisasContext *dc)
     dc->abort_at_next_insn = 1;
 }
 
+/* Insns connected to FSL or AXI stream attached devices.  */
+static void dec_stream(DisasContext *dc)
+{
+    int mem_index = cpu_mmu_index(dc->env);
+    TCGv_i32 t_id, t_ctrl;
+    int ctrl;
+
+    LOG_DIS("%s%s imm=%x\n", dc->rd ? "get" : "put",
+            dc->type_b ? "" : "d", dc->imm);
+
+    if ((dc->tb_flags & MSR_EE_FLAG) && (mem_index == MMU_USER_IDX)) {
+        tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_PRIVINSN);
+        t_gen_raise_exception(dc, EXCP_HW_EXCP);
+        return;
+    }
+
+    t_id = tcg_temp_new();
+    if (dc->type_b) {
+        tcg_gen_movi_tl(t_id, dc->imm & 0xf);
+        ctrl = dc->imm >> 10;
+    } else {
+        tcg_gen_andi_tl(t_id, cpu_R[dc->rb], 0xf);
+        ctrl = dc->imm >> 5;
+    }
+
+    t_ctrl = tcg_const_tl(ctrl);
+
+    if (dc->rd == 0) {
+        gen_helper_put(t_id, t_ctrl, cpu_R[dc->ra]);
+    } else {
+        gen_helper_get(cpu_R[dc->rd], t_id, t_ctrl);
+    }
+    tcg_temp_free(t_id);
+    tcg_temp_free(t_ctrl);
+}
+
 static struct decoder_info {
     struct {
         uint32_t bits;
@@ -1181,6 +1535,7 @@ static struct decoder_info {
     {DEC_MUL, dec_mul},
     {DEC_DIV, dec_div},
     {DEC_MSR, dec_msr},
+    {DEC_STREAM, dec_stream},
     {{0, 0}, dec_null}
 };
 
@@ -1270,9 +1625,10 @@ gen_intermediate_code_internal(CPUState *env, TranslationBlock *tb,
     dc->is_jmp = DISAS_NEXT;
     dc->jmp = 0;
     dc->delayed_branch = !!(dc->tb_flags & D_FLAG);
-    dc->ppc = pc_start;
+    if (dc->delayed_branch) {
+        dc->jmp = JMP_INDIRECT;
+    }
     dc->pc = pc_start;
-    dc->cache_pc = -1;
     dc->singlestep_enabled = env->singlestep_enabled;
     dc->cpustate_changed = 0;
     dc->abort_at_next_insn = 0;
@@ -1328,7 +1684,6 @@ gen_intermediate_code_internal(CPUState *env, TranslationBlock *tb,
        decode(dc);
         if (dc->clear_imm)
             dc->tb_flags &= ~IMM_FLAG;
-        dc->ppc = dc->pc;
         dc->pc += 4;
         num_insns++;
 
@@ -1344,9 +1699,25 @@ gen_intermediate_code_internal(CPUState *env, TranslationBlock *tb,
                 /* Clear the delay slot flag.  */
                 dc->tb_flags &= ~D_FLAG;
                 /* If it is a direct jump, try direct chaining.  */
-                if (dc->jmp != JMP_DIRECT) {
+                if (dc->jmp == JMP_INDIRECT) {
                     eval_cond_jmp(dc, env_btarget, tcg_const_tl(dc->pc));
                     dc->is_jmp = DISAS_JUMP;
+                } else if (dc->jmp == JMP_DIRECT) {
+                    t_sync_flags(dc);
+                    gen_goto_tb(dc, 0, dc->jmp_pc);
+                    dc->is_jmp = DISAS_TB_JUMP;
+                } else if (dc->jmp == JMP_DIRECT_CC) {
+                    int l1;
+
+                    t_sync_flags(dc);
+                    l1 = gen_new_label();
+                    /* Conditional jmp.  */
+                    tcg_gen_brcondi_tl(TCG_COND_NE, env_btaken, 0, l1);
+                    gen_goto_tb(dc, 1, dc->pc);
+                    gen_set_label(l1);
+                    gen_goto_tb(dc, 0, dc->jmp_pc);
+
+                    dc->is_jmp = DISAS_TB_JUMP;
                 }
                 break;
             }
@@ -1360,7 +1731,7 @@ gen_intermediate_code_internal(CPUState *env, TranslationBlock *tb,
                  && num_insns < max_insns);
 
     npc = dc->pc;
-    if (dc->jmp == JMP_DIRECT) {
+    if (dc->jmp == JMP_DIRECT || dc->jmp == JMP_DIRECT_CC) {
         if (dc->tb_flags & D_FLAG) {
             dc->is_jmp = DISAS_UPDATE;
             tcg_gen_movi_tl(cpu_SR[SR_PC], npc);
@@ -1380,9 +1751,13 @@ gen_intermediate_code_internal(CPUState *env, TranslationBlock *tb,
     t_sync_flags(dc);
 
     if (unlikely(env->singlestep_enabled)) {
-        t_gen_raise_exception(dc, EXCP_DEBUG);
-        if (dc->is_jmp == DISAS_NEXT)
+        TCGv_i32 tmp = tcg_const_i32(EXCP_DEBUG);
+
+        if (dc->is_jmp != DISAS_JUMP) {
             tcg_gen_movi_tl(cpu_SR[SR_PC], npc);
+        }
+        gen_helper_raise_exception(tmp);
+        tcg_temp_free_i32(tmp);
     } else {
         switch(dc->is_jmp) {
             case DISAS_NEXT:
@@ -1419,7 +1794,7 @@ gen_intermediate_code_internal(CPUState *env, TranslationBlock *tb,
 #if DISAS_GNU
         log_target_disas(pc_start, dc->pc - pc_start, 0);
 #endif
-        qemu_log("\nisize=%d osize=%zd\n",
+        qemu_log("\nisize=%d osize=%td\n",
             dc->pc - pc_start, gen_opc_ptr - gen_opc_buf);
     }
 #endif
@@ -1437,8 +1812,7 @@ void gen_intermediate_code_pc (CPUState *env, struct TranslationBlock *tb)
     gen_intermediate_code_internal(env, tb, 1);
 }
 
-void cpu_dump_state (CPUState *env, FILE *f,
-                     int (*cpu_fprintf)(FILE *f, const char *fmt, ...),
+void cpu_dump_state (CPUState *env, FILE *f, fprintf_function cpu_fprintf,
                      int flags)
 {
     int i;
@@ -1448,13 +1822,16 @@ void cpu_dump_state (CPUState *env, FILE *f,
 
     cpu_fprintf(f, "IN: PC=%x %s\n",
                 env->sregs[SR_PC], lookup_symbol(env->sregs[SR_PC]));
-    cpu_fprintf(f, "rmsr=%x resr=%x debug[%x] imm=%x iflags=%x\n",
-             env->sregs[SR_MSR], env->sregs[SR_ESR],
-             env->debug, env->imm, env->iflags);
-    cpu_fprintf(f, "btaken=%d btarget=%x mode=%s(saved=%s)\n",
+    cpu_fprintf(f, "rmsr=%x resr=%x rear=%x debug=%x imm=%x iflags=%x fsr=%x\n",
+             env->sregs[SR_MSR], env->sregs[SR_ESR], env->sregs[SR_EAR],
+             env->debug, env->imm, env->iflags, env->sregs[SR_FSR]);
+    cpu_fprintf(f, "btaken=%d btarget=%x mode=%s(saved=%s) eip=%d ie=%d\n",
              env->btaken, env->btarget,
              (env->sregs[SR_MSR] & MSR_UM) ? "user" : "kernel",
-             (env->sregs[SR_MSR] & MSR_UMS) ? "user" : "kernel");
+             (env->sregs[SR_MSR] & MSR_UMS) ? "user" : "kernel",
+             (env->sregs[SR_MSR] & MSR_EIP),
+             (env->sregs[SR_MSR] & MSR_IE));
+
     for (i = 0; i < 32; i++) {
         cpu_fprintf(f, "r%2.2d=%8.8x ", i, env->regs[i]);
         if ((i + 1) % 4 == 0)
@@ -1473,7 +1850,7 @@ CPUState *cpu_mb_init (const char *cpu_model)
 
     cpu_exec_init(env);
     cpu_reset(env);
-
+    set_float_rounding_mode(float_round_nearest_even, &env->fp_status);
 
     if (tcg_initialized)
         return env;
@@ -1542,15 +1919,19 @@ void cpu_reset (CPUState *env)
                         | PVR2_USE_DIV_MASK \
                         | PVR2_USE_HW_MUL_MASK \
                         | PVR2_USE_MUL64_MASK \
+                        | PVR2_USE_FPU_MASK \
+                        | PVR2_USE_FPU2_MASK \
+                        | PVR2_FPU_EXC_MASK \
                         | 0;
     env->pvr.regs[10] = 0x0c000000; /* Default to spartan 3a dsp family.  */
     env->pvr.regs[11] = PVR11_USE_MMU | (16 << 17);
 
-    env->sregs[SR_MSR] = 0;
 #if defined(CONFIG_USER_ONLY)
     /* start in user mode with interrupts enabled.  */
+    env->sregs[SR_MSR] = MSR_EE | MSR_IE | MSR_VM | MSR_UM;
     env->pvr.regs[10] = 0x0c000000; /* Spartan 3a dsp.  */
 #else
+    env->sregs[SR_MSR] = 0;
     mmu_init(&env->mmu);
     env->mmu.c_mmu = 3;
     env->mmu.c_mmu_tlb_access = 3;
@@ -1558,8 +1939,7 @@ void cpu_reset (CPUState *env)
 #endif
 }
 
-void gen_pc_load(CPUState *env, struct TranslationBlock *tb,
-                 unsigned long searched_pc, int pc_pos, void *puc)
+void restore_state_to_opc(CPUState *env, TranslationBlock *tb, int pc_pos)
 {
     env->sregs[SR_PC] = gen_opc_pc[pc_pos];
 }