/*
 *  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_ld_i64(tval, taddr, ctx->mem_idx, MO_TEUQ | MO_ALIGN);
    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);
    gen_store_gpr(tmp2, reg2);
    tcg_gen_st_i64(tval, cpu_env, offsetof(CPUMIPSState, llval_wp));
    tcg_gen_st_tl(taddr, cpu_env, offsetof(CPUMIPSState, lladdr));
}

static void gen_scwp(DisasContext *ctx, uint32_t base, int16_t offset,
                    uint32_t reg1, uint32_t reg2, bool eva)
{
    TCGv taddr = tcg_temp_new();
    TCGv lladdr = tcg_temp_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 | MO_ALIGN);
    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);
}

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);
}

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;
    }
}

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;
            }
#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);
        }
        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));
        }
        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;
            }
        }
        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);
        }
        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);
        }
        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;
    }
}


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;
    }
}

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;
    }
}

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);
                gen_move_low32(cpu_LO[acc], t2);
                gen_move_high32(cpu_HI[acc], 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);
            }
            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);
                gen_move_low32(cpu_LO[acc], t2);
                gen_move_high32(cpu_HI[acc], 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);
            }
            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);
                gen_move_low32(cpu_LO[acc], t2);
                gen_move_high32(cpu_HI[acc], 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);
                gen_move_low32(cpu_LO[acc], t2);
                gen_move_high32(cpu_HI[acc], 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;
    }
}

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);
        }
        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;
    }
}

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;
    }
}


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;
            }
            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;
            }
            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 */
            return;
        } 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);
            return;
        } else if (rt == 0 && opc == NM_BBEQZC) {
            /* Unconditional branch */
        } else if (rt == 0 && opc == NM_BBNEZC) {
            /* Treat as NOP */
            return;
        } 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 */
            return;
        } 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);
        return;
    }

    /* 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);
    }
}

/* 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();
}

/* 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);
        }
        break;
    default:
        MIPS_INVAL("Compact branch/jump");
        gen_reserved_instruction(ctx);
        return;
    }

    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);
            return;
        }
    } 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);
            return;
        }

        /* 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);
    }
}


/* 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);
        return;
    }

    tcg_gen_trunc_i64_tl(bcond, t0);

    ctx->btarget = btarget;
}


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);
            return;
        }
    }
    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 | ctx->default_tcg_memop_mask);
        gen_store_gpr(t0, rd);
        break;
    case NM_LWX:
    /*case NM_LWXS:*/
        tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx,
                           MO_TESL | ctx->default_tcg_memop_mask);
        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 | ctx->default_tcg_memop_mask);
        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 | ctx->default_tcg_memop_mask);
        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 | ctx->default_tcg_memop_mask);
        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;
    }
}

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_constant_i32(rd);
                gen_helper_precr_sra_ph_w(v1_t, sa_t, v1_t,
                                          cpu_gpr[rt]);
                gen_store_gpr(v1_t, rt);
            }
            break;
        case 1:
            /* PRECR_SRA_R_PH_W */
            {
                TCGv_i32 sa_t = tcg_constant_i32(rd);
                gen_helper_precr_sra_r_ph_w(v1_t, sa_t, v1_t,
                                            cpu_gpr[rt]);
                gen_store_gpr(v1_t, rt);
            }
            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);
        }
        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;
    }
}

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 | ctx->default_tcg_memop_mask);
                }
                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 | ctx->default_tcg_memop_mask);
                }
                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);
            }
            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_constant_i32(extract32(ctx->opcode, 0, 5));
                TCGv_i32 shiftx =
                    tcg_constant_i32(extract32(ctx->opcode, 7, 4) << 1);
                TCGv_i32 stripe =
                    tcg_constant_i32(extract32(ctx->opcode, 6, 1));

                gen_load_gpr(t0, rs);
                gen_helper_rotx(cpu_gpr[rt], t0, shift, shiftx, 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;
                        }
                    }
                    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 : MO_ALIGN;

                    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++;
                    }
                }
                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);
        }
        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_constant_tl(ctx->base.pc_next);
        tcg_gen_st_tl(tmp, cpu_env, offsetof(CPUMIPSState, CP0_BadVAddr));
        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);
        }
        break;
    default:
        return decode_nanomips_32_48_opc(env, ctx);
    }

    return 2;
}
