| /* |
| * MIPS16 extension (Code Compaction) ASE translation routines |
| * |
| * Copyright (c) 2004-2005 Jocelyn Mayer |
| * Copyright (c) 2006 Marius Groeger (FPU operations) |
| * Copyright (c) 2006 Thiemo Seufer (MIPS32R2 support) |
| * Copyright (c) 2009 CodeSourcery (MIPS16 and microMIPS support) |
| * |
| * SPDX-License-Identifier: LGPL-2.1-or-later |
| */ |
| |
| /* MIPS16 major opcodes */ |
| enum { |
| M16_OPC_ADDIUSP = 0x00, |
| M16_OPC_ADDIUPC = 0x01, |
| M16_OPC_B = 0x02, |
| M16_OPC_JAL = 0x03, |
| M16_OPC_BEQZ = 0x04, |
| M16_OPC_BNEQZ = 0x05, |
| M16_OPC_SHIFT = 0x06, |
| M16_OPC_LD = 0x07, |
| M16_OPC_RRIA = 0x08, |
| M16_OPC_ADDIU8 = 0x09, |
| M16_OPC_SLTI = 0x0a, |
| M16_OPC_SLTIU = 0x0b, |
| M16_OPC_I8 = 0x0c, |
| M16_OPC_LI = 0x0d, |
| M16_OPC_CMPI = 0x0e, |
| M16_OPC_SD = 0x0f, |
| M16_OPC_LB = 0x10, |
| M16_OPC_LH = 0x11, |
| M16_OPC_LWSP = 0x12, |
| M16_OPC_LW = 0x13, |
| M16_OPC_LBU = 0x14, |
| M16_OPC_LHU = 0x15, |
| M16_OPC_LWPC = 0x16, |
| M16_OPC_LWU = 0x17, |
| M16_OPC_SB = 0x18, |
| M16_OPC_SH = 0x19, |
| M16_OPC_SWSP = 0x1a, |
| M16_OPC_SW = 0x1b, |
| M16_OPC_RRR = 0x1c, |
| M16_OPC_RR = 0x1d, |
| M16_OPC_EXTEND = 0x1e, |
| M16_OPC_I64 = 0x1f |
| }; |
| |
| /* I8 funct field */ |
| enum { |
| I8_BTEQZ = 0x0, |
| I8_BTNEZ = 0x1, |
| I8_SWRASP = 0x2, |
| I8_ADJSP = 0x3, |
| I8_SVRS = 0x4, |
| I8_MOV32R = 0x5, |
| I8_MOVR32 = 0x7 |
| }; |
| |
| /* RRR f field */ |
| enum { |
| RRR_DADDU = 0x0, |
| RRR_ADDU = 0x1, |
| RRR_DSUBU = 0x2, |
| RRR_SUBU = 0x3 |
| }; |
| |
| /* RR funct field */ |
| enum { |
| RR_JR = 0x00, |
| RR_SDBBP = 0x01, |
| RR_SLT = 0x02, |
| RR_SLTU = 0x03, |
| RR_SLLV = 0x04, |
| RR_BREAK = 0x05, |
| RR_SRLV = 0x06, |
| RR_SRAV = 0x07, |
| RR_DSRL = 0x08, |
| RR_CMP = 0x0a, |
| RR_NEG = 0x0b, |
| RR_AND = 0x0c, |
| RR_OR = 0x0d, |
| RR_XOR = 0x0e, |
| RR_NOT = 0x0f, |
| RR_MFHI = 0x10, |
| RR_CNVT = 0x11, |
| RR_MFLO = 0x12, |
| RR_DSRA = 0x13, |
| RR_DSLLV = 0x14, |
| RR_DSRLV = 0x16, |
| RR_DSRAV = 0x17, |
| RR_MULT = 0x18, |
| RR_MULTU = 0x19, |
| RR_DIV = 0x1a, |
| RR_DIVU = 0x1b, |
| RR_DMULT = 0x1c, |
| RR_DMULTU = 0x1d, |
| RR_DDIV = 0x1e, |
| RR_DDIVU = 0x1f |
| }; |
| |
| /* I64 funct field */ |
| enum { |
| I64_LDSP = 0x0, |
| I64_SDSP = 0x1, |
| I64_SDRASP = 0x2, |
| I64_DADJSP = 0x3, |
| I64_LDPC = 0x4, |
| I64_DADDIU5 = 0x5, |
| I64_DADDIUPC = 0x6, |
| I64_DADDIUSP = 0x7 |
| }; |
| |
| /* RR ry field for CNVT */ |
| enum { |
| RR_RY_CNVT_ZEB = 0x0, |
| RR_RY_CNVT_ZEH = 0x1, |
| RR_RY_CNVT_ZEW = 0x2, |
| RR_RY_CNVT_SEB = 0x4, |
| RR_RY_CNVT_SEH = 0x5, |
| RR_RY_CNVT_SEW = 0x6, |
| }; |
| |
| static int xlat(int r) |
| { |
| static int map[] = { 16, 17, 2, 3, 4, 5, 6, 7 }; |
| |
| return map[r]; |
| } |
| |
| static void gen_mips16_save(DisasContext *ctx, |
| int xsregs, int aregs, |
| int do_ra, int do_s0, int do_s1, |
| int framesize) |
| { |
| TCGv t0 = tcg_temp_new(); |
| TCGv t1 = tcg_temp_new(); |
| TCGv t2 = tcg_temp_new(); |
| int args, astatic; |
| |
| switch (aregs) { |
| case 0: |
| case 1: |
| case 2: |
| case 3: |
| case 11: |
| args = 0; |
| break; |
| case 4: |
| case 5: |
| case 6: |
| case 7: |
| args = 1; |
| break; |
| case 8: |
| case 9: |
| case 10: |
| args = 2; |
| break; |
| case 12: |
| case 13: |
| args = 3; |
| break; |
| case 14: |
| args = 4; |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| return; |
| } |
| |
| switch (args) { |
| case 4: |
| gen_base_offset_addr(ctx, t0, 29, 12); |
| gen_load_gpr(t1, 7); |
| tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL); |
| /* Fall through */ |
| case 3: |
| gen_base_offset_addr(ctx, t0, 29, 8); |
| gen_load_gpr(t1, 6); |
| tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL); |
| /* Fall through */ |
| case 2: |
| gen_base_offset_addr(ctx, t0, 29, 4); |
| gen_load_gpr(t1, 5); |
| tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL); |
| /* Fall through */ |
| case 1: |
| gen_base_offset_addr(ctx, t0, 29, 0); |
| gen_load_gpr(t1, 4); |
| tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL); |
| } |
| |
| gen_load_gpr(t0, 29); |
| |
| #define DECR_AND_STORE(reg) do { \ |
| tcg_gen_movi_tl(t2, -4); \ |
| gen_op_addr_add(ctx, t0, t0, t2); \ |
| gen_load_gpr(t1, reg); \ |
| tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL); \ |
| } while (0) |
| |
| if (do_ra) { |
| DECR_AND_STORE(31); |
| } |
| |
| switch (xsregs) { |
| case 7: |
| DECR_AND_STORE(30); |
| /* Fall through */ |
| case 6: |
| DECR_AND_STORE(23); |
| /* Fall through */ |
| case 5: |
| DECR_AND_STORE(22); |
| /* Fall through */ |
| case 4: |
| DECR_AND_STORE(21); |
| /* Fall through */ |
| case 3: |
| DECR_AND_STORE(20); |
| /* Fall through */ |
| case 2: |
| DECR_AND_STORE(19); |
| /* Fall through */ |
| case 1: |
| DECR_AND_STORE(18); |
| } |
| |
| if (do_s1) { |
| DECR_AND_STORE(17); |
| } |
| if (do_s0) { |
| DECR_AND_STORE(16); |
| } |
| |
| switch (aregs) { |
| case 0: |
| case 4: |
| case 8: |
| case 12: |
| case 14: |
| astatic = 0; |
| break; |
| case 1: |
| case 5: |
| case 9: |
| case 13: |
| astatic = 1; |
| break; |
| case 2: |
| case 6: |
| case 10: |
| astatic = 2; |
| break; |
| case 3: |
| case 7: |
| astatic = 3; |
| break; |
| case 11: |
| astatic = 4; |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| return; |
| } |
| |
| if (astatic > 0) { |
| DECR_AND_STORE(7); |
| if (astatic > 1) { |
| DECR_AND_STORE(6); |
| if (astatic > 2) { |
| DECR_AND_STORE(5); |
| if (astatic > 3) { |
| DECR_AND_STORE(4); |
| } |
| } |
| } |
| } |
| #undef DECR_AND_STORE |
| |
| tcg_gen_movi_tl(t2, -framesize); |
| gen_op_addr_add(ctx, cpu_gpr[29], cpu_gpr[29], t2); |
| } |
| |
| static void gen_mips16_restore(DisasContext *ctx, |
| int xsregs, int aregs, |
| int do_ra, int do_s0, int do_s1, |
| int framesize) |
| { |
| int astatic; |
| TCGv t0 = tcg_temp_new(); |
| TCGv t1 = tcg_temp_new(); |
| TCGv t2 = tcg_temp_new(); |
| |
| tcg_gen_movi_tl(t2, framesize); |
| gen_op_addr_add(ctx, t0, cpu_gpr[29], t2); |
| |
| #define DECR_AND_LOAD(reg) do { \ |
| tcg_gen_movi_tl(t2, -4); \ |
| gen_op_addr_add(ctx, t0, t0, t2); \ |
| tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_TESL); \ |
| gen_store_gpr(t1, reg); \ |
| } while (0) |
| |
| if (do_ra) { |
| DECR_AND_LOAD(31); |
| } |
| |
| switch (xsregs) { |
| case 7: |
| DECR_AND_LOAD(30); |
| /* Fall through */ |
| case 6: |
| DECR_AND_LOAD(23); |
| /* Fall through */ |
| case 5: |
| DECR_AND_LOAD(22); |
| /* Fall through */ |
| case 4: |
| DECR_AND_LOAD(21); |
| /* Fall through */ |
| case 3: |
| DECR_AND_LOAD(20); |
| /* Fall through */ |
| case 2: |
| DECR_AND_LOAD(19); |
| /* Fall through */ |
| case 1: |
| DECR_AND_LOAD(18); |
| } |
| |
| if (do_s1) { |
| DECR_AND_LOAD(17); |
| } |
| if (do_s0) { |
| DECR_AND_LOAD(16); |
| } |
| |
| switch (aregs) { |
| case 0: |
| case 4: |
| case 8: |
| case 12: |
| case 14: |
| astatic = 0; |
| break; |
| case 1: |
| case 5: |
| case 9: |
| case 13: |
| astatic = 1; |
| break; |
| case 2: |
| case 6: |
| case 10: |
| astatic = 2; |
| break; |
| case 3: |
| case 7: |
| astatic = 3; |
| break; |
| case 11: |
| astatic = 4; |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| return; |
| } |
| |
| if (astatic > 0) { |
| DECR_AND_LOAD(7); |
| if (astatic > 1) { |
| DECR_AND_LOAD(6); |
| if (astatic > 2) { |
| DECR_AND_LOAD(5); |
| if (astatic > 3) { |
| DECR_AND_LOAD(4); |
| } |
| } |
| } |
| } |
| #undef DECR_AND_LOAD |
| |
| tcg_gen_movi_tl(t2, framesize); |
| gen_op_addr_add(ctx, cpu_gpr[29], cpu_gpr[29], t2); |
| } |
| |
| #if defined(TARGET_MIPS64) |
| static void decode_i64_mips16(DisasContext *ctx, |
| int ry, int funct, int16_t offset, |
| int extended) |
| { |
| switch (funct) { |
| case I64_LDSP: |
| check_insn(ctx, ISA_MIPS3); |
| check_mips_64(ctx); |
| offset = extended ? offset : offset << 3; |
| gen_ld(ctx, OPC_LD, ry, 29, offset); |
| break; |
| case I64_SDSP: |
| check_insn(ctx, ISA_MIPS3); |
| check_mips_64(ctx); |
| offset = extended ? offset : offset << 3; |
| gen_st(ctx, OPC_SD, ry, 29, offset); |
| break; |
| case I64_SDRASP: |
| check_insn(ctx, ISA_MIPS3); |
| check_mips_64(ctx); |
| offset = extended ? offset : (ctx->opcode & 0xff) << 3; |
| gen_st(ctx, OPC_SD, 31, 29, offset); |
| break; |
| case I64_DADJSP: |
| check_insn(ctx, ISA_MIPS3); |
| check_mips_64(ctx); |
| offset = extended ? offset : ((int8_t)ctx->opcode) << 3; |
| gen_arith_imm(ctx, OPC_DADDIU, 29, 29, offset); |
| break; |
| case I64_LDPC: |
| check_insn(ctx, ISA_MIPS3); |
| check_mips_64(ctx); |
| if (extended && (ctx->hflags & MIPS_HFLAG_BMASK)) { |
| gen_reserved_instruction(ctx); |
| } else { |
| offset = extended ? offset : offset << 3; |
| gen_ld(ctx, OPC_LDPC, ry, 0, offset); |
| } |
| break; |
| case I64_DADDIU5: |
| check_insn(ctx, ISA_MIPS3); |
| check_mips_64(ctx); |
| offset = extended ? offset : ((int8_t)(offset << 3)) >> 3; |
| gen_arith_imm(ctx, OPC_DADDIU, ry, ry, offset); |
| break; |
| case I64_DADDIUPC: |
| check_insn(ctx, ISA_MIPS3); |
| check_mips_64(ctx); |
| offset = extended ? offset : offset << 2; |
| gen_addiupc(ctx, ry, offset, 1, extended); |
| break; |
| case I64_DADDIUSP: |
| check_insn(ctx, ISA_MIPS3); |
| check_mips_64(ctx); |
| offset = extended ? offset : offset << 2; |
| gen_arith_imm(ctx, OPC_DADDIU, ry, 29, offset); |
| break; |
| } |
| } |
| #endif |
| |
| static int decode_extended_mips16_opc(CPUMIPSState *env, DisasContext *ctx) |
| { |
| int extend = translator_lduw(env, &ctx->base, ctx->base.pc_next + 2); |
| int op, rx, ry, funct, sa; |
| int16_t imm, offset; |
| |
| ctx->opcode = (ctx->opcode << 16) | extend; |
| op = (ctx->opcode >> 11) & 0x1f; |
| sa = (ctx->opcode >> 22) & 0x1f; |
| funct = (ctx->opcode >> 8) & 0x7; |
| rx = xlat((ctx->opcode >> 8) & 0x7); |
| ry = xlat((ctx->opcode >> 5) & 0x7); |
| offset = imm = (int16_t) (((ctx->opcode >> 16) & 0x1f) << 11 |
| | ((ctx->opcode >> 21) & 0x3f) << 5 |
| | (ctx->opcode & 0x1f)); |
| |
| /* |
| * The extended opcodes cleverly reuse the opcodes from their 16-bit |
| * counterparts. |
| */ |
| switch (op) { |
| case M16_OPC_ADDIUSP: |
| gen_arith_imm(ctx, OPC_ADDIU, rx, 29, imm); |
| break; |
| case M16_OPC_ADDIUPC: |
| gen_addiupc(ctx, rx, imm, 0, 1); |
| break; |
| case M16_OPC_B: |
| gen_compute_branch(ctx, OPC_BEQ, 4, 0, 0, offset << 1, 0); |
| /* No delay slot, so just process as a normal instruction */ |
| break; |
| case M16_OPC_BEQZ: |
| gen_compute_branch(ctx, OPC_BEQ, 4, rx, 0, offset << 1, 0); |
| /* No delay slot, so just process as a normal instruction */ |
| break; |
| case M16_OPC_BNEQZ: |
| gen_compute_branch(ctx, OPC_BNE, 4, rx, 0, offset << 1, 0); |
| /* No delay slot, so just process as a normal instruction */ |
| break; |
| case M16_OPC_SHIFT: |
| switch (ctx->opcode & 0x3) { |
| case 0x0: |
| gen_shift_imm(ctx, OPC_SLL, rx, ry, sa); |
| break; |
| case 0x1: |
| #if defined(TARGET_MIPS64) |
| check_mips_64(ctx); |
| gen_shift_imm(ctx, OPC_DSLL, rx, ry, sa); |
| #else |
| gen_reserved_instruction(ctx); |
| #endif |
| break; |
| case 0x2: |
| gen_shift_imm(ctx, OPC_SRL, rx, ry, sa); |
| break; |
| case 0x3: |
| gen_shift_imm(ctx, OPC_SRA, rx, ry, sa); |
| break; |
| } |
| break; |
| #if defined(TARGET_MIPS64) |
| case M16_OPC_LD: |
| check_insn(ctx, ISA_MIPS3); |
| check_mips_64(ctx); |
| gen_ld(ctx, OPC_LD, ry, rx, offset); |
| break; |
| #endif |
| case M16_OPC_RRIA: |
| imm = ctx->opcode & 0xf; |
| imm = imm | ((ctx->opcode >> 20) & 0x7f) << 4; |
| imm = imm | ((ctx->opcode >> 16) & 0xf) << 11; |
| imm = (int16_t) (imm << 1) >> 1; |
| if ((ctx->opcode >> 4) & 0x1) { |
| #if defined(TARGET_MIPS64) |
| check_mips_64(ctx); |
| gen_arith_imm(ctx, OPC_DADDIU, ry, rx, imm); |
| #else |
| gen_reserved_instruction(ctx); |
| #endif |
| } else { |
| gen_arith_imm(ctx, OPC_ADDIU, ry, rx, imm); |
| } |
| break; |
| case M16_OPC_ADDIU8: |
| gen_arith_imm(ctx, OPC_ADDIU, rx, rx, imm); |
| break; |
| case M16_OPC_SLTI: |
| gen_slt_imm(ctx, OPC_SLTI, 24, rx, imm); |
| break; |
| case M16_OPC_SLTIU: |
| gen_slt_imm(ctx, OPC_SLTIU, 24, rx, imm); |
| break; |
| case M16_OPC_I8: |
| switch (funct) { |
| case I8_BTEQZ: |
| gen_compute_branch(ctx, OPC_BEQ, 4, 24, 0, offset << 1, 0); |
| break; |
| case I8_BTNEZ: |
| gen_compute_branch(ctx, OPC_BNE, 4, 24, 0, offset << 1, 0); |
| break; |
| case I8_SWRASP: |
| gen_st(ctx, OPC_SW, 31, 29, imm); |
| break; |
| case I8_ADJSP: |
| gen_arith_imm(ctx, OPC_ADDIU, 29, 29, imm); |
| break; |
| case I8_SVRS: |
| check_insn(ctx, ISA_MIPS_R1); |
| { |
| int xsregs = (ctx->opcode >> 24) & 0x7; |
| int aregs = (ctx->opcode >> 16) & 0xf; |
| int do_ra = (ctx->opcode >> 6) & 0x1; |
| int do_s0 = (ctx->opcode >> 5) & 0x1; |
| int do_s1 = (ctx->opcode >> 4) & 0x1; |
| int framesize = (((ctx->opcode >> 20) & 0xf) << 4 |
| | (ctx->opcode & 0xf)) << 3; |
| |
| if (ctx->opcode & (1 << 7)) { |
| gen_mips16_save(ctx, xsregs, aregs, |
| do_ra, do_s0, do_s1, |
| framesize); |
| } else { |
| gen_mips16_restore(ctx, xsregs, aregs, |
| do_ra, do_s0, do_s1, |
| framesize); |
| } |
| } |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| break; |
| case M16_OPC_LI: |
| tcg_gen_movi_tl(cpu_gpr[rx], (uint16_t) imm); |
| break; |
| case M16_OPC_CMPI: |
| tcg_gen_xori_tl(cpu_gpr[24], cpu_gpr[rx], (uint16_t) imm); |
| break; |
| #if defined(TARGET_MIPS64) |
| case M16_OPC_SD: |
| check_insn(ctx, ISA_MIPS3); |
| check_mips_64(ctx); |
| gen_st(ctx, OPC_SD, ry, rx, offset); |
| break; |
| #endif |
| case M16_OPC_LB: |
| gen_ld(ctx, OPC_LB, ry, rx, offset); |
| break; |
| case M16_OPC_LH: |
| gen_ld(ctx, OPC_LH, ry, rx, offset); |
| break; |
| case M16_OPC_LWSP: |
| gen_ld(ctx, OPC_LW, rx, 29, offset); |
| break; |
| case M16_OPC_LW: |
| gen_ld(ctx, OPC_LW, ry, rx, offset); |
| break; |
| case M16_OPC_LBU: |
| gen_ld(ctx, OPC_LBU, ry, rx, offset); |
| break; |
| case M16_OPC_LHU: |
| gen_ld(ctx, OPC_LHU, ry, rx, offset); |
| break; |
| case M16_OPC_LWPC: |
| gen_ld(ctx, OPC_LWPC, rx, 0, offset); |
| break; |
| #if defined(TARGET_MIPS64) |
| case M16_OPC_LWU: |
| check_insn(ctx, ISA_MIPS3); |
| check_mips_64(ctx); |
| gen_ld(ctx, OPC_LWU, ry, rx, offset); |
| break; |
| #endif |
| case M16_OPC_SB: |
| gen_st(ctx, OPC_SB, ry, rx, offset); |
| break; |
| case M16_OPC_SH: |
| gen_st(ctx, OPC_SH, ry, rx, offset); |
| break; |
| case M16_OPC_SWSP: |
| gen_st(ctx, OPC_SW, rx, 29, offset); |
| break; |
| case M16_OPC_SW: |
| gen_st(ctx, OPC_SW, ry, rx, offset); |
| break; |
| #if defined(TARGET_MIPS64) |
| case M16_OPC_I64: |
| decode_i64_mips16(ctx, ry, funct, offset, 1); |
| break; |
| #endif |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| |
| return 4; |
| } |
| |
| static int decode_ase_mips16e(CPUMIPSState *env, DisasContext *ctx) |
| { |
| int rx, ry; |
| int sa; |
| int op, cnvt_op, op1, offset; |
| int funct; |
| int n_bytes; |
| |
| op = (ctx->opcode >> 11) & 0x1f; |
| sa = (ctx->opcode >> 2) & 0x7; |
| sa = sa == 0 ? 8 : sa; |
| rx = xlat((ctx->opcode >> 8) & 0x7); |
| cnvt_op = (ctx->opcode >> 5) & 0x7; |
| ry = xlat((ctx->opcode >> 5) & 0x7); |
| op1 = offset = ctx->opcode & 0x1f; |
| |
| n_bytes = 2; |
| |
| switch (op) { |
| case M16_OPC_ADDIUSP: |
| { |
| int16_t imm = ((uint8_t) ctx->opcode) << 2; |
| |
| gen_arith_imm(ctx, OPC_ADDIU, rx, 29, imm); |
| } |
| break; |
| case M16_OPC_ADDIUPC: |
| gen_addiupc(ctx, rx, ((uint8_t) ctx->opcode) << 2, 0, 0); |
| break; |
| case M16_OPC_B: |
| offset = (ctx->opcode & 0x7ff) << 1; |
| offset = (int16_t)(offset << 4) >> 4; |
| gen_compute_branch(ctx, OPC_BEQ, 2, 0, 0, offset, 0); |
| /* No delay slot, so just process as a normal instruction */ |
| break; |
| case M16_OPC_JAL: |
| offset = translator_lduw(env, &ctx->base, ctx->base.pc_next + 2); |
| offset = (((ctx->opcode & 0x1f) << 21) |
| | ((ctx->opcode >> 5) & 0x1f) << 16 |
| | offset) << 2; |
| op = ((ctx->opcode >> 10) & 0x1) ? OPC_JALX : OPC_JAL; |
| gen_compute_branch(ctx, op, 4, rx, ry, offset, 2); |
| n_bytes = 4; |
| break; |
| case M16_OPC_BEQZ: |
| gen_compute_branch(ctx, OPC_BEQ, 2, rx, 0, |
| ((int8_t)ctx->opcode) << 1, 0); |
| /* No delay slot, so just process as a normal instruction */ |
| break; |
| case M16_OPC_BNEQZ: |
| gen_compute_branch(ctx, OPC_BNE, 2, rx, 0, |
| ((int8_t)ctx->opcode) << 1, 0); |
| /* No delay slot, so just process as a normal instruction */ |
| break; |
| case M16_OPC_SHIFT: |
| switch (ctx->opcode & 0x3) { |
| case 0x0: |
| gen_shift_imm(ctx, OPC_SLL, rx, ry, sa); |
| break; |
| case 0x1: |
| #if defined(TARGET_MIPS64) |
| check_insn(ctx, ISA_MIPS3); |
| check_mips_64(ctx); |
| gen_shift_imm(ctx, OPC_DSLL, rx, ry, sa); |
| #else |
| gen_reserved_instruction(ctx); |
| #endif |
| break; |
| case 0x2: |
| gen_shift_imm(ctx, OPC_SRL, rx, ry, sa); |
| break; |
| case 0x3: |
| gen_shift_imm(ctx, OPC_SRA, rx, ry, sa); |
| break; |
| } |
| break; |
| #if defined(TARGET_MIPS64) |
| case M16_OPC_LD: |
| check_insn(ctx, ISA_MIPS3); |
| check_mips_64(ctx); |
| gen_ld(ctx, OPC_LD, ry, rx, offset << 3); |
| break; |
| #endif |
| case M16_OPC_RRIA: |
| { |
| int16_t imm = (int8_t)((ctx->opcode & 0xf) << 4) >> 4; |
| |
| if ((ctx->opcode >> 4) & 1) { |
| #if defined(TARGET_MIPS64) |
| check_insn(ctx, ISA_MIPS3); |
| check_mips_64(ctx); |
| gen_arith_imm(ctx, OPC_DADDIU, ry, rx, imm); |
| #else |
| gen_reserved_instruction(ctx); |
| #endif |
| } else { |
| gen_arith_imm(ctx, OPC_ADDIU, ry, rx, imm); |
| } |
| } |
| break; |
| case M16_OPC_ADDIU8: |
| { |
| int16_t imm = (int8_t) ctx->opcode; |
| |
| gen_arith_imm(ctx, OPC_ADDIU, rx, rx, imm); |
| } |
| break; |
| case M16_OPC_SLTI: |
| { |
| int16_t imm = (uint8_t) ctx->opcode; |
| gen_slt_imm(ctx, OPC_SLTI, 24, rx, imm); |
| } |
| break; |
| case M16_OPC_SLTIU: |
| { |
| int16_t imm = (uint8_t) ctx->opcode; |
| gen_slt_imm(ctx, OPC_SLTIU, 24, rx, imm); |
| } |
| break; |
| case M16_OPC_I8: |
| { |
| int reg32; |
| |
| funct = (ctx->opcode >> 8) & 0x7; |
| switch (funct) { |
| case I8_BTEQZ: |
| gen_compute_branch(ctx, OPC_BEQ, 2, 24, 0, |
| ((int8_t)ctx->opcode) << 1, 0); |
| break; |
| case I8_BTNEZ: |
| gen_compute_branch(ctx, OPC_BNE, 2, 24, 0, |
| ((int8_t)ctx->opcode) << 1, 0); |
| break; |
| case I8_SWRASP: |
| gen_st(ctx, OPC_SW, 31, 29, (ctx->opcode & 0xff) << 2); |
| break; |
| case I8_ADJSP: |
| gen_arith_imm(ctx, OPC_ADDIU, 29, 29, |
| ((int8_t)ctx->opcode) << 3); |
| break; |
| case I8_SVRS: |
| check_insn(ctx, ISA_MIPS_R1); |
| { |
| int do_ra = ctx->opcode & (1 << 6); |
| int do_s0 = ctx->opcode & (1 << 5); |
| int do_s1 = ctx->opcode & (1 << 4); |
| int framesize = ctx->opcode & 0xf; |
| |
| if (framesize == 0) { |
| framesize = 128; |
| } else { |
| framesize = framesize << 3; |
| } |
| |
| if (ctx->opcode & (1 << 7)) { |
| gen_mips16_save(ctx, 0, 0, |
| do_ra, do_s0, do_s1, framesize); |
| } else { |
| gen_mips16_restore(ctx, 0, 0, |
| do_ra, do_s0, do_s1, framesize); |
| } |
| } |
| break; |
| case I8_MOV32R: |
| { |
| int rz = xlat(ctx->opcode & 0x7); |
| |
| reg32 = (((ctx->opcode >> 3) & 0x3) << 3) | |
| ((ctx->opcode >> 5) & 0x7); |
| gen_arith(ctx, OPC_ADDU, reg32, rz, 0); |
| } |
| break; |
| case I8_MOVR32: |
| reg32 = ctx->opcode & 0x1f; |
| gen_arith(ctx, OPC_ADDU, ry, reg32, 0); |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| } |
| break; |
| case M16_OPC_LI: |
| { |
| int16_t imm = (uint8_t) ctx->opcode; |
| |
| gen_arith_imm(ctx, OPC_ADDIU, rx, 0, imm); |
| } |
| break; |
| case M16_OPC_CMPI: |
| { |
| int16_t imm = (uint8_t) ctx->opcode; |
| gen_logic_imm(ctx, OPC_XORI, 24, rx, imm); |
| } |
| break; |
| #if defined(TARGET_MIPS64) |
| case M16_OPC_SD: |
| check_insn(ctx, ISA_MIPS3); |
| check_mips_64(ctx); |
| gen_st(ctx, OPC_SD, ry, rx, offset << 3); |
| break; |
| #endif |
| case M16_OPC_LB: |
| gen_ld(ctx, OPC_LB, ry, rx, offset); |
| break; |
| case M16_OPC_LH: |
| gen_ld(ctx, OPC_LH, ry, rx, offset << 1); |
| break; |
| case M16_OPC_LWSP: |
| gen_ld(ctx, OPC_LW, rx, 29, ((uint8_t)ctx->opcode) << 2); |
| break; |
| case M16_OPC_LW: |
| gen_ld(ctx, OPC_LW, ry, rx, offset << 2); |
| break; |
| case M16_OPC_LBU: |
| gen_ld(ctx, OPC_LBU, ry, rx, offset); |
| break; |
| case M16_OPC_LHU: |
| gen_ld(ctx, OPC_LHU, ry, rx, offset << 1); |
| break; |
| case M16_OPC_LWPC: |
| gen_ld(ctx, OPC_LWPC, rx, 0, ((uint8_t)ctx->opcode) << 2); |
| break; |
| #if defined(TARGET_MIPS64) |
| case M16_OPC_LWU: |
| check_insn(ctx, ISA_MIPS3); |
| check_mips_64(ctx); |
| gen_ld(ctx, OPC_LWU, ry, rx, offset << 2); |
| break; |
| #endif |
| case M16_OPC_SB: |
| gen_st(ctx, OPC_SB, ry, rx, offset); |
| break; |
| case M16_OPC_SH: |
| gen_st(ctx, OPC_SH, ry, rx, offset << 1); |
| break; |
| case M16_OPC_SWSP: |
| gen_st(ctx, OPC_SW, rx, 29, ((uint8_t)ctx->opcode) << 2); |
| break; |
| case M16_OPC_SW: |
| gen_st(ctx, OPC_SW, ry, rx, offset << 2); |
| break; |
| case M16_OPC_RRR: |
| { |
| int rz = xlat((ctx->opcode >> 2) & 0x7); |
| int mips32_op; |
| |
| switch (ctx->opcode & 0x3) { |
| case RRR_ADDU: |
| mips32_op = OPC_ADDU; |
| break; |
| case RRR_SUBU: |
| mips32_op = OPC_SUBU; |
| break; |
| #if defined(TARGET_MIPS64) |
| case RRR_DADDU: |
| mips32_op = OPC_DADDU; |
| check_insn(ctx, ISA_MIPS3); |
| check_mips_64(ctx); |
| break; |
| case RRR_DSUBU: |
| mips32_op = OPC_DSUBU; |
| check_insn(ctx, ISA_MIPS3); |
| check_mips_64(ctx); |
| break; |
| #endif |
| default: |
| gen_reserved_instruction(ctx); |
| goto done; |
| } |
| |
| gen_arith(ctx, mips32_op, rz, rx, ry); |
| done: |
| ; |
| } |
| break; |
| case M16_OPC_RR: |
| switch (op1) { |
| case RR_JR: |
| { |
| int nd = (ctx->opcode >> 7) & 0x1; |
| int link = (ctx->opcode >> 6) & 0x1; |
| int ra = (ctx->opcode >> 5) & 0x1; |
| |
| if (nd) { |
| check_insn(ctx, ISA_MIPS_R1); |
| } |
| |
| if (link) { |
| op = OPC_JALR; |
| } else { |
| op = OPC_JR; |
| } |
| |
| gen_compute_branch(ctx, op, 2, ra ? 31 : rx, 31, 0, |
| (nd ? 0 : 2)); |
| } |
| break; |
| case RR_SDBBP: |
| if (is_uhi(ctx, extract32(ctx->opcode, 5, 6))) { |
| ctx->base.is_jmp = DISAS_SEMIHOST; |
| } else { |
| /* |
| * XXX: not clear which exception should be raised |
| * when in debug mode... |
| */ |
| check_insn(ctx, ISA_MIPS_R1); |
| generate_exception_end(ctx, EXCP_DBp); |
| } |
| break; |
| case RR_SLT: |
| gen_slt(ctx, OPC_SLT, 24, rx, ry); |
| break; |
| case RR_SLTU: |
| gen_slt(ctx, OPC_SLTU, 24, rx, ry); |
| break; |
| case RR_BREAK: |
| generate_exception_break(ctx, extract32(ctx->opcode, 5, 6)); |
| break; |
| case RR_SLLV: |
| gen_shift(ctx, OPC_SLLV, ry, rx, ry); |
| break; |
| case RR_SRLV: |
| gen_shift(ctx, OPC_SRLV, ry, rx, ry); |
| break; |
| case RR_SRAV: |
| gen_shift(ctx, OPC_SRAV, ry, rx, ry); |
| break; |
| #if defined(TARGET_MIPS64) |
| case RR_DSRL: |
| check_insn(ctx, ISA_MIPS3); |
| check_mips_64(ctx); |
| gen_shift_imm(ctx, OPC_DSRL, ry, ry, sa); |
| break; |
| #endif |
| case RR_CMP: |
| gen_logic(ctx, OPC_XOR, 24, rx, ry); |
| break; |
| case RR_NEG: |
| gen_arith(ctx, OPC_SUBU, rx, 0, ry); |
| break; |
| case RR_AND: |
| gen_logic(ctx, OPC_AND, rx, rx, ry); |
| break; |
| case RR_OR: |
| gen_logic(ctx, OPC_OR, rx, rx, ry); |
| break; |
| case RR_XOR: |
| gen_logic(ctx, OPC_XOR, rx, rx, ry); |
| break; |
| case RR_NOT: |
| gen_logic(ctx, OPC_NOR, rx, ry, 0); |
| break; |
| case RR_MFHI: |
| gen_HILO(ctx, OPC_MFHI, 0, rx); |
| break; |
| case RR_CNVT: |
| check_insn(ctx, ISA_MIPS_R1); |
| switch (cnvt_op) { |
| case RR_RY_CNVT_ZEB: |
| tcg_gen_ext8u_tl(cpu_gpr[rx], cpu_gpr[rx]); |
| break; |
| case RR_RY_CNVT_ZEH: |
| tcg_gen_ext16u_tl(cpu_gpr[rx], cpu_gpr[rx]); |
| break; |
| case RR_RY_CNVT_SEB: |
| tcg_gen_ext8s_tl(cpu_gpr[rx], cpu_gpr[rx]); |
| break; |
| case RR_RY_CNVT_SEH: |
| tcg_gen_ext16s_tl(cpu_gpr[rx], cpu_gpr[rx]); |
| break; |
| #if defined(TARGET_MIPS64) |
| case RR_RY_CNVT_ZEW: |
| check_insn(ctx, ISA_MIPS_R1); |
| check_mips_64(ctx); |
| tcg_gen_ext32u_tl(cpu_gpr[rx], cpu_gpr[rx]); |
| break; |
| case RR_RY_CNVT_SEW: |
| check_insn(ctx, ISA_MIPS_R1); |
| check_mips_64(ctx); |
| tcg_gen_ext32s_tl(cpu_gpr[rx], cpu_gpr[rx]); |
| break; |
| #endif |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| break; |
| case RR_MFLO: |
| gen_HILO(ctx, OPC_MFLO, 0, rx); |
| break; |
| #if defined(TARGET_MIPS64) |
| case RR_DSRA: |
| check_insn(ctx, ISA_MIPS3); |
| check_mips_64(ctx); |
| gen_shift_imm(ctx, OPC_DSRA, ry, ry, sa); |
| break; |
| case RR_DSLLV: |
| check_insn(ctx, ISA_MIPS3); |
| check_mips_64(ctx); |
| gen_shift(ctx, OPC_DSLLV, ry, rx, ry); |
| break; |
| case RR_DSRLV: |
| check_insn(ctx, ISA_MIPS3); |
| check_mips_64(ctx); |
| gen_shift(ctx, OPC_DSRLV, ry, rx, ry); |
| break; |
| case RR_DSRAV: |
| check_insn(ctx, ISA_MIPS3); |
| check_mips_64(ctx); |
| gen_shift(ctx, OPC_DSRAV, ry, rx, ry); |
| break; |
| #endif |
| case RR_MULT: |
| gen_muldiv(ctx, OPC_MULT, 0, rx, ry); |
| break; |
| case RR_MULTU: |
| gen_muldiv(ctx, OPC_MULTU, 0, rx, ry); |
| break; |
| case RR_DIV: |
| gen_muldiv(ctx, OPC_DIV, 0, rx, ry); |
| break; |
| case RR_DIVU: |
| gen_muldiv(ctx, OPC_DIVU, 0, rx, ry); |
| break; |
| #if defined(TARGET_MIPS64) |
| case RR_DMULT: |
| check_insn(ctx, ISA_MIPS3); |
| check_mips_64(ctx); |
| gen_muldiv(ctx, OPC_DMULT, 0, rx, ry); |
| break; |
| case RR_DMULTU: |
| check_insn(ctx, ISA_MIPS3); |
| check_mips_64(ctx); |
| gen_muldiv(ctx, OPC_DMULTU, 0, rx, ry); |
| break; |
| case RR_DDIV: |
| check_insn(ctx, ISA_MIPS3); |
| check_mips_64(ctx); |
| gen_muldiv(ctx, OPC_DDIV, 0, rx, ry); |
| break; |
| case RR_DDIVU: |
| check_insn(ctx, ISA_MIPS3); |
| check_mips_64(ctx); |
| gen_muldiv(ctx, OPC_DDIVU, 0, rx, ry); |
| break; |
| #endif |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| break; |
| case M16_OPC_EXTEND: |
| decode_extended_mips16_opc(env, ctx); |
| n_bytes = 4; |
| break; |
| #if defined(TARGET_MIPS64) |
| case M16_OPC_I64: |
| funct = (ctx->opcode >> 8) & 0x7; |
| decode_i64_mips16(ctx, ry, funct, offset, 0); |
| break; |
| #endif |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| |
| return n_bytes; |
| } |