// Copyright 2014 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::internal::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_S390

#include "src/base/platform/platform.h"
#include "src/disasm.h"
#include "src/macro-assembler.h"
#include "src/s390/constants-s390.h"

namespace v8 {
namespace internal {

const auto GetRegConfig = RegisterConfiguration::Crankshaft;

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

// 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, Vector<char> out_buffer)
      : converter_(converter), out_buffer_(out_buffer), out_buffer_pos_(0) {
    out_buffer_[out_buffer_pos_] = '\0';
  }

  ~Decoder() {}

  // 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 PrintDRegister(int reg);
  void PrintSoftwareInterrupt(SoftwareInterruptCodes svc);

  // Handle formatting of instructions and their options.
  int FormatRegister(Instruction* instr, const char* option);
  int FormatFloatingRegister(Instruction* instr, const char* option);
  int FormatMask(Instruction* instr, const char* option);
  int FormatDisplacement(Instruction* instr, const char* option);
  int FormatImmediate(Instruction* instr, const char* option);
  int FormatOption(Instruction* instr, const char* option);
  void Format(Instruction* instr, const char* format);
  void Unknown(Instruction* instr);
  void UnknownFormat(Instruction* instr, const char* opcname);

  bool DecodeTwoByte(Instruction* instr);
  bool DecodeFourByte(Instruction* instr);
  bool DecodeSixByte(Instruction* instr);

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

  DISALLOW_COPY_AND_ASSIGN(Decoder);
};

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

// Print the double FP register name according to the active name converter.
void Decoder::PrintDRegister(int reg) {
  Print(GetRegConfig()->GetDoubleRegisterName(reg));
}

// Print SoftwareInterrupt codes. Factoring this out reduces the complexity of
// the FormatOption method.
void Decoder::PrintSoftwareInterrupt(SoftwareInterruptCodes svc) {
  switch (svc) {
    case kCallRtRedirected:
      Print("call rt redirected");
      return;
    case kBreakpoint:
      Print("breakpoint");
      return;
    default:
      if (svc >= kStopCode) {
        out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d - 0x%x",
                                    svc & kStopCodeMask, svc & kStopCodeMask);
      } else {
        out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", svc);
      }
      return;
  }
}

// Handle all register based formatting in this function to reduce the
// complexity of FormatOption.
int Decoder::FormatRegister(Instruction* instr, const char* format) {
  DCHECK(format[0] == 'r');

  if (format[1] == '1') {  // 'r1: register resides in bit 8-11
    RRInstruction* rrinstr = reinterpret_cast<RRInstruction*>(instr);
    int reg = rrinstr->R1Value();
    PrintRegister(reg);
    return 2;
  } else if (format[1] == '2') {  // 'r2: register resides in bit 12-15
    RRInstruction* rrinstr = reinterpret_cast<RRInstruction*>(instr);
    int reg = rrinstr->R2Value();
    // indicating it is a r0 for displacement, in which case the offset
    // should be 0.
    if (format[2] == 'd') {
      if (reg == 0) return 4;
      PrintRegister(reg);
      return 3;
    } else {
      PrintRegister(reg);
      return 2;
    }
  } else if (format[1] == '3') {  // 'r3: register resides in bit 16-19
    RSInstruction* rsinstr = reinterpret_cast<RSInstruction*>(instr);
    int reg = rsinstr->B2Value();
    PrintRegister(reg);
    return 2;
  } else if (format[1] == '4') {  // 'r4: register resides in bit 20-23
    RSInstruction* rsinstr = reinterpret_cast<RSInstruction*>(instr);
    int reg = rsinstr->B2Value();
    PrintRegister(reg);
    return 2;
  } else if (format[1] == '5') {  // 'r5: register resides in bit 24-28
    RREInstruction* rreinstr = reinterpret_cast<RREInstruction*>(instr);
    int reg = rreinstr->R1Value();
    PrintRegister(reg);
    return 2;
  } else if (format[1] == '6') {  // 'r6: register resides in bit 29-32
    RREInstruction* rreinstr = reinterpret_cast<RREInstruction*>(instr);
    int reg = rreinstr->R2Value();
    PrintRegister(reg);
    return 2;
  } else if (format[1] == '7') {  // 'r6: register resides in bit 32-35
    SSInstruction* ssinstr = reinterpret_cast<SSInstruction*>(instr);
    int reg = ssinstr->B2Value();
    PrintRegister(reg);
    return 2;
  }

  UNREACHABLE();
  return -1;
}

int Decoder::FormatFloatingRegister(Instruction* instr, const char* format) {
  DCHECK(format[0] == 'f');

  // reuse 1, 5 and 6 because it is coresponding
  if (format[1] == '1') {  // 'r1: register resides in bit 8-11
    RRInstruction* rrinstr = reinterpret_cast<RRInstruction*>(instr);
    int reg = rrinstr->R1Value();
    PrintDRegister(reg);
    return 2;
  } else if (format[1] == '2') {  // 'f2: register resides in bit 12-15
    RRInstruction* rrinstr = reinterpret_cast<RRInstruction*>(instr);
    int reg = rrinstr->R2Value();
    PrintDRegister(reg);
    return 2;
  } else if (format[1] == '3') {  // 'f3: register resides in bit 16-19
    RRDInstruction* rrdinstr = reinterpret_cast<RRDInstruction*>(instr);
    int reg = rrdinstr->R1Value();
    PrintDRegister(reg);
    return 2;
  } else if (format[1] == '5') {  // 'f5: register resides in bit 24-28
    RREInstruction* rreinstr = reinterpret_cast<RREInstruction*>(instr);
    int reg = rreinstr->R1Value();
    PrintDRegister(reg);
    return 2;
  } else if (format[1] == '6') {  // 'f6: register resides in bit 29-32
    RREInstruction* rreinstr = reinterpret_cast<RREInstruction*>(instr);
    int reg = rreinstr->R2Value();
    PrintDRegister(reg);
    return 2;
  }
  UNREACHABLE();
  return -1;
}

// 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 'o': {
      if (instr->Bit(10) == 1) {
        Print("o");
      }
      return 1;
    }
    case '.': {
      if (instr->Bit(0) == 1) {
        Print(".");
      } else {
        Print(" ");  // ensure consistent spacing
      }
      return 1;
    }
    case 'r': {
      return FormatRegister(instr, format);
    }
    case 'f': {
      return FormatFloatingRegister(instr, format);
    }
    case 'i': {  // int16
      return FormatImmediate(instr, format);
    }
    case 'u': {  // uint16
      int32_t value = instr->Bits(15, 0);
      out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", value);
      return 6;
    }
    case 'l': {
      // Link (LK) Bit 0
      if (instr->Bit(0) == 1) {
        Print("l");
      }
      return 1;
    }
    case 'a': {
      // Absolute Address Bit 1
      if (instr->Bit(1) == 1) {
        Print("a");
      }
      return 1;
    }
    case 't': {  // 'target: target of branch instructions
      // target26 or target16
      DCHECK(STRING_STARTS_WITH(format, "target"));
      if ((format[6] == '2') && (format[7] == '6')) {
        int off = ((instr->Bits(25, 2)) << 8) >> 6;
        out_buffer_pos_ += SNPrintF(
            out_buffer_ + out_buffer_pos_, "%+d -> %s", off,
            converter_.NameOfAddress(reinterpret_cast<byte*>(instr) + off));
        return 8;
      } else if ((format[6] == '1') && (format[7] == '6')) {
        int off = ((instr->Bits(15, 2)) << 18) >> 16;
        out_buffer_pos_ += SNPrintF(
            out_buffer_ + out_buffer_pos_, "%+d -> %s", off,
            converter_.NameOfAddress(reinterpret_cast<byte*>(instr) + off));
        return 8;
      }
      case 'm': {
        return FormatMask(instr, format);
      }
    }
    case 'd': {  // ds value for offset
      return FormatDisplacement(instr, format);
    }
    default: {
      UNREACHABLE();
      break;
    }
  }

  UNREACHABLE();
  return -1;
}

int Decoder::FormatMask(Instruction* instr, const char* format) {
  DCHECK(format[0] == 'm');
  int32_t value = 0;
  if ((format[1] == '1')) {  // prints the mask format in bits 8-12
    value = reinterpret_cast<RRInstruction*>(instr)->R1Value();
    out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "0x%x", value);
    return 2;
  } else if (format[1] == '2') {  // mask format in bits 16-19
    value = reinterpret_cast<RXInstruction*>(instr)->B2Value();
    out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "0x%x", value);
    return 2;
  } else if (format[1] == '3') {  // mask format in bits 20-23
    value = reinterpret_cast<RRFInstruction*>(instr)->M4Value();
    out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "0x%x", value);
    return 2;
  }

  out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", value);
  return 2;
}

int Decoder::FormatDisplacement(Instruction* instr, const char* format) {
  DCHECK(format[0] == 'd');

  if (format[1] == '1') {  // displacement in 20-31
    RSInstruction* rsinstr = reinterpret_cast<RSInstruction*>(instr);
    uint16_t value = rsinstr->D2Value();
    out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", value);

    return 2;
  } else if (format[1] == '2') {  // displacement in 20-39
    RXYInstruction* rxyinstr = reinterpret_cast<RXYInstruction*>(instr);
    int32_t value = rxyinstr->D2Value();
    out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", value);
    return 2;
  } else if (format[1] == '4') {  // SS displacement 2 36-47
    SSInstruction* ssInstr = reinterpret_cast<SSInstruction*>(instr);
    uint16_t value = ssInstr->D2Value();
    out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", value);
    return 2;
  } else if (format[1] == '3') {  // SS displacement 1 20 - 32
    SSInstruction* ssInstr = reinterpret_cast<SSInstruction*>(instr);
    uint16_t value = ssInstr->D1Value();
    out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", value);
    return 2;
  } else {  // s390 specific
    int32_t value = SIGN_EXT_IMM16(instr->Bits(15, 0) & ~3);
    out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", value);
    return 1;
  }
}

int Decoder::FormatImmediate(Instruction* instr, const char* format) {
  DCHECK(format[0] == 'i');

  if (format[1] == '1') {  // immediate in 16-31
    RIInstruction* riinstr = reinterpret_cast<RIInstruction*>(instr);
    int16_t value = riinstr->I2Value();
    out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", value);
    return 2;
  } else if (format[1] == '2') {  // immediate in 16-48
    RILInstruction* rilinstr = reinterpret_cast<RILInstruction*>(instr);
    int32_t value = rilinstr->I2Value();
    out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", value);
    return 2;
  } else if (format[1] == '3') {  // immediate in I format
    IInstruction* iinstr = reinterpret_cast<IInstruction*>(instr);
    int8_t value = iinstr->IValue();
    out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", value);
    return 2;
  } else if (format[1] == '4') {  // immediate in 16-31, but outputs as offset
    RIInstruction* riinstr = reinterpret_cast<RIInstruction*>(instr);
    int16_t value = riinstr->I2Value() * 2;
    if (value >= 0)
      out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "*+");
    else
      out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "*");

    out_buffer_pos_ += SNPrintF(
        out_buffer_ + out_buffer_pos_, "%d -> %s", value,
        converter_.NameOfAddress(reinterpret_cast<byte*>(instr) + value));
    return 2;
  } else if (format[1] == '5') {  // immediate in 16-31, but outputs as offset
    RILInstruction* rilinstr = reinterpret_cast<RILInstruction*>(instr);
    int32_t value = rilinstr->I2Value() * 2;
    if (value >= 0)
      out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "*+");
    else
      out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "*");

    out_buffer_pos_ += SNPrintF(
        out_buffer_ + out_buffer_pos_, "%d -> %s", value,
        converter_.NameOfAddress(reinterpret_cast<byte*>(instr) + value));
    return 2;
  } else if (format[1] == '6') {  // unsigned immediate in 16-31
    RIInstruction* riinstr = reinterpret_cast<RIInstruction*>(instr);
    uint16_t value = riinstr->I2UnsignedValue();
    out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", value);
    return 2;
  } else if (format[1] == '7') {  // unsigned immediate in 16-47
    RILInstruction* rilinstr = reinterpret_cast<RILInstruction*>(instr);
    uint32_t value = rilinstr->I2UnsignedValue();
    out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", value);
    return 2;
  } else if (format[1] == '8') {  // unsigned immediate in 8-15
    SSInstruction* ssinstr = reinterpret_cast<SSInstruction*>(instr);
    uint8_t value = ssinstr->Length();
    out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", value);
    return 2;
  } else if (format[1] == '9') {  // unsigned immediate in 16-23
    RIEInstruction* rie_instr = reinterpret_cast<RIEInstruction*>(instr);
    uint8_t value = rie_instr->I3Value();
    out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", value);
    return 2;
  } else if (format[1] == 'a') {  // unsigned immediate in 24-31
    RIEInstruction* rie_instr = reinterpret_cast<RIEInstruction*>(instr);
    uint8_t value = rie_instr->I4Value();
    out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", value);
    return 2;
  } else if (format[1] == 'b') {  // unsigned immediate in 32-39
    RIEInstruction* rie_instr = reinterpret_cast<RIEInstruction*>(instr);
    uint8_t value = rie_instr->I5Value();
    out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", value);
    return 2;
  } else if (format[1] == 'c') {  // signed immediate in 8-15
    SSInstruction* ssinstr = reinterpret_cast<SSInstruction*>(instr);
    int8_t value = ssinstr->Length();
    out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", value);
    return 2;
  } else if (format[1] == 'd') {  // signed immediate in 32-47
    SILInstruction* silinstr = reinterpret_cast<SILInstruction*>(instr);
    int16_t value = silinstr->I2Value();
    out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", value);
    return 2;
  } else if (format[1] == 'e') {  // immediate in 16-47, but outputs as offset
    RILInstruction* rilinstr = reinterpret_cast<RILInstruction*>(instr);
    int32_t value = rilinstr->I2Value() * 2;
    if (value >= 0)
      out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "*+");
    else
      out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "*");

    out_buffer_pos_ += SNPrintF(
        out_buffer_ + out_buffer_pos_, "%d -> %s", value,
        converter_.NameOfAddress(reinterpret_cast<byte*>(instr) + value));
    return 2;
  }

  UNREACHABLE();
  return -1;
}

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

// The disassembler may end up decoding data inlined in the code. We do not want
// it to crash if the data does not ressemble any known instruction.
#define VERIFY(condition) \
  if (!(condition)) {     \
    Unknown(instr);       \
    return;               \
  }

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

// For currently unimplemented decodings the disassembler calls
// UnknownFormat(instr) which will just print opcode name of the
// instruction bits.
void Decoder::UnknownFormat(Instruction* instr, const char* name) {
  char buffer[100];
  snprintf(buffer, sizeof(buffer), "%s (unknown-format)", name);
  Format(instr, buffer);
}

// Disassembles Two Byte S390 Instructions
// @return true if successfully decoded
bool Decoder::DecodeTwoByte(Instruction* instr) {
  // Print the Instruction bits.
  out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%04x           ",
                              instr->InstructionBits<TwoByteInstr>());

  Opcode opcode = instr->S390OpcodeValue();
  switch (opcode) {
    case AR:
      Format(instr, "ar\t'r1,'r2");
      break;
    case SR:
      Format(instr, "sr\t'r1,'r2");
      break;
    case MR:
      Format(instr, "mr\t'r1,'r2");
      break;
    case DR:
      Format(instr, "dr\t'r1,'r2");
      break;
    case OR:
      Format(instr, "or\t'r1,'r2");
      break;
    case NR:
      Format(instr, "nr\t'r1,'r2");
      break;
    case XR:
      Format(instr, "xr\t'r1,'r2");
      break;
    case LR:
      Format(instr, "lr\t'r1,'r2");
      break;
    case CR:
      Format(instr, "cr\t'r1,'r2");
      break;
    case CLR:
      Format(instr, "clr\t'r1,'r2");
      break;
    case BCR:
      Format(instr, "bcr\t'm1,'r2");
      break;
    case LTR:
      Format(instr, "ltr\t'r1,'r2");
      break;
    case ALR:
      Format(instr, "alr\t'r1,'r2");
      break;
    case SLR:
      Format(instr, "slr\t'r1,'r2");
      break;
    case LNR:
      Format(instr, "lnr\t'r1,'r2");
      break;
    case LCR:
      Format(instr, "lcr\t'r1,'r2");
      break;
    case BASR:
      Format(instr, "basr\t'r1,'r2");
      break;
    case LDR:
      Format(instr, "ldr\t'f1,'f2");
      break;
    case BKPT:
      Format(instr, "bkpt");
      break;
    case LPR:
      Format(instr, "lpr\t'r1, 'r2");
      break;
    default:
      return false;
  }
  return true;
}

// Disassembles Four Byte S390 Instructions
// @return true if successfully decoded
bool Decoder::DecodeFourByte(Instruction* instr) {
  // Print the Instruction bits.
  out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%08x       ",
                              instr->InstructionBits<FourByteInstr>());

  Opcode opcode = instr->S390OpcodeValue();
  switch (opcode) {
    case AHI:
      Format(instr, "ahi\t'r1,'i1");
      break;
    case AGHI:
      Format(instr, "aghi\t'r1,'i1");
      break;
    case LHI:
      Format(instr, "lhi\t'r1,'i1");
      break;
    case LGHI:
      Format(instr, "lghi\t'r1,'i1");
      break;
    case MHI:
      Format(instr, "mhi\t'r1,'i1");
      break;
    case MGHI:
      Format(instr, "mghi\t'r1,'i1");
      break;
    case CHI:
      Format(instr, "chi\t'r1,'i1");
      break;
    case CGHI:
      Format(instr, "cghi\t'r1,'i1");
      break;
    case BRAS:
      Format(instr, "bras\t'r1,'i1");
      break;
    case BRC:
      Format(instr, "brc\t'm1,'i4");
      break;
    case BRCT:
      Format(instr, "brct\t'r1,'i4");
      break;
    case BRCTG:
      Format(instr, "brctg\t'r1,'i4");
      break;
    case IIHH:
      Format(instr, "iihh\t'r1,'i1");
      break;
    case IIHL:
      Format(instr, "iihl\t'r1,'i1");
      break;
    case IILH:
      Format(instr, "iilh\t'r1,'i1");
      break;
    case IILL:
      Format(instr, "iill\t'r1,'i1");
      break;
    case OILL:
      Format(instr, "oill\t'r1,'i1");
      break;
    case TMLL:
      Format(instr, "tmll\t'r1,'i1");
      break;
    case STM:
      Format(instr, "stm\t'r1,'r2,'d1('r3)");
      break;
    case LM:
      Format(instr, "lm\t'r1,'r2,'d1('r3)");
      break;
    case SLL:
      Format(instr, "sll\t'r1,'d1('r3)");
      break;
    case SRL:
      Format(instr, "srl\t'r1,'d1('r3)");
      break;
    case SLA:
      Format(instr, "sla\t'r1,'d1('r3)");
      break;
    case SRA:
      Format(instr, "sra\t'r1,'d1('r3)");
      break;
    case SLDL:
      Format(instr, "sldl\t'r1,'d1('r3)");
      break;
    case AGR:
      Format(instr, "agr\t'r5,'r6");
      break;
    case AGFR:
      Format(instr, "agfr\t'r5,'r6");
      break;
    case ARK:
      Format(instr, "ark\t'r5,'r6,'r3");
      break;
    case AGRK:
      Format(instr, "agrk\t'r5,'r6,'r3");
      break;
    case SGR:
      Format(instr, "sgr\t'r5,'r6");
      break;
    case SGFR:
      Format(instr, "sgfr\t'r5,'r6");
      break;
    case SRK:
      Format(instr, "srk\t'r5,'r6,'r3");
      break;
    case SGRK:
      Format(instr, "sgrk\t'r5,'r6,'r3");
      break;
    case NGR:
      Format(instr, "ngr\t'r5,'r6");
      break;
    case NRK:
      Format(instr, "nrk\t'r5,'r6,'r3");
      break;
    case NGRK:
      Format(instr, "ngrk\t'r5,'r6,'r3");
      break;
    case NILL:
      Format(instr, "nill\t'r1,'i1");
      break;
    case NILH:
      Format(instr, "nilh\t'r1,'i1");
      break;
    case OGR:
      Format(instr, "ogr\t'r5,'r6");
      break;
    case ORK:
      Format(instr, "ork\t'r5,'r6,'r3");
      break;
    case OGRK:
      Format(instr, "ogrk\t'r5,'r6,'r3");
      break;
    case XGR:
      Format(instr, "xgr\t'r5,'r6");
      break;
    case XRK:
      Format(instr, "xrk\t'r5,'r6,'r3");
      break;
    case XGRK:
      Format(instr, "xgrk\t'r5,'r6,'r3");
      break;
    case CGFR:
      Format(instr, "cgfr\t'r5,'r6");
      break;
    case CGR:
      Format(instr, "cgr\t'r5,'r6");
      break;
    case CLGR:
      Format(instr, "clgr\t'r5,'r6");
      break;
    case LLGFR:
      Format(instr, "llgfr\t'r5,'r6");
      break;
    case POPCNT_Z:
      Format(instr, "popcnt\t'r5,'r6");
      break;
    case LLGCR:
      Format(instr, "llgcr\t'r5,'r6");
      break;
    case LLCR:
      Format(instr, "llcr\t'r5,'r6");
      break;
    case LBR:
      Format(instr, "lbr\t'r5,'r6");
      break;
    case LEDBR:
      Format(instr, "ledbr\t'f5,'f6");
      break;
    case LDEBR:
      Format(instr, "ldebr\t'f5,'f6");
      break;
    case LTGR:
      Format(instr, "ltgr\t'r5,'r6");
      break;
    case LTDBR:
      Format(instr, "ltdbr\t'f5,'f6");
      break;
    case LTEBR:
      Format(instr, "ltebr\t'f5,'f6");
      break;
    case LRVR:
      Format(instr, "lrvr\t'r5,'r6");
      break;
    case LRVGR:
      Format(instr, "lrvgr\t'r5,'r6");
      break;
    case LGR:
      Format(instr, "lgr\t'r5,'r6");
      break;
    case LGDR:
      Format(instr, "lgdr\t'r5,'f6");
      break;
    case LGFR:
      Format(instr, "lgfr\t'r5,'r6");
      break;
    case LTGFR:
      Format(instr, "ltgfr\t'r5,'r6");
      break;
    case LCGR:
      Format(instr, "lcgr\t'r5,'r6");
      break;
    case MSR:
      Format(instr, "msr\t'r5,'r6");
      break;
    case MSRKC:
      Format(instr, "msrkc\t'r5,'r6,'r3");
      break;
    case LGBR:
      Format(instr, "lgbr\t'r5,'r6");
      break;
    case LGHR:
      Format(instr, "lghr\t'r5,'r6");
      break;
    case MSGR:
      Format(instr, "msgr\t'r5,'r6");
      break;
    case MSGRKC:
      Format(instr, "msgrkc\t'r5,'r6,'r3");
      break;
    case DSGR:
      Format(instr, "dsgr\t'r5,'r6");
      break;
    case DSGFR:
      Format(instr, "dsgfr\t'r5,'r6");
      break;
    case MSGFR:
      Format(instr, "msgfr\t'r5,'r6");
      break;
    case LZDR:
      Format(instr, "lzdr\t'f5");
      break;
    case MLR:
      Format(instr, "mlr\t'r5,'r6");
      break;
    case MLGR:
      Format(instr, "mlgr\t'r5,'r6");
      break;
    case ALCR:
      Format(instr, "alcr\t'r5,'r6");
      break;
    case ALGR:
      Format(instr, "algr\t'r5,'r6");
      break;
    case ALRK:
      Format(instr, "alrk\t'r5,'r6,'r3");
      break;
    case ALGRK:
      Format(instr, "algrk\t'r5,'r6,'r3");
      break;
    case SLGR:
      Format(instr, "slgr\t'r5,'r6");
      break;
    case SLBR:
      Format(instr, "slbr\t'r5,'r6");
      break;
    case DLR:
      Format(instr, "dlr\t'r5,'r6");
      break;
    case DLGR:
      Format(instr, "dlgr\t'r5,'r6");
      break;
    case SLRK:
      Format(instr, "slrk\t'r5,'r6,'r3");
      break;
    case SLGRK:
      Format(instr, "slgrk\t'r5,'r6,'r3");
      break;
    case LHR:
      Format(instr, "lhr\t'r5,'r6");
      break;
    case LLHR:
      Format(instr, "llhr\t'r5,'r6");
      break;
    case LLGHR:
      Format(instr, "llghr\t'r5,'r6");
      break;
    case LOCR:
      Format(instr, "locr\t'm1,'r5,'r6");
      break;
    case LOCGR:
      Format(instr, "locgr\t'm1,'r5,'r6");
      break;
    case LNGR:
      Format(instr, "lngr\t'r5,'r6");
      break;
    case A:
      Format(instr, "a\t'r1,'d1('r2d,'r3)");
      break;
    case S:
      Format(instr, "s\t'r1,'d1('r2d,'r3)");
      break;
    case M:
      Format(instr, "m\t'r1,'d1('r2d,'r3)");
      break;
    case D:
      Format(instr, "d\t'r1,'d1('r2d,'r3)");
      break;
    case O:
      Format(instr, "o\t'r1,'d1('r2d,'r3)");
      break;
    case N:
      Format(instr, "n\t'r1,'d1('r2d,'r3)");
      break;
    case L:
      Format(instr, "l\t'r1,'d1('r2d,'r3)");
      break;
    case C:
      Format(instr, "c\t'r1,'d1('r2d,'r3)");
      break;
    case AH:
      Format(instr, "ah\t'r1,'d1('r2d,'r3)");
      break;
    case SH:
      Format(instr, "sh\t'r1,'d1('r2d,'r3)");
      break;
    case MH:
      Format(instr, "mh\t'r1,'d1('r2d,'r3)");
      break;
    case AL:
      Format(instr, "al\t'r1,'d1('r2d,'r3)");
      break;
    case SL:
      Format(instr, "sl\t'r1,'d1('r2d,'r3)");
      break;
    case LA:
      Format(instr, "la\t'r1,'d1('r2d,'r3)");
      break;
    case CH:
      Format(instr, "ch\t'r1,'d1('r2d,'r3)");
      break;
    case CL:
      Format(instr, "cl\t'r1,'d1('r2d,'r3)");
      break;
    case CLI:
      Format(instr, "cli\t'd1('r3),'i8");
      break;
    case TM:
      Format(instr, "tm\t'd1('r3),'i8");
      break;
    case BC:
      Format(instr, "bc\t'm1,'d1('r2d,'r3)");
      break;
    case BCT:
      Format(instr, "bct\t'r1,'d1('r2d,'r3)");
      break;
    case ST:
      Format(instr, "st\t'r1,'d1('r2d,'r3)");
      break;
    case STC:
      Format(instr, "stc\t'r1,'d1('r2d,'r3)");
      break;
    case IC_z:
      Format(instr, "ic\t'r1,'d1('r2d,'r3)");
      break;
    case LD:
      Format(instr, "ld\t'f1,'d1('r2d,'r3)");
      break;
    case LE:
      Format(instr, "le\t'f1,'d1('r2d,'r3)");
      break;
    case LDGR:
      Format(instr, "ldgr\t'f5,'r6");
      break;
    case MS:
      Format(instr, "ms\t'r1,'d1('r2d,'r3)");
      break;
    case STE:
      Format(instr, "ste\t'f1,'d1('r2d,'r3)");
      break;
    case STD:
      Format(instr, "std\t'f1,'d1('r2d,'r3)");
      break;
    case CFDBR:
      Format(instr, "cfdbr\t'r5,'m2,'f6");
      break;
    case CDFBR:
      Format(instr, "cdfbr\t'f5,'m2,'r6");
      break;
    case CFEBR:
      Format(instr, "cfebr\t'r5,'m2,'f6");
      break;
    case CEFBR:
      Format(instr, "cefbr\t'f5,'m2,'r6");
      break;
    case CELFBR:
      Format(instr, "celfbr\t'f5,'m2,'r6");
      break;
    case CGEBR:
      Format(instr, "cgebr\t'r5,'m2,'f6");
      break;
    case CGDBR:
      Format(instr, "cgdbr\t'r5,'m2,'f6");
      break;
    case CEGBR:
      Format(instr, "cegbr\t'f5,'m2,'r6");
      break;
    case CDGBR:
      Format(instr, "cdgbr\t'f5,'m2,'r6");
      break;
    case CDLFBR:
      Format(instr, "cdlfbr\t'f5,'m2,'r6");
      break;
    case CDLGBR:
      Format(instr, "cdlgbr\t'f5,'m2,'r6");
      break;
    case CELGBR:
      Format(instr, "celgbr\t'f5,'m2,'r6");
      break;
    case CLFDBR:
      Format(instr, "clfdbr\t'r5,'m2,'f6");
      break;
    case CLFEBR:
      Format(instr, "clfebr\t'r5,'m2,'f6");
      break;
    case CLGEBR:
      Format(instr, "clgebr\t'r5,'m2,'f6");
      break;
    case CLGDBR:
      Format(instr, "clgdbr\t'r5,'m2,'f6");
      break;
    case AEBR:
      Format(instr, "aebr\t'f5,'f6");
      break;
    case SEBR:
      Format(instr, "sebr\t'f5,'f6");
      break;
    case MEEBR:
      Format(instr, "meebr\t'f5,'f6");
      break;
    case DEBR:
      Format(instr, "debr\t'f5,'f6");
      break;
    case ADBR:
      Format(instr, "adbr\t'f5,'f6");
      break;
    case SDBR:
      Format(instr, "sdbr\t'f5,'f6");
      break;
    case MDBR:
      Format(instr, "mdbr\t'f5,'f6");
      break;
    case DDBR:
      Format(instr, "ddbr\t'f5,'f6");
      break;
    case CDBR:
      Format(instr, "cdbr\t'f5,'f6");
      break;
    case CEBR:
      Format(instr, "cebr\t'f5,'f6");
      break;
    case SQDBR:
      Format(instr, "sqdbr\t'f5,'f6");
      break;
    case SQEBR:
      Format(instr, "sqebr\t'f5,'f6");
      break;
    case LCDBR:
      Format(instr, "lcdbr\t'f5,'f6");
      break;
    case LCEBR:
      Format(instr, "lcebr\t'f5,'f6");
      break;
    case STH:
      Format(instr, "sth\t'r1,'d1('r2d,'r3)");
      break;
    case SRDA:
      Format(instr, "srda\t'r1,'d1('r3)");
      break;
    case SRDL:
      Format(instr, "srdl\t'r1,'d1('r3)");
      break;
    case MADBR:
      Format(instr, "madbr\t'f3,'f5,'f6");
      break;
    case MSDBR:
      Format(instr, "msdbr\t'f3,'f5,'f6");
      break;
    case FLOGR:
      Format(instr, "flogr\t'r5,'r6");
      break;
    case FIEBRA:
      Format(instr, "fiebra\t'f5,'m2,'f6,'m3");
      break;
    case FIDBRA:
      Format(instr, "fidbra\t'f5,'m2,'f6,'m3");
      break;
    // TRAP4 is used in calling to native function. it will not be generated
    // in native code.
    case TRAP4: {
      Format(instr, "trap4");
      break;
    }
    case LPGR:
      Format(instr, "lpgr\t'r1, 'r2");
      break;
    case LPGFR:
      Format(instr, "lpgfr\t'r1,'r2");
      break;
    default:
      return false;
  }
  return true;
}

// Disassembles Six Byte S390 Instructions
// @return true if successfully decoded
bool Decoder::DecodeSixByte(Instruction* instr) {
  // Print the Instruction bits.
  out_buffer_pos_ +=
      SNPrintF(out_buffer_ + out_buffer_pos_, "%012" PRIx64 "   ",
               instr->InstructionBits<SixByteInstr>());

  Opcode opcode = instr->S390OpcodeValue();
  switch (opcode) {
    case DUMY:
      Format(instr, "dumy\t'r1, 'd2 ( 'r2d, 'r3 )");
      break;
#define DECODE_VRR_C_INSTRUCTIONS(name, opcode_name, opcode_value) \
  case opcode_name:                                                \
    Format(instr, #name "\t'f1,'f2,'f3");                          \
    break;
      S390_VRR_C_OPCODE_LIST(DECODE_VRR_C_INSTRUCTIONS)
#undef DECODE_VRR_C_INSTRUCTIONS
    case LLILF:
      Format(instr, "llilf\t'r1,'i7");
      break;
    case LLIHF:
      Format(instr, "llihf\t'r1,'i7");
      break;
    case AFI:
      Format(instr, "afi\t'r1,'i7");
      break;
    case AIH:
      Format(instr, "aih\t'r1,'i7");
      break;
    case ASI:
      Format(instr, "asi\t'd2('r3),'ic");
      break;
    case AGSI:
      Format(instr, "agsi\t'd2('r3),'ic");
      break;
    case ALFI:
      Format(instr, "alfi\t'r1,'i7");
      break;
    case AHIK:
      Format(instr, "ahik\t'r1,'r2,'i1");
      break;
    case AGHIK:
      Format(instr, "aghik\t'r1,'r2,'i1");
      break;
    case CLGFI:
      Format(instr, "clgfi\t'r1,'i7");
      break;
    case CLFI:
      Format(instr, "clfi\t'r1,'i7");
      break;
    case CLIH:
      Format(instr, "clih\t'r1,'i7");
      break;
    case CIH:
      Format(instr, "cih\t'r1,'i2");
      break;
    case CFI:
      Format(instr, "cfi\t'r1,'i2");
      break;
    case CGFI:
      Format(instr, "cgfi\t'r1,'i2");
      break;
    case BRASL:
      Format(instr, "brasl\t'r1,'ie");
      break;
    case BRCL:
      Format(instr, "brcl\t'm1,'i5");
      break;
    case IIHF:
      Format(instr, "iihf\t'r1,'i7");
      break;
    case LGFI:
      Format(instr, "lgfi\t'r1,'i7");
      break;
    case IILF:
      Format(instr, "iilf\t'r1,'i7");
      break;
    case XIHF:
      Format(instr, "xihf\t'r1,'i7");
      break;
    case XILF:
      Format(instr, "xilf\t'r1,'i7");
      break;
    case SLLK:
      Format(instr, "sllk\t'r1,'r2,'d2('r3)");
      break;
    case SLLG:
      Format(instr, "sllg\t'r1,'r2,'d2('r3)");
      break;
    case RLL:
      Format(instr, "rll\t'r1,'r2,'d2('r3)");
      break;
    case RLLG:
      Format(instr, "rllg\t'r1,'r2,'d2('r3)");
      break;
    case SRLK:
      Format(instr, "srlk\t'r1,'r2,'d2('r3)");
      break;
    case SRLG:
      Format(instr, "srlg\t'r1,'r2,'d2('r3)");
      break;
    case SLAK:
      Format(instr, "slak\t'r1,'r2,'d2('r3)");
      break;
    case SLAG:
      Format(instr, "slag\t'r1,'r2,'d2('r3)");
      break;
    case SRAK:
      Format(instr, "srak\t'r1,'r2,'d2('r3)");
      break;
    case SRAG:
      Format(instr, "srag\t'r1,'r2,'d2('r3)");
      break;
    case RISBG:
      Format(instr, "risbg\t'r1,'r2,'i9,'ia,'ib");
      break;
    case RISBGN:
      Format(instr, "risbgn\t'r1,'r2,'i9,'ia,'ib");
      break;
    case LOCG:
      Format(instr, "locg\t'm2,'r1,'d2('r3)");
      break;
    case LOC:
      Format(instr, "loc\t'm2,'r1,'d2('r3)");
      break;
    case LMY:
      Format(instr, "lmy\t'r1,'r2,'d2('r3)");
      break;
    case LMG:
      Format(instr, "lmg\t'r1,'r2,'d2('r3)");
      break;
    case STMY:
      Format(instr, "stmy\t'r1,'r2,'d2('r3)");
      break;
    case STMG:
      Format(instr, "stmg\t'r1,'r2,'d2('r3)");
      break;
    case LT:
      Format(instr, "lt\t'r1,'d2('r2d,'r3)");
      break;
    case LTG:
      Format(instr, "ltg\t'r1,'d2('r2d,'r3)");
      break;
    case ML:
      Format(instr, "ml\t'r1,'d2('r2d,'r3)");
      break;
    case AY:
      Format(instr, "ay\t'r1,'d2('r2d,'r3)");
      break;
    case SY:
      Format(instr, "sy\t'r1,'d2('r2d,'r3)");
      break;
    case NY:
      Format(instr, "ny\t'r1,'d2('r2d,'r3)");
      break;
    case OY:
      Format(instr, "oy\t'r1,'d2('r2d,'r3)");
      break;
    case XY:
      Format(instr, "xy\t'r1,'d2('r2d,'r3)");
      break;
    case CY:
      Format(instr, "cy\t'r1,'d2('r2d,'r3)");
      break;
    case AHY:
      Format(instr, "ahy\t'r1,'d2('r2d,'r3)");
      break;
    case SHY:
      Format(instr, "shy\t'r1,'d2('r2d,'r3)");
      break;
    case LGH:
      Format(instr, "lgh\t'r1,'d2('r2d,'r3)");
      break;
    case AG:
      Format(instr, "ag\t'r1,'d2('r2d,'r3)");
      break;
    case AGF:
      Format(instr, "agf\t'r1,'d2('r2d,'r3)");
      break;
    case SG:
      Format(instr, "sg\t'r1,'d2('r2d,'r3)");
      break;
    case NG:
      Format(instr, "ng\t'r1,'d2('r2d,'r3)");
      break;
    case OG:
      Format(instr, "og\t'r1,'d2('r2d,'r3)");
      break;
    case XG:
      Format(instr, "xg\t'r1,'d2('r2d,'r3)");
      break;
    case CG:
      Format(instr, "cg\t'r1,'d2('r2d,'r3)");
      break;
    case LB:
      Format(instr, "lb\t'r1,'d2('r2d,'r3)");
      break;
    case LRVH:
      Format(instr, "lrvh\t'r1,'d2('r2d,'r3)");
      break;
    case LRV:
      Format(instr, "lrv\t'r1,'d2('r2d,'r3)");
      break;
    case LRVG:
      Format(instr, "lrvg\t'r1,'d2('r2d,'r3)");
      break;
    case LG:
      Format(instr, "lg\t'r1,'d2('r2d,'r3)");
      break;
    case LGF:
      Format(instr, "lgf\t'r1,'d2('r2d,'r3)");
      break;
    case LLGF:
      Format(instr, "llgf\t'r1,'d2('r2d,'r3)");
      break;
    case LY:
      Format(instr, "ly\t'r1,'d2('r2d,'r3)");
      break;
    case ALY:
      Format(instr, "aly\t'r1,'d2('r2d,'r3)");
      break;
    case ALG:
      Format(instr, "alg\t'r1,'d2('r2d,'r3)");
      break;
    case SLG:
      Format(instr, "slg\t'r1,'d2('r2d,'r3)");
      break;
    case SGF:
      Format(instr, "sgf\t'r1,'d2('r2d,'r3)");
      break;
    case SLY:
      Format(instr, "sly\t'r1,'d2('r2d,'r3)");
      break;
    case LLH:
      Format(instr, "llh\t'r1,'d2('r2d,'r3)");
      break;
    case LLGH:
      Format(instr, "llgh\t'r1,'d2('r2d,'r3)");
      break;
    case LLC:
      Format(instr, "llc\t'r1,'d2('r2d,'r3)");
      break;
    case LLGC:
      Format(instr, "llgc\t'r1,'d2('r2d,'r3)");
      break;
    case LDEB:
      Format(instr, "ldeb\t'f1,'d2('r2d,'r3)");
      break;
    case LAY:
      Format(instr, "lay\t'r1,'d2('r2d,'r3)");
      break;
    case LARL:
      Format(instr, "larl\t'r1,'i5");
      break;
    case LGB:
      Format(instr, "lgb\t'r1,'d2('r2d,'r3)");
      break;
    case CHY:
      Format(instr, "chy\t'r1,'d2('r2d,'r3)");
      break;
    case CLY:
      Format(instr, "cly\t'r1,'d2('r2d,'r3)");
      break;
    case CLIY:
      Format(instr, "cliy\t'd2('r3),'i8");
      break;
    case TMY:
      Format(instr, "tmy\t'd2('r3),'i8");
      break;
    case CLG:
      Format(instr, "clg\t'r1,'d2('r2d,'r3)");
      break;
    case BCTG:
      Format(instr, "bctg\t'r1,'d2('r2d,'r3)");
      break;
    case STY:
      Format(instr, "sty\t'r1,'d2('r2d,'r3)");
      break;
    case STRVH:
      Format(instr, "strvh\t'r1,'d2('r2d,'r3)");
      break;
    case STRV:
      Format(instr, "strv\t'r1,'d2('r2d,'r3)");
      break;
    case STRVG:
      Format(instr, "strvg\t'r1,'d2('r2d,'r3)");
      break;
    case STG:
      Format(instr, "stg\t'r1,'d2('r2d,'r3)");
      break;
    case ICY:
      Format(instr, "icy\t'r1,'d2('r2d,'r3)");
      break;
    case MVC:
      Format(instr, "mvc\t'd3('i8,'r3),'d4('r7)");
      break;
    case MVHI:
      Format(instr, "mvhi\t'd3('r3),'id");
      break;
    case MVGHI:
      Format(instr, "mvghi\t'd3('r3),'id");
      break;
    case ALGFI:
      Format(instr, "algfi\t'r1,'i7");
      break;
    case SLGFI:
      Format(instr, "slgfi\t'r1,'i7");
      break;
    case SLFI:
      Format(instr, "slfi\t'r1,'i7");
      break;
    case NIHF:
      Format(instr, "nihf\t'r1,'i7");
      break;
    case NILF:
      Format(instr, "nilf\t'r1,'i7");
      break;
    case OIHF:
      Format(instr, "oihf\t'r1,'i7");
      break;
    case OILF:
      Format(instr, "oilf\t'r1,'i7");
      break;
    case MSFI:
      Format(instr, "msfi\t'r1,'i7");
      break;
    case MSGFI:
      Format(instr, "msgfi\t'r1,'i7");
      break;
    case LDY:
      Format(instr, "ldy\t'f1,'d2('r2d,'r3)");
      break;
    case LEY:
      Format(instr, "ley\t'f1,'d2('r2d,'r3)");
      break;
    case MSG:
      Format(instr, "msg\t'r1,'d2('r2d,'r3)");
      break;
    case DSG:
      Format(instr, "dsg\t'r1,'d2('r2d,'r3)");
      break;
    case DSGF:
      Format(instr, "dsgf\t'r1,'d2('r2d,'r3)");
      break;
    case MSGF:
      Format(instr, "msgf\t'r1,'d2('r2d,'r3)");
      break;
    case MSY:
      Format(instr, "msy\t'r1,'d2('r2d,'r3)");
      break;
    case STEY:
      Format(instr, "stey\t'f1,'d2('r2d,'r3)");
      break;
    case STDY:
      Format(instr, "stdy\t'f1,'d2('r2d,'r3)");
      break;
    case ADB:
      Format(instr, "adb\t'f1,'d1('r2d, 'r3)");
      break;
    case CDB:
      Format(instr, "cdb\t'f1,'d1('r2d, 'r3)");
      break;
    case CEB:
      Format(instr, "ceb\t'f1,'d1('r2d, 'r3)");
      break;
    case SDB:
      Format(instr, "sdb\t'r1,'d1('r2d, 'r3)");
      break;
    case MDB:
      Format(instr, "mdb\t'r1,'d1('r2d, 'r3)");
      break;
    case DDB:
      Format(instr, "ddb\t'r1,'d1('r2d, 'r3)");
      break;
    case SQDB:
      Format(instr, "sqdb\t'r1,'d1('r2d, 'r3)");
      break;
    case PFD:
      Format(instr, "pfd\t'm1,'d2('r2d,'r3)");
      break;
    default:
      return false;
  }
  return true;
}

#undef VERIFIY

// Disassemble the instruction at *instr_ptr into the output buffer.
int Decoder::InstructionDecode(byte* instr_ptr) {
  Instruction* instr = Instruction::At(instr_ptr);
  int instrLength = instr->InstructionLength();

  if (2 == instrLength)
    DecodeTwoByte(instr);
  else if (4 == instrLength)
    DecodeFourByte(instr);
  else
    DecodeSixByte(instr);

  return instrLength;
}

}  // namespace internal
}  // namespace v8

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

namespace disasm {

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

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

const char* NameConverter::NameOfCPURegister(int reg) const {
  return v8::internal::GetRegConfig()->GetGeneralRegisterName(reg);
}

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

const char* NameConverter::NameOfXMMRegister(int reg) const {
  // S390 does not have XMM register
  // TODO(joransiu): Consider update this for Vector Regs
  UNREACHABLE();
  return "noxmmreg";
}

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

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

Disassembler::Disassembler(const NameConverter& converter)
    : converter_(converter) {}

Disassembler::~Disassembler() {}

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

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

void Disassembler::Disassemble(FILE* f, byte* begin, byte* end) {
  NameConverter converter;
  Disassembler d(converter);
  for (byte* pc = begin; pc < end;) {
    v8::internal::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.start());
  }
}

}  // namespace disasm

#endif  // V8_TARGET_ARCH_S390