/* * microMIPS 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 */ /* * microMIPS32/microMIPS64 major opcodes * * 1. MIPS Architecture for Programmers Volume II-B: * The microMIPS32 Instruction Set (Revision 3.05) * * Table 6.2 microMIPS32 Encoding of Major Opcode Field * * 2. MIPS Architecture For Programmers Volume II-A: * The MIPS64 Instruction Set (Revision 3.51) */ enum { POOL32A = 0x00, POOL16A = 0x01, LBU16 = 0x02, MOVE16 = 0x03, ADDI32 = 0x04, R6_LUI = 0x04, AUI = 0x04, LBU32 = 0x05, SB32 = 0x06, LB32 = 0x07, POOL32B = 0x08, POOL16B = 0x09, LHU16 = 0x0a, ANDI16 = 0x0b, ADDIU32 = 0x0c, LHU32 = 0x0d, SH32 = 0x0e, LH32 = 0x0f, POOL32I = 0x10, POOL16C = 0x11, LWSP16 = 0x12, POOL16D = 0x13, ORI32 = 0x14, POOL32F = 0x15, POOL32S = 0x16, /* MIPS64 */ DADDIU32 = 0x17, /* MIPS64 */ POOL32C = 0x18, LWGP16 = 0x19, LW16 = 0x1a, POOL16E = 0x1b, XORI32 = 0x1c, JALS32 = 0x1d, BOVC = 0x1d, BEQC = 0x1d, BEQZALC = 0x1d, ADDIUPC = 0x1e, PCREL = 0x1e, BNVC = 0x1f, BNEC = 0x1f, BNEZALC = 0x1f, R6_BEQZC = 0x20, JIC = 0x20, POOL16F = 0x21, SB16 = 0x22, BEQZ16 = 0x23, BEQZC16 = 0x23, SLTI32 = 0x24, BEQ32 = 0x25, BC = 0x25, SWC132 = 0x26, LWC132 = 0x27, /* 0x29 is reserved */ RES_29 = 0x29, R6_BNEZC = 0x28, JIALC = 0x28, SH16 = 0x2a, BNEZ16 = 0x2b, BNEZC16 = 0x2b, SLTIU32 = 0x2c, BNE32 = 0x2d, BALC = 0x2d, SDC132 = 0x2e, LDC132 = 0x2f, /* 0x31 is reserved */ RES_31 = 0x31, BLEZALC = 0x30, BGEZALC = 0x30, BGEUC = 0x30, SWSP16 = 0x32, B16 = 0x33, BC16 = 0x33, ANDI32 = 0x34, J32 = 0x35, BGTZC = 0x35, BLTZC = 0x35, BLTC = 0x35, SD32 = 0x36, /* MIPS64 */ LD32 = 0x37, /* MIPS64 */ /* 0x39 is reserved */ RES_39 = 0x39, BGTZALC = 0x38, BLTZALC = 0x38, BLTUC = 0x38, SW16 = 0x3a, LI16 = 0x3b, JALX32 = 0x3c, JAL32 = 0x3d, BLEZC = 0x3d, BGEZC = 0x3d, BGEC = 0x3d, SW32 = 0x3e, LW32 = 0x3f }; /* PCREL Instructions perform PC-Relative address calculation. bits 20..16 */ enum { ADDIUPC_00 = 0x00, ADDIUPC_01 = 0x01, ADDIUPC_02 = 0x02, ADDIUPC_03 = 0x03, ADDIUPC_04 = 0x04, ADDIUPC_05 = 0x05, ADDIUPC_06 = 0x06, ADDIUPC_07 = 0x07, AUIPC = 0x1e, ALUIPC = 0x1f, LWPC_08 = 0x08, LWPC_09 = 0x09, LWPC_0A = 0x0A, LWPC_0B = 0x0B, LWPC_0C = 0x0C, LWPC_0D = 0x0D, LWPC_0E = 0x0E, LWPC_0F = 0x0F, }; /* POOL32A encoding of minor opcode field */ enum { /* * These opcodes are distinguished only by bits 9..6; those bits are * what are recorded below. */ SLL32 = 0x0, SRL32 = 0x1, SRA = 0x2, ROTR = 0x3, SELEQZ = 0x5, SELNEZ = 0x6, R6_RDHWR = 0x7, SLLV = 0x0, SRLV = 0x1, SRAV = 0x2, ROTRV = 0x3, ADD = 0x4, ADDU32 = 0x5, SUB = 0x6, SUBU32 = 0x7, MUL = 0x8, AND = 0x9, OR32 = 0xa, NOR = 0xb, XOR32 = 0xc, SLT = 0xd, SLTU = 0xe, MOVN = 0x0, R6_MUL = 0x0, MOVZ = 0x1, MUH = 0x1, MULU = 0x2, MUHU = 0x3, LWXS = 0x4, R6_DIV = 0x4, MOD = 0x5, R6_DIVU = 0x6, MODU = 0x7, /* The following can be distinguished by their lower 6 bits. */ BREAK32 = 0x07, INS = 0x0c, LSA = 0x0f, ALIGN = 0x1f, EXT = 0x2c, POOL32AXF = 0x3c, SIGRIE = 0x3f }; /* POOL32AXF encoding of minor opcode field extension */ /* * 1. MIPS Architecture for Programmers Volume II-B: * The microMIPS32 Instruction Set (Revision 3.05) * * Table 6.5 POOL32Axf Encoding of Minor Opcode Extension Field * * 2. MIPS Architecture for Programmers VolumeIV-e: * The MIPS DSP Application-Specific Extension * to the microMIPS32 Architecture (Revision 2.34) * * Table 5.5 POOL32Axf Encoding of Minor Opcode Extension Field */ enum { /* bits 11..6 */ TEQ = 0x00, TGE = 0x08, TGEU = 0x10, TLT = 0x20, TLTU = 0x28, TNE = 0x30, MFC0 = 0x03, MTC0 = 0x0b, /* begin of microMIPS32 DSP */ /* bits 13..12 for 0x01 */ MFHI_ACC = 0x0, MFLO_ACC = 0x1, MTHI_ACC = 0x2, MTLO_ACC = 0x3, /* bits 13..12 for 0x2a */ MADD_ACC = 0x0, MADDU_ACC = 0x1, MSUB_ACC = 0x2, MSUBU_ACC = 0x3, /* bits 13..12 for 0x32 */ MULT_ACC = 0x0, MULTU_ACC = 0x1, /* end of microMIPS32 DSP */ /* bits 15..12 for 0x2c */ BITSWAP = 0x0, SEB = 0x2, SEH = 0x3, CLO = 0x4, CLZ = 0x5, RDHWR = 0x6, WSBH = 0x7, MULT = 0x8, MULTU = 0x9, DIV = 0xa, DIVU = 0xb, MADD = 0xc, MADDU = 0xd, MSUB = 0xe, MSUBU = 0xf, /* bits 15..12 for 0x34 */ MFC2 = 0x4, MTC2 = 0x5, MFHC2 = 0x8, MTHC2 = 0x9, CFC2 = 0xc, CTC2 = 0xd, /* bits 15..12 for 0x3c */ JALR = 0x0, JR = 0x0, /* alias */ JALRC = 0x0, JRC = 0x0, JALR_HB = 0x1, JALRC_HB = 0x1, JALRS = 0x4, JALRS_HB = 0x5, /* bits 15..12 for 0x05 */ RDPGPR = 0xe, WRPGPR = 0xf, /* bits 15..12 for 0x0d */ TLBP = 0x0, TLBR = 0x1, TLBWI = 0x2, TLBWR = 0x3, TLBINV = 0x4, TLBINVF = 0x5, WAIT = 0x9, IRET = 0xd, DERET = 0xe, ERET = 0xf, /* bits 15..12 for 0x15 */ DMT = 0x0, DVPE = 0x1, EMT = 0x2, EVPE = 0x3, /* bits 15..12 for 0x1d */ DI = 0x4, EI = 0x5, /* bits 15..12 for 0x2d */ SYNC = 0x6, SYSCALL = 0x8, SDBBP = 0xd, /* bits 15..12 for 0x35 */ MFHI32 = 0x0, MFLO32 = 0x1, MTHI32 = 0x2, MTLO32 = 0x3, }; /* POOL32B encoding of minor opcode field (bits 15..12) */ enum { LWC2 = 0x0, LWP = 0x1, LDP = 0x4, LWM32 = 0x5, CACHE = 0x6, LDM = 0x7, SWC2 = 0x8, SWP = 0x9, SDP = 0xc, SWM32 = 0xd, SDM = 0xf }; /* POOL32C encoding of minor opcode field (bits 15..12) */ enum { LWL = 0x0, SWL = 0x8, LWR = 0x1, SWR = 0x9, PREF = 0x2, ST_EVA = 0xa, LL = 0x3, SC = 0xb, LDL = 0x4, SDL = 0xc, LDR = 0x5, SDR = 0xd, LD_EVA = 0x6, LWU = 0xe, LLD = 0x7, SCD = 0xf }; /* POOL32C LD-EVA encoding of minor opcode field (bits 11..9) */ enum { LBUE = 0x0, LHUE = 0x1, LWLE = 0x2, LWRE = 0x3, LBE = 0x4, LHE = 0x5, LLE = 0x6, LWE = 0x7, }; /* POOL32C ST-EVA encoding of minor opcode field (bits 11..9) */ enum { SWLE = 0x0, SWRE = 0x1, PREFE = 0x2, CACHEE = 0x3, SBE = 0x4, SHE = 0x5, SCE = 0x6, SWE = 0x7, }; /* POOL32F encoding of minor opcode field (bits 5..0) */ enum { /* These are the bit 7..6 values */ ADD_FMT = 0x0, SUB_FMT = 0x1, MUL_FMT = 0x2, DIV_FMT = 0x3, /* These are the bit 8..6 values */ MOVN_FMT = 0x0, RSQRT2_FMT = 0x0, MOVF_FMT = 0x0, RINT_FMT = 0x0, SELNEZ_FMT = 0x0, MOVZ_FMT = 0x1, LWXC1 = 0x1, MOVT_FMT = 0x1, CLASS_FMT = 0x1, SELEQZ_FMT = 0x1, PLL_PS = 0x2, SWXC1 = 0x2, SEL_FMT = 0x2, PLU_PS = 0x3, LDXC1 = 0x3, MOVN_FMT_04 = 0x4, PUL_PS = 0x4, SDXC1 = 0x4, RECIP2_FMT = 0x4, MOVZ_FMT_05 = 0x05, PUU_PS = 0x5, LUXC1 = 0x5, CVT_PS_S = 0x6, SUXC1 = 0x6, ADDR_PS = 0x6, PREFX = 0x6, MADDF_FMT = 0x6, MULR_PS = 0x7, MSUBF_FMT = 0x7, MADD_S = 0x01, MADD_D = 0x09, MADD_PS = 0x11, ALNV_PS = 0x19, MSUB_S = 0x21, MSUB_D = 0x29, MSUB_PS = 0x31, NMADD_S = 0x02, NMADD_D = 0x0a, NMADD_PS = 0x12, NMSUB_S = 0x22, NMSUB_D = 0x2a, NMSUB_PS = 0x32, MIN_FMT = 0x3, MAX_FMT = 0xb, MINA_FMT = 0x23, MAXA_FMT = 0x2b, POOL32FXF = 0x3b, CABS_COND_FMT = 0x1c, /* MIPS3D */ C_COND_FMT = 0x3c, CMP_CONDN_S = 0x5, CMP_CONDN_D = 0x15 }; /* POOL32Fxf encoding of minor opcode extension field */ enum { CVT_L = 0x04, RSQRT_FMT = 0x08, FLOOR_L = 0x0c, CVT_PW_PS = 0x1c, CVT_W = 0x24, SQRT_FMT = 0x28, FLOOR_W = 0x2c, CVT_PS_PW = 0x3c, CFC1 = 0x40, RECIP_FMT = 0x48, CEIL_L = 0x4c, CTC1 = 0x60, CEIL_W = 0x6c, MFC1 = 0x80, CVT_S_PL = 0x84, TRUNC_L = 0x8c, MTC1 = 0xa0, CVT_S_PU = 0xa4, TRUNC_W = 0xac, MFHC1 = 0xc0, ROUND_L = 0xcc, MTHC1 = 0xe0, ROUND_W = 0xec, MOV_FMT = 0x01, MOVF = 0x05, ABS_FMT = 0x0d, RSQRT1_FMT = 0x1d, MOVT = 0x25, NEG_FMT = 0x2d, CVT_D = 0x4d, RECIP1_FMT = 0x5d, CVT_S = 0x6d }; /* POOL32I encoding of minor opcode field (bits 25..21) */ enum { BLTZ = 0x00, BLTZAL = 0x01, BGEZ = 0x02, BGEZAL = 0x03, BLEZ = 0x04, BNEZC = 0x05, BGTZ = 0x06, BEQZC = 0x07, TLTI = 0x08, BC1EQZC = 0x08, TGEI = 0x09, BC1NEZC = 0x09, TLTIU = 0x0a, BC2EQZC = 0x0a, TGEIU = 0x0b, BC2NEZC = 0x0a, TNEI = 0x0c, R6_SYNCI = 0x0c, LUI = 0x0d, TEQI = 0x0e, SYNCI = 0x10, BLTZALS = 0x11, BGEZALS = 0x13, BC2F = 0x14, BC2T = 0x15, /* These overlap and are distinguished by bit16 of the instruction */ BC1F = 0x1c, BC1T = 0x1d, BC1ANY2F = 0x1c, BC1ANY2T = 0x1d, BC1ANY4F = 0x1e, BC1ANY4T = 0x1f }; /* POOL16A encoding of minor opcode field */ enum { ADDU16 = 0x0, SUBU16 = 0x1 }; /* POOL16B encoding of minor opcode field */ enum { SLL16 = 0x0, SRL16 = 0x1 }; /* POOL16C encoding of minor opcode field */ enum { NOT16 = 0x00, XOR16 = 0x04, AND16 = 0x08, OR16 = 0x0c, LWM16 = 0x10, SWM16 = 0x14, JR16 = 0x18, JRC16 = 0x1a, JALR16 = 0x1c, JALR16S = 0x1e, MFHI16 = 0x20, MFLO16 = 0x24, BREAK16 = 0x28, SDBBP16 = 0x2c, JRADDIUSP = 0x30 }; /* R6 POOL16C encoding of minor opcode field (bits 0..5) */ enum { R6_NOT16 = 0x00, R6_AND16 = 0x01, R6_LWM16 = 0x02, R6_JRC16 = 0x03, MOVEP = 0x04, MOVEP_05 = 0x05, MOVEP_06 = 0x06, MOVEP_07 = 0x07, R6_XOR16 = 0x08, R6_OR16 = 0x09, R6_SWM16 = 0x0a, JALRC16 = 0x0b, MOVEP_0C = 0x0c, MOVEP_0D = 0x0d, MOVEP_0E = 0x0e, MOVEP_0F = 0x0f, JRCADDIUSP = 0x13, R6_BREAK16 = 0x1b, R6_SDBBP16 = 0x3b }; /* POOL16D encoding of minor opcode field */ enum { ADDIUS5 = 0x0, ADDIUSP = 0x1 }; /* POOL16E encoding of minor opcode field */ enum { ADDIUR2 = 0x0, ADDIUR1SP = 0x1 }; static int mmreg(int r) { static const int map[] = { 16, 17, 2, 3, 4, 5, 6, 7 }; return map[r]; } /* Used for 16-bit store instructions. */ static int mmreg2(int r) { static const int map[] = { 0, 17, 2, 3, 4, 5, 6, 7 }; return map[r]; } #define uMIPS_RD(op) ((op >> 7) & 0x7) #define uMIPS_RS(op) ((op >> 4) & 0x7) #define uMIPS_RS2(op) uMIPS_RS(op) #define uMIPS_RS1(op) ((op >> 1) & 0x7) #define uMIPS_RD5(op) ((op >> 5) & 0x1f) #define uMIPS_RS5(op) (op & 0x1f) /* Signed immediate */ #define SIMM(op, start, width) \ ((int32_t)(((op >> start) & ((~0U) >> (32 - width))) \ << (32 - width)) \ >> (32 - width)) /* Zero-extended immediate */ #define ZIMM(op, start, width) ((op >> start) & ((~0U) >> (32 - width))) static void gen_addiur1sp(DisasContext *ctx) { int rd = mmreg(uMIPS_RD(ctx->opcode)); gen_arith_imm(ctx, OPC_ADDIU, rd, 29, ((ctx->opcode >> 1) & 0x3f) << 2); } static void gen_addiur2(DisasContext *ctx) { static const int decoded_imm[] = { 1, 4, 8, 12, 16, 20, 24, -1 }; int rd = mmreg(uMIPS_RD(ctx->opcode)); int rs = mmreg(uMIPS_RS(ctx->opcode)); gen_arith_imm(ctx, OPC_ADDIU, rd, rs, decoded_imm[ZIMM(ctx->opcode, 1, 3)]); } static void gen_addiusp(DisasContext *ctx) { int encoded = ZIMM(ctx->opcode, 1, 9); int decoded; if (encoded <= 1) { decoded = 256 + encoded; } else if (encoded <= 255) { decoded = encoded; } else if (encoded <= 509) { decoded = encoded - 512; } else { decoded = encoded - 768; } gen_arith_imm(ctx, OPC_ADDIU, 29, 29, decoded << 2); } static void gen_addius5(DisasContext *ctx) { int imm = SIMM(ctx->opcode, 1, 4); int rd = (ctx->opcode >> 5) & 0x1f; gen_arith_imm(ctx, OPC_ADDIU, rd, rd, imm); } static void gen_andi16(DisasContext *ctx) { static const int decoded_imm[] = { 128, 1, 2, 3, 4, 7, 8, 15, 16, 31, 32, 63, 64, 255, 32768, 65535 }; int rd = mmreg(uMIPS_RD(ctx->opcode)); int rs = mmreg(uMIPS_RS(ctx->opcode)); int encoded = ZIMM(ctx->opcode, 0, 4); gen_logic_imm(ctx, OPC_ANDI, rd, rs, decoded_imm[encoded]); } static void gen_ldst_multiple(DisasContext *ctx, uint32_t opc, int reglist, int base, int16_t offset) { TCGv t0, t1; TCGv_i32 t2; if (ctx->hflags & MIPS_HFLAG_BMASK) { gen_reserved_instruction(ctx); return; } t0 = tcg_temp_new(); gen_base_offset_addr(ctx, t0, base, offset); t1 = tcg_constant_tl(reglist); t2 = tcg_constant_i32(ctx->mem_idx); save_cpu_state(ctx, 1); switch (opc) { case LWM32: gen_helper_lwm(tcg_env, t0, t1, t2); break; case SWM32: gen_helper_swm(tcg_env, t0, t1, t2); break; #ifdef TARGET_MIPS64 case LDM: gen_helper_ldm(tcg_env, t0, t1, t2); break; case SDM: gen_helper_sdm(tcg_env, t0, t1, t2); break; #endif } } static void gen_pool16c_insn(DisasContext *ctx) { int rd = mmreg((ctx->opcode >> 3) & 0x7); int rs = mmreg(ctx->opcode & 0x7); switch (((ctx->opcode) >> 4) & 0x3f) { case NOT16 + 0: case NOT16 + 1: case NOT16 + 2: case NOT16 + 3: gen_logic(ctx, OPC_NOR, rd, rs, 0); break; case XOR16 + 0: case XOR16 + 1: case XOR16 + 2: case XOR16 + 3: gen_logic(ctx, OPC_XOR, rd, rd, rs); break; case AND16 + 0: case AND16 + 1: case AND16 + 2: case AND16 + 3: gen_logic(ctx, OPC_AND, rd, rd, rs); break; case OR16 + 0: case OR16 + 1: case OR16 + 2: case OR16 + 3: gen_logic(ctx, OPC_OR, rd, rd, rs); break; case LWM16 + 0: case LWM16 + 1: case LWM16 + 2: case LWM16 + 3: { static const int lwm_convert[] = { 0x11, 0x12, 0x13, 0x14 }; int offset = ZIMM(ctx->opcode, 0, 4); gen_ldst_multiple(ctx, LWM32, lwm_convert[(ctx->opcode >> 4) & 0x3], 29, offset << 2); } break; case SWM16 + 0: case SWM16 + 1: case SWM16 + 2: case SWM16 + 3: { static const int swm_convert[] = { 0x11, 0x12, 0x13, 0x14 }; int offset = ZIMM(ctx->opcode, 0, 4); gen_ldst_multiple(ctx, SWM32, swm_convert[(ctx->opcode >> 4) & 0x3], 29, offset << 2); } break; case JR16 + 0: case JR16 + 1: { int reg = ctx->opcode & 0x1f; gen_compute_branch(ctx, OPC_JR, 2, reg, 0, 0, 4); } break; case JRC16 + 0: case JRC16 + 1: { int reg = ctx->opcode & 0x1f; gen_compute_branch(ctx, OPC_JR, 2, reg, 0, 0, 0); /* * Let normal delay slot handling in our caller take us * to the branch target. */ } break; case JALR16 + 0: case JALR16 + 1: gen_compute_branch(ctx, OPC_JALR, 2, ctx->opcode & 0x1f, 31, 0, 4); ctx->hflags |= MIPS_HFLAG_BDS_STRICT; break; case JALR16S + 0: case JALR16S + 1: gen_compute_branch(ctx, OPC_JALR, 2, ctx->opcode & 0x1f, 31, 0, 2); ctx->hflags |= MIPS_HFLAG_BDS_STRICT; break; case MFHI16 + 0: case MFHI16 + 1: gen_HILO(ctx, OPC_MFHI, 0, uMIPS_RS5(ctx->opcode)); break; case MFLO16 + 0: case MFLO16 + 1: gen_HILO(ctx, OPC_MFLO, 0, uMIPS_RS5(ctx->opcode)); break; case BREAK16: generate_exception_break(ctx, extract32(ctx->opcode, 0, 4)); break; case SDBBP16: if (is_uhi(ctx, extract32(ctx->opcode, 0, 4))) { 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 JRADDIUSP + 0: case JRADDIUSP + 1: { int imm = ZIMM(ctx->opcode, 0, 5); gen_compute_branch(ctx, OPC_JR, 2, 31, 0, 0, 0); gen_arith_imm(ctx, OPC_ADDIU, 29, 29, imm << 2); /* * Let normal delay slot handling in our caller take us * to the branch target. */ } break; default: gen_reserved_instruction(ctx); break; } } static inline void gen_movep(DisasContext *ctx, int enc_dest, int enc_rt, int enc_rs) { int rd, re; static const int rd_enc[] = { 5, 5, 6, 4, 4, 4, 4, 4 }; static const int re_enc[] = { 6, 7, 7, 21, 22, 5, 6, 7 }; static const int rs_rt_enc[] = { 0, 17, 2, 3, 16, 18, 19, 20 }; rd = rd_enc[enc_dest]; re = re_enc[enc_dest]; gen_load_gpr(cpu_gpr[rd], rs_rt_enc[enc_rs]); gen_load_gpr(cpu_gpr[re], rs_rt_enc[enc_rt]); } static void gen_pool16c_r6_insn(DisasContext *ctx) { int rt = mmreg((ctx->opcode >> 7) & 0x7); int rs = mmreg((ctx->opcode >> 4) & 0x7); switch (ctx->opcode & 0xf) { case R6_NOT16: gen_logic(ctx, OPC_NOR, rt, rs, 0); break; case R6_AND16: gen_logic(ctx, OPC_AND, rt, rt, rs); break; case R6_LWM16: { int lwm_converted = 0x11 + extract32(ctx->opcode, 8, 2); int offset = extract32(ctx->opcode, 4, 4); gen_ldst_multiple(ctx, LWM32, lwm_converted, 29, offset << 2); } break; case R6_JRC16: /* JRCADDIUSP */ if ((ctx->opcode >> 4) & 1) { /* JRCADDIUSP */ int imm = extract32(ctx->opcode, 5, 5); gen_compute_branch(ctx, OPC_JR, 2, 31, 0, 0, 0); gen_arith_imm(ctx, OPC_ADDIU, 29, 29, imm << 2); } else { /* JRC16 */ rs = extract32(ctx->opcode, 5, 5); gen_compute_branch(ctx, OPC_JR, 2, rs, 0, 0, 0); } break; case MOVEP: case MOVEP_05: case MOVEP_06: case MOVEP_07: case MOVEP_0C: case MOVEP_0D: case MOVEP_0E: case MOVEP_0F: { int enc_dest = uMIPS_RD(ctx->opcode); int enc_rt = uMIPS_RS2(ctx->opcode); int enc_rs = (ctx->opcode & 3) | ((ctx->opcode >> 1) & 4); gen_movep(ctx, enc_dest, enc_rt, enc_rs); } break; case R6_XOR16: gen_logic(ctx, OPC_XOR, rt, rt, rs); break; case R6_OR16: gen_logic(ctx, OPC_OR, rt, rt, rs); break; case R6_SWM16: { int swm_converted = 0x11 + extract32(ctx->opcode, 8, 2); int offset = extract32(ctx->opcode, 4, 4); gen_ldst_multiple(ctx, SWM32, swm_converted, 29, offset << 2); } break; case JALRC16: /* BREAK16, SDBBP16 */ switch (ctx->opcode & 0x3f) { case JALRC16: case JALRC16 + 0x20: /* JALRC16 */ gen_compute_branch(ctx, OPC_JALR, 2, (ctx->opcode >> 5) & 0x1f, 31, 0, 0); break; case R6_BREAK16: /* BREAK16 */ generate_exception_break(ctx, extract32(ctx->opcode, 6, 4)); break; case R6_SDBBP16: /* SDBBP16 */ if (is_uhi(ctx, extract32(ctx->opcode, 6, 4))) { ctx->base.is_jmp = DISAS_SEMIHOST; } else { if (ctx->hflags & MIPS_HFLAG_SBRI) { generate_exception(ctx, EXCP_RI); } else { generate_exception(ctx, EXCP_DBp); } } break; } break; default: generate_exception(ctx, EXCP_RI); break; } } static void gen_ldst_pair(DisasContext *ctx, uint32_t opc, int rd, int base, int16_t offset) { TCGv t0, t1; if (ctx->hflags & MIPS_HFLAG_BMASK || rd == 31) { gen_reserved_instruction(ctx); return; } t0 = tcg_temp_new(); t1 = tcg_temp_new(); gen_base_offset_addr(ctx, t0, base, offset); switch (opc) { case LWP: if (rd == base) { gen_reserved_instruction(ctx); return; } tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, mo_endian(ctx) | MO_SL | ctx->default_tcg_memop_mask); gen_store_gpr(t1, rd); gen_op_addr_addi(ctx, t0, t0, 4); tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, mo_endian(ctx) | MO_SL | ctx->default_tcg_memop_mask); gen_store_gpr(t1, rd + 1); break; case SWP: gen_load_gpr(t1, rd); tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, mo_endian(ctx) | MO_UL | ctx->default_tcg_memop_mask); gen_op_addr_addi(ctx, t0, t0, 4); gen_load_gpr(t1, rd + 1); tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, mo_endian(ctx) | MO_UL | ctx->default_tcg_memop_mask); break; #ifdef TARGET_MIPS64 case LDP: if (rd == base) { gen_reserved_instruction(ctx); return; } tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, mo_endian(ctx) | MO_UQ | ctx->default_tcg_memop_mask); gen_store_gpr(t1, rd); gen_op_addr_addi(ctx, t0, t0, 8); tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, mo_endian(ctx) | MO_UQ | ctx->default_tcg_memop_mask); gen_store_gpr(t1, rd + 1); break; case SDP: gen_load_gpr(t1, rd); tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, mo_endian(ctx) | MO_UQ | ctx->default_tcg_memop_mask); gen_op_addr_addi(ctx, t0, t0, 8); gen_load_gpr(t1, rd + 1); tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, mo_endian(ctx) | MO_UQ | ctx->default_tcg_memop_mask); break; #endif } } static void gen_pool32axf(CPUMIPSState *env, DisasContext *ctx, int rt, int rs) { int extension = (ctx->opcode >> 6) & 0x3f; int minor = (ctx->opcode >> 12) & 0xf; uint32_t mips32_op; switch (extension) { case TEQ: mips32_op = OPC_TEQ; goto do_trap; case TGE: mips32_op = OPC_TGE; goto do_trap; case TGEU: mips32_op = OPC_TGEU; goto do_trap; case TLT: mips32_op = OPC_TLT; goto do_trap; case TLTU: mips32_op = OPC_TLTU; goto do_trap; case TNE: mips32_op = OPC_TNE; do_trap: gen_trap(ctx, mips32_op, rs, rt, -1, extract32(ctx->opcode, 12, 4)); break; #ifndef CONFIG_USER_ONLY case MFC0: case MFC0 + 32: check_cp0_enabled(ctx); if (rt == 0) { /* Treat as NOP. */ break; } gen_mfc0(ctx, cpu_gpr[rt], rs, (ctx->opcode >> 11) & 0x7); break; case MTC0: case MTC0 + 32: check_cp0_enabled(ctx); { TCGv t0 = tcg_temp_new(); gen_load_gpr(t0, rt); gen_mtc0(ctx, t0, rs, (ctx->opcode >> 11) & 0x7); } break; #endif case 0x2a: switch (minor & 3) { case MADD_ACC: gen_muldiv(ctx, OPC_MADD, (ctx->opcode >> 14) & 3, rs, rt); break; case MADDU_ACC: gen_muldiv(ctx, OPC_MADDU, (ctx->opcode >> 14) & 3, rs, rt); break; case MSUB_ACC: gen_muldiv(ctx, OPC_MSUB, (ctx->opcode >> 14) & 3, rs, rt); break; case MSUBU_ACC: gen_muldiv(ctx, OPC_MSUBU, (ctx->opcode >> 14) & 3, rs, rt); break; default: goto pool32axf_invalid; } break; case 0x32: switch (minor & 3) { case MULT_ACC: gen_muldiv(ctx, OPC_MULT, (ctx->opcode >> 14) & 3, rs, rt); break; case MULTU_ACC: gen_muldiv(ctx, OPC_MULTU, (ctx->opcode >> 14) & 3, rs, rt); break; default: goto pool32axf_invalid; } break; case 0x2c: switch (minor) { case BITSWAP: check_insn(ctx, ISA_MIPS_R6); gen_bitswap(ctx, OPC_BITSWAP, rs, rt); break; case SEB: gen_bshfl(ctx, OPC_SEB, rs, rt); break; case SEH: gen_bshfl(ctx, OPC_SEH, rs, rt); break; case CLO: mips32_op = OPC_CLO; goto do_cl; case CLZ: mips32_op = OPC_CLZ; do_cl: check_insn(ctx, ISA_MIPS_R1); gen_cl(ctx, mips32_op, rt, rs); break; case RDHWR: check_insn_opc_removed(ctx, ISA_MIPS_R6); gen_rdhwr(ctx, rt, rs, 0); break; case WSBH: gen_bshfl(ctx, OPC_WSBH, rs, rt); break; case MULT: check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = OPC_MULT; goto do_mul; case MULTU: check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = OPC_MULTU; goto do_mul; case DIV: check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = OPC_DIV; goto do_div; case DIVU: check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = OPC_DIVU; goto do_div; do_div: check_insn(ctx, ISA_MIPS_R1); gen_muldiv(ctx, mips32_op, 0, rs, rt); break; case MADD: check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = OPC_MADD; goto do_mul; case MADDU: check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = OPC_MADDU; goto do_mul; case MSUB: check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = OPC_MSUB; goto do_mul; case MSUBU: check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = OPC_MSUBU; do_mul: check_insn(ctx, ISA_MIPS_R1); gen_muldiv(ctx, mips32_op, 0, rs, rt); break; default: goto pool32axf_invalid; } break; case 0x34: switch (minor) { case MFC2: case MTC2: case MFHC2: case MTHC2: case CFC2: case CTC2: generate_exception_err(ctx, EXCP_CpU, 2); break; default: goto pool32axf_invalid; } break; case 0x3c: switch (minor) { case JALR: /* JALRC */ case JALR_HB: /* JALRC_HB */ if (ctx->insn_flags & ISA_MIPS_R6) { /* JALRC, JALRC_HB */ gen_compute_branch(ctx, OPC_JALR, 4, rs, rt, 0, 0); } else { /* JALR, JALR_HB */ gen_compute_branch(ctx, OPC_JALR, 4, rs, rt, 0, 4); ctx->hflags |= MIPS_HFLAG_BDS_STRICT; } break; case JALRS: case JALRS_HB: check_insn_opc_removed(ctx, ISA_MIPS_R6); gen_compute_branch(ctx, OPC_JALR, 4, rs, rt, 0, 2); ctx->hflags |= MIPS_HFLAG_BDS_STRICT; break; default: goto pool32axf_invalid; } break; case 0x05: switch (minor) { case RDPGPR: check_cp0_enabled(ctx); check_insn(ctx, ISA_MIPS_R2); gen_load_srsgpr(rs, rt); break; case WRPGPR: check_cp0_enabled(ctx); check_insn(ctx, ISA_MIPS_R2); gen_store_srsgpr(rs, rt); break; default: goto pool32axf_invalid; } break; #ifndef CONFIG_USER_ONLY case 0x0d: switch (minor) { case TLBP: mips32_op = OPC_TLBP; goto do_cp0; case TLBR: mips32_op = OPC_TLBR; goto do_cp0; case TLBWI: mips32_op = OPC_TLBWI; goto do_cp0; case TLBWR: mips32_op = OPC_TLBWR; goto do_cp0; case TLBINV: mips32_op = OPC_TLBINV; goto do_cp0; case TLBINVF: mips32_op = OPC_TLBINVF; goto do_cp0; case WAIT: mips32_op = OPC_WAIT; goto do_cp0; case DERET: mips32_op = OPC_DERET; goto do_cp0; case ERET: mips32_op = OPC_ERET; do_cp0: gen_cp0(env, ctx, mips32_op, rt, rs); break; default: goto pool32axf_invalid; } break; case 0x1d: switch (minor) { case DI: check_cp0_enabled(ctx); { TCGv t0 = tcg_temp_new(); save_cpu_state(ctx, 1); gen_helper_di(t0, tcg_env); gen_store_gpr(t0, rs); /* * Stop translation as we may have switched the execution * mode. */ ctx->base.is_jmp = DISAS_STOP; } break; case EI: check_cp0_enabled(ctx); { TCGv t0 = tcg_temp_new(); save_cpu_state(ctx, 1); gen_helper_ei(t0, tcg_env); gen_store_gpr(t0, rs); /* * DISAS_STOP isn't sufficient, we need to ensure we break out * of translated code to check for pending interrupts. */ gen_save_pc(ctx->base.pc_next + 4); ctx->base.is_jmp = DISAS_EXIT; } break; default: goto pool32axf_invalid; } break; #endif case 0x2d: switch (minor) { case SYNC: gen_sync(extract32(ctx->opcode, 16, 5)); break; case SYSCALL: generate_exception_end(ctx, EXCP_SYSCALL); break; case SDBBP: if (is_uhi(ctx, extract32(ctx->opcode, 16, 10))) { ctx->base.is_jmp = DISAS_SEMIHOST; } else { check_insn(ctx, ISA_MIPS_R1); if (ctx->hflags & MIPS_HFLAG_SBRI) { gen_reserved_instruction(ctx); } else { generate_exception_end(ctx, EXCP_DBp); } } break; default: goto pool32axf_invalid; } break; case 0x01: switch (minor & 3) { case MFHI_ACC: gen_HILO(ctx, OPC_MFHI, minor >> 2, rs); break; case MFLO_ACC: gen_HILO(ctx, OPC_MFLO, minor >> 2, rs); break; case MTHI_ACC: gen_HILO(ctx, OPC_MTHI, minor >> 2, rs); break; case MTLO_ACC: gen_HILO(ctx, OPC_MTLO, minor >> 2, rs); break; default: goto pool32axf_invalid; } break; case 0x35: check_insn_opc_removed(ctx, ISA_MIPS_R6); switch (minor) { case MFHI32: gen_HILO(ctx, OPC_MFHI, 0, rs); break; case MFLO32: gen_HILO(ctx, OPC_MFLO, 0, rs); break; case MTHI32: gen_HILO(ctx, OPC_MTHI, 0, rs); break; case MTLO32: gen_HILO(ctx, OPC_MTLO, 0, rs); break; default: goto pool32axf_invalid; } break; default: pool32axf_invalid: MIPS_INVAL("pool32axf"); gen_reserved_instruction(ctx); break; } } static void gen_pool32fxf(DisasContext *ctx, int rt, int rs) { int extension = (ctx->opcode >> 6) & 0x3ff; uint32_t mips32_op; #define FLOAT_1BIT_FMT(opc, fmt) ((fmt << 8) | opc) #define FLOAT_2BIT_FMT(opc, fmt) ((fmt << 7) | opc) #define COND_FLOAT_MOV(opc, cond) ((cond << 7) | opc) switch (extension) { case FLOAT_1BIT_FMT(CFC1, 0): mips32_op = OPC_CFC1; goto do_cp1; case FLOAT_1BIT_FMT(CTC1, 0): mips32_op = OPC_CTC1; goto do_cp1; case FLOAT_1BIT_FMT(MFC1, 0): mips32_op = OPC_MFC1; goto do_cp1; case FLOAT_1BIT_FMT(MTC1, 0): mips32_op = OPC_MTC1; goto do_cp1; case FLOAT_1BIT_FMT(MFHC1, 0): mips32_op = OPC_MFHC1; goto do_cp1; case FLOAT_1BIT_FMT(MTHC1, 0): mips32_op = OPC_MTHC1; do_cp1: gen_cp1(ctx, mips32_op, rt, rs); break; /* Reciprocal square root */ case FLOAT_1BIT_FMT(RSQRT_FMT, FMT_SD_S): mips32_op = OPC_RSQRT_S; goto do_unaryfp; case FLOAT_1BIT_FMT(RSQRT_FMT, FMT_SD_D): mips32_op = OPC_RSQRT_D; goto do_unaryfp; /* Square root */ case FLOAT_1BIT_FMT(SQRT_FMT, FMT_SD_S): mips32_op = OPC_SQRT_S; goto do_unaryfp; case FLOAT_1BIT_FMT(SQRT_FMT, FMT_SD_D): mips32_op = OPC_SQRT_D; goto do_unaryfp; /* Reciprocal */ case FLOAT_1BIT_FMT(RECIP_FMT, FMT_SD_S): mips32_op = OPC_RECIP_S; goto do_unaryfp; case FLOAT_1BIT_FMT(RECIP_FMT, FMT_SD_D): mips32_op = OPC_RECIP_D; goto do_unaryfp; /* Floor */ case FLOAT_1BIT_FMT(FLOOR_L, FMT_SD_S): mips32_op = OPC_FLOOR_L_S; goto do_unaryfp; case FLOAT_1BIT_FMT(FLOOR_L, FMT_SD_D): mips32_op = OPC_FLOOR_L_D; goto do_unaryfp; case FLOAT_1BIT_FMT(FLOOR_W, FMT_SD_S): mips32_op = OPC_FLOOR_W_S; goto do_unaryfp; case FLOAT_1BIT_FMT(FLOOR_W, FMT_SD_D): mips32_op = OPC_FLOOR_W_D; goto do_unaryfp; /* Ceiling */ case FLOAT_1BIT_FMT(CEIL_L, FMT_SD_S): mips32_op = OPC_CEIL_L_S; goto do_unaryfp; case FLOAT_1BIT_FMT(CEIL_L, FMT_SD_D): mips32_op = OPC_CEIL_L_D; goto do_unaryfp; case FLOAT_1BIT_FMT(CEIL_W, FMT_SD_S): mips32_op = OPC_CEIL_W_S; goto do_unaryfp; case FLOAT_1BIT_FMT(CEIL_W, FMT_SD_D): mips32_op = OPC_CEIL_W_D; goto do_unaryfp; /* Truncation */ case FLOAT_1BIT_FMT(TRUNC_L, FMT_SD_S): mips32_op = OPC_TRUNC_L_S; goto do_unaryfp; case FLOAT_1BIT_FMT(TRUNC_L, FMT_SD_D): mips32_op = OPC_TRUNC_L_D; goto do_unaryfp; case FLOAT_1BIT_FMT(TRUNC_W, FMT_SD_S): mips32_op = OPC_TRUNC_W_S; goto do_unaryfp; case FLOAT_1BIT_FMT(TRUNC_W, FMT_SD_D): mips32_op = OPC_TRUNC_W_D; goto do_unaryfp; /* Round */ case FLOAT_1BIT_FMT(ROUND_L, FMT_SD_S): mips32_op = OPC_ROUND_L_S; goto do_unaryfp; case FLOAT_1BIT_FMT(ROUND_L, FMT_SD_D): mips32_op = OPC_ROUND_L_D; goto do_unaryfp; case FLOAT_1BIT_FMT(ROUND_W, FMT_SD_S): mips32_op = OPC_ROUND_W_S; goto do_unaryfp; case FLOAT_1BIT_FMT(ROUND_W, FMT_SD_D): mips32_op = OPC_ROUND_W_D; goto do_unaryfp; /* Integer to floating-point conversion */ case FLOAT_1BIT_FMT(CVT_L, FMT_SD_S): mips32_op = OPC_CVT_L_S; goto do_unaryfp; case FLOAT_1BIT_FMT(CVT_L, FMT_SD_D): mips32_op = OPC_CVT_L_D; goto do_unaryfp; case FLOAT_1BIT_FMT(CVT_W, FMT_SD_S): mips32_op = OPC_CVT_W_S; goto do_unaryfp; case FLOAT_1BIT_FMT(CVT_W, FMT_SD_D): mips32_op = OPC_CVT_W_D; goto do_unaryfp; /* Paired-foo conversions */ case FLOAT_1BIT_FMT(CVT_S_PL, 0): mips32_op = OPC_CVT_S_PL; goto do_unaryfp; case FLOAT_1BIT_FMT(CVT_S_PU, 0): mips32_op = OPC_CVT_S_PU; goto do_unaryfp; case FLOAT_1BIT_FMT(CVT_PW_PS, 0): mips32_op = OPC_CVT_PW_PS; goto do_unaryfp; case FLOAT_1BIT_FMT(CVT_PS_PW, 0): mips32_op = OPC_CVT_PS_PW; goto do_unaryfp; /* Floating-point moves */ case FLOAT_2BIT_FMT(MOV_FMT, FMT_SDPS_S): mips32_op = OPC_MOV_S; goto do_unaryfp; case FLOAT_2BIT_FMT(MOV_FMT, FMT_SDPS_D): mips32_op = OPC_MOV_D; goto do_unaryfp; case FLOAT_2BIT_FMT(MOV_FMT, FMT_SDPS_PS): mips32_op = OPC_MOV_PS; goto do_unaryfp; /* Absolute value */ case FLOAT_2BIT_FMT(ABS_FMT, FMT_SDPS_S): mips32_op = OPC_ABS_S; goto do_unaryfp; case FLOAT_2BIT_FMT(ABS_FMT, FMT_SDPS_D): mips32_op = OPC_ABS_D; goto do_unaryfp; case FLOAT_2BIT_FMT(ABS_FMT, FMT_SDPS_PS): mips32_op = OPC_ABS_PS; goto do_unaryfp; /* Negation */ case FLOAT_2BIT_FMT(NEG_FMT, FMT_SDPS_S): mips32_op = OPC_NEG_S; goto do_unaryfp; case FLOAT_2BIT_FMT(NEG_FMT, FMT_SDPS_D): mips32_op = OPC_NEG_D; goto do_unaryfp; case FLOAT_2BIT_FMT(NEG_FMT, FMT_SDPS_PS): mips32_op = OPC_NEG_PS; goto do_unaryfp; /* Reciprocal square root step */ case FLOAT_2BIT_FMT(RSQRT1_FMT, FMT_SDPS_S): mips32_op = OPC_RSQRT1_S; goto do_unaryfp; case FLOAT_2BIT_FMT(RSQRT1_FMT, FMT_SDPS_D): mips32_op = OPC_RSQRT1_D; goto do_unaryfp; case FLOAT_2BIT_FMT(RSQRT1_FMT, FMT_SDPS_PS): mips32_op = OPC_RSQRT1_PS; goto do_unaryfp; /* Reciprocal step */ case FLOAT_2BIT_FMT(RECIP1_FMT, FMT_SDPS_S): mips32_op = OPC_RECIP1_S; goto do_unaryfp; case FLOAT_2BIT_FMT(RECIP1_FMT, FMT_SDPS_D): mips32_op = OPC_RECIP1_S; goto do_unaryfp; case FLOAT_2BIT_FMT(RECIP1_FMT, FMT_SDPS_PS): mips32_op = OPC_RECIP1_PS; goto do_unaryfp; /* Conversions from double */ case FLOAT_2BIT_FMT(CVT_D, FMT_SWL_S): mips32_op = OPC_CVT_D_S; goto do_unaryfp; case FLOAT_2BIT_FMT(CVT_D, FMT_SWL_W): mips32_op = OPC_CVT_D_W; goto do_unaryfp; case FLOAT_2BIT_FMT(CVT_D, FMT_SWL_L): mips32_op = OPC_CVT_D_L; goto do_unaryfp; /* Conversions from single */ case FLOAT_2BIT_FMT(CVT_S, FMT_DWL_D): mips32_op = OPC_CVT_S_D; goto do_unaryfp; case FLOAT_2BIT_FMT(CVT_S, FMT_DWL_W): mips32_op = OPC_CVT_S_W; goto do_unaryfp; case FLOAT_2BIT_FMT(CVT_S, FMT_DWL_L): mips32_op = OPC_CVT_S_L; do_unaryfp: gen_farith(ctx, mips32_op, -1, rs, rt, 0); break; /* Conditional moves on floating-point codes */ case COND_FLOAT_MOV(MOVT, 0): case COND_FLOAT_MOV(MOVT, 1): case COND_FLOAT_MOV(MOVT, 2): case COND_FLOAT_MOV(MOVT, 3): case COND_FLOAT_MOV(MOVT, 4): case COND_FLOAT_MOV(MOVT, 5): case COND_FLOAT_MOV(MOVT, 6): case COND_FLOAT_MOV(MOVT, 7): check_insn_opc_removed(ctx, ISA_MIPS_R6); gen_movci(ctx, rt, rs, (ctx->opcode >> 13) & 0x7, 1); break; case COND_FLOAT_MOV(MOVF, 0): case COND_FLOAT_MOV(MOVF, 1): case COND_FLOAT_MOV(MOVF, 2): case COND_FLOAT_MOV(MOVF, 3): case COND_FLOAT_MOV(MOVF, 4): case COND_FLOAT_MOV(MOVF, 5): case COND_FLOAT_MOV(MOVF, 6): case COND_FLOAT_MOV(MOVF, 7): check_insn_opc_removed(ctx, ISA_MIPS_R6); gen_movci(ctx, rt, rs, (ctx->opcode >> 13) & 0x7, 0); break; default: MIPS_INVAL("pool32fxf"); gen_reserved_instruction(ctx); break; } } static void decode_micromips32_opc(CPUMIPSState *env, DisasContext *ctx) { int32_t offset; uint16_t insn; int rt, rs, rd, rr; int16_t imm; uint32_t op, minor, minor2, mips32_op; uint32_t cond, fmt, cc; insn = translator_lduw(env, &ctx->base, ctx->base.pc_next + 2); ctx->opcode = (ctx->opcode << 16) | insn; rt = (ctx->opcode >> 21) & 0x1f; rs = (ctx->opcode >> 16) & 0x1f; rd = (ctx->opcode >> 11) & 0x1f; rr = (ctx->opcode >> 6) & 0x1f; imm = (int16_t) ctx->opcode; op = (ctx->opcode >> 26) & 0x3f; switch (op) { case POOL32A: minor = ctx->opcode & 0x3f; switch (minor) { case 0x00: minor = (ctx->opcode >> 6) & 0xf; switch (minor) { case SLL32: mips32_op = OPC_SLL; goto do_shifti; case SRA: mips32_op = OPC_SRA; goto do_shifti; case SRL32: mips32_op = OPC_SRL; goto do_shifti; case ROTR: mips32_op = OPC_ROTR; do_shifti: gen_shift_imm(ctx, mips32_op, rt, rs, rd); break; case SELEQZ: check_insn(ctx, ISA_MIPS_R6); gen_cond_move(ctx, OPC_SELEQZ, rd, rs, rt); break; case SELNEZ: check_insn(ctx, ISA_MIPS_R6); gen_cond_move(ctx, OPC_SELNEZ, rd, rs, rt); break; case R6_RDHWR: check_insn(ctx, ISA_MIPS_R6); gen_rdhwr(ctx, rt, rs, extract32(ctx->opcode, 11, 3)); break; default: goto pool32a_invalid; } break; case 0x10: minor = (ctx->opcode >> 6) & 0xf; switch (minor) { /* Arithmetic */ case ADD: mips32_op = OPC_ADD; goto do_arith; case ADDU32: mips32_op = OPC_ADDU; goto do_arith; case SUB: mips32_op = OPC_SUB; goto do_arith; case SUBU32: mips32_op = OPC_SUBU; goto do_arith; case MUL: check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = OPC_MUL; do_arith: gen_arith(ctx, mips32_op, rd, rs, rt); break; /* Shifts */ case SLLV: mips32_op = OPC_SLLV; goto do_shift; case SRLV: mips32_op = OPC_SRLV; goto do_shift; case SRAV: mips32_op = OPC_SRAV; goto do_shift; case ROTRV: mips32_op = OPC_ROTRV; do_shift: gen_shift(ctx, mips32_op, rd, rs, rt); break; /* Logical operations */ case AND: mips32_op = OPC_AND; goto do_logic; case OR32: mips32_op = OPC_OR; goto do_logic; case NOR: mips32_op = OPC_NOR; goto do_logic; case XOR32: mips32_op = OPC_XOR; do_logic: gen_logic(ctx, mips32_op, rd, rs, rt); break; /* Set less than */ case SLT: mips32_op = OPC_SLT; goto do_slt; case SLTU: mips32_op = OPC_SLTU; do_slt: gen_slt(ctx, mips32_op, rd, rs, rt); break; default: goto pool32a_invalid; } break; case 0x18: minor = (ctx->opcode >> 6) & 0xf; switch (minor) { /* Conditional moves */ case MOVN: /* MUL */ if (ctx->insn_flags & ISA_MIPS_R6) { /* MUL */ gen_r6_muldiv(ctx, R6_OPC_MUL, rd, rs, rt); } else { /* MOVN */ gen_cond_move(ctx, OPC_MOVN, rd, rs, rt); } break; case MOVZ: /* MUH */ if (ctx->insn_flags & ISA_MIPS_R6) { /* MUH */ gen_r6_muldiv(ctx, R6_OPC_MUH, rd, rs, rt); } else { /* MOVZ */ gen_cond_move(ctx, OPC_MOVZ, rd, rs, rt); } break; case MULU: check_insn(ctx, ISA_MIPS_R6); gen_r6_muldiv(ctx, R6_OPC_MULU, rd, rs, rt); break; case MUHU: check_insn(ctx, ISA_MIPS_R6); gen_r6_muldiv(ctx, R6_OPC_MUHU, rd, rs, rt); break; case LWXS: /* DIV */ if (ctx->insn_flags & ISA_MIPS_R6) { /* DIV */ gen_r6_muldiv(ctx, R6_OPC_DIV, rd, rs, rt); } else { /* LWXS */ gen_ldxs(ctx, rs, rt, rd); } break; case MOD: check_insn(ctx, ISA_MIPS_R6); gen_r6_muldiv(ctx, R6_OPC_MOD, rd, rs, rt); break; case R6_DIVU: check_insn(ctx, ISA_MIPS_R6); gen_r6_muldiv(ctx, R6_OPC_DIVU, rd, rs, rt); break; case MODU: check_insn(ctx, ISA_MIPS_R6); gen_r6_muldiv(ctx, R6_OPC_MODU, rd, rs, rt); break; default: goto pool32a_invalid; } break; case INS: gen_bitops(ctx, OPC_INS, rt, rs, rr, rd); return; case LSA: check_insn(ctx, ISA_MIPS_R6); gen_lsa(ctx, rd, rt, rs, extract32(ctx->opcode, 9, 2)); break; case ALIGN: check_insn(ctx, ISA_MIPS_R6); gen_align(ctx, 32, rd, rs, rt, extract32(ctx->opcode, 9, 2)); break; case EXT: gen_bitops(ctx, OPC_EXT, rt, rs, rr, rd); return; case POOL32AXF: gen_pool32axf(env, ctx, rt, rs); break; case BREAK32: generate_exception_break(ctx, extract32(ctx->opcode, 6, 20)); break; case SIGRIE: check_insn(ctx, ISA_MIPS_R6); gen_reserved_instruction(ctx); break; default: pool32a_invalid: MIPS_INVAL("pool32a"); gen_reserved_instruction(ctx); break; } break; case POOL32B: minor = (ctx->opcode >> 12) & 0xf; switch (minor) { case CACHE: check_cp0_enabled(ctx); if (ctx->hflags & MIPS_HFLAG_ITC_CACHE) { gen_cache_operation(ctx, rt, rs, imm); } break; case LWC2: case SWC2: /* COP2: Not implemented. */ generate_exception_err(ctx, EXCP_CpU, 2); break; #ifdef TARGET_MIPS64 case LDP: case SDP: check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); #endif /* fall through */ case LWP: case SWP: gen_ldst_pair(ctx, minor, rt, rs, SIMM(ctx->opcode, 0, 12)); break; #ifdef TARGET_MIPS64 case LDM: case SDM: check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); #endif /* fall through */ case LWM32: case SWM32: gen_ldst_multiple(ctx, minor, rt, rs, SIMM(ctx->opcode, 0, 12)); break; default: MIPS_INVAL("pool32b"); gen_reserved_instruction(ctx); break; } break; case POOL32F: if (ctx->CP0_Config1 & (1 << CP0C1_FP)) { minor = ctx->opcode & 0x3f; check_cp1_enabled(ctx); switch (minor) { case ALNV_PS: check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = OPC_ALNV_PS; goto do_madd; case MADD_S: check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = OPC_MADD_S; goto do_madd; case MADD_D: check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = OPC_MADD_D; goto do_madd; case MADD_PS: check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = OPC_MADD_PS; goto do_madd; case MSUB_S: check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = OPC_MSUB_S; goto do_madd; case MSUB_D: check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = OPC_MSUB_D; goto do_madd; case MSUB_PS: check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = OPC_MSUB_PS; goto do_madd; case NMADD_S: check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = OPC_NMADD_S; goto do_madd; case NMADD_D: check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = OPC_NMADD_D; goto do_madd; case NMADD_PS: check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = OPC_NMADD_PS; goto do_madd; case NMSUB_S: check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = OPC_NMSUB_S; goto do_madd; case NMSUB_D: check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = OPC_NMSUB_D; goto do_madd; case NMSUB_PS: check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = OPC_NMSUB_PS; do_madd: gen_flt3_arith(ctx, mips32_op, rd, rr, rs, rt); break; case CABS_COND_FMT: check_insn_opc_removed(ctx, ISA_MIPS_R6); cond = (ctx->opcode >> 6) & 0xf; cc = (ctx->opcode >> 13) & 0x7; fmt = (ctx->opcode >> 10) & 0x3; switch (fmt) { case 0x0: gen_cmpabs_s(ctx, cond, rt, rs, cc); break; case 0x1: gen_cmpabs_d(ctx, cond, rt, rs, cc); break; case 0x2: gen_cmpabs_ps(ctx, cond, rt, rs, cc); break; default: goto pool32f_invalid; } break; case C_COND_FMT: check_insn_opc_removed(ctx, ISA_MIPS_R6); cond = (ctx->opcode >> 6) & 0xf; cc = (ctx->opcode >> 13) & 0x7; fmt = (ctx->opcode >> 10) & 0x3; switch (fmt) { case 0x0: gen_cmp_s(ctx, cond, rt, rs, cc); break; case 0x1: gen_cmp_d(ctx, cond, rt, rs, cc); break; case 0x2: gen_cmp_ps(ctx, cond, rt, rs, cc); break; default: goto pool32f_invalid; } break; case CMP_CONDN_S: check_insn(ctx, ISA_MIPS_R6); gen_r6_cmp_s(ctx, (ctx->opcode >> 6) & 0x1f, rt, rs, rd); break; case CMP_CONDN_D: check_insn(ctx, ISA_MIPS_R6); gen_r6_cmp_d(ctx, (ctx->opcode >> 6) & 0x1f, rt, rs, rd); break; case POOL32FXF: gen_pool32fxf(ctx, rt, rs); break; case 0x00: /* PLL foo */ switch ((ctx->opcode >> 6) & 0x7) { case PLL_PS: mips32_op = OPC_PLL_PS; goto do_ps; case PLU_PS: mips32_op = OPC_PLU_PS; goto do_ps; case PUL_PS: mips32_op = OPC_PUL_PS; goto do_ps; case PUU_PS: mips32_op = OPC_PUU_PS; goto do_ps; case CVT_PS_S: check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = OPC_CVT_PS_S; do_ps: gen_farith(ctx, mips32_op, rt, rs, rd, 0); break; default: goto pool32f_invalid; } break; case MIN_FMT: check_insn(ctx, ISA_MIPS_R6); switch ((ctx->opcode >> 9) & 0x3) { case FMT_SDPS_S: gen_farith(ctx, OPC_MIN_S, rt, rs, rd, 0); break; case FMT_SDPS_D: gen_farith(ctx, OPC_MIN_D, rt, rs, rd, 0); break; default: goto pool32f_invalid; } break; case 0x08: /* [LS][WDU]XC1 */ switch ((ctx->opcode >> 6) & 0x7) { case LWXC1: check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = OPC_LWXC1; goto do_ldst_cp1; case SWXC1: check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = OPC_SWXC1; goto do_ldst_cp1; case LDXC1: check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = OPC_LDXC1; goto do_ldst_cp1; case SDXC1: check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = OPC_SDXC1; goto do_ldst_cp1; case LUXC1: check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = OPC_LUXC1; goto do_ldst_cp1; case SUXC1: check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = OPC_SUXC1; do_ldst_cp1: gen_flt3_ldst(ctx, mips32_op, rd, rd, rt, rs); break; default: goto pool32f_invalid; } break; case MAX_FMT: check_insn(ctx, ISA_MIPS_R6); switch ((ctx->opcode >> 9) & 0x3) { case FMT_SDPS_S: gen_farith(ctx, OPC_MAX_S, rt, rs, rd, 0); break; case FMT_SDPS_D: gen_farith(ctx, OPC_MAX_D, rt, rs, rd, 0); break; default: goto pool32f_invalid; } break; case 0x18: /* 3D insns */ check_insn_opc_removed(ctx, ISA_MIPS_R6); fmt = (ctx->opcode >> 9) & 0x3; switch ((ctx->opcode >> 6) & 0x7) { case RSQRT2_FMT: switch (fmt) { case FMT_SDPS_S: mips32_op = OPC_RSQRT2_S; goto do_3d; case FMT_SDPS_D: mips32_op = OPC_RSQRT2_D; goto do_3d; case FMT_SDPS_PS: mips32_op = OPC_RSQRT2_PS; goto do_3d; default: goto pool32f_invalid; } break; case RECIP2_FMT: switch (fmt) { case FMT_SDPS_S: mips32_op = OPC_RECIP2_S; goto do_3d; case FMT_SDPS_D: mips32_op = OPC_RECIP2_D; goto do_3d; case FMT_SDPS_PS: mips32_op = OPC_RECIP2_PS; goto do_3d; default: goto pool32f_invalid; } break; case ADDR_PS: mips32_op = OPC_ADDR_PS; goto do_3d; case MULR_PS: mips32_op = OPC_MULR_PS; do_3d: gen_farith(ctx, mips32_op, rt, rs, rd, 0); break; default: goto pool32f_invalid; } break; case 0x20: /* MOV[FT].fmt, PREFX, RINT.fmt, CLASS.fmt*/ cc = (ctx->opcode >> 13) & 0x7; fmt = (ctx->opcode >> 9) & 0x3; switch ((ctx->opcode >> 6) & 0x7) { case MOVF_FMT: /* RINT_FMT */ if (ctx->insn_flags & ISA_MIPS_R6) { /* RINT_FMT */ switch (fmt) { case FMT_SDPS_S: gen_farith(ctx, OPC_RINT_S, 0, rt, rs, 0); break; case FMT_SDPS_D: gen_farith(ctx, OPC_RINT_D, 0, rt, rs, 0); break; default: goto pool32f_invalid; } } else { /* MOVF_FMT */ switch (fmt) { case FMT_SDPS_S: gen_movcf_s(ctx, rs, rt, cc, 0); break; case FMT_SDPS_D: gen_movcf_d(ctx, rs, rt, cc, 0); break; case FMT_SDPS_PS: check_ps(ctx); gen_movcf_ps(ctx, rs, rt, cc, 0); break; default: goto pool32f_invalid; } } break; case MOVT_FMT: /* CLASS_FMT */ if (ctx->insn_flags & ISA_MIPS_R6) { /* CLASS_FMT */ switch (fmt) { case FMT_SDPS_S: gen_farith(ctx, OPC_CLASS_S, 0, rt, rs, 0); break; case FMT_SDPS_D: gen_farith(ctx, OPC_CLASS_D, 0, rt, rs, 0); break; default: goto pool32f_invalid; } } else { /* MOVT_FMT */ switch (fmt) { case FMT_SDPS_S: gen_movcf_s(ctx, rs, rt, cc, 1); break; case FMT_SDPS_D: gen_movcf_d(ctx, rs, rt, cc, 1); break; case FMT_SDPS_PS: check_ps(ctx); gen_movcf_ps(ctx, rs, rt, cc, 1); break; default: goto pool32f_invalid; } } break; case PREFX: check_insn_opc_removed(ctx, ISA_MIPS_R6); break; default: goto pool32f_invalid; } break; #define FINSN_3ARG_SDPS(prfx) \ switch ((ctx->opcode >> 8) & 0x3) { \ case FMT_SDPS_S: \ mips32_op = OPC_##prfx##_S; \ goto do_fpop; \ case FMT_SDPS_D: \ mips32_op = OPC_##prfx##_D; \ goto do_fpop; \ case FMT_SDPS_PS: \ check_ps(ctx); \ mips32_op = OPC_##prfx##_PS; \ goto do_fpop; \ default: \ goto pool32f_invalid; \ } case MINA_FMT: check_insn(ctx, ISA_MIPS_R6); switch ((ctx->opcode >> 9) & 0x3) { case FMT_SDPS_S: gen_farith(ctx, OPC_MINA_S, rt, rs, rd, 0); break; case FMT_SDPS_D: gen_farith(ctx, OPC_MINA_D, rt, rs, rd, 0); break; default: goto pool32f_invalid; } break; case MAXA_FMT: check_insn(ctx, ISA_MIPS_R6); switch ((ctx->opcode >> 9) & 0x3) { case FMT_SDPS_S: gen_farith(ctx, OPC_MAXA_S, rt, rs, rd, 0); break; case FMT_SDPS_D: gen_farith(ctx, OPC_MAXA_D, rt, rs, rd, 0); break; default: goto pool32f_invalid; } break; case 0x30: /* regular FP ops */ switch ((ctx->opcode >> 6) & 0x3) { case ADD_FMT: FINSN_3ARG_SDPS(ADD); break; case SUB_FMT: FINSN_3ARG_SDPS(SUB); break; case MUL_FMT: FINSN_3ARG_SDPS(MUL); break; case DIV_FMT: fmt = (ctx->opcode >> 8) & 0x3; if (fmt == 1) { mips32_op = OPC_DIV_D; } else if (fmt == 0) { mips32_op = OPC_DIV_S; } else { goto pool32f_invalid; } goto do_fpop; default: goto pool32f_invalid; } break; case 0x38: /* cmovs */ switch ((ctx->opcode >> 6) & 0x7) { case MOVN_FMT: /* SELEQZ_FMT */ if (ctx->insn_flags & ISA_MIPS_R6) { /* SELEQZ_FMT */ switch ((ctx->opcode >> 9) & 0x3) { case FMT_SDPS_S: gen_sel_s(ctx, OPC_SELEQZ_S, rd, rt, rs); break; case FMT_SDPS_D: gen_sel_d(ctx, OPC_SELEQZ_D, rd, rt, rs); break; default: goto pool32f_invalid; } } else { /* MOVN_FMT */ FINSN_3ARG_SDPS(MOVN); } break; case MOVN_FMT_04: check_insn_opc_removed(ctx, ISA_MIPS_R6); FINSN_3ARG_SDPS(MOVN); break; case MOVZ_FMT: /* SELNEZ_FMT */ if (ctx->insn_flags & ISA_MIPS_R6) { /* SELNEZ_FMT */ switch ((ctx->opcode >> 9) & 0x3) { case FMT_SDPS_S: gen_sel_s(ctx, OPC_SELNEZ_S, rd, rt, rs); break; case FMT_SDPS_D: gen_sel_d(ctx, OPC_SELNEZ_D, rd, rt, rs); break; default: goto pool32f_invalid; } } else { /* MOVZ_FMT */ FINSN_3ARG_SDPS(MOVZ); } break; case MOVZ_FMT_05: check_insn_opc_removed(ctx, ISA_MIPS_R6); FINSN_3ARG_SDPS(MOVZ); break; case SEL_FMT: check_insn(ctx, ISA_MIPS_R6); switch ((ctx->opcode >> 9) & 0x3) { case FMT_SDPS_S: gen_sel_s(ctx, OPC_SEL_S, rd, rt, rs); break; case FMT_SDPS_D: gen_sel_d(ctx, OPC_SEL_D, rd, rt, rs); break; default: goto pool32f_invalid; } break; case MADDF_FMT: check_insn(ctx, ISA_MIPS_R6); switch ((ctx->opcode >> 9) & 0x3) { case FMT_SDPS_S: mips32_op = OPC_MADDF_S; goto do_fpop; case FMT_SDPS_D: mips32_op = OPC_MADDF_D; goto do_fpop; default: goto pool32f_invalid; } break; case MSUBF_FMT: check_insn(ctx, ISA_MIPS_R6); switch ((ctx->opcode >> 9) & 0x3) { case FMT_SDPS_S: mips32_op = OPC_MSUBF_S; goto do_fpop; case FMT_SDPS_D: mips32_op = OPC_MSUBF_D; goto do_fpop; default: goto pool32f_invalid; } break; default: goto pool32f_invalid; } break; do_fpop: gen_farith(ctx, mips32_op, rt, rs, rd, 0); break; default: pool32f_invalid: MIPS_INVAL("pool32f"); gen_reserved_instruction(ctx); break; } } else { generate_exception_err(ctx, EXCP_CpU, 1); } break; case POOL32I: minor = (ctx->opcode >> 21) & 0x1f; switch (minor) { case BLTZ: check_insn_opc_removed(ctx, ISA_MIPS_R6); gen_compute_branch(ctx, OPC_BLTZ, 4, rs, -1, imm << 1, 4); break; case BLTZAL: check_insn_opc_removed(ctx, ISA_MIPS_R6); gen_compute_branch(ctx, OPC_BLTZAL, 4, rs, -1, imm << 1, 4); ctx->hflags |= MIPS_HFLAG_BDS_STRICT; break; case BLTZALS: check_insn_opc_removed(ctx, ISA_MIPS_R6); gen_compute_branch(ctx, OPC_BLTZAL, 4, rs, -1, imm << 1, 2); ctx->hflags |= MIPS_HFLAG_BDS_STRICT; break; case BGEZ: check_insn_opc_removed(ctx, ISA_MIPS_R6); gen_compute_branch(ctx, OPC_BGEZ, 4, rs, -1, imm << 1, 4); break; case BGEZAL: check_insn_opc_removed(ctx, ISA_MIPS_R6); gen_compute_branch(ctx, OPC_BGEZAL, 4, rs, -1, imm << 1, 4); ctx->hflags |= MIPS_HFLAG_BDS_STRICT; break; case BGEZALS: check_insn_opc_removed(ctx, ISA_MIPS_R6); gen_compute_branch(ctx, OPC_BGEZAL, 4, rs, -1, imm << 1, 2); ctx->hflags |= MIPS_HFLAG_BDS_STRICT; break; case BLEZ: check_insn_opc_removed(ctx, ISA_MIPS_R6); gen_compute_branch(ctx, OPC_BLEZ, 4, rs, -1, imm << 1, 4); break; case BGTZ: check_insn_opc_removed(ctx, ISA_MIPS_R6); gen_compute_branch(ctx, OPC_BGTZ, 4, rs, -1, imm << 1, 4); break; /* Traps */ case TLTI: /* BC1EQZC */ if (ctx->insn_flags & ISA_MIPS_R6) { /* BC1EQZC */ check_cp1_enabled(ctx); gen_compute_branch1_r6(ctx, OPC_BC1EQZ, rs, imm << 1, 0); } else { /* TLTI */ mips32_op = OPC_TLTI; goto do_trapi; } break; case TGEI: /* BC1NEZC */ if (ctx->insn_flags & ISA_MIPS_R6) { /* BC1NEZC */ check_cp1_enabled(ctx); gen_compute_branch1_r6(ctx, OPC_BC1NEZ, rs, imm << 1, 0); } else { /* TGEI */ mips32_op = OPC_TGEI; goto do_trapi; } break; case TLTIU: check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = OPC_TLTIU; goto do_trapi; case TGEIU: check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = OPC_TGEIU; goto do_trapi; case TNEI: /* SYNCI */ if (ctx->insn_flags & ISA_MIPS_R6) { /* SYNCI */ /* * Break the TB to be able to sync copied instructions * immediately. */ ctx->base.is_jmp = DISAS_STOP; } else { /* TNEI */ mips32_op = OPC_TNEI; goto do_trapi; } break; case TEQI: check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = OPC_TEQI; do_trapi: gen_trap(ctx, mips32_op, rs, -1, imm, 0); break; case BNEZC: case BEQZC: check_insn_opc_removed(ctx, ISA_MIPS_R6); gen_compute_branch(ctx, minor == BNEZC ? OPC_BNE : OPC_BEQ, 4, rs, 0, imm << 1, 0); /* * Compact branches don't have a delay slot, so just let * the normal delay slot handling take us to the branch * target. */ break; case LUI: check_insn_opc_removed(ctx, ISA_MIPS_R6); gen_logic_imm(ctx, OPC_LUI, rs, 0, imm); break; case SYNCI: check_insn_opc_removed(ctx, ISA_MIPS_R6); /* * Break the TB to be able to sync copied instructions * immediately. */ ctx->base.is_jmp = DISAS_STOP; break; case BC2F: case BC2T: check_insn_opc_removed(ctx, ISA_MIPS_R6); /* COP2: Not implemented. */ generate_exception_err(ctx, EXCP_CpU, 2); break; case BC1F: check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = (ctx->opcode & (1 << 16)) ? OPC_BC1FANY2 : OPC_BC1F; goto do_cp1branch; case BC1T: check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = (ctx->opcode & (1 << 16)) ? OPC_BC1TANY2 : OPC_BC1T; goto do_cp1branch; case BC1ANY4F: check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = OPC_BC1FANY4; goto do_cp1mips3d; case BC1ANY4T: check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = OPC_BC1TANY4; do_cp1mips3d: check_cop1x(ctx); if (!ase_3d_available(env)) { gen_reserved_instruction(ctx); break; } /* Fall through */ do_cp1branch: if (env->CP0_Config1 & (1 << CP0C1_FP)) { check_cp1_enabled(ctx); gen_compute_branch1(ctx, mips32_op, (ctx->opcode >> 18) & 0x7, imm << 1); } else { generate_exception_err(ctx, EXCP_CpU, 1); } break; default: MIPS_INVAL("pool32i"); gen_reserved_instruction(ctx); break; } break; case POOL32C: minor = (ctx->opcode >> 12) & 0xf; offset = sextract32(ctx->opcode, 0, (ctx->insn_flags & ISA_MIPS_R6) ? 9 : 12); switch (minor) { case LWL: check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = OPC_LWL; goto do_ld_lr; case SWL: check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = OPC_SWL; goto do_st_lr; case LWR: check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = OPC_LWR; goto do_ld_lr; case SWR: check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = OPC_SWR; goto do_st_lr; #if defined(TARGET_MIPS64) case LDL: check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = OPC_LDL; goto do_ld_lr; case SDL: check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = OPC_SDL; goto do_st_lr; case LDR: check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = OPC_LDR; goto do_ld_lr; case SDR: check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = OPC_SDR; goto do_st_lr; case LWU: check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); mips32_op = OPC_LWU; goto do_ld_lr; case LLD: check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); mips32_op = OPC_LLD; goto do_ld_lr; #endif case LL: mips32_op = OPC_LL; goto do_ld_lr; do_ld_lr: gen_ld(ctx, mips32_op, rt, rs, offset); break; do_st_lr: gen_st(ctx, mips32_op, rt, rs, offset); break; case SC: gen_st_cond(ctx, rt, rs, offset, mo_endian(ctx) | MO_SL, false); break; #if defined(TARGET_MIPS64) case SCD: check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); gen_st_cond(ctx, rt, rs, offset, mo_endian(ctx) | MO_UQ, false); break; #endif case LD_EVA: if (!ctx->eva) { MIPS_INVAL("pool32c ld-eva"); gen_reserved_instruction(ctx); break; } check_cp0_enabled(ctx); minor2 = (ctx->opcode >> 9) & 0x7; offset = sextract32(ctx->opcode, 0, 9); switch (minor2) { case LBUE: mips32_op = OPC_LBUE; goto do_ld_lr; case LHUE: mips32_op = OPC_LHUE; goto do_ld_lr; case LWLE: check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = OPC_LWLE; goto do_ld_lr; case LWRE: check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = OPC_LWRE; goto do_ld_lr; case LBE: mips32_op = OPC_LBE; goto do_ld_lr; case LHE: mips32_op = OPC_LHE; goto do_ld_lr; case LLE: mips32_op = OPC_LLE; goto do_ld_lr; case LWE: mips32_op = OPC_LWE; goto do_ld_lr; }; break; case ST_EVA: if (!ctx->eva) { MIPS_INVAL("pool32c st-eva"); gen_reserved_instruction(ctx); break; } check_cp0_enabled(ctx); minor2 = (ctx->opcode >> 9) & 0x7; offset = sextract32(ctx->opcode, 0, 9); switch (minor2) { case SWLE: check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = OPC_SWLE; goto do_st_lr; case SWRE: check_insn_opc_removed(ctx, ISA_MIPS_R6); mips32_op = OPC_SWRE; goto do_st_lr; case PREFE: /* Treat as no-op */ if ((ctx->insn_flags & ISA_MIPS_R6) && (rt >= 24)) { /* hint codes 24-31 are reserved and signal RI */ generate_exception(ctx, EXCP_RI); } break; case CACHEE: /* Treat as no-op */ if (ctx->hflags & MIPS_HFLAG_ITC_CACHE) { gen_cache_operation(ctx, rt, rs, offset); } break; case SBE: mips32_op = OPC_SBE; goto do_st_lr; case SHE: mips32_op = OPC_SHE; goto do_st_lr; case SCE: gen_st_cond(ctx, rt, rs, offset, mo_endian(ctx) | MO_SL, true); break; case SWE: mips32_op = OPC_SWE; goto do_st_lr; }; break; case PREF: /* Treat as no-op */ if ((ctx->insn_flags & ISA_MIPS_R6) && (rt >= 24)) { /* hint codes 24-31 are reserved and signal RI */ generate_exception(ctx, EXCP_RI); } break; default: MIPS_INVAL("pool32c"); gen_reserved_instruction(ctx); break; } break; case ADDI32: /* AUI, LUI */ if (ctx->insn_flags & ISA_MIPS_R6) { /* AUI, LUI */ gen_logic_imm(ctx, OPC_LUI, rt, rs, imm); } else { /* ADDI32 */ mips32_op = OPC_ADDI; goto do_addi; } break; case ADDIU32: mips32_op = OPC_ADDIU; do_addi: gen_arith_imm(ctx, mips32_op, rt, rs, imm); break; /* Logical operations */ case ORI32: mips32_op = OPC_ORI; goto do_logici; case XORI32: mips32_op = OPC_XORI; goto do_logici; case ANDI32: mips32_op = OPC_ANDI; do_logici: gen_logic_imm(ctx, mips32_op, rt, rs, imm); break; /* Set less than immediate */ case SLTI32: mips32_op = OPC_SLTI; goto do_slti; case SLTIU32: mips32_op = OPC_SLTIU; do_slti: gen_slt_imm(ctx, mips32_op, rt, rs, imm); break; case JALX32: check_insn_opc_removed(ctx, ISA_MIPS_R6); offset = (int32_t)(ctx->opcode & 0x3FFFFFF) << 2; gen_compute_branch(ctx, OPC_JALX, 4, rt, rs, offset, 4); ctx->hflags |= MIPS_HFLAG_BDS_STRICT; break; case JALS32: /* BOVC, BEQC, BEQZALC */ if (ctx->insn_flags & ISA_MIPS_R6) { if (rs >= rt) { /* BOVC */ mips32_op = OPC_BOVC; } else if (rs < rt && rs == 0) { /* BEQZALC */ mips32_op = OPC_BEQZALC; } else { /* BEQC */ mips32_op = OPC_BEQC; } gen_compute_compact_branch(ctx, mips32_op, rs, rt, imm << 1); } else { /* JALS32 */ offset = (int32_t)(ctx->opcode & 0x3FFFFFF) << 1; gen_compute_branch(ctx, OPC_JAL, 4, rt, rs, offset, 2); ctx->hflags |= MIPS_HFLAG_BDS_STRICT; } break; case BEQ32: /* BC */ if (ctx->insn_flags & ISA_MIPS_R6) { /* BC */ gen_compute_compact_branch(ctx, OPC_BC, 0, 0, sextract32(ctx->opcode << 1, 0, 27)); } else { /* BEQ32 */ gen_compute_branch(ctx, OPC_BEQ, 4, rt, rs, imm << 1, 4); } break; case BNE32: /* BALC */ if (ctx->insn_flags & ISA_MIPS_R6) { /* BALC */ gen_compute_compact_branch(ctx, OPC_BALC, 0, 0, sextract32(ctx->opcode << 1, 0, 27)); } else { /* BNE32 */ gen_compute_branch(ctx, OPC_BNE, 4, rt, rs, imm << 1, 4); } break; case J32: /* BGTZC, BLTZC, BLTC */ if (ctx->insn_flags & ISA_MIPS_R6) { if (rs == 0 && rt != 0) { /* BGTZC */ mips32_op = OPC_BGTZC; } else if (rs != 0 && rt != 0 && rs == rt) { /* BLTZC */ mips32_op = OPC_BLTZC; } else { /* BLTC */ mips32_op = OPC_BLTC; } gen_compute_compact_branch(ctx, mips32_op, rs, rt, imm << 1); } else { /* J32 */ gen_compute_branch(ctx, OPC_J, 4, rt, rs, (int32_t)(ctx->opcode & 0x3FFFFFF) << 1, 4); } break; case JAL32: /* BLEZC, BGEZC, BGEC */ if (ctx->insn_flags & ISA_MIPS_R6) { if (rs == 0 && rt != 0) { /* BLEZC */ mips32_op = OPC_BLEZC; } else if (rs != 0 && rt != 0 && rs == rt) { /* BGEZC */ mips32_op = OPC_BGEZC; } else { /* BGEC */ mips32_op = OPC_BGEC; } gen_compute_compact_branch(ctx, mips32_op, rs, rt, imm << 1); } else { /* JAL32 */ gen_compute_branch(ctx, OPC_JAL, 4, rt, rs, (int32_t)(ctx->opcode & 0x3FFFFFF) << 1, 4); ctx->hflags |= MIPS_HFLAG_BDS_STRICT; } break; /* Floating point (COP1) */ case LWC132: mips32_op = OPC_LWC1; goto do_cop1; case LDC132: mips32_op = OPC_LDC1; goto do_cop1; case SWC132: mips32_op = OPC_SWC1; goto do_cop1; case SDC132: mips32_op = OPC_SDC1; do_cop1: gen_cop1_ldst(ctx, mips32_op, rt, rs, imm); break; case ADDIUPC: /* PCREL: ADDIUPC, AUIPC, ALUIPC, LWPC */ if (ctx->insn_flags & ISA_MIPS_R6) { /* PCREL: ADDIUPC, AUIPC, ALUIPC, LWPC */ switch ((ctx->opcode >> 16) & 0x1f) { case ADDIUPC_00: case ADDIUPC_01: case ADDIUPC_02: case ADDIUPC_03: case ADDIUPC_04: case ADDIUPC_05: case ADDIUPC_06: case ADDIUPC_07: gen_pcrel(ctx, OPC_ADDIUPC, ctx->base.pc_next & ~0x3, rt); break; case AUIPC: gen_pcrel(ctx, OPC_AUIPC, ctx->base.pc_next, rt); break; case ALUIPC: gen_pcrel(ctx, OPC_ALUIPC, ctx->base.pc_next, rt); break; case LWPC_08: case LWPC_09: case LWPC_0A: case LWPC_0B: case LWPC_0C: case LWPC_0D: case LWPC_0E: case LWPC_0F: gen_pcrel(ctx, R6_OPC_LWPC, ctx->base.pc_next & ~0x3, rt); break; default: generate_exception(ctx, EXCP_RI); break; } } else { /* ADDIUPC */ int reg = mmreg(ZIMM(ctx->opcode, 23, 3)); offset = SIMM(ctx->opcode, 0, 23) << 2; gen_addiupc(ctx, reg, offset, 0, 0); } break; case BNVC: /* BNEC, BNEZALC */ check_insn(ctx, ISA_MIPS_R6); if (rs >= rt) { /* BNVC */ mips32_op = OPC_BNVC; } else if (rs < rt && rs == 0) { /* BNEZALC */ mips32_op = OPC_BNEZALC; } else { /* BNEC */ mips32_op = OPC_BNEC; } gen_compute_compact_branch(ctx, mips32_op, rs, rt, imm << 1); break; case R6_BNEZC: /* JIALC */ check_insn(ctx, ISA_MIPS_R6); if (rt != 0) { /* BNEZC */ gen_compute_compact_branch(ctx, OPC_BNEZC, rt, 0, sextract32(ctx->opcode << 1, 0, 22)); } else { /* JIALC */ gen_compute_compact_branch(ctx, OPC_JIALC, 0, rs, imm); } break; case R6_BEQZC: /* JIC */ check_insn(ctx, ISA_MIPS_R6); if (rt != 0) { /* BEQZC */ gen_compute_compact_branch(ctx, OPC_BEQZC, rt, 0, sextract32(ctx->opcode << 1, 0, 22)); } else { /* JIC */ gen_compute_compact_branch(ctx, OPC_JIC, 0, rs, imm); } break; case BLEZALC: /* BGEZALC, BGEUC */ check_insn(ctx, ISA_MIPS_R6); if (rs == 0 && rt != 0) { /* BLEZALC */ mips32_op = OPC_BLEZALC; } else if (rs != 0 && rt != 0 && rs == rt) { /* BGEZALC */ mips32_op = OPC_BGEZALC; } else { /* BGEUC */ mips32_op = OPC_BGEUC; } gen_compute_compact_branch(ctx, mips32_op, rs, rt, imm << 1); break; case BGTZALC: /* BLTZALC, BLTUC */ check_insn(ctx, ISA_MIPS_R6); if (rs == 0 && rt != 0) { /* BGTZALC */ mips32_op = OPC_BGTZALC; } else if (rs != 0 && rt != 0 && rs == rt) { /* BLTZALC */ mips32_op = OPC_BLTZALC; } else { /* BLTUC */ mips32_op = OPC_BLTUC; } gen_compute_compact_branch(ctx, mips32_op, rs, rt, imm << 1); break; /* Loads and stores */ case LB32: mips32_op = OPC_LB; goto do_ld; case LBU32: mips32_op = OPC_LBU; goto do_ld; case LH32: mips32_op = OPC_LH; goto do_ld; case LHU32: mips32_op = OPC_LHU; goto do_ld; case LW32: mips32_op = OPC_LW; goto do_ld; #ifdef TARGET_MIPS64 case LD32: check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); mips32_op = OPC_LD; goto do_ld; case SD32: check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); mips32_op = OPC_SD; goto do_st; #endif case SB32: mips32_op = OPC_SB; goto do_st; case SH32: mips32_op = OPC_SH; goto do_st; case SW32: mips32_op = OPC_SW; goto do_st; do_ld: gen_ld(ctx, mips32_op, rt, rs, imm); break; do_st: gen_st(ctx, mips32_op, rt, rs, imm); break; default: gen_reserved_instruction(ctx); break; } } static int decode_isa_micromips(CPUMIPSState *env, DisasContext *ctx) { uint32_t op; /* make sure instructions are on a halfword boundary */ if (ctx->base.pc_next & 0x1) { env->CP0_BadVAddr = ctx->base.pc_next; generate_exception_end(ctx, EXCP_AdEL); return 2; } op = (ctx->opcode >> 10) & 0x3f; /* Enforce properly-sized instructions in a delay slot */ if (ctx->hflags & MIPS_HFLAG_BDS_STRICT) { switch (op & 0x7) { /* MSB-3..MSB-5 */ case 0: /* POOL32A, POOL32B, POOL32I, POOL32C */ case 4: /* ADDI32, ADDIU32, ORI32, XORI32, SLTI32, SLTIU32, ANDI32, JALX32 */ case 5: /* LBU32, LHU32, POOL32F, JALS32, BEQ32, BNE32, J32, JAL32 */ case 6: /* SB32, SH32, ADDIUPC, SWC132, SDC132, SW32 */ case 7: /* LB32, LH32, LWC132, LDC132, LW32 */ if (ctx->hflags & MIPS_HFLAG_BDS16) { gen_reserved_instruction(ctx); return 2; } break; case 1: /* POOL16A, POOL16B, POOL16C, LWGP16, POOL16F */ case 2: /* LBU16, LHU16, LWSP16, LW16, SB16, SH16, SWSP16, SW16 */ case 3: /* MOVE16, ANDI16, POOL16D, POOL16E, BEQZ16, BNEZ16, B16, LI16 */ if (ctx->hflags & MIPS_HFLAG_BDS32) { gen_reserved_instruction(ctx); return 2; } break; } } switch (op) { case POOL16A: { int rd = mmreg(uMIPS_RD(ctx->opcode)); int rs1 = mmreg(uMIPS_RS1(ctx->opcode)); int rs2 = mmreg(uMIPS_RS2(ctx->opcode)); uint32_t opc = 0; switch (ctx->opcode & 0x1) { case ADDU16: opc = OPC_ADDU; break; case SUBU16: opc = OPC_SUBU; break; } if (ctx->insn_flags & ISA_MIPS_R6) { /* * In the Release 6, the register number location in * the instruction encoding has changed. */ gen_arith(ctx, opc, rs1, rd, rs2); } else { gen_arith(ctx, opc, rd, rs1, rs2); } } break; case POOL16B: { int rd = mmreg(uMIPS_RD(ctx->opcode)); int rs = mmreg(uMIPS_RS(ctx->opcode)); int amount = (ctx->opcode >> 1) & 0x7; uint32_t opc = 0; amount = amount == 0 ? 8 : amount; switch (ctx->opcode & 0x1) { case SLL16: opc = OPC_SLL; break; case SRL16: opc = OPC_SRL; break; } gen_shift_imm(ctx, opc, rd, rs, amount); } break; case POOL16C: if (ctx->insn_flags & ISA_MIPS_R6) { gen_pool16c_r6_insn(ctx); } else { gen_pool16c_insn(ctx); } break; case LWGP16: { int rd = mmreg(uMIPS_RD(ctx->opcode)); int rb = 28; /* GP */ int16_t offset = SIMM(ctx->opcode, 0, 7) << 2; gen_ld(ctx, OPC_LW, rd, rb, offset); } break; case POOL16F: check_insn_opc_removed(ctx, ISA_MIPS_R6); if (ctx->opcode & 1) { gen_reserved_instruction(ctx); } else { /* MOVEP */ int enc_dest = uMIPS_RD(ctx->opcode); int enc_rt = uMIPS_RS2(ctx->opcode); int enc_rs = uMIPS_RS1(ctx->opcode); gen_movep(ctx, enc_dest, enc_rt, enc_rs); } break; case LBU16: { int rd = mmreg(uMIPS_RD(ctx->opcode)); int rb = mmreg(uMIPS_RS(ctx->opcode)); int16_t offset = ZIMM(ctx->opcode, 0, 4); offset = (offset == 0xf ? -1 : offset); gen_ld(ctx, OPC_LBU, rd, rb, offset); } break; case LHU16: { int rd = mmreg(uMIPS_RD(ctx->opcode)); int rb = mmreg(uMIPS_RS(ctx->opcode)); int16_t offset = ZIMM(ctx->opcode, 0, 4) << 1; gen_ld(ctx, OPC_LHU, rd, rb, offset); } break; case LWSP16: { int rd = (ctx->opcode >> 5) & 0x1f; int rb = 29; /* SP */ int16_t offset = ZIMM(ctx->opcode, 0, 5) << 2; gen_ld(ctx, OPC_LW, rd, rb, offset); } break; case LW16: { int rd = mmreg(uMIPS_RD(ctx->opcode)); int rb = mmreg(uMIPS_RS(ctx->opcode)); int16_t offset = ZIMM(ctx->opcode, 0, 4) << 2; gen_ld(ctx, OPC_LW, rd, rb, offset); } break; case SB16: { int rd = mmreg2(uMIPS_RD(ctx->opcode)); int rb = mmreg(uMIPS_RS(ctx->opcode)); int16_t offset = ZIMM(ctx->opcode, 0, 4); gen_st(ctx, OPC_SB, rd, rb, offset); } break; case SH16: { int rd = mmreg2(uMIPS_RD(ctx->opcode)); int rb = mmreg(uMIPS_RS(ctx->opcode)); int16_t offset = ZIMM(ctx->opcode, 0, 4) << 1; gen_st(ctx, OPC_SH, rd, rb, offset); } break; case SWSP16: { int rd = (ctx->opcode >> 5) & 0x1f; int rb = 29; /* SP */ int16_t offset = ZIMM(ctx->opcode, 0, 5) << 2; gen_st(ctx, OPC_SW, rd, rb, offset); } break; case SW16: { int rd = mmreg2(uMIPS_RD(ctx->opcode)); int rb = mmreg(uMIPS_RS(ctx->opcode)); int16_t offset = ZIMM(ctx->opcode, 0, 4) << 2; gen_st(ctx, OPC_SW, rd, rb, offset); } break; case MOVE16: { int rd = uMIPS_RD5(ctx->opcode); int rs = uMIPS_RS5(ctx->opcode); gen_arith(ctx, OPC_ADDU, rd, rs, 0); } break; case ANDI16: gen_andi16(ctx); break; case POOL16D: switch (ctx->opcode & 0x1) { case ADDIUS5: gen_addius5(ctx); break; case ADDIUSP: gen_addiusp(ctx); break; } break; case POOL16E: switch (ctx->opcode & 0x1) { case ADDIUR2: gen_addiur2(ctx); break; case ADDIUR1SP: gen_addiur1sp(ctx); break; } break; case B16: /* BC16 */ gen_compute_branch(ctx, OPC_BEQ, 2, 0, 0, sextract32(ctx->opcode, 0, 10) << 1, (ctx->insn_flags & ISA_MIPS_R6) ? 0 : 4); break; case BNEZ16: /* BNEZC16 */ case BEQZ16: /* BEQZC16 */ gen_compute_branch(ctx, op == BNEZ16 ? OPC_BNE : OPC_BEQ, 2, mmreg(uMIPS_RD(ctx->opcode)), 0, sextract32(ctx->opcode, 0, 7) << 1, (ctx->insn_flags & ISA_MIPS_R6) ? 0 : 4); break; case LI16: { int reg = mmreg(uMIPS_RD(ctx->opcode)); int imm = ZIMM(ctx->opcode, 0, 7); imm = (imm == 0x7f ? -1 : imm); tcg_gen_movi_tl(cpu_gpr[reg], imm); } break; case RES_29: case RES_31: case RES_39: gen_reserved_instruction(ctx); break; default: decode_micromips32_opc(env, ctx); return 4; } return 2; }