// Copyright 2012 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// A Disassembler object is used to disassemble a block of code instruction by
// instruction. The default implementation of the NameConverter object can be
// overriden to modify register names or to do symbol lookup on addresses.
//
// The example below will disassemble a block of code and print it to stdout.
//
//   NameConverter converter;
//   Disassembler d(converter);
//   for (byte* pc = begin; pc < end;) {
//     v8::base::EmbeddedVector<char, 256> buffer;
//     byte* prev_pc = pc;
//     pc += d.InstructionDecode(buffer, pc);
//     printf("%p    %08x      %s\n",
//            prev_pc, *reinterpret_cast<int32_t*>(prev_pc), buffer);
//   }
//
// The Disassembler class also has a convenience method to disassemble a block
// of code into a FILE*, meaning that the above functionality could also be
// achieved by just calling Disassembler::Disassemble(stdout, begin, end);

#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>

#if V8_TARGET_ARCH_MIPS

#include "src/base/platform/platform.h"
#include "src/base/strings.h"
#include "src/base/vector.h"
#include "src/codegen/macro-assembler.h"
#include "src/codegen/mips/constants-mips.h"
#include "src/diagnostics/disasm.h"

namespace v8 {
namespace internal {

//------------------------------------------------------------------------------

// Decoder decodes and disassembles instructions into an output buffer.
// It uses the converter to convert register names and call destinations into
// more informative description.
class Decoder {
 public:
  Decoder(const disasm::NameConverter& converter,
          v8::base::Vector<char> out_buffer)
      : converter_(converter), out_buffer_(out_buffer), out_buffer_pos_(0) {
    out_buffer_[out_buffer_pos_] = '\0';
  }

  ~Decoder() {}

  Decoder(const Decoder&) = delete;
  Decoder& operator=(const Decoder&) = delete;

  // Writes one disassembled instruction into 'buffer' (0-terminated).
  // Returns the length of the disassembled machine instruction in bytes.
  int InstructionDecode(byte* instruction);

 private:
  // Bottleneck functions to print into the out_buffer.
  void PrintChar(const char ch);
  void Print(const char* str);

  // Printing of common values.
  void PrintRegister(int reg);
  void PrintFPURegister(int freg);
  void PrintMSARegister(int wreg);
  void PrintFPUStatusRegister(int freg);
  void PrintMSAControlRegister(int creg);
  void PrintRs(Instruction* instr);
  void PrintRt(Instruction* instr);
  void PrintRd(Instruction* instr);
  void PrintFs(Instruction* instr);
  void PrintFt(Instruction* instr);
  void PrintFd(Instruction* instr);
  void PrintSa(Instruction* instr);
  void PrintLsaSa(Instruction* instr);
  void PrintSd(Instruction* instr);
  void PrintSs1(Instruction* instr);
  void PrintSs2(Instruction* instr);
  void PrintBc(Instruction* instr);
  void PrintCc(Instruction* instr);
  void PrintBp2(Instruction* instr);
  void PrintFunction(Instruction* instr);
  void PrintSecondaryField(Instruction* instr);
  void PrintUImm9(Instruction* instr);
  void PrintSImm9(Instruction* instr);
  void PrintUImm16(Instruction* instr);
  void PrintSImm16(Instruction* instr);
  void PrintXImm16(Instruction* instr);
  void PrintPCImm16(Instruction* instr, int delta_pc, int n_bits);
  void PrintXImm18(Instruction* instr);
  void PrintSImm18(Instruction* instr);
  void PrintXImm19(Instruction* instr);
  void PrintSImm19(Instruction* instr);
  void PrintXImm21(Instruction* instr);
  void PrintSImm21(Instruction* instr);
  void PrintPCImm21(Instruction* instr, int delta_pc, int n_bits);
  void PrintXImm26(Instruction* instr);
  void PrintSImm26(Instruction* instr);
  void PrintPCImm26(Instruction* instr, int delta_pc, int n_bits);
  void PrintPCImm26(Instruction* instr);
  void PrintCode(Instruction* instr);    // For break and trap instructions.
  void PrintFormat(Instruction* instr);  // For floating format postfix.
  void PrintMsaDataFormat(Instruction* instr);
  void PrintMsaXImm8(Instruction* instr);
  void PrintMsaImm8(Instruction* instr);
  void PrintMsaImm5(Instruction* instr);
  void PrintMsaSImm5(Instruction* instr);
  void PrintMsaSImm10(Instruction* instr, bool is_mi10 = false);
  void PrintMsaImmBit(Instruction* instr);
  void PrintMsaImmElm(Instruction* instr);
  void PrintMsaCopy(Instruction* instr);
  // Printing of instruction name.
  void PrintInstructionName(Instruction* instr);

  // Handle formatting of instructions and their options.
  int FormatRegister(Instruction* instr, const char* option);
  int FormatFPURegister(Instruction* instr, const char* option);
  int FormatMSARegister(Instruction* instr, const char* option);
  int FormatOption(Instruction* instr, const char* option);
  void Format(Instruction* instr, const char* format);
  void Unknown(Instruction* instr);

  // Each of these functions decodes one particular instruction type.
  bool DecodeTypeRegisterRsType(Instruction* instr);
  void DecodeTypeRegisterSRsType(Instruction* instr);
  void DecodeTypeRegisterDRsType(Instruction* instr);
  void DecodeTypeRegisterLRsType(Instruction* instr);
  void DecodeTypeRegisterWRsType(Instruction* instr);
  void DecodeTypeRegisterSPECIAL(Instruction* instr);
  void DecodeTypeRegisterSPECIAL2(Instruction* instr);
  void DecodeTypeRegisterSPECIAL3(Instruction* instr);
  void DecodeTypeRegister(Instruction* instr);
  void DecodeTypeImmediate(Instruction* instr);
  void DecodeTypeImmediateSPECIAL3(Instruction* instr);
  void DecodeTypeJump(Instruction* instr);
  void DecodeTypeMsaI8(Instruction* instr);
  void DecodeTypeMsaI5(Instruction* instr);
  void DecodeTypeMsaI10(Instruction* instr);
  void DecodeTypeMsaELM(Instruction* instr);
  void DecodeTypeMsaBIT(Instruction* instr);
  void DecodeTypeMsaMI10(Instruction* instr);
  void DecodeTypeMsa3R(Instruction* instr);
  void DecodeTypeMsa3RF(Instruction* instr);
  void DecodeTypeMsaVec(Instruction* instr);
  void DecodeTypeMsa2R(Instruction* instr);
  void DecodeTypeMsa2RF(Instruction* instr);

  const disasm::NameConverter& converter_;
  v8::base::Vector<char> out_buffer_;
  int out_buffer_pos_;
};

// Support for assertions in the Decoder formatting functions.
#define STRING_STARTS_WITH(string, compare_string) \
  (strncmp(string, compare_string, strlen(compare_string)) == 0)

// Append the ch to the output buffer.
void Decoder::PrintChar(const char ch) { out_buffer_[out_buffer_pos_++] = ch; }

// Append the str to the output buffer.
void Decoder::Print(const char* str) {
  char cur = *str++;
  while (cur != '\0' && (out_buffer_pos_ < (out_buffer_.length() - 1))) {
    PrintChar(cur);
    cur = *str++;
  }
  out_buffer_[out_buffer_pos_] = 0;
}

// Print the register name according to the active name converter.
void Decoder::PrintRegister(int reg) {
  Print(converter_.NameOfCPURegister(reg));
}

void Decoder::PrintRs(Instruction* instr) {
  int reg = instr->RsValue();
  PrintRegister(reg);
}

void Decoder::PrintRt(Instruction* instr) {
  int reg = instr->RtValue();
  PrintRegister(reg);
}

void Decoder::PrintRd(Instruction* instr) {
  int reg = instr->RdValue();
  PrintRegister(reg);
}

// Print the FPUregister name according to the active name converter.
void Decoder::PrintFPURegister(int freg) {
  Print(converter_.NameOfXMMRegister(freg));
}

void Decoder::PrintMSARegister(int wreg) { Print(MSARegisters::Name(wreg)); }

void Decoder::PrintFPUStatusRegister(int freg) {
  switch (freg) {
    case kFCSRRegister:
      Print("FCSR");
      break;
    default:
      Print(converter_.NameOfXMMRegister(freg));
  }
}

void Decoder::PrintMSAControlRegister(int creg) {
  switch (creg) {
    case kMSAIRRegister:
      Print("MSAIR");
      break;
    case kMSACSRRegister:
      Print("MSACSR");
      break;
    default:
      Print("no_msacreg");
  }
}

void Decoder::PrintFs(Instruction* instr) {
  int freg = instr->RsValue();
  PrintFPURegister(freg);
}

void Decoder::PrintFt(Instruction* instr) {
  int freg = instr->RtValue();
  PrintFPURegister(freg);
}

void Decoder::PrintFd(Instruction* instr) {
  int freg = instr->RdValue();
  PrintFPURegister(freg);
}

// Print the integer value of the sa field.
void Decoder::PrintSa(Instruction* instr) {
  int sa = instr->SaValue();
  out_buffer_pos_ += base::SNPrintF(out_buffer_ + out_buffer_pos_, "%d", sa);
}

// Print the integer value of the sa field of a lsa instruction.
void Decoder::PrintLsaSa(Instruction* instr) {
  int sa = instr->LsaSaValue() + 1;
  out_buffer_pos_ += base::SNPrintF(out_buffer_ + out_buffer_pos_, "%d", sa);
}

// Print the integer value of the rd field, when it is not used as reg.
void Decoder::PrintSd(Instruction* instr) {
  int sd = instr->RdValue();
  out_buffer_pos_ += base::SNPrintF(out_buffer_ + out_buffer_pos_, "%d", sd);
}

// Print the integer value of the rd field, when used as 'ext' size.
void Decoder::PrintSs1(Instruction* instr) {
  int ss = instr->RdValue();
  out_buffer_pos_ +=
      base::SNPrintF(out_buffer_ + out_buffer_pos_, "%d", ss + 1);
}

// Print the integer value of the rd field, when used as 'ins' size.
void Decoder::PrintSs2(Instruction* instr) {
  int ss = instr->RdValue();
  int pos = instr->SaValue();
  out_buffer_pos_ +=
      base::SNPrintF(out_buffer_ + out_buffer_pos_, "%d", ss - pos + 1);
}

// Print the integer value of the cc field for the bc1t/f instructions.
void Decoder::PrintBc(Instruction* instr) {
  int cc = instr->FBccValue();
  out_buffer_pos_ += base::SNPrintF(out_buffer_ + out_buffer_pos_, "%d", cc);
}

// Print the integer value of the cc field for the FP compare instructions.
void Decoder::PrintCc(Instruction* instr) {
  int cc = instr->FCccValue();
  out_buffer_pos_ +=
      base::SNPrintF(out_buffer_ + out_buffer_pos_, "cc(%d)", cc);
}

void Decoder::PrintBp2(Instruction* instr) {
  int bp2 = instr->Bp2Value();
  out_buffer_pos_ += base::SNPrintF(out_buffer_ + out_buffer_pos_, "%d", bp2);
}

// Print 9-bit unsigned immediate value.
void Decoder::PrintUImm9(Instruction* instr) {
  int32_t imm = instr->Imm9Value();
  out_buffer_pos_ += base::SNPrintF(out_buffer_ + out_buffer_pos_, "%u", imm);
}

// Print 9-bit signed immediate value.
void Decoder::PrintSImm9(Instruction* instr) {
  int32_t imm = ((instr->Imm9Value()) << 23) >> 23;
  out_buffer_pos_ += base::SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm);
}

// Print 16-bit unsigned immediate value.
void Decoder::PrintUImm16(Instruction* instr) {
  int32_t imm = instr->Imm16Value();
  out_buffer_pos_ += base::SNPrintF(out_buffer_ + out_buffer_pos_, "%u", imm);
}

// Print 16-bit signed immediate value.
void Decoder::PrintSImm16(Instruction* instr) {
  int32_t imm = ((instr->Imm16Value()) << 16) >> 16;
  out_buffer_pos_ += base::SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm);
}

// Print 16-bit hexa immediate value.
void Decoder::PrintXImm16(Instruction* instr) {
  int32_t imm = instr->Imm16Value();
  out_buffer_pos_ += base::SNPrintF(out_buffer_ + out_buffer_pos_, "0x%x", imm);
}

// Print absoulte address for 16-bit offset or immediate value.
// The absolute address is calculated according following expression:
//      PC + delta_pc + (offset << n_bits)
void Decoder::PrintPCImm16(Instruction* instr, int delta_pc, int n_bits) {
  int16_t offset = instr->Imm16Value();
  out_buffer_pos_ +=
      base::SNPrintF(out_buffer_ + out_buffer_pos_, "%s",
                     converter_.NameOfAddress(reinterpret_cast<byte*>(instr) +
                                              delta_pc + (offset << n_bits)));
}

// Print 18-bit signed immediate value.
void Decoder::PrintSImm18(Instruction* instr) {
  int32_t imm =
      ((instr->Imm18Value()) << (32 - kImm18Bits)) >> (32 - kImm18Bits);
  out_buffer_pos_ += base::SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm);
}

// Print 18-bit hexa immediate value.
void Decoder::PrintXImm18(Instruction* instr) {
  int32_t imm = instr->Imm18Value();
  out_buffer_pos_ += base::SNPrintF(out_buffer_ + out_buffer_pos_, "0x%x", imm);
}

// Print 19-bit hexa immediate value.
void Decoder::PrintXImm19(Instruction* instr) {
  int32_t imm = instr->Imm19Value();
  out_buffer_pos_ += base::SNPrintF(out_buffer_ + out_buffer_pos_, "0x%x", imm);
}

// Print 19-bit signed immediate value.
void Decoder::PrintSImm19(Instruction* instr) {
  int32_t imm19 = instr->Imm19Value();
  // set sign
  imm19 <<= (32 - kImm19Bits);
  imm19 >>= (32 - kImm19Bits);
  out_buffer_pos_ += base::SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm19);
}

// Print 21-bit immediate value.
void Decoder::PrintXImm21(Instruction* instr) {
  uint32_t imm = instr->Imm21Value();
  out_buffer_pos_ += base::SNPrintF(out_buffer_ + out_buffer_pos_, "0x%x", imm);
}

// Print 21-bit signed immediate value.
void Decoder::PrintSImm21(Instruction* instr) {
  int32_t imm21 = instr->Imm21Value();
  // set sign
  imm21 <<= (32 - kImm21Bits);
  imm21 >>= (32 - kImm21Bits);
  out_buffer_pos_ += base::SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm21);
}

// Print absoulte address for 21-bit offset or immediate value.
// The absolute address is calculated according following expression:
//      PC + delta_pc + (offset << n_bits)
void Decoder::PrintPCImm21(Instruction* instr, int delta_pc, int n_bits) {
  int32_t imm21 = instr->Imm21Value();
  // set sign
  imm21 <<= (32 - kImm21Bits);
  imm21 >>= (32 - kImm21Bits);
  out_buffer_pos_ +=
      base::SNPrintF(out_buffer_ + out_buffer_pos_, "%s",
                     converter_.NameOfAddress(reinterpret_cast<byte*>(instr) +
                                              delta_pc + (imm21 << n_bits)));
}

// Print 26-bit hex immediate value.
void Decoder::PrintXImm26(Instruction* instr) {
  uint32_t target = static_cast<uint32_t>(instr->Imm26Value())
                    << kImmFieldShift;
  target = (reinterpret_cast<uint32_t>(instr) & ~0xFFFFFFF) | target;
  out_buffer_pos_ +=
      base::SNPrintF(out_buffer_ + out_buffer_pos_, "0x%x", target);
}

// Print 26-bit signed immediate value.
void Decoder::PrintSImm26(Instruction* instr) {
  int32_t imm26 = instr->Imm26Value();
  // set sign
  imm26 <<= (32 - kImm26Bits);
  imm26 >>= (32 - kImm26Bits);
  out_buffer_pos_ += base::SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm26);
}

// Print absoulte address for 26-bit offset or immediate value.
// The absolute address is calculated according following expression:
//      PC + delta_pc + (offset << n_bits)
void Decoder::PrintPCImm26(Instruction* instr, int delta_pc, int n_bits) {
  int32_t imm26 = instr->Imm26Value();
  // set sign
  imm26 <<= (32 - kImm26Bits);
  imm26 >>= (32 - kImm26Bits);
  out_buffer_pos_ +=
      base::SNPrintF(out_buffer_ + out_buffer_pos_, "%s",
                     converter_.NameOfAddress(reinterpret_cast<byte*>(instr) +
                                              delta_pc + (imm26 << n_bits)));
}

// Print absoulte address for 26-bit offset or immediate value.
// The absolute address is calculated according following expression:
//      PC[GPRLEN-1 .. 28] || instr_index26 || 00
void Decoder::PrintPCImm26(Instruction* instr) {
  int32_t imm26 = instr->Imm26Value();
  uint32_t pc_mask = ~0xFFFFFFF;
  uint32_t pc = ((uint32_t)(instr + 1) & pc_mask) | (imm26 << 2);
  out_buffer_pos_ +=
      base::SNPrintF(out_buffer_ + out_buffer_pos_, "%s",
                     converter_.NameOfAddress((reinterpret_cast<byte*>(pc))));
}

// Print 26-bit immediate value.
void Decoder::PrintCode(Instruction* instr) {
  if (instr->OpcodeFieldRaw() != SPECIAL)
    return;  // Not a break or trap instruction.
  switch (instr->FunctionFieldRaw()) {
    case BREAK: {
      int32_t code = instr->Bits(25, 6);
      out_buffer_pos_ += base::SNPrintF(out_buffer_ + out_buffer_pos_,
                                        "0x%05x (%d)", code, code);
      break;
    }
    case TGE:
    case TGEU:
    case TLT:
    case TLTU:
    case TEQ:
    case TNE: {
      int32_t code = instr->Bits(15, 6);
      out_buffer_pos_ +=
          base::SNPrintF(out_buffer_ + out_buffer_pos_, "0x%03x", code);
      break;
    }
    default:  // Not a break or trap instruction.
      break;
  }
}

void Decoder::PrintMsaXImm8(Instruction* instr) {
  int32_t imm = instr->MsaImm8Value();
  out_buffer_pos_ += base::SNPrintF(out_buffer_ + out_buffer_pos_, "0x%x", imm);
}

void Decoder::PrintMsaImm8(Instruction* instr) {
  int32_t imm = instr->MsaImm8Value();
  out_buffer_pos_ += base::SNPrintF(out_buffer_ + out_buffer_pos_, "%u", imm);
}

void Decoder::PrintMsaImm5(Instruction* instr) {
  int32_t imm = instr->MsaImm5Value();
  out_buffer_pos_ += base::SNPrintF(out_buffer_ + out_buffer_pos_, "%u", imm);
}

void Decoder::PrintMsaSImm5(Instruction* instr) {
  int32_t imm = instr->MsaImm5Value();
  imm <<= (32 - kMsaImm5Bits);
  imm >>= (32 - kMsaImm5Bits);
  out_buffer_pos_ += base::SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm);
}

void Decoder::PrintMsaSImm10(Instruction* instr, bool is_mi10) {
  int32_t imm = is_mi10 ? instr->MsaImmMI10Value() : instr->MsaImm10Value();
  imm <<= (32 - kMsaImm10Bits);
  imm >>= (32 - kMsaImm10Bits);
  out_buffer_pos_ += base::SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm);
}

void Decoder::PrintMsaImmBit(Instruction* instr) {
  int32_t m = instr->MsaBitMValue();
  out_buffer_pos_ += base::SNPrintF(out_buffer_ + out_buffer_pos_, "%u", m);
}

void Decoder::PrintMsaImmElm(Instruction* instr) {
  int32_t n = instr->MsaElmNValue();
  out_buffer_pos_ += base::SNPrintF(out_buffer_ + out_buffer_pos_, "%u", n);
}

void Decoder::PrintMsaCopy(Instruction* instr) {
  int32_t rd = instr->WdValue();
  int32_t ws = instr->WsValue();
  int32_t n = instr->MsaElmNValue();
  out_buffer_pos_ += base::SNPrintF(out_buffer_ + out_buffer_pos_, "%s, %s[%u]",
                                    converter_.NameOfCPURegister(rd),
                                    MSARegisters::Name(ws), n);
}

void Decoder::PrintFormat(Instruction* instr) {
  char formatLetter = ' ';
  switch (instr->RsFieldRaw()) {
    case S:
      formatLetter = 's';
      break;
    case D:
      formatLetter = 'd';
      break;
    case W:
      formatLetter = 'w';
      break;
    case L:
      formatLetter = 'l';
      break;
    default:
      UNREACHABLE();
  }
  PrintChar(formatLetter);
}

void Decoder::PrintMsaDataFormat(Instruction* instr) {
  DCHECK(instr->IsMSAInstr());
  char df = ' ';
  if (instr->IsMSABranchInstr()) {
    switch (instr->RsFieldRaw()) {
      case BZ_V:
      case BNZ_V:
        df = 'v';
        break;
      case BZ_B:
      case BNZ_B:
        df = 'b';
        break;
      case BZ_H:
      case BNZ_H:
        df = 'h';
        break;
      case BZ_W:
      case BNZ_W:
        df = 'w';
        break;
      case BZ_D:
      case BNZ_D:
        df = 'd';
        break;
      default:
        UNREACHABLE();
    }
  } else {
    char DF[] = {'b', 'h', 'w', 'd'};
    switch (instr->MSAMinorOpcodeField()) {
      case kMsaMinorI5:
      case kMsaMinorI10:
      case kMsaMinor3R:
        df = DF[instr->Bits(22, 21)];
        break;
      case kMsaMinorMI10:
        df = DF[instr->Bits(1, 0)];
        break;
      case kMsaMinorBIT:
        df = DF[instr->MsaBitDf()];
        break;
      case kMsaMinorELM:
        df = DF[instr->MsaElmDf()];
        break;
      case kMsaMinor3RF: {
        uint32_t opcode = instr->InstructionBits() & kMsa3RFMask;
        switch (opcode) {
          case FEXDO:
          case FTQ:
          case MUL_Q:
          case MADD_Q:
          case MSUB_Q:
          case MULR_Q:
          case MADDR_Q:
          case MSUBR_Q:
            df = DF[1 + instr->Bit(21)];
            break;
          default:
            df = DF[2 + instr->Bit(21)];
            break;
        }
      } break;
      case kMsaMinor2R:
        df = DF[instr->Bits(17, 16)];
        break;
      case kMsaMinor2RF:
        df = DF[2 + instr->Bit(16)];
        break;
      default:
        UNREACHABLE();
    }
  }

  PrintChar(df);
}

// Printing of instruction name.
void Decoder::PrintInstructionName(Instruction* instr) {}

// Handle all register based formatting in this function to reduce the
// complexity of FormatOption.
int Decoder::FormatRegister(Instruction* instr, const char* format) {
  DCHECK_EQ(format[0], 'r');
  if (format[1] == 's') {  // 'rs: Rs register.
    int reg = instr->RsValue();
    PrintRegister(reg);
    return 2;
  } else if (format[1] == 't') {  // 'rt: rt register.
    int reg = instr->RtValue();
    PrintRegister(reg);
    return 2;
  } else if (format[1] == 'd') {  // 'rd: rd register.
    int reg = instr->RdValue();
    PrintRegister(reg);
    return 2;
  }
  UNREACHABLE();
}

// Handle all FPUregister based formatting in this function to reduce the
// complexity of FormatOption.
int Decoder::FormatFPURegister(Instruction* instr, const char* format) {
  DCHECK_EQ(format[0], 'f');
  if ((CTC1 == instr->RsFieldRaw()) || (CFC1 == instr->RsFieldRaw())) {
    if (format[1] == 's') {  // 'fs: fs register.
      int reg = instr->FsValue();
      PrintFPUStatusRegister(reg);
      return 2;
    } else if (format[1] == 't') {  // 'ft: ft register.
      int reg = instr->FtValue();
      PrintFPUStatusRegister(reg);
      return 2;
    } else if (format[1] == 'd') {  // 'fd: fd register.
      int reg = instr->FdValue();
      PrintFPUStatusRegister(reg);
      return 2;
    } else if (format[1] == 'r') {  // 'fr: fr register.
      int reg = instr->FrValue();
      PrintFPUStatusRegister(reg);
      return 2;
    }
  } else {
    if (format[1] == 's') {  // 'fs: fs register.
      int reg = instr->FsValue();
      PrintFPURegister(reg);
      return 2;
    } else if (format[1] == 't') {  // 'ft: ft register.
      int reg = instr->FtValue();
      PrintFPURegister(reg);
      return 2;
    } else if (format[1] == 'd') {  // 'fd: fd register.
      int reg = instr->FdValue();
      PrintFPURegister(reg);
      return 2;
    } else if (format[1] == 'r') {  // 'fr: fr register.
      int reg = instr->FrValue();
      PrintFPURegister(reg);
      return 2;
    }
  }
  UNREACHABLE();
}

// Handle all MSARegister based formatting in this function to reduce the
// complexity of FormatOption.
int Decoder::FormatMSARegister(Instruction* instr, const char* format) {
  DCHECK_EQ(format[0], 'w');
  if (format[1] == 's') {
    int reg = instr->WsValue();
    PrintMSARegister(reg);
    return 2;
  } else if (format[1] == 't') {
    int reg = instr->WtValue();
    PrintMSARegister(reg);
    return 2;
  } else if (format[1] == 'd') {
    int reg = instr->WdValue();
    PrintMSARegister(reg);
    return 2;
  }

  UNREACHABLE();
}

// FormatOption takes a formatting string and interprets it based on
// the current instructions. The format string points to the first
// character of the option string (the option escape has already been
// consumed by the caller.)  FormatOption returns the number of
// characters that were consumed from the formatting string.
int Decoder::FormatOption(Instruction* instr, const char* format) {
  switch (format[0]) {
    case 'c': {  // 'code for break or trap instructions.
      DCHECK(STRING_STARTS_WITH(format, "code"));
      PrintCode(instr);
      return 4;
    }
    case 'i': {  // 'imm16u or 'imm26.
      if (format[3] == '1') {
        if (format[4] == '6') {
          DCHECK(STRING_STARTS_WITH(format, "imm16"));
          switch (format[5]) {
            case 's':
              DCHECK(STRING_STARTS_WITH(format, "imm16s"));
              PrintSImm16(instr);
              break;
            case 'u':
              DCHECK(STRING_STARTS_WITH(format, "imm16u"));
              PrintSImm16(instr);
              break;
            case 'x':
              DCHECK(STRING_STARTS_WITH(format, "imm16x"));
              PrintXImm16(instr);
              break;
            case 'p': {  // The PC relative address.
              DCHECK(STRING_STARTS_WITH(format, "imm16p"));
              int delta_pc = 0;
              int n_bits = 0;
              switch (format[6]) {
                case '4': {
                  DCHECK(STRING_STARTS_WITH(format, "imm16p4"));
                  delta_pc = 4;
                  switch (format[8]) {
                    case '2':
                      DCHECK(STRING_STARTS_WITH(format, "imm16p4s2"));
                      n_bits = 2;
                      PrintPCImm16(instr, delta_pc, n_bits);
                      return 9;
                  }
                }
              }
            }
          }
          return 6;
        } else if (format[4] == '8') {
          DCHECK(STRING_STARTS_WITH(format, "imm18"));
          switch (format[5]) {
            case 's':
              DCHECK(STRING_STARTS_WITH(format, "imm18s"));
              PrintSImm18(instr);
              break;
            case 'x':
              DCHECK(STRING_STARTS_WITH(format, "imm18x"));
              PrintXImm18(instr);
              break;
          }
          return 6;
        } else if (format[4] == '9') {
          DCHECK(STRING_STARTS_WITH(format, "imm19"));
          switch (format[5]) {
            case 's':
              DCHECK(STRING_STARTS_WITH(format, "imm19s"));
              PrintSImm19(instr);
              break;
            case 'x':
              DCHECK(STRING_STARTS_WITH(format, "imm19x"));
              PrintXImm19(instr);
              break;
          }
          return 6;
        } else if (format[4] == '0' && format[5] == 's') {
          DCHECK(STRING_STARTS_WITH(format, "imm10s"));
          if (format[6] == '1') {
            DCHECK(STRING_STARTS_WITH(format, "imm10s1"));
            PrintMsaSImm10(instr, false);
          } else if (format[6] == '2') {
            DCHECK(STRING_STARTS_WITH(format, "imm10s2"));
            PrintMsaSImm10(instr, true);
          }
          return 7;
        }
      } else if (format[3] == '2' && format[4] == '1') {
        DCHECK(STRING_STARTS_WITH(format, "imm21"));
        switch (format[5]) {
          case 's':
            DCHECK(STRING_STARTS_WITH(format, "imm21s"));
            PrintSImm21(instr);
            break;
          case 'x':
            DCHECK(STRING_STARTS_WITH(format, "imm21x"));
            PrintXImm21(instr);
            break;
          case 'p': {  // The PC relative address.
            DCHECK(STRING_STARTS_WITH(format, "imm21p"));
            int delta_pc = 0;
            int n_bits = 0;
            switch (format[6]) {
              case '4': {
                DCHECK(STRING_STARTS_WITH(format, "imm21p4"));
                delta_pc = 4;
                switch (format[8]) {
                  case '2':
                    DCHECK(STRING_STARTS_WITH(format, "imm21p4s2"));
                    n_bits = 2;
                    PrintPCImm21(instr, delta_pc, n_bits);
                    return 9;
                }
              }
            }
          }
        }
        return 6;
      } else if (format[3] == '2' && format[4] == '6') {
        DCHECK(STRING_STARTS_WITH(format, "imm26"));
        switch (format[5]) {
          case 's':
            DCHECK(STRING_STARTS_WITH(format, "imm26s"));
            PrintSImm26(instr);
            break;
          case 'x':
            DCHECK(STRING_STARTS_WITH(format, "imm26x"));
            PrintXImm26(instr);
            break;
          case 'p': {  // The PC relative address.
            DCHECK(STRING_STARTS_WITH(format, "imm26p"));
            int delta_pc = 0;
            int n_bits = 0;
            switch (format[6]) {
              case '4': {
                DCHECK(STRING_STARTS_WITH(format, "imm26p4"));
                delta_pc = 4;
                switch (format[8]) {
                  case '2':
                    DCHECK(STRING_STARTS_WITH(format, "imm26p4s2"));
                    n_bits = 2;
                    PrintPCImm26(instr, delta_pc, n_bits);
                    return 9;
                }
              }
            }
          }
          case 'j': {  // Absolute address for jump instructions.
            DCHECK(STRING_STARTS_WITH(format, "imm26j"));
            PrintPCImm26(instr);
            break;
          }
        }
        return 6;
      } else if (format[3] == '5') {
        DCHECK(STRING_STARTS_WITH(format, "imm5"));
        if (format[4] == 'u') {
          DCHECK(STRING_STARTS_WITH(format, "imm5u"));
          PrintMsaImm5(instr);
        } else if (format[4] == 's') {
          DCHECK(STRING_STARTS_WITH(format, "imm5s"));
          PrintMsaSImm5(instr);
        }
        return 5;
      } else if (format[3] == '8') {
        DCHECK(STRING_STARTS_WITH(format, "imm8"));
        PrintMsaImm8(instr);
        return 4;
      } else if (format[3] == '9') {
        DCHECK(STRING_STARTS_WITH(format, "imm9"));
        if (format[4] == 'u') {
          DCHECK(STRING_STARTS_WITH(format, "imm9u"));
          PrintUImm9(instr);
        } else if (format[4] == 's') {
          DCHECK(STRING_STARTS_WITH(format, "imm9s"));
          PrintSImm9(instr);
        }
        return 5;
      } else if (format[3] == 'b') {
        DCHECK(STRING_STARTS_WITH(format, "immb"));
        PrintMsaImmBit(instr);
        return 4;
      } else if (format[3] == 'e') {
        DCHECK(STRING_STARTS_WITH(format, "imme"));
        PrintMsaImmElm(instr);
        return 4;
      }
      UNREACHABLE();
    }
    case 'r': {  // 'r: registers.
      return FormatRegister(instr, format);
    }
    case 'f': {  // 'f: FPUregisters.
      return FormatFPURegister(instr, format);
    }
    case 'w': {  // 'w: MSA Register
      return FormatMSARegister(instr, format);
    }
    case 's': {  // 'sa.
      switch (format[1]) {
        case 'a':
          if (format[2] == '2') {
            DCHECK(STRING_STARTS_WITH(format, "sa2"));  // 'sa2
            PrintLsaSa(instr);
            return 3;
          } else {
            DCHECK(STRING_STARTS_WITH(format, "sa"));
            PrintSa(instr);
            return 2;
          }
        case 'd': {
          DCHECK(STRING_STARTS_WITH(format, "sd"));
          PrintSd(instr);
          return 2;
        }
        case 's': {
          if (format[2] == '1') {
            DCHECK(STRING_STARTS_WITH(format, "ss1")); /* ext size */
            PrintSs1(instr);
            return 3;
          } else {
            DCHECK(STRING_STARTS_WITH(format, "ss2")); /* ins size */
            PrintSs2(instr);
            return 3;
          }
        }
      }
    }
    case 'b': {
      switch (format[1]) {
        case 'c': {  // 'bc - Special for bc1 cc field.
          DCHECK(STRING_STARTS_WITH(format, "bc"));
          PrintBc(instr);
          return 2;
        }
        case 'p': {
          switch (format[2]) {
            case '2': {  // 'bp2
              DCHECK(STRING_STARTS_WITH(format, "bp2"));
              PrintBp2(instr);
              return 3;
            }
          }
        }
      }
    }
    case 'C': {  // 'Cc - Special for c.xx.d cc field.
      DCHECK(STRING_STARTS_WITH(format, "Cc"));
      PrintCc(instr);
      return 2;
    }
    case 't':
      if (instr->IsMSAInstr()) {
        PrintMsaDataFormat(instr);
      } else {
        PrintFormat(instr);
      }
      return 1;
  }
  UNREACHABLE();
}

// Format takes a formatting string for a whole instruction and prints it into
// the output buffer. All escaped options are handed to FormatOption to be
// parsed further.
void Decoder::Format(Instruction* instr, const char* format) {
  char cur = *format++;
  while ((cur != 0) && (out_buffer_pos_ < (out_buffer_.length() - 1))) {
    if (cur == '\'') {  // Single quote is used as the formatting escape.
      format += FormatOption(instr, format);
    } else {
      out_buffer_[out_buffer_pos_++] = cur;
    }
    cur = *format++;
  }
  out_buffer_[out_buffer_pos_] = '\0';
}

// For currently unimplemented decodings the disassembler calls Unknown(instr)
// which will just print "unknown" of the instruction bits.
void Decoder::Unknown(Instruction* instr) { Format(instr, "unknown"); }

bool Decoder::DecodeTypeRegisterRsType(Instruction* instr) {
  switch (instr->FunctionFieldRaw()) {
    case RINT:
      Format(instr, "rint.'t    'fd, 'fs");
      break;
    case MIN:
      Format(instr, "min.'t    'fd, 'fs, 'ft");
      break;
    case MAX:
      Format(instr, "max.'t    'fd, 'fs, 'ft");
      break;
    case MINA:
      Format(instr, "mina.'t   'fd, 'fs, 'ft");
      break;
    case MAXA:
      Format(instr, "maxa.'t   'fd, 'fs, 'ft");
      break;
    case SEL:
      Format(instr, "sel.'t      'fd, 'fs, 'ft");
      break;
    case SELEQZ_C:
      Format(instr, "seleqz.'t    'fd, 'fs, 'ft");
      break;
    case SELNEZ_C:
      Format(instr, "selnez.'t    'fd, 'fs, 'ft");
      break;
    case MOVZ_C:
      Format(instr, "movz.'t    'fd, 'fs, 'rt");
      break;
    case MOVN_C:
      Format(instr, "movn.'t    'fd, 'fs, 'rt");
      break;
    case MOVF:
      if (instr->Bit(16)) {
        Format(instr, "movt.'t    'fd, 'fs, 'Cc");
      } else {
        Format(instr, "movf.'t    'fd, 'fs, 'Cc");
      }
      break;
    case ADD_D:
      Format(instr, "add.'t   'fd, 'fs, 'ft");
      break;
    case SUB_D:
      Format(instr, "sub.'t   'fd, 'fs, 'ft");
      break;
    case MUL_D:
      Format(instr, "mul.'t   'fd, 'fs, 'ft");
      break;
    case DIV_D:
      Format(instr, "div.'t   'fd, 'fs, 'ft");
      break;
    case ABS_D:
      Format(instr, "abs.'t   'fd, 'fs");
      break;
    case MOV_D:
      Format(instr, "mov.'t   'fd, 'fs");
      break;
    case NEG_D:
      Format(instr, "neg.'t   'fd, 'fs");
      break;
    case SQRT_D:
      Format(instr, "sqrt.'t  'fd, 'fs");
      break;
    case RECIP_D:
      Format(instr, "recip.'t  'fd, 'fs");
      break;
    case RSQRT_D:
      Format(instr, "rsqrt.'t  'fd, 'fs");
      break;
    case CVT_W_D:
      Format(instr, "cvt.w.'t 'fd, 'fs");
      break;
    case CVT_L_D:
      Format(instr, "cvt.l.'t 'fd, 'fs");
      break;
    case TRUNC_W_D:
      Format(instr, "trunc.w.'t 'fd, 'fs");
      break;
    case TRUNC_L_D:
      Format(instr, "trunc.l.'t 'fd, 'fs");
      break;
    case ROUND_W_D:
      Format(instr, "round.w.'t 'fd, 'fs");
      break;
    case ROUND_L_D:
      Format(instr, "round.l.'t 'fd, 'fs");
      break;
    case FLOOR_W_D:
      Format(instr, "floor.w.'t 'fd, 'fs");
      break;
    case FLOOR_L_D:
      Format(instr, "floor.l.'t 'fd, 'fs");
      break;
    case CEIL_W_D:
      Format(instr, "ceil.w.'t 'fd, 'fs");
      break;
    case CLASS_D:
      Format(instr, "class.'t 'fd, 'fs");
      break;
    case CEIL_L_D:
      Format(instr, "ceil.l.'t 'fd, 'fs");
      break;
    case CVT_S_D:
      Format(instr, "cvt.s.'t 'fd, 'fs");
      break;
    case C_F_D:
      Format(instr, "c.f.'t   'fs, 'ft, 'Cc");
      break;
    case C_UN_D:
      Format(instr, "c.un.'t  'fs, 'ft, 'Cc");
      break;
    case C_EQ_D:
      Format(instr, "c.eq.'t  'fs, 'ft, 'Cc");
      break;
    case C_UEQ_D:
      Format(instr, "c.ueq.'t 'fs, 'ft, 'Cc");
      break;
    case C_OLT_D:
      Format(instr, "c.olt.'t 'fs, 'ft, 'Cc");
      break;
    case C_ULT_D:
      Format(instr, "c.ult.'t 'fs, 'ft, 'Cc");
      break;
    case C_OLE_D:
      Format(instr, "c.ole.'t 'fs, 'ft, 'Cc");
      break;
    case C_ULE_D:
      Format(instr, "c.ule.'t 'fs, 'ft, 'Cc");
      break;
    default:
      return false;
  }
  return true;
}

void Decoder::DecodeTypeRegisterSRsType(Instruction* instr) {
  if (!DecodeTypeRegisterRsType(instr)) {
    switch (instr->FunctionFieldRaw()) {
      case CVT_D_S:
        Format(instr, "cvt.d.'t 'fd, 'fs");
        break;
      case MADDF_S:
        Format(instr, "maddf.s  'fd, 'fs, 'ft");
        break;
      case MSUBF_S:
        Format(instr, "msubf.s  'fd, 'fs, 'ft");
        break;
      default:
        Format(instr, "unknown.cop1.'t");
        break;
    }
  }
}

void Decoder::DecodeTypeRegisterDRsType(Instruction* instr) {
  if (!DecodeTypeRegisterRsType(instr)) {
    switch (instr->FunctionFieldRaw()) {
      case MADDF_D:
        Format(instr, "maddf.d  'fd, 'fs, 'ft");
        break;
      case MSUBF_D:
        Format(instr, "msubf.d  'fd, 'fs, 'ft");
        break;
      default:
        Format(instr, "unknown.cop1.'t");
        break;
    }
  }
}

void Decoder::DecodeTypeRegisterLRsType(Instruction* instr) {
  switch (instr->FunctionFieldRaw()) {
    case CVT_D_L:
      Format(instr, "cvt.d.l 'fd, 'fs");
      break;
    case CVT_S_L:
      Format(instr, "cvt.s.l 'fd, 'fs");
      break;
    case CMP_AF:
      Format(instr, "cmp.af.d  'fd,  'fs, 'ft");
      break;
    case CMP_UN:
      Format(instr, "cmp.un.d  'fd,  'fs, 'ft");
      break;
    case CMP_EQ:
      Format(instr, "cmp.eq.d  'fd,  'fs, 'ft");
      break;
    case CMP_UEQ:
      Format(instr, "cmp.ueq.d  'fd,  'fs, 'ft");
      break;
    case CMP_LT:
      Format(instr, "cmp.lt.d  'fd,  'fs, 'ft");
      break;
    case CMP_ULT:
      Format(instr, "cmp.ult.d  'fd,  'fs, 'ft");
      break;
    case CMP_LE:
      Format(instr, "cmp.le.d  'fd,  'fs, 'ft");
      break;
    case CMP_ULE:
      Format(instr, "cmp.ule.d  'fd,  'fs, 'ft");
      break;
    case CMP_OR:
      Format(instr, "cmp.or.d  'fd,  'fs, 'ft");
      break;
    case CMP_UNE:
      Format(instr, "cmp.une.d  'fd,  'fs, 'ft");
      break;
    case CMP_NE:
      Format(instr, "cmp.ne.d  'fd,  'fs, 'ft");
      break;
    default:
      UNREACHABLE();
  }
}

void Decoder::DecodeTypeRegisterWRsType(Instruction* instr) {
  switch (instr->FunctionValue()) {
    case CVT_S_W:  // Convert word to float (single).
      Format(instr, "cvt.s.w 'fd, 'fs");
      break;
    case CVT_D_W:  // Convert word to double.
      Format(instr, "cvt.d.w 'fd, 'fs");
      break;
    case CMP_AF:
      Format(instr, "cmp.af.s    'fd, 'fs, 'ft");
      break;
    case CMP_UN:
      Format(instr, "cmp.un.s    'fd, 'fs, 'ft");
      break;
    case CMP_EQ:
      Format(instr, "cmp.eq.s    'fd, 'fs, 'ft");
      break;
    case CMP_UEQ:
      Format(instr, "cmp.ueq.s   'fd, 'fs, 'ft");
      break;
    case CMP_LT:
      Format(instr, "cmp.lt.s    'fd, 'fs, 'ft");
      break;
    case CMP_ULT:
      Format(instr, "cmp.ult.s   'fd, 'fs, 'ft");
      break;
    case CMP_LE:
      Format(instr, "cmp.le.s    'fd, 'fs, 'ft");
      break;
    case CMP_ULE:
      Format(instr, "cmp.ule.s   'fd, 'fs, 'ft");
      break;
    case CMP_OR:
      Format(instr, "cmp.or.s    'fd, 'fs, 'ft");
      break;
    case CMP_UNE:
      Format(instr, "cmp.une.s   'fd, 'fs, 'ft");
      break;
    case CMP_NE:
      Format(instr, "cmp.ne.s    'fd, 'fs, 'ft");
      break;
    default:
      UNREACHABLE();
  }
}

void Decoder::DecodeTypeRegisterSPECIAL(Instruction* instr) {
  switch (instr->FunctionFieldRaw()) {
    case JR:
      Format(instr, "jr      'rs");
      break;
    case JALR:
      Format(instr, "jalr    'rs, 'rd");
      break;
    case SLL:
      if (0x0 == static_cast<int>(instr->InstructionBits()))
        Format(instr, "nop");
      else
        Format(instr, "sll     'rd, 'rt, 'sa");
      break;
    case SRL:
      if (instr->RsValue() == 0) {
        Format(instr, "srl     'rd, 'rt, 'sa");
      } else {
        if (IsMipsArchVariant(kMips32r2)) {
          Format(instr, "rotr    'rd, 'rt, 'sa");
        } else {
          Unknown(instr);
        }
      }
      break;
    case SRA:
      Format(instr, "sra     'rd, 'rt, 'sa");
      break;
    case SLLV:
      Format(instr, "sllv    'rd, 'rt, 'rs");
      break;
    case SRLV:
      if (instr->SaValue() == 0) {
        Format(instr, "srlv    'rd, 'rt, 'rs");
      } else {
        if (IsMipsArchVariant(kMips32r2)) {
          Format(instr, "rotrv   'rd, 'rt, 'rs");
        } else {
          Unknown(instr);
        }
      }
      break;
    case SRAV:
      Format(instr, "srav    'rd, 'rt, 'rs");
      break;
    case LSA:
      Format(instr, "lsa     'rd, 'rt, 'rs, 'sa2");
      break;
    case MFHI:
      if (instr->Bits(25, 16) == 0) {
        Format(instr, "mfhi    'rd");
      } else {
        if ((instr->FunctionFieldRaw() == CLZ_R6) && (instr->FdValue() == 1)) {
          Format(instr, "clz     'rd, 'rs");
        } else if ((instr->FunctionFieldRaw() == CLO_R6) &&
                   (instr->FdValue() == 1)) {
          Format(instr, "clo     'rd, 'rs");
        }
      }
      break;
    case MFLO:
      Format(instr, "mflo    'rd");
      break;
    case MULT:  // @Mips32r6 == MUL_MUH.
      if (!IsMipsArchVariant(kMips32r6)) {
        Format(instr, "mult    'rs, 'rt");
      } else {
        if (instr->SaValue() == MUL_OP) {
          Format(instr, "mul    'rd, 'rs, 'rt");
        } else {
          Format(instr, "muh    'rd, 'rs, 'rt");
        }
      }
      break;
    case MULTU:  // @Mips32r6 == MUL_MUH_U.
      if (!IsMipsArchVariant(kMips32r6)) {
        Format(instr, "multu   'rs, 'rt");
      } else {
        if (instr->SaValue() == MUL_OP) {
          Format(instr, "mulu   'rd, 'rs, 'rt");
        } else {
          Format(instr, "muhu   'rd, 'rs, 'rt");
        }
      }
      break;
    case DIV:  // @Mips32r6 == DIV_MOD.
      if (!IsMipsArchVariant(kMips32r6)) {
        Format(instr, "div     'rs, 'rt");
      } else {
        if (instr->SaValue() == DIV_OP) {
          Format(instr, "div    'rd, 'rs, 'rt");
        } else {
          Format(instr, "mod    'rd, 'rs, 'rt");
        }
      }
      break;
    case DIVU:  // @Mips32r6 == DIV_MOD_U.
      if (!IsMipsArchVariant(kMips32r6)) {
        Format(instr, "divu    'rs, 'rt");
      } else {
        if (instr->SaValue() == DIV_OP) {
          Format(instr, "divu   'rd, 'rs, 'rt");
        } else {
          Format(instr, "modu   'rd, 'rs, 'rt");
        }
      }
      break;
    case ADD:
      Format(instr, "add     'rd, 'rs, 'rt");
      break;
    case ADDU:
      Format(instr, "addu    'rd, 'rs, 'rt");
      break;
    case SUB:
      Format(instr, "sub     'rd, 'rs, 'rt");
      break;
    case SUBU:
      Format(instr, "subu    'rd, 'rs, 'rt");
      break;
    case AND:
      Format(instr, "and     'rd, 'rs, 'rt");
      break;
    case OR:
      if (0 == instr->RsValue()) {
        Format(instr, "mov     'rd, 'rt");
      } else if (0 == instr->RtValue()) {
        Format(instr, "mov     'rd, 'rs");
      } else {
        Format(instr, "or      'rd, 'rs, 'rt");
      }
      break;
    case XOR:
      Format(instr, "xor     'rd, 'rs, 'rt");
      break;
    case NOR:
      Format(instr, "nor     'rd, 'rs, 'rt");
      break;
    case SLT:
      Format(instr, "slt     'rd, 'rs, 'rt");
      break;
    case SLTU:
      Format(instr, "sltu    'rd, 'rs, 'rt");
      break;
    case BREAK:
      Format(instr, "break, code: 'code");
      break;
    case TGE:
      Format(instr, "tge     'rs, 'rt, code: 'code");
      break;
    case TGEU:
      Format(instr, "tgeu    'rs, 'rt, code: 'code");
      break;
    case TLT:
      Format(instr, "tlt     'rs, 'rt, code: 'code");
      break;
    case TLTU:
      Format(instr, "tltu    'rs, 'rt, code: 'code");
      break;
    case TEQ:
      Format(instr, "teq     'rs, 'rt, code: 'code");
      break;
    case TNE:
      Format(instr, "tne     'rs, 'rt, code: 'code");
      break;
    case SYNC:
      Format(instr, "sync");
      break;
    case MOVZ:
      Format(instr, "movz    'rd, 'rs, 'rt");
      break;
    case MOVN:
      Format(instr, "movn    'rd, 'rs, 'rt");
      break;
    case MOVCI:
      if (instr->Bit(16)) {
        Format(instr, "movt    'rd, 'rs, 'bc");
      } else {
        Format(instr, "movf    'rd, 'rs, 'bc");
      }
      break;
    case SELEQZ_S:
      Format(instr, "seleqz    'rd, 'rs, 'rt");
      break;
    case SELNEZ_S:
      Format(instr, "selnez    'rd, 'rs, 'rt");
      break;
    default:
      UNREACHABLE();
  }
}

void Decoder::DecodeTypeRegisterSPECIAL2(Instruction* instr) {
  switch (instr->FunctionFieldRaw()) {
    case MUL:
      Format(instr, "mul     'rd, 'rs, 'rt");
      break;
    case CLZ:
      if (!IsMipsArchVariant(kMips32r6)) {
        Format(instr, "clz     'rd, 'rs");
      }
      break;
    default:
      UNREACHABLE();
  }
}

void Decoder::DecodeTypeRegisterSPECIAL3(Instruction* instr) {
  switch (instr->FunctionFieldRaw()) {
    case INS: {
      if (IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6)) {
        Format(instr, "ins     'rt, 'rs, 'sa, 'ss2");
      } else {
        Unknown(instr);
      }
      break;
    }
    case EXT: {
      if (IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6)) {
        Format(instr, "ext     'rt, 'rs, 'sa, 'ss1");
      } else {
        Unknown(instr);
      }
      break;
    }
    case BSHFL: {
      int sa = instr->SaFieldRaw() >> kSaShift;
      switch (sa) {
        case BITSWAP: {
          if (IsMipsArchVariant(kMips32r6)) {
            Format(instr, "bitswap 'rd, 'rt");
          } else {
            Unknown(instr);
          }
          break;
        }
        case SEB: {
          if (IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6)) {
            Format(instr, "seb     'rd, 'rt");
          } else {
            Unknown(instr);
          }
          break;
        }
        case SEH: {
          if (IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6)) {
            Format(instr, "seh     'rd, 'rt");
          } else {
            Unknown(instr);
          }
          break;
        }
        case WSBH: {
          if (IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6)) {
            Format(instr, "wsbh    'rd, 'rt");
          } else {
            Unknown(instr);
          }
          break;
        }
        case LL_R6: {
          DCHECK(IsMipsArchVariant(kMips32r6));
          Format(instr, "llwp    'rd, 'rt, 0('rs)");
          break;
        }
        case SC_R6: {
          DCHECK(IsMipsArchVariant(kMips32r6));
          Format(instr, "scwp    'rd, 'rt, 0('rs)");
          break;
        }
        default: {
          sa >>= kBp2Bits;
          switch (sa) {
            case ALIGN: {
              if (IsMipsArchVariant(kMips32r6)) {
                Format(instr, "align  'rd, 'rs, 'rt, 'bp2");
              } else {
                Unknown(instr);
              }
              break;
            }
            default:
              UNREACHABLE();
          }
        }
      }
      break;
    }
    default:
      UNREACHABLE();
  }
}

void Decoder::DecodeTypeRegister(Instruction* instr) {
  switch (instr->OpcodeFieldRaw()) {
    case COP1:  // Coprocessor instructions.
      switch (instr->RsFieldRaw()) {
        case BC1:  // bc1 handled in DecodeTypeImmediate.
          UNREACHABLE();
        case MFC1:
          Format(instr, "mfc1    'rt, 'fs");
          break;
        case MFHC1:
          Format(instr, "mfhc1   'rt, 'fs");
          break;
        case MTC1:
          Format(instr, "mtc1    'rt, 'fs");
          break;
        // These are called "fs" too, although they are not FPU registers.
        case CTC1:
          Format(instr, "ctc1    'rt, 'fs");
          break;
        case CFC1:
          Format(instr, "cfc1    'rt, 'fs");
          break;
        case MTHC1:
          Format(instr, "mthc1   'rt, 'fs");
          break;
        case S:
          DecodeTypeRegisterSRsType(instr);
          break;
        case D:
          DecodeTypeRegisterDRsType(instr);
          break;
        case L:
          DecodeTypeRegisterLRsType(instr);
          break;
        case W:
          DecodeTypeRegisterWRsType(instr);
          break;
        case PS:
          UNIMPLEMENTED_MIPS();
          break;
        default:
          UNREACHABLE();
      }
      break;
    case COP1X:
      switch (instr->FunctionFieldRaw()) {
        case MADD_S:
          Format(instr, "madd.s  'fd, 'fr, 'fs, 'ft");
          break;
        case MADD_D:
          Format(instr, "madd.d  'fd, 'fr, 'fs, 'ft");
          break;
        case MSUB_S:
          Format(instr, "msub.s  'fd, 'fr, 'fs, 'ft");
          break;
        case MSUB_D:
          Format(instr, "msub.d  'fd, 'fr, 'fs, 'ft");
          break;
        default:
          UNREACHABLE();
      }
      break;
    case SPECIAL:
      DecodeTypeRegisterSPECIAL(instr);
      break;
    case SPECIAL2:
      DecodeTypeRegisterSPECIAL2(instr);
      break;
    case SPECIAL3:
      DecodeTypeRegisterSPECIAL3(instr);
      break;
    case MSA:
      switch (instr->MSAMinorOpcodeField()) {
        case kMsaMinor3R:
          DecodeTypeMsa3R(instr);
          break;
        case kMsaMinor3RF:
          DecodeTypeMsa3RF(instr);
          break;
        case kMsaMinorVEC:
          DecodeTypeMsaVec(instr);
          break;
        case kMsaMinor2R:
          DecodeTypeMsa2R(instr);
          break;
        case kMsaMinor2RF:
          DecodeTypeMsa2RF(instr);
          break;
        case kMsaMinorELM:
          DecodeTypeMsaELM(instr);
          break;
        default:
          UNREACHABLE();
      }
      break;
    default:
      UNREACHABLE();
  }
}

void Decoder::DecodeTypeImmediateSPECIAL3(Instruction* instr) {
  switch (instr->FunctionFieldRaw()) {
    case LL_R6: {
      if (IsMipsArchVariant(kMips32r6)) {
        if (instr->Bit(6)) {
          Format(instr, "llx     'rt, 'imm9s('rs)");
        } else {
          Format(instr, "ll      'rt, 'imm9s('rs)");
        }
      } else {
        Unknown(instr);
      }
      break;
    }
    case SC_R6: {
      if (IsMipsArchVariant(kMips32r6)) {
        if (instr->Bit(6)) {
          Format(instr, "scx     'rt, 'imm9s('rs)");
        } else {
          Format(instr, "sc      'rt, 'imm9s('rs)");
        }
      } else {
        Unknown(instr);
      }
      break;
    }
    default:
      UNREACHABLE();
  }
}

void Decoder::DecodeTypeImmediate(Instruction* instr) {
  switch (instr->OpcodeFieldRaw()) {
    case COP1:
      switch (instr->RsFieldRaw()) {
        case BC1:
          if (instr->FBtrueValue()) {
            Format(instr, "bc1t    'bc, 'imm16u -> 'imm16p4s2");
          } else {
            Format(instr, "bc1f    'bc, 'imm16u -> 'imm16p4s2");
          }
          break;
        case BC1EQZ:
          Format(instr, "bc1eqz    'ft, 'imm16u -> 'imm16p4s2");
          break;
        case BC1NEZ:
          Format(instr, "bc1nez    'ft, 'imm16u -> 'imm16p4s2");
          break;
        case BZ_V:
        case BZ_B:
        case BZ_H:
        case BZ_W:
        case BZ_D:
          Format(instr, "bz.'t  'wt, 'imm16s -> 'imm16p4s2");
          break;
        case BNZ_V:
        case BNZ_B:
        case BNZ_H:
        case BNZ_W:
        case BNZ_D:
          Format(instr, "bnz.'t  'wt, 'imm16s -> 'imm16p4s2");
          break;
        default:
          UNREACHABLE();
      }

      break;  // Case COP1.
    // ------------- REGIMM class.
    case REGIMM:
      switch (instr->RtFieldRaw()) {
        case BLTZ:
          Format(instr, "bltz    'rs, 'imm16u -> 'imm16p4s2");
          break;
        case BLTZAL:
          if (instr->RsValue() == 0) {
            Format(instr, "nal");
          } else {
            Format(instr, "bltzal  'rs, 'imm16u -> 'imm16p4s2");
          }
          break;
        case BGEZ:
          Format(instr, "bgez    'rs, 'imm16u -> 'imm16p4s2");
          break;
        case BGEZAL: {
          if (instr->RsValue() == 0)
            Format(instr, "bal     'imm16s -> 'imm16p4s2");
          else
            Format(instr, "bgezal  'rs, 'imm16u -> 'imm16p4s2");
          break;
        }
        case BGEZALL:
          Format(instr, "bgezall 'rs, 'imm16u -> 'imm16p4s2");
          break;
        default:
          UNREACHABLE();
      }
      break;  // Case REGIMM.
    // ------------- Branch instructions.
    case BEQ:
      Format(instr, "beq     'rs, 'rt, 'imm16u -> 'imm16p4s2");
      break;
    case BC:
      Format(instr, "bc      'imm26s -> 'imm26p4s2");
      break;
    case BALC:
      Format(instr, "balc    'imm26s -> 'imm26p4s2");
      break;
    case BNE:
      Format(instr, "bne     'rs, 'rt, 'imm16u -> 'imm16p4s2");
      break;
    case BLEZ:
      if ((instr->RtValue() == 0) && (instr->RsValue() != 0)) {
        Format(instr, "blez    'rs, 'imm16u -> 'imm16p4s2");
      } else if ((instr->RtValue() != instr->RsValue()) &&
                 (instr->RsValue() != 0) && (instr->RtValue() != 0)) {
        Format(instr, "bgeuc   'rs, 'rt, 'imm16u -> 'imm16p4s2");
      } else if ((instr->RtValue() == instr->RsValue()) &&
                 (instr->RtValue() != 0)) {
        Format(instr, "bgezalc 'rs, 'imm16u -> 'imm16p4s2");
      } else if ((instr->RsValue() == 0) && (instr->RtValue() != 0)) {
        Format(instr, "blezalc 'rt, 'imm16u -> 'imm16p4s2");
      } else {
        UNREACHABLE();
      }
      break;
    case BGTZ:
      if ((instr->RtValue() == 0) && (instr->RsValue() != 0)) {
        Format(instr, "bgtz    'rs, 'imm16u -> 'imm16p4s2");
      } else if ((instr->RtValue() != instr->RsValue()) &&
                 (instr->RsValue() != 0) && (instr->RtValue() != 0)) {
        Format(instr, "bltuc   'rs, 'rt, 'imm16u -> 'imm16p4s2");
      } else if ((instr->RtValue() == instr->RsValue()) &&
                 (instr->RtValue() != 0)) {
        Format(instr, "bltzalc 'rt, 'imm16u -> 'imm16p4s2");
      } else if ((instr->RsValue() == 0) && (instr->RtValue() != 0)) {
        Format(instr, "bgtzalc 'rt, 'imm16u -> 'imm16p4s2");
      } else {
        UNREACHABLE();
      }
      break;
    case BLEZL:
      if ((instr->RtValue() == instr->RsValue()) && (instr->RtValue() != 0)) {
        Format(instr, "bgezc    'rt, 'imm16u -> 'imm16p4s2");
      } else if ((instr->RtValue() != instr->RsValue()) &&
                 (instr->RsValue() != 0) && (instr->RtValue() != 0)) {
        Format(instr, "bgec     'rs, 'rt, 'imm16u -> 'imm16p4s2");
      } else if ((instr->RsValue() == 0) && (instr->RtValue() != 0)) {
        Format(instr, "blezc    'rt, 'imm16u -> 'imm16p4s2");
      } else {
        UNREACHABLE();
      }
      break;
    case BGTZL:
      if ((instr->RtValue() == instr->RsValue()) && (instr->RtValue() != 0)) {
        Format(instr, "bltzc    'rt, 'imm16u -> 'imm16p4s2");
      } else if ((instr->RtValue() != instr->RsValue()) &&
                 (instr->RsValue() != 0) && (instr->RtValue() != 0)) {
        Format(instr, "bltc    'rs, 'rt, 'imm16u -> 'imm16p4s2");
      } else if ((instr->RsValue() == 0) && (instr->RtValue() != 0)) {
        Format(instr, "bgtzc    'rt, 'imm16u -> 'imm16p4s2");
      } else {
        UNREACHABLE();
      }
      break;
    case POP66:
      if (instr->RsValue() == JIC) {
        Format(instr, "jic     'rt, 'imm16s");
      } else {
        Format(instr, "beqzc   'rs, 'imm21s -> 'imm21p4s2");
      }
      break;
    case POP76:
      if (instr->RsValue() == JIALC) {
        Format(instr, "jialc   'rt, 'imm16s");
      } else {
        Format(instr, "bnezc   'rs, 'imm21s -> 'imm21p4s2");
      }
      break;
    // ------------- Arithmetic instructions.
    case ADDI:
      if (!IsMipsArchVariant(kMips32r6)) {
        Format(instr, "addi    'rt, 'rs, 'imm16s");
      } else {
        int rs_reg = instr->RsValue();
        int rt_reg = instr->RtValue();
        // Check if BOVC, BEQZALC or BEQC instruction.
        if (rs_reg >= rt_reg) {
          Format(instr, "bovc  'rs, 'rt, 'imm16s -> 'imm16p4s2");
        } else {
          DCHECK_GT(rt_reg, 0);
          if (rs_reg == 0) {
            Format(instr, "beqzalc 'rt, 'imm16s -> 'imm16p4s2");
          } else {
            Format(instr, "beqc    'rs, 'rt, 'imm16s -> 'imm16p4s2");
          }
        }
      }
      break;
    case DADDI:
      if (IsMipsArchVariant(kMips32r6)) {
        int rs_reg = instr->RsValue();
        int rt_reg = instr->RtValue();
        // Check if BNVC, BNEZALC or BNEC instruction.
        if (rs_reg >= rt_reg) {
          Format(instr, "bnvc  'rs, 'rt, 'imm16s -> 'imm16p4s2");
        } else {
          DCHECK_GT(rt_reg, 0);
          if (rs_reg == 0) {
            Format(instr, "bnezalc 'rt, 'imm16s -> 'imm16p4s2");
          } else {
            Format(instr, "bnec  'rs, 'rt, 'imm16s -> 'imm16p4s2");
          }
        }
      }
      break;
    case ADDIU:
      Format(instr, "addiu   'rt, 'rs, 'imm16s");
      break;
    case SLTI:
      Format(instr, "slti    'rt, 'rs, 'imm16s");
      break;
    case SLTIU:
      Format(instr, "sltiu   'rt, 'rs, 'imm16u");
      break;
    case ANDI:
      Format(instr, "andi    'rt, 'rs, 'imm16x");
      break;
    case ORI:
      Format(instr, "ori     'rt, 'rs, 'imm16x");
      break;
    case XORI:
      Format(instr, "xori    'rt, 'rs, 'imm16x");
      break;
    case LUI:
      if (!IsMipsArchVariant(kMips32r6)) {
        Format(instr, "lui     'rt, 'imm16x");
      } else {
        if (instr->RsValue() != 0) {
          Format(instr, "aui     'rt, 'rs, 'imm16x");
        } else {
          Format(instr, "lui     'rt, 'imm16x");
        }
      }
      break;
    // ------------- Memory instructions.
    case LB:
      Format(instr, "lb      'rt, 'imm16s('rs)");
      break;
    case LH:
      Format(instr, "lh      'rt, 'imm16s('rs)");
      break;
    case LWL:
      Format(instr, "lwl     'rt, 'imm16s('rs)");
      break;
    case LW:
      Format(instr, "lw      'rt, 'imm16s('rs)");
      break;
    case LBU:
      Format(instr, "lbu     'rt, 'imm16s('rs)");
      break;
    case LHU:
      Format(instr, "lhu     'rt, 'imm16s('rs)");
      break;
    case LWR:
      Format(instr, "lwr     'rt, 'imm16s('rs)");
      break;
    case PREF:
      Format(instr, "pref    'rt, 'imm16s('rs)");
      break;
    case SB:
      Format(instr, "sb      'rt, 'imm16s('rs)");
      break;
    case SH:
      Format(instr, "sh      'rt, 'imm16s('rs)");
      break;
    case SWL:
      Format(instr, "swl     'rt, 'imm16s('rs)");
      break;
    case SW:
      Format(instr, "sw      'rt, 'imm16s('rs)");
      break;
    case SWR:
      Format(instr, "swr     'rt, 'imm16s('rs)");
      break;
    case LL:
      if (IsMipsArchVariant(kMips32r6)) {
        Unknown(instr);
      } else {
        Format(instr, "ll      'rt, 'imm16s('rs)");
      }
      break;
    case SC:
      if (IsMipsArchVariant(kMips32r6)) {
        Unknown(instr);
      } else {
        Format(instr, "sc      'rt, 'imm16s('rs)");
      }
      break;
    case LWC1:
      Format(instr, "lwc1    'ft, 'imm16s('rs)");
      break;
    case LDC1:
      Format(instr, "ldc1    'ft, 'imm16s('rs)");
      break;
    case SWC1:
      Format(instr, "swc1    'ft, 'imm16s('rs)");
      break;
    case SDC1:
      Format(instr, "sdc1    'ft, 'imm16s('rs)");
      break;
    case PCREL: {
      int32_t imm21 = instr->Imm21Value();
      // rt field: 5-bits checking
      uint8_t rt = (imm21 >> kImm16Bits);
      switch (rt) {
        case ALUIPC:
          Format(instr, "aluipc  'rs, 'imm16s");
          break;
        case AUIPC:
          Format(instr, "auipc   'rs, 'imm16s");
          break;
        default: {
          // rt field: checking of the most significant 2-bits
          rt = (imm21 >> kImm19Bits);
          switch (rt) {
            case LWPC:
              Format(instr, "lwpc    'rs, 'imm19s");
              break;
            case ADDIUPC:
              Format(instr, "addiupc 'rs, 'imm19s");
              break;
            default:
              UNREACHABLE();
          }
        }
      }
      break;
    }
    case SPECIAL3:
      DecodeTypeImmediateSPECIAL3(instr);
      break;
    case MSA:
      switch (instr->MSAMinorOpcodeField()) {
        case kMsaMinorI8:
          DecodeTypeMsaI8(instr);
          break;
        case kMsaMinorI5:
          DecodeTypeMsaI5(instr);
          break;
        case kMsaMinorI10:
          DecodeTypeMsaI10(instr);
          break;
        case kMsaMinorELM:
          DecodeTypeMsaELM(instr);
          break;
        case kMsaMinorBIT:
          DecodeTypeMsaBIT(instr);
          break;
        case kMsaMinorMI10:
          DecodeTypeMsaMI10(instr);
          break;
        default:
          UNREACHABLE();
      }
      break;
    default:
      printf("a 0x%x \n", instr->OpcodeFieldRaw());
      UNREACHABLE();
  }
}

void Decoder::DecodeTypeJump(Instruction* instr) {
  switch (instr->OpcodeFieldRaw()) {
    case J:
      Format(instr, "j       'imm26x -> 'imm26j");
      break;
    case JAL:
      Format(instr, "jal     'imm26x -> 'imm26j");
      break;
    default:
      UNREACHABLE();
  }
}

void Decoder::DecodeTypeMsaI8(Instruction* instr) {
  uint32_t opcode = instr->InstructionBits() & kMsaI8Mask;

  switch (opcode) {
    case ANDI_B:
      Format(instr, "andi.b  'wd, 'ws, 'imm8");
      break;
    case ORI_B:
      Format(instr, "ori.b  'wd, 'ws, 'imm8");
      break;
    case NORI_B:
      Format(instr, "nori.b  'wd, 'ws, 'imm8");
      break;
    case XORI_B:
      Format(instr, "xori.b  'wd, 'ws, 'imm8");
      break;
    case BMNZI_B:
      Format(instr, "bmnzi.b  'wd, 'ws, 'imm8");
      break;
    case BMZI_B:
      Format(instr, "bmzi.b  'wd, 'ws, 'imm8");
      break;
    case BSELI_B:
      Format(instr, "bseli.b  'wd, 'ws, 'imm8");
      break;
    case SHF_B:
      Format(instr, "shf.b  'wd, 'ws, 'imm8");
      break;
    case SHF_H:
      Format(instr, "shf.h  'wd, 'ws, 'imm8");
      break;
    case SHF_W:
      Format(instr, "shf.w  'wd, 'ws, 'imm8");
      break;
    default:
      UNREACHABLE();
  }
}

void Decoder::DecodeTypeMsaI5(Instruction* instr) {
  uint32_t opcode = instr->InstructionBits() & kMsaI5Mask;

  switch (opcode) {
    case ADDVI:
      Format(instr, "addvi.'t  'wd, 'ws, 'imm5u");
      break;
    case SUBVI:
      Format(instr, "subvi.'t  'wd, 'ws, 'imm5u");
      break;
    case MAXI_S:
      Format(instr, "maxi_s.'t  'wd, 'ws, 'imm5s");
      break;
    case MAXI_U:
      Format(instr, "maxi_u.'t  'wd, 'ws, 'imm5u");
      break;
    case MINI_S:
      Format(instr, "mini_s.'t  'wd, 'ws, 'imm5s");
      break;
    case MINI_U:
      Format(instr, "mini_u.'t  'wd, 'ws, 'imm5u");
      break;
    case CEQI:
      Format(instr, "ceqi.'t  'wd, 'ws, 'imm5s");
      break;
    case CLTI_S:
      Format(instr, "clti_s.'t  'wd, 'ws, 'imm5s");
      break;
    case CLTI_U:
      Format(instr, "clti_u.'t  'wd, 'ws, 'imm5u");
      break;
    case CLEI_S:
      Format(instr, "clei_s.'t  'wd, 'ws, 'imm5s");
      break;
    case CLEI_U:
      Format(instr, "clei_u.'t  'wd, 'ws, 'imm5u");
      break;
    default:
      UNREACHABLE();
  }
}

void Decoder::DecodeTypeMsaI10(Instruction* instr) {
  uint32_t opcode = instr->InstructionBits() & kMsaI5Mask;
  if (opcode == LDI) {
    Format(instr, "ldi.'t  'wd, 'imm10s1");
  } else {
    UNREACHABLE();
  }
}

void Decoder::DecodeTypeMsaELM(Instruction* instr) {
  uint32_t opcode = instr->InstructionBits() & kMsaELMMask;
  switch (opcode) {
    case SLDI:
      if (instr->Bits(21, 16) == 0x3E) {
        Format(instr, "ctcmsa  ");
        PrintMSAControlRegister(instr->WdValue());
        Print(", ");
        PrintRegister(instr->WsValue());
      } else {
        Format(instr, "sldi.'t  'wd, 'ws['imme]");
      }
      break;
    case SPLATI:
      if (instr->Bits(21, 16) == 0x3E) {
        Format(instr, "cfcmsa  ");
        PrintRegister(instr->WdValue());
        Print(", ");
        PrintMSAControlRegister(instr->WsValue());
      } else {
        Format(instr, "splati.'t  'wd, 'ws['imme]");
      }
      break;
    case COPY_S:
      if (instr->Bits(21, 16) == 0x3E) {
        Format(instr, "move.v  'wd, 'ws");
      } else {
        Format(instr, "copy_s.'t  ");
        PrintMsaCopy(instr);
      }
      break;
    case COPY_U:
      Format(instr, "copy_u.'t  ");
      PrintMsaCopy(instr);
      break;
    case INSERT:
      Format(instr, "insert.'t  'wd['imme], ");
      PrintRegister(instr->WsValue());
      break;
    case INSVE:
      Format(instr, "insve.'t  'wd['imme], 'ws[0]");
      break;
    default:
      UNREACHABLE();
  }
}

void Decoder::DecodeTypeMsaBIT(Instruction* instr) {
  uint32_t opcode = instr->InstructionBits() & kMsaBITMask;

  switch (opcode) {
    case SLLI:
      Format(instr, "slli.'t  'wd, 'ws, 'immb");
      break;
    case SRAI:
      Format(instr, "srai.'t  'wd, 'ws, 'immb");
      break;
    case SRLI:
      Format(instr, "srli.'t  'wd, 'ws, 'immb");
      break;
    case BCLRI:
      Format(instr, "bclri.'t  'wd, 'ws, 'immb");
      break;
    case BSETI:
      Format(instr, "bseti.'t  'wd, 'ws, 'immb");
      break;
    case BNEGI:
      Format(instr, "bnegi.'t  'wd, 'ws, 'immb");
      break;
    case BINSLI:
      Format(instr, "binsli.'t  'wd, 'ws, 'immb");
      break;
    case BINSRI:
      Format(instr, "binsri.'t  'wd, 'ws, 'immb");
      break;
    case SAT_S:
      Format(instr, "sat_s.'t  'wd, 'ws, 'immb");
      break;
    case SAT_U:
      Format(instr, "sat_u.'t  'wd, 'ws, 'immb");
      break;
    case SRARI:
      Format(instr, "srari.'t  'wd, 'ws, 'immb");
      break;
    case SRLRI:
      Format(instr, "srlri.'t  'wd, 'ws, 'immb");
      break;
    default:
      UNREACHABLE();
  }
}

void Decoder::DecodeTypeMsaMI10(Instruction* instr) {
  uint32_t opcode = instr->InstructionBits() & kMsaMI10Mask;
  if (opcode == MSA_LD) {
    Format(instr, "ld.'t  'wd, 'imm10s2(");
    PrintRegister(instr->WsValue());
    Print(")");
  } else if (opcode == MSA_ST) {
    Format(instr, "st.'t  'wd, 'imm10s2(");
    PrintRegister(instr->WsValue());
    Print(")");
  } else {
    UNREACHABLE();
  }
}

void Decoder::DecodeTypeMsa3R(Instruction* instr) {
  uint32_t opcode = instr->InstructionBits() & kMsa3RMask;
  switch (opcode) {
    case SLL_MSA:
      Format(instr, "sll.'t  'wd, 'ws, 'wt");
      break;
    case SRA_MSA:
      Format(instr, "sra.'t  'wd, 'ws, 'wt");
      break;
    case SRL_MSA:
      Format(instr, "srl.'t  'wd, 'ws, 'wt");
      break;
    case BCLR:
      Format(instr, "bclr.'t  'wd, 'ws, 'wt");
      break;
    case BSET:
      Format(instr, "bset.'t  'wd, 'ws, 'wt");
      break;
    case BNEG:
      Format(instr, "bneg.'t  'wd, 'ws, 'wt");
      break;
    case BINSL:
      Format(instr, "binsl.'t  'wd, 'ws, 'wt");
      break;
    case BINSR:
      Format(instr, "binsr.'t  'wd, 'ws, 'wt");
      break;
    case ADDV:
      Format(instr, "addv.'t  'wd, 'ws, 'wt");
      break;
    case SUBV:
      Format(instr, "subv.'t  'wd, 'ws, 'wt");
      break;
    case MAX_S:
      Format(instr, "max_s.'t  'wd, 'ws, 'wt");
      break;
    case MAX_U:
      Format(instr, "max_u.'t  'wd, 'ws, 'wt");
      break;
    case MIN_S:
      Format(instr, "min_s.'t  'wd, 'ws, 'wt");
      break;
    case MIN_U:
      Format(instr, "min_u.'t  'wd, 'ws, 'wt");
      break;
    case MAX_A:
      Format(instr, "max_a.'t  'wd, 'ws, 'wt");
      break;
    case MIN_A:
      Format(instr, "min_a.'t  'wd, 'ws, 'wt");
      break;
    case CEQ:
      Format(instr, "ceq.'t  'wd, 'ws, 'wt");
      break;
    case CLT_S:
      Format(instr, "clt_s.'t  'wd, 'ws, 'wt");
      break;
    case CLT_U:
      Format(instr, "clt_u.'t  'wd, 'ws, 'wt");
      break;
    case CLE_S:
      Format(instr, "cle_s.'t  'wd, 'ws, 'wt");
      break;
    case CLE_U:
      Format(instr, "cle_u.'t  'wd, 'ws, 'wt");
      break;
    case ADD_A:
      Format(instr, "add_a.'t  'wd, 'ws, 'wt");
      break;
    case ADDS_A:
      Format(instr, "adds_a.'t  'wd, 'ws, 'wt");
      break;
    case ADDS_S:
      Format(instr, "adds_s.'t  'wd, 'ws, 'wt");
      break;
    case ADDS_U:
      Format(instr, "adds_u.'t  'wd, 'ws, 'wt");
      break;
    case AVE_S:
      Format(instr, "ave_s.'t  'wd, 'ws, 'wt");
      break;
    case AVE_U:
      Format(instr, "ave_u.'t  'wd, 'ws, 'wt");
      break;
    case AVER_S:
      Format(instr, "aver_s.'t  'wd, 'ws, 'wt");
      break;
    case AVER_U:
      Format(instr, "aver_u.'t  'wd, 'ws, 'wt");
      break;
    case SUBS_S:
      Format(instr, "subs_s.'t  'wd, 'ws, 'wt");
      break;
    case SUBS_U:
      Format(instr, "subs_u.'t  'wd, 'ws, 'wt");
      break;
    case SUBSUS_U:
      Format(instr, "subsus_u.'t  'wd, 'ws, 'wt");
      break;
    case SUBSUU_S:
      Format(instr, "subsuu_s.'t  'wd, 'ws, 'wt");
      break;
    case ASUB_S:
      Format(instr, "asub_s.'t  'wd, 'ws, 'wt");
      break;
    case ASUB_U:
      Format(instr, "asub_u.'t  'wd, 'ws, 'wt");
      break;
    case MULV:
      Format(instr, "mulv.'t  'wd, 'ws, 'wt");
      break;
    case MADDV:
      Format(instr, "maddv.'t  'wd, 'ws, 'wt");
      break;
    case MSUBV:
      Format(instr, "msubv.'t  'wd, 'ws, 'wt");
      break;
    case DIV_S_MSA:
      Format(instr, "div_s.'t  'wd, 'ws, 'wt");
      break;
    case DIV_U:
      Format(instr, "div_u.'t  'wd, 'ws, 'wt");
      break;
    case MOD_S:
      Format(instr, "mod_s.'t  'wd, 'ws, 'wt");
      break;
    case MOD_U:
      Format(instr, "mod_u.'t  'wd, 'ws, 'wt");
      break;
    case DOTP_S:
      Format(instr, "dotp_s.'t  'wd, 'ws, 'wt");
      break;
    case DOTP_U:
      Format(instr, "dotp_u.'t  'wd, 'ws, 'wt");
      break;
    case DPADD_S:
      Format(instr, "dpadd_s.'t  'wd, 'ws, 'wt");
      break;
    case DPADD_U:
      Format(instr, "dpadd_u.'t  'wd, 'ws, 'wt");
      break;
    case DPSUB_S:
      Format(instr, "dpsub_s.'t  'wd, 'ws, 'wt");
      break;
    case DPSUB_U:
      Format(instr, "dpsub_u.'t  'wd, 'ws, 'wt");
      break;
    case SLD:
      Format(instr, "sld.'t  'wd, 'ws['rt]");
      break;
    case SPLAT:
      Format(instr, "splat.'t  'wd, 'ws['rt]");
      break;
    case PCKEV:
      Format(instr, "pckev.'t  'wd, 'ws, 'wt");
      break;
    case PCKOD:
      Format(instr, "pckod.'t  'wd, 'ws, 'wt");
      break;
    case ILVL:
      Format(instr, "ilvl.'t  'wd, 'ws, 'wt");
      break;
    case ILVR:
      Format(instr, "ilvr.'t  'wd, 'ws, 'wt");
      break;
    case ILVEV:
      Format(instr, "ilvev.'t  'wd, 'ws, 'wt");
      break;
    case ILVOD:
      Format(instr, "ilvod.'t  'wd, 'ws, 'wt");
      break;
    case VSHF:
      Format(instr, "vshf.'t  'wd, 'ws, 'wt");
      break;
    case SRAR:
      Format(instr, "srar.'t  'wd, 'ws, 'wt");
      break;
    case SRLR:
      Format(instr, "srlr.'t  'wd, 'ws, 'wt");
      break;
    case HADD_S:
      Format(instr, "hadd_s.'t  'wd, 'ws, 'wt");
      break;
    case HADD_U:
      Format(instr, "hadd_u.'t  'wd, 'ws, 'wt");
      break;
    case HSUB_S:
      Format(instr, "hsub_s.'t  'wd, 'ws, 'wt");
      break;
    case HSUB_U:
      Format(instr, "hsub_u.'t  'wd, 'ws, 'wt");
      break;
    default:
      UNREACHABLE();
  }
}

void Decoder::DecodeTypeMsa3RF(Instruction* instr) {
  uint32_t opcode = instr->InstructionBits() & kMsa3RFMask;
  switch (opcode) {
    case FCAF:
      Format(instr, "fcaf.'t  'wd, 'ws, 'wt");
      break;
    case FCUN:
      Format(instr, "fcun.'t  'wd, 'ws, 'wt");
      break;
    case FCEQ:
      Format(instr, "fceq.'t  'wd, 'ws, 'wt");
      break;
    case FCUEQ:
      Format(instr, "fcueq.'t  'wd, 'ws, 'wt");
      break;
    case FCLT:
      Format(instr, "fclt.'t  'wd, 'ws, 'wt");
      break;
    case FCULT:
      Format(instr, "fcult.'t  'wd, 'ws, 'wt");
      break;
    case FCLE:
      Format(instr, "fcle.'t  'wd, 'ws, 'wt");
      break;
    case FCULE:
      Format(instr, "fcule.'t  'wd, 'ws, 'wt");
      break;
    case FSAF:
      Format(instr, "fsaf.'t  'wd, 'ws, 'wt");
      break;
    case FSUN:
      Format(instr, "fsun.'t  'wd, 'ws, 'wt");
      break;
    case FSEQ:
      Format(instr, "fseq.'t  'wd, 'ws, 'wt");
      break;
    case FSUEQ:
      Format(instr, "fsueq.'t  'wd, 'ws, 'wt");
      break;
    case FSLT:
      Format(instr, "fslt.'t  'wd, 'ws, 'wt");
      break;
    case FSULT:
      Format(instr, "fsult.'t  'wd, 'ws, 'wt");
      break;
    case FSLE:
      Format(instr, "fsle.'t  'wd, 'ws, 'wt");
      break;
    case FSULE:
      Format(instr, "fsule.'t  'wd, 'ws, 'wt");
      break;
    case FADD:
      Format(instr, "fadd.'t  'wd, 'ws, 'wt");
      break;
    case FSUB:
      Format(instr, "fsub.'t  'wd, 'ws, 'wt");
      break;
    case FMUL:
      Format(instr, "fmul.'t  'wd, 'ws, 'wt");
      break;
    case FDIV:
      Format(instr, "fdiv.'t  'wd, 'ws, 'wt");
      break;
    case FMADD:
      Format(instr, "fmadd.'t  'wd, 'ws, 'wt");
      break;
    case FMSUB:
      Format(instr, "fmsub.'t  'wd, 'ws, 'wt");
      break;
    case FEXP2:
      Format(instr, "fexp2.'t  'wd, 'ws, 'wt");
      break;
    case FEXDO:
      Format(instr, "fexdo.'t  'wd, 'ws, 'wt");
      break;
    case FTQ:
      Format(instr, "ftq.'t  'wd, 'ws, 'wt");
      break;
    case FMIN:
      Format(instr, "fmin.'t  'wd, 'ws, 'wt");
      break;
    case FMIN_A:
      Format(instr, "fmin_a.'t  'wd, 'ws, 'wt");
      break;
    case FMAX:
      Format(instr, "fmax.'t  'wd, 'ws, 'wt");
      break;
    case FMAX_A:
      Format(instr, "fmax_a.'t  'wd, 'ws, 'wt");
      break;
    case FCOR:
      Format(instr, "fcor.'t  'wd, 'ws, 'wt");
      break;
    case FCUNE:
      Format(instr, "fcune.'t  'wd, 'ws, 'wt");
      break;
    case FCNE:
      Format(instr, "fcne.'t  'wd, 'ws, 'wt");
      break;
    case MUL_Q:
      Format(instr, "mul_q.'t  'wd, 'ws, 'wt");
      break;
    case MADD_Q:
      Format(instr, "madd_q.'t  'wd, 'ws, 'wt");
      break;
    case MSUB_Q:
      Format(instr, "msub_q.'t  'wd, 'ws, 'wt");
      break;
    case FSOR:
      Format(instr, "fsor.'t  'wd, 'ws, 'wt");
      break;
    case FSUNE:
      Format(instr, "fsune.'t  'wd, 'ws, 'wt");
      break;
    case FSNE:
      Format(instr, "fsne.'t  'wd, 'ws, 'wt");
      break;
    case MULR_Q:
      Format(instr, "mulr_q.'t  'wd, 'ws, 'wt");
      break;
    case MADDR_Q:
      Format(instr, "maddr_q.'t  'wd, 'ws, 'wt");
      break;
    case MSUBR_Q:
      Format(instr, "msubr_q.'t  'wd, 'ws, 'wt");
      break;
    default:
      UNREACHABLE();
  }
}

void Decoder::DecodeTypeMsaVec(Instruction* instr) {
  uint32_t opcode = instr->InstructionBits() & kMsaVECMask;
  switch (opcode) {
    case AND_V:
      Format(instr, "and.v  'wd, 'ws, 'wt");
      break;
    case OR_V:
      Format(instr, "or.v  'wd, 'ws, 'wt");
      break;
    case NOR_V:
      Format(instr, "nor.v  'wd, 'ws, 'wt");
      break;
    case XOR_V:
      Format(instr, "xor.v  'wd, 'ws, 'wt");
      break;
    case BMNZ_V:
      Format(instr, "bmnz.v  'wd, 'ws, 'wt");
      break;
    case BMZ_V:
      Format(instr, "bmz.v  'wd, 'ws, 'wt");
      break;
    case BSEL_V:
      Format(instr, "bsel.v  'wd, 'ws, 'wt");
      break;
    default:
      UNREACHABLE();
  }
}

void Decoder::DecodeTypeMsa2R(Instruction* instr) {
  uint32_t opcode = instr->InstructionBits() & kMsa2RMask;
  switch (opcode) {
    case FILL: {
      Format(instr, "fill.'t  'wd, ");
      PrintRegister(instr->WsValue());  // rs value is in ws field
    } break;
    case PCNT:
      Format(instr, "pcnt.'t  'wd, 'ws");
      break;
    case NLOC:
      Format(instr, "nloc.'t  'wd, 'ws");
      break;
    case NLZC:
      Format(instr, "nlzc.'t  'wd, 'ws");
      break;
    default:
      UNREACHABLE();
  }
}

void Decoder::DecodeTypeMsa2RF(Instruction* instr) {
  uint32_t opcode = instr->InstructionBits() & kMsa2RFMask;
  switch (opcode) {
    case FCLASS:
      Format(instr, "fclass.'t  'wd, 'ws");
      break;
    case FTRUNC_S:
      Format(instr, "ftrunc_s.'t  'wd, 'ws");
      break;
    case FTRUNC_U:
      Format(instr, "ftrunc_u.'t  'wd, 'ws");
      break;
    case FSQRT:
      Format(instr, "fsqrt.'t  'wd, 'ws");
      break;
    case FRSQRT:
      Format(instr, "frsqrt.'t  'wd, 'ws");
      break;
    case FRCP:
      Format(instr, "frcp.'t  'wd, 'ws");
      break;
    case FRINT:
      Format(instr, "frint.'t  'wd, 'ws");
      break;
    case FLOG2:
      Format(instr, "flog2.'t  'wd, 'ws");
      break;
    case FEXUPL:
      Format(instr, "fexupl.'t  'wd, 'ws");
      break;
    case FEXUPR:
      Format(instr, "fexupr.'t  'wd, 'ws");
      break;
    case FFQL:
      Format(instr, "ffql.'t  'wd, 'ws");
      break;
    case FFQR:
      Format(instr, "ffqr.'t  'wd, 'ws");
      break;
    case FTINT_S:
      Format(instr, "ftint_s.'t  'wd, 'ws");
      break;
    case FTINT_U:
      Format(instr, "ftint_u.'t  'wd, 'ws");
      break;
    case FFINT_S:
      Format(instr, "ffint_s.'t  'wd, 'ws");
      break;
    case FFINT_U:
      Format(instr, "ffint_u.'t  'wd, 'ws");
      break;
    default:
      UNREACHABLE();
  }
}

// Disassemble the instruction at *instr_ptr into the output buffer.
int Decoder::InstructionDecode(byte* instr_ptr) {
  Instruction* instr = Instruction::At(instr_ptr);
  // Print raw instruction bytes.
  out_buffer_pos_ += base::SNPrintF(out_buffer_ + out_buffer_pos_,
                                    "%08x       ", instr->InstructionBits());
  switch (instr->InstructionType()) {
    case Instruction::kRegisterType: {
      DecodeTypeRegister(instr);
      break;
    }
    case Instruction::kImmediateType: {
      DecodeTypeImmediate(instr);
      break;
    }
    case Instruction::kJumpType: {
      DecodeTypeJump(instr);
      break;
    }
    default: {
      Format(instr, "UNSUPPORTED");
      UNSUPPORTED_MIPS();
    }
  }
  return kInstrSize;
}

}  // namespace internal
}  // namespace v8

//------------------------------------------------------------------------------

namespace disasm {

const char* NameConverter::NameOfAddress(byte* addr) const {
  v8::base::SNPrintF(tmp_buffer_, "%p", static_cast<void*>(addr));
  return tmp_buffer_.begin();
}

const char* NameConverter::NameOfConstant(byte* addr) const {
  return NameOfAddress(addr);
}

const char* NameConverter::NameOfCPURegister(int reg) const {
  return v8::internal::Registers::Name(reg);
}

const char* NameConverter::NameOfXMMRegister(int reg) const {
  return v8::internal::FPURegisters::Name(reg);
}

const char* NameConverter::NameOfByteCPURegister(int reg) const {
  UNREACHABLE();  // MIPS does not have the concept of a byte register.
}

const char* NameConverter::NameInCode(byte* addr) const {
  // The default name converter is called for unknown code. So we will not try
  // to access any memory.
  return "";
}

//------------------------------------------------------------------------------

int Disassembler::InstructionDecode(v8::base::Vector<char> buffer,
                                    byte* instruction) {
  v8::internal::Decoder d(converter_, buffer);
  return d.InstructionDecode(instruction);
}

// The MIPS assembler does not currently use constant pools.
int Disassembler::ConstantPoolSizeAt(byte* instruction) { return -1; }

void Disassembler::Disassemble(FILE* f, byte* begin, byte* end,
                               UnimplementedOpcodeAction unimplemented_action) {
  NameConverter converter;
  Disassembler d(converter, unimplemented_action);
  for (byte* pc = begin; pc < end;) {
    v8::base::EmbeddedVector<char, 128> buffer;
    buffer[0] = '\0';
    byte* prev_pc = pc;
    pc += d.InstructionDecode(buffer, pc);
    v8::internal::PrintF(f, "%p    %08x      %s\n", static_cast<void*>(prev_pc),
                         *reinterpret_cast<int32_t*>(prev_pc), buffer.begin());
  }
}

#undef STRING_STARTS_WITH

}  // namespace disasm

#endif  // V8_TARGET_ARCH_MIPS
