/* Capstone Disassembly Engine, http://www.capstone-engine.org */
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2013-2022, */
/*    Rot127 <unisono@quyllur.org> 2022-2023 */
/* Automatically translated source file from LLVM. */

/* LLVM-commit: <commit> */
/* LLVM-tag: <tag> */

/* Only small edits allowed. */
/* For multiple similar edits, please create a Patch for the translator. */

/* Capstone's C++ file translator: */
/* https://github.com/capstone-engine/capstone/tree/next/suite/auto-sync */

//===- MipsDisassembler.cpp - Disassembler for Mips -----------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file is part of the Mips Disassembler.
//
//===----------------------------------------------------------------------===//

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <capstone/platform.h>

#include "../../MCInst.h"
#include "../../MathExtras.h"
#include "../../MCInstPrinter.h"
#include "../../MCDisassembler.h"
#include "../../MCRegisterInfo.h"
#include "../../MCFixedLenDisassembler.h"
#include "../../cs_priv.h"
#include "../../utils.h"
#define GET_SUBTARGETINFO_ENUM
#include "MipsGenSubtargetInfo.inc"

#define GET_INSTRINFO_ENUM
#include "MipsGenInstrInfo.inc"

#define GET_REGINFO_ENUM
#include "MipsGenRegisterInfo.inc"

#define CONCAT(a, b) CONCAT_(a, b)
#define CONCAT_(a, b) a##_##b

#define DEBUG_TYPE "mips-disassembler"

bool Mips_getFeatureBits(unsigned int mode, unsigned int feature)
{
	switch(feature) {
	case Mips_FeatureGP64Bit:
		return mode & (CS_MODE_MIPS3 | CS_MODE_MIPS4 | 
						CS_MODE_MIPS5 | CS_MODE_MIPS64 | 
						CS_MODE_MIPS64R2 | CS_MODE_MIPS64R3 | 
						CS_MODE_MIPS64R5 | CS_MODE_MIPS64R6 | 
						CS_MODE_OCTEON | CS_MODE_OCTEONP);
	case Mips_FeatureFP64Bit:
		return mode & (CS_MODE_MIPS32R6 | CS_MODE_MIPS3 | 
						CS_MODE_MIPS4 | CS_MODE_MIPS5 | 
						CS_MODE_MIPS32R2 | CS_MODE_MIPS32R3 |
						CS_MODE_MIPS32R5 | CS_MODE_MIPS64 |
						CS_MODE_MIPS64R2 | CS_MODE_MIPS64R3 |
						CS_MODE_MIPS64R5 | CS_MODE_MIPS64R6 |
						CS_MODE_OCTEON | CS_MODE_OCTEONP);
	case Mips_FeatureNaN2008:
		return mode & (CS_MODE_MIPS32R6 | CS_MODE_MIPS64R6);
	case Mips_FeatureAbs2008:
		return mode & (CS_MODE_MIPS32R6 | CS_MODE_MIPS64R6);
	case Mips_FeatureMips1:
		return mode & (CS_MODE_MIPS1 | CS_MODE_MIPS2 | 
						CS_MODE_MIPS32 | CS_MODE_MIPS32R2 | 
						CS_MODE_MIPS32R3 | CS_MODE_MIPS32R5 | 
						CS_MODE_MIPS32R6 | CS_MODE_MIPS3 | 
						CS_MODE_MIPS4 | CS_MODE_MIPS5 | 
						CS_MODE_MIPS64 | CS_MODE_MIPS64R2 | 
						CS_MODE_MIPS64R3 | CS_MODE_MIPS64R5 | 
						CS_MODE_MIPS64R6 | CS_MODE_OCTEON | 
						CS_MODE_OCTEONP);
	case Mips_FeatureMips2:
		return mode & (CS_MODE_MIPS2 | CS_MODE_MIPS32 | 
						CS_MODE_MIPS32R2 | CS_MODE_MIPS32R3 | 
						CS_MODE_MIPS32R5 | CS_MODE_MIPS32R6 | 
						CS_MODE_MIPS3 | CS_MODE_MIPS4 | 
						CS_MODE_MIPS5 | CS_MODE_MIPS64 | 
						CS_MODE_MIPS64R2 | CS_MODE_MIPS64R3 | 
						CS_MODE_MIPS64R5 | CS_MODE_MIPS64R6 | 
						CS_MODE_OCTEON | CS_MODE_OCTEONP);
	case Mips_FeatureMips3_32:
		return mode & (CS_MODE_MIPS32 | CS_MODE_MIPS32R2 | 
						CS_MODE_MIPS32R3 | CS_MODE_MIPS32R5 | 
						CS_MODE_MIPS32R6 | CS_MODE_MIPS3 | 
						CS_MODE_MIPS4 | CS_MODE_MIPS5 | 
						CS_MODE_MIPS64 | CS_MODE_MIPS64R2 | 
						CS_MODE_MIPS64R3 | CS_MODE_MIPS64R5 | 
						CS_MODE_MIPS64R6 | CS_MODE_OCTEON | 
						CS_MODE_OCTEONP);
	case Mips_FeatureMips3_32r2:
		return mode & (CS_MODE_MIPS32R2 | CS_MODE_MIPS32R3 | 
						CS_MODE_MIPS32R5 | CS_MODE_MIPS32R6 | 
						CS_MODE_MIPS3 | CS_MODE_MIPS4 | 
						CS_MODE_MIPS5 | CS_MODE_MIPS64 | 
						CS_MODE_MIPS64R2 | CS_MODE_MIPS64R3 | 
						CS_MODE_MIPS64R5 | CS_MODE_MIPS64R6 | 
						CS_MODE_OCTEON | CS_MODE_OCTEONP);
	case Mips_FeatureMips3:
		return mode & (CS_MODE_MIPS3 | CS_MODE_MIPS4 | 
						CS_MODE_MIPS5 | CS_MODE_MIPS64 | 
						CS_MODE_MIPS64R2 | CS_MODE_MIPS64R3 | 
						CS_MODE_MIPS64R5 | CS_MODE_MIPS64R6 | 
						CS_MODE_OCTEON | CS_MODE_OCTEONP);
	case Mips_FeatureMips4_32:
		return mode & (CS_MODE_MIPS32 | CS_MODE_MIPS32R2 | 
						CS_MODE_MIPS32R3 | CS_MODE_MIPS32R5 | 
						CS_MODE_MIPS32R6 | CS_MODE_MIPS4 | 
						CS_MODE_MIPS5 | CS_MODE_MIPS64 | 
						CS_MODE_MIPS64R2 | CS_MODE_MIPS64R3 | 
						CS_MODE_MIPS64R5 | CS_MODE_MIPS64R6 | 
						CS_MODE_OCTEON | CS_MODE_OCTEONP);
	case Mips_FeatureMips4_32r2:
		return mode & (CS_MODE_MIPS32R2 | CS_MODE_MIPS32R3 | 
						CS_MODE_MIPS32R5 | CS_MODE_MIPS32R6 | 
						CS_MODE_MIPS4 | CS_MODE_MIPS5 | 
						CS_MODE_MIPS64 | CS_MODE_MIPS64R2 | 
						CS_MODE_MIPS64R3 | CS_MODE_MIPS64R5 | 
						CS_MODE_MIPS64R6 | CS_MODE_OCTEON | 
						CS_MODE_OCTEONP);
	case Mips_FeatureMips4:
		return mode & (CS_MODE_MIPS4 | CS_MODE_MIPS5 | 
						CS_MODE_MIPS64 | CS_MODE_MIPS64R2 | 
						CS_MODE_MIPS64R3 | CS_MODE_MIPS64R5 | 
						CS_MODE_MIPS64R6 | CS_MODE_OCTEON | 
						CS_MODE_OCTEONP);
	case Mips_FeatureMips5_32r2:
		return mode & (CS_MODE_MIPS32R2 | CS_MODE_MIPS32R3 | 
						CS_MODE_MIPS32R5 | CS_MODE_MIPS32R6 | 
						CS_MODE_MIPS5 | CS_MODE_MIPS64 | 
						CS_MODE_MIPS64R2 | CS_MODE_MIPS64R3 | 
						CS_MODE_MIPS64R5 | CS_MODE_MIPS64R6 | 
						CS_MODE_OCTEON | CS_MODE_OCTEONP);
	case Mips_FeatureMips5:
		return mode & (CS_MODE_MIPS5 | CS_MODE_MIPS64 | 
						CS_MODE_MIPS64R2 | CS_MODE_MIPS64R3 | 
						CS_MODE_MIPS64R5 | CS_MODE_MIPS64R6 | 
						CS_MODE_OCTEON | CS_MODE_OCTEONP);
	case Mips_FeatureMips32:
		return mode & (CS_MODE_MIPS32 | CS_MODE_MIPS32R2 | 
						CS_MODE_MIPS32R3 | CS_MODE_MIPS32R5 | 
						CS_MODE_MIPS32R6 | CS_MODE_MIPS64 | 
						CS_MODE_MIPS64R2 | CS_MODE_MIPS64R3 | 
						CS_MODE_MIPS64R5 | CS_MODE_MIPS64R6 | 
						CS_MODE_OCTEON | CS_MODE_OCTEONP);
	case Mips_FeatureMips32r2:
		return mode & (CS_MODE_MIPS32R2 | CS_MODE_MIPS32R3 | 
						CS_MODE_MIPS32R5 | CS_MODE_MIPS32R6 | 
						CS_MODE_MIPS64R2 | CS_MODE_MIPS64R3 | 
						CS_MODE_MIPS64R5 | CS_MODE_MIPS64R6 | 
						CS_MODE_OCTEON | CS_MODE_OCTEONP);
	case Mips_FeatureMips32r3:
		return mode & (CS_MODE_MIPS32R3 | CS_MODE_MIPS32R5 | 
						CS_MODE_MIPS32R6 | CS_MODE_MIPS64R3 | 
						CS_MODE_MIPS64R5 | CS_MODE_MIPS64R6);
	case Mips_FeatureMips32r5:
		return mode & (CS_MODE_MIPS32R5 | CS_MODE_MIPS32R6 | 
						CS_MODE_MIPS64R5 | CS_MODE_MIPS64R6);
	case Mips_FeatureMips32r6:
		return mode & (CS_MODE_MIPS32R6 | CS_MODE_MIPS64R6);
	case Mips_FeatureMips64:
		return mode & (CS_MODE_MIPS64 | CS_MODE_MIPS64R2 | 
						CS_MODE_MIPS64R3 | CS_MODE_MIPS64R5 | 
						CS_MODE_MIPS64R6 | CS_MODE_OCTEON | 
						CS_MODE_OCTEONP);
	case Mips_FeatureMips64r2:
		return mode & (CS_MODE_MIPS64R2 | CS_MODE_MIPS64R3 | 
						CS_MODE_MIPS64R5 | CS_MODE_MIPS64R6 | 
						CS_MODE_OCTEON | CS_MODE_OCTEONP);
	case Mips_FeatureMips64r3:
		return mode & (CS_MODE_MIPS64R3 | CS_MODE_MIPS64R5 | 
						CS_MODE_MIPS64R6);
	case Mips_FeatureMips64r5:
		return mode & (CS_MODE_MIPS64R5 | CS_MODE_MIPS64R6);
	case Mips_FeatureMips64r6:
		return mode & CS_MODE_MIPS64R6;
	case Mips_FeatureMips16:
		return mode & CS_MODE_MIPS16;
	case Mips_FeatureMicroMips:
		return mode & CS_MODE_MICRO;
	case Mips_FeatureNanoMips:
		return mode & (CS_MODE_NANOMIPS | CS_MODE_NMS1 | 
						CS_MODE_I7200);
	case Mips_FeatureNMS1:
		return mode & CS_MODE_NMS1;
	case Mips_FeatureTLB:
		return mode & CS_MODE_I7200;
	case Mips_FeatureCnMips:
		return mode & (CS_MODE_OCTEON | CS_MODE_OCTEONP);
	case Mips_FeatureCnMipsP:
		return mode & CS_MODE_OCTEONP;
	case Mips_FeaturePTR64Bit:
		return mode & CS_MODE_MIPS_PTR64;
	case Mips_FeatureSoftFloat:
		return mode & CS_MODE_MIPS_NOFLOAT;
	case Mips_FeatureI7200:
		return mode & CS_MODE_I7200;
	// optional features always enabled
	case Mips_FeatureDSP: // Mips DSP ASE
		return true;
	case Mips_FeatureDSPR2: // Mips DSP-R2 ASE
		return true;
	case Mips_FeatureDSPR3: // Mips DSP-R3 ASE
		return true;
	case Mips_FeatureMips3D: // Mips 3D ASE
		return true;
	case Mips_FeatureMSA: // Mips MSA ASE
		return true;
	case Mips_FeatureEVA: { // Mips EVA ASE
		if (mode & CS_MODE_NANOMIPS) {
			return mode & CS_MODE_I7200;
		}
		return true;
	}
	case Mips_FeatureCRC: // Mips R6 CRC ASE
		return true;
	case Mips_FeatureVirt: // Mips Virtualization ASE
		return true;
	case Mips_FeatureGINV: // Mips Global Invalidate ASE
		return true;
	case Mips_FeatureMT: { // Mips MT ASE
		if (mode & CS_MODE_NANOMIPS) {
			return mode & CS_MODE_I7200;
		}
		return true;
	}
	default:
		return false;
	}
}

static DecodeStatus getInstruction(MCInst *Instr, uint64_t *Size, const uint8_t *Bytes,
			    size_t BytesLen, uint64_t Address, SStream *CStream);

// end anonymous namespace

// Forward declare these because the autogenerated code will reference them.
// Definitions are further down.
static DecodeStatus DecodeGPR64RegisterClass(MCInst *Inst, unsigned RegNo,
					     uint64_t Address,
					     const void *Decoder);

static DecodeStatus DecodeCPU16RegsRegisterClass(MCInst *Inst, unsigned RegNo,
						 uint64_t Address,
						 const void *Decoder);

static DecodeStatus DecodeGPRMM16RegisterClass(MCInst *Inst, unsigned RegNo,
					       uint64_t Address,
					       const void *Decoder);

static DecodeStatus DecodeGPRMM16ZeroRegisterClass(MCInst *Inst, unsigned RegNo,
						   uint64_t Address,
						   const void *Decoder);

static DecodeStatus DecodeGPRMM16MovePRegisterClass(MCInst *Inst,
						    unsigned RegNo,
						    uint64_t Address,
						    const void *Decoder);

static DecodeStatus DecodeGPR32RegisterClass(MCInst *Inst, unsigned RegNo,
					     uint64_t Address,
					     const void *Decoder);

static DecodeStatus DecodeGPRNM3RegisterClass(MCInst *Inst, unsigned RegNo,
					      uint64_t Address,
					      const void *Decoder);

static DecodeStatus DecodeGPRNM4RegisterClass(MCInst *Inst, unsigned RegNo,
					      uint64_t Address,
					      const void *Decoder);

static DecodeStatus DecodeGPRNMRARegisterClass(MCInst *Inst, unsigned RegNo,
					       uint64_t Address,
					       const void *Decoder);

static DecodeStatus DecodeGPRNM3ZRegisterClass(MCInst *Inst, unsigned RegNo,
					       uint64_t Address,
					       const void *Decoder);

static DecodeStatus DecodeGPRNM4ZRegisterClass(MCInst *Inst, unsigned RegNo,
					       uint64_t Address,
					       const void *Decoder);

static DecodeStatus DecodeGPRNM32NZRegisterClass(MCInst *Inst, unsigned RegNo,
						 uint64_t Address,
						 const void *Decoder);

static DecodeStatus DecodeGPRNM32RegisterClass(MCInst *Inst, unsigned RegNo,
					       uint64_t Address,
					       const void *Decoder);

static DecodeStatus DecodeGPRNM2R1RegisterClass(MCInst *Inst, unsigned RegNo,
						uint64_t Address,
						const void *Decoder);

static DecodeStatus DecodeGPRNM1R1RegisterClass(MCInst *Inst, unsigned RegNo,
						uint64_t Address,
						const void *Decoder);

static DecodeStatus DecodePtrRegisterClass(MCInst *Inst, uint32_t Insn,
					   uint64_t Address,
					   const void *Decoder);

static DecodeStatus DecodeDSPRRegisterClass(MCInst *Inst, unsigned RegNo,
					    uint64_t Address,
					    const void *Decoder);

static DecodeStatus DecodeFGR64RegisterClass(MCInst *Inst, unsigned RegNo,
					     uint64_t Address,
					     const void *Decoder);

static DecodeStatus DecodeFGR32RegisterClass(MCInst *Inst, unsigned RegNo,
					     uint64_t Address,
					     const void *Decoder);

static DecodeStatus DecodeCCRRegisterClass(MCInst *Inst, unsigned RegNo,
					   uint64_t Address,
					   const void *Decoder);

static DecodeStatus DecodeFCCRegisterClass(MCInst *Inst, unsigned RegNo,
					   uint64_t Address,
					   const void *Decoder);

static DecodeStatus DecodeFGRCCRegisterClass(MCInst *Inst, unsigned RegNo,
					     uint64_t Address,
					     const void *Decoder);

static DecodeStatus DecodeHWRegsRegisterClass(MCInst *Inst, uint32_t Insn,
					      uint64_t Address,
					      const void *Decoder);

static DecodeStatus DecodeAFGR64RegisterClass(MCInst *Inst, unsigned RegNo,
					      uint64_t Address,
					      const void *Decoder);

static DecodeStatus DecodeACC64DSPRegisterClass(MCInst *Inst, unsigned RegNo,
						uint64_t Address,
						const void *Decoder);

static DecodeStatus DecodeHI32DSPRegisterClass(MCInst *Inst, unsigned RegNo,
					       uint64_t Address,
					       const void *Decoder);

static DecodeStatus DecodeLO32DSPRegisterClass(MCInst *Inst, unsigned RegNo,
					       uint64_t Address,
					       const void *Decoder);

static DecodeStatus DecodeMSA128BRegisterClass(MCInst *Inst, unsigned RegNo,
					       uint64_t Address,
					       const void *Decoder);

static DecodeStatus DecodeMSA128HRegisterClass(MCInst *Inst, unsigned RegNo,
					       uint64_t Address,
					       const void *Decoder);

static DecodeStatus DecodeMSA128WRegisterClass(MCInst *Inst, unsigned RegNo,
					       uint64_t Address,
					       const void *Decoder);

static DecodeStatus DecodeMSA128DRegisterClass(MCInst *Inst, unsigned RegNo,
					       uint64_t Address,
					       const void *Decoder);

static DecodeStatus DecodeMSACtrlRegisterClass(MCInst *Inst, unsigned RegNo,
					       uint64_t Address,
					       const void *Decoder);

static DecodeStatus DecodeCOP0RegisterClass(MCInst *Inst, unsigned RegNo,
					    uint64_t Address,
					    const void *Decoder);

static DecodeStatus DecodeCOP0SelRegisterClass(MCInst *Inst, unsigned RegNo,
					       uint64_t Address,
					       const void *Decoder);

static DecodeStatus DecodeCOP2RegisterClass(MCInst *Inst, unsigned RegNo,
					    uint64_t Address,
					    const void *Decoder);

static DecodeStatus DecodeBranchTarget(MCInst *Inst, unsigned Offset,
				       uint64_t Address, const void *Decoder);

static DecodeStatus DecodeBranchTarget1SImm16(MCInst *Inst, unsigned Offset,
					      uint64_t Address,
					      const void *Decoder);

static DecodeStatus DecodeJumpTarget(MCInst *Inst, uint32_t Insn,
				     uint64_t Address, const void *Decoder);

static DecodeStatus DecodeBranchTarget21(MCInst *Inst, unsigned Offset,
					 uint64_t Address, const void *Decoder);

static DecodeStatus DecodeBranchTarget21MM(MCInst *Inst, unsigned Offset,
					   uint64_t Address,
					   const void *Decoder);

static DecodeStatus DecodeBranchTarget26(MCInst *Inst, unsigned Offset,
					 uint64_t Address, const void *Decoder);

// DecodeBranchTarget7MM - Decode microMIPS branch offset, which is
// shifted left by 1 bit.
static DecodeStatus DecodeBranchTarget7MM(MCInst *Inst, unsigned Offset,
					  uint64_t Address,
					  const void *Decoder);

// DecodeBranchTarget10MM - Decode microMIPS branch offset, which is
// shifted left by 1 bit.
static DecodeStatus DecodeBranchTarget10MM(MCInst *Inst, unsigned Offset,
					   uint64_t Address,
					   const void *Decoder);

// DecodeBranchTargetMM - Decode microMIPS branch offset, which is
// shifted left by 1 bit.
static DecodeStatus DecodeBranchTargetMM(MCInst *Inst, unsigned Offset,
					 uint64_t Address, const void *Decoder);

// DecodeBranchTarget26MM - Decode microMIPS branch offset, which is
// shifted left by 1 bit.
static DecodeStatus DecodeBranchTarget26MM(MCInst *Inst, unsigned Offset,
					   uint64_t Address,
					   const void *Decoder);

// DecodeBranchTargetMM - Decode nanoMIPS branch offset, which is
// shifted left by 1 bit.
#define DECLARE_DecodeBranchTargetNM(bits) \
	static DecodeStatus CONCAT(DecodeBranchTargetNM, bits)( \
		MCInst * Inst, unsigned Offset, uint64_t Address, \
		const void *Decoder);
DECLARE_DecodeBranchTargetNM(10);
DECLARE_DecodeBranchTargetNM(7);
DECLARE_DecodeBranchTargetNM(21);
DECLARE_DecodeBranchTargetNM(25);
DECLARE_DecodeBranchTargetNM(14);
DECLARE_DecodeBranchTargetNM(11);
DECLARE_DecodeBranchTargetNM(5);

// DecodeJumpTargetMM - Decode microMIPS jump target, which is
// shifted left by 1 bit.
static DecodeStatus DecodeJumpTargetMM(MCInst *Inst, uint32_t Insn,
				       uint64_t Address, const void *Decoder);

// DecodeJumpTargetXMM - Decode microMIPS jump and link exchange target,
// which is shifted left by 2 bit.
static DecodeStatus DecodeJumpTargetXMM(MCInst *Inst, uint32_t Insn,
					uint64_t Address, const void *Decoder);

static DecodeStatus DecodeMem(MCInst *Inst, uint32_t Insn, uint64_t Address,
			      const void *Decoder);

#define DECLARE_DecodeMemNM(Offbits, isSigned, rt) \
	static DecodeStatus CONCAT(DecodeMemNM, \
				   CONCAT(Offbits, CONCAT(isSigned, rt)))( \
		MCInst * Inst, uint32_t Insn, uint64_t Address, \
		const void *Decoder);
DECLARE_DecodeMemNM(6, 0, Mips_GPRNM3RegClassID);
DECLARE_DecodeMemNM(7, 0, Mips_GPRNMSPRegClassID);
DECLARE_DecodeMemNM(9, 0, Mips_GPRNMGPRegClassID);
DECLARE_DecodeMemNM(2, 0, Mips_GPRNM3RegClassID);
DECLARE_DecodeMemNM(3, 0, Mips_GPRNM3RegClassID);
DECLARE_DecodeMemNM(21, 0, Mips_GPRNMGPRegClassID);
DECLARE_DecodeMemNM(18, 0, Mips_GPRNMGPRegClassID);
DECLARE_DecodeMemNM(12, 0, Mips_GPRNM32RegClassID);
DECLARE_DecodeMemNM(9, 1, Mips_GPRNM32RegClassID);

static DecodeStatus DecodeMemZeroNM(MCInst *Inst, uint32_t Insn,
				    uint64_t Address, const void *Decoder);

#define DECLARE_DecodeMemNMRX(rt) \
	static DecodeStatus CONCAT(DecodeMemNMRX, \
				   rt)(MCInst * Inst, uint32_t Insn, \
				       uint64_t Address, const void *Decoder);
DECLARE_DecodeMemNMRX(Mips_GPRNM3RegClassID);
DECLARE_DecodeMemNMRX(Mips_GPRNM32RegClassID);

static DecodeStatus DecodeMemNM4x4(MCInst *Inst, uint32_t Insn,
				   uint64_t Address, const void *Decoder);

static DecodeStatus DecodeMemEVA(MCInst *Inst, uint32_t Insn, uint64_t Address,
				 const void *Decoder);

static DecodeStatus DecodeLoadByte15(MCInst *Inst, uint32_t Insn,
				     uint64_t Address, const void *Decoder);

static DecodeStatus DecodeCacheOp(MCInst *Inst, uint32_t Insn, uint64_t Address,
				  const void *Decoder);

static DecodeStatus DecodeCacheeOp_CacheOpR6(MCInst *Inst, uint32_t Insn,
					     uint64_t Address,
					     const void *Decoder);

static DecodeStatus DecodeCacheOpMM(MCInst *Inst, uint32_t Insn,
				    uint64_t Address, const void *Decoder);

static DecodeStatus DecodePrefeOpMM(MCInst *Inst, uint32_t Insn,
				    uint64_t Address, const void *Decoder);

static DecodeStatus DecodeSyncI(MCInst *Inst, uint32_t Insn, uint64_t Address,
				const void *Decoder);

static DecodeStatus DecodeSyncI_MM(MCInst *Inst, uint32_t Insn,
				   uint64_t Address, const void *Decoder);

static DecodeStatus DecodeSynciR6(MCInst *Inst, uint32_t Insn, uint64_t Address,
				  const void *Decoder);

static DecodeStatus DecodeMSA128Mem(MCInst *Inst, uint32_t Insn,
				    uint64_t Address, const void *Decoder);

static DecodeStatus DecodeMemMMImm4(MCInst *Inst, uint32_t Insn,
				    uint64_t Address, const void *Decoder);

static DecodeStatus DecodeMemMMSPImm5Lsl2(MCInst *Inst, uint32_t Insn,
					  uint64_t Address,
					  const void *Decoder);

static DecodeStatus DecodeMemMMGPImm7Lsl2(MCInst *Inst, uint32_t Insn,
					  uint64_t Address,
					  const void *Decoder);

static DecodeStatus DecodeMemMMReglistImm4Lsl2(MCInst *Inst, uint32_t Insn,
					       uint64_t Address,
					       const void *Decoder);

static DecodeStatus DecodeMemMMImm9(MCInst *Inst, uint32_t Insn,
				    uint64_t Address, const void *Decoder);

static DecodeStatus DecodeMemMMImm12(MCInst *Inst, uint32_t Insn,
				     uint64_t Address, const void *Decoder);

static DecodeStatus DecodeMemMMImm16(MCInst *Inst, uint32_t Insn,
				     uint64_t Address, const void *Decoder);

static DecodeStatus DecodeFMem(MCInst *Inst, uint32_t Insn, uint64_t Address,
			       const void *Decoder);

static DecodeStatus DecodeFMemMMR2(MCInst *Inst, uint32_t Insn,
				   uint64_t Address, const void *Decoder);

static DecodeStatus DecodeFMem2(MCInst *Inst, uint32_t Insn, uint64_t Address,
				const void *Decoder);

static DecodeStatus DecodeFMem3(MCInst *Inst, uint32_t Insn, uint64_t Address,
				const void *Decoder);

static DecodeStatus DecodeFMemCop2R6(MCInst *Inst, uint32_t Insn,
				     uint64_t Address, const void *Decoder);

static DecodeStatus DecodeFMemCop2MMR6(MCInst *Inst, uint32_t Insn,
				       uint64_t Address, const void *Decoder);

static DecodeStatus DecodeSpecial3LlSc(MCInst *Inst, uint32_t Insn,
				       uint64_t Address, const void *Decoder);

static DecodeStatus DecodeAddiur2Simm7(MCInst *Inst, unsigned Value,
				       uint64_t Address, const void *Decoder);

static DecodeStatus DecodeLi16Imm(MCInst *Inst, unsigned Value,
				  uint64_t Address, const void *Decoder);

static DecodeStatus DecodePOOL16BEncodedField(MCInst *Inst, unsigned Value,
					      uint64_t Address,
					      const void *Decoder);

#define DECLARE_DecodeUImmWithOffsetAndScale(Bits, Offset, Scale) \
	static DecodeStatus CONCAT(DecodeUImmWithOffsetAndScale, \
				   CONCAT(Bits, CONCAT(Offset, Scale)))( \
		MCInst * Inst, unsigned Value, uint64_t Address, \
		const void *Decoder);
DECLARE_DecodeUImmWithOffsetAndScale(5, 0, 4);
DECLARE_DecodeUImmWithOffsetAndScale(6, 0, 4);
DECLARE_DecodeUImmWithOffsetAndScale(2, 1, 1);
DECLARE_DecodeUImmWithOffsetAndScale(5, 1, 1);
DECLARE_DecodeUImmWithOffsetAndScale(8, 0, 1);
DECLARE_DecodeUImmWithOffsetAndScale(18, 0, 1);
DECLARE_DecodeUImmWithOffsetAndScale(21, 0, 1);

#define DEFINE_DecodeUImmWithOffset(Bits, Offset) \
	static DecodeStatus CONCAT(DecodeUImmWithOffset, \
				   CONCAT(Bits, Offset))(MCInst * Inst, \
							 unsigned Value, \
							 uint64_t Address, \
							 const void *Decoder) \
	{ \
		return CONCAT(DecodeUImmWithOffsetAndScale, \
			      CONCAT(Bits, CONCAT(Offset, 1)))( \
			Inst, Value, Address, Decoder); \
	}
DEFINE_DecodeUImmWithOffset(5, 1);
DEFINE_DecodeUImmWithOffset(2, 1);

#define DECLARE_DecodeSImmWithOffsetAndScale(Bits, Offset, ScaleBy) \
	static DecodeStatus CONCAT( \
		DecodeSImmWithOffsetAndScale, \
		CONCAT(Bits, CONCAT(Offset, ScaleBy)))( \
		MCInst * Inst, unsigned Value, uint64_t Address, \
		const void *Decoder);

#define DECLARE_DecodeSImmWithOffsetAndScale_2(Bits, Offset) DECLARE_DecodeSImmWithOffsetAndScale(Bits, Offset, 1)
#define DECLARE_DecodeSImmWithOffsetAndScale_3(Bits) DECLARE_DecodeSImmWithOffsetAndScale(Bits, 0, 1)

DECLARE_DecodeSImmWithOffsetAndScale_3(16);
DECLARE_DecodeSImmWithOffsetAndScale_3(10);
DECLARE_DecodeSImmWithOffsetAndScale_3(4);
DECLARE_DecodeSImmWithOffsetAndScale_3(6);
DECLARE_DecodeSImmWithOffsetAndScale_3(32);

static DecodeStatus DecodeInsSize(MCInst *Inst, uint32_t Insn, uint64_t Address,
				  const void *Decoder);

static DecodeStatus DecodeImmM1To126(MCInst *Inst, unsigned Value,
				     uint64_t Address, const void *Decoder);

static DecodeStatus DecodeUImm4Mask(MCInst *Inst, unsigned Value,
				    uint64_t Address, const void *Decoder);

static DecodeStatus DecodeUImm3Shift(MCInst *Inst, unsigned Value,
				     uint64_t Address, const void *Decoder);

static DecodeStatus DecodeNMRegListOperand(MCInst *Inst, uint32_t Insn,
					   uint64_t Address,
					   const void *Decoder);

static DecodeStatus DecodeNMRegList16Operand(MCInst *Inst, uint32_t Insn,
					     uint64_t Address,
					     const void *Decoder);

static DecodeStatus DecodeNegImm12(MCInst *Inst, uint32_t Insn,
				   uint64_t Address, const void *Decoder);

#define DECLARE_DecodeSImmWithReg(Bits, Offset, Scale, RegNum) \
	static DecodeStatus CONCAT( \
		DecodeSImmWithReg, \
		CONCAT(Bits, CONCAT(Offset, CONCAT(Scale, RegNum))))( \
		MCInst * Inst, unsigned Value, uint64_t Address, \
		const void *Decoder);
DECLARE_DecodeSImmWithReg(32, 0, 1, Mips_GP_NM);

#define DECLARE_DecodeUImmWithReg(Bits, Offset, Scale, RegNum) \
	static DecodeStatus CONCAT( \
		DecodeUImmWithReg, \
		CONCAT(Bits, CONCAT(Offset, CONCAT(Scale, RegNum))))( \
		MCInst * Inst, unsigned Value, uint64_t Address, \
		const void *Decoder);
DECLARE_DecodeUImmWithReg(8, 0, 1, Mips_SP_NM);
DECLARE_DecodeUImmWithReg(21, 0, 1, Mips_GP_NM);
DECLARE_DecodeUImmWithReg(18, 0, 1, Mips_GP_NM);

static DecodeStatus DecodeSImm32s12(MCInst *Inst, uint32_t Insn,
				    uint64_t Address, const void *Decoder);

#define DECLARE_DecodeAddressPCRelNM(Bits) \
	static DecodeStatus CONCAT(DecodeAddressPCRelNM, Bits)( \
		MCInst * Inst, uint32_t Insn, uint64_t Address, \
		const void *Decoder);
DECLARE_DecodeAddressPCRelNM(22);
DECLARE_DecodeAddressPCRelNM(32);

static DecodeStatus DecodeBranchConflictNM(MCInst *Inst, uint32_t Insn,
					   uint64_t Address,
					   const void *Decoder);

static DecodeStatus DecodeSimm19Lsl2(MCInst *Inst, uint32_t Insn,
				     uint64_t Address, const void *Decoder);

static DecodeStatus DecodeSimm18Lsl3(MCInst *Inst, uint32_t Insn,
				     uint64_t Address, const void *Decoder);

static DecodeStatus DecodeSimm9SP(MCInst *Inst, uint32_t Insn, uint64_t Address,
				  const void *Decoder);

static DecodeStatus DecodeANDI16Imm(MCInst *Inst, uint32_t Insn,
				    uint64_t Address, const void *Decoder);

static DecodeStatus DecodeSimm23Lsl2(MCInst *Inst, uint32_t Insn,
				     uint64_t Address, const void *Decoder);

/// INSVE_[BHWD] have an implicit operand that the generated decoder doesn't
/// handle.
static DecodeStatus DecodeINSVE_DF(MCInst *MI, uint32_t insn, uint64_t Address,
				   const void *Decoder);

/*
static DecodeStatus DecodeDAHIDATIMMR6(MCInst *MI, uint32_t insn,
					  uint64_t Address, const void *Decoder);
*/

static DecodeStatus DecodeDAHIDATI(MCInst *MI, uint32_t insn, uint64_t Address,
                                  const void *Decoder);

static DecodeStatus DecodeAddiGroupBranch(MCInst *MI, uint32_t insn,
					  uint64_t Address,
					  const void *Decoder);

static DecodeStatus DecodePOP35GroupBranchMMR6(MCInst *MI, uint32_t insn,
					       uint64_t Address,
					       const void *Decoder);

static DecodeStatus DecodeDaddiGroupBranch(MCInst *MI, uint32_t insn,
					   uint64_t Address,
					   const void *Decoder);

static DecodeStatus DecodePOP37GroupBranchMMR6(MCInst *MI, uint32_t insn,
					       uint64_t Address,
					       const void *Decoder);

static DecodeStatus DecodePOP65GroupBranchMMR6(MCInst *MI, uint32_t insn,
					       uint64_t Address,
					       const void *Decoder);

static DecodeStatus DecodePOP75GroupBranchMMR6(MCInst *MI, uint32_t insn,
					       uint64_t Address,
					       const void *Decoder);

static DecodeStatus DecodeBlezlGroupBranch(MCInst *MI, uint32_t insn,
					   uint64_t Address,
					   const void *Decoder);

static DecodeStatus DecodeBgtzlGroupBranch(MCInst *MI, uint32_t insn,
					   uint64_t Address,
					   const void *Decoder);

static DecodeStatus DecodeBgtzGroupBranch(MCInst *MI, uint32_t insn,
					  uint64_t Address,
					  const void *Decoder);

static DecodeStatus DecodeBlezGroupBranch(MCInst *MI, uint32_t insn,
					  uint64_t Address,
					  const void *Decoder);

static DecodeStatus DecodeBgtzGroupBranchMMR6(MCInst *MI, uint32_t insn,
					      uint64_t Address,
					      const void *Decoder);

static DecodeStatus DecodeBlezGroupBranchMMR6(MCInst *MI, uint32_t insn,
					      uint64_t Address,
					      const void *Decoder);

static DecodeStatus DecodeDINS(MCInst *MI, uint32_t Insn, uint64_t Address,
			       const void *Decoder);

static DecodeStatus DecodeDEXT(MCInst *MI, uint32_t Insn, uint64_t Address,
			       const void *Decoder);

static DecodeStatus DecodeCRC(MCInst *MI, uint32_t Insn, uint64_t Address,
			      const void *Decoder);

static DecodeStatus DecodeRegListOperand(MCInst *Inst, uint32_t Insn,
					 uint64_t Address, const void *Decoder);

static DecodeStatus DecodeRegListOperand16(MCInst *Inst, uint32_t Insn,
					   uint64_t Address,
					   const void *Decoder);

static DecodeStatus DecodeMovePRegPair(MCInst *Inst, unsigned RegPair,
				       uint64_t Address, const void *Decoder);

static DecodeStatus DecodeMovePOperands(MCInst *Inst, uint32_t Insn,
					uint64_t Address, const void *Decoder);

static DecodeStatus DecodeFIXMEInstruction(MCInst *Inst, uint32_t Insn,
					   uint64_t Address,
					   const void *Decoder);

#include "MipsGenDisassemblerTables.inc"

static unsigned getReg(const MCInst *Inst, unsigned RC, unsigned RegNo)
{
	const MCRegisterClass* c = MCRegisterInfo_getRegClass(Inst->MRI, RC);
	return MCRegisterClass_getRegister(c, RegNo);
}

typedef DecodeStatus (*DecodeFN)(MCInst *Inst, uint32_t Insn,
					   uint64_t Address,
					   const void *Decoder);

static DecodeStatus DecodeINSVE_DF(MCInst *MI, uint32_t insn, uint64_t Address,
				   const void *Decoder)
{
	// The size of the n field depends on the element size
	// The register class also depends on this.
	uint32_t tmp = fieldFromInstruction_4(insn, 17, 5);
	unsigned NSize = 0;
	DecodeFN RegDecoder = NULL;
	if ((tmp & 0x18) == 0x00) {	   // INSVE_B
		NSize = 4;
		RegDecoder = DecodeMSA128BRegisterClass;
	} else if ((tmp & 0x1c) == 0x10) { // INSVE_H
		NSize = 3;
		RegDecoder = DecodeMSA128HRegisterClass;
	} else if ((tmp & 0x1e) == 0x18) { // INSVE_W
		NSize = 2;
		RegDecoder = DecodeMSA128WRegisterClass;
	} else if ((tmp & 0x1f) == 0x1c) { // INSVE_D
		NSize = 1;
		RegDecoder = DecodeMSA128DRegisterClass;
	} else
		CS_ASSERT_RET_VAL(0 && "Invalid encoding", MCDisassembler_Fail);

	// $wd
	tmp = fieldFromInstruction_4(insn, 6, 5);
	if (RegDecoder(MI, tmp, Address, Decoder) == MCDisassembler_Fail)
		return MCDisassembler_Fail;
	// $wd_in
	if (RegDecoder(MI, tmp, Address, Decoder) == MCDisassembler_Fail)
		return MCDisassembler_Fail;
	// $n
	tmp = fieldFromInstruction_4(insn, 16, NSize);
	MCOperand_CreateImm0(MI, (tmp));
	// $ws
	tmp = fieldFromInstruction_4(insn, 11, 5);
	if (RegDecoder(MI, tmp, Address, Decoder) == MCDisassembler_Fail)
		return MCDisassembler_Fail;
	// $n2
	MCOperand_CreateImm0(MI, (0));

	return MCDisassembler_Success;
}

/*
static DecodeStatus DecodeDAHIDATIMMR6(MCInst *MI, uint32_t insn,
                                      uint64_t Address, const void *Decoder)
{
       uint32_t Rs = fieldFromInstruction_4(insn, 16, 5);
       uint32_t Imm = fieldFromInstruction_4(insn, 0, 16);
       MCOperand_CreateReg0(MI, (getReg(MI, Mips_GPR64RegClassID, Rs)));
       MCOperand_CreateReg0(MI, (getReg(MI, Mips_GPR64RegClassID, Rs)));
       MCOperand_CreateImm0(MI, (Imm));

       return MCDisassembler_Success;
}
*/

static DecodeStatus DecodeDAHIDATI(MCInst *MI, uint32_t insn, uint64_t Address,
                                   const void *Decoder)
 {
       uint32_t Rs = fieldFromInstruction_4(insn, 21, 5);
       uint32_t Imm = fieldFromInstruction_4(insn, 0, 16);
       MCOperand_CreateReg0(MI, (getReg(MI, Mips_GPR64RegClassID, Rs)));
       MCOperand_CreateReg0(MI, (getReg(MI, Mips_GPR64RegClassID, Rs)));
        MCOperand_CreateImm0(MI, (Imm));
 
        return MCDisassembler_Success;
 }

static DecodeStatus DecodeAddiGroupBranch(MCInst *MI, uint32_t insn,
					  uint64_t Address, const void *Decoder)
{
	// If we are called then we can assume that MIPS32r6/MIPS64r6 is enabled
	// (otherwise we would have matched the ADDI instruction from the earlier
	// ISA's instead).
	//
	// We have:
	//    0b001000 sssss ttttt iiiiiiiiiiiiiiii
	//      BOVC if rs >= rt
	//      BEQZALC if rs == 0 && rt != 0
	//      BEQC if rs < rt && rs != 0

	uint32_t Rs = fieldFromInstruction_4(insn, 21, 5);
	uint32_t Rt = fieldFromInstruction_4(insn, 16, 5);
	int64_t Imm =
		SignExtend64(fieldFromInstruction_4(insn, 0, 16), 16) * 4 + 4;
	bool HasRs = false;

	if (Rs >= Rt) {
		MCInst_setOpcode(MI, (Mips_BOVC));
		HasRs = true;
	} else if (Rs != 0 && Rs < Rt) {
		MCInst_setOpcode(MI, (Mips_BEQC));
		HasRs = true;
	} else
		MCInst_setOpcode(MI, (Mips_BEQZALC));

	if (HasRs)
		MCOperand_CreateReg0(
			MI, (getReg(MI, Mips_GPR32RegClassID, Rs)));

	MCOperand_CreateReg0(MI, (getReg(MI, Mips_GPR32RegClassID, Rt)));
	MCOperand_CreateImm0(MI, (Imm));

	return MCDisassembler_Success;
}

static DecodeStatus DecodePOP35GroupBranchMMR6(MCInst *MI, uint32_t insn,
					       uint64_t Address,
					       const void *Decoder)
{
	uint32_t Rt = fieldFromInstruction_4(insn, 21, 5);
	uint32_t Rs = fieldFromInstruction_4(insn, 16, 5);
	int64_t Imm = 0;

	if (Rs >= Rt) {
		MCInst_setOpcode(MI, (Mips_BOVC_MMR6));
		MCOperand_CreateReg0(
			MI, (getReg(MI, Mips_GPR32RegClassID, Rt)));
		MCOperand_CreateReg0(
			MI, (getReg(MI, Mips_GPR32RegClassID, Rs)));
		Imm = SignExtend64(fieldFromInstruction_4(insn, 0, 16), 16) *
			      2 +
		      4;
	} else if (Rs != 0 && Rs < Rt) {
		MCInst_setOpcode(MI, (Mips_BEQC_MMR6));
		MCOperand_CreateReg0(
			MI, (getReg(MI, Mips_GPR32RegClassID, Rs)));
		MCOperand_CreateReg0(
			MI, (getReg(MI, Mips_GPR32RegClassID, Rt)));
		Imm = SignExtend64(fieldFromInstruction_4(insn, 0, 16), 16) *
			      4 +
		      4;
	} else {
		MCInst_setOpcode(MI, (Mips_BEQZALC_MMR6));
		MCOperand_CreateReg0(
			MI, (getReg(MI, Mips_GPR32RegClassID, Rt)));
		Imm = SignExtend64(fieldFromInstruction_4(insn, 0, 16), 16) *
			      2 +
		      4;
	}

	MCOperand_CreateImm0(MI, (Imm));

	return MCDisassembler_Success;
}

static DecodeStatus DecodeDaddiGroupBranch(MCInst *MI, uint32_t insn,
					   uint64_t Address,
					   const void *Decoder)
{
	// If we are called then we can assume that MIPS32r6/MIPS64r6 is enabled
	// (otherwise we would have matched the ADDI instruction from the earlier
	// ISA's instead).
	//
	// We have:
	//    0b011000 sssss ttttt iiiiiiiiiiiiiiii
	//      BNVC if rs >= rt
	//      BNEZALC if rs == 0 && rt != 0
	//      BNEC if rs < rt && rs != 0

	uint32_t Rs = fieldFromInstruction_4(insn, 21, 5);
	uint32_t Rt = fieldFromInstruction_4(insn, 16, 5);
	int64_t Imm =
		SignExtend64(fieldFromInstruction_4(insn, 0, 16), 16) * 4 + 4;
	bool HasRs = false;

	if (Rs >= Rt) {
		MCInst_setOpcode(MI, (Mips_BNVC));
		HasRs = true;
	} else if (Rs != 0 && Rs < Rt) {
		MCInst_setOpcode(MI, (Mips_BNEC));
		HasRs = true;
	} else
		MCInst_setOpcode(MI, (Mips_BNEZALC));

	if (HasRs)
		MCOperand_CreateReg0(
			MI, (getReg(MI, Mips_GPR32RegClassID, Rs)));

	MCOperand_CreateReg0(MI, (getReg(MI, Mips_GPR32RegClassID, Rt)));
	MCOperand_CreateImm0(MI, (Imm));

	return MCDisassembler_Success;
}

static DecodeStatus DecodePOP37GroupBranchMMR6(MCInst *MI, uint32_t insn,
					       uint64_t Address,
					       const void *Decoder)
{
	uint32_t Rt = fieldFromInstruction_4(insn, 21, 5);
	uint32_t Rs = fieldFromInstruction_4(insn, 16, 5);
	int64_t Imm = 0;

	if (Rs >= Rt) {
		MCInst_setOpcode(MI, (Mips_BNVC_MMR6));
		MCOperand_CreateReg0(
			MI, (getReg(MI, Mips_GPR32RegClassID, Rt)));
		MCOperand_CreateReg0(
			MI, (getReg(MI, Mips_GPR32RegClassID, Rs)));
		Imm = SignExtend64(fieldFromInstruction_4(insn, 0, 16), 16) *
			      2 +
		      4;
	} else if (Rs != 0 && Rs < Rt) {
		MCInst_setOpcode(MI, (Mips_BNEC_MMR6));
		MCOperand_CreateReg0(
			MI, (getReg(MI, Mips_GPR32RegClassID, Rs)));
		MCOperand_CreateReg0(
			MI, (getReg(MI, Mips_GPR32RegClassID, Rt)));
		Imm = SignExtend64(fieldFromInstruction_4(insn, 0, 16), 16) *
			      4 +
		      4;
	} else {
		MCInst_setOpcode(MI, (Mips_BNEZALC_MMR6));
		MCOperand_CreateReg0(
			MI, (getReg(MI, Mips_GPR32RegClassID, Rt)));
		Imm = SignExtend64(fieldFromInstruction_4(insn, 0, 16), 16) *
			      2 +
		      4;
	}

	MCOperand_CreateImm0(MI, (Imm));

	return MCDisassembler_Success;
}

static DecodeStatus DecodePOP65GroupBranchMMR6(MCInst *MI, uint32_t insn,
					       uint64_t Address,
					       const void *Decoder)
{
	// We have:
	//    0b110101 ttttt sssss iiiiiiiiiiiiiiii
	//      Invalid if rt == 0
	//      BGTZC_MMR6   if rs == 0  && rt != 0
	//      BLTZC_MMR6   if rs == rt && rt != 0
	//      BLTC_MMR6    if rs != rt && rs != 0  && rt != 0

	uint32_t Rt = fieldFromInstruction_4(insn, 21, 5);
	uint32_t Rs = fieldFromInstruction_4(insn, 16, 5);
	int64_t Imm =
		SignExtend64(fieldFromInstruction_4(insn, 0, 16), 16) * 4 + 4;
	bool HasRs = false;

	if (Rt == 0)
		return MCDisassembler_Fail;
	else if (Rs == 0)
		MCInst_setOpcode(MI, (Mips_BGTZC_MMR6));
	else if (Rs == Rt)
		MCInst_setOpcode(MI, (Mips_BLTZC_MMR6));
	else {
		MCInst_setOpcode(MI, (Mips_BLTC_MMR6));
		HasRs = true;
	}

	if (HasRs)
		MCOperand_CreateReg0(
			MI, (getReg(MI, Mips_GPR32RegClassID, Rs)));

	MCOperand_CreateReg0(MI, (getReg(MI, Mips_GPR32RegClassID, Rt)));

	MCOperand_CreateImm0(MI, (Imm));

	return MCDisassembler_Success;
}

static DecodeStatus DecodePOP75GroupBranchMMR6(MCInst *MI, uint32_t insn,
					       uint64_t Address,
					       const void *Decoder)
{
	// We have:
	//    0b111101 ttttt sssss iiiiiiiiiiiiiiii
	//      Invalid if rt == 0
	//      BLEZC_MMR6   if rs == 0  && rt != 0
	//      BGEZC_MMR6   if rs == rt && rt != 0
	//      BGEC_MMR6    if rs != rt && rs != 0  && rt != 0

	uint32_t Rt = fieldFromInstruction_4(insn, 21, 5);
	uint32_t Rs = fieldFromInstruction_4(insn, 16, 5);
	int64_t Imm =
		SignExtend64(fieldFromInstruction_4(insn, 0, 16), 16) * 4 + 4;
	bool HasRs = false;

	if (Rt == 0)
		return MCDisassembler_Fail;
	else if (Rs == 0)
		MCInst_setOpcode(MI, (Mips_BLEZC_MMR6));
	else if (Rs == Rt)
		MCInst_setOpcode(MI, (Mips_BGEZC_MMR6));
	else {
		HasRs = true;
		MCInst_setOpcode(MI, (Mips_BGEC_MMR6));
	}

	if (HasRs)
		MCOperand_CreateReg0(
			MI, (getReg(MI, Mips_GPR32RegClassID, Rs)));

	MCOperand_CreateReg0(MI, (getReg(MI, Mips_GPR32RegClassID, Rt)));

	MCOperand_CreateImm0(MI, (Imm));

	return MCDisassembler_Success;
}

static DecodeStatus DecodeBlezlGroupBranch(MCInst *MI, uint32_t insn,
					   uint64_t Address,
					   const void *Decoder)
{
	// If we are called then we can assume that MIPS32r6/MIPS64r6 is enabled
	// (otherwise we would have matched the BLEZL instruction from the earlier
	// ISA's instead).
	//
	// We have:
	//    0b010110 sssss ttttt iiiiiiiiiiiiiiii
	//      Invalid if rs == 0
	//      BLEZC   if rs == 0  && rt != 0
	//      BGEZC   if rs == rt && rt != 0
	//      BGEC    if rs != rt && rs != 0  && rt != 0

	uint32_t Rs = fieldFromInstruction_4(insn, 21, 5);
	uint32_t Rt = fieldFromInstruction_4(insn, 16, 5);
	int64_t Imm =
		SignExtend64(fieldFromInstruction_4(insn, 0, 16), 16) * 4 + 4;
	bool HasRs = false;

	if (Rt == 0)
		return MCDisassembler_Fail;
	else if (Rs == 0)
		MCInst_setOpcode(MI, (Mips_BLEZC));
	else if (Rs == Rt)
		MCInst_setOpcode(MI, (Mips_BGEZC));
	else {
		HasRs = true;
		MCInst_setOpcode(MI, (Mips_BGEC));
	}

	if (HasRs)
		MCOperand_CreateReg0(
			MI, (getReg(MI, Mips_GPR32RegClassID, Rs)));

	MCOperand_CreateReg0(MI, (getReg(MI, Mips_GPR32RegClassID, Rt)));

	MCOperand_CreateImm0(MI, (Imm));

	return MCDisassembler_Success;
}

static DecodeStatus DecodeBgtzlGroupBranch(MCInst *MI, uint32_t insn,
					   uint64_t Address,
					   const void *Decoder)
{
	// If we are called then we can assume that MIPS32r6/MIPS64r6 is enabled
	// (otherwise we would have matched the BGTZL instruction from the earlier
	// ISA's instead).
	//
	// We have:
	//    0b010111 sssss ttttt iiiiiiiiiiiiiiii
	//      Invalid if rs == 0
	//      BGTZC   if rs == 0  && rt != 0
	//      BLTZC   if rs == rt && rt != 0
	//      BLTC    if rs != rt && rs != 0  && rt != 0

	bool HasRs = false;

	uint32_t Rs = fieldFromInstruction_4(insn, 21, 5);
	uint32_t Rt = fieldFromInstruction_4(insn, 16, 5);
	int64_t Imm =
		SignExtend64(fieldFromInstruction_4(insn, 0, 16), 16) * 4 + 4;

	if (Rt == 0)
		return MCDisassembler_Fail;
	else if (Rs == 0)
		MCInst_setOpcode(MI, (Mips_BGTZC));
	else if (Rs == Rt)
		MCInst_setOpcode(MI, (Mips_BLTZC));
	else {
		MCInst_setOpcode(MI, (Mips_BLTC));
		HasRs = true;
	}

	if (HasRs)
		MCOperand_CreateReg0(
			MI, (getReg(MI, Mips_GPR32RegClassID, Rs)));

	MCOperand_CreateReg0(MI, (getReg(MI, Mips_GPR32RegClassID, Rt)));

	MCOperand_CreateImm0(MI, (Imm));

	return MCDisassembler_Success;
}

static DecodeStatus DecodeBgtzGroupBranch(MCInst *MI, uint32_t insn,
					  uint64_t Address, const void *Decoder)
{
	// If we are called then we can assume that MIPS32r6/MIPS64r6 is enabled
	// (otherwise we would have matched the BGTZ instruction from the earlier
	// ISA's instead).
	//
	// We have:
	//    0b000111 sssss ttttt iiiiiiiiiiiiiiii
	//      BGTZ    if rt == 0
	//      BGTZALC if rs == 0 && rt != 0
	//      BLTZALC if rs != 0 && rs == rt
	//      BLTUC   if rs != 0 && rs != rt

	uint32_t Rs = fieldFromInstruction_4(insn, 21, 5);
	uint32_t Rt = fieldFromInstruction_4(insn, 16, 5);
	int64_t Imm =
		SignExtend64(fieldFromInstruction_4(insn, 0, 16), 16) * 4 + 4;
	bool HasRs = false;
	bool HasRt = false;

	if (Rt == 0) {
		MCInst_setOpcode(MI, (Mips_BGTZ));
		HasRs = true;
	} else if (Rs == 0) {
		MCInst_setOpcode(MI, (Mips_BGTZALC));
		HasRt = true;
	} else if (Rs == Rt) {
		MCInst_setOpcode(MI, (Mips_BLTZALC));
		HasRs = true;
	} else {
		MCInst_setOpcode(MI, (Mips_BLTUC));
		HasRs = true;
		HasRt = true;
	}

	if (HasRs)
		MCOperand_CreateReg0(
			MI, (getReg(MI, Mips_GPR32RegClassID, Rs)));

	if (HasRt)
		MCOperand_CreateReg0(
			MI, (getReg(MI, Mips_GPR32RegClassID, Rt)));

	MCOperand_CreateImm0(MI, (Imm));

	return MCDisassembler_Success;
}

static DecodeStatus DecodeBlezGroupBranch(MCInst *MI, uint32_t insn,
					  uint64_t Address, const void *Decoder)
{
	// If we are called then we can assume that MIPS32r6/MIPS64r6 is enabled
	// (otherwise we would have matched the BLEZL instruction from the earlier
	// ISA's instead).
	//
	// We have:
	//    0b000110 sssss ttttt iiiiiiiiiiiiiiii
	//      Invalid   if rs == 0
	//      BLEZALC   if rs == 0  && rt != 0
	//      BGEZALC   if rs == rt && rt != 0
	//      BGEUC     if rs != rt && rs != 0  && rt != 0

	uint32_t Rs = fieldFromInstruction_4(insn, 21, 5);
	uint32_t Rt = fieldFromInstruction_4(insn, 16, 5);
	int64_t Imm =
		SignExtend64(fieldFromInstruction_4(insn, 0, 16), 16) * 4 + 4;
	bool HasRs = false;

	if (Rt == 0)
		return MCDisassembler_Fail;
	else if (Rs == 0)
		MCInst_setOpcode(MI, (Mips_BLEZALC));
	else if (Rs == Rt)
		MCInst_setOpcode(MI, (Mips_BGEZALC));
	else {
		HasRs = true;
		MCInst_setOpcode(MI, (Mips_BGEUC));
	}

	if (HasRs)
		MCOperand_CreateReg0(
			MI, (getReg(MI, Mips_GPR32RegClassID, Rs)));
	MCOperand_CreateReg0(MI, (getReg(MI, Mips_GPR32RegClassID, Rt)));

	MCOperand_CreateImm0(MI, (Imm));

	return MCDisassembler_Success;
}

// Override the generated disassembler to produce DEXT all the time. This is
// for feature / behaviour parity with  binutils.
static DecodeStatus DecodeDEXT(MCInst *MI, uint32_t Insn, uint64_t Address,
			       const void *Decoder)
{
	unsigned Msbd = fieldFromInstruction_4(Insn, 11, 5);
	unsigned Lsb = fieldFromInstruction_4(Insn, 6, 5);
	unsigned Size = 0;
	unsigned Pos = 0;

	switch (MCInst_getOpcode(MI)) {
	case Mips_DEXT:
		Pos = Lsb;
		Size = Msbd + 1;
		break;
	case Mips_DEXTM:
		Pos = Lsb;
		Size = Msbd + 1 + 32;
		break;
	case Mips_DEXTU:
		Pos = Lsb + 32;
		Size = Msbd + 1;
		break;
	default:
		CS_ASSERT_RET_VAL(0 && "Unknown DEXT instruction!", MCDisassembler_Fail);
	}

	MCInst_setOpcode(MI, (Mips_DEXT));

	uint32_t Rs = fieldFromInstruction_4(Insn, 21, 5);
	uint32_t Rt = fieldFromInstruction_4(Insn, 16, 5);

	MCOperand_CreateReg0(MI, (getReg(MI, Mips_GPR64RegClassID, Rt)));
	MCOperand_CreateReg0(MI, (getReg(MI, Mips_GPR64RegClassID, Rs)));
	MCOperand_CreateImm0(MI, (Pos));
	MCOperand_CreateImm0(MI, (Size));

	return MCDisassembler_Success;
}

// Override the generated disassembler to produce DINS all the time. This is
// for feature / behaviour parity with binutils.
static DecodeStatus DecodeDINS(MCInst *MI, uint32_t Insn, uint64_t Address,
			       const void *Decoder)
{
	unsigned Msbd = fieldFromInstruction_4(Insn, 11, 5);
	unsigned Lsb = fieldFromInstruction_4(Insn, 6, 5);
	unsigned Size = 0;
	unsigned Pos = 0;

	switch (MCInst_getOpcode(MI)) {
	case Mips_DINS:
		Pos = Lsb;
		Size = Msbd + 1 - Pos;
		break;
	case Mips_DINSM:
		Pos = Lsb;
		Size = Msbd + 33 - Pos;
		break;
	case Mips_DINSU:
		Pos = Lsb + 32;
		// mbsd = pos + size - 33
		// mbsd - pos + 33 = size
		Size = Msbd + 33 - Pos;
		break;
	default:
		CS_ASSERT_RET_VAL(0 && "Unknown DINS instruction!", MCDisassembler_Fail);
	}

	uint32_t Rs = fieldFromInstruction_4(Insn, 21, 5);
	uint32_t Rt = fieldFromInstruction_4(Insn, 16, 5);

	MCInst_setOpcode(MI, (Mips_DINS));
	MCOperand_CreateReg0(MI, (getReg(MI, Mips_GPR64RegClassID, Rt)));
	MCOperand_CreateReg0(MI, (getReg(MI, Mips_GPR64RegClassID, Rs)));
	MCOperand_CreateImm0(MI, (Pos));
	MCOperand_CreateImm0(MI, (Size));

	return MCDisassembler_Success;
}

// Auto-generated decoder wouldn't add the third operand for CRC32*.
static DecodeStatus DecodeCRC(MCInst *MI, uint32_t Insn, uint64_t Address,
			      const void *Decoder)
{
	uint32_t Rs = fieldFromInstruction_4(Insn, 21, 5);
	uint32_t Rt = fieldFromInstruction_4(Insn, 16, 5);
	MCOperand_CreateReg0(MI, (getReg(MI, Mips_GPR32RegClassID, Rt)));
	MCOperand_CreateReg0(MI, (getReg(MI, Mips_GPR32RegClassID, Rs)));
	MCOperand_CreateReg0(MI, (getReg(MI, Mips_GPR32RegClassID, Rt)));
	return MCDisassembler_Success;
}

/// Read two bytes from the ArrayRef and return 16 bit halfword sorted
/// according to the given endianness.
static DecodeStatus readInstruction16(const uint8_t *Bytes, size_t BytesLen,
				      uint64_t Address, uint64_t *Size,
				      uint64_t *Insn, bool IsBigEndian)
{
	// We want to read exactly 2 Bytes of data.
	if (BytesLen < 2) {
		*Size = 0;
		return MCDisassembler_Fail;
	}

	if (IsBigEndian) {
		*Insn = (Bytes[0] << 8) | Bytes[1];
	} else {
		*Insn = (Bytes[1] << 8) | Bytes[0];
	}

	return MCDisassembler_Success;
}

/// Read four bytes from the ArrayRef and return 32 bit word sorted
/// according to the given endianness.
static DecodeStatus readInstruction32(const uint8_t *Bytes, size_t BytesLen,
				      uint64_t Address, uint64_t *Size,
				      uint64_t *Insn, bool IsBigEndian,
				      bool IsMicroMips)
{
	// We want to read exactly 4 Bytes of data.
	if (BytesLen < 4) {
		*Size = 0;
		return MCDisassembler_Fail;
	}

	// High 16 bits of a 32-bit microMIPS instruction (where the opcode is)
	// always precede the low 16 bits in the instruction stream (that is, they
	// are placed at lower addresses in the instruction stream).
	//
	// microMIPS byte ordering:
	//   Big-endian:    0 | 1 | 2 | 3
	//   Little-endian: 1 | 0 | 3 | 2

	if (IsBigEndian) {
		// Encoded as a big-endian 32-bit word in the stream.
		*Insn = (Bytes[3] << 0) | (Bytes[2] << 8) | (Bytes[1] << 16) |
		       ((unsigned)Bytes[0] << 24);
	} else {
		if (IsMicroMips) {
			*Insn = (Bytes[2] << 0) | (Bytes[3] << 8) |
			       (Bytes[0] << 16) | ((unsigned)Bytes[1] << 24);
		} else {
			*Insn = (Bytes[0] << 0) | (Bytes[1] << 8) |
			       (Bytes[2] << 16) | ((unsigned)Bytes[3] << 24);
		}
	}

	return MCDisassembler_Success;
}

/// Read 6 bytes from the ArrayRef and return in a 64-bit bit word sorted
/// according to the given endianness and encoding byte-order.
static DecodeStatus readInstruction48(const uint8_t *Bytes, size_t BytesLen,
				      uint64_t Address, uint64_t *Size,
				      uint64_t *Insn, bool IsBigEndian,
				      bool IsNanoMips)
{
	// We want to read exactly 6 Bytes of little-endian data in nanoMIPS mode.
	if (BytesLen < 6 || IsBigEndian || !IsNanoMips) {
		*Size = 0;
		return MCDisassembler_Fail;
	}

	// High 16 bits of a 32-bit nanoMIPS instruction (where the opcode is)
	// always precede the low 16 bits in the instruction stream (that is, they
	// are placed at lower addresses in the instruction stream).
	//
	// nanoMIPS byte ordering:
	//   Little-endian: 1 | 0 | 3 | 2 | 5 | 4

	*Insn = (Bytes[0] << 0) | (Bytes[1] << 8);
	*Insn = ((*Insn << 32) | (Bytes[4] << 0) | (Bytes[5] << 8) |
		(Bytes[2] << 16) | ((unsigned)Bytes[3] << 24));
	return MCDisassembler_Success;
}

static DecodeStatus getInstruction(MCInst *Instr, uint64_t *Size, const uint8_t *Bytes,
			    size_t BytesLen, uint64_t Address, SStream *CStream)
{
	uint64_t Insn;
	DecodeStatus Result;
	*Size = 0;

	cs_mode mode = Instr->csh->mode;
	bool IsBigEndian = mode & CS_MODE_BIG_ENDIAN;
	bool IsMicroMips = Mips_getFeatureBits(mode, Mips_FeatureMicroMips);
	bool IsNanoMips = Mips_getFeatureBits(mode, Mips_FeatureNanoMips);
	bool IsMips32r6 = Mips_getFeatureBits(mode, Mips_FeatureMips32r6);
	bool IsMips64r6 = Mips_getFeatureBits(mode, Mips_FeatureMips64r6);
	bool IsMips2 = Mips_getFeatureBits(mode, Mips_FeatureMips2);
	bool IsMips16 = Mips_getFeatureBits(mode, Mips_FeatureMips16);
	bool IsCnMips = Mips_getFeatureBits(mode, Mips_FeatureCnMips);
	bool IsCnMipsP = Mips_getFeatureBits(mode, Mips_FeatureCnMipsP);
	bool IsFP64 = Mips_getFeatureBits(mode, Mips_FeatureFP64Bit);
	bool IsGP64 = Mips_getFeatureBits(mode, Mips_FeatureGP64Bit);
	bool IsPTR64 = Mips_getFeatureBits(mode, Mips_FeaturePTR64Bit);
	// Only present in MIPS-I and MIPS-II
	bool HasCOP3 = !Mips_getFeatureBits(mode, Mips_FeatureMips32) &&
					 !Mips_getFeatureBits(mode, Mips_FeatureMips3);

	if (IsNanoMips) {
		uint64_t Insn2;
		Result = readInstruction48(Bytes, BytesLen, Address, Size,
						&Insn2, IsBigEndian, IsNanoMips);
		if (Result != MCDisassembler_Fail) {
			// Calling the auto-generated decoder function.
			Result = decodeInstruction_8(DecoderTableNanoMips48,
						   Instr, Insn2, Address, NULL);
			if (Result != MCDisassembler_Fail) {
				*Size = 6;
				return Result;
			}
		}

		Result = readInstruction32(Bytes, BytesLen, Address, Size,
						&Insn, IsBigEndian, IsNanoMips);
		if (Result != MCDisassembler_Fail) {
			// Calling the auto-generated decoder function.
			Result = decodeInstruction_4(DecoderTableNanoMips32,
						     Instr, Insn, Address,
						     NULL);
			if (Result != MCDisassembler_Fail) {
				*Size = 4;
				return Result;
			}
		}

		Result = readInstruction16(Bytes, BytesLen, Address, Size,
						&Insn, IsBigEndian);
		if (Result != MCDisassembler_Fail) {
			// Calling the auto-generated decoder function for NanoMips
			// 16-bit instructions.
			Result = decodeInstruction_2(DecoderTableNanoMips16,
						     Instr, Insn, Address,
						     NULL);
			if (Result != MCDisassembler_Fail) {
				*Size = 2;
				return Result;
			}

			Result = decodeInstruction_2(DecoderTableNanoMips_Conflict_Space16,
						     Instr, Insn, Address,
						     NULL);
			if (Result != MCDisassembler_Fail) {
				*Size = 2;
				return Result;
			}
		}

		// This is an invalid instruction. Claim that the Size is 2 bytes. Since
		// nanoMIPS instructions have a minimum alignment of 2, the next 2 bytes
		// could form a valid instruction.
		*Size = 2;
		return MCDisassembler_Fail;
	}

	if (IsMicroMips) {
		Result = readInstruction16(Bytes, BytesLen, Address, Size,
					   &Insn, IsBigEndian);
		if (Result == MCDisassembler_Fail)
			return MCDisassembler_Fail;

		if (IsMips32r6) {
			// Calling the auto-generated decoder function for microMIPS32R6
			// 16-bit instructions.
			Result = decodeInstruction_2(DecoderTableMicroMipsR616,
						     Instr, Insn, Address,
						     NULL);
			if (Result != MCDisassembler_Fail) {
				*Size = 2;
				return Result;
			}
		}

		// Calling the auto-generated decoder function for microMIPS 16-bit
		// instructions.
		Result = decodeInstruction_2(DecoderTableMicroMips16, Instr,
					     Insn, Address, NULL);
		if (Result != MCDisassembler_Fail) {
			*Size = 2;
			return Result;
		}

		Result = readInstruction32(Bytes, BytesLen, Address, Size,
					   &Insn, IsBigEndian, IsMicroMips);
		if (Result == MCDisassembler_Fail)
			return MCDisassembler_Fail;

		if (IsMips32r6) {
			// Calling the auto-generated decoder function.
			Result = decodeInstruction_4(DecoderTableMicroMipsR6_Ambiguous32,
						     Instr, Insn, Address,
						     NULL);
			if (Result != MCDisassembler_Fail) {
				*Size = 4;
				return Result;
			}

			Result = decodeInstruction_4(DecoderTableMicroMipsR632,
						     Instr, Insn, Address,
						     NULL);
			if (Result != MCDisassembler_Fail) {
				*Size = 4;
				return Result;
			}
		}

		// Calling the auto-generated decoder function.
		Result = decodeInstruction_4(DecoderTableMicroMips32, Instr,
					     Insn, Address, NULL);
		if (Result != MCDisassembler_Fail) {
			*Size = 4;
			return Result;
		}

		Result = decodeInstruction_4(DecoderTableMicroMipsDSP32, Instr,
					     Insn, Address, NULL);
		if (Result != MCDisassembler_Fail) {
			*Size = 4;
			return Result;
		}

		if (IsFP64) {
			Result =
				decodeInstruction_4(DecoderTableMicroMipsFP6432,
						    Instr, Insn, Address, NULL);
			if (Result != MCDisassembler_Fail) {
				*Size = 4;
				return Result;
			}
		}

		// This is an invalid instruction. Claim that the Size is 2 bytes. Since
		// microMIPS instructions have a minimum alignment of 2, the next 2 bytes
		// could form a valid instruction. The two bytes we rejected as an
		// instruction could have actually beeen an inline constant pool that is
		// unconditionally branched over.
		*Size = 2;
		return MCDisassembler_Fail;
	}

	if (IsMips16) {
		Result = readInstruction32(Bytes, BytesLen, Address, Size,
					   &Insn, IsBigEndian, IsMicroMips);
		if (Result != MCDisassembler_Fail) {
			Result = decodeInstruction_4(DecoderTable32, Instr, Insn, Address,
						     NULL);
			if (Result != MCDisassembler_Fail) {
				*Size = 4;
				return Result;
			}
		}

		Result = readInstruction16(Bytes, BytesLen, Address, Size,
					   &Insn, IsBigEndian);
		if (Result == MCDisassembler_Fail) {
			return MCDisassembler_Fail;
		}

		*Size = 2;
		return decodeInstruction_2(DecoderTable16, Instr, Insn, Address, NULL);
	}

	// Attempt to read the instruction so that we can attempt to decode it. If
	// the buffer is not 4 bytes long, let the higher level logic figure out
	// what to do with a size of zero and MCDisassembler::Fail.
	Result = readInstruction32(Bytes, BytesLen, Address, Size, &Insn, IsBigEndian,
				   IsMicroMips);
	if (Result == MCDisassembler_Fail)
		return MCDisassembler_Fail;

	// The only instruction size for standard encoded MIPS.
	*Size = 4;

	if (HasCOP3) {
		Result = decodeInstruction_4(DecoderTableCOP3_32, Instr, Insn,
					     Address, NULL);
		if (Result != MCDisassembler_Fail)
			return Result;
	}

	if (IsMips32r6 && IsGP64) {
		Result = decodeInstruction_4(DecoderTableMips32r6_64r6_GP6432,
					     Instr, Insn, Address, NULL);
		if (Result != MCDisassembler_Fail)
			return Result;
	}

	if (IsMips32r6 && IsPTR64) {
		Result = decodeInstruction_4(DecoderTableMips32r6_64r6_PTR6432,
					     Instr, Insn, Address, NULL);
		if (Result != MCDisassembler_Fail)
			return Result;
	}

	if (IsMips32r6 || IsMips64r6) {
		Result = decodeInstruction_4(DecoderTableMips32r6_64r6_BranchZero32, Instr,
					     Insn, Address, NULL);
		if (Result != MCDisassembler_Fail)
			return Result;

		Result = decodeInstruction_4(DecoderTableMips32r6_64r632, Instr,
					     Insn, Address, NULL);
		if (Result != MCDisassembler_Fail)
			return Result;

		Result = decodeInstruction_4(DecoderTableMips32r6_64r6_Ambiguous32, Instr,
					     Insn, Address, NULL);
		if (Result != MCDisassembler_Fail)
			return Result;
	}

	if (IsMips2 && IsPTR64) {
		Result = decodeInstruction_4(DecoderTableMips32_64_PTR6432,
					     Instr, Insn, Address, NULL);
		if (Result != MCDisassembler_Fail)
			return Result;
	}

	if (IsCnMips) {
		Result = decodeInstruction_4(DecoderTableCnMips32, Instr, Insn,
					     Address, NULL);
		if (Result != MCDisassembler_Fail)
			return Result;
	}

	if (IsCnMipsP) {
		Result = decodeInstruction_4(DecoderTableCnMipsP32, Instr, Insn,
					     Address, NULL);
		if (Result != MCDisassembler_Fail)
			return Result;
	}

	if (IsGP64) {
		Result = decodeInstruction_4(DecoderTableMips6432, Instr, Insn,
					     Address, NULL);
		if (Result != MCDisassembler_Fail)
			return Result;
	}

	if (IsFP64) {
		Result = decodeInstruction_4(DecoderTableMipsFP6432, Instr,
					     Insn, Address, NULL);
		if (Result != MCDisassembler_Fail)
			return Result;
	}

	Result = decodeInstruction_4(DecoderTableMipsDSP32, Instr, Insn,
				     Address, NULL);
	if (Result != MCDisassembler_Fail)
		return Result;

	// Calling the auto-generated decoder function.
	Result = decodeInstruction_4(DecoderTableMips32, Instr, Insn, Address,
				     NULL);
	if (Result != MCDisassembler_Fail)
		return Result;

	return MCDisassembler_Fail;
}

static DecodeStatus DecodeCPU16RegsRegisterClass(MCInst *Inst, unsigned RegNo,
						 uint64_t Address,
						 const void *Decoder)
{
	if (RegNo > 7)
		return MCDisassembler_Fail;

	if (RegNo < 2)
		RegNo += 16;

	unsigned Reg = getReg(Inst, Mips_GPR32RegClassID, RegNo);
	MCOperand_CreateReg0(Inst, (Reg));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeGPR64RegisterClass(MCInst *Inst, unsigned RegNo,
					     uint64_t Address,
					     const void *Decoder)
{
	if (RegNo > 31)
		return MCDisassembler_Fail;

	unsigned Reg = getReg(Inst, Mips_GPR64RegClassID, RegNo);
	MCOperand_CreateReg0(Inst, (Reg));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeGPRMM16RegisterClass(MCInst *Inst, unsigned RegNo,
					       uint64_t Address,
					       const void *Decoder)
{
	if (RegNo > 7)
		return MCDisassembler_Fail;
	unsigned Reg = getReg(Inst, Mips_GPRMM16RegClassID, RegNo);
	MCOperand_CreateReg0(Inst, (Reg));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeGPRMM16ZeroRegisterClass(MCInst *Inst, unsigned RegNo,
						   uint64_t Address,
						   const void *Decoder)
{
	if (RegNo > 7)
		return MCDisassembler_Fail;
	unsigned Reg = getReg(Inst, Mips_GPRMM16ZeroRegClassID, RegNo);
	MCOperand_CreateReg0(Inst, (Reg));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeGPRMM16MovePRegisterClass(MCInst *Inst,
						    unsigned RegNo,
						    uint64_t Address,
						    const void *Decoder)
{
	if (RegNo > 7)
		return MCDisassembler_Fail;
	unsigned Reg = getReg(Inst, Mips_GPRMM16MovePRegClassID, RegNo);
	MCOperand_CreateReg0(Inst, (Reg));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeGPR32RegisterClass(MCInst *Inst, unsigned RegNo,
					     uint64_t Address,
					     const void *Decoder)
{
	if (RegNo > 31)
		return MCDisassembler_Fail;
	unsigned Reg = getReg(Inst, Mips_GPR32RegClassID, RegNo);
	MCOperand_CreateReg0(Inst, (Reg));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeGPRNM3RegisterClass(MCInst *Inst, unsigned RegNo,
					      uint64_t Address,
					      const void *Decoder)
{
	if (RegNo > 7)
		return MCDisassembler_Fail;
	RegNo |= ((RegNo & 0x4) ^ 0x4) << 2;
	unsigned Reg = getReg(Inst, Mips_GPRNM32RegClassID, RegNo);
	MCOperand_CreateReg0(Inst, (Reg));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeGPRNMRARegisterClass(MCInst *Inst, unsigned RegNo,
					       uint64_t Address,
					       const void *Decoder)
{
	MCOperand_CreateReg0(Inst, (Mips_RA_NM));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeGPRNM3ZRegisterClass(MCInst *Inst, unsigned RegNo,
					       uint64_t Address,
					       const void *Decoder)
{
	if (RegNo > 7)
		return MCDisassembler_Fail;
	if (RegNo != 0)
		RegNo |= ((RegNo & 0x4) ^ 0x4) << 2;
	unsigned Reg = getReg(Inst, Mips_GPRNM32RegClassID, RegNo);
	MCOperand_CreateReg0(Inst, (Reg));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeGPRNM4RegisterClass(MCInst *Inst, unsigned RegNo,
					      uint64_t Address,
					      const void *Decoder)
{
	if (RegNo > 31)
		return MCDisassembler_Fail;
	RegNo &= ~0x8;
	RegNo += (RegNo < 4 ? 8 : 0);
	unsigned Reg = getReg(Inst, Mips_GPRNM32RegClassID, RegNo);
	MCOperand_CreateReg0(Inst, (Reg));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeGPRNM4ZRegisterClass(MCInst *Inst, unsigned RegNo,
					       uint64_t Address,
					       const void *Decoder)
{
	if (RegNo > 31)
		return MCDisassembler_Fail;
	RegNo &= ~0x8;
	if (RegNo == 3)
		RegNo = 0;
	else
		RegNo += (RegNo < 3 ? 8 : 0);
	unsigned Reg = getReg(Inst, Mips_GPRNM32RegClassID, RegNo);
	MCOperand_CreateReg0(Inst, (Reg));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeGPRNM32NZRegisterClass(MCInst *Inst, unsigned RegNo,
						 uint64_t Address,
						 const void *Decoder)
{
	if (RegNo == 0)
		return MCDisassembler_Fail;
	unsigned Reg = getReg(Inst, Mips_GPRNM32RegClassID, RegNo);
	MCOperand_CreateReg0(Inst, (Reg));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeGPRNM32RegisterClass(MCInst *Inst, unsigned RegNo,
					       uint64_t Address,
					       const void *Decoder)
{
	if (RegNo > 31)
		return MCDisassembler_Fail;
	unsigned Reg = getReg(Inst, Mips_GPRNM32RegClassID, RegNo);
	MCOperand_CreateReg0(Inst, (Reg));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeGPRNM2R1RegisterClass(MCInst *Inst, unsigned RegNo,
						uint64_t Address,
						const void *Decoder)
{
	if (RegNo > 31)
		return MCDisassembler_Fail;
	RegNo += 4;
	unsigned Reg = getReg(Inst, Mips_GPRNM32RegClassID, RegNo);
	MCOperand_CreateReg0(Inst, (Reg));
	MCOperand_CreateReg0(Inst, (Reg + 1));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeGPRNM1R1RegisterClass(MCInst *Inst, unsigned RegNo,
						uint64_t Address,
						const void *Decoder)
{
	if (RegNo != 0 && RegNo != 1)
		return MCDisassembler_Fail;
	RegNo += 4;
	unsigned Reg = getReg(Inst, Mips_GPRNM32RegClassID, RegNo);
	MCOperand_CreateReg0(Inst, (Reg));
	return MCDisassembler_Success;
}

static DecodeStatus DecodePtrRegisterClass(MCInst *Inst, unsigned RegNo,
					   uint64_t Address,
					   const void *Decoder)
{
	if (Mips_getFeatureBits(Inst->csh->mode, Mips_FeatureGP64Bit))
		return DecodeGPR64RegisterClass(Inst, RegNo, Address, Decoder);

	return DecodeGPR32RegisterClass(Inst, RegNo, Address, Decoder);
}

static DecodeStatus DecodeDSPRRegisterClass(MCInst *Inst, unsigned RegNo,
					    uint64_t Address,
					    const void *Decoder)
{
	return DecodeGPR32RegisterClass(Inst, RegNo, Address, Decoder);
}

static DecodeStatus DecodeFGR64RegisterClass(MCInst *Inst, unsigned RegNo,
					     uint64_t Address,
					     const void *Decoder)
{
	if (RegNo > 31)
		return MCDisassembler_Fail;

	unsigned Reg = getReg(Inst, Mips_FGR64RegClassID, RegNo);
	MCOperand_CreateReg0(Inst, (Reg));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeFGR32RegisterClass(MCInst *Inst, unsigned RegNo,
					     uint64_t Address,
					     const void *Decoder)
{
	if (RegNo > 31)
		return MCDisassembler_Fail;

	unsigned Reg = getReg(Inst, Mips_FGR32RegClassID, RegNo);
	MCOperand_CreateReg0(Inst, (Reg));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeCCRRegisterClass(MCInst *Inst, unsigned RegNo,
					   uint64_t Address,
					   const void *Decoder)
{
	if (RegNo > 31)
		return MCDisassembler_Fail;
	unsigned Reg = getReg(Inst, Mips_CCRRegClassID, RegNo);
	MCOperand_CreateReg0(Inst, (Reg));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeFCCRegisterClass(MCInst *Inst, unsigned RegNo,
					   uint64_t Address,
					   const void *Decoder)
{
	if (RegNo > 7)
		return MCDisassembler_Fail;
	unsigned Reg = getReg(Inst, Mips_FCCRegClassID, RegNo);
	MCOperand_CreateReg0(Inst, (Reg));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeFGRCCRegisterClass(MCInst *Inst, unsigned RegNo,
					     uint64_t Address,
					     const void *Decoder)
{
	if (RegNo > 31)
		return MCDisassembler_Fail;

	unsigned Reg = getReg(Inst, Mips_FGRCCRegClassID, RegNo);
	MCOperand_CreateReg0(Inst, (Reg));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeMem(MCInst *Inst, uint32_t Insn, uint64_t Address,
			      const void *Decoder)
{
	int Offset = SignExtend32((Insn & 0xffff), 16);
	unsigned Reg = fieldFromInstruction_4(Insn, 16, 5);
	unsigned Base = fieldFromInstruction_4(Insn, 21, 5);

	Reg = getReg(Inst, Mips_GPR32RegClassID, Reg);
	Base = getReg(Inst, Mips_GPR32RegClassID, Base);

	if (MCInst_getOpcode(Inst) == Mips_SC ||
	    MCInst_getOpcode(Inst) == Mips_SCD)
		MCOperand_CreateReg0(Inst, (Reg));

	MCOperand_CreateReg0(Inst, (Reg));
	MCOperand_CreateReg0(Inst, (Base));
	MCOperand_CreateImm0(Inst, (Offset));

	return MCDisassembler_Success;
}

#define DEFINE_DecodeMemNM(Offbits, isSigned, rt) \
	static DecodeStatus CONCAT(DecodeMemNM, \
		CONCAT(Offbits, CONCAT(isSigned, rt)))( \
		MCInst * Inst, uint32_t Insn, uint64_t Address, \
		const void *Decoder) \
	{ \
		int Offset = (Insn & ((1 << Offbits) - 1)); \
		if (isSigned) \
			Offset = SignExtend32((Offset), Offbits); \
		unsigned Base; \
\
		switch (rt) { \
		case Mips_GPRNMGPRegClassID: \
		case Mips_GPRNMSPRegClassID: \
			Base = 0; \
			break; \
		case Mips_GPRNM3RegClassID: \
			Base = fieldFromInstruction_4(Insn, Offbits, 3); \
			break; \
		case Mips_GPRNM4RegClassID: \
		case Mips_GPRNM4ZRegClassID: \
\
			break; \
		default: \
			Base = fieldFromInstruction_4(Insn, Offbits, 5); \
		} \
		Base = getReg(Inst, rt, Base); \
\
		MCOperand_CreateReg0(Inst, (Base)); \
		MCOperand_CreateImm0(Inst, (Offset)); \
\
		return MCDisassembler_Success; \
	}
DEFINE_DecodeMemNM(6, 0, Mips_GPRNM3RegClassID);
DEFINE_DecodeMemNM(7, 0, Mips_GPRNMSPRegClassID);
DEFINE_DecodeMemNM(9, 0, Mips_GPRNMGPRegClassID);
DEFINE_DecodeMemNM(2, 0, Mips_GPRNM3RegClassID);
DEFINE_DecodeMemNM(3, 0, Mips_GPRNM3RegClassID);
DEFINE_DecodeMemNM(21, 0, Mips_GPRNMGPRegClassID);
DEFINE_DecodeMemNM(18, 0, Mips_GPRNMGPRegClassID);
DEFINE_DecodeMemNM(12, 0, Mips_GPRNM32RegClassID);
DEFINE_DecodeMemNM(9, 1, Mips_GPRNM32RegClassID);

static DecodeStatus DecodeMemZeroNM(MCInst *Inst, uint32_t Insn,
				    uint64_t Address, const void *Decoder)
{
	unsigned Base;
	Base = fieldFromInstruction_4(Insn, 0, 5);
	Base = getReg(Inst, Mips_GPRNM32RegClassID, Base);
	MCOperand_CreateReg0(Inst, (Base));

	return MCDisassembler_Success;
}

#define DEFINE_DecodeMemNMRX(RegClass) \
	static DecodeStatus CONCAT(DecodeMemNMRX, RegClass)( \
		MCInst * Inst, uint32_t Insn, uint64_t Address, \
		const void *Decoder) \
	{ \
		unsigned Offset; \
		unsigned Base; \
		Offset = fieldFromInstruction_4(Insn, 0, 5); \
		Base = fieldFromInstruction_4(Insn, 5, 5); \
\
		Base = getReg(Inst, RegClass, Base); \
		Offset = getReg(Inst, RegClass, Offset); \
		MCOperand_CreateReg0(Inst, (Base)); \
		MCOperand_CreateReg0(Inst, (Offset)); \
\
		return MCDisassembler_Success; \
	}
DEFINE_DecodeMemNMRX(Mips_GPRNM3RegClassID);
DEFINE_DecodeMemNMRX(Mips_GPRNM32RegClassID);

static DecodeStatus DecodeMemNM4x4(MCInst *Inst, uint32_t Insn,
				   uint64_t Address, const void *Decoder)
{
	int Offset = fieldFromInstruction_4(Insn, 0, 4);
	unsigned Base;

	Base = getReg(Inst, Mips_GPRNM32RegClassID,
		      fieldFromInstruction_4(Insn, 4, 5) & ~0x8);

	MCOperand_CreateReg0(Inst, (Base));
	MCOperand_CreateImm0(Inst, (Offset));

	return MCDisassembler_Success;
}

static DecodeStatus DecodeMemEVA(MCInst *Inst, uint32_t Insn, uint64_t Address,
				 const void *Decoder)
{
	int Offset = SignExtend32((Insn >> 7), 9);
	unsigned Reg = fieldFromInstruction_4(Insn, 16, 5);
	unsigned Base = fieldFromInstruction_4(Insn, 21, 5);

	Reg = getReg(Inst, Mips_GPR32RegClassID, Reg);
	Base = getReg(Inst, Mips_GPR32RegClassID, Base);

	if (MCInst_getOpcode(Inst) == Mips_SCE)
		MCOperand_CreateReg0(Inst, (Reg));

	MCOperand_CreateReg0(Inst, (Reg));
	MCOperand_CreateReg0(Inst, (Base));
	MCOperand_CreateImm0(Inst, (Offset));

	return MCDisassembler_Success;
}

#include "MipsCP0RegisterMap.h"

static DecodeStatus DecodeCOP0SelRegisterClass(MCInst *Inst, unsigned RegNo,
					       uint64_t Address,
					       const void *Decoder)
{
	int Reg = COP0Map_getEncIndexMap(RegNo);

	if (Reg != -1) {
		Reg = getReg(Inst, Mips_COP0SelRegClassID, Reg);
		MCOperand_CreateReg0(Inst, (Reg));
	} else {
		// Not a named register encoding - print numeric register and select value
		switch (MCInst_getOpcode(Inst)) {
		case Mips_MFC0Sel_NM:
			MCInst_setOpcode(Inst, (Mips_MFC0_NM));
			break;
		case Mips_MFHC0Sel_NM:
			MCInst_setOpcode(Inst, (Mips_MFHC0_NM));
			break;
		case Mips_MTC0Sel_NM:
			MCInst_setOpcode(Inst, (Mips_MTC0_NM));
			break;
		case Mips_MTHC0Sel_NM:
			MCInst_setOpcode(Inst, (Mips_MTHC0_NM));
			break;
		default:
			CS_ASSERT_RET_VAL(0 && "Unknown instruction!", MCDisassembler_Fail);
		}
		Reg = getReg(Inst, Mips_COP0RegClassID, RegNo >> 5);
		MCOperand_CreateReg0(Inst, (Reg));
		MCOperand_CreateImm0(Inst, (RegNo & 0x1f));
	}
	return MCDisassembler_Success;
}

static DecodeStatus DecodeLoadByte15(MCInst *Inst, uint32_t Insn,
				     uint64_t Address, const void *Decoder)
{
	int Offset = SignExtend32((Insn & 0xffff), 16);
	unsigned Base = fieldFromInstruction_4(Insn, 16, 5);
	unsigned Reg = fieldFromInstruction_4(Insn, 21, 5);

	Base = getReg(Inst, Mips_GPR32RegClassID, Base);
	Reg = getReg(Inst, Mips_GPR32RegClassID, Reg);

	MCOperand_CreateReg0(Inst, (Reg));
	MCOperand_CreateReg0(Inst, (Base));
	MCOperand_CreateImm0(Inst, (Offset));

	return MCDisassembler_Success;
}

static DecodeStatus DecodeCacheOp(MCInst *Inst, uint32_t Insn, uint64_t Address,
				  const void *Decoder)
{
	int Offset = SignExtend32((Insn & 0xffff), 16);
	unsigned Hint = fieldFromInstruction_4(Insn, 16, 5);
	unsigned Base = fieldFromInstruction_4(Insn, 21, 5);

	Base = getReg(Inst, Mips_GPR32RegClassID, Base);

	MCOperand_CreateReg0(Inst, (Base));
	MCOperand_CreateImm0(Inst, (Offset));
	MCOperand_CreateImm0(Inst, (Hint));

	return MCDisassembler_Success;
}

static DecodeStatus DecodeCacheOpMM(MCInst *Inst, uint32_t Insn,
				    uint64_t Address, const void *Decoder)
{
	int Offset = SignExtend32((Insn & 0xfff), 12);
	unsigned Base = fieldFromInstruction_4(Insn, 16, 5);
	unsigned Hint = fieldFromInstruction_4(Insn, 21, 5);

	Base = getReg(Inst, Mips_GPR32RegClassID, Base);

	MCOperand_CreateReg0(Inst, (Base));
	MCOperand_CreateImm0(Inst, (Offset));
	MCOperand_CreateImm0(Inst, (Hint));

	return MCDisassembler_Success;
}

static DecodeStatus DecodePrefeOpMM(MCInst *Inst, uint32_t Insn,
				    uint64_t Address, const void *Decoder)
{
	int Offset = SignExtend32((Insn & 0x1ff), 9);
	unsigned Base = fieldFromInstruction_4(Insn, 16, 5);
	unsigned Hint = fieldFromInstruction_4(Insn, 21, 5);

	Base = getReg(Inst, Mips_GPR32RegClassID, Base);

	MCOperand_CreateReg0(Inst, (Base));
	MCOperand_CreateImm0(Inst, (Offset));
	MCOperand_CreateImm0(Inst, (Hint));

	return MCDisassembler_Success;
}

static DecodeStatus DecodeCacheeOp_CacheOpR6(MCInst *Inst, uint32_t Insn,
					     uint64_t Address,
					     const void *Decoder)
{
	int Offset = SignExtend32((Insn >> 7), 9);
	unsigned Hint = fieldFromInstruction_4(Insn, 16, 5);
	unsigned Base = fieldFromInstruction_4(Insn, 21, 5);

	Base = getReg(Inst, Mips_GPR32RegClassID, Base);

	MCOperand_CreateReg0(Inst, (Base));
	MCOperand_CreateImm0(Inst, (Offset));
	MCOperand_CreateImm0(Inst, (Hint));

	return MCDisassembler_Success;
}

static DecodeStatus DecodeSyncI(MCInst *Inst, uint32_t Insn, uint64_t Address,
				const void *Decoder)
{
	int Offset = SignExtend32((Insn & 0xffff), 16);
	unsigned Base = fieldFromInstruction_4(Insn, 21, 5);

	Base = getReg(Inst, Mips_GPR32RegClassID, Base);

	MCOperand_CreateReg0(Inst, (Base));
	MCOperand_CreateImm0(Inst, (Offset));

	return MCDisassembler_Success;
}

static DecodeStatus DecodeSyncI_MM(MCInst *Inst, uint32_t Insn,
				   uint64_t Address, const void *Decoder)
{
	int Offset = SignExtend32((Insn & 0xffff), 16);
	unsigned Base = fieldFromInstruction_4(Insn, 16, 5);

	Base = getReg(Inst, Mips_GPR32RegClassID, Base);

	MCOperand_CreateReg0(Inst, (Base));
	MCOperand_CreateImm0(Inst, (Offset));

	return MCDisassembler_Success;
}

static DecodeStatus DecodeSynciR6(MCInst *Inst, uint32_t Insn, uint64_t Address,
				  const void *Decoder)
{
	int Immediate = SignExtend32((Insn & 0xffff), 16);
	unsigned Base = fieldFromInstruction_4(Insn, 16, 5);

	Base = getReg(Inst, Mips_GPR32RegClassID, Base);

	MCOperand_CreateReg0(Inst, (Base));
	MCOperand_CreateImm0(Inst, (Immediate));

	return MCDisassembler_Success;
}

static DecodeStatus DecodeMSA128Mem(MCInst *Inst, uint32_t Insn,
				    uint64_t Address, const void *Decoder)
{
	int Offset = SignExtend32((fieldFromInstruction_4(Insn, 16, 10)), 10);
	unsigned Reg = fieldFromInstruction_4(Insn, 6, 5);
	unsigned Base = fieldFromInstruction_4(Insn, 11, 5);

	Reg = getReg(Inst, Mips_MSA128BRegClassID, Reg);
	Base = getReg(Inst, Mips_GPR32RegClassID, Base);

	MCOperand_CreateReg0(Inst, (Reg));
	MCOperand_CreateReg0(Inst, (Base));

	// The immediate field of an LD/ST instruction is scaled which means it must
	// be multiplied (when decoding) by the size (in bytes) of the instructions'
	// data format.
	// .b - 1 byte
	// .h - 2 bytes
	// .w - 4 bytes
	// .d - 8 bytes
	switch (MCInst_getOpcode(Inst)) {
	default:

		return MCDisassembler_Fail;
		break;
	case Mips_LD_B:
	case Mips_ST_B:
		MCOperand_CreateImm0(Inst, (Offset));
		break;
	case Mips_LD_H:
	case Mips_ST_H:
		MCOperand_CreateImm0(Inst, (Offset * 2));
		break;
	case Mips_LD_W:
	case Mips_ST_W:
		MCOperand_CreateImm0(Inst, (Offset * 4));
		break;
	case Mips_LD_D:
	case Mips_ST_D:
		MCOperand_CreateImm0(Inst, (Offset * 8));
		break;
	}

	return MCDisassembler_Success;
}

static DecodeStatus DecodeMemMMImm4(MCInst *Inst, uint32_t Insn,
				    uint64_t Address, const void *Decoder)
{
	unsigned Offset = Insn & 0xf;
	unsigned Reg = fieldFromInstruction_4(Insn, 7, 3);
	unsigned Base = fieldFromInstruction_4(Insn, 4, 3);

	switch (MCInst_getOpcode(Inst)) {
	case Mips_LBU16_MM:
	case Mips_LHU16_MM:
	case Mips_LW16_MM:
		if (DecodeGPRMM16RegisterClass(Inst, Reg, Address, Decoder) ==
		    MCDisassembler_Fail)
			return MCDisassembler_Fail;
		break;
	case Mips_SB16_MM:
	case Mips_SB16_MMR6:
	case Mips_SH16_MM:
	case Mips_SH16_MMR6:
	case Mips_SW16_MM:
	case Mips_SW16_MMR6:
		if (DecodeGPRMM16ZeroRegisterClass(
			    Inst, Reg, Address, Decoder) == MCDisassembler_Fail)
			return MCDisassembler_Fail;
		break;
	}

	if (DecodeGPRMM16RegisterClass(Inst, Base, Address, Decoder) ==
	    MCDisassembler_Fail)
		return MCDisassembler_Fail;

	switch (MCInst_getOpcode(Inst)) {
	case Mips_LBU16_MM:
		if (Offset == 0xf)
			MCOperand_CreateImm0(Inst, (-1));
		else
			MCOperand_CreateImm0(Inst, (Offset));
		break;
	case Mips_SB16_MM:
	case Mips_SB16_MMR6:
		MCOperand_CreateImm0(Inst, (Offset));
		break;
	case Mips_LHU16_MM:
	case Mips_SH16_MM:
	case Mips_SH16_MMR6:
		MCOperand_CreateImm0(Inst, (Offset << 1));
		break;
	case Mips_LW16_MM:
	case Mips_SW16_MM:
	case Mips_SW16_MMR6:
		MCOperand_CreateImm0(Inst, (Offset << 2));
		break;
	}

	return MCDisassembler_Success;
}

static DecodeStatus DecodeMemMMSPImm5Lsl2(MCInst *Inst, uint32_t Insn,
					  uint64_t Address, const void *Decoder)
{
	unsigned Offset = Insn & 0x1F;
	unsigned Reg = fieldFromInstruction_4(Insn, 5, 5);

	Reg = getReg(Inst, Mips_GPR32RegClassID, Reg);

	MCOperand_CreateReg0(Inst, (Reg));
	MCOperand_CreateReg0(Inst, (Mips_SP));
	MCOperand_CreateImm0(Inst, (Offset << 2));

	return MCDisassembler_Success;
}

static DecodeStatus DecodeMemMMGPImm7Lsl2(MCInst *Inst, uint32_t Insn,
					  uint64_t Address, const void *Decoder)
{
	unsigned Offset = Insn & 0x7F;
	unsigned Reg = fieldFromInstruction_4(Insn, 7, 3);

	Reg = getReg(Inst, Mips_GPR32RegClassID, Reg);

	MCOperand_CreateReg0(Inst, (Reg));
	MCOperand_CreateReg0(Inst, (Mips_GP));
	MCOperand_CreateImm0(Inst, (Offset << 2));

	return MCDisassembler_Success;
}

static DecodeStatus DecodeMemMMReglistImm4Lsl2(MCInst *Inst, uint32_t Insn,
					       uint64_t Address,
					       const void *Decoder)
{
	int Offset;
	switch (MCInst_getOpcode(Inst)) {
	case Mips_LWM16_MMR6:
	case Mips_SWM16_MMR6:
		Offset = fieldFromInstruction_4(Insn, 4, 4);
		break;
	default:
		Offset = SignExtend32((Insn & 0xf), 4);
		break;
	}

	if (DecodeRegListOperand16(Inst, Insn, Address, Decoder) ==
	    MCDisassembler_Fail)
		return MCDisassembler_Fail;

	MCOperand_CreateReg0(Inst, (Mips_SP));
	MCOperand_CreateImm0(Inst, (Offset << 2));

	return MCDisassembler_Success;
}

static DecodeStatus DecodeMemMMImm9(MCInst *Inst, uint32_t Insn,
				    uint64_t Address, const void *Decoder)
{
	int Offset = SignExtend32((Insn & 0x1ff), 9);
	unsigned Reg = fieldFromInstruction_4(Insn, 21, 5);
	unsigned Base = fieldFromInstruction_4(Insn, 16, 5);

	Reg = getReg(Inst, Mips_GPR32RegClassID, Reg);
	Base = getReg(Inst, Mips_GPR32RegClassID, Base);

	if (MCInst_getOpcode(Inst) == Mips_SCE_MM ||
	    MCInst_getOpcode(Inst) == Mips_SC_MMR6)
		MCOperand_CreateReg0(Inst, (Reg));

	MCOperand_CreateReg0(Inst, (Reg));
	MCOperand_CreateReg0(Inst, (Base));
	MCOperand_CreateImm0(Inst, (Offset));

	return MCDisassembler_Success;
}

static DecodeStatus DecodeMemMMImm12(MCInst *Inst, uint32_t Insn,
				     uint64_t Address, const void *Decoder)
{
	int Offset = SignExtend32((Insn & 0x0fff), 12);
	unsigned Reg = fieldFromInstruction_4(Insn, 21, 5);
	unsigned Base = fieldFromInstruction_4(Insn, 16, 5);

	Reg = getReg(Inst, Mips_GPR32RegClassID, Reg);
	Base = getReg(Inst, Mips_GPR32RegClassID, Base);

	switch (MCInst_getOpcode(Inst)) {
	case Mips_SWM32_MM:
	case Mips_LWM32_MM:
		if (DecodeRegListOperand(Inst, Insn, Address, Decoder) ==
		    MCDisassembler_Fail)
			return MCDisassembler_Fail;
		MCOperand_CreateReg0(Inst, (Base));
		MCOperand_CreateImm0(Inst, (Offset));
		break;
	case Mips_SC_MM:
		MCOperand_CreateReg0(Inst, (Reg));
		// fall through
	default:
		MCOperand_CreateReg0(Inst, (Reg));
		if (MCInst_getOpcode(Inst) == Mips_LWP_MM ||
		    MCInst_getOpcode(Inst) == Mips_SWP_MM)
			MCOperand_CreateReg0(Inst, (Reg + 1));

		MCOperand_CreateReg0(Inst, (Base));
		MCOperand_CreateImm0(Inst, (Offset));
	}

	return MCDisassembler_Success;
}

static DecodeStatus DecodeMemMMImm16(MCInst *Inst, uint32_t Insn,
				     uint64_t Address, const void *Decoder)
{
	int Offset = SignExtend32((Insn & 0xffff), 16);
	unsigned Reg = fieldFromInstruction_4(Insn, 21, 5);
	unsigned Base = fieldFromInstruction_4(Insn, 16, 5);

	Reg = getReg(Inst, Mips_GPR32RegClassID, Reg);
	Base = getReg(Inst, Mips_GPR32RegClassID, Base);

	MCOperand_CreateReg0(Inst, (Reg));
	MCOperand_CreateReg0(Inst, (Base));
	MCOperand_CreateImm0(Inst, (Offset));

	return MCDisassembler_Success;
}

static DecodeStatus DecodeFMem(MCInst *Inst, uint32_t Insn, uint64_t Address,
			       const void *Decoder)
{
	int Offset = SignExtend32((Insn & 0xffff), 16);
	unsigned Reg = fieldFromInstruction_4(Insn, 16, 5);
	unsigned Base = fieldFromInstruction_4(Insn, 21, 5);

	Reg = getReg(Inst, Mips_FGR64RegClassID, Reg);
	Base = getReg(Inst, Mips_GPR32RegClassID, Base);

	MCOperand_CreateReg0(Inst, (Reg));
	MCOperand_CreateReg0(Inst, (Base));
	MCOperand_CreateImm0(Inst, (Offset));

	return MCDisassembler_Success;
}

static DecodeStatus DecodeFMemMMR2(MCInst *Inst, uint32_t Insn,
				   uint64_t Address, const void *Decoder)
{
	// This function is the same as DecodeFMem but with the Reg and Base fields
	// swapped according to microMIPS spec.
	int Offset = SignExtend32((Insn & 0xffff), 16);
	unsigned Base = fieldFromInstruction_4(Insn, 16, 5);
	unsigned Reg = fieldFromInstruction_4(Insn, 21, 5);

	Reg = getReg(Inst, Mips_FGR64RegClassID, Reg);
	Base = getReg(Inst, Mips_GPR32RegClassID, Base);

	MCOperand_CreateReg0(Inst, (Reg));
	MCOperand_CreateReg0(Inst, (Base));
	MCOperand_CreateImm0(Inst, (Offset));

	return MCDisassembler_Success;
}

static DecodeStatus DecodeFMem2(MCInst *Inst, uint32_t Insn, uint64_t Address,
				const void *Decoder)
{
	int Offset = SignExtend32((Insn & 0xffff), 16);
	unsigned Reg = fieldFromInstruction_4(Insn, 16, 5);
	unsigned Base = fieldFromInstruction_4(Insn, 21, 5);

	Reg = getReg(Inst, Mips_COP2RegClassID, Reg);
	Base = getReg(Inst, Mips_GPR32RegClassID, Base);

	MCOperand_CreateReg0(Inst, (Reg));
	MCOperand_CreateReg0(Inst, (Base));
	MCOperand_CreateImm0(Inst, (Offset));

	return MCDisassembler_Success;
}

static DecodeStatus DecodeFMem3(MCInst *Inst, uint32_t Insn, uint64_t Address,
				const void *Decoder)
{
	int Offset = SignExtend32((Insn & 0xffff), 16);
	unsigned Reg = fieldFromInstruction_4(Insn, 16, 5);
	unsigned Base = fieldFromInstruction_4(Insn, 21, 5);

	Reg = getReg(Inst, Mips_COP3RegClassID, Reg);
	Base = getReg(Inst, Mips_GPR32RegClassID, Base);

	MCOperand_CreateReg0(Inst, (Reg));
	MCOperand_CreateReg0(Inst, (Base));
	MCOperand_CreateImm0(Inst, (Offset));

	return MCDisassembler_Success;
}

static DecodeStatus DecodeFMemCop2R6(MCInst *Inst, uint32_t Insn,
				     uint64_t Address, const void *Decoder)
{
	int Offset = SignExtend32((Insn & 0x07ff), 11);
	unsigned Reg = fieldFromInstruction_4(Insn, 16, 5);
	unsigned Base = fieldFromInstruction_4(Insn, 11, 5);

	Reg = getReg(Inst, Mips_COP2RegClassID, Reg);
	Base = getReg(Inst, Mips_GPR32RegClassID, Base);

	MCOperand_CreateReg0(Inst, (Reg));
	MCOperand_CreateReg0(Inst, (Base));
	MCOperand_CreateImm0(Inst, (Offset));

	return MCDisassembler_Success;
}

static DecodeStatus DecodeFMemCop2MMR6(MCInst *Inst, uint32_t Insn,
				       uint64_t Address, const void *Decoder)
{
	int Offset = SignExtend32((Insn & 0x07ff), 11);
	unsigned Reg = fieldFromInstruction_4(Insn, 21, 5);
	unsigned Base = fieldFromInstruction_4(Insn, 16, 5);

	Reg = getReg(Inst, Mips_COP2RegClassID, Reg);
	Base = getReg(Inst, Mips_GPR32RegClassID, Base);

	MCOperand_CreateReg0(Inst, (Reg));
	MCOperand_CreateReg0(Inst, (Base));
	MCOperand_CreateImm0(Inst, (Offset));

	return MCDisassembler_Success;
}

static DecodeStatus DecodeSpecial3LlSc(MCInst *Inst, uint32_t Insn,
				       uint64_t Address, const void *Decoder)
{
	int64_t Offset = SignExtend64(((Insn >> 7) & 0x1ff), 9);
	unsigned Rt = fieldFromInstruction_4(Insn, 16, 5);
	unsigned Base = fieldFromInstruction_4(Insn, 21, 5);

	Rt = getReg(Inst, Mips_GPR32RegClassID, Rt);
	Base = getReg(Inst, Mips_GPR32RegClassID, Base);

	if (MCInst_getOpcode(Inst) == Mips_SC_R6 ||
	    MCInst_getOpcode(Inst) == Mips_SCD_R6) {
		MCOperand_CreateReg0(Inst, (Rt));
	}

	MCOperand_CreateReg0(Inst, (Rt));
	MCOperand_CreateReg0(Inst, (Base));
	MCOperand_CreateImm0(Inst, (Offset));

	return MCDisassembler_Success;
}

static DecodeStatus DecodeHWRegsRegisterClass(MCInst *Inst, unsigned RegNo,
					      uint64_t Address,
					      const void *Decoder)
{
	// Currently only hardware register 29 is supported.
	if (RegNo != 29)
		return MCDisassembler_Fail;
	MCOperand_CreateReg0(Inst, (Mips_HWR29));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeAFGR64RegisterClass(MCInst *Inst, unsigned RegNo,
					      uint64_t Address,
					      const void *Decoder)
{
	if (RegNo > 30 || RegNo % 2)
		return MCDisassembler_Fail;

	unsigned Reg = getReg(Inst, Mips_AFGR64RegClassID, RegNo / 2);
	MCOperand_CreateReg0(Inst, (Reg));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeACC64DSPRegisterClass(MCInst *Inst, unsigned RegNo,
						uint64_t Address,
						const void *Decoder)
{
	if (RegNo >= 4)
		return MCDisassembler_Fail;

	unsigned Reg = getReg(Inst, Mips_ACC64DSPRegClassID, RegNo);
	MCOperand_CreateReg0(Inst, (Reg));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeHI32DSPRegisterClass(MCInst *Inst, unsigned RegNo,
					       uint64_t Address,
					       const void *Decoder)
{
	if (RegNo >= 4)
		return MCDisassembler_Fail;

	unsigned Reg = getReg(Inst, Mips_HI32DSPRegClassID, RegNo);
	MCOperand_CreateReg0(Inst, (Reg));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeLO32DSPRegisterClass(MCInst *Inst, unsigned RegNo,
					       uint64_t Address,
					       const void *Decoder)
{
	if (RegNo >= 4)
		return MCDisassembler_Fail;

	unsigned Reg = getReg(Inst, Mips_LO32DSPRegClassID, RegNo);
	MCOperand_CreateReg0(Inst, (Reg));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeMSA128BRegisterClass(MCInst *Inst, unsigned RegNo,
					       uint64_t Address,
					       const void *Decoder)
{
	if (RegNo > 31)
		return MCDisassembler_Fail;

	unsigned Reg = getReg(Inst, Mips_MSA128BRegClassID, RegNo);
	MCOperand_CreateReg0(Inst, (Reg));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeMSA128HRegisterClass(MCInst *Inst, unsigned RegNo,
					       uint64_t Address,
					       const void *Decoder)
{
	if (RegNo > 31)
		return MCDisassembler_Fail;

	unsigned Reg = getReg(Inst, Mips_MSA128HRegClassID, RegNo);
	MCOperand_CreateReg0(Inst, (Reg));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeMSA128WRegisterClass(MCInst *Inst, unsigned RegNo,
					       uint64_t Address,
					       const void *Decoder)
{
	if (RegNo > 31)
		return MCDisassembler_Fail;

	unsigned Reg = getReg(Inst, Mips_MSA128WRegClassID, RegNo);
	MCOperand_CreateReg0(Inst, (Reg));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeMSA128DRegisterClass(MCInst *Inst, unsigned RegNo,
					       uint64_t Address,
					       const void *Decoder)
{
	if (RegNo > 31)
		return MCDisassembler_Fail;

	unsigned Reg = getReg(Inst, Mips_MSA128DRegClassID, RegNo);
	MCOperand_CreateReg0(Inst, (Reg));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeMSACtrlRegisterClass(MCInst *Inst, unsigned RegNo,
					       uint64_t Address,
					       const void *Decoder)
{
	if (RegNo > 7)
		return MCDisassembler_Fail;

	unsigned Reg = getReg(Inst, Mips_MSACtrlRegClassID, RegNo);
	MCOperand_CreateReg0(Inst, (Reg));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeCOP0RegisterClass(MCInst *Inst, unsigned RegNo,
					    uint64_t Address,
					    const void *Decoder)
{
	if (RegNo > 31)
		return MCDisassembler_Fail;

	unsigned Reg = getReg(Inst, Mips_COP0RegClassID, RegNo);
	MCOperand_CreateReg0(Inst, (Reg));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeCOP2RegisterClass(MCInst *Inst, unsigned RegNo,
					    uint64_t Address,
					    const void *Decoder)
{
	if (RegNo > 31)
		return MCDisassembler_Fail;

	unsigned Reg = getReg(Inst, Mips_COP2RegClassID, RegNo);
	MCOperand_CreateReg0(Inst, (Reg));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeBranchTarget(MCInst *Inst, unsigned Offset,
				       uint64_t Address, const void *Decoder)
{
	int32_t BranchOffset = (SignExtend32((Offset), 16) * 4) + 4;
	MCOperand_CreateImm0(Inst, (BranchOffset));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeBranchTarget1SImm16(MCInst *Inst, unsigned Offset,
					      uint64_t Address,
					      const void *Decoder)
{
	int32_t BranchOffset = (SignExtend32((Offset), 16) * 2);
	MCOperand_CreateImm0(Inst, (BranchOffset));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeJumpTarget(MCInst *Inst, uint32_t Insn,
				     uint64_t Address, const void *Decoder)
{
	unsigned JumpOffset = fieldFromInstruction_4(Insn, 0, 26) << 2;
	MCOperand_CreateImm0(Inst, (JumpOffset));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeBranchTarget21(MCInst *Inst, unsigned Offset,
					 uint64_t Address, const void *Decoder)
{
	int32_t BranchOffset = SignExtend32((Offset), 21) * 4 + 4;

	MCOperand_CreateImm0(Inst, (BranchOffset));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeBranchTarget21MM(MCInst *Inst, unsigned Offset,
					   uint64_t Address,
					   const void *Decoder)
{
	int32_t BranchOffset = SignExtend32((Offset), 21) * 4 + 4;

	MCOperand_CreateImm0(Inst, (BranchOffset));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeBranchTarget26(MCInst *Inst, unsigned Offset,
					 uint64_t Address, const void *Decoder)
{
	int32_t BranchOffset = SignExtend32((Offset), 26) * 4 + 4;

	MCOperand_CreateImm0(Inst, (BranchOffset));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeBranchTarget7MM(MCInst *Inst, unsigned Offset,
					  uint64_t Address, const void *Decoder)
{
	int32_t BranchOffset = SignExtend32((Offset << 1), 8);
	MCOperand_CreateImm0(Inst, (BranchOffset));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeBranchTarget10MM(MCInst *Inst, unsigned Offset,
					   uint64_t Address,
					   const void *Decoder)
{
	int32_t BranchOffset = SignExtend32((Offset << 1), 11);
	MCOperand_CreateImm0(Inst, (BranchOffset));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeBranchTargetMM(MCInst *Inst, unsigned Offset,
					 uint64_t Address, const void *Decoder)
{
	int32_t BranchOffset = SignExtend32((Offset), 16) * 2 + 4;
	MCOperand_CreateImm0(Inst, (BranchOffset));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeBranchTarget26MM(MCInst *Inst, unsigned Offset,
					   uint64_t Address,
					   const void *Decoder)
{
	int32_t BranchOffset = SignExtend32((Offset << 1), 27);

	MCOperand_CreateImm0(Inst, (BranchOffset));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeJumpTargetMM(MCInst *Inst, uint32_t Insn,
				       uint64_t Address, const void *Decoder)
{
	unsigned JumpOffset = fieldFromInstruction_4(Insn, 0, 26) << 1;
	MCOperand_CreateImm0(Inst, (JumpOffset));
	return MCDisassembler_Success;
}

#define DEFINE_DecodeBranchTargetNM(Bits) \
	static DecodeStatus CONCAT(DecodeBranchTargetNM, Bits)( \
		MCInst * Inst, unsigned Offset, uint64_t Address, \
		const void *Decoder) \
	{ \
		uint32_t InsnSize = (Bits <= 10) ? 2 : 4; \
		int32_t BranchOffset = \
			SignExtend32((Offset), Bits + 1) + InsnSize; \
\
		MCOperand_CreateImm0(Inst, (BranchOffset)); \
		return MCDisassembler_Success; \
	}
DEFINE_DecodeBranchTargetNM(10);
DEFINE_DecodeBranchTargetNM(7);
DEFINE_DecodeBranchTargetNM(21);
DEFINE_DecodeBranchTargetNM(25);
DEFINE_DecodeBranchTargetNM(14);
DEFINE_DecodeBranchTargetNM(11);
DEFINE_DecodeBranchTargetNM(5);

static DecodeStatus DecodeJumpTargetXMM(MCInst *Inst, uint32_t Insn,
					uint64_t Address, const void *Decoder)
{
	unsigned JumpOffset = fieldFromInstruction_4(Insn, 0, 26) << 2;
	MCOperand_CreateImm0(Inst, (JumpOffset));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeAddiur2Simm7(MCInst *Inst, unsigned Value,
				       uint64_t Address, const void *Decoder)
{
	if (Value == 0)
		MCOperand_CreateImm0(Inst, (1));
	else if (Value == 0x7)
		MCOperand_CreateImm0(Inst, (-1));
	else
		MCOperand_CreateImm0(Inst, (Value << 2));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeLi16Imm(MCInst *Inst, unsigned Value,
				  uint64_t Address, const void *Decoder)
{
	if (Value == 0x7F)
		MCOperand_CreateImm0(Inst, (-1));
	else
		MCOperand_CreateImm0(Inst, (Value));
	return MCDisassembler_Success;
}

static DecodeStatus DecodePOOL16BEncodedField(MCInst *Inst, unsigned Value,
					      uint64_t Address,
					      const void *Decoder)
{
	MCOperand_CreateImm0(Inst, (Value == 0x0 ? 8 : Value));
	return MCDisassembler_Success;
}

#define DEFINE_DecodeUImmWithOffsetAndScale(Bits, Offset, Scale) \
	static DecodeStatus CONCAT(DecodeUImmWithOffsetAndScale, \
				   CONCAT(Bits, CONCAT(Offset, Scale)))( \
		MCInst * Inst, unsigned Value, uint64_t Address, \
		const void *Decoder) \
	{ \
		Value &= ((1 << Bits) - 1); \
		Value *= Scale; \
		MCOperand_CreateImm0(Inst, (Value + Offset)); \
		return MCDisassembler_Success; \
	}
DEFINE_DecodeUImmWithOffsetAndScale(5, 0, 4);
DEFINE_DecodeUImmWithOffsetAndScale(6, 0, 4);
DEFINE_DecodeUImmWithOffsetAndScale(2, 1, 1);
DEFINE_DecodeUImmWithOffsetAndScale(5, 1, 1);
DEFINE_DecodeUImmWithOffsetAndScale(8, 0, 1);
DEFINE_DecodeUImmWithOffsetAndScale(18, 0, 1);
DEFINE_DecodeUImmWithOffsetAndScale(21, 0, 1);

#define DEFINE_DecodeSImmWithOffsetAndScale(Bits, Offset, ScaleBy) \
	static DecodeStatus CONCAT(DecodeSImmWithOffsetAndScale, \
				   CONCAT(Bits, CONCAT(Offset, ScaleBy)))( \
		MCInst * Inst, unsigned Value, uint64_t Address, \
		const void *Decoder) \
	{ \
		int32_t Imm = SignExtend32((Value), Bits) * ScaleBy; \
		MCOperand_CreateImm0(Inst, (Imm + Offset)); \
		return MCDisassembler_Success; \
	}

#define DEFINE_DecodeSImmWithOffsetAndScale_2(Bits, Offset) DEFINE_DecodeSImmWithOffsetAndScale(Bits, Offset, 1)
#define DEFINE_DecodeSImmWithOffsetAndScale_3(Bits) DEFINE_DecodeSImmWithOffsetAndScale(Bits, 0, 1)

DEFINE_DecodeSImmWithOffsetAndScale_3(16);
DEFINE_DecodeSImmWithOffsetAndScale_3(10);
DEFINE_DecodeSImmWithOffsetAndScale_3(4);
DEFINE_DecodeSImmWithOffsetAndScale_3(6);
DEFINE_DecodeSImmWithOffsetAndScale_3(32);

static DecodeStatus DecodeInsSize(MCInst *Inst, uint32_t Insn, uint64_t Address,
				  const void *Decoder)
{
	// First we need to grab the pos(lsb) from MCInst.
	// This function only handles the 32 bit variants of ins, as dins
	// variants are handled differently.
	int Pos = MCOperand_getImm(MCInst_getOperand(Inst, (2)));
	int Size = (int)Insn - Pos + 1;
	MCOperand_CreateImm0(Inst, (SignExtend32((Size), 16)));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeSimm19Lsl2(MCInst *Inst, uint32_t Insn,
				     uint64_t Address, const void *Decoder)
{
	MCOperand_CreateImm0(Inst, (SignExtend32((Insn), 19) * 4));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeSimm18Lsl3(MCInst *Inst, uint32_t Insn,
				     uint64_t Address, const void *Decoder)
{
	MCOperand_CreateImm0(Inst, (SignExtend32((Insn), 18) * 8));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeSimm9SP(MCInst *Inst, uint32_t Insn, uint64_t Address,
				  const void *Decoder)
{
	int32_t DecodedValue;
	switch (Insn) {
	case 0:
		DecodedValue = 256;
		break;
	case 1:
		DecodedValue = 257;
		break;
	case 510:
		DecodedValue = -258;
		break;
	case 511:
		DecodedValue = -257;
		break;
	default:
		DecodedValue = SignExtend32((Insn), 9);
		break;
	}
	MCOperand_CreateImm0(Inst, (DecodedValue * 4));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeANDI16Imm(MCInst *Inst, uint32_t Insn,
				    uint64_t Address, const void *Decoder)
{
	// Insn must be >= 0, since it is unsigned that condition is always true.

	int32_t DecodedValues[] = { 128, 1,  2,	 3,  4,	 7,   8,     15,
				    16,	 31, 32, 63, 64, 255, 32768, 65535 };
	MCOperand_CreateImm0(Inst, (DecodedValues[Insn]));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeRegListOperand(MCInst *Inst, uint32_t Insn,
					 uint64_t Address, const void *Decoder)
{
	unsigned Regs[] = { Mips_S0, Mips_S1, Mips_S2, Mips_S3, Mips_S4,
			    Mips_S5, Mips_S6, Mips_S7, Mips_FP };
	unsigned RegNum;

	unsigned RegLst = fieldFromInstruction_4(Insn, 21, 5);

	// Empty register lists are not allowed.
	if (RegLst == 0)
		return MCDisassembler_Fail;

	RegNum = RegLst & 0xf;

	// RegLst values 10-15, and 26-31 are reserved.
	if (RegNum > 9)
		return MCDisassembler_Fail;

	for (unsigned i = 0; i < RegNum; i++)
		MCOperand_CreateReg0(Inst, (Regs[i]));

	if (RegLst & 0x10)
		MCOperand_CreateReg0(Inst, (Mips_RA));

	return MCDisassembler_Success;
}

static DecodeStatus DecodeRegListOperand16(MCInst *Inst, uint32_t Insn,
					   uint64_t Address,
					   const void *Decoder)
{
	unsigned Regs[] = { Mips_S0, Mips_S1, Mips_S2, Mips_S3 };
	unsigned RegLst;
	switch (MCInst_getOpcode(Inst)) {
	default:
		RegLst = fieldFromInstruction_4(Insn, 4, 2);
		break;
	case Mips_LWM16_MMR6:
	case Mips_SWM16_MMR6:
		RegLst = fieldFromInstruction_4(Insn, 8, 2);
		break;
	}
	unsigned RegNum = RegLst & 0x3;

	for (unsigned i = 0; i <= RegNum; i++)
		MCOperand_CreateReg0(Inst, (Regs[i]));

	MCOperand_CreateReg0(Inst, (Mips_RA));

	return MCDisassembler_Success;
}

static DecodeStatus DecodeMovePOperands(MCInst *Inst, uint32_t Insn,
					uint64_t Address, const void *Decoder)
{
	unsigned RegPair = fieldFromInstruction_4(Insn, 7, 3);
	if (DecodeMovePRegPair(Inst, RegPair, Address, Decoder) ==
	    MCDisassembler_Fail)
		return MCDisassembler_Fail;

	unsigned RegRs;
	if (Inst->csh->mode & CS_MODE_MIPS32R6)
		RegRs = fieldFromInstruction_4(Insn, 0, 2) |
			(fieldFromInstruction_4(Insn, 3, 1) << 2);
	else
		RegRs = fieldFromInstruction_4(Insn, 1, 3);
	if (DecodeGPRMM16MovePRegisterClass(Inst, RegRs, Address, Decoder) ==
	    MCDisassembler_Fail)
		return MCDisassembler_Fail;

	unsigned RegRt = fieldFromInstruction_4(Insn, 4, 3);
	if (DecodeGPRMM16MovePRegisterClass(Inst, RegRt, Address, Decoder) ==
	    MCDisassembler_Fail)
		return MCDisassembler_Fail;

	return MCDisassembler_Success;
}

static DecodeStatus DecodeMovePRegPair(MCInst *Inst, unsigned RegPair,
				       uint64_t Address, const void *Decoder)
{
	switch (RegPair) {
	default:
		return MCDisassembler_Fail;
	case 0:
		MCOperand_CreateReg0(Inst, (Mips_A1));
		MCOperand_CreateReg0(Inst, (Mips_A2));
		break;
	case 1:
		MCOperand_CreateReg0(Inst, (Mips_A1));
		MCOperand_CreateReg0(Inst, (Mips_A3));
		break;
	case 2:
		MCOperand_CreateReg0(Inst, (Mips_A2));
		MCOperand_CreateReg0(Inst, (Mips_A3));
		break;
	case 3:
		MCOperand_CreateReg0(Inst, (Mips_A0));
		MCOperand_CreateReg0(Inst, (Mips_S5));
		break;
	case 4:
		MCOperand_CreateReg0(Inst, (Mips_A0));
		MCOperand_CreateReg0(Inst, (Mips_S6));
		break;
	case 5:
		MCOperand_CreateReg0(Inst, (Mips_A0));
		MCOperand_CreateReg0(Inst, (Mips_A1));
		break;
	case 6:
		MCOperand_CreateReg0(Inst, (Mips_A0));
		MCOperand_CreateReg0(Inst, (Mips_A2));
		break;
	case 7:
		MCOperand_CreateReg0(Inst, (Mips_A0));
		MCOperand_CreateReg0(Inst, (Mips_A3));
		break;
	}

	return MCDisassembler_Success;
}

static DecodeStatus DecodeSimm23Lsl2(MCInst *Inst, uint32_t Insn,
				     uint64_t Address, const void *Decoder)
{
	MCOperand_CreateImm0(Inst, (SignExtend32((Insn << 2), 25)));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeBgtzGroupBranchMMR6(MCInst *MI, uint32_t insn,
					      uint64_t Address,
					      const void *Decoder)
{
	// We have:
	//    0b000111 ttttt sssss iiiiiiiiiiiiiiii
	//      Invalid      if rt == 0
	//      BGTZALC_MMR6 if rs == 0 && rt != 0
	//      BLTZALC_MMR6 if rs != 0 && rs == rt
	//      BLTUC_MMR6   if rs != 0 && rs != rt

	uint32_t Rt = fieldFromInstruction_4(insn, 21, 5);
	uint32_t Rs = fieldFromInstruction_4(insn, 16, 5);
	uint32_t Imm = 0;
	bool HasRs = false;
	bool HasRt = false;

	if (Rt == 0)
		return MCDisassembler_Fail;
	else if (Rs == 0) {
		MCInst_setOpcode(MI, (Mips_BGTZALC_MMR6));
		HasRt = true;
		Imm = SignExtend64(fieldFromInstruction_4(insn, 0, 16), 16) *
			      2 +
		      4;
	} else if (Rs == Rt) {
		MCInst_setOpcode(MI, (Mips_BLTZALC_MMR6));
		HasRs = true;
		Imm = SignExtend64(fieldFromInstruction_4(insn, 0, 16), 16) *
			      2 +
		      4;
	} else {
		MCInst_setOpcode(MI, (Mips_BLTUC_MMR6));
		HasRs = true;
		HasRt = true;
		Imm = SignExtend64(fieldFromInstruction_4(insn, 0, 16), 16) *
			      4 +
		      4;
	}

	if (HasRs)
		MCOperand_CreateReg0(
			MI, (getReg(MI, Mips_GPR32RegClassID, Rs)));

	if (HasRt)
		MCOperand_CreateReg0(
			MI, (getReg(MI, Mips_GPR32RegClassID, Rt)));

	MCOperand_CreateImm0(MI, (Imm));

	return MCDisassembler_Success;
}

static DecodeStatus DecodeBlezGroupBranchMMR6(MCInst *MI, uint32_t insn,
					      uint64_t Address,
					      const void *Decoder)
{
	// We have:
	//    0b000110 ttttt sssss iiiiiiiiiiiiiiii
	//      Invalid        if rt == 0
	//      BLEZALC_MMR6   if rs == 0  && rt != 0
	//      BGEZALC_MMR6   if rs == rt && rt != 0
	//      BGEUC_MMR6     if rs != rt && rs != 0  && rt != 0

	uint32_t Rt = fieldFromInstruction_4(insn, 21, 5);
	uint32_t Rs = fieldFromInstruction_4(insn, 16, 5);
	uint32_t Imm = 0;
	bool HasRs = false;

	if (Rt == 0)
		return MCDisassembler_Fail;
	else if (Rs == 0) {
		MCInst_setOpcode(MI, (Mips_BLEZALC_MMR6));
		Imm = SignExtend64(fieldFromInstruction_4(insn, 0, 16), 16) *
			      2 +
		      4;
	} else if (Rs == Rt) {
		MCInst_setOpcode(MI, (Mips_BGEZALC_MMR6));
		Imm = SignExtend64(fieldFromInstruction_4(insn, 0, 16), 16) *
			      2 +
		      4;
	} else {
		HasRs = true;
		MCInst_setOpcode(MI, (Mips_BGEUC_MMR6));
		Imm = SignExtend64(fieldFromInstruction_4(insn, 0, 16), 16) *
			      4 +
		      4;
	}

	if (HasRs)
		MCOperand_CreateReg0(
			MI, (getReg(MI, Mips_GPR32RegClassID, Rs)));
	MCOperand_CreateReg0(MI, (getReg(MI, Mips_GPR32RegClassID, Rt)));

	MCOperand_CreateImm0(MI, (Imm));

	return MCDisassembler_Success;
}

// This instruction does not have a working decoder, and needs to be
// fixed. This "fixme" function was introduced to keep the backend compiling,
// while making changes to tablegen code.
static DecodeStatus DecodeFIXMEInstruction(MCInst *Inst, uint32_t Insn,
					   uint64_t Address,
					   const void *Decoder)
{
	return MCDisassembler_Fail;
}

static DecodeStatus DecodeImmM1To126(MCInst *Inst, unsigned Value,
				     uint64_t Address, const void *Decoder)
{
	if (Value == 127)
		MCOperand_CreateImm0(Inst, (-1));
	else
		MCOperand_CreateImm0(Inst, (Value));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeUImm4Mask(MCInst *Inst, unsigned Value,
				    uint64_t Address, const void *Decoder)
{
	if (Value == 12)
		MCOperand_CreateImm0(Inst, (0xff));
	else if (Value == 13)
		MCOperand_CreateImm0(Inst, (0xffff));
	else
		MCOperand_CreateImm0(Inst, (Value));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeUImm3Shift(MCInst *Inst, unsigned Value,
				     uint64_t Address, const void *Decoder)
{
	if (Value == 0)
		MCOperand_CreateImm0(Inst, (8));
	else
		MCOperand_CreateImm0(Inst, (Value));
	return MCDisassembler_Success;
}

static DecodeStatus DecodeNMRegListOperand(MCInst *Inst, uint32_t Insn,
					   uint64_t Address,
					   const void *Decoder)
{
	unsigned RegStart = fieldFromInstruction_4(Insn, 5, 5);
	unsigned RegCount = fieldFromInstruction_4(Insn, 1, 4);
	unsigned GP_bit = fieldFromInstruction_4(Insn, 0, 1);
	unsigned i;
	unsigned RegNo;

	MCOperand_CreateReg0(
		Inst, (getReg(Inst, Mips_GPRNM32RegClassID, RegStart)));
	for (i = RegStart + 1; i < RegStart + RegCount; i++) {
		if (i == RegStart + RegCount - 1 && GP_bit)
			RegNo = 28;
		else if (i > 31)
			RegNo = 16 + (i % 32); // $ra+1 wraps to $s0
		else
			RegNo = i;
		MCOperand_CreateReg0(
			Inst, (getReg(Inst, Mips_GPRNM32RegClassID, RegNo)));
	}
	return MCDisassembler_Success;
}

static DecodeStatus DecodeNMRegList16Operand(MCInst *Inst, uint32_t Insn,
					     uint64_t Address,
					     const void *Decoder)
{
	unsigned RegStart = 30 + fieldFromInstruction_4(Insn, 4, 1);
	unsigned RegCount = fieldFromInstruction_4(Insn, 0, 4);
	// Re-encode the parameters for 32-bit instruction operand
	// and call it's decoder
	return DecodeNMRegListOperand(Inst, (RegStart << 5) | (RegCount << 1),
				      Address, Decoder);
}

static DecodeStatus DecodeNegImm12(MCInst *Inst, uint32_t Insn,
				   uint64_t Address, const void *Decoder)
{
	int Imm = fieldFromInstruction_4(Insn, 0, 12);

	MCOperand_CreateImm0(Inst, (-Imm));
	return MCDisassembler_Success;
}

#define DEFINE_DecodeSImmWithReg(Bits, Offset, Scale, RegNum) \
	static DecodeStatus CONCAT( \
		DecodeSImmWithReg, \
		CONCAT(Bits, CONCAT(Offset, CONCAT(Scale, RegNum))))( \
		MCInst * Inst, unsigned Value, uint64_t Address, \
		const void *Decoder) \
	{ \
		MCOperand_CreateReg0(Inst, (RegNum)); \
		return CONCAT(DecodeSImmWithOffsetAndScale, \
			      CONCAT(Bits, CONCAT(Offset, Scale)))( \
			Inst, Value, Address, Decoder); \
	}
DEFINE_DecodeSImmWithReg(32, 0, 1, Mips_GP_NM);

#define DEFINE_DecodeUImmWithReg(Bits, Offset, Scale, RegNum) \
	static DecodeStatus CONCAT( \
		DecodeUImmWithReg, \
		CONCAT(Bits, CONCAT(Offset, CONCAT(Scale, RegNum))))( \
		MCInst * Inst, unsigned Value, uint64_t Address, \
		const void *Decoder) \
	{ \
		MCOperand_CreateReg0(Inst, (RegNum)); \
		return CONCAT(DecodeUImmWithOffsetAndScale, \
			      CONCAT(Bits, CONCAT(Offset, Scale)))( \
			Inst, Value, Address, Decoder); \
	}
DEFINE_DecodeUImmWithReg(8, 0, 1, Mips_SP_NM);
DEFINE_DecodeUImmWithReg(21, 0, 1, Mips_GP_NM);
DEFINE_DecodeUImmWithReg(18, 0, 1, Mips_GP_NM);

static DecodeStatus DecodeSImm32s12(MCInst *Inst, uint32_t Insn,
				    uint64_t Address, const void *Decoder)
{
	uint64_t Imm = ((uint64_t)Insn) << 12;
	MCOperand_CreateImm0(Inst, (Imm));
	return MCDisassembler_Success;
}

#define DEFINE_DecodeAddressPCRelNM(Bits) \
	static DecodeStatus CONCAT(DecodeAddressPCRelNM, Bits)( \
		MCInst * Inst, unsigned Offset, uint64_t Address, \
		const void *Decoder) \
	{ \
		uint32_t InsnSize = Bits == 32 ? 6 : 4; \
		int32_t BranchOffset = \
			SignExtend32((Offset), Bits) + InsnSize; \
\
		MCOperand_CreateImm0(Inst, (BranchOffset)); \
		return MCDisassembler_Success; \
	}
DEFINE_DecodeAddressPCRelNM(22);
DEFINE_DecodeAddressPCRelNM(32);

static DecodeStatus DecodeBranchConflictNM(MCInst *Inst, uint32_t Insn,
					   uint64_t Address,
					   const void *Decoder)
{
	unsigned Rt = fieldFromInstruction_4(Insn, 7, 3);
	unsigned Rs = fieldFromInstruction_4(Insn, 4, 3);
	unsigned Offset = fieldFromInstruction_4(Insn, 0, 4) << 1;
	if (Rs < Rt)
		MCInst_setOpcode(Inst, (Mips_BEQC16_NM));
	else
		MCInst_setOpcode(Inst, (Mips_BNEC16_NM));
	if (DecodeGPRNM3RegisterClass(Inst, Rt, Address, Decoder) ==
		    MCDisassembler_Success &&
	    DecodeGPRNM3RegisterClass(Inst, Rs, Address, Decoder) ==
		    MCDisassembler_Success)
		return CONCAT(DecodeBranchTargetNM, 5)(Inst, Offset, Address,
						       Decoder);
	else
		return MCDisassembler_Fail;
}

DecodeStatus Mips_LLVM_getInstruction(MCInst *Instr, uint64_t *Size, const uint8_t *Bytes,
			    size_t BytesLen, uint64_t Address, SStream *CStream)
{
	return getInstruction(Instr, Size, Bytes, BytesLen, Address, CStream);
}
