aboutsummaryrefslogtreecommitdiff
path: root/target
diff options
context:
space:
mode:
authorStefan Markovic <smarkovic@wavecomp.com>2018-08-02 16:16:07 +0200
committerAleksandar Markovic <amarkovic@wavecomp.com>2018-08-24 17:51:59 +0200
commit764371d24c51336c7713eb6f350218683871d05e (patch)
tree6d5663fec4be33fc2983a4f6b0c3a8fbc013efc3 /target
parentea4ca3c22646f5826e4adbfa006b564a5d38da8f (diff)
target/mips: Add emulation of nanoMIPS 16-bit branch instructions
Add emulation of nanoMIPS 16-bit branch instructions. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Yongbok Kim <yongbok.kim@mips.com> Signed-off-by: Aleksandar Markovic <amarkovic@wavecomp.com> Signed-off-by: Stefan Markovic <smarkovic@wavecomp.com>
Diffstat (limited to 'target')
-rw-r--r--target/mips/translate.c158
1 files changed, 158 insertions, 0 deletions
diff --git a/target/mips/translate.c b/target/mips/translate.c
index 261680e642..b0bbf4cbbd 100644
--- a/target/mips/translate.c
+++ b/target/mips/translate.c
@@ -4564,6 +4564,128 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc,
tcg_temp_free(t1);
}
+
+/* nanoMIPS Branches */
+static void gen_compute_branch_nm(DisasContext *ctx, uint32_t opc,
+ int insn_bytes,
+ int rs, int rt, int32_t offset)
+{
+ target_ulong btgt = -1;
+ int bcond_compute = 0;
+ TCGv t0 = tcg_temp_new();
+ TCGv t1 = tcg_temp_new();
+
+ /* Load needed operands */
+ switch (opc) {
+ case OPC_BEQ:
+ case OPC_BNE:
+ /* Compare two registers */
+ if (rs != rt) {
+ gen_load_gpr(t0, rs);
+ gen_load_gpr(t1, rt);
+ bcond_compute = 1;
+ }
+ btgt = ctx->base.pc_next + insn_bytes + offset;
+ break;
+ case OPC_BGEZAL:
+ /* Compare to zero */
+ if (rs != 0) {
+ gen_load_gpr(t0, rs);
+ bcond_compute = 1;
+ }
+ btgt = ctx->base.pc_next + insn_bytes + offset;
+ break;
+ case OPC_BPOSGE32:
+ tcg_gen_andi_tl(t0, cpu_dspctrl, 0x3F);
+ bcond_compute = 1;
+ btgt = ctx->base.pc_next + insn_bytes + offset;
+ break;
+ case OPC_JR:
+ case OPC_JALR:
+ /* Jump to register */
+ if (offset != 0 && offset != 16) {
+ /* Hint = 0 is JR/JALR, hint 16 is JR.HB/JALR.HB, the
+ others are reserved. */
+ MIPS_INVAL("jump hint");
+ generate_exception_end(ctx, EXCP_RI);
+ goto out;
+ }
+ gen_load_gpr(btarget, rs);
+ break;
+ default:
+ MIPS_INVAL("branch/jump");
+ generate_exception_end(ctx, EXCP_RI);
+ goto out;
+ }
+ if (bcond_compute == 0) {
+ /* No condition to be computed */
+ switch (opc) {
+ case OPC_BEQ: /* rx == rx */
+ /* Always take */
+ ctx->hflags |= MIPS_HFLAG_B;
+ break;
+ case OPC_BGEZAL: /* 0 >= 0 */
+ /* Always take and link */
+ tcg_gen_movi_tl(cpu_gpr[31],
+ ctx->base.pc_next + insn_bytes);
+ ctx->hflags |= MIPS_HFLAG_B;
+ break;
+ case OPC_BNE: /* rx != rx */
+ tcg_gen_movi_tl(cpu_gpr[31], ctx->base.pc_next + 8);
+ /* Skip the instruction in the delay slot */
+ ctx->base.pc_next += 4;
+ goto out;
+ case OPC_JR:
+ ctx->hflags |= MIPS_HFLAG_BR;
+ break;
+ case OPC_JALR:
+ if (rt > 0) {
+ tcg_gen_movi_tl(cpu_gpr[rt],
+ ctx->base.pc_next + insn_bytes);
+ }
+ ctx->hflags |= MIPS_HFLAG_BR;
+ break;
+ default:
+ MIPS_INVAL("branch/jump");
+ generate_exception_end(ctx, EXCP_RI);
+ goto out;
+ }
+ } else {
+ switch (opc) {
+ case OPC_BEQ:
+ tcg_gen_setcond_tl(TCG_COND_EQ, bcond, t0, t1);
+ goto not_likely;
+ case OPC_BNE:
+ tcg_gen_setcond_tl(TCG_COND_NE, bcond, t0, t1);
+ goto not_likely;
+ case OPC_BGEZAL:
+ tcg_gen_setcondi_tl(TCG_COND_GE, bcond, t0, 0);
+ tcg_gen_movi_tl(cpu_gpr[31],
+ ctx->base.pc_next + insn_bytes);
+ goto not_likely;
+ case OPC_BPOSGE32:
+ tcg_gen_setcondi_tl(TCG_COND_GE, bcond, t0, 32);
+ not_likely:
+ ctx->hflags |= MIPS_HFLAG_BC;
+ break;
+ default:
+ MIPS_INVAL("conditional branch/jump");
+ generate_exception_end(ctx, EXCP_RI);
+ goto out;
+ }
+ }
+
+ ctx->btarget = btgt;
+
+ out:
+ if (insn_bytes == 2) {
+ ctx->hflags |= MIPS_HFLAG_B16;
+ }
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+}
+
+
/* special3 bitfield operations */
static void gen_bitops (DisasContext *ctx, uint32_t opc, int rt,
int rs, int lsb, int msb)
@@ -16729,14 +16851,50 @@ static int decode_nanomips_opc(CPUMIPSState *env, DisasContext *ctx)
case NM_SWGP16:
break;
case NM_BC16:
+ gen_compute_branch_nm(ctx, OPC_BEQ, 2, 0, 0,
+ (sextract32(ctx->opcode, 0, 1) << 10) |
+ (extract32(ctx->opcode, 1, 9) << 1));
break;
case NM_BALC16:
+ gen_compute_branch_nm(ctx, OPC_BGEZAL, 2, 0, 0,
+ (sextract32(ctx->opcode, 0, 1) << 10) |
+ (extract32(ctx->opcode, 1, 9) << 1));
break;
case NM_BEQZC16:
+ gen_compute_branch_nm(ctx, OPC_BEQ, 2, rt, 0,
+ (sextract32(ctx->opcode, 0, 1) << 7) |
+ (extract32(ctx->opcode, 1, 6) << 1));
break;
case NM_BNEZC16:
+ gen_compute_branch_nm(ctx, OPC_BNE, 2, rt, 0,
+ (sextract32(ctx->opcode, 0, 1) << 7) |
+ (extract32(ctx->opcode, 1, 6) << 1));
break;
case NM_P16_BR:
+ switch (ctx->opcode & 0xf) {
+ case 0:
+ /* P16.JRC */
+ switch (extract32(ctx->opcode, 4, 1)) {
+ case NM_JRC:
+ gen_compute_branch_nm(ctx, OPC_JR, 2,
+ extract32(ctx->opcode, 5, 5), 0, 0);
+ break;
+ case NM_JALRC16:
+ gen_compute_branch_nm(ctx, OPC_JALR, 2,
+ extract32(ctx->opcode, 5, 5), 31, 0);
+ break;
+ }
+ break;
+ default:
+ {
+ /* P16.BRI */
+ uint32_t opc = extract32(ctx->opcode, 4, 3) <
+ extract32(ctx->opcode, 7, 3) ? OPC_BEQ : OPC_BNE;
+ gen_compute_branch_nm(ctx, opc, 2, rs, rt,
+ extract32(ctx->opcode, 0, 4) << 1);
+ }
+ break;
+ }
break;
case NM_P16_SR:
break;