| /* |
| * MIPS emulation for QEMU - nanoMIPS 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) |
| * Copyright (c) 2012 Jia Liu & Dongxue Zhang (MIPS ASE DSP support) |
| * |
| * SPDX-License-Identifier: LGPL-2.1-or-later |
| */ |
| |
| /* MAJOR, P16, and P32 pools opcodes */ |
| enum { |
| NM_P_ADDIU = 0x00, |
| NM_ADDIUPC = 0x01, |
| NM_MOVE_BALC = 0x02, |
| NM_P16_MV = 0x04, |
| NM_LW16 = 0x05, |
| NM_BC16 = 0x06, |
| NM_P16_SR = 0x07, |
| |
| NM_POOL32A = 0x08, |
| NM_P_BAL = 0x0a, |
| NM_P16_SHIFT = 0x0c, |
| NM_LWSP16 = 0x0d, |
| NM_BALC16 = 0x0e, |
| NM_P16_4X4 = 0x0f, |
| |
| NM_P_GP_W = 0x10, |
| NM_P_GP_BH = 0x11, |
| NM_P_J = 0x12, |
| NM_P16C = 0x14, |
| NM_LWGP16 = 0x15, |
| NM_P16_LB = 0x17, |
| |
| NM_P48I = 0x18, |
| NM_P16_A1 = 0x1c, |
| NM_LW4X4 = 0x1d, |
| NM_P16_LH = 0x1f, |
| |
| NM_P_U12 = 0x20, |
| NM_P_LS_U12 = 0x21, |
| NM_P_BR1 = 0x22, |
| NM_P16_A2 = 0x24, |
| NM_SW16 = 0x25, |
| NM_BEQZC16 = 0x26, |
| |
| NM_POOL32F = 0x28, |
| NM_P_LS_S9 = 0x29, |
| NM_P_BR2 = 0x2a, |
| |
| NM_P16_ADDU = 0x2c, |
| NM_SWSP16 = 0x2d, |
| NM_BNEZC16 = 0x2e, |
| NM_MOVEP = 0x2f, |
| |
| NM_POOL32S = 0x30, |
| NM_P_BRI = 0x32, |
| NM_LI16 = 0x34, |
| NM_SWGP16 = 0x35, |
| NM_P16_BR = 0x36, |
| |
| NM_P_LUI = 0x38, |
| NM_ANDI16 = 0x3c, |
| NM_SW4X4 = 0x3d, |
| NM_MOVEPREV = 0x3f, |
| }; |
| |
| /* POOL32A instruction pool */ |
| enum { |
| NM_POOL32A0 = 0x00, |
| NM_SPECIAL2 = 0x01, |
| NM_COP2_1 = 0x02, |
| NM_UDI = 0x03, |
| NM_POOL32A5 = 0x05, |
| NM_POOL32A7 = 0x07, |
| }; |
| |
| /* P.GP.W instruction pool */ |
| enum { |
| NM_ADDIUGP_W = 0x00, |
| NM_LWGP = 0x02, |
| NM_SWGP = 0x03, |
| }; |
| |
| /* P48I instruction pool */ |
| enum { |
| NM_LI48 = 0x00, |
| NM_ADDIU48 = 0x01, |
| NM_ADDIUGP48 = 0x02, |
| NM_ADDIUPC48 = 0x03, |
| NM_LWPC48 = 0x0b, |
| NM_SWPC48 = 0x0f, |
| }; |
| |
| /* P.U12 instruction pool */ |
| enum { |
| NM_ORI = 0x00, |
| NM_XORI = 0x01, |
| NM_ANDI = 0x02, |
| NM_P_SR = 0x03, |
| NM_SLTI = 0x04, |
| NM_SLTIU = 0x05, |
| NM_SEQI = 0x06, |
| NM_ADDIUNEG = 0x08, |
| NM_P_SHIFT = 0x0c, |
| NM_P_ROTX = 0x0d, |
| NM_P_INS = 0x0e, |
| NM_P_EXT = 0x0f, |
| }; |
| |
| /* POOL32F instruction pool */ |
| enum { |
| NM_POOL32F_0 = 0x00, |
| NM_POOL32F_3 = 0x03, |
| NM_POOL32F_5 = 0x05, |
| }; |
| |
| /* POOL32S instruction pool */ |
| enum { |
| NM_POOL32S_0 = 0x00, |
| NM_POOL32S_4 = 0x04, |
| }; |
| |
| /* P.LUI instruction pool */ |
| enum { |
| NM_LUI = 0x00, |
| NM_ALUIPC = 0x01, |
| }; |
| |
| /* P.GP.BH instruction pool */ |
| enum { |
| NM_LBGP = 0x00, |
| NM_SBGP = 0x01, |
| NM_LBUGP = 0x02, |
| NM_ADDIUGP_B = 0x03, |
| NM_P_GP_LH = 0x04, |
| NM_P_GP_SH = 0x05, |
| NM_P_GP_CP1 = 0x06, |
| }; |
| |
| /* P.LS.U12 instruction pool */ |
| enum { |
| NM_LB = 0x00, |
| NM_SB = 0x01, |
| NM_LBU = 0x02, |
| NM_P_PREFU12 = 0x03, |
| NM_LH = 0x04, |
| NM_SH = 0x05, |
| NM_LHU = 0x06, |
| NM_LWU = 0x07, |
| NM_LW = 0x08, |
| NM_SW = 0x09, |
| NM_LWC1 = 0x0a, |
| NM_SWC1 = 0x0b, |
| NM_LDC1 = 0x0e, |
| NM_SDC1 = 0x0f, |
| }; |
| |
| /* P.LS.S9 instruction pool */ |
| enum { |
| NM_P_LS_S0 = 0x00, |
| NM_P_LS_S1 = 0x01, |
| NM_P_LS_E0 = 0x02, |
| NM_P_LS_WM = 0x04, |
| NM_P_LS_UAWM = 0x05, |
| }; |
| |
| /* P.BAL instruction pool */ |
| enum { |
| NM_BC = 0x00, |
| NM_BALC = 0x01, |
| }; |
| |
| /* P.J instruction pool */ |
| enum { |
| NM_JALRC = 0x00, |
| NM_JALRC_HB = 0x01, |
| NM_P_BALRSC = 0x08, |
| }; |
| |
| /* P.BR1 instruction pool */ |
| enum { |
| NM_BEQC = 0x00, |
| NM_P_BR3A = 0x01, |
| NM_BGEC = 0x02, |
| NM_BGEUC = 0x03, |
| }; |
| |
| /* P.BR2 instruction pool */ |
| enum { |
| NM_BNEC = 0x00, |
| NM_BLTC = 0x02, |
| NM_BLTUC = 0x03, |
| }; |
| |
| /* P.BRI instruction pool */ |
| enum { |
| NM_BEQIC = 0x00, |
| NM_BBEQZC = 0x01, |
| NM_BGEIC = 0x02, |
| NM_BGEIUC = 0x03, |
| NM_BNEIC = 0x04, |
| NM_BBNEZC = 0x05, |
| NM_BLTIC = 0x06, |
| NM_BLTIUC = 0x07, |
| }; |
| |
| /* P16.SHIFT instruction pool */ |
| enum { |
| NM_SLL16 = 0x00, |
| NM_SRL16 = 0x01, |
| }; |
| |
| /* POOL16C instruction pool */ |
| enum { |
| NM_POOL16C_0 = 0x00, |
| NM_LWXS16 = 0x01, |
| }; |
| |
| /* P16.A1 instruction pool */ |
| enum { |
| NM_ADDIUR1SP = 0x01, |
| }; |
| |
| /* P16.A2 instruction pool */ |
| enum { |
| NM_ADDIUR2 = 0x00, |
| NM_P_ADDIURS5 = 0x01, |
| }; |
| |
| /* P16.ADDU instruction pool */ |
| enum { |
| NM_ADDU16 = 0x00, |
| NM_SUBU16 = 0x01, |
| }; |
| |
| /* P16.SR instruction pool */ |
| enum { |
| NM_SAVE16 = 0x00, |
| NM_RESTORE_JRC16 = 0x01, |
| }; |
| |
| /* P16.4X4 instruction pool */ |
| enum { |
| NM_ADDU4X4 = 0x00, |
| NM_MUL4X4 = 0x01, |
| }; |
| |
| /* P16.LB instruction pool */ |
| enum { |
| NM_LB16 = 0x00, |
| NM_SB16 = 0x01, |
| NM_LBU16 = 0x02, |
| }; |
| |
| /* P16.LH instruction pool */ |
| enum { |
| NM_LH16 = 0x00, |
| NM_SH16 = 0x01, |
| NM_LHU16 = 0x02, |
| }; |
| |
| /* P.RI instruction pool */ |
| enum { |
| NM_SIGRIE = 0x00, |
| NM_P_SYSCALL = 0x01, |
| NM_BREAK = 0x02, |
| NM_SDBBP = 0x03, |
| }; |
| |
| /* POOL32A0 instruction pool */ |
| enum { |
| NM_P_TRAP = 0x00, |
| NM_SEB = 0x01, |
| NM_SLLV = 0x02, |
| NM_MUL = 0x03, |
| NM_MFC0 = 0x06, |
| NM_MFHC0 = 0x07, |
| NM_SEH = 0x09, |
| NM_SRLV = 0x0a, |
| NM_MUH = 0x0b, |
| NM_MTC0 = 0x0e, |
| NM_MTHC0 = 0x0f, |
| NM_SRAV = 0x12, |
| NM_MULU = 0x13, |
| NM_ROTRV = 0x1a, |
| NM_MUHU = 0x1b, |
| NM_ADD = 0x22, |
| NM_DIV = 0x23, |
| NM_ADDU = 0x2a, |
| NM_MOD = 0x2b, |
| NM_SUB = 0x32, |
| NM_DIVU = 0x33, |
| NM_RDHWR = 0x38, |
| NM_SUBU = 0x3a, |
| NM_MODU = 0x3b, |
| NM_P_CMOVE = 0x42, |
| NM_FORK = 0x45, |
| NM_MFTR = 0x46, |
| NM_MFHTR = 0x47, |
| NM_AND = 0x4a, |
| NM_YIELD = 0x4d, |
| NM_MTTR = 0x4e, |
| NM_MTHTR = 0x4f, |
| NM_OR = 0x52, |
| NM_D_E_MT_VPE = 0x56, |
| NM_NOR = 0x5a, |
| NM_XOR = 0x62, |
| NM_SLT = 0x6a, |
| NM_P_SLTU = 0x72, |
| NM_SOV = 0x7a, |
| }; |
| |
| /* CRC32 instruction pool */ |
| enum { |
| NM_CRC32B = 0x00, |
| NM_CRC32H = 0x01, |
| NM_CRC32W = 0x02, |
| NM_CRC32CB = 0x04, |
| NM_CRC32CH = 0x05, |
| NM_CRC32CW = 0x06, |
| }; |
| |
| /* POOL32A5 instruction pool */ |
| enum { |
| NM_CMP_EQ_PH = 0x00, |
| NM_CMP_LT_PH = 0x08, |
| NM_CMP_LE_PH = 0x10, |
| NM_CMPGU_EQ_QB = 0x18, |
| NM_CMPGU_LT_QB = 0x20, |
| NM_CMPGU_LE_QB = 0x28, |
| NM_CMPGDU_EQ_QB = 0x30, |
| NM_CMPGDU_LT_QB = 0x38, |
| NM_CMPGDU_LE_QB = 0x40, |
| NM_CMPU_EQ_QB = 0x48, |
| NM_CMPU_LT_QB = 0x50, |
| NM_CMPU_LE_QB = 0x58, |
| NM_ADDQ_S_W = 0x60, |
| NM_SUBQ_S_W = 0x68, |
| NM_ADDSC = 0x70, |
| NM_ADDWC = 0x78, |
| |
| NM_ADDQ_S_PH = 0x01, |
| NM_ADDQH_R_PH = 0x09, |
| NM_ADDQH_R_W = 0x11, |
| NM_ADDU_S_QB = 0x19, |
| NM_ADDU_S_PH = 0x21, |
| NM_ADDUH_R_QB = 0x29, |
| NM_SHRAV_R_PH = 0x31, |
| NM_SHRAV_R_QB = 0x39, |
| NM_SUBQ_S_PH = 0x41, |
| NM_SUBQH_R_PH = 0x49, |
| NM_SUBQH_R_W = 0x51, |
| NM_SUBU_S_QB = 0x59, |
| NM_SUBU_S_PH = 0x61, |
| NM_SUBUH_R_QB = 0x69, |
| NM_SHLLV_S_PH = 0x71, |
| NM_PRECR_SRA_R_PH_W = 0x79, |
| |
| NM_MULEU_S_PH_QBL = 0x12, |
| NM_MULEU_S_PH_QBR = 0x1a, |
| NM_MULQ_RS_PH = 0x22, |
| NM_MULQ_S_PH = 0x2a, |
| NM_MULQ_RS_W = 0x32, |
| NM_MULQ_S_W = 0x3a, |
| NM_APPEND = 0x42, |
| NM_MODSUB = 0x52, |
| NM_SHRAV_R_W = 0x5a, |
| NM_SHRLV_PH = 0x62, |
| NM_SHRLV_QB = 0x6a, |
| NM_SHLLV_QB = 0x72, |
| NM_SHLLV_S_W = 0x7a, |
| |
| NM_SHILO = 0x03, |
| |
| NM_MULEQ_S_W_PHL = 0x04, |
| NM_MULEQ_S_W_PHR = 0x0c, |
| |
| NM_MUL_S_PH = 0x05, |
| NM_PRECR_QB_PH = 0x0d, |
| NM_PRECRQ_QB_PH = 0x15, |
| NM_PRECRQ_PH_W = 0x1d, |
| NM_PRECRQ_RS_PH_W = 0x25, |
| NM_PRECRQU_S_QB_PH = 0x2d, |
| NM_PACKRL_PH = 0x35, |
| NM_PICK_QB = 0x3d, |
| NM_PICK_PH = 0x45, |
| |
| NM_SHRA_R_W = 0x5e, |
| NM_SHRA_R_PH = 0x66, |
| NM_SHLL_S_PH = 0x76, |
| NM_SHLL_S_W = 0x7e, |
| |
| NM_REPL_PH = 0x07 |
| }; |
| |
| /* POOL32A7 instruction pool */ |
| enum { |
| NM_P_LSX = 0x00, |
| NM_LSA = 0x01, |
| NM_EXTW = 0x03, |
| NM_POOL32AXF = 0x07, |
| }; |
| |
| /* P.SR instruction pool */ |
| enum { |
| NM_PP_SR = 0x00, |
| NM_P_SR_F = 0x01, |
| }; |
| |
| /* P.SHIFT instruction pool */ |
| enum { |
| NM_P_SLL = 0x00, |
| NM_SRL = 0x02, |
| NM_SRA = 0x04, |
| NM_ROTR = 0x06, |
| }; |
| |
| /* P.ROTX instruction pool */ |
| enum { |
| NM_ROTX = 0x00, |
| }; |
| |
| /* P.INS instruction pool */ |
| enum { |
| NM_INS = 0x00, |
| }; |
| |
| /* P.EXT instruction pool */ |
| enum { |
| NM_EXT = 0x00, |
| }; |
| |
| /* POOL32F_0 (fmt) instruction pool */ |
| enum { |
| NM_RINT_S = 0x04, |
| NM_RINT_D = 0x44, |
| NM_ADD_S = 0x06, |
| NM_SELEQZ_S = 0x07, |
| NM_SELEQZ_D = 0x47, |
| NM_CLASS_S = 0x0c, |
| NM_CLASS_D = 0x4c, |
| NM_SUB_S = 0x0e, |
| NM_SELNEZ_S = 0x0f, |
| NM_SELNEZ_D = 0x4f, |
| NM_MUL_S = 0x16, |
| NM_SEL_S = 0x17, |
| NM_SEL_D = 0x57, |
| NM_DIV_S = 0x1e, |
| NM_ADD_D = 0x26, |
| NM_SUB_D = 0x2e, |
| NM_MUL_D = 0x36, |
| NM_MADDF_S = 0x37, |
| NM_MADDF_D = 0x77, |
| NM_DIV_D = 0x3e, |
| NM_MSUBF_S = 0x3f, |
| NM_MSUBF_D = 0x7f, |
| }; |
| |
| /* POOL32F_3 instruction pool */ |
| enum { |
| NM_MIN_FMT = 0x00, |
| NM_MAX_FMT = 0x01, |
| NM_MINA_FMT = 0x04, |
| NM_MAXA_FMT = 0x05, |
| NM_POOL32FXF = 0x07, |
| }; |
| |
| /* POOL32F_5 instruction pool */ |
| enum { |
| NM_CMP_CONDN_S = 0x00, |
| NM_CMP_CONDN_D = 0x02, |
| }; |
| |
| /* P.GP.LH instruction pool */ |
| enum { |
| NM_LHGP = 0x00, |
| NM_LHUGP = 0x01, |
| }; |
| |
| /* P.GP.SH instruction pool */ |
| enum { |
| NM_SHGP = 0x00, |
| }; |
| |
| /* P.GP.CP1 instruction pool */ |
| enum { |
| NM_LWC1GP = 0x00, |
| NM_SWC1GP = 0x01, |
| NM_LDC1GP = 0x02, |
| NM_SDC1GP = 0x03, |
| }; |
| |
| /* P.LS.S0 instruction pool */ |
| enum { |
| NM_LBS9 = 0x00, |
| NM_LHS9 = 0x04, |
| NM_LWS9 = 0x08, |
| NM_LDS9 = 0x0c, |
| |
| NM_SBS9 = 0x01, |
| NM_SHS9 = 0x05, |
| NM_SWS9 = 0x09, |
| NM_SDS9 = 0x0d, |
| |
| NM_LBUS9 = 0x02, |
| NM_LHUS9 = 0x06, |
| NM_LWC1S9 = 0x0a, |
| NM_LDC1S9 = 0x0e, |
| |
| NM_P_PREFS9 = 0x03, |
| NM_LWUS9 = 0x07, |
| NM_SWC1S9 = 0x0b, |
| NM_SDC1S9 = 0x0f, |
| }; |
| |
| /* P.LS.S1 instruction pool */ |
| enum { |
| NM_ASET_ACLR = 0x02, |
| NM_UALH = 0x04, |
| NM_UASH = 0x05, |
| NM_CACHE = 0x07, |
| NM_P_LL = 0x0a, |
| NM_P_SC = 0x0b, |
| }; |
| |
| /* P.LS.E0 instruction pool */ |
| enum { |
| NM_LBE = 0x00, |
| NM_SBE = 0x01, |
| NM_LBUE = 0x02, |
| NM_P_PREFE = 0x03, |
| NM_LHE = 0x04, |
| NM_SHE = 0x05, |
| NM_LHUE = 0x06, |
| NM_CACHEE = 0x07, |
| NM_LWE = 0x08, |
| NM_SWE = 0x09, |
| NM_P_LLE = 0x0a, |
| NM_P_SCE = 0x0b, |
| }; |
| |
| /* P.PREFE instruction pool */ |
| enum { |
| NM_SYNCIE = 0x00, |
| NM_PREFE = 0x01, |
| }; |
| |
| /* P.LLE instruction pool */ |
| enum { |
| NM_LLE = 0x00, |
| NM_LLWPE = 0x01, |
| }; |
| |
| /* P.SCE instruction pool */ |
| enum { |
| NM_SCE = 0x00, |
| NM_SCWPE = 0x01, |
| }; |
| |
| /* P.LS.WM instruction pool */ |
| enum { |
| NM_LWM = 0x00, |
| NM_SWM = 0x01, |
| }; |
| |
| /* P.LS.UAWM instruction pool */ |
| enum { |
| NM_UALWM = 0x00, |
| NM_UASWM = 0x01, |
| }; |
| |
| /* P.BR3A instruction pool */ |
| enum { |
| NM_BC1EQZC = 0x00, |
| NM_BC1NEZC = 0x01, |
| NM_BC2EQZC = 0x02, |
| NM_BC2NEZC = 0x03, |
| NM_BPOSGE32C = 0x04, |
| }; |
| |
| /* P16.RI instruction pool */ |
| enum { |
| NM_P16_SYSCALL = 0x01, |
| NM_BREAK16 = 0x02, |
| NM_SDBBP16 = 0x03, |
| }; |
| |
| /* POOL16C_0 instruction pool */ |
| enum { |
| NM_POOL16C_00 = 0x00, |
| }; |
| |
| /* P16.JRC instruction pool */ |
| enum { |
| NM_JRC = 0x00, |
| NM_JALRC16 = 0x01, |
| }; |
| |
| /* P.SYSCALL instruction pool */ |
| enum { |
| NM_SYSCALL = 0x00, |
| NM_HYPCALL = 0x01, |
| }; |
| |
| /* P.TRAP instruction pool */ |
| enum { |
| NM_TEQ = 0x00, |
| NM_TNE = 0x01, |
| }; |
| |
| /* P.CMOVE instruction pool */ |
| enum { |
| NM_MOVZ = 0x00, |
| NM_MOVN = 0x01, |
| }; |
| |
| /* POOL32Axf instruction pool */ |
| enum { |
| NM_POOL32AXF_1 = 0x01, |
| NM_POOL32AXF_2 = 0x02, |
| NM_POOL32AXF_4 = 0x04, |
| NM_POOL32AXF_5 = 0x05, |
| NM_POOL32AXF_7 = 0x07, |
| }; |
| |
| /* POOL32Axf_1 instruction pool */ |
| enum { |
| NM_POOL32AXF_1_0 = 0x00, |
| NM_POOL32AXF_1_1 = 0x01, |
| NM_POOL32AXF_1_3 = 0x03, |
| NM_POOL32AXF_1_4 = 0x04, |
| NM_POOL32AXF_1_5 = 0x05, |
| NM_POOL32AXF_1_7 = 0x07, |
| }; |
| |
| /* POOL32Axf_2 instruction pool */ |
| enum { |
| NM_POOL32AXF_2_0_7 = 0x00, |
| NM_POOL32AXF_2_8_15 = 0x01, |
| NM_POOL32AXF_2_16_23 = 0x02, |
| NM_POOL32AXF_2_24_31 = 0x03, |
| }; |
| |
| /* POOL32Axf_7 instruction pool */ |
| enum { |
| NM_SHRA_R_QB = 0x0, |
| NM_SHRL_PH = 0x1, |
| NM_REPL_QB = 0x2, |
| }; |
| |
| /* POOL32Axf_1_0 instruction pool */ |
| enum { |
| NM_MFHI = 0x0, |
| NM_MFLO = 0x1, |
| NM_MTHI = 0x2, |
| NM_MTLO = 0x3, |
| }; |
| |
| /* POOL32Axf_1_1 instruction pool */ |
| enum { |
| NM_MTHLIP = 0x0, |
| NM_SHILOV = 0x1, |
| }; |
| |
| /* POOL32Axf_1_3 instruction pool */ |
| enum { |
| NM_RDDSP = 0x0, |
| NM_WRDSP = 0x1, |
| NM_EXTP = 0x2, |
| NM_EXTPDP = 0x3, |
| }; |
| |
| /* POOL32Axf_1_4 instruction pool */ |
| enum { |
| NM_SHLL_QB = 0x0, |
| NM_SHRL_QB = 0x1, |
| }; |
| |
| /* POOL32Axf_1_5 instruction pool */ |
| enum { |
| NM_MAQ_S_W_PHR = 0x0, |
| NM_MAQ_S_W_PHL = 0x1, |
| NM_MAQ_SA_W_PHR = 0x2, |
| NM_MAQ_SA_W_PHL = 0x3, |
| }; |
| |
| /* POOL32Axf_1_7 instruction pool */ |
| enum { |
| NM_EXTR_W = 0x0, |
| NM_EXTR_R_W = 0x1, |
| NM_EXTR_RS_W = 0x2, |
| NM_EXTR_S_H = 0x3, |
| }; |
| |
| /* POOL32Axf_2_0_7 instruction pool */ |
| enum { |
| NM_DPA_W_PH = 0x0, |
| NM_DPAQ_S_W_PH = 0x1, |
| NM_DPS_W_PH = 0x2, |
| NM_DPSQ_S_W_PH = 0x3, |
| NM_BALIGN = 0x4, |
| NM_MADD = 0x5, |
| NM_MULT = 0x6, |
| NM_EXTRV_W = 0x7, |
| }; |
| |
| /* POOL32Axf_2_8_15 instruction pool */ |
| enum { |
| NM_DPAX_W_PH = 0x0, |
| NM_DPAQ_SA_L_W = 0x1, |
| NM_DPSX_W_PH = 0x2, |
| NM_DPSQ_SA_L_W = 0x3, |
| NM_MADDU = 0x5, |
| NM_MULTU = 0x6, |
| NM_EXTRV_R_W = 0x7, |
| }; |
| |
| /* POOL32Axf_2_16_23 instruction pool */ |
| enum { |
| NM_DPAU_H_QBL = 0x0, |
| NM_DPAQX_S_W_PH = 0x1, |
| NM_DPSU_H_QBL = 0x2, |
| NM_DPSQX_S_W_PH = 0x3, |
| NM_EXTPV = 0x4, |
| NM_MSUB = 0x5, |
| NM_MULSA_W_PH = 0x6, |
| NM_EXTRV_RS_W = 0x7, |
| }; |
| |
| /* POOL32Axf_2_24_31 instruction pool */ |
| enum { |
| NM_DPAU_H_QBR = 0x0, |
| NM_DPAQX_SA_W_PH = 0x1, |
| NM_DPSU_H_QBR = 0x2, |
| NM_DPSQX_SA_W_PH = 0x3, |
| NM_EXTPDPV = 0x4, |
| NM_MSUBU = 0x5, |
| NM_MULSAQ_S_W_PH = 0x6, |
| NM_EXTRV_S_H = 0x7, |
| }; |
| |
| /* POOL32Axf_{4, 5} instruction pool */ |
| enum { |
| NM_CLO = 0x25, |
| NM_CLZ = 0x2d, |
| |
| NM_TLBP = 0x01, |
| NM_TLBR = 0x09, |
| NM_TLBWI = 0x11, |
| NM_TLBWR = 0x19, |
| NM_TLBINV = 0x03, |
| NM_TLBINVF = 0x0b, |
| NM_DI = 0x23, |
| NM_EI = 0x2b, |
| NM_RDPGPR = 0x70, |
| NM_WRPGPR = 0x78, |
| NM_WAIT = 0x61, |
| NM_DERET = 0x71, |
| NM_ERETX = 0x79, |
| |
| /* nanoMIPS DSP instructions */ |
| NM_ABSQ_S_QB = 0x00, |
| NM_ABSQ_S_PH = 0x08, |
| NM_ABSQ_S_W = 0x10, |
| NM_PRECEQ_W_PHL = 0x28, |
| NM_PRECEQ_W_PHR = 0x30, |
| NM_PRECEQU_PH_QBL = 0x38, |
| NM_PRECEQU_PH_QBR = 0x48, |
| NM_PRECEU_PH_QBL = 0x58, |
| NM_PRECEU_PH_QBR = 0x68, |
| NM_PRECEQU_PH_QBLA = 0x39, |
| NM_PRECEQU_PH_QBRA = 0x49, |
| NM_PRECEU_PH_QBLA = 0x59, |
| NM_PRECEU_PH_QBRA = 0x69, |
| NM_REPLV_PH = 0x01, |
| NM_REPLV_QB = 0x09, |
| NM_BITREV = 0x18, |
| NM_INSV = 0x20, |
| NM_RADDU_W_QB = 0x78, |
| |
| NM_BITSWAP = 0x05, |
| NM_WSBH = 0x3d, |
| }; |
| |
| /* PP.SR instruction pool */ |
| enum { |
| NM_SAVE = 0x00, |
| NM_RESTORE = 0x02, |
| NM_RESTORE_JRC = 0x03, |
| }; |
| |
| /* P.SR.F instruction pool */ |
| enum { |
| NM_SAVEF = 0x00, |
| NM_RESTOREF = 0x01, |
| }; |
| |
| /* P16.SYSCALL instruction pool */ |
| enum { |
| NM_SYSCALL16 = 0x00, |
| NM_HYPCALL16 = 0x01, |
| }; |
| |
| /* POOL16C_00 instruction pool */ |
| enum { |
| NM_NOT16 = 0x00, |
| NM_XOR16 = 0x01, |
| NM_AND16 = 0x02, |
| NM_OR16 = 0x03, |
| }; |
| |
| /* PP.LSX and PP.LSXS instruction pool */ |
| enum { |
| NM_LBX = 0x00, |
| NM_LHX = 0x04, |
| NM_LWX = 0x08, |
| NM_LDX = 0x0c, |
| |
| NM_SBX = 0x01, |
| NM_SHX = 0x05, |
| NM_SWX = 0x09, |
| NM_SDX = 0x0d, |
| |
| NM_LBUX = 0x02, |
| NM_LHUX = 0x06, |
| NM_LWC1X = 0x0a, |
| NM_LDC1X = 0x0e, |
| |
| NM_LWUX = 0x07, |
| NM_SWC1X = 0x0b, |
| NM_SDC1X = 0x0f, |
| |
| NM_LHXS = 0x04, |
| NM_LWXS = 0x08, |
| NM_LDXS = 0x0c, |
| |
| NM_SHXS = 0x05, |
| NM_SWXS = 0x09, |
| NM_SDXS = 0x0d, |
| |
| NM_LHUXS = 0x06, |
| NM_LWC1XS = 0x0a, |
| NM_LDC1XS = 0x0e, |
| |
| NM_LWUXS = 0x07, |
| NM_SWC1XS = 0x0b, |
| NM_SDC1XS = 0x0f, |
| }; |
| |
| /* ERETx instruction pool */ |
| enum { |
| NM_ERET = 0x00, |
| NM_ERETNC = 0x01, |
| }; |
| |
| /* POOL32FxF_{0, 1} insturction pool */ |
| enum { |
| NM_CFC1 = 0x40, |
| NM_CTC1 = 0x60, |
| NM_MFC1 = 0x80, |
| NM_MTC1 = 0xa0, |
| NM_MFHC1 = 0xc0, |
| NM_MTHC1 = 0xe0, |
| |
| NM_CVT_S_PL = 0x84, |
| NM_CVT_S_PU = 0xa4, |
| |
| NM_CVT_L_S = 0x004, |
| NM_CVT_L_D = 0x104, |
| NM_CVT_W_S = 0x024, |
| NM_CVT_W_D = 0x124, |
| |
| NM_RSQRT_S = 0x008, |
| NM_RSQRT_D = 0x108, |
| |
| NM_SQRT_S = 0x028, |
| NM_SQRT_D = 0x128, |
| |
| NM_RECIP_S = 0x048, |
| NM_RECIP_D = 0x148, |
| |
| NM_FLOOR_L_S = 0x00c, |
| NM_FLOOR_L_D = 0x10c, |
| |
| NM_FLOOR_W_S = 0x02c, |
| NM_FLOOR_W_D = 0x12c, |
| |
| NM_CEIL_L_S = 0x04c, |
| NM_CEIL_L_D = 0x14c, |
| NM_CEIL_W_S = 0x06c, |
| NM_CEIL_W_D = 0x16c, |
| NM_TRUNC_L_S = 0x08c, |
| NM_TRUNC_L_D = 0x18c, |
| NM_TRUNC_W_S = 0x0ac, |
| NM_TRUNC_W_D = 0x1ac, |
| NM_ROUND_L_S = 0x0cc, |
| NM_ROUND_L_D = 0x1cc, |
| NM_ROUND_W_S = 0x0ec, |
| NM_ROUND_W_D = 0x1ec, |
| |
| NM_MOV_S = 0x01, |
| NM_MOV_D = 0x81, |
| NM_ABS_S = 0x0d, |
| NM_ABS_D = 0x8d, |
| NM_NEG_S = 0x2d, |
| NM_NEG_D = 0xad, |
| NM_CVT_D_S = 0x04d, |
| NM_CVT_D_W = 0x0cd, |
| NM_CVT_D_L = 0x14d, |
| NM_CVT_S_D = 0x06d, |
| NM_CVT_S_W = 0x0ed, |
| NM_CVT_S_L = 0x16d, |
| }; |
| |
| /* P.LL instruction pool */ |
| enum { |
| NM_LL = 0x00, |
| NM_LLWP = 0x01, |
| }; |
| |
| /* P.SC instruction pool */ |
| enum { |
| NM_SC = 0x00, |
| NM_SCWP = 0x01, |
| }; |
| |
| /* P.DVP instruction pool */ |
| enum { |
| NM_DVP = 0x00, |
| NM_EVP = 0x01, |
| }; |
| |
| |
| /* |
| * |
| * nanoMIPS decoding engine |
| * |
| */ |
| |
| |
| /* extraction utilities */ |
| |
| #define NANOMIPS_EXTRACT_RT3(op) ((op >> 7) & 0x7) |
| #define NANOMIPS_EXTRACT_RS3(op) ((op >> 4) & 0x7) |
| #define NANOMIPS_EXTRACT_RD3(op) ((op >> 1) & 0x7) |
| #define NANOMIPS_EXTRACT_RD5(op) ((op >> 5) & 0x1f) |
| #define NANOMIPS_EXTRACT_RS5(op) (op & 0x1f) |
| |
| /* Implement nanoMIPS pseudocode decode_gpr(encoded_gpr, 'gpr3'). */ |
| static inline int decode_gpr_gpr3(int r) |
| { |
| static const int map[] = { 16, 17, 18, 19, 4, 5, 6, 7 }; |
| |
| return map[r & 0x7]; |
| } |
| |
| /* Implement nanoMIPS pseudocode decode_gpr(encoded_gpr, 'gpr3.src.store'). */ |
| static inline int decode_gpr_gpr3_src_store(int r) |
| { |
| static const int map[] = { 0, 17, 18, 19, 4, 5, 6, 7 }; |
| |
| return map[r & 0x7]; |
| } |
| |
| /* Implement nanoMIPS pseudocode decode_gpr(encoded_gpr, 'gpr4'). */ |
| static inline int decode_gpr_gpr4(int r) |
| { |
| static const int map[] = { 8, 9, 10, 11, 4, 5, 6, 7, |
| 16, 17, 18, 19, 20, 21, 22, 23 }; |
| |
| return map[r & 0xf]; |
| } |
| |
| /* Implement nanoMIPS pseudocode decode_gpr(encoded_gpr, 'gpr4.zero'). */ |
| static inline int decode_gpr_gpr4_zero(int r) |
| { |
| static const int map[] = { 8, 9, 10, 0, 4, 5, 6, 7, |
| 16, 17, 18, 19, 20, 21, 22, 23 }; |
| |
| return map[r & 0xf]; |
| } |
| |
| static void gen_ext(DisasContext *ctx, int wordsz, int rd, int rs, int rt, |
| int shift) |
| { |
| gen_align_bits(ctx, wordsz, rd, rs, rt, wordsz - shift); |
| } |
| |
| static void gen_llwp(DisasContext *ctx, uint32_t base, int16_t offset, |
| uint32_t reg1, uint32_t reg2) |
| { |
| TCGv taddr = tcg_temp_new(); |
| TCGv_i64 tval = tcg_temp_new_i64(); |
| TCGv tmp1 = tcg_temp_new(); |
| TCGv tmp2 = tcg_temp_new(); |
| |
| gen_base_offset_addr(ctx, taddr, base, offset); |
| tcg_gen_qemu_ld64(tval, taddr, ctx->mem_idx); |
| if (cpu_is_bigendian(ctx)) { |
| tcg_gen_extr_i64_tl(tmp2, tmp1, tval); |
| } else { |
| tcg_gen_extr_i64_tl(tmp1, tmp2, tval); |
| } |
| gen_store_gpr(tmp1, reg1); |
| tcg_temp_free(tmp1); |
| gen_store_gpr(tmp2, reg2); |
| tcg_temp_free(tmp2); |
| tcg_gen_st_i64(tval, cpu_env, offsetof(CPUMIPSState, llval_wp)); |
| tcg_temp_free_i64(tval); |
| tcg_gen_st_tl(taddr, cpu_env, offsetof(CPUMIPSState, lladdr)); |
| tcg_temp_free(taddr); |
| } |
| |
| static void gen_scwp(DisasContext *ctx, uint32_t base, int16_t offset, |
| uint32_t reg1, uint32_t reg2, bool eva) |
| { |
| TCGv taddr = tcg_temp_local_new(); |
| TCGv lladdr = tcg_temp_local_new(); |
| TCGv_i64 tval = tcg_temp_new_i64(); |
| TCGv_i64 llval = tcg_temp_new_i64(); |
| TCGv_i64 val = tcg_temp_new_i64(); |
| TCGv tmp1 = tcg_temp_new(); |
| TCGv tmp2 = tcg_temp_new(); |
| TCGLabel *lab_fail = gen_new_label(); |
| TCGLabel *lab_done = gen_new_label(); |
| |
| gen_base_offset_addr(ctx, taddr, base, offset); |
| |
| tcg_gen_ld_tl(lladdr, cpu_env, offsetof(CPUMIPSState, lladdr)); |
| tcg_gen_brcond_tl(TCG_COND_NE, taddr, lladdr, lab_fail); |
| |
| gen_load_gpr(tmp1, reg1); |
| gen_load_gpr(tmp2, reg2); |
| |
| if (cpu_is_bigendian(ctx)) { |
| tcg_gen_concat_tl_i64(tval, tmp2, tmp1); |
| } else { |
| tcg_gen_concat_tl_i64(tval, tmp1, tmp2); |
| } |
| |
| tcg_gen_ld_i64(llval, cpu_env, offsetof(CPUMIPSState, llval_wp)); |
| tcg_gen_atomic_cmpxchg_i64(val, taddr, llval, tval, |
| eva ? MIPS_HFLAG_UM : ctx->mem_idx, MO_64); |
| if (reg1 != 0) { |
| tcg_gen_movi_tl(cpu_gpr[reg1], 1); |
| } |
| tcg_gen_brcond_i64(TCG_COND_EQ, val, llval, lab_done); |
| |
| gen_set_label(lab_fail); |
| |
| if (reg1 != 0) { |
| tcg_gen_movi_tl(cpu_gpr[reg1], 0); |
| } |
| gen_set_label(lab_done); |
| tcg_gen_movi_tl(lladdr, -1); |
| tcg_gen_st_tl(lladdr, cpu_env, offsetof(CPUMIPSState, lladdr)); |
| } |
| |
| static void gen_adjust_sp(DisasContext *ctx, int u) |
| { |
| gen_op_addr_addi(ctx, cpu_gpr[29], cpu_gpr[29], u); |
| } |
| |
| static void gen_save(DisasContext *ctx, uint8_t rt, uint8_t count, |
| uint8_t gp, uint16_t u) |
| { |
| int counter = 0; |
| TCGv va = tcg_temp_new(); |
| TCGv t0 = tcg_temp_new(); |
| |
| while (counter != count) { |
| bool use_gp = gp && (counter == count - 1); |
| int this_rt = use_gp ? 28 : (rt & 0x10) | ((rt + counter) & 0x1f); |
| int this_offset = -((counter + 1) << 2); |
| gen_base_offset_addr(ctx, va, 29, this_offset); |
| gen_load_gpr(t0, this_rt); |
| tcg_gen_qemu_st_tl(t0, va, ctx->mem_idx, |
| (MO_TEUL | ctx->default_tcg_memop_mask)); |
| counter++; |
| } |
| |
| /* adjust stack pointer */ |
| gen_adjust_sp(ctx, -u); |
| |
| tcg_temp_free(t0); |
| tcg_temp_free(va); |
| } |
| |
| static void gen_restore(DisasContext *ctx, uint8_t rt, uint8_t count, |
| uint8_t gp, uint16_t u) |
| { |
| int counter = 0; |
| TCGv va = tcg_temp_new(); |
| TCGv t0 = tcg_temp_new(); |
| |
| while (counter != count) { |
| bool use_gp = gp && (counter == count - 1); |
| int this_rt = use_gp ? 28 : (rt & 0x10) | ((rt + counter) & 0x1f); |
| int this_offset = u - ((counter + 1) << 2); |
| gen_base_offset_addr(ctx, va, 29, this_offset); |
| tcg_gen_qemu_ld_tl(t0, va, ctx->mem_idx, MO_TESL | |
| ctx->default_tcg_memop_mask); |
| tcg_gen_ext32s_tl(t0, t0); |
| gen_store_gpr(t0, this_rt); |
| counter++; |
| } |
| |
| /* adjust stack pointer */ |
| gen_adjust_sp(ctx, u); |
| |
| tcg_temp_free(t0); |
| tcg_temp_free(va); |
| } |
| |
| static void gen_compute_branch_nm(DisasContext *ctx, uint32_t opc, |
| int insn_bytes, |
| int rs, int rt, int32_t offset) |
| { |
| target_ulong btgt = -1; |
| int bcond_compute = 0; |
| TCGv t0 = tcg_temp_new(); |
| TCGv t1 = tcg_temp_new(); |
| |
| /* Load needed operands */ |
| switch (opc) { |
| case OPC_BEQ: |
| case OPC_BNE: |
| /* Compare two registers */ |
| if (rs != rt) { |
| gen_load_gpr(t0, rs); |
| gen_load_gpr(t1, rt); |
| bcond_compute = 1; |
| } |
| btgt = ctx->base.pc_next + insn_bytes + offset; |
| break; |
| case OPC_BGEZAL: |
| /* Compare to zero */ |
| if (rs != 0) { |
| gen_load_gpr(t0, rs); |
| bcond_compute = 1; |
| } |
| btgt = ctx->base.pc_next + insn_bytes + offset; |
| break; |
| case OPC_BPOSGE32: |
| tcg_gen_andi_tl(t0, cpu_dspctrl, 0x3F); |
| bcond_compute = 1; |
| btgt = ctx->base.pc_next + insn_bytes + offset; |
| break; |
| case OPC_JR: |
| case OPC_JALR: |
| /* Jump to register */ |
| if (offset != 0 && offset != 16) { |
| /* |
| * Hint = 0 is JR/JALR, hint 16 is JR.HB/JALR.HB, the |
| * others are reserved. |
| */ |
| MIPS_INVAL("jump hint"); |
| gen_reserved_instruction(ctx); |
| goto out; |
| } |
| gen_load_gpr(btarget, rs); |
| break; |
| default: |
| MIPS_INVAL("branch/jump"); |
| gen_reserved_instruction(ctx); |
| goto out; |
| } |
| if (bcond_compute == 0) { |
| /* No condition to be computed */ |
| switch (opc) { |
| case OPC_BEQ: /* rx == rx */ |
| /* Always take */ |
| ctx->hflags |= MIPS_HFLAG_B; |
| break; |
| case OPC_BGEZAL: /* 0 >= 0 */ |
| /* Always take and link */ |
| tcg_gen_movi_tl(cpu_gpr[31], |
| ctx->base.pc_next + insn_bytes); |
| ctx->hflags |= MIPS_HFLAG_B; |
| break; |
| case OPC_BNE: /* rx != rx */ |
| tcg_gen_movi_tl(cpu_gpr[31], ctx->base.pc_next + 8); |
| /* Skip the instruction in the delay slot */ |
| ctx->base.pc_next += 4; |
| goto out; |
| case OPC_JR: |
| ctx->hflags |= MIPS_HFLAG_BR; |
| break; |
| case OPC_JALR: |
| if (rt > 0) { |
| tcg_gen_movi_tl(cpu_gpr[rt], |
| ctx->base.pc_next + insn_bytes); |
| } |
| ctx->hflags |= MIPS_HFLAG_BR; |
| break; |
| default: |
| MIPS_INVAL("branch/jump"); |
| gen_reserved_instruction(ctx); |
| goto out; |
| } |
| } else { |
| switch (opc) { |
| case OPC_BEQ: |
| tcg_gen_setcond_tl(TCG_COND_EQ, bcond, t0, t1); |
| goto not_likely; |
| case OPC_BNE: |
| tcg_gen_setcond_tl(TCG_COND_NE, bcond, t0, t1); |
| goto not_likely; |
| case OPC_BGEZAL: |
| tcg_gen_setcondi_tl(TCG_COND_GE, bcond, t0, 0); |
| tcg_gen_movi_tl(cpu_gpr[31], |
| ctx->base.pc_next + insn_bytes); |
| goto not_likely; |
| case OPC_BPOSGE32: |
| tcg_gen_setcondi_tl(TCG_COND_GE, bcond, t0, 32); |
| not_likely: |
| ctx->hflags |= MIPS_HFLAG_BC; |
| break; |
| default: |
| MIPS_INVAL("conditional branch/jump"); |
| gen_reserved_instruction(ctx); |
| goto out; |
| } |
| } |
| |
| ctx->btarget = btgt; |
| |
| out: |
| if (insn_bytes == 2) { |
| ctx->hflags |= MIPS_HFLAG_B16; |
| } |
| tcg_temp_free(t0); |
| tcg_temp_free(t1); |
| } |
| |
| static void gen_pool16c_nanomips_insn(DisasContext *ctx) |
| { |
| int rt = decode_gpr_gpr3(NANOMIPS_EXTRACT_RT3(ctx->opcode)); |
| int rs = decode_gpr_gpr3(NANOMIPS_EXTRACT_RS3(ctx->opcode)); |
| |
| switch (extract32(ctx->opcode, 2, 2)) { |
| case NM_NOT16: |
| gen_logic(ctx, OPC_NOR, rt, rs, 0); |
| break; |
| case NM_AND16: |
| gen_logic(ctx, OPC_AND, rt, rt, rs); |
| break; |
| case NM_XOR16: |
| gen_logic(ctx, OPC_XOR, rt, rt, rs); |
| break; |
| case NM_OR16: |
| gen_logic(ctx, OPC_OR, rt, rt, rs); |
| break; |
| } |
| } |
| |
| static void gen_pool32a0_nanomips_insn(CPUMIPSState *env, DisasContext *ctx) |
| { |
| int rt = extract32(ctx->opcode, 21, 5); |
| int rs = extract32(ctx->opcode, 16, 5); |
| int rd = extract32(ctx->opcode, 11, 5); |
| |
| switch (extract32(ctx->opcode, 3, 7)) { |
| case NM_P_TRAP: |
| switch (extract32(ctx->opcode, 10, 1)) { |
| case NM_TEQ: |
| check_nms(ctx); |
| gen_trap(ctx, OPC_TEQ, rs, rt, -1, rd); |
| break; |
| case NM_TNE: |
| check_nms(ctx); |
| gen_trap(ctx, OPC_TNE, rs, rt, -1, rd); |
| break; |
| } |
| break; |
| case NM_RDHWR: |
| check_nms(ctx); |
| gen_rdhwr(ctx, rt, rs, extract32(ctx->opcode, 11, 3)); |
| break; |
| case NM_SEB: |
| check_nms(ctx); |
| gen_bshfl(ctx, OPC_SEB, rs, rt); |
| break; |
| case NM_SEH: |
| gen_bshfl(ctx, OPC_SEH, rs, rt); |
| break; |
| case NM_SLLV: |
| gen_shift(ctx, OPC_SLLV, rd, rt, rs); |
| break; |
| case NM_SRLV: |
| gen_shift(ctx, OPC_SRLV, rd, rt, rs); |
| break; |
| case NM_SRAV: |
| gen_shift(ctx, OPC_SRAV, rd, rt, rs); |
| break; |
| case NM_ROTRV: |
| gen_shift(ctx, OPC_ROTRV, rd, rt, rs); |
| break; |
| case NM_ADD: |
| gen_arith(ctx, OPC_ADD, rd, rs, rt); |
| break; |
| case NM_ADDU: |
| gen_arith(ctx, OPC_ADDU, rd, rs, rt); |
| break; |
| case NM_SUB: |
| check_nms(ctx); |
| gen_arith(ctx, OPC_SUB, rd, rs, rt); |
| break; |
| case NM_SUBU: |
| gen_arith(ctx, OPC_SUBU, rd, rs, rt); |
| break; |
| case NM_P_CMOVE: |
| switch (extract32(ctx->opcode, 10, 1)) { |
| case NM_MOVZ: |
| gen_cond_move(ctx, OPC_MOVZ, rd, rs, rt); |
| break; |
| case NM_MOVN: |
| gen_cond_move(ctx, OPC_MOVN, rd, rs, rt); |
| break; |
| } |
| break; |
| case NM_AND: |
| gen_logic(ctx, OPC_AND, rd, rs, rt); |
| break; |
| case NM_OR: |
| gen_logic(ctx, OPC_OR, rd, rs, rt); |
| break; |
| case NM_NOR: |
| gen_logic(ctx, OPC_NOR, rd, rs, rt); |
| break; |
| case NM_XOR: |
| gen_logic(ctx, OPC_XOR, rd, rs, rt); |
| break; |
| case NM_SLT: |
| gen_slt(ctx, OPC_SLT, rd, rs, rt); |
| break; |
| case NM_P_SLTU: |
| if (rd == 0) { |
| /* P_DVP */ |
| #ifndef CONFIG_USER_ONLY |
| TCGv t0 = tcg_temp_new(); |
| switch (extract32(ctx->opcode, 10, 1)) { |
| case NM_DVP: |
| if (ctx->vp) { |
| check_cp0_enabled(ctx); |
| gen_helper_dvp(t0, cpu_env); |
| gen_store_gpr(t0, rt); |
| } |
| break; |
| case NM_EVP: |
| if (ctx->vp) { |
| check_cp0_enabled(ctx); |
| gen_helper_evp(t0, cpu_env); |
| gen_store_gpr(t0, rt); |
| } |
| break; |
| } |
| tcg_temp_free(t0); |
| #endif |
| } else { |
| gen_slt(ctx, OPC_SLTU, rd, rs, rt); |
| } |
| break; |
| case NM_SOV: |
| { |
| TCGv t0 = tcg_temp_new(); |
| TCGv t1 = tcg_temp_new(); |
| TCGv t2 = tcg_temp_new(); |
| |
| gen_load_gpr(t1, rs); |
| gen_load_gpr(t2, rt); |
| tcg_gen_add_tl(t0, t1, t2); |
| tcg_gen_ext32s_tl(t0, t0); |
| tcg_gen_xor_tl(t1, t1, t2); |
| tcg_gen_xor_tl(t2, t0, t2); |
| tcg_gen_andc_tl(t1, t2, t1); |
| |
| /* operands of same sign, result different sign */ |
| tcg_gen_setcondi_tl(TCG_COND_LT, t0, t1, 0); |
| gen_store_gpr(t0, rd); |
| |
| tcg_temp_free(t0); |
| tcg_temp_free(t1); |
| tcg_temp_free(t2); |
| } |
| break; |
| case NM_MUL: |
| gen_r6_muldiv(ctx, R6_OPC_MUL, rd, rs, rt); |
| break; |
| case NM_MUH: |
| gen_r6_muldiv(ctx, R6_OPC_MUH, rd, rs, rt); |
| break; |
| case NM_MULU: |
| gen_r6_muldiv(ctx, R6_OPC_MULU, rd, rs, rt); |
| break; |
| case NM_MUHU: |
| gen_r6_muldiv(ctx, R6_OPC_MUHU, rd, rs, rt); |
| break; |
| case NM_DIV: |
| gen_r6_muldiv(ctx, R6_OPC_DIV, rd, rs, rt); |
| break; |
| case NM_MOD: |
| gen_r6_muldiv(ctx, R6_OPC_MOD, rd, rs, rt); |
| break; |
| case NM_DIVU: |
| gen_r6_muldiv(ctx, R6_OPC_DIVU, rd, rs, rt); |
| break; |
| case NM_MODU: |
| gen_r6_muldiv(ctx, R6_OPC_MODU, rd, rs, rt); |
| break; |
| #ifndef CONFIG_USER_ONLY |
| case NM_MFC0: |
| check_cp0_enabled(ctx); |
| if (rt == 0) { |
| /* Treat as NOP. */ |
| break; |
| } |
| gen_mfc0(ctx, cpu_gpr[rt], rs, extract32(ctx->opcode, 11, 3)); |
| break; |
| case NM_MTC0: |
| check_cp0_enabled(ctx); |
| { |
| TCGv t0 = tcg_temp_new(); |
| |
| gen_load_gpr(t0, rt); |
| gen_mtc0(ctx, t0, rs, extract32(ctx->opcode, 11, 3)); |
| tcg_temp_free(t0); |
| } |
| break; |
| case NM_D_E_MT_VPE: |
| { |
| uint8_t sc = extract32(ctx->opcode, 10, 1); |
| TCGv t0 = tcg_temp_new(); |
| |
| switch (sc) { |
| case 0: |
| if (rs == 1) { |
| /* DMT */ |
| check_cp0_mt(ctx); |
| gen_helper_dmt(t0); |
| gen_store_gpr(t0, rt); |
| } else if (rs == 0) { |
| /* DVPE */ |
| check_cp0_mt(ctx); |
| gen_helper_dvpe(t0, cpu_env); |
| gen_store_gpr(t0, rt); |
| } else { |
| gen_reserved_instruction(ctx); |
| } |
| break; |
| case 1: |
| if (rs == 1) { |
| /* EMT */ |
| check_cp0_mt(ctx); |
| gen_helper_emt(t0); |
| gen_store_gpr(t0, rt); |
| } else if (rs == 0) { |
| /* EVPE */ |
| check_cp0_mt(ctx); |
| gen_helper_evpe(t0, cpu_env); |
| gen_store_gpr(t0, rt); |
| } else { |
| gen_reserved_instruction(ctx); |
| } |
| break; |
| } |
| |
| tcg_temp_free(t0); |
| } |
| break; |
| case NM_FORK: |
| check_mt(ctx); |
| { |
| TCGv t0 = tcg_temp_new(); |
| TCGv t1 = tcg_temp_new(); |
| |
| gen_load_gpr(t0, rt); |
| gen_load_gpr(t1, rs); |
| gen_helper_fork(t0, t1); |
| tcg_temp_free(t0); |
| tcg_temp_free(t1); |
| } |
| break; |
| case NM_MFTR: |
| case NM_MFHTR: |
| check_cp0_enabled(ctx); |
| if (rd == 0) { |
| /* Treat as NOP. */ |
| return; |
| } |
| gen_mftr(env, ctx, rs, rt, extract32(ctx->opcode, 10, 1), |
| extract32(ctx->opcode, 11, 5), extract32(ctx->opcode, 3, 1)); |
| break; |
| case NM_MTTR: |
| case NM_MTHTR: |
| check_cp0_enabled(ctx); |
| gen_mttr(env, ctx, rs, rt, extract32(ctx->opcode, 10, 1), |
| extract32(ctx->opcode, 11, 5), extract32(ctx->opcode, 3, 1)); |
| break; |
| case NM_YIELD: |
| check_mt(ctx); |
| { |
| TCGv t0 = tcg_temp_new(); |
| |
| gen_load_gpr(t0, rs); |
| gen_helper_yield(t0, cpu_env, t0); |
| gen_store_gpr(t0, rt); |
| tcg_temp_free(t0); |
| } |
| break; |
| #endif |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| } |
| |
| /* dsp */ |
| static void gen_pool32axf_1_5_nanomips_insn(DisasContext *ctx, uint32_t opc, |
| int ret, int v1, int v2) |
| { |
| TCGv_i32 t0; |
| TCGv v0_t; |
| TCGv v1_t; |
| |
| t0 = tcg_temp_new_i32(); |
| |
| v0_t = tcg_temp_new(); |
| v1_t = tcg_temp_new(); |
| |
| tcg_gen_movi_i32(t0, v2 >> 3); |
| |
| gen_load_gpr(v0_t, ret); |
| gen_load_gpr(v1_t, v1); |
| |
| switch (opc) { |
| case NM_MAQ_S_W_PHR: |
| check_dsp(ctx); |
| gen_helper_maq_s_w_phr(t0, v1_t, v0_t, cpu_env); |
| break; |
| case NM_MAQ_S_W_PHL: |
| check_dsp(ctx); |
| gen_helper_maq_s_w_phl(t0, v1_t, v0_t, cpu_env); |
| break; |
| case NM_MAQ_SA_W_PHR: |
| check_dsp(ctx); |
| gen_helper_maq_sa_w_phr(t0, v1_t, v0_t, cpu_env); |
| break; |
| case NM_MAQ_SA_W_PHL: |
| check_dsp(ctx); |
| gen_helper_maq_sa_w_phl(t0, v1_t, v0_t, cpu_env); |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| |
| tcg_temp_free_i32(t0); |
| |
| tcg_temp_free(v0_t); |
| tcg_temp_free(v1_t); |
| } |
| |
| |
| static void gen_pool32axf_1_nanomips_insn(DisasContext *ctx, uint32_t opc, |
| int ret, int v1, int v2) |
| { |
| int16_t imm; |
| TCGv t0 = tcg_temp_new(); |
| TCGv t1 = tcg_temp_new(); |
| TCGv v0_t = tcg_temp_new(); |
| |
| gen_load_gpr(v0_t, v1); |
| |
| switch (opc) { |
| case NM_POOL32AXF_1_0: |
| check_dsp(ctx); |
| switch (extract32(ctx->opcode, 12, 2)) { |
| case NM_MFHI: |
| gen_HILO(ctx, OPC_MFHI, v2 >> 3, ret); |
| break; |
| case NM_MFLO: |
| gen_HILO(ctx, OPC_MFLO, v2 >> 3, ret); |
| break; |
| case NM_MTHI: |
| gen_HILO(ctx, OPC_MTHI, v2 >> 3, v1); |
| break; |
| case NM_MTLO: |
| gen_HILO(ctx, OPC_MTLO, v2 >> 3, v1); |
| break; |
| } |
| break; |
| case NM_POOL32AXF_1_1: |
| check_dsp(ctx); |
| switch (extract32(ctx->opcode, 12, 2)) { |
| case NM_MTHLIP: |
| tcg_gen_movi_tl(t0, v2 >> 3); |
| gen_helper_mthlip(t0, v0_t, cpu_env); |
| break; |
| case NM_SHILOV: |
| tcg_gen_movi_tl(t0, v2 >> 3); |
| gen_helper_shilo(t0, v0_t, cpu_env); |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| break; |
| case NM_POOL32AXF_1_3: |
| check_dsp(ctx); |
| imm = extract32(ctx->opcode, 14, 7); |
| switch (extract32(ctx->opcode, 12, 2)) { |
| case NM_RDDSP: |
| tcg_gen_movi_tl(t0, imm); |
| gen_helper_rddsp(t0, t0, cpu_env); |
| gen_store_gpr(t0, ret); |
| break; |
| case NM_WRDSP: |
| gen_load_gpr(t0, ret); |
| tcg_gen_movi_tl(t1, imm); |
| gen_helper_wrdsp(t0, t1, cpu_env); |
| break; |
| case NM_EXTP: |
| tcg_gen_movi_tl(t0, v2 >> 3); |
| tcg_gen_movi_tl(t1, v1); |
| gen_helper_extp(t0, t0, t1, cpu_env); |
| gen_store_gpr(t0, ret); |
| break; |
| case NM_EXTPDP: |
| tcg_gen_movi_tl(t0, v2 >> 3); |
| tcg_gen_movi_tl(t1, v1); |
| gen_helper_extpdp(t0, t0, t1, cpu_env); |
| gen_store_gpr(t0, ret); |
| break; |
| } |
| break; |
| case NM_POOL32AXF_1_4: |
| check_dsp(ctx); |
| tcg_gen_movi_tl(t0, v2 >> 2); |
| switch (extract32(ctx->opcode, 12, 1)) { |
| case NM_SHLL_QB: |
| gen_helper_shll_qb(t0, t0, v0_t, cpu_env); |
| gen_store_gpr(t0, ret); |
| break; |
| case NM_SHRL_QB: |
| gen_helper_shrl_qb(t0, t0, v0_t); |
| gen_store_gpr(t0, ret); |
| break; |
| } |
| break; |
| case NM_POOL32AXF_1_5: |
| opc = extract32(ctx->opcode, 12, 2); |
| gen_pool32axf_1_5_nanomips_insn(ctx, opc, ret, v1, v2); |
| break; |
| case NM_POOL32AXF_1_7: |
| check_dsp(ctx); |
| tcg_gen_movi_tl(t0, v2 >> 3); |
| tcg_gen_movi_tl(t1, v1); |
| switch (extract32(ctx->opcode, 12, 2)) { |
| case NM_EXTR_W: |
| gen_helper_extr_w(t0, t0, t1, cpu_env); |
| gen_store_gpr(t0, ret); |
| break; |
| case NM_EXTR_R_W: |
| gen_helper_extr_r_w(t0, t0, t1, cpu_env); |
| gen_store_gpr(t0, ret); |
| break; |
| case NM_EXTR_RS_W: |
| gen_helper_extr_rs_w(t0, t0, t1, cpu_env); |
| gen_store_gpr(t0, ret); |
| break; |
| case NM_EXTR_S_H: |
| gen_helper_extr_s_h(t0, t0, t1, cpu_env); |
| gen_store_gpr(t0, ret); |
| break; |
| } |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| |
| tcg_temp_free(t0); |
| tcg_temp_free(t1); |
| tcg_temp_free(v0_t); |
| } |
| |
| static void gen_pool32axf_2_multiply(DisasContext *ctx, uint32_t opc, |
| TCGv v0, TCGv v1, int rd) |
| { |
| TCGv_i32 t0; |
| |
| t0 = tcg_temp_new_i32(); |
| |
| tcg_gen_movi_i32(t0, rd >> 3); |
| |
| switch (opc) { |
| case NM_POOL32AXF_2_0_7: |
| switch (extract32(ctx->opcode, 9, 3)) { |
| case NM_DPA_W_PH: |
| check_dsp_r2(ctx); |
| gen_helper_dpa_w_ph(t0, v1, v0, cpu_env); |
| break; |
| case NM_DPAQ_S_W_PH: |
| check_dsp(ctx); |
| gen_helper_dpaq_s_w_ph(t0, v1, v0, cpu_env); |
| break; |
| case NM_DPS_W_PH: |
| check_dsp_r2(ctx); |
| gen_helper_dps_w_ph(t0, v1, v0, cpu_env); |
| break; |
| case NM_DPSQ_S_W_PH: |
| check_dsp(ctx); |
| gen_helper_dpsq_s_w_ph(t0, v1, v0, cpu_env); |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| break; |
| case NM_POOL32AXF_2_8_15: |
| switch (extract32(ctx->opcode, 9, 3)) { |
| case NM_DPAX_W_PH: |
| check_dsp_r2(ctx); |
| gen_helper_dpax_w_ph(t0, v0, v1, cpu_env); |
| break; |
| case NM_DPAQ_SA_L_W: |
| check_dsp(ctx); |
| gen_helper_dpaq_sa_l_w(t0, v0, v1, cpu_env); |
| break; |
| case NM_DPSX_W_PH: |
| check_dsp_r2(ctx); |
| gen_helper_dpsx_w_ph(t0, v0, v1, cpu_env); |
| break; |
| case NM_DPSQ_SA_L_W: |
| check_dsp(ctx); |
| gen_helper_dpsq_sa_l_w(t0, v0, v1, cpu_env); |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| break; |
| case NM_POOL32AXF_2_16_23: |
| switch (extract32(ctx->opcode, 9, 3)) { |
| case NM_DPAU_H_QBL: |
| check_dsp(ctx); |
| gen_helper_dpau_h_qbl(t0, v0, v1, cpu_env); |
| break; |
| case NM_DPAQX_S_W_PH: |
| check_dsp_r2(ctx); |
| gen_helper_dpaqx_s_w_ph(t0, v0, v1, cpu_env); |
| break; |
| case NM_DPSU_H_QBL: |
| check_dsp(ctx); |
| gen_helper_dpsu_h_qbl(t0, v0, v1, cpu_env); |
| break; |
| case NM_DPSQX_S_W_PH: |
| check_dsp_r2(ctx); |
| gen_helper_dpsqx_s_w_ph(t0, v0, v1, cpu_env); |
| break; |
| case NM_MULSA_W_PH: |
| check_dsp_r2(ctx); |
| gen_helper_mulsa_w_ph(t0, v0, v1, cpu_env); |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| break; |
| case NM_POOL32AXF_2_24_31: |
| switch (extract32(ctx->opcode, 9, 3)) { |
| case NM_DPAU_H_QBR: |
| check_dsp(ctx); |
| gen_helper_dpau_h_qbr(t0, v1, v0, cpu_env); |
| break; |
| case NM_DPAQX_SA_W_PH: |
| check_dsp_r2(ctx); |
| gen_helper_dpaqx_sa_w_ph(t0, v1, v0, cpu_env); |
| break; |
| case NM_DPSU_H_QBR: |
| check_dsp(ctx); |
| gen_helper_dpsu_h_qbr(t0, v1, v0, cpu_env); |
| break; |
| case NM_DPSQX_SA_W_PH: |
| check_dsp_r2(ctx); |
| gen_helper_dpsqx_sa_w_ph(t0, v1, v0, cpu_env); |
| break; |
| case NM_MULSAQ_S_W_PH: |
| check_dsp(ctx); |
| gen_helper_mulsaq_s_w_ph(t0, v1, v0, cpu_env); |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| |
| tcg_temp_free_i32(t0); |
| } |
| |
| static void gen_pool32axf_2_nanomips_insn(DisasContext *ctx, uint32_t opc, |
| int rt, int rs, int rd) |
| { |
| int ret = rt; |
| TCGv t0 = tcg_temp_new(); |
| TCGv t1 = tcg_temp_new(); |
| TCGv v0_t = tcg_temp_new(); |
| TCGv v1_t = tcg_temp_new(); |
| |
| gen_load_gpr(v0_t, rt); |
| gen_load_gpr(v1_t, rs); |
| |
| switch (opc) { |
| case NM_POOL32AXF_2_0_7: |
| switch (extract32(ctx->opcode, 9, 3)) { |
| case NM_DPA_W_PH: |
| case NM_DPAQ_S_W_PH: |
| case NM_DPS_W_PH: |
| case NM_DPSQ_S_W_PH: |
| gen_pool32axf_2_multiply(ctx, opc, v0_t, v1_t, rd); |
| break; |
| case NM_BALIGN: |
| check_dsp_r2(ctx); |
| if (rt != 0) { |
| gen_load_gpr(t0, rs); |
| rd &= 3; |
| if (rd != 0 && rd != 2) { |
| tcg_gen_shli_tl(cpu_gpr[ret], cpu_gpr[ret], 8 * rd); |
| tcg_gen_ext32u_tl(t0, t0); |
| tcg_gen_shri_tl(t0, t0, 8 * (4 - rd)); |
| tcg_gen_or_tl(cpu_gpr[ret], cpu_gpr[ret], t0); |
| } |
| tcg_gen_ext32s_tl(cpu_gpr[ret], cpu_gpr[ret]); |
| } |
| break; |
| case NM_MADD: |
| check_dsp(ctx); |
| { |
| int acc = extract32(ctx->opcode, 14, 2); |
| TCGv_i64 t2 = tcg_temp_new_i64(); |
| TCGv_i64 t3 = tcg_temp_new_i64(); |
| |
| gen_load_gpr(t0, rt); |
| gen_load_gpr(t1, rs); |
| tcg_gen_ext_tl_i64(t2, t0); |
| tcg_gen_ext_tl_i64(t3, t1); |
| tcg_gen_mul_i64(t2, t2, t3); |
| tcg_gen_concat_tl_i64(t3, cpu_LO[acc], cpu_HI[acc]); |
| tcg_gen_add_i64(t2, t2, t3); |
| tcg_temp_free_i64(t3); |
| gen_move_low32(cpu_LO[acc], t2); |
| gen_move_high32(cpu_HI[acc], t2); |
| tcg_temp_free_i64(t2); |
| } |
| break; |
| case NM_MULT: |
| check_dsp(ctx); |
| { |
| int acc = extract32(ctx->opcode, 14, 2); |
| TCGv_i32 t2 = tcg_temp_new_i32(); |
| TCGv_i32 t3 = tcg_temp_new_i32(); |
| |
| if (acc || ctx->insn_flags & ISA_MIPS_R6) { |
| check_dsp_r2(ctx); |
| } |
| gen_load_gpr(t0, rs); |
| gen_load_gpr(t1, rt); |
| tcg_gen_trunc_tl_i32(t2, t0); |
| tcg_gen_trunc_tl_i32(t3, t1); |
| tcg_gen_muls2_i32(t2, t3, t2, t3); |
| tcg_gen_ext_i32_tl(cpu_LO[acc], t2); |
| tcg_gen_ext_i32_tl(cpu_HI[acc], t3); |
| tcg_temp_free_i32(t2); |
| tcg_temp_free_i32(t3); |
| } |
| break; |
| case NM_EXTRV_W: |
| check_dsp(ctx); |
| gen_load_gpr(v1_t, rs); |
| tcg_gen_movi_tl(t0, rd >> 3); |
| gen_helper_extr_w(t0, t0, v1_t, cpu_env); |
| gen_store_gpr(t0, ret); |
| break; |
| } |
| break; |
| case NM_POOL32AXF_2_8_15: |
| switch (extract32(ctx->opcode, 9, 3)) { |
| case NM_DPAX_W_PH: |
| case NM_DPAQ_SA_L_W: |
| case NM_DPSX_W_PH: |
| case NM_DPSQ_SA_L_W: |
| gen_pool32axf_2_multiply(ctx, opc, v0_t, v1_t, rd); |
| break; |
| case NM_MADDU: |
| check_dsp(ctx); |
| { |
| int acc = extract32(ctx->opcode, 14, 2); |
| TCGv_i64 t2 = tcg_temp_new_i64(); |
| TCGv_i64 t3 = tcg_temp_new_i64(); |
| |
| gen_load_gpr(t0, rs); |
| gen_load_gpr(t1, rt); |
| tcg_gen_ext32u_tl(t0, t0); |
| tcg_gen_ext32u_tl(t1, t1); |
| tcg_gen_extu_tl_i64(t2, t0); |
| tcg_gen_extu_tl_i64(t3, t1); |
| tcg_gen_mul_i64(t2, t2, t3); |
| tcg_gen_concat_tl_i64(t3, cpu_LO[acc], cpu_HI[acc]); |
| tcg_gen_add_i64(t2, t2, t3); |
| tcg_temp_free_i64(t3); |
| gen_move_low32(cpu_LO[acc], t2); |
| gen_move_high32(cpu_HI[acc], t2); |
| tcg_temp_free_i64(t2); |
| } |
| break; |
| case NM_MULTU: |
| check_dsp(ctx); |
| { |
| int acc = extract32(ctx->opcode, 14, 2); |
| TCGv_i32 t2 = tcg_temp_new_i32(); |
| TCGv_i32 t3 = tcg_temp_new_i32(); |
| |
| if (acc || ctx->insn_flags & ISA_MIPS_R6) { |
| check_dsp_r2(ctx); |
| } |
| gen_load_gpr(t0, rs); |
| gen_load_gpr(t1, rt); |
| tcg_gen_trunc_tl_i32(t2, t0); |
| tcg_gen_trunc_tl_i32(t3, t1); |
| tcg_gen_mulu2_i32(t2, t3, t2, t3); |
| tcg_gen_ext_i32_tl(cpu_LO[acc], t2); |
| tcg_gen_ext_i32_tl(cpu_HI[acc], t3); |
| tcg_temp_free_i32(t2); |
| tcg_temp_free_i32(t3); |
| } |
| break; |
| case NM_EXTRV_R_W: |
| check_dsp(ctx); |
| tcg_gen_movi_tl(t0, rd >> 3); |
| gen_helper_extr_r_w(t0, t0, v1_t, cpu_env); |
| gen_store_gpr(t0, ret); |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| break; |
| case NM_POOL32AXF_2_16_23: |
| switch (extract32(ctx->opcode, 9, 3)) { |
| case NM_DPAU_H_QBL: |
| case NM_DPAQX_S_W_PH: |
| case NM_DPSU_H_QBL: |
| case NM_DPSQX_S_W_PH: |
| case NM_MULSA_W_PH: |
| gen_pool32axf_2_multiply(ctx, opc, v0_t, v1_t, rd); |
| break; |
| case NM_EXTPV: |
| check_dsp(ctx); |
| tcg_gen_movi_tl(t0, rd >> 3); |
| gen_helper_extp(t0, t0, v1_t, cpu_env); |
| gen_store_gpr(t0, ret); |
| break; |
| case NM_MSUB: |
| check_dsp(ctx); |
| { |
| int acc = extract32(ctx->opcode, 14, 2); |
| TCGv_i64 t2 = tcg_temp_new_i64(); |
| TCGv_i64 t3 = tcg_temp_new_i64(); |
| |
| gen_load_gpr(t0, rs); |
| gen_load_gpr(t1, rt); |
| tcg_gen_ext_tl_i64(t2, t0); |
| tcg_gen_ext_tl_i64(t3, t1); |
| tcg_gen_mul_i64(t2, t2, t3); |
| tcg_gen_concat_tl_i64(t3, cpu_LO[acc], cpu_HI[acc]); |
| tcg_gen_sub_i64(t2, t3, t2); |
| tcg_temp_free_i64(t3); |
| gen_move_low32(cpu_LO[acc], t2); |
| gen_move_high32(cpu_HI[acc], t2); |
| tcg_temp_free_i64(t2); |
| } |
| break; |
| case NM_EXTRV_RS_W: |
| check_dsp(ctx); |
| tcg_gen_movi_tl(t0, rd >> 3); |
| gen_helper_extr_rs_w(t0, t0, v1_t, cpu_env); |
| gen_store_gpr(t0, ret); |
| break; |
| } |
| break; |
| case NM_POOL32AXF_2_24_31: |
| switch (extract32(ctx->opcode, 9, 3)) { |
| case NM_DPAU_H_QBR: |
| case NM_DPAQX_SA_W_PH: |
| case NM_DPSU_H_QBR: |
| case NM_DPSQX_SA_W_PH: |
| case NM_MULSAQ_S_W_PH: |
| gen_pool32axf_2_multiply(ctx, opc, v0_t, v1_t, rd); |
| break; |
| case NM_EXTPDPV: |
| check_dsp(ctx); |
| tcg_gen_movi_tl(t0, rd >> 3); |
| gen_helper_extpdp(t0, t0, v1_t, cpu_env); |
| gen_store_gpr(t0, ret); |
| break; |
| case NM_MSUBU: |
| check_dsp(ctx); |
| { |
| int acc = extract32(ctx->opcode, 14, 2); |
| TCGv_i64 t2 = tcg_temp_new_i64(); |
| TCGv_i64 t3 = tcg_temp_new_i64(); |
| |
| gen_load_gpr(t0, rs); |
| gen_load_gpr(t1, rt); |
| tcg_gen_ext32u_tl(t0, t0); |
| tcg_gen_ext32u_tl(t1, t1); |
| tcg_gen_extu_tl_i64(t2, t0); |
| tcg_gen_extu_tl_i64(t3, t1); |
| tcg_gen_mul_i64(t2, t2, t3); |
| tcg_gen_concat_tl_i64(t3, cpu_LO[acc], cpu_HI[acc]); |
| tcg_gen_sub_i64(t2, t3, t2); |
| tcg_temp_free_i64(t3); |
| gen_move_low32(cpu_LO[acc], t2); |
| gen_move_high32(cpu_HI[acc], t2); |
| tcg_temp_free_i64(t2); |
| } |
| break; |
| case NM_EXTRV_S_H: |
| check_dsp(ctx); |
| tcg_gen_movi_tl(t0, rd >> 3); |
| gen_helper_extr_s_h(t0, t0, v1_t, cpu_env); |
| gen_store_gpr(t0, ret); |
| break; |
| } |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| |
| tcg_temp_free(t0); |
| tcg_temp_free(t1); |
| |
| tcg_temp_free(v0_t); |
| tcg_temp_free(v1_t); |
| } |
| |
| static void gen_pool32axf_4_nanomips_insn(DisasContext *ctx, uint32_t opc, |
| int rt, int rs) |
| { |
| int ret = rt; |
| TCGv t0 = tcg_temp_new(); |
| TCGv v0_t = tcg_temp_new(); |
| |
| gen_load_gpr(v0_t, rs); |
| |
| switch (opc) { |
| case NM_ABSQ_S_QB: |
| check_dsp_r2(ctx); |
| gen_helper_absq_s_qb(v0_t, v0_t, cpu_env); |
| gen_store_gpr(v0_t, ret); |
| break; |
| case NM_ABSQ_S_PH: |
| check_dsp(ctx); |
| gen_helper_absq_s_ph(v0_t, v0_t, cpu_env); |
| gen_store_gpr(v0_t, ret); |
| break; |
| case NM_ABSQ_S_W: |
| check_dsp(ctx); |
| gen_helper_absq_s_w(v0_t, v0_t, cpu_env); |
| gen_store_gpr(v0_t, ret); |
| break; |
| case NM_PRECEQ_W_PHL: |
| check_dsp(ctx); |
| tcg_gen_andi_tl(v0_t, v0_t, 0xFFFF0000); |
| tcg_gen_ext32s_tl(v0_t, v0_t); |
| gen_store_gpr(v0_t, ret); |
| break; |
| case NM_PRECEQ_W_PHR: |
| check_dsp(ctx); |
| tcg_gen_andi_tl(v0_t, v0_t, 0x0000FFFF); |
| tcg_gen_shli_tl(v0_t, v0_t, 16); |
| tcg_gen_ext32s_tl(v0_t, v0_t); |
| gen_store_gpr(v0_t, ret); |
| break; |
| case NM_PRECEQU_PH_QBL: |
| check_dsp(ctx); |
| gen_helper_precequ_ph_qbl(v0_t, v0_t); |
| gen_store_gpr(v0_t, ret); |
| break; |
| case NM_PRECEQU_PH_QBR: |
| check_dsp(ctx); |
| gen_helper_precequ_ph_qbr(v0_t, v0_t); |
| gen_store_gpr(v0_t, ret); |
| break; |
| case NM_PRECEQU_PH_QBLA: |
| check_dsp(ctx); |
| gen_helper_precequ_ph_qbla(v0_t, v0_t); |
| gen_store_gpr(v0_t, ret); |
| break; |
| case NM_PRECEQU_PH_QBRA: |
| check_dsp(ctx); |
| gen_helper_precequ_ph_qbra(v0_t, v0_t); |
| gen_store_gpr(v0_t, ret); |
| break; |
| case NM_PRECEU_PH_QBL: |
| check_dsp(ctx); |
| gen_helper_preceu_ph_qbl(v0_t, v0_t); |
| gen_store_gpr(v0_t, ret); |
| break; |
| case NM_PRECEU_PH_QBR: |
| check_dsp(ctx); |
| gen_helper_preceu_ph_qbr(v0_t, v0_t); |
| gen_store_gpr(v0_t, ret); |
| break; |
| case NM_PRECEU_PH_QBLA: |
| check_dsp(ctx); |
| gen_helper_preceu_ph_qbla(v0_t, v0_t); |
| gen_store_gpr(v0_t, ret); |
| break; |
| case NM_PRECEU_PH_QBRA: |
| check_dsp(ctx); |
| gen_helper_preceu_ph_qbra(v0_t, v0_t); |
| gen_store_gpr(v0_t, ret); |
| break; |
| case NM_REPLV_PH: |
| check_dsp(ctx); |
| tcg_gen_ext16u_tl(v0_t, v0_t); |
| tcg_gen_shli_tl(t0, v0_t, 16); |
| tcg_gen_or_tl(v0_t, v0_t, t0); |
| tcg_gen_ext32s_tl(v0_t, v0_t); |
| gen_store_gpr(v0_t, ret); |
| break; |
| case NM_REPLV_QB: |
| check_dsp(ctx); |
| tcg_gen_ext8u_tl(v0_t, v0_t); |
| tcg_gen_shli_tl(t0, v0_t, 8); |
| tcg_gen_or_tl(v0_t, v0_t, t0); |
| tcg_gen_shli_tl(t0, v0_t, 16); |
| tcg_gen_or_tl(v0_t, v0_t, t0); |
| tcg_gen_ext32s_tl(v0_t, v0_t); |
| gen_store_gpr(v0_t, ret); |
| break; |
| case NM_BITREV: |
| check_dsp(ctx); |
| gen_helper_bitrev(v0_t, v0_t); |
| gen_store_gpr(v0_t, ret); |
| break; |
| case NM_INSV: |
| check_dsp(ctx); |
| { |
| TCGv tv0 = tcg_temp_new(); |
| |
| gen_load_gpr(tv0, rt); |
| gen_helper_insv(v0_t, cpu_env, v0_t, tv0); |
| gen_store_gpr(v0_t, ret); |
| tcg_temp_free(tv0); |
| } |
| break; |
| case NM_RADDU_W_QB: |
| check_dsp(ctx); |
| gen_helper_raddu_w_qb(v0_t, v0_t); |
| gen_store_gpr(v0_t, ret); |
| break; |
| case NM_BITSWAP: |
| gen_bitswap(ctx, OPC_BITSWAP, ret, rs); |
| break; |
| case NM_CLO: |
| check_nms(ctx); |
| gen_cl(ctx, OPC_CLO, ret, rs); |
| break; |
| case NM_CLZ: |
| check_nms(ctx); |
| gen_cl(ctx, OPC_CLZ, ret, rs); |
| break; |
| case NM_WSBH: |
| gen_bshfl(ctx, OPC_WSBH, ret, rs); |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| |
| tcg_temp_free(v0_t); |
| tcg_temp_free(t0); |
| } |
| |
| static void gen_pool32axf_7_nanomips_insn(DisasContext *ctx, uint32_t opc, |
| int rt, int rs, int rd) |
| { |
| TCGv t0 = tcg_temp_new(); |
| TCGv rs_t = tcg_temp_new(); |
| |
| gen_load_gpr(rs_t, rs); |
| |
| switch (opc) { |
| case NM_SHRA_R_QB: |
| check_dsp_r2(ctx); |
| tcg_gen_movi_tl(t0, rd >> 2); |
| switch (extract32(ctx->opcode, 12, 1)) { |
| case 0: |
| /* NM_SHRA_QB */ |
| gen_helper_shra_qb(t0, t0, rs_t); |
| gen_store_gpr(t0, rt); |
| break; |
| case 1: |
| /* NM_SHRA_R_QB */ |
| gen_helper_shra_r_qb(t0, t0, rs_t); |
| gen_store_gpr(t0, rt); |
| break; |
| } |
| break; |
| case NM_SHRL_PH: |
| check_dsp_r2(ctx); |
| tcg_gen_movi_tl(t0, rd >> 1); |
| gen_helper_shrl_ph(t0, t0, rs_t); |
| gen_store_gpr(t0, rt); |
| break; |
| case NM_REPL_QB: |
| check_dsp(ctx); |
| { |
| int16_t imm; |
| target_long result; |
| imm = extract32(ctx->opcode, 13, 8); |
| result = (uint32_t)imm << 24 | |
| (uint32_t)imm << 16 | |
| (uint32_t)imm << 8 | |
| (uint32_t)imm; |
| result = (int32_t)result; |
| tcg_gen_movi_tl(t0, result); |
| gen_store_gpr(t0, rt); |
| } |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| tcg_temp_free(t0); |
| tcg_temp_free(rs_t); |
| } |
| |
| |
| static void gen_pool32axf_nanomips_insn(CPUMIPSState *env, DisasContext *ctx) |
| { |
| int rt = extract32(ctx->opcode, 21, 5); |
| int rs = extract32(ctx->opcode, 16, 5); |
| int rd = extract32(ctx->opcode, 11, 5); |
| |
| switch (extract32(ctx->opcode, 6, 3)) { |
| case NM_POOL32AXF_1: |
| { |
| int32_t op1 = extract32(ctx->opcode, 9, 3); |
| gen_pool32axf_1_nanomips_insn(ctx, op1, rt, rs, rd); |
| } |
| break; |
| case NM_POOL32AXF_2: |
| { |
| int32_t op1 = extract32(ctx->opcode, 12, 2); |
| gen_pool32axf_2_nanomips_insn(ctx, op1, rt, rs, rd); |
| } |
| break; |
| case NM_POOL32AXF_4: |
| { |
| int32_t op1 = extract32(ctx->opcode, 9, 7); |
| gen_pool32axf_4_nanomips_insn(ctx, op1, rt, rs); |
| } |
| break; |
| case NM_POOL32AXF_5: |
| switch (extract32(ctx->opcode, 9, 7)) { |
| #ifndef CONFIG_USER_ONLY |
| case NM_TLBP: |
| gen_cp0(env, ctx, OPC_TLBP, 0, 0); |
| break; |
| case NM_TLBR: |
| gen_cp0(env, ctx, OPC_TLBR, 0, 0); |
| break; |
| case NM_TLBWI: |
| gen_cp0(env, ctx, OPC_TLBWI, 0, 0); |
| break; |
| case NM_TLBWR: |
| gen_cp0(env, ctx, OPC_TLBWR, 0, 0); |
| break; |
| case NM_TLBINV: |
| gen_cp0(env, ctx, OPC_TLBINV, 0, 0); |
| break; |
| case NM_TLBINVF: |
| gen_cp0(env, ctx, OPC_TLBINVF, 0, 0); |
| break; |
| case NM_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, rt); |
| /* Stop translation as we may have switched the execution mode */ |
| ctx->base.is_jmp = DISAS_STOP; |
| tcg_temp_free(t0); |
| } |
| break; |
| case NM_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, rt); |
| /* Stop translation as we may have switched the execution mode */ |
| ctx->base.is_jmp = DISAS_STOP; |
| tcg_temp_free(t0); |
| } |
| break; |
| case NM_RDPGPR: |
| check_cp0_enabled(ctx); |
| gen_load_srsgpr(rs, rt); |
| break; |
| case NM_WRPGPR: |
| check_cp0_enabled(ctx); |
| gen_store_srsgpr(rs, rt); |
| break; |
| case NM_WAIT: |
| gen_cp0(env, ctx, OPC_WAIT, 0, 0); |
| break; |
| case NM_DERET: |
| gen_cp0(env, ctx, OPC_DERET, 0, 0); |
| break; |
| case NM_ERETX: |
| gen_cp0(env, ctx, OPC_ERET, 0, 0); |
| break; |
| #endif |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| break; |
| case NM_POOL32AXF_7: |
| { |
| int32_t op1 = extract32(ctx->opcode, 9, 3); |
| gen_pool32axf_7_nanomips_insn(ctx, op1, rt, rs, rd); |
| } |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| } |
| |
| /* Immediate Value Compact Branches */ |
| static void gen_compute_imm_branch(DisasContext *ctx, uint32_t opc, |
| int rt, int32_t imm, int32_t offset) |
| { |
| TCGCond cond = TCG_COND_ALWAYS; |
| TCGv t0 = tcg_temp_new(); |
| TCGv t1 = tcg_temp_new(); |
| |
| gen_load_gpr(t0, rt); |
| tcg_gen_movi_tl(t1, imm); |
| ctx->btarget = addr_add(ctx, ctx->base.pc_next + 4, offset); |
| |
| /* Load needed operands and calculate btarget */ |
| switch (opc) { |
| case NM_BEQIC: |
| if (rt == 0 && imm == 0) { |
| /* Unconditional branch */ |
| } else if (rt == 0 && imm != 0) { |
| /* Treat as NOP */ |
| goto out; |
| } else { |
| cond = TCG_COND_EQ; |
| } |
| break; |
| case NM_BBEQZC: |
| case NM_BBNEZC: |
| check_nms(ctx); |
| if (imm >= 32 && !(ctx->hflags & MIPS_HFLAG_64)) { |
| gen_reserved_instruction(ctx); |
| goto out; |
| } else if (rt == 0 && opc == NM_BBEQZC) { |
| /* Unconditional branch */ |
| } else if (rt == 0 && opc == NM_BBNEZC) { |
| /* Treat as NOP */ |
| goto out; |
| } else { |
| tcg_gen_shri_tl(t0, t0, imm); |
| tcg_gen_andi_tl(t0, t0, 1); |
| tcg_gen_movi_tl(t1, 0); |
| if (opc == NM_BBEQZC) { |
| cond = TCG_COND_EQ; |
| } else { |
| cond = TCG_COND_NE; |
| } |
| } |
| break; |
| case NM_BNEIC: |
| if (rt == 0 && imm == 0) { |
| /* Treat as NOP */ |
| goto out; |
| } else if (rt == 0 && imm != 0) { |
| /* Unconditional branch */ |
| } else { |
| cond = TCG_COND_NE; |
| } |
| break; |
| case NM_BGEIC: |
| if (rt == 0 && imm == 0) { |
| /* Unconditional branch */ |
| } else { |
| cond = TCG_COND_GE; |
| } |
| break; |
| case NM_BLTIC: |
| cond = TCG_COND_LT; |
| break; |
| case NM_BGEIUC: |
| if (rt == 0 && imm == 0) { |
| /* Unconditional branch */ |
| } else { |
| cond = TCG_COND_GEU; |
| } |
| break; |
| case NM_BLTIUC: |
| cond = TCG_COND_LTU; |
| break; |
| default: |
| MIPS_INVAL("Immediate Value Compact branch"); |
| gen_reserved_instruction(ctx); |
| goto out; |
| } |
| |
| /* branch completion */ |
| clear_branch_hflags(ctx); |
| ctx->base.is_jmp = DISAS_NORETURN; |
| |
| if (cond == TCG_COND_ALWAYS) { |
| /* Uncoditional compact branch */ |
| gen_goto_tb(ctx, 0, ctx->btarget); |
| } else { |
| /* Conditional compact branch */ |
| TCGLabel *fs = gen_new_label(); |
| |
| tcg_gen_brcond_tl(tcg_invert_cond(cond), t0, t1, fs); |
| |
| gen_goto_tb(ctx, 1, ctx->btarget); |
| gen_set_label(fs); |
| |
| gen_goto_tb(ctx, 0, ctx->base.pc_next + 4); |
| } |
| |
| out: |
| tcg_temp_free(t0); |
| tcg_temp_free(t1); |
| } |
| |
| /* P.BALRSC type nanoMIPS R6 branches: BALRSC and BRSC */ |
| static void gen_compute_nanomips_pbalrsc_branch(DisasContext *ctx, int rs, |
| int rt) |
| { |
| TCGv t0 = tcg_temp_new(); |
| TCGv t1 = tcg_temp_new(); |
| |
| /* load rs */ |
| gen_load_gpr(t0, rs); |
| |
| /* link */ |
| if (rt != 0) { |
| tcg_gen_movi_tl(cpu_gpr[rt], ctx->base.pc_next + 4); |
| } |
| |
| /* calculate btarget */ |
| tcg_gen_shli_tl(t0, t0, 1); |
| tcg_gen_movi_tl(t1, ctx->base.pc_next + 4); |
| gen_op_addr_add(ctx, btarget, t1, t0); |
| |
| /* branch completion */ |
| clear_branch_hflags(ctx); |
| ctx->base.is_jmp = DISAS_NORETURN; |
| |
| /* unconditional branch to register */ |
| tcg_gen_mov_tl(cpu_PC, btarget); |
| tcg_gen_lookup_and_goto_ptr(); |
| |
| tcg_temp_free(t0); |
| tcg_temp_free(t1); |
| } |
| |
| /* nanoMIPS Branches */ |
| static void gen_compute_compact_branch_nm(DisasContext *ctx, uint32_t opc, |
| int rs, int rt, int32_t offset) |
| { |
| int bcond_compute = 0; |
| TCGv t0 = tcg_temp_new(); |
| TCGv t1 = tcg_temp_new(); |
| |
| /* Load needed operands and calculate btarget */ |
| switch (opc) { |
| /* compact branch */ |
| case OPC_BGEC: |
| case OPC_BLTC: |
| gen_load_gpr(t0, rs); |
| gen_load_gpr(t1, rt); |
| bcond_compute = 1; |
| ctx->btarget = addr_add(ctx, ctx->base.pc_next + 4, offset); |
| break; |
| case OPC_BGEUC: |
| case OPC_BLTUC: |
| if (rs == 0 || rs == rt) { |
| /* OPC_BLEZALC, OPC_BGEZALC */ |
| /* OPC_BGTZALC, OPC_BLTZALC */ |
| tcg_gen_movi_tl(cpu_gpr[31], ctx->base.pc_next + 4); |
| } |
| gen_load_gpr(t0, rs); |
| gen_load_gpr(t1, rt); |
| bcond_compute = 1; |
| ctx->btarget = addr_add(ctx, ctx->base.pc_next + 4, offset); |
| break; |
| case OPC_BC: |
| ctx->btarget = addr_add(ctx, ctx->base.pc_next + 4, offset); |
| break; |
| case OPC_BEQZC: |
| if (rs != 0) { |
| /* OPC_BEQZC, OPC_BNEZC */ |
| gen_load_gpr(t0, rs); |
| bcond_compute = 1; |
| ctx->btarget = addr_add(ctx, ctx->base.pc_next + 4, offset); |
| } else { |
| /* OPC_JIC, OPC_JIALC */ |
| TCGv tbase = tcg_temp_new(); |
| TCGv toffset = tcg_temp_new(); |
| |
| gen_load_gpr(tbase, rt); |
| tcg_gen_movi_tl(toffset, offset); |
| gen_op_addr_add(ctx, btarget, tbase, toffset); |
| tcg_temp_free(tbase); |
| tcg_temp_free(toffset); |
| } |
| break; |
| default: |
| MIPS_INVAL("Compact branch/jump"); |
| gen_reserved_instruction(ctx); |
| goto out; |
| } |
| |
| if (bcond_compute == 0) { |
| /* Uncoditional compact branch */ |
| switch (opc) { |
| case OPC_BC: |
| gen_goto_tb(ctx, 0, ctx->btarget); |
| break; |
| default: |
| MIPS_INVAL("Compact branch/jump"); |
| gen_reserved_instruction(ctx); |
| goto out; |
| } |
| } else { |
| /* Conditional compact branch */ |
| TCGLabel *fs = gen_new_label(); |
| |
| switch (opc) { |
| case OPC_BGEUC: |
| if (rs == 0 && rt != 0) { |
| /* OPC_BLEZALC */ |
| tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_LE), t1, 0, fs); |
| } else if (rs != 0 && rt != 0 && rs == rt) { |
| /* OPC_BGEZALC */ |
| tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_GE), t1, 0, fs); |
| } else { |
| /* OPC_BGEUC */ |
| tcg_gen_brcond_tl(tcg_invert_cond(TCG_COND_GEU), t0, t1, fs); |
| } |
| break; |
| case OPC_BLTUC: |
| if (rs == 0 && rt != 0) { |
| /* OPC_BGTZALC */ |
| tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_GT), t1, 0, fs); |
| } else if (rs != 0 && rt != 0 && rs == rt) { |
| /* OPC_BLTZALC */ |
| tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_LT), t1, 0, fs); |
| } else { |
| /* OPC_BLTUC */ |
| tcg_gen_brcond_tl(tcg_invert_cond(TCG_COND_LTU), t0, t1, fs); |
| } |
| break; |
| case OPC_BGEC: |
| if (rs == 0 && rt != 0) { |
| /* OPC_BLEZC */ |
| tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_LE), t1, 0, fs); |
| } else if (rs != 0 && rt != 0 && rs == rt) { |
| /* OPC_BGEZC */ |
| tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_GE), t1, 0, fs); |
| } else { |
| /* OPC_BGEC */ |
| tcg_gen_brcond_tl(tcg_invert_cond(TCG_COND_GE), t0, t1, fs); |
| } |
| break; |
| case OPC_BLTC: |
| if (rs == 0 && rt != 0) { |
| /* OPC_BGTZC */ |
| tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_GT), t1, 0, fs); |
| } else if (rs != 0 && rt != 0 && rs == rt) { |
| /* OPC_BLTZC */ |
| tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_LT), t1, 0, fs); |
| } else { |
| /* OPC_BLTC */ |
| tcg_gen_brcond_tl(tcg_invert_cond(TCG_COND_LT), t0, t1, fs); |
| } |
| break; |
| case OPC_BEQZC: |
| tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_EQ), t0, 0, fs); |
| break; |
| default: |
| MIPS_INVAL("Compact conditional branch/jump"); |
| gen_reserved_instruction(ctx); |
| goto out; |
| } |
| |
| /* branch completion */ |
| clear_branch_hflags(ctx); |
| ctx->base.is_jmp = DISAS_NORETURN; |
| |
| /* Generating branch here as compact branches don't have delay slot */ |
| gen_goto_tb(ctx, 1, ctx->btarget); |
| gen_set_label(fs); |
| |
| gen_goto_tb(ctx, 0, ctx->base.pc_next + 4); |
| } |
| |
| out: |
| tcg_temp_free(t0); |
| tcg_temp_free(t1); |
| } |
| |
| |
| /* nanoMIPS CP1 Branches */ |
| static void gen_compute_branch_cp1_nm(DisasContext *ctx, uint32_t op, |
| int32_t ft, int32_t offset) |
| { |
| target_ulong btarget; |
| TCGv_i64 t0 = tcg_temp_new_i64(); |
| |
| gen_load_fpr64(ctx, t0, ft); |
| tcg_gen_andi_i64(t0, t0, 1); |
| |
| btarget = addr_add(ctx, ctx->base.pc_next + 4, offset); |
| |
| switch (op) { |
| case NM_BC1EQZC: |
| tcg_gen_xori_i64(t0, t0, 1); |
| ctx->hflags |= MIPS_HFLAG_BC; |
| break; |
| case NM_BC1NEZC: |
| /* t0 already set */ |
| ctx->hflags |= MIPS_HFLAG_BC; |
| break; |
| default: |
| MIPS_INVAL("cp1 cond branch"); |
| gen_reserved_instruction(ctx); |
| goto out; |
| } |
| |
| tcg_gen_trunc_i64_tl(bcond, t0); |
| |
| ctx->btarget = btarget; |
| |
| out: |
| tcg_temp_free_i64(t0); |
| } |
| |
| |
| static void gen_p_lsx(DisasContext *ctx, int rd, int rs, int rt) |
| { |
| TCGv t0, t1; |
| t0 = tcg_temp_new(); |
| t1 = tcg_temp_new(); |
| |
| gen_load_gpr(t0, rs); |
| gen_load_gpr(t1, rt); |
| |
| if ((extract32(ctx->opcode, 6, 1)) == 1) { |
| /* PP.LSXS instructions require shifting */ |
| switch (extract32(ctx->opcode, 7, 4)) { |
| case NM_SHXS: |
| check_nms(ctx); |
| /* fall through */ |
| case NM_LHXS: |
| case NM_LHUXS: |
| tcg_gen_shli_tl(t0, t0, 1); |
| break; |
| case NM_SWXS: |
| check_nms(ctx); |
| /* fall through */ |
| case NM_LWXS: |
| case NM_LWC1XS: |
| case NM_SWC1XS: |
| tcg_gen_shli_tl(t0, t0, 2); |
| break; |
| case NM_LDC1XS: |
| case NM_SDC1XS: |
| tcg_gen_shli_tl(t0, t0, 3); |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| goto out; |
| } |
| } |
| gen_op_addr_add(ctx, t0, t0, t1); |
| |
| switch (extract32(ctx->opcode, 7, 4)) { |
| case NM_LBX: |
| tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, |
| MO_SB); |
| gen_store_gpr(t0, rd); |
| break; |
| case NM_LHX: |
| /*case NM_LHXS:*/ |
| tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, |
| MO_TESW); |
| gen_store_gpr(t0, rd); |
| break; |
| case NM_LWX: |
| /*case NM_LWXS:*/ |
| tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, |
| MO_TESL); |
| gen_store_gpr(t0, rd); |
| break; |
| case NM_LBUX: |
| tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, |
| MO_UB); |
| gen_store_gpr(t0, rd); |
| break; |
| case NM_LHUX: |
| /*case NM_LHUXS:*/ |
| tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, |
| MO_TEUW); |
| gen_store_gpr(t0, rd); |
| break; |
| case NM_SBX: |
| check_nms(ctx); |
| gen_load_gpr(t1, rd); |
| tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, |
| MO_8); |
| break; |
| case NM_SHX: |
| /*case NM_SHXS:*/ |
| check_nms(ctx); |
| gen_load_gpr(t1, rd); |
| tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, |
| MO_TEUW); |
| break; |
| case NM_SWX: |
| /*case NM_SWXS:*/ |
| check_nms(ctx); |
| gen_load_gpr(t1, rd); |
| tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, |
| MO_TEUL); |
| break; |
| case NM_LWC1X: |
| /*case NM_LWC1XS:*/ |
| case NM_LDC1X: |
| /*case NM_LDC1XS:*/ |
| case NM_SWC1X: |
| /*case NM_SWC1XS:*/ |
| case NM_SDC1X: |
| /*case NM_SDC1XS:*/ |
| if (ctx->CP0_Config1 & (1 << CP0C1_FP)) { |
| check_cp1_enabled(ctx); |
| switch (extract32(ctx->opcode, 7, 4)) { |
| case NM_LWC1X: |
| /*case NM_LWC1XS:*/ |
| gen_flt_ldst(ctx, OPC_LWC1, rd, t0); |
| break; |
| case NM_LDC1X: |
| /*case NM_LDC1XS:*/ |
| gen_flt_ldst(ctx, OPC_LDC1, rd, t0); |
| break; |
| case NM_SWC1X: |
| /*case NM_SWC1XS:*/ |
| gen_flt_ldst(ctx, OPC_SWC1, rd, t0); |
| break; |
| case NM_SDC1X: |
| /*case NM_SDC1XS:*/ |
| gen_flt_ldst(ctx, OPC_SDC1, rd, t0); |
| break; |
| } |
| } else { |
| generate_exception_err(ctx, EXCP_CpU, 1); |
| } |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| |
| out: |
| tcg_temp_free(t0); |
| tcg_temp_free(t1); |
| } |
| |
| static void gen_pool32f_nanomips_insn(DisasContext *ctx) |
| { |
| int rt, rs, rd; |
| |
| rt = extract32(ctx->opcode, 21, 5); |
| rs = extract32(ctx->opcode, 16, 5); |
| rd = extract32(ctx->opcode, 11, 5); |
| |
| if (!(ctx->CP0_Config1 & (1 << CP0C1_FP))) { |
| gen_reserved_instruction(ctx); |
| return; |
| } |
| check_cp1_enabled(ctx); |
| switch (extract32(ctx->opcode, 0, 3)) { |
| case NM_POOL32F_0: |
| switch (extract32(ctx->opcode, 3, 7)) { |
| case NM_RINT_S: |
| gen_farith(ctx, OPC_RINT_S, 0, rt, rs, 0); |
| break; |
| case NM_RINT_D: |
| gen_farith(ctx, OPC_RINT_D, 0, rt, rs, 0); |
| break; |
| case NM_CLASS_S: |
| gen_farith(ctx, OPC_CLASS_S, 0, rt, rs, 0); |
| break; |
| case NM_CLASS_D: |
| gen_farith(ctx, OPC_CLASS_D, 0, rt, rs, 0); |
| break; |
| case NM_ADD_S: |
| gen_farith(ctx, OPC_ADD_S, rt, rs, rd, 0); |
| break; |
| case NM_ADD_D: |
| gen_farith(ctx, OPC_ADD_D, rt, rs, rd, 0); |
| break; |
| case NM_SUB_S: |
| gen_farith(ctx, OPC_SUB_S, rt, rs, rd, 0); |
| break; |
| case NM_SUB_D: |
| gen_farith(ctx, OPC_SUB_D, rt, rs, rd, 0); |
| break; |
| case NM_MUL_S: |
| gen_farith(ctx, OPC_MUL_S, rt, rs, rd, 0); |
| break; |
| case NM_MUL_D: |
| gen_farith(ctx, OPC_MUL_D, rt, rs, rd, 0); |
| break; |
| case NM_DIV_S: |
| gen_farith(ctx, OPC_DIV_S, rt, rs, rd, 0); |
| break; |
| case NM_DIV_D: |
| gen_farith(ctx, OPC_DIV_D, rt, rs, rd, 0); |
| break; |
| case NM_SELEQZ_S: |
| gen_sel_s(ctx, OPC_SELEQZ_S, rd, rt, rs); |
| break; |
| case NM_SELEQZ_D: |
| gen_sel_d(ctx, OPC_SELEQZ_D, rd, rt, rs); |
| break; |
| case NM_SELNEZ_S: |
| gen_sel_s(ctx, OPC_SELNEZ_S, rd, rt, rs); |
| break; |
| case NM_SELNEZ_D: |
| gen_sel_d(ctx, OPC_SELNEZ_D, rd, rt, rs); |
| break; |
| case NM_SEL_S: |
| gen_sel_s(ctx, OPC_SEL_S, rd, rt, rs); |
| break; |
| case NM_SEL_D: |
| gen_sel_d(ctx, OPC_SEL_D, rd, rt, rs); |
| break; |
| case NM_MADDF_S: |
| gen_farith(ctx, OPC_MADDF_S, rt, rs, rd, 0); |
| break; |
| case NM_MADDF_D: |
| gen_farith(ctx, OPC_MADDF_D, rt, rs, rd, 0); |
| break; |
| case NM_MSUBF_S: |
| gen_farith(ctx, OPC_MSUBF_S, rt, rs, rd, 0); |
| break; |
| case NM_MSUBF_D: |
| gen_farith(ctx, OPC_MSUBF_D, rt, rs, rd, 0); |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| break; |
| case NM_POOL32F_3: |
| switch (extract32(ctx->opcode, 3, 3)) { |
| case NM_MIN_FMT: |
| switch (extract32(ctx->opcode, 9, 1)) { |
| 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; |
| } |
| break; |
| case NM_MAX_FMT: |
| switch (extract32(ctx->opcode, 9, 1)) { |
| 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; |
| } |
| break; |
| case NM_MINA_FMT: |
| switch (extract32(ctx->opcode, 9, 1)) { |
| 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; |
| } |
| break; |
| case NM_MAXA_FMT: |
| switch (extract32(ctx->opcode, 9, 1)) { |
| 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; |
| } |
| break; |
| case NM_POOL32FXF: |
| switch (extract32(ctx->opcode, 6, 8)) { |
| case NM_CFC1: |
| gen_cp1(ctx, OPC_CFC1, rt, rs); |
| break; |
| case NM_CTC1: |
| gen_cp1(ctx, OPC_CTC1, rt, rs); |
| break; |
| case NM_MFC1: |
| gen_cp1(ctx, OPC_MFC1, rt, rs); |
| break; |
| case NM_MTC1: |
| gen_cp1(ctx, OPC_MTC1, rt, rs); |
| break; |
| case NM_MFHC1: |
| gen_cp1(ctx, OPC_MFHC1, rt, rs); |
| break; |
| case NM_MTHC1: |
| gen_cp1(ctx, OPC_MTHC1, rt, rs); |
| break; |
| case NM_CVT_S_PL: |
| gen_farith(ctx, OPC_CVT_S_PL, -1, rs, rt, 0); |
| break; |
| case NM_CVT_S_PU: |
| gen_farith(ctx, OPC_CVT_S_PU, -1, rs, rt, 0); |
| break; |
| default: |
| switch (extract32(ctx->opcode, 6, 9)) { |
| case NM_CVT_L_S: |
| gen_farith(ctx, OPC_CVT_L_S, -1, rs, rt, 0); |
| break; |
| case NM_CVT_L_D: |
| gen_farith(ctx, OPC_CVT_L_D, -1, rs, rt, 0); |
| break; |
| case NM_CVT_W_S: |
| gen_farith(ctx, OPC_CVT_W_S, -1, rs, rt, 0); |
| break; |
| case NM_CVT_W_D: |
| gen_farith(ctx, OPC_CVT_W_D, -1, rs, rt, 0); |
| break; |
| case NM_RSQRT_S: |
| gen_farith(ctx, OPC_RSQRT_S, -1, rs, rt, 0); |
| break; |
| case NM_RSQRT_D: |
| gen_farith(ctx, OPC_RSQRT_D, -1, rs, rt, 0); |
| break; |
| case NM_SQRT_S: |
| gen_farith(ctx, OPC_SQRT_S, -1, rs, rt, 0); |
| break; |
| case NM_SQRT_D: |
| gen_farith(ctx, OPC_SQRT_D, -1, rs, rt, 0); |
| break; |
| case NM_RECIP_S: |
| gen_farith(ctx, OPC_RECIP_S, -1, rs, rt, 0); |
| break; |
| case NM_RECIP_D: |
| gen_farith(ctx, OPC_RECIP_D, -1, rs, rt, 0); |
| break; |
| case NM_FLOOR_L_S: |
| gen_farith(ctx, OPC_FLOOR_L_S, -1, rs, rt, 0); |
| break; |
| case NM_FLOOR_L_D: |
| gen_farith(ctx, OPC_FLOOR_L_D, -1, rs, rt, 0); |
| break; |
| case NM_FLOOR_W_S: |
| gen_farith(ctx, OPC_FLOOR_W_S, -1, rs, rt, 0); |
| break; |
| case NM_FLOOR_W_D: |
| gen_farith(ctx, OPC_FLOOR_W_D, -1, rs, rt, 0); |
| break; |
| case NM_CEIL_L_S: |
| gen_farith(ctx, OPC_CEIL_L_S, -1, rs, rt, 0); |
| break; |
| case NM_CEIL_L_D: |
| gen_farith(ctx, OPC_CEIL_L_D, -1, rs, rt, 0); |
| break; |
| case NM_CEIL_W_S: |
| gen_farith(ctx, OPC_CEIL_W_S, -1, rs, rt, 0); |
| break; |
| case NM_CEIL_W_D: |
| gen_farith(ctx, OPC_CEIL_W_D, -1, rs, rt, 0); |
| break; |
| case NM_TRUNC_L_S: |
| gen_farith(ctx, OPC_TRUNC_L_S, -1, rs, rt, 0); |
| break; |
| case NM_TRUNC_L_D: |
| gen_farith(ctx, OPC_TRUNC_L_D, -1, rs, rt, 0); |
| break; |
| case NM_TRUNC_W_S: |
| gen_farith(ctx, OPC_TRUNC_W_S, -1, rs, rt, 0); |
| break; |
| case NM_TRUNC_W_D: |
| gen_farith(ctx, OPC_TRUNC_W_D, -1, rs, rt, 0); |
| break; |
| case NM_ROUND_L_S: |
| gen_farith(ctx, OPC_ROUND_L_S, -1, rs, rt, 0); |
| break; |
| case NM_ROUND_L_D: |
| gen_farith(ctx, OPC_ROUND_L_D, -1, rs, rt, 0); |
| break; |
| case NM_ROUND_W_S: |
| gen_farith(ctx, OPC_ROUND_W_S, -1, rs, rt, 0); |
| break; |
| case NM_ROUND_W_D: |
| gen_farith(ctx, OPC_ROUND_W_D, -1, rs, rt, 0); |
| break; |
| case NM_MOV_S: |
| gen_farith(ctx, OPC_MOV_S, -1, rs, rt, 0); |
| break; |
| case NM_MOV_D: |
| gen_farith(ctx, OPC_MOV_D, -1, rs, rt, 0); |
| break; |
| case NM_ABS_S: |
| gen_farith(ctx, OPC_ABS_S, -1, rs, rt, 0); |
| break; |
| case NM_ABS_D: |
| gen_farith(ctx, OPC_ABS_D, -1, rs, rt, 0); |
| break; |
| case NM_NEG_S: |
| gen_farith(ctx, OPC_NEG_S, -1, rs, rt, 0); |
| break; |
| case NM_NEG_D: |
| gen_farith(ctx, OPC_NEG_D, -1, rs, rt, 0); |
| break; |
| case NM_CVT_D_S: |
| gen_farith(ctx, OPC_CVT_D_S, -1, rs, rt, 0); |
| break; |
| case NM_CVT_D_W: |
| gen_farith(ctx, OPC_CVT_D_W, -1, rs, rt, 0); |
| break; |
| case NM_CVT_D_L: |
| gen_farith(ctx, OPC_CVT_D_L, -1, rs, rt, 0); |
| break; |
| case NM_CVT_S_D: |
| gen_farith(ctx, OPC_CVT_S_D, -1, rs, rt, 0); |
| break; |
| case NM_CVT_S_W: |
| gen_farith(ctx, OPC_CVT_S_W, -1, rs, rt, 0); |
| break; |
| case NM_CVT_S_L: |
| gen_farith(ctx, OPC_CVT_S_L, -1, rs, rt, 0); |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| break; |
| } |
| break; |
| } |
| break; |
| case NM_POOL32F_5: |
| switch (extract32(ctx->opcode, 3, 3)) { |
| case NM_CMP_CONDN_S: |
| gen_r6_cmp_s(ctx, extract32(ctx->opcode, 6, 5), rt, rs, rd); |
| break; |
| case NM_CMP_CONDN_D: |
| gen_r6_cmp_d(ctx, extract32(ctx->opcode, 6, 5), rt, rs, rd); |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| } |
| |
| static void gen_pool32a5_nanomips_insn(DisasContext *ctx, int opc, |
| int rd, int rs, int rt) |
| { |
| int ret = rd; |
| TCGv t0 = tcg_temp_new(); |
| TCGv v1_t = tcg_temp_new(); |
| TCGv v2_t = tcg_temp_new(); |
| |
| gen_load_gpr(v1_t, rs); |
| gen_load_gpr(v2_t, rt); |
| |
| switch (opc) { |
| case NM_CMP_EQ_PH: |
| check_dsp(ctx); |
| gen_helper_cmp_eq_ph(v1_t, v2_t, cpu_env); |
| break; |
| case NM_CMP_LT_PH: |
| check_dsp(ctx); |
| gen_helper_cmp_lt_ph(v1_t, v2_t, cpu_env); |
| break; |
| case NM_CMP_LE_PH: |
| check_dsp(ctx); |
| gen_helper_cmp_le_ph(v1_t, v2_t, cpu_env); |
| break; |
| case NM_CMPU_EQ_QB: |
| check_dsp(ctx); |
| gen_helper_cmpu_eq_qb(v1_t, v2_t, cpu_env); |
| break; |
| case NM_CMPU_LT_QB: |
| check_dsp(ctx); |
| gen_helper_cmpu_lt_qb(v1_t, v2_t, cpu_env); |
| break; |
| case NM_CMPU_LE_QB: |
| check_dsp(ctx); |
| gen_helper_cmpu_le_qb(v1_t, v2_t, cpu_env); |
| break; |
| case NM_CMPGU_EQ_QB: |
| check_dsp(ctx); |
| gen_helper_cmpgu_eq_qb(v1_t, v1_t, v2_t); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case NM_CMPGU_LT_QB: |
| check_dsp(ctx); |
| gen_helper_cmpgu_lt_qb(v1_t, v1_t, v2_t); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case NM_CMPGU_LE_QB: |
| check_dsp(ctx); |
| gen_helper_cmpgu_le_qb(v1_t, v1_t, v2_t); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case NM_CMPGDU_EQ_QB: |
| check_dsp_r2(ctx); |
| gen_helper_cmpgu_eq_qb(v1_t, v1_t, v2_t); |
| tcg_gen_deposit_tl(cpu_dspctrl, cpu_dspctrl, v1_t, 24, 4); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case NM_CMPGDU_LT_QB: |
| check_dsp_r2(ctx); |
| gen_helper_cmpgu_lt_qb(v1_t, v1_t, v2_t); |
| tcg_gen_deposit_tl(cpu_dspctrl, cpu_dspctrl, v1_t, 24, 4); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case NM_CMPGDU_LE_QB: |
| check_dsp_r2(ctx); |
| gen_helper_cmpgu_le_qb(v1_t, v1_t, v2_t); |
| tcg_gen_deposit_tl(cpu_dspctrl, cpu_dspctrl, v1_t, 24, 4); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case NM_PACKRL_PH: |
| check_dsp(ctx); |
| gen_helper_packrl_ph(v1_t, v1_t, v2_t); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case NM_PICK_QB: |
| check_dsp(ctx); |
| gen_helper_pick_qb(v1_t, v1_t, v2_t, cpu_env); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case NM_PICK_PH: |
| check_dsp(ctx); |
| gen_helper_pick_ph(v1_t, v1_t, v2_t, cpu_env); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case NM_ADDQ_S_W: |
| check_dsp(ctx); |
| gen_helper_addq_s_w(v1_t, v1_t, v2_t, cpu_env); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case NM_SUBQ_S_W: |
| check_dsp(ctx); |
| gen_helper_subq_s_w(v1_t, v1_t, v2_t, cpu_env); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case NM_ADDSC: |
| check_dsp(ctx); |
| gen_helper_addsc(v1_t, v1_t, v2_t, cpu_env); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case NM_ADDWC: |
| check_dsp(ctx); |
| gen_helper_addwc(v1_t, v1_t, v2_t, cpu_env); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case NM_ADDQ_S_PH: |
| check_dsp(ctx); |
| switch (extract32(ctx->opcode, 10, 1)) { |
| case 0: |
| /* ADDQ_PH */ |
| gen_helper_addq_ph(v1_t, v1_t, v2_t, cpu_env); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case 1: |
| /* ADDQ_S_PH */ |
| gen_helper_addq_s_ph(v1_t, v1_t, v2_t, cpu_env); |
| gen_store_gpr(v1_t, ret); |
| break; |
| } |
| break; |
| case NM_ADDQH_R_PH: |
| check_dsp_r2(ctx); |
| switch (extract32(ctx->opcode, 10, 1)) { |
| case 0: |
| /* ADDQH_PH */ |
| gen_helper_addqh_ph(v1_t, v1_t, v2_t); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case 1: |
| /* ADDQH_R_PH */ |
| gen_helper_addqh_r_ph(v1_t, v1_t, v2_t); |
| gen_store_gpr(v1_t, ret); |
| break; |
| } |
| break; |
| case NM_ADDQH_R_W: |
| check_dsp_r2(ctx); |
| switch (extract32(ctx->opcode, 10, 1)) { |
| case 0: |
| /* ADDQH_W */ |
| gen_helper_addqh_w(v1_t, v1_t, v2_t); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case 1: |
| /* ADDQH_R_W */ |
| gen_helper_addqh_r_w(v1_t, v1_t, v2_t); |
| gen_store_gpr(v1_t, ret); |
| break; |
| } |
| break; |
| case NM_ADDU_S_QB: |
| check_dsp(ctx); |
| switch (extract32(ctx->opcode, 10, 1)) { |
| case 0: |
| /* ADDU_QB */ |
| gen_helper_addu_qb(v1_t, v1_t, v2_t, cpu_env); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case 1: |
| /* ADDU_S_QB */ |
| gen_helper_addu_s_qb(v1_t, v1_t, v2_t, cpu_env); |
| gen_store_gpr(v1_t, ret); |
| break; |
| } |
| break; |
| case NM_ADDU_S_PH: |
| check_dsp_r2(ctx); |
| switch (extract32(ctx->opcode, 10, 1)) { |
| case 0: |
| /* ADDU_PH */ |
| gen_helper_addu_ph(v1_t, v1_t, v2_t, cpu_env); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case 1: |
| /* ADDU_S_PH */ |
| gen_helper_addu_s_ph(v1_t, v1_t, v2_t, cpu_env); |
| gen_store_gpr(v1_t, ret); |
| break; |
| } |
| break; |
| case NM_ADDUH_R_QB: |
| check_dsp_r2(ctx); |
| switch (extract32(ctx->opcode, 10, 1)) { |
| case 0: |
| /* ADDUH_QB */ |
| gen_helper_adduh_qb(v1_t, v1_t, v2_t); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case 1: |
| /* ADDUH_R_QB */ |
| gen_helper_adduh_r_qb(v1_t, v1_t, v2_t); |
| gen_store_gpr(v1_t, ret); |
| break; |
| } |
| break; |
| case NM_SHRAV_R_PH: |
| check_dsp(ctx); |
| switch (extract32(ctx->opcode, 10, 1)) { |
| case 0: |
| /* SHRAV_PH */ |
| gen_helper_shra_ph(v1_t, v1_t, v2_t); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case 1: |
| /* SHRAV_R_PH */ |
| gen_helper_shra_r_ph(v1_t, v1_t, v2_t); |
| gen_store_gpr(v1_t, ret); |
| break; |
| } |
| break; |
| case NM_SHRAV_R_QB: |
| check_dsp_r2(ctx); |
| switch (extract32(ctx->opcode, 10, 1)) { |
| case 0: |
| /* SHRAV_QB */ |
| gen_helper_shra_qb(v1_t, v1_t, v2_t); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case 1: |
| /* SHRAV_R_QB */ |
| gen_helper_shra_r_qb(v1_t, v1_t, v2_t); |
| gen_store_gpr(v1_t, ret); |
| break; |
| } |
| break; |
| case NM_SUBQ_S_PH: |
| check_dsp(ctx); |
| switch (extract32(ctx->opcode, 10, 1)) { |
| case 0: |
| /* SUBQ_PH */ |
| gen_helper_subq_ph(v1_t, v1_t, v2_t, cpu_env); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case 1: |
| /* SUBQ_S_PH */ |
| gen_helper_subq_s_ph(v1_t, v1_t, v2_t, cpu_env); |
| gen_store_gpr(v1_t, ret); |
| break; |
| } |
| break; |
| case NM_SUBQH_R_PH: |
| check_dsp_r2(ctx); |
| switch (extract32(ctx->opcode, 10, 1)) { |
| case 0: |
| /* SUBQH_PH */ |
| gen_helper_subqh_ph(v1_t, v1_t, v2_t); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case 1: |
| /* SUBQH_R_PH */ |
| gen_helper_subqh_r_ph(v1_t, v1_t, v2_t); |
| gen_store_gpr(v1_t, ret); |
| break; |
| } |
| break; |
| case NM_SUBQH_R_W: |
| check_dsp_r2(ctx); |
| switch (extract32(ctx->opcode, 10, 1)) { |
| case 0: |
| /* SUBQH_W */ |
| gen_helper_subqh_w(v1_t, v1_t, v2_t); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case 1: |
| /* SUBQH_R_W */ |
| gen_helper_subqh_r_w(v1_t, v1_t, v2_t); |
| gen_store_gpr(v1_t, ret); |
| break; |
| } |
| break; |
| case NM_SUBU_S_QB: |
| check_dsp(ctx); |
| switch (extract32(ctx->opcode, 10, 1)) { |
| case 0: |
| /* SUBU_QB */ |
| gen_helper_subu_qb(v1_t, v1_t, v2_t, cpu_env); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case 1: |
| /* SUBU_S_QB */ |
| gen_helper_subu_s_qb(v1_t, v1_t, v2_t, cpu_env); |
| gen_store_gpr(v1_t, ret); |
| break; |
| } |
| break; |
| case NM_SUBU_S_PH: |
| check_dsp_r2(ctx); |
| switch (extract32(ctx->opcode, 10, 1)) { |
| case 0: |
| /* SUBU_PH */ |
| gen_helper_subu_ph(v1_t, v1_t, v2_t, cpu_env); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case 1: |
| /* SUBU_S_PH */ |
| gen_helper_subu_s_ph(v1_t, v1_t, v2_t, cpu_env); |
| gen_store_gpr(v1_t, ret); |
| break; |
| } |
| break; |
| case NM_SUBUH_R_QB: |
| check_dsp_r2(ctx); |
| switch (extract32(ctx->opcode, 10, 1)) { |
| case 0: |
| /* SUBUH_QB */ |
| gen_helper_subuh_qb(v1_t, v1_t, v2_t); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case 1: |
| /* SUBUH_R_QB */ |
| gen_helper_subuh_r_qb(v1_t, v1_t, v2_t); |
| gen_store_gpr(v1_t, ret); |
| break; |
| } |
| break; |
| case NM_SHLLV_S_PH: |
| check_dsp(ctx); |
| switch (extract32(ctx->opcode, 10, 1)) { |
| case 0: |
| /* SHLLV_PH */ |
| gen_helper_shll_ph(v1_t, v1_t, v2_t, cpu_env); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case 1: |
| /* SHLLV_S_PH */ |
| gen_helper_shll_s_ph(v1_t, v1_t, v2_t, cpu_env); |
| gen_store_gpr(v1_t, ret); |
| break; |
| } |
| break; |
| case NM_PRECR_SRA_R_PH_W: |
| check_dsp_r2(ctx); |
| switch (extract32(ctx->opcode, 10, 1)) { |
| case 0: |
| /* PRECR_SRA_PH_W */ |
| { |
| TCGv_i32 sa_t = tcg_const_i32(rd); |
| gen_helper_precr_sra_ph_w(v1_t, sa_t, v1_t, |
| cpu_gpr[rt]); |
| gen_store_gpr(v1_t, rt); |
| tcg_temp_free_i32(sa_t); |
| } |
| break; |
| case 1: |
| /* PRECR_SRA_R_PH_W */ |
| { |
| TCGv_i32 sa_t = tcg_const_i32(rd); |
| gen_helper_precr_sra_r_ph_w(v1_t, sa_t, v1_t, |
| cpu_gpr[rt]); |
| gen_store_gpr(v1_t, rt); |
| tcg_temp_free_i32(sa_t); |
| } |
| break; |
| } |
| break; |
| case NM_MULEU_S_PH_QBL: |
| check_dsp(ctx); |
| gen_helper_muleu_s_ph_qbl(v1_t, v1_t, v2_t, cpu_env); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case NM_MULEU_S_PH_QBR: |
| check_dsp(ctx); |
| gen_helper_muleu_s_ph_qbr(v1_t, v1_t, v2_t, cpu_env); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case NM_MULQ_RS_PH: |
| check_dsp(ctx); |
| gen_helper_mulq_rs_ph(v1_t, v1_t, v2_t, cpu_env); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case NM_MULQ_S_PH: |
| check_dsp_r2(ctx); |
| gen_helper_mulq_s_ph(v1_t, v1_t, v2_t, cpu_env); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case NM_MULQ_RS_W: |
| check_dsp_r2(ctx); |
| gen_helper_mulq_rs_w(v1_t, v1_t, v2_t, cpu_env); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case NM_MULQ_S_W: |
| check_dsp_r2(ctx); |
| gen_helper_mulq_s_w(v1_t, v1_t, v2_t, cpu_env); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case NM_APPEND: |
| check_dsp_r2(ctx); |
| gen_load_gpr(t0, rs); |
| if (rd != 0) { |
| tcg_gen_deposit_tl(cpu_gpr[rt], t0, cpu_gpr[rt], rd, 32 - rd); |
| } |
| tcg_gen_ext32s_tl(cpu_gpr[rt], cpu_gpr[rt]); |
| break; |
| case NM_MODSUB: |
| check_dsp(ctx); |
| gen_helper_modsub(v1_t, v1_t, v2_t); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case NM_SHRAV_R_W: |
| check_dsp(ctx); |
| gen_helper_shra_r_w(v1_t, v1_t, v2_t); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case NM_SHRLV_PH: |
| check_dsp_r2(ctx); |
| gen_helper_shrl_ph(v1_t, v1_t, v2_t); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case NM_SHRLV_QB: |
| check_dsp(ctx); |
| gen_helper_shrl_qb(v1_t, v1_t, v2_t); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case NM_SHLLV_QB: |
| check_dsp(ctx); |
| gen_helper_shll_qb(v1_t, v1_t, v2_t, cpu_env); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case NM_SHLLV_S_W: |
| check_dsp(ctx); |
| gen_helper_shll_s_w(v1_t, v1_t, v2_t, cpu_env); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case NM_SHILO: |
| check_dsp(ctx); |
| { |
| TCGv tv0 = tcg_temp_new(); |
| TCGv tv1 = tcg_temp_new(); |
| int16_t imm = extract32(ctx->opcode, 16, 7); |
| |
| tcg_gen_movi_tl(tv0, rd >> 3); |
| tcg_gen_movi_tl(tv1, imm); |
| gen_helper_shilo(tv0, tv1, cpu_env); |
| tcg_temp_free(tv1); |
| tcg_temp_free(tv0); |
| } |
| break; |
| case NM_MULEQ_S_W_PHL: |
| check_dsp(ctx); |
| gen_helper_muleq_s_w_phl(v1_t, v1_t, v2_t, cpu_env); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case NM_MULEQ_S_W_PHR: |
| check_dsp(ctx); |
| gen_helper_muleq_s_w_phr(v1_t, v1_t, v2_t, cpu_env); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case NM_MUL_S_PH: |
| check_dsp_r2(ctx); |
| switch (extract32(ctx->opcode, 10, 1)) { |
| case 0: |
| /* MUL_PH */ |
| gen_helper_mul_ph(v1_t, v1_t, v2_t, cpu_env); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case 1: |
| /* MUL_S_PH */ |
| gen_helper_mul_s_ph(v1_t, v1_t, v2_t, cpu_env); |
| gen_store_gpr(v1_t, ret); |
| break; |
| } |
| break; |
| case NM_PRECR_QB_PH: |
| check_dsp_r2(ctx); |
| gen_helper_precr_qb_ph(v1_t, v1_t, v2_t); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case NM_PRECRQ_QB_PH: |
| check_dsp(ctx); |
| gen_helper_precrq_qb_ph(v1_t, v1_t, v2_t); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case NM_PRECRQ_PH_W: |
| check_dsp(ctx); |
| gen_helper_precrq_ph_w(v1_t, v1_t, v2_t); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case NM_PRECRQ_RS_PH_W: |
| check_dsp(ctx); |
| gen_helper_precrq_rs_ph_w(v1_t, v1_t, v2_t, cpu_env); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case NM_PRECRQU_S_QB_PH: |
| check_dsp(ctx); |
| gen_helper_precrqu_s_qb_ph(v1_t, v1_t, v2_t, cpu_env); |
| gen_store_gpr(v1_t, ret); |
| break; |
| case NM_SHRA_R_W: |
| check_dsp(ctx); |
| tcg_gen_movi_tl(t0, rd); |
| gen_helper_shra_r_w(v1_t, t0, v1_t); |
| gen_store_gpr(v1_t, rt); |
| break; |
| case NM_SHRA_R_PH: |
| check_dsp(ctx); |
| tcg_gen_movi_tl(t0, rd >> 1); |
| switch (extract32(ctx->opcode, 10, 1)) { |
| case 0: |
| /* SHRA_PH */ |
| gen_helper_shra_ph(v1_t, t0, v1_t); |
| gen_store_gpr(v1_t, rt); |
| break; |
| case 1: |
| /* SHRA_R_PH */ |
| gen_helper_shra_r_ph(v1_t, t0, v1_t); |
| gen_store_gpr(v1_t, rt); |
| break; |
| } |
| break; |
| case NM_SHLL_S_PH: |
| check_dsp(ctx); |
| tcg_gen_movi_tl(t0, rd >> 1); |
| switch (extract32(ctx->opcode, 10, 2)) { |
| case 0: |
| /* SHLL_PH */ |
| gen_helper_shll_ph(v1_t, t0, v1_t, cpu_env); |
| gen_store_gpr(v1_t, rt); |
| break; |
| case 2: |
| /* SHLL_S_PH */ |
| gen_helper_shll_s_ph(v1_t, t0, v1_t, cpu_env); |
| gen_store_gpr(v1_t, rt); |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| break; |
| case NM_SHLL_S_W: |
| check_dsp(ctx); |
| tcg_gen_movi_tl(t0, rd); |
| gen_helper_shll_s_w(v1_t, t0, v1_t, cpu_env); |
| gen_store_gpr(v1_t, rt); |
| break; |
| case NM_REPL_PH: |
| check_dsp(ctx); |
| { |
| int16_t imm; |
| imm = sextract32(ctx->opcode, 11, 11); |
| imm = (int16_t)(imm << 6) >> 6; |
| if (rt != 0) { |
| tcg_gen_movi_tl(cpu_gpr[rt], dup_const(MO_16, imm)); |
| } |
| } |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| |
| tcg_temp_free(v2_t); |
| tcg_temp_free(v1_t); |
| tcg_temp_free(t0); |
| } |
| |
| static int decode_nanomips_32_48_opc(CPUMIPSState *env, DisasContext *ctx) |
| { |
| uint16_t insn; |
| uint32_t op; |
| int rt, rs, rd; |
| int offset; |
| int imm; |
| |
| insn = translator_lduw(env, &ctx->base, ctx->base.pc_next + 2); |
| ctx->opcode = (ctx->opcode << 16) | insn; |
| |
| rt = extract32(ctx->opcode, 21, 5); |
| rs = extract32(ctx->opcode, 16, 5); |
| rd = extract32(ctx->opcode, 11, 5); |
| |
| op = extract32(ctx->opcode, 26, 6); |
| switch (op) { |
| case NM_P_ADDIU: |
| if (rt == 0) { |
| /* P.RI */ |
| switch (extract32(ctx->opcode, 19, 2)) { |
| case NM_SIGRIE: |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| case NM_P_SYSCALL: |
| if ((extract32(ctx->opcode, 18, 1)) == NM_SYSCALL) { |
| generate_exception_end(ctx, EXCP_SYSCALL); |
| } else { |
| gen_reserved_instruction(ctx); |
| } |
| break; |
| case NM_BREAK: |
| generate_exception_end(ctx, EXCP_BREAK); |
| break; |
| case NM_SDBBP: |
| if (is_uhi(ctx, extract32(ctx->opcode, 0, 19))) { |
| ctx->base.is_jmp = DISAS_SEMIHOST; |
| } else { |
| if (ctx->hflags & MIPS_HFLAG_SBRI) { |
| gen_reserved_instruction(ctx); |
| } else { |
| generate_exception_end(ctx, EXCP_DBp); |
| } |
| } |
| break; |
| } |
| } else { |
| /* NM_ADDIU */ |
| imm = extract32(ctx->opcode, 0, 16); |
| if (rs != 0) { |
| tcg_gen_addi_tl(cpu_gpr[rt], cpu_gpr[rs], imm); |
| } else { |
| tcg_gen_movi_tl(cpu_gpr[rt], imm); |
| } |
| tcg_gen_ext32s_tl(cpu_gpr[rt], cpu_gpr[rt]); |
| } |
| break; |
| case NM_ADDIUPC: |
| if (rt != 0) { |
| offset = sextract32(ctx->opcode, 0, 1) << 21 | |
| extract32(ctx->opcode, 1, 20) << 1; |
| target_long addr = addr_add(ctx, ctx->base.pc_next + 4, offset); |
| tcg_gen_movi_tl(cpu_gpr[rt], addr); |
| } |
| break; |
| case NM_POOL32A: |
| switch (ctx->opcode & 0x07) { |
| case NM_POOL32A0: |
| gen_pool32a0_nanomips_insn(env, ctx); |
| break; |
| case NM_POOL32A5: |
| { |
| int32_t op1 = extract32(ctx->opcode, 3, 7); |
| gen_pool32a5_nanomips_insn(ctx, op1, rd, rs, rt); |
| } |
| break; |
| case NM_POOL32A7: |
| switch (extract32(ctx->opcode, 3, 3)) { |
| case NM_P_LSX: |
| gen_p_lsx(ctx, rd, rs, rt); |
| break; |
| case NM_LSA: |
| /* |
| * In nanoMIPS, the shift field directly encodes the shift |
| * amount, meaning that the supported shift values are in |
| * the range 0 to 3 (instead of 1 to 4 in MIPSR6). |
| */ |
| gen_lsa(ctx, rd, rt, rs, extract32(ctx->opcode, 9, 2) - 1); |
| break; |
| case NM_EXTW: |
| gen_ext(ctx, 32, rd, rs, rt, extract32(ctx->opcode, 6, 5)); |
| break; |
| case NM_POOL32AXF: |
| gen_pool32axf_nanomips_insn(env, ctx); |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| break; |
| case NM_P_GP_W: |
| switch (ctx->opcode & 0x03) { |
| case NM_ADDIUGP_W: |
| if (rt != 0) { |
| offset = extract32(ctx->opcode, 0, 21); |
| gen_op_addr_addi(ctx, cpu_gpr[rt], cpu_gpr[28], offset); |
| } |
| break; |
| case NM_LWGP: |
| gen_ld(ctx, OPC_LW, rt, 28, extract32(ctx->opcode, 2, 19) << 2); |
| break; |
| case NM_SWGP: |
| gen_st(ctx, OPC_SW, rt, 28, extract32(ctx->opcode, 2, 19) << 2); |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| break; |
| case NM_P48I: |
| { |
| insn = translator_lduw(env, &ctx->base, ctx->base.pc_next + 4); |
| target_long addr_off = extract32(ctx->opcode, 0, 16) | insn << 16; |
| switch (extract32(ctx->opcode, 16, 5)) { |
| case NM_LI48: |
| check_nms(ctx); |
| if (rt != 0) { |
| tcg_gen_movi_tl(cpu_gpr[rt], addr_off); |
| } |
| break; |
| case NM_ADDIU48: |
| check_nms(ctx); |
| if (rt != 0) { |
| tcg_gen_addi_tl(cpu_gpr[rt], cpu_gpr[rt], addr_off); |
| tcg_gen_ext32s_tl(cpu_gpr[rt], cpu_gpr[rt]); |
| } |
| break; |
| case NM_ADDIUGP48: |
| check_nms(ctx); |
| if (rt != 0) { |
| gen_op_addr_addi(ctx, cpu_gpr[rt], cpu_gpr[28], addr_off); |
| } |
| break; |
| case NM_ADDIUPC48: |
| check_nms(ctx); |
| if (rt != 0) { |
| target_long addr = addr_add(ctx, ctx->base.pc_next + 6, |
| addr_off); |
| |
| tcg_gen_movi_tl(cpu_gpr[rt], addr); |
| } |
| break; |
| case NM_LWPC48: |
| check_nms(ctx); |
| if (rt != 0) { |
| TCGv t0; |
| t0 = tcg_temp_new(); |
| |
| target_long addr = addr_add(ctx, ctx->base.pc_next + 6, |
| addr_off); |
| |
| tcg_gen_movi_tl(t0, addr); |
| tcg_gen_qemu_ld_tl(cpu_gpr[rt], t0, ctx->mem_idx, MO_TESL); |
| tcg_temp_free(t0); |
| } |
| break; |
| case NM_SWPC48: |
| check_nms(ctx); |
| { |
| TCGv t0, t1; |
| t0 = tcg_temp_new(); |
| t1 = tcg_temp_new(); |
| |
| target_long addr = addr_add(ctx, ctx->base.pc_next + 6, |
| addr_off); |
| |
| tcg_gen_movi_tl(t0, addr); |
| gen_load_gpr(t1, rt); |
| |
| tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL); |
| |
| tcg_temp_free(t0); |
| tcg_temp_free(t1); |
| } |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| return 6; |
| } |
| case NM_P_U12: |
| switch (extract32(ctx->opcode, 12, 4)) { |
| case NM_ORI: |
| gen_logic_imm(ctx, OPC_ORI, rt, rs, extract32(ctx->opcode, 0, 12)); |
| break; |
| case NM_XORI: |
| gen_logic_imm(ctx, OPC_XORI, rt, rs, extract32(ctx->opcode, 0, 12)); |
| break; |
| case NM_ANDI: |
| gen_logic_imm(ctx, OPC_ANDI, rt, rs, extract32(ctx->opcode, 0, 12)); |
| break; |
| case NM_P_SR: |
| switch (extract32(ctx->opcode, 20, 1)) { |
| case NM_PP_SR: |
| switch (ctx->opcode & 3) { |
| case NM_SAVE: |
| gen_save(ctx, rt, extract32(ctx->opcode, 16, 4), |
| extract32(ctx->opcode, 2, 1), |
| extract32(ctx->opcode, 3, 9) << 3); |
| break; |
| case NM_RESTORE: |
| case NM_RESTORE_JRC: |
| gen_restore(ctx, rt, extract32(ctx->opcode, 16, 4), |
| extract32(ctx->opcode, 2, 1), |
| extract32(ctx->opcode, 3, 9) << 3); |
| if ((ctx->opcode & 3) == NM_RESTORE_JRC) { |
| gen_compute_branch_nm(ctx, OPC_JR, 2, 31, 0, 0); |
| } |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| break; |
| case NM_P_SR_F: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| break; |
| case NM_SLTI: |
| gen_slt_imm(ctx, OPC_SLTI, rt, rs, extract32(ctx->opcode, 0, 12)); |
| break; |
| case NM_SLTIU: |
| gen_slt_imm(ctx, OPC_SLTIU, rt, rs, extract32(ctx->opcode, 0, 12)); |
| break; |
| case NM_SEQI: |
| { |
| TCGv t0 = tcg_temp_new(); |
| |
| imm = extract32(ctx->opcode, 0, 12); |
| gen_load_gpr(t0, rs); |
| tcg_gen_setcondi_tl(TCG_COND_EQ, t0, t0, imm); |
| gen_store_gpr(t0, rt); |
| |
| tcg_temp_free(t0); |
| } |
| break; |
| case NM_ADDIUNEG: |
| imm = (int16_t) extract32(ctx->opcode, 0, 12); |
| gen_arith_imm(ctx, OPC_ADDIU, rt, rs, -imm); |
| break; |
| case NM_P_SHIFT: |
| { |
| int shift = extract32(ctx->opcode, 0, 5); |
| switch (extract32(ctx->opcode, 5, 4)) { |
| case NM_P_SLL: |
| if (rt == 0 && shift == 0) { |
| /* NOP */ |
| } else if (rt == 0 && shift == 3) { |
| /* EHB - treat as NOP */ |
| } else if (rt == 0 && shift == 5) { |
| /* PAUSE - treat as NOP */ |
| } else if (rt == 0 && shift == 6) { |
| /* SYNC */ |
| gen_sync(extract32(ctx->opcode, 16, 5)); |
| } else { |
| /* SLL */ |
| gen_shift_imm(ctx, OPC_SLL, rt, rs, |
| extract32(ctx->opcode, 0, 5)); |
| } |
| break; |
| case NM_SRL: |
| gen_shift_imm(ctx, OPC_SRL, rt, rs, |
| extract32(ctx->opcode, 0, 5)); |
| break; |
| case NM_SRA: |
| gen_shift_imm(ctx, OPC_SRA, rt, rs, |
| extract32(ctx->opcode, 0, 5)); |
| break; |
| case NM_ROTR: |
| gen_shift_imm(ctx, OPC_ROTR, rt, rs, |
| extract32(ctx->opcode, 0, 5)); |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| } |
| break; |
| case NM_P_ROTX: |
| check_nms(ctx); |
| if (rt != 0) { |
| TCGv t0 = tcg_temp_new(); |
| TCGv_i32 shift = tcg_const_i32(extract32(ctx->opcode, 0, 5)); |
| TCGv_i32 shiftx = tcg_const_i32(extract32(ctx->opcode, 7, 4) |
| << 1); |
| TCGv_i32 stripe = tcg_const_i32(extract32(ctx->opcode, 6, 1)); |
| |
| gen_load_gpr(t0, rs); |
| gen_helper_rotx(cpu_gpr[rt], t0, shift, shiftx, stripe); |
| tcg_temp_free(t0); |
| |
| tcg_temp_free_i32(shift); |
| tcg_temp_free_i32(shiftx); |
| tcg_temp_free_i32(stripe); |
| } |
| break; |
| case NM_P_INS: |
| switch (((ctx->opcode >> 10) & 2) | |
| (extract32(ctx->opcode, 5, 1))) { |
| case NM_INS: |
| check_nms(ctx); |
| gen_bitops(ctx, OPC_INS, rt, rs, extract32(ctx->opcode, 0, 5), |
| extract32(ctx->opcode, 6, 5)); |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| break; |
| case NM_P_EXT: |
| switch (((ctx->opcode >> 10) & 2) | |
| (extract32(ctx->opcode, 5, 1))) { |
| case NM_EXT: |
| check_nms(ctx); |
| gen_bitops(ctx, OPC_EXT, rt, rs, extract32(ctx->opcode, 0, 5), |
| extract32(ctx->opcode, 6, 5)); |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| break; |
| case NM_POOL32F: |
| gen_pool32f_nanomips_insn(ctx); |
| break; |
| case NM_POOL32S: |
| break; |
| case NM_P_LUI: |
| switch (extract32(ctx->opcode, 1, 1)) { |
| case NM_LUI: |
| if (rt != 0) { |
| tcg_gen_movi_tl(cpu_gpr[rt], |
| sextract32(ctx->opcode, 0, 1) << 31 | |
| extract32(ctx->opcode, 2, 10) << 21 | |
| extract32(ctx->opcode, 12, 9) << 12); |
| } |
| break; |
| case NM_ALUIPC: |
| if (rt != 0) { |
| offset = sextract32(ctx->opcode, 0, 1) << 31 | |
| extract32(ctx->opcode, 2, 10) << 21 | |
| extract32(ctx->opcode, 12, 9) << 12; |
| target_long addr; |
| addr = ~0xFFF & addr_add(ctx, ctx->base.pc_next + 4, offset); |
| tcg_gen_movi_tl(cpu_gpr[rt], addr); |
| } |
| break; |
| } |
| break; |
| case NM_P_GP_BH: |
| { |
| uint32_t u = extract32(ctx->opcode, 0, 18); |
| |
| switch (extract32(ctx->opcode, 18, 3)) { |
| case NM_LBGP: |
| gen_ld(ctx, OPC_LB, rt, 28, u); |
| break; |
| case NM_SBGP: |
| gen_st(ctx, OPC_SB, rt, 28, u); |
| break; |
| case NM_LBUGP: |
| gen_ld(ctx, OPC_LBU, rt, 28, u); |
| break; |
| case NM_ADDIUGP_B: |
| if (rt != 0) { |
| gen_op_addr_addi(ctx, cpu_gpr[rt], cpu_gpr[28], u); |
| } |
| break; |
| case NM_P_GP_LH: |
| u &= ~1; |
| switch (ctx->opcode & 1) { |
| case NM_LHGP: |
| gen_ld(ctx, OPC_LH, rt, 28, u); |
| break; |
| case NM_LHUGP: |
| gen_ld(ctx, OPC_LHU, rt, 28, u); |
| break; |
| } |
| break; |
| case NM_P_GP_SH: |
| u &= ~1; |
| switch (ctx->opcode & 1) { |
| case NM_SHGP: |
| gen_st(ctx, OPC_SH, rt, 28, u); |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| break; |
| case NM_P_GP_CP1: |
| u &= ~0x3; |
| switch (ctx->opcode & 0x3) { |
| case NM_LWC1GP: |
| gen_cop1_ldst(ctx, OPC_LWC1, rt, 28, u); |
| break; |
| case NM_LDC1GP: |
| gen_cop1_ldst(ctx, OPC_LDC1, rt, 28, u); |
| break; |
| case NM_SWC1GP: |
| gen_cop1_ldst(ctx, OPC_SWC1, rt, 28, u); |
| break; |
| case NM_SDC1GP: |
| gen_cop1_ldst(ctx, OPC_SDC1, rt, 28, u); |
| break; |
| } |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| } |
| break; |
| case NM_P_LS_U12: |
| { |
| uint32_t u = extract32(ctx->opcode, 0, 12); |
| |
| switch (extract32(ctx->opcode, 12, 4)) { |
| case NM_P_PREFU12: |
| if (rt == 31) { |
| /* SYNCI */ |
| /* |
| * Break the TB to be able to sync copied instructions |
| * immediately. |
| */ |
| ctx->base.is_jmp = DISAS_STOP; |
| } else { |
| /* PREF */ |
| /* Treat as NOP. */ |
| } |
| break; |
| case NM_LB: |
| gen_ld(ctx, OPC_LB, rt, rs, u); |
| break; |
| case NM_LH: |
| gen_ld(ctx, OPC_LH, rt, rs, u); |
| break; |
| case NM_LW: |
| gen_ld(ctx, OPC_LW, rt, rs, u); |
| break; |
| case NM_LBU: |
| gen_ld(ctx, OPC_LBU, rt, rs, u); |
| break; |
| case NM_LHU: |
| gen_ld(ctx, OPC_LHU, rt, rs, u); |
| break; |
| case NM_SB: |
| gen_st(ctx, OPC_SB, rt, rs, u); |
| break; |
| case NM_SH: |
| gen_st(ctx, OPC_SH, rt, rs, u); |
| break; |
| case NM_SW: |
| gen_st(ctx, OPC_SW, rt, rs, u); |
| break; |
| case NM_LWC1: |
| gen_cop1_ldst(ctx, OPC_LWC1, rt, rs, u); |
| break; |
| case NM_LDC1: |
| gen_cop1_ldst(ctx, OPC_LDC1, rt, rs, u); |
| break; |
| case NM_SWC1: |
| gen_cop1_ldst(ctx, OPC_SWC1, rt, rs, u); |
| break; |
| case NM_SDC1: |
| gen_cop1_ldst(ctx, OPC_SDC1, rt, rs, u); |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| } |
| break; |
| case NM_P_LS_S9: |
| { |
| int32_t s = (sextract32(ctx->opcode, 15, 1) << 8) | |
| extract32(ctx->opcode, 0, 8); |
| |
| switch (extract32(ctx->opcode, 8, 3)) { |
| case NM_P_LS_S0: |
| switch (extract32(ctx->opcode, 11, 4)) { |
| case NM_LBS9: |
| gen_ld(ctx, OPC_LB, rt, rs, s); |
| break; |
| case NM_LHS9: |
| gen_ld(ctx, OPC_LH, rt, rs, s); |
| break; |
| case NM_LWS9: |
| gen_ld(ctx, OPC_LW, rt, rs, s); |
| break; |
| case NM_LBUS9: |
| gen_ld(ctx, OPC_LBU, rt, rs, s); |
| break; |
| case NM_LHUS9: |
| gen_ld(ctx, OPC_LHU, rt, rs, s); |
| break; |
| case NM_SBS9: |
| gen_st(ctx, OPC_SB, rt, rs, s); |
| break; |
| case NM_SHS9: |
| gen_st(ctx, OPC_SH, rt, rs, s); |
| break; |
| case NM_SWS9: |
| gen_st(ctx, OPC_SW, rt, rs, s); |
| break; |
| case NM_LWC1S9: |
| gen_cop1_ldst(ctx, OPC_LWC1, rt, rs, s); |
| break; |
| case NM_LDC1S9: |
| gen_cop1_ldst(ctx, OPC_LDC1, rt, rs, s); |
| break; |
| case NM_SWC1S9: |
| gen_cop1_ldst(ctx, OPC_SWC1, rt, rs, s); |
| break; |
| case NM_SDC1S9: |
| gen_cop1_ldst(ctx, OPC_SDC1, rt, rs, s); |
| break; |
| case NM_P_PREFS9: |
| if (rt == 31) { |
| /* SYNCI */ |
| /* |
| * Break the TB to be able to sync copied instructions |
| * immediately. |
| */ |
| ctx->base.is_jmp = DISAS_STOP; |
| } else { |
| /* PREF */ |
| /* Treat as NOP. */ |
| } |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| break; |
| case NM_P_LS_S1: |
| switch (extract32(ctx->opcode, 11, 4)) { |
| case NM_UALH: |
| case NM_UASH: |
| check_nms(ctx); |
| { |
| TCGv t0 = tcg_temp_new(); |
| TCGv t1 = tcg_temp_new(); |
| |
| gen_base_offset_addr(ctx, t0, rs, s); |
| |
| switch (extract32(ctx->opcode, 11, 4)) { |
| case NM_UALH: |
| tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TESW | |
| MO_UNALN); |
| gen_store_gpr(t0, rt); |
| break; |
| case NM_UASH: |
| gen_load_gpr(t1, rt); |
| tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUW | |
| MO_UNALN); |
| break; |
| } |
| tcg_temp_free(t0); |
| tcg_temp_free(t1); |
| } |
| break; |
| case NM_P_LL: |
| switch (ctx->opcode & 0x03) { |
| case NM_LL: |
| gen_ld(ctx, OPC_LL, rt, rs, s); |
| break; |
| case NM_LLWP: |
| check_xnp(ctx); |
| gen_llwp(ctx, rs, 0, rt, extract32(ctx->opcode, 3, 5)); |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| break; |
| case NM_P_SC: |
| switch (ctx->opcode & 0x03) { |
| case NM_SC: |
| gen_st_cond(ctx, rt, rs, s, MO_TESL, false); |
| break; |
| case NM_SCWP: |
| check_xnp(ctx); |
| gen_scwp(ctx, rs, 0, rt, extract32(ctx->opcode, 3, 5), |
| false); |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| break; |
| case NM_CACHE: |
| check_cp0_enabled(ctx); |
| if (ctx->hflags & MIPS_HFLAG_ITC_CACHE) { |
| gen_cache_operation(ctx, rt, rs, s); |
| } |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| break; |
| case NM_P_LS_E0: |
| switch (extract32(ctx->opcode, 11, 4)) { |
| case NM_LBE: |
| check_eva(ctx); |
| check_cp0_enabled(ctx); |
| gen_ld(ctx, OPC_LBE, rt, rs, s); |
| break; |
| case NM_SBE: |
| check_eva(ctx); |
| check_cp0_enabled(ctx); |
| gen_st(ctx, OPC_SBE, rt, rs, s); |
| break; |
| case NM_LBUE: |
| check_eva(ctx); |
| check_cp0_enabled(ctx); |
| gen_ld(ctx, OPC_LBUE, rt, rs, s); |
| break; |
| case NM_P_PREFE: |
| if (rt == 31) { |
| /* case NM_SYNCIE */ |
| check_eva(ctx); |
| check_cp0_enabled(ctx); |
| /* |
| * Break the TB to be able to sync copied instructions |
| * immediately. |
| */ |
| ctx->base.is_jmp = DISAS_STOP; |
| } else { |
| /* case NM_PREFE */ |
| check_eva(ctx); |
| check_cp0_enabled(ctx); |
| /* Treat as NOP. */ |
| } |
| break; |
| case NM_LHE: |
| check_eva(ctx); |
| check_cp0_enabled(ctx); |
| gen_ld(ctx, OPC_LHE, rt, rs, s); |
| break; |
| case NM_SHE: |
| check_eva(ctx); |
| check_cp0_enabled(ctx); |
| gen_st(ctx, OPC_SHE, rt, rs, s); |
| break; |
| case NM_LHUE: |
| check_eva(ctx); |
| check_cp0_enabled(ctx); |
| gen_ld(ctx, OPC_LHUE, rt, rs, s); |
| break; |
| case NM_CACHEE: |
| check_eva(ctx); |
| check_cp0_enabled(ctx); |
| check_nms_dl_il_sl_tl_l2c(ctx); |
| gen_cache_operation(ctx, rt, rs, s); |
| break; |
| case NM_LWE: |
| check_eva(ctx); |
| check_cp0_enabled(ctx); |
| gen_ld(ctx, OPC_LWE, rt, rs, s); |
| break; |
| case NM_SWE: |
| check_eva(ctx); |
| check_cp0_enabled(ctx); |
| gen_st(ctx, OPC_SWE, rt, rs, s); |
| break; |
| case NM_P_LLE: |
| switch (extract32(ctx->opcode, 2, 2)) { |
| case NM_LLE: |
| check_xnp(ctx); |
| check_eva(ctx); |
| check_cp0_enabled(ctx); |
| gen_ld(ctx, OPC_LLE, rt, rs, s); |
| break; |
| case NM_LLWPE: |
| check_xnp(ctx); |
| check_eva(ctx); |
| check_cp0_enabled(ctx); |
| gen_llwp(ctx, rs, 0, rt, extract32(ctx->opcode, 3, 5)); |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| break; |
| case NM_P_SCE: |
| switch (extract32(ctx->opcode, 2, 2)) { |
| case NM_SCE: |
| check_xnp(ctx); |
| check_eva(ctx); |
| check_cp0_enabled(ctx); |
| gen_st_cond(ctx, rt, rs, s, MO_TESL, true); |
| break; |
| case NM_SCWPE: |
| check_xnp(ctx); |
| check_eva(ctx); |
| check_cp0_enabled(ctx); |
| gen_scwp(ctx, rs, 0, rt, extract32(ctx->opcode, 3, 5), |
| true); |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| break; |
| case NM_P_LS_WM: |
| case NM_P_LS_UAWM: |
| check_nms(ctx); |
| { |
| int count = extract32(ctx->opcode, 12, 3); |
| int counter = 0; |
| |
| offset = sextract32(ctx->opcode, 15, 1) << 8 | |
| extract32(ctx->opcode, 0, 8); |
| TCGv va = tcg_temp_new(); |
| TCGv t1 = tcg_temp_new(); |
| MemOp memop = (extract32(ctx->opcode, 8, 3)) == |
| NM_P_LS_UAWM ? MO_UNALN : 0; |
| |
| count = (count == 0) ? 8 : count; |
| while (counter != count) { |
| int this_rt = ((rt + counter) & 0x1f) | (rt & 0x10); |
| int this_offset = offset + (counter << 2); |
| |
| gen_base_offset_addr(ctx, va, rs, this_offset); |
| |
| switch (extract32(ctx->opcode, 11, 1)) { |
| case NM_LWM: |
| tcg_gen_qemu_ld_tl(t1, va, ctx->mem_idx, |
| memop | MO_TESL); |
| gen_store_gpr(t1, this_rt); |
| if ((this_rt == rs) && |
| (counter != (count - 1))) { |
| /* UNPREDICTABLE */ |
| } |
| break; |
| case NM_SWM: |
| this_rt = (rt == 0) ? 0 : this_rt; |
| gen_load_gpr(t1, this_rt); |
| tcg_gen_qemu_st_tl(t1, va, ctx->mem_idx, |
| memop | MO_TEUL); |
| break; |
| } |
| counter++; |
| } |
| tcg_temp_free(va); |
| tcg_temp_free(t1); |
| } |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| } |
| break; |
| case NM_MOVE_BALC: |
| check_nms(ctx); |
| { |
| TCGv t0 = tcg_temp_new(); |
| int32_t s = sextract32(ctx->opcode, 0, 1) << 21 | |
| extract32(ctx->opcode, 1, 20) << 1; |
| rd = (extract32(ctx->opcode, 24, 1)) == 0 ? 4 : 5; |
| rt = decode_gpr_gpr4_zero(extract32(ctx->opcode, 25, 1) << 3 | |
| extract32(ctx->opcode, 21, 3)); |
| gen_load_gpr(t0, rt); |
| tcg_gen_mov_tl(cpu_gpr[rd], t0); |
| gen_compute_branch_nm(ctx, OPC_BGEZAL, 4, 0, 0, s); |
| tcg_temp_free(t0); |
| } |
| break; |
| case NM_P_BAL: |
| { |
| int32_t s = sextract32(ctx->opcode, 0, 1) << 25 | |
| extract32(ctx->opcode, 1, 24) << 1; |
| |
| if ((extract32(ctx->opcode, 25, 1)) == 0) { |
| /* BC */ |
| gen_compute_branch_nm(ctx, OPC_BEQ, 4, 0, 0, s); |
| } else { |
| /* BALC */ |
| gen_compute_branch_nm(ctx, OPC_BGEZAL, 4, 0, 0, s); |
| } |
| } |
| break; |
| case NM_P_J: |
| switch (extract32(ctx->opcode, 12, 4)) { |
| case NM_JALRC: |
| case NM_JALRC_HB: |
| gen_compute_branch_nm(ctx, OPC_JALR, 4, rs, rt, 0); |
| break; |
| case NM_P_BALRSC: |
| gen_compute_nanomips_pbalrsc_branch(ctx, rs, rt); |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| break; |
| case NM_P_BR1: |
| { |
| int32_t s = sextract32(ctx->opcode, 0, 1) << 14 | |
| extract32(ctx->opcode, 1, 13) << 1; |
| switch (extract32(ctx->opcode, 14, 2)) { |
| case NM_BEQC: |
| check_nms(ctx); |
| gen_compute_branch_nm(ctx, OPC_BEQ, 4, rs, rt, s); |
| break; |
| case NM_P_BR3A: |
| s = sextract32(ctx->opcode, 0, 1) << 14 | |
| extract32(ctx->opcode, 1, 13) << 1; |
| switch (extract32(ctx->opcode, 16, 5)) { |
| case NM_BC1EQZC: |
| check_cp1_enabled(ctx); |
| gen_compute_branch_cp1_nm(ctx, OPC_BC1EQZ, rt, s); |
| break; |
| case NM_BC1NEZC: |
| check_cp1_enabled(ctx); |
| gen_compute_branch_cp1_nm(ctx, OPC_BC1NEZ, rt, s); |
| break; |
| case NM_BPOSGE32C: |
| check_dsp_r3(ctx); |
| { |
| int32_t imm = extract32(ctx->opcode, 1, 13) | |
| extract32(ctx->opcode, 0, 1) << 13; |
| |
| gen_compute_branch_nm(ctx, OPC_BPOSGE32, 4, -1, -2, |
| imm << 1); |
| } |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| break; |
| case NM_BGEC: |
| if (rs == rt) { |
| gen_compute_compact_branch_nm(ctx, OPC_BC, rs, rt, s); |
| } else { |
| gen_compute_compact_branch_nm(ctx, OPC_BGEC, rs, rt, s); |
| } |
| break; |
| case NM_BGEUC: |
| if (rs == rt || rt == 0) { |
| gen_compute_compact_branch_nm(ctx, OPC_BC, 0, 0, s); |
| } else if (rs == 0) { |
| gen_compute_compact_branch_nm(ctx, OPC_BEQZC, rt, 0, s); |
| } else { |
| gen_compute_compact_branch_nm(ctx, OPC_BGEUC, rs, rt, s); |
| } |
| break; |
| } |
| } |
| break; |
| case NM_P_BR2: |
| { |
| int32_t s = sextract32(ctx->opcode, 0, 1) << 14 | |
| extract32(ctx->opcode, 1, 13) << 1; |
| switch (extract32(ctx->opcode, 14, 2)) { |
| case NM_BNEC: |
| check_nms(ctx); |
| if (rs == rt) { |
| /* NOP */ |
| ctx->hflags |= MIPS_HFLAG_FBNSLOT; |
| } else { |
| gen_compute_branch_nm(ctx, OPC_BNE, 4, rs, rt, s); |
| } |
| break; |
| case NM_BLTC: |
| if (rs != 0 && rt != 0 && rs == rt) { |
| /* NOP */ |
| ctx->hflags |= MIPS_HFLAG_FBNSLOT; |
| } else { |
| gen_compute_compact_branch_nm(ctx, OPC_BLTC, rs, rt, s); |
| } |
| break; |
| case NM_BLTUC: |
| if (rs == 0 || rs == rt) { |
| /* NOP */ |
| ctx->hflags |= MIPS_HFLAG_FBNSLOT; |
| } else { |
| gen_compute_compact_branch_nm(ctx, OPC_BLTUC, rs, rt, s); |
| } |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| } |
| break; |
| case NM_P_BRI: |
| { |
| int32_t s = sextract32(ctx->opcode, 0, 1) << 11 | |
| extract32(ctx->opcode, 1, 10) << 1; |
| uint32_t u = extract32(ctx->opcode, 11, 7); |
| |
| gen_compute_imm_branch(ctx, extract32(ctx->opcode, 18, 3), |
| rt, u, s); |
| } |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| return 4; |
| } |
| |
| static int decode_isa_nanomips(CPUMIPSState *env, DisasContext *ctx) |
| { |
| uint32_t op; |
| int rt = decode_gpr_gpr3(NANOMIPS_EXTRACT_RT3(ctx->opcode)); |
| int rs = decode_gpr_gpr3(NANOMIPS_EXTRACT_RS3(ctx->opcode)); |
| int rd = decode_gpr_gpr3(NANOMIPS_EXTRACT_RD3(ctx->opcode)); |
| int offset; |
| int imm; |
| |
| /* make sure instructions are on a halfword boundary */ |
| if (ctx->base.pc_next & 0x1) { |
| TCGv tmp = tcg_const_tl(ctx->base.pc_next); |
| tcg_gen_st_tl(tmp, cpu_env, offsetof(CPUMIPSState, CP0_BadVAddr)); |
| tcg_temp_free(tmp); |
| generate_exception_end(ctx, EXCP_AdEL); |
| return 2; |
| } |
| |
| op = extract32(ctx->opcode, 10, 6); |
| switch (op) { |
| case NM_P16_MV: |
| rt = NANOMIPS_EXTRACT_RD5(ctx->opcode); |
| if (rt != 0) { |
| /* MOVE */ |
| rs = NANOMIPS_EXTRACT_RS5(ctx->opcode); |
| gen_arith(ctx, OPC_ADDU, rt, rs, 0); |
| } else { |
| /* P16.RI */ |
| switch (extract32(ctx->opcode, 3, 2)) { |
| case NM_P16_SYSCALL: |
| if (extract32(ctx->opcode, 2, 1) == 0) { |
| generate_exception_end(ctx, EXCP_SYSCALL); |
| } else { |
| gen_reserved_instruction(ctx); |
| } |
| break; |
| case NM_BREAK16: |
| generate_exception_end(ctx, EXCP_BREAK); |
| break; |
| case NM_SDBBP16: |
| if (is_uhi(ctx, extract32(ctx->opcode, 0, 3))) { |
| ctx->base.is_jmp = DISAS_SEMIHOST; |
| } else { |
| if (ctx->hflags & MIPS_HFLAG_SBRI) { |
| gen_reserved_instruction(ctx); |
| } else { |
| generate_exception_end(ctx, EXCP_DBp); |
| } |
| } |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| } |
| break; |
| case NM_P16_SHIFT: |
| { |
| int shift = extract32(ctx->opcode, 0, 3); |
| uint32_t opc = 0; |
| shift = (shift == 0) ? 8 : shift; |
| |
| switch (extract32(ctx->opcode, 3, 1)) { |
| case NM_SLL16: |
| opc = OPC_SLL; |
| break; |
| case NM_SRL16: |
| opc = OPC_SRL; |
| break; |
| } |
| gen_shift_imm(ctx, opc, rt, rs, shift); |
| } |
| break; |
| case NM_P16C: |
| switch (ctx->opcode & 1) { |
| case NM_POOL16C_0: |
| gen_pool16c_nanomips_insn(ctx); |
| break; |
| case NM_LWXS16: |
| gen_ldxs(ctx, rt, rs, rd); |
| break; |
| } |
| break; |
| case NM_P16_A1: |
| switch (extract32(ctx->opcode, 6, 1)) { |
| case NM_ADDIUR1SP: |
| imm = extract32(ctx->opcode, 0, 6) << 2; |
| gen_arith_imm(ctx, OPC_ADDIU, rt, 29, imm); |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| break; |
| case NM_P16_A2: |
| switch (extract32(ctx->opcode, 3, 1)) { |
| case NM_ADDIUR2: |
| imm = extract32(ctx->opcode, 0, 3) << 2; |
| gen_arith_imm(ctx, OPC_ADDIU, rt, rs, imm); |
| break; |
| case NM_P_ADDIURS5: |
| rt = extract32(ctx->opcode, 5, 5); |
| if (rt != 0) { |
| /* imm = sign_extend(s[3] . s[2:0] , from_nbits = 4) */ |
| imm = (sextract32(ctx->opcode, 4, 1) << 3) | |
| (extract32(ctx->opcode, 0, 3)); |
| gen_arith_imm(ctx, OPC_ADDIU, rt, rt, imm); |
| } |
| break; |
| } |
| break; |
| case NM_P16_ADDU: |
| switch (ctx->opcode & 0x1) { |
| case NM_ADDU16: |
| gen_arith(ctx, OPC_ADDU, rd, rs, rt); |
| break; |
| case NM_SUBU16: |
| gen_arith(ctx, OPC_SUBU, rd, rs, rt); |
| break; |
| } |
| break; |
| case NM_P16_4X4: |
| rt = (extract32(ctx->opcode, 9, 1) << 3) | |
| extract32(ctx->opcode, 5, 3); |
| rs = (extract32(ctx->opcode, 4, 1) << 3) | |
| extract32(ctx->opcode, 0, 3); |
| rt = decode_gpr_gpr4(rt); |
| rs = decode_gpr_gpr4(rs); |
| switch ((extract32(ctx->opcode, 7, 2) & 0x2) | |
| (extract32(ctx->opcode, 3, 1))) { |
| case NM_ADDU4X4: |
| check_nms(ctx); |
| gen_arith(ctx, OPC_ADDU, rt, rs, rt); |
| break; |
| case NM_MUL4X4: |
| check_nms(ctx); |
| gen_r6_muldiv(ctx, R6_OPC_MUL, rt, rs, rt); |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| break; |
| case NM_LI16: |
| { |
| int imm = extract32(ctx->opcode, 0, 7); |
| imm = (imm == 0x7f ? -1 : imm); |
| if (rt != 0) { |
| tcg_gen_movi_tl(cpu_gpr[rt], imm); |
| } |
| } |
| break; |
| case NM_ANDI16: |
| { |
| uint32_t u = extract32(ctx->opcode, 0, 4); |
| u = (u == 12) ? 0xff : |
| (u == 13) ? 0xffff : u; |
| gen_logic_imm(ctx, OPC_ANDI, rt, rs, u); |
| } |
| break; |
| case NM_P16_LB: |
| offset = extract32(ctx->opcode, 0, 2); |
| switch (extract32(ctx->opcode, 2, 2)) { |
| case NM_LB16: |
| gen_ld(ctx, OPC_LB, rt, rs, offset); |
| break; |
| case NM_SB16: |
| rt = decode_gpr_gpr3_src_store( |
| NANOMIPS_EXTRACT_RT3(ctx->opcode)); |
| gen_st(ctx, OPC_SB, rt, rs, offset); |
| break; |
| case NM_LBU16: |
| gen_ld(ctx, OPC_LBU, rt, rs, offset); |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| break; |
| case NM_P16_LH: |
| offset = extract32(ctx->opcode, 1, 2) << 1; |
| switch ((extract32(ctx->opcode, 3, 1) << 1) | (ctx->opcode & 1)) { |
| case NM_LH16: |
| gen_ld(ctx, OPC_LH, rt, rs, offset); |
| break; |
| case NM_SH16: |
| rt = decode_gpr_gpr3_src_store( |
| NANOMIPS_EXTRACT_RT3(ctx->opcode)); |
| gen_st(ctx, OPC_SH, rt, rs, offset); |
| break; |
| case NM_LHU16: |
| gen_ld(ctx, OPC_LHU, rt, rs, offset); |
| break; |
| default: |
| gen_reserved_instruction(ctx); |
| break; |
| } |
| break; |
| case NM_LW16: |
| offset = extract32(ctx->opcode, 0, 4) << 2; |
| gen_ld(ctx, OPC_LW, rt, rs, offset); |
| break; |
| case NM_LWSP16: |
| rt = NANOMIPS_EXTRACT_RD5(ctx->opcode); |
| offset = extract32(ctx->opcode, 0, 5) << 2; |
| gen_ld(ctx, OPC_LW, rt, 29, offset); |
| break; |
| case NM_LW4X4: |
| check_nms(ctx); |
| rt = (extract32(ctx->opcode, 9, 1) << 3) | |
| extract32(ctx->opcode, 5, 3); |
| rs = (extract32(ctx->opcode, 4, 1) << 3) | |
| extract32(ctx->opcode, 0, 3); |
| offset = (extract32(ctx->opcode, 3, 1) << 3) | |
| (extract32(ctx->opcode, 8, 1) << 2); |
| rt = decode_gpr_gpr4(rt); |
| rs = decode_gpr_gpr4(rs); |
| gen_ld(ctx, OPC_LW, rt, rs, offset); |
| break; |
| case NM_SW4X4: |
| check_nms(ctx); |
| rt = (extract32(ctx->opcode, 9, 1) << 3) | |
| extract32(ctx->opcode, 5, 3); |
| rs = (extract32(ctx->opcode, 4, 1) << 3) | |
| extract32(ctx->opcode, 0, 3); |
| offset = (extract32(ctx->opcode, 3, 1) << 3) | |
| (extract32(ctx->opcode, 8, 1) << 2); |
| rt = decode_gpr_gpr4_zero(rt); |
| rs = decode_gpr_gpr4(rs); |
| gen_st(ctx, OPC_SW, rt, rs, offset); |
| break; |
| case NM_LWGP16: |
| offset = extract32(ctx->opcode, 0, 7) << 2; |
| gen_ld(ctx, OPC_LW, rt, 28, offset); |
| break; |
| case NM_SWSP16: |
| rt = NANOMIPS_EXTRACT_RD5(ctx->opcode); |
| offset = extract32(ctx->opcode, 0, 5) << 2; |
| gen_st(ctx, OPC_SW, rt, 29, offset); |
| break; |
| case NM_SW16: |
| rt = decode_gpr_gpr3_src_store( |
| NANOMIPS_EXTRACT_RT3(ctx->opcode)); |
| rs = decode_gpr_gpr3(NANOMIPS_EXTRACT_RS3(ctx->opcode)); |
| offset = extract32(ctx->opcode, 0, 4) << 2; |
| gen_st(ctx, OPC_SW, rt, rs, offset); |
| break; |
| case NM_SWGP16: |
| rt = decode_gpr_gpr3_src_store( |
| NANOMIPS_EXTRACT_RT3(ctx->opcode)); |
| offset = extract32(ctx->opcode, 0, 7) << 2; |
| gen_st(ctx, OPC_SW, rt, 28, offset); |
| break; |
| case NM_BC16: |
| gen_compute_branch_nm(ctx, OPC_BEQ, 2, 0, 0, |
| (sextract32(ctx->opcode, 0, 1) << 10) | |
| (extract32(ctx->opcode, 1, 9) << 1)); |
| break; |
| case NM_BALC16: |
| gen_compute_branch_nm(ctx, OPC_BGEZAL, 2, 0, 0, |
| (sextract32(ctx->opcode, 0, 1) << 10) | |
| (extract32(ctx->opcode, 1, 9) << 1)); |
| break; |
| case NM_BEQZC16: |
| gen_compute_branch_nm(ctx, OPC_BEQ, 2, rt, 0, |
| (sextract32(ctx->opcode, 0, 1) << 7) | |
| (extract32(ctx->opcode, 1, 6) << 1)); |
| break; |
| case NM_BNEZC16: |
| gen_compute_branch_nm(ctx, OPC_BNE, 2, rt, 0, |
| (sextract32(ctx->opcode, 0, 1) << 7) | |
| (extract32(ctx->opcode, 1, 6) << 1)); |
| break; |
| case NM_P16_BR: |
| switch (ctx->opcode & 0xf) { |
| case 0: |
| /* P16.JRC */ |
| switch (extract32(ctx->opcode, 4, 1)) { |
| case NM_JRC: |
| gen_compute_branch_nm(ctx, OPC_JR, 2, |
| extract32(ctx->opcode, 5, 5), 0, 0); |
| break; |
| case NM_JALRC16: |
| gen_compute_branch_nm(ctx, OPC_JALR, 2, |
| extract32(ctx->opcode, 5, 5), 31, 0); |
| break; |
| } |
| break; |
| default: |
| { |
| /* P16.BRI */ |
| uint32_t opc = extract32(ctx->opcode, 4, 3) < |
| extract32(ctx->opcode, 7, 3) ? OPC_BEQ : OPC_BNE; |
| gen_compute_branch_nm(ctx, opc, 2, rs, rt, |
| extract32(ctx->opcode, 0, 4) << 1); |
| } |
| break; |
| } |
| break; |
| case NM_P16_SR: |
| { |
| int count = extract32(ctx->opcode, 0, 4); |
| int u = extract32(ctx->opcode, 4, 4) << 4; |
| |
| rt = 30 + extract32(ctx->opcode, 9, 1); |
| switch (extract32(ctx->opcode, 8, 1)) { |
| case NM_SAVE16: |
| gen_save(ctx, rt, count, 0, u); |
| break; |
| case NM_RESTORE_JRC16: |
| gen_restore(ctx, rt, count, 0, u); |
| gen_compute_branch_nm(ctx, OPC_JR, 2, 31, 0, 0); |
| break; |
| } |
| } |
| break; |
| case NM_MOVEP: |
| case NM_MOVEPREV: |
| check_nms(ctx); |
| { |
| static const int gpr2reg1[] = {4, 5, 6, 7}; |
| static const int gpr2reg2[] = {5, 6, 7, 8}; |
| int re; |
| int rd2 = extract32(ctx->opcode, 3, 1) << 1 | |
| extract32(ctx->opcode, 8, 1); |
| int r1 = gpr2reg1[rd2]; |
| int r2 = gpr2reg2[rd2]; |
| int r3 = extract32(ctx->opcode, 4, 1) << 3 | |
| extract32(ctx->opcode, 0, 3); |
| int r4 = extract32(ctx->opcode, 9, 1) << 3 | |
| extract32(ctx->opcode, 5, 3); |
| TCGv t0 = tcg_temp_new(); |
| TCGv t1 = tcg_temp_new(); |
| if (op == NM_MOVEP) { |
| rd = r1; |
| re = r2; |
| rs = decode_gpr_gpr4_zero(r3); |
| rt = decode_gpr_gpr4_zero(r4); |
| } else { |
| rd = decode_gpr_gpr4(r3); |
| re = decode_gpr_gpr4(r4); |
| rs = r1; |
| rt = r2; |
| } |
| gen_load_gpr(t0, rs); |
| gen_load_gpr(t1, rt); |
| tcg_gen_mov_tl(cpu_gpr[rd], t0); |
| tcg_gen_mov_tl(cpu_gpr[re], t1); |
| tcg_temp_free(t0); |
| tcg_temp_free(t1); |
| } |
| break; |
| default: |
| return decode_nanomips_32_48_opc(env, ctx); |
| } |
| |
| return 2; |
| } |