3232 lines
92 KiB
PHP
3232 lines
92 KiB
PHP
/*
|
|
* 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_const_tl(reglist);
|
|
t2 = tcg_const_i32(ctx->mem_idx);
|
|
|
|
save_cpu_state(ctx, 1);
|
|
switch (opc) {
|
|
case LWM32:
|
|
gen_helper_lwm(cpu_env, t0, t1, t2);
|
|
break;
|
|
case SWM32:
|
|
gen_helper_swm(cpu_env, t0, t1, t2);
|
|
break;
|
|
#ifdef TARGET_MIPS64
|
|
case LDM:
|
|
gen_helper_ldm(cpu_env, t0, t1, t2);
|
|
break;
|
|
case SDM:
|
|
gen_helper_sdm(cpu_env, t0, t1, t2);
|
|
break;
|
|
#endif
|
|
}
|
|
tcg_temp_free(t0);
|
|
tcg_temp_free(t1);
|
|
tcg_temp_free_i32(t2);
|
|
}
|
|
|
|
|
|
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(extract32(ctx->opcode, 0, 4))) {
|
|
generate_exception_end(ctx, EXCP_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(extract32(ctx->opcode, 6, 4))) {
|
|
generate_exception_end(ctx, EXCP_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_TESL);
|
|
gen_store_gpr(t1, rd);
|
|
tcg_gen_movi_tl(t1, 4);
|
|
gen_op_addr_add(ctx, t0, t0, t1);
|
|
tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_TESL);
|
|
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_TEUL);
|
|
tcg_gen_movi_tl(t1, 4);
|
|
gen_op_addr_add(ctx, t0, t0, t1);
|
|
gen_load_gpr(t1, rd + 1);
|
|
tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL);
|
|
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_TEUQ);
|
|
gen_store_gpr(t1, rd);
|
|
tcg_gen_movi_tl(t1, 8);
|
|
gen_op_addr_add(ctx, t0, t0, t1);
|
|
tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_TEUQ);
|
|
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_TEUQ);
|
|
tcg_gen_movi_tl(t1, 8);
|
|
gen_op_addr_add(ctx, t0, t0, t1);
|
|
gen_load_gpr(t1, rd + 1);
|
|
tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUQ);
|
|
break;
|
|
#endif
|
|
}
|
|
tcg_temp_free(t0);
|
|
tcg_temp_free(t1);
|
|
}
|
|
|
|
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);
|
|
tcg_temp_free(t0);
|
|
}
|
|
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, cpu_env);
|
|
gen_store_gpr(t0, rs);
|
|
/*
|
|
* Stop translation as we may have switched the execution
|
|
* mode.
|
|
*/
|
|
ctx->base.is_jmp = DISAS_STOP;
|
|
tcg_temp_free(t0);
|
|
}
|
|
break;
|
|
case EI:
|
|
check_cp0_enabled(ctx);
|
|
{
|
|
TCGv t0 = tcg_temp_new();
|
|
|
|
save_cpu_state(ctx, 1);
|
|
gen_helper_ei(t0, cpu_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;
|
|
tcg_temp_free(t0);
|
|
}
|
|
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(extract32(ctx->opcode, 16, 10))) {
|
|
generate_exception_end(ctx, EXCP_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);
|
|
check_insn(ctx, ASE_MIPS3D);
|
|
/* 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_TESL, 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_TEUQ, 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_TESL, 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;
|
|
}
|