/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-â
âvi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8                                :viâ
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ¡
â Copyright 2020 Justine Alexandra Roberts Tunney                              â
â                                                                              â
â Permission to use, copy, modify, and/or distribute this software for         â
â any purpose with or without fee is hereby granted, provided that the         â
â above copyright notice and this permission notice appear in all copies.      â
â                                                                              â
â THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL                â
â WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED                â
â WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE             â
â AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL         â
â DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR        â
â PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER               â
â TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR             â
â PERFORMANCE OF THIS SOFTWARE.                                                â
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ*/
#include "libc/str/str.h"
#include "third_party/xed/x86.h"
#include "tool/build/lib/case.h"
#include "tool/build/lib/modrm.h"

#define UNKNOWN     "wut"
#define RCASE(x, y) CASE(x, return y)

static const char kFpuName[][8][8] = {
    {"fadd", "fmul", "fcom", "fcomp", "fsub", "fsubr", "fdiv", "fdivr"},
    {"fchs", "fabs", UNKNOWN, UNKNOWN, "ftst", "fxam", UNKNOWN, UNKNOWN},
    {"fld1", "fldl2t", "fldl2e", "fldpi", "fldlg2", "fldln2", "fldz"},
    {"f2xm1", "fyl2x", "fptan", "fpatan", "fxtract", "fprem1", "fdecstp",
     "fincstp"},
    {"fprem", "fyl2xp1", "fsqrt", "fsincos", "frndint", "fscale", "fsin",
     "fcos"},
    {"fneni", "fndisi", "fnclex", "fninit", "fnsetpm"},
};

char *DisOpFpu1(struct XedDecodedInst *x, char *p, const char *extra) {
  stpcpy(stpcpy(p, kFpuName[0][ModrmReg(x->op.rde)]), extra);
  return p;
}

char *DisOp66(struct XedDecodedInst *x, char *p, const char *s, const char *a,
              const char *b) {
  stpcpy(stpcpy(p, s), !Osz(x->op.rde) ? a : b);
  return p;
}

char *DisOpVpsWpsVssWss(struct XedDecodedInst *x, char *p, const char *s) {
  return DisOp66(x, p, s, "ps %Vps Wps", "ss %Vss Wss");
}

char *DisOpVpdWpdVpsWps(struct XedDecodedInst *x, char *p, const char *s) {
  return DisOp66(x, p, s, "ps %Vps Wps", "pd %Vpd Wpd");
}

char *DisOpPqQqVdqWdq(struct XedDecodedInst *x, char *p, const char *s) {
  return DisOp66(x, p, s, " %Pq Qq", " %Vdq Wdq");
}

char *DisOpPqQqIbVdqWdqIb(struct XedDecodedInst *x, char *p, const char *s) {
  return DisOp66(x, p, s, " %Pq Qq Ib", " %Vdq Wdq Ib");
}

char *DisOpNqIbUdqIb(struct XedDecodedInst *x, char *p, const char *s) {
  return DisOp66(x, p, s, " %Nq Ib", " %Udq Ib");
}

char *DisOpVpsWpsVssWssVpdWpdVsdWsd(struct XedDecodedInst *x, char *p,
                                    const char *s) {
  char *q = stpcpy(p, s);
  if (Rep(x->op.rde) == 3) {
    stpcpy(q, "ss %Vss Wss");
  } else if (Rep(x->op.rde) == 2) {
    stpcpy(q, "sd %Vsd Wsd");
  } else if (Osz(x->op.rde)) {
    stpcpy(q, "pd %Vpd Wpd");
  } else {
    stpcpy(q, "ps %Vps Wps");
  }
  return p;
}

const char *DisSpecFpu0(struct XedDecodedInst *x, int group) {
  const char *s;
  s = kFpuName[group][ModrmRm(x->op.rde)];
  return *s ? s : UNKNOWN;
}

const char *DisSpecRegMem(struct XedDecodedInst *x, const char *a,
                          const char *b) {
  if (IsModrmRegister(x->op.rde)) {
    return a;
  } else {
    return b;
  }
}

const char *DisSpecRegMemFpu0(struct XedDecodedInst *x, int group,
                              const char *b) {
  return DisSpecRegMem(x, DisSpecFpu0(x, group), b);
}

const char *DisSpecMap0(struct XedDecodedInst *x, char *p) {
  switch (x->op.opcode & 0xff) {
    RCASE(0x00, "ALU Eb %Gb");
    RCASE(0x01, "ALU Evqp %Gvqp");
    RCASE(0x02, "ALU %Gb Eb");
    RCASE(0x03, "ALU %Gvqp Evqp");
    RCASE(0x04, "ALU %al Ib");
    RCASE(0x05, "ALU %rAX Ivds");
    RCASE(0x06, "push %es");
    RCASE(0x07, "pop %es");
    RCASE(0x08, "ALU Eb %Gb");
    RCASE(0x09, "ALU Evqp %Gvqp");
    RCASE(0x0a, "ALU %Gb Eb");
    RCASE(0x0b, "ALU %Gvqp Evqp");
    RCASE(0x0c, "ALU %al Ib");
    RCASE(0x0d, "ALU %rAX Ivds");
    RCASE(0x0e, "push %cs");
    RCASE(0x0f, "pop %cs");
    RCASE(0x10, "ALU Eb %Gb");
    RCASE(0x11, "ALU Evqp %Gvqp");
    RCASE(0x12, "ALU %Gb Eb");
    RCASE(0x13, "ALU %Gvqp Evqp");
    RCASE(0x14, "ALU %al Ib");
    RCASE(0x15, "ALU %rAX Ivds");
    RCASE(0x16, "push %ss");
    RCASE(0x17, "pop %ss");
    RCASE(0x18, "ALU Eb %Gb");
    RCASE(0x19, "ALU Evqp %Gvqp");
    RCASE(0x1a, "ALU %Gb Eb");
    RCASE(0x1b, "ALU %Gvqp Evqp");
    RCASE(0x1c, "ALU %al Ib");
    RCASE(0x1d, "ALU %rAX Ivds");
    RCASE(0x1e, "push %ds");
    RCASE(0x1f, "pop %ds");
    RCASE(0x20, "ALU Eb %Gb");
    RCASE(0x21, "ALU Evqp %Gvqp");
    RCASE(0x22, "ALU %Gb Eb");
    RCASE(0x23, "ALU %Gvqp Evqp");
    RCASE(0x24, "ALU %al Ib");
    RCASE(0x25, "ALU %rAX Ivds");
    RCASE(0x26, "push %es");
    RCASE(0x27, "pop %es");
    RCASE(0x28, "ALU Eb %Gb");
    RCASE(0x29, "ALU Evqp %Gvqp");
    RCASE(0x2a, "ALU %Gb Eb");
    RCASE(0x2b, "ALU %Gvqp Evqp");
    RCASE(0x2c, "ALU %al Ib");
    RCASE(0x2d, "ALU %rAX Ivds");
    RCASE(0x2F, "das");
    RCASE(0x30, "ALU Eb %Gb");
    RCASE(0x31, "ALU Evqp %Gvqp");
    RCASE(0x32, "ALU %Gb Eb");
    RCASE(0x33, "ALU %Gvqp Evqp");
    RCASE(0x34, "ALU %al Ib");
    RCASE(0x35, "ALU %rAX Ivds");
    RCASE(0x37, "aaa");
    RCASE(0x38, "ALU Eb %Gb");
    RCASE(0x39, "ALU Evqp %Gvqp");
    RCASE(0x3A, "ALU %Gb Eb");
    RCASE(0x3B, "ALU %Gvqp Evqp");
    RCASE(0x3C, "ALU %al Ib");
    RCASE(0x3D, "ALU %rAX Ivds");
    RCASE(0x3F, "aas");
    RCASE(0x40 ... 0x47, "inc %Zv");
    RCASE(0x48 ... 0x4f, "dec %Zv");
    RCASE(0x50 ... 0x57, "push %Zvq");
    RCASE(0x58 ... 0x5f, "pop %Zvq");
    RCASE(0x60, "pusha");
    RCASE(0x61, "popa");
    RCASE(0x62, "bound");
    RCASE(0x63, "movslLQ %Gdqp Ed");
    RCASE(0x68, "pushWQ Ivs");
    RCASE(0x69, "imul %Gvqp Evqp Ivds");
    RCASE(0x6A, "pushWQ Ibss");
    RCASE(0x6B, "imul %Gvqp Evqp Ibs");
    RCASE(0x6C, "insb Yb DX");
    RCASE(0x6D, "insWL Yv DX");
    RCASE(0x6E, "outsb DX Xb");
    RCASE(0x6F, "outsWL DX Xv");
    RCASE(0x70 ... 0x7f, "jCC Jbs");
    RCASE(0x80, "ALU2 Eb Ib");
    RCASE(0x81, "ALU2 Evqp Ivds");
    RCASE(0x82, "ALU2 Eb Ib");
    RCASE(0x83, "ALU2 Evqp Ibs");
    RCASE(0x84, "test Eb %Gb");
    RCASE(0x85, "test %Gvqp Evqp");
    RCASE(0x86, "xchg %Gb Eb");
    RCASE(0x87, "xchg %Gvqp Evqp");
    RCASE(0x88, "mov Eb %Gb");
    RCASE(0x89, "mov Evqp %Gvqp");
    RCASE(0x8A, "mov %Gb Eb");
    RCASE(0x8B, "mov %Gvqp Evqp");
    RCASE(0x8C, "mov Evqp %Sw");
    RCASE(0x8D, "lea %Gvqp M");
    RCASE(0x8E, "mov %Sw Evqp");
    RCASE(0x90, "nop");
    RCASE(0x91 ... 0x97, "xchg %Zvqp %rAX");
    RCASE(0x98, "cwtl");
    RCASE(0x99, "cltd");
    RCASE(0x9A, "lcall Pvds Kvds");
    RCASE(0x9B, "fwait");
    RCASE(0x9C, "pushfWQ");
    RCASE(0x9D, "popfWQ");
    RCASE(0x9E, "sahf");
    RCASE(0x9F, "lahf");
    RCASE(0xA0, "movABS %al Ob");
    RCASE(0xA1, "movABS %rAX Ovqp");
    RCASE(0xA2, "movABS Ob %al");
    RCASE(0xA3, "movABS Ovqp %rAX");
    RCASE(0xA4, "movsb Yb Xb");
    RCASE(0xA5, "movsWLQ Yvqp Xvqp");
    RCASE(0xA6, "cmpsb Yb Xb");
    RCASE(0xA7, "cmpsWLQ Yvqp Xvqp");
    RCASE(0xA8, "test %al Ib");
    RCASE(0xA9, "test %rAX Ivds");
    RCASE(0xAA, "stosb Yb %al");
    RCASE(0xAB, "stosWLQ Yvqp %rAX");
    RCASE(0xAC, "lodsb %al Xb");
    RCASE(0xAD, "lodsWLQ %rAX Xvqp");
    RCASE(0xAE, "scasb %al Yb");
    RCASE(0xAF, "scasWLQ %rAX Yvqp");
    RCASE(0xB0 ... 0xb7, "mov %Zb Ib");
    RCASE(0xB8 ... 0xbf, "movABS %Zvqp Ivqp");
    RCASE(0xC0, "BIT Eb Ib");
    RCASE(0xC1, "BIT Evqp Ib");
    RCASE(0xC2, "ret Iw");
    RCASE(0xC3, "ret");
    RCASE(0xC4, "les %Gv Mp");
    RCASE(0xC5, "lds %Gv Mp");
    RCASE(0xC6, "mov Eb Ib");
    RCASE(0xC7, "mov Evqp Ivds");
    RCASE(0xC9, "leave");
    RCASE(0xCA, "lret Iw");
    RCASE(0xCB, "lret");
    RCASE(0xCC, "int3");
    RCASE(0xCD, "int Ib");
    RCASE(0xCE, "into");
    RCASE(0xCF, "iret");
    RCASE(0xD0, "BIT Eb");
    RCASE(0xD1, "BIT Evqp");
    RCASE(0xD2, "BIT Evqp %cl");
    RCASE(0xD3, "BIT Evqp %cl");
    RCASE(0xD4, x->op.uimm0 == 0x0a ? "aam" : "aam Ib");
    RCASE(0xD5, x->op.uimm0 == 0x0a ? "aad" : "aad Ib");
    RCASE(0xD6, "salc");
    RCASE(0xD7, "xlat BBb");
    RCASE(0xE0, "loopne Jbs");
    RCASE(0xE1, "loope Jbs");
    RCASE(0xE2, "loop Jbs");
    RCASE(0xE3, "jcxz Jbs");
    RCASE(0xE4, "in %al Ib");
    RCASE(0xE5, "in %eAX Ib");
    RCASE(0xE6, "out Ib %al");
    RCASE(0xE7, "out Ib %eAX");
    RCASE(0xE8, "call Jvds");
    RCASE(0xE9, "jmp Jvds");
    RCASE(0xEA, "ljmp Rvds Kvds");
    RCASE(0xEB, "jmp Jbs");
    RCASE(0xEC, "in %al DX");
    RCASE(0xED, "in %eAX DX");
    RCASE(0xEE, "out DX %al");
    RCASE(0xEF, "out DX %eAX");
    RCASE(0xF1, "int1");
    RCASE(0xF4, "hlt");
    RCASE(0xF5, "cmc");
    RCASE(0xF8, "clc");
    RCASE(0xF9, "stc");
    RCASE(0xFA, "cli");
    RCASE(0xFB, "sti");
    RCASE(0xFC, "cld");
    RCASE(0xFD, "std");
    case 0x8F:
      switch (ModrmReg(x->op.rde)) {
        RCASE(0, "popWQ Evq");
        default:
          break;
      }
      break;
    case 0xD9:
      switch (ModrmReg(x->op.rde)) {
        RCASE(1, "fxch EST1");
        RCASE(3, "fstps Msr %st");
        RCASE(0, DisSpecRegMem(x, "fld EST", "flds Msr"));
        RCASE(2, DisSpecRegMem(x, "fnop", "fsts Msr %st"));
        RCASE(4, DisSpecRegMemFpu0(x, 1, "fldenv Me"));
        RCASE(5, DisSpecRegMemFpu0(x, 2, "fldcw Mw"));
        RCASE(6, DisSpecRegMemFpu0(x, 3, "fnstenv M"));
        RCASE(7, DisSpecRegMemFpu0(x, 4, "fnstcw Mw"));
      }
      break;
    case 0xDA:
      switch (ModrmReg(x->op.rde)) {
        RCASE(0, DisSpecRegMem(x, "fcmovb %st EST", "fiaddl Mdi"));
        RCASE(1, DisSpecRegMem(x, "fcmove %st EST", "fimull Mdi"));
        RCASE(2, DisSpecRegMem(x, "fcmovbe %st EST", "ficoml Mdi"));
        RCASE(3, DisSpecRegMem(x, "fcmovu %st EST", "ficompl Mdi"));
        RCASE(4, DisSpecRegMem(x, "fisubr Mdi", "fisubl Mdi"));
        RCASE(5, DisSpecRegMem(x, "fucompp", "fisubrl Mdi"));
        RCASE(6, DisSpecRegMem(x, "fidivl Mdi", "UNKNOWN"));
        RCASE(7, DisSpecRegMem(x, "fidivrl Mdi", "UNKNOWN"));
      }
      break;
    case 0xDB:
      switch (ModrmReg(x->op.rde)) {
        RCASE(0, DisSpecRegMem(x, "fcmovnb %st EST", "fildl Mdi"));
        RCASE(1, DisSpecRegMem(x, "fcmovne %st EST", "fisttpl Mdi"));
        RCASE(2, DisSpecRegMem(x, "fcmovnbe %st EST", "fistl Mdi"));
        RCASE(3, DisSpecRegMem(x, "fcmovnu %st EST", "fistpl Mdi"));
        RCASE(4, DisSpecFpu0(x, 5));
        RCASE(5, DisSpecRegMem(x, "fucomi %st EST", "fldt Mer"));
        RCASE(6, DisSpecRegMem(x, "fcomi %st EST", UNKNOWN));
        RCASE(7, DisSpecRegMem(x, UNKNOWN, "fstpt Mer"));
      }
      break;
    case 0xD8:
      return DisOpFpu1(x, p, !IsModrmRegister(x->op.rde) ? "s Msr" : " EST1");
    case 0xDC:
      if (!IsModrmRegister(x->op.rde)) {
        return DisOpFpu1(x, p, "l Mdr");
      } else {
        switch (ModrmReg(x->op.rde)) {
          RCASE(0, "fadd EST %st");
          RCASE(1, "fmul EST %st");
          RCASE(2, "fcom %st EST");
          RCASE(3, "fcomp %st EST");
          RCASE(4, "fsub EST %st");
          RCASE(5, "fsubr EST %st");
          RCASE(6, "fdiv EST %st");
          RCASE(7, "fdivr EST %st");
        }
      }
      break;
    case 0xDD:
      if (!IsModrmRegister(x->op.rde)) {
        switch (ModrmReg(x->op.rde)) {
          RCASE(0, "fldl Mdr");
          RCASE(1, "fisttpll Mqi");
          RCASE(2, "fstl Mdr");
          RCASE(3, "fstpl Mdr");
          RCASE(4, "frstor Mdr");
          RCASE(6, "fnsave Mst");
          RCASE(7, "fnstsw Mst");
        }
      } else {
        switch (ModrmReg(x->op.rde)) {
          RCASE(0, "ffree EST");
          RCASE(1, "fxch EST");
          RCASE(2, "fst EST");
          RCASE(3, "fstp EST");
          RCASE(4, "fucom EST1");
          RCASE(5, "fucomp EST1");
        }
      }
      break;
    case 0xDE:
      if (!IsModrmRegister(x->op.rde)) {
        switch (ModrmReg(x->op.rde)) {
          RCASE(0, "fiadds Mwi");
          RCASE(1, "fimuls Mwi");
          RCASE(2, "ficoms Mwi");
          RCASE(3, "ficomps Mwi");
          RCASE(4, "fisubs Mwi");
          RCASE(5, "fisubrs Mwi");
          RCASE(6, "fidivs Mwi");
          RCASE(7, "fidivrs Mwi");
        }
      } else {
        switch (ModrmReg(x->op.rde)) {
          RCASE(0, "faddp EST1");
          RCASE(1, "fmulp EST1");
          RCASE(2, "fcomp EST1");
          RCASE(3, "fcompp");
          RCASE(4, "fsubp EST1");
          RCASE(5, "fsubrp EST1");
          RCASE(6, "fdivp EST1");
          RCASE(7, "fdivrp EST1");
        }
      }
      break;
    case 0xDF:
      if (!IsModrmRegister(x->op.rde)) {
        switch (ModrmReg(x->op.rde)) {
          RCASE(0, "filds Mwi");
          RCASE(1, "fisttps Mwi");
          RCASE(2, "fists Mwi");
          RCASE(3, "fistps Mwi");
          RCASE(4, "fbld");
          RCASE(5, "fildll Mqi");
          RCASE(6, "fbstp");
          RCASE(7, "fistpll Mqi");
        }
      } else {
        switch (ModrmReg(x->op.rde)) {
          RCASE(0, "ffreep EST");
          RCASE(1, "fxch");
          RCASE(2, "fstp EST");
          RCASE(3, "fstp EST");
          RCASE(4, "fnstsw");
          RCASE(5, "fucomip EST");
          RCASE(6, "fcomip EST");
        }
      }
      break;
    case 0xF6:
      switch (ModrmReg(x->op.rde)) {
        RCASE(0, "test Eb Ib");
        RCASE(1, "test Eb Ib");
        RCASE(2, "not Eb");
        RCASE(3, "neg Eb");
        RCASE(4, "mulb Eb");
        RCASE(5, "imulb Eb");
        RCASE(6, "divb Eb");
        RCASE(7, "idivb Eb");
      }
      break;
    case 0xF7:
      switch (ModrmReg(x->op.rde)) {
        RCASE(0, "test Evqp Ivds");
        RCASE(1, "test Evqp Ivds");
        RCASE(2, "not Evqp");
        RCASE(3, "neg Evqp");
        RCASE(4, "mul Evqp");
        RCASE(5, "imul Evqp");
        RCASE(6, "div Evqp");
        RCASE(7, "idiv Evqp");
      }
      break;
    case 0xFE:
      switch (ModrmReg(x->op.rde)) {
        RCASE(0, "inc Eb");
        RCASE(1, "dec Eb");
      }
      break;
    case 0xFF:
      switch (ModrmReg(x->op.rde)) {
        RCASE(0, "inc Evqp");
        RCASE(1, "dec Evqp");
        RCASE(2, "CALL Eq");
        RCASE(4, "JMP Eq");
        RCASE(6, "pushWQ Evq");
      }
      break;
  }
  return UNKNOWN;
}

const char *DisSpecMap1(struct XedDecodedInst *x, char *p) {
  bool isreg;
  isreg = IsModrmRegister(x->op.rde);
  switch (x->op.opcode & 0xff) {
    RCASE(0x02, "lar %Gvqp Ev");
    RCASE(0x03, "lsl %Gvqp Ev");
    RCASE(0x05, "syscall");
    RCASE(0x0B, "ud2");
    RCASE(0x0D, "nop Ev");
    RCASE(0x18, "nop Ev");
    RCASE(0x19, "nop Ev");
    RCASE(0x1A, "nop Ev");
    RCASE(0x1B, "nop Ev");
    RCASE(0x1C, "nop Ev");
    RCASE(0x1D, "nop Ev");
    RCASE(0x20, "mov %Hd %Cd");
    RCASE(0x22, "mov %Cd %Hd");
    RCASE(0x28, "movapSD %Vps Wps");
    RCASE(0x29, "movapSD Wps %Vps");
    RCASE(0x2B, "movntpSD Mps %Vps");
    RCASE(0x2E, Osz(x->op.rde) ? "ucomisd %Vsd Wsd" : "ucomiss %Vss Wss");
    RCASE(0x2F, Osz(x->op.rde) ? "comisd %Vsd Wsd" : "comiss %Vss Wss");
    RCASE(0x30, "wrmsr");
    RCASE(0x31, "rdtsc");
    RCASE(0x32, "rdmsr");
    RCASE(0x33, "rdpmc");
    RCASE(0x34, "sysenter");
    RCASE(0x35, "sysexit");
    RCASE(0x40 ... 0x4f, "cmovCC %Gvqp Evqp");
    RCASE(0x52, DisOpVpsWpsVssWss(x, p, "rsqrt"));
    RCASE(0x53, DisOpVpsWpsVssWss(x, p, "rcp"));
    RCASE(0x54, DisOpVpdWpdVpsWps(x, p, "and"));
    RCASE(0x55, DisOpVpdWpdVpsWps(x, p, "andn"));
    RCASE(0x56, DisOpVpdWpdVpsWps(x, p, "or"));
    RCASE(0x57, DisOpVpdWpdVpsWps(x, p, "xor"));
    RCASE(0x58, DisOpVpsWpsVssWssVpdWpdVsdWsd(x, p, "add"));
    RCASE(0x59, DisOpVpsWpsVssWssVpdWpdVsdWsd(x, p, "mul"));
    RCASE(0x5C, DisOpVpsWpsVssWssVpdWpdVsdWsd(x, p, "sub"));
    RCASE(0x5D, DisOpVpsWpsVssWssVpdWpdVsdWsd(x, p, "min"));
    RCASE(0x5E, DisOpVpsWpsVssWssVpdWpdVsdWsd(x, p, "div"));
    RCASE(0x5F, DisOpVpsWpsVssWssVpdWpdVsdWsd(x, p, "max"));
    RCASE(0x60, DisOpPqQqVdqWdq(x, p, "punpcklbw"));
    RCASE(0x61, DisOpPqQqVdqWdq(x, p, "punpcklwd"));
    RCASE(0x62, DisOpPqQqVdqWdq(x, p, "punpckldq"));
    RCASE(0x63, DisOpPqQqVdqWdq(x, p, "packsswb"));
    RCASE(0x64, DisOpPqQqVdqWdq(x, p, "pcmpgtb"));
    RCASE(0x65, DisOpPqQqVdqWdq(x, p, "pcmpgtw"));
    RCASE(0x66, DisOpPqQqVdqWdq(x, p, "pcmpgtd"));
    RCASE(0x67, DisOpPqQqVdqWdq(x, p, "packuswb"));
    RCASE(0x68, DisOpPqQqVdqWdq(x, p, "punpckhbw"));
    RCASE(0x69, DisOpPqQqVdqWdq(x, p, "punpckhwd"));
    RCASE(0x6A, DisOpPqQqVdqWdq(x, p, "punpckhdq"));
    RCASE(0x6B, DisOpPqQqVdqWdq(x, p, "packssdw"));
    RCASE(0x6C, DisOpPqQqVdqWdq(x, p, "punpcklqdq"));
    RCASE(0x6D, DisOpPqQqVdqWdq(x, p, "punpckhqdq"));
    RCASE(0x74, DisOpPqQqVdqWdq(x, p, "pcmpeqb"));
    RCASE(0x75, DisOpPqQqVdqWdq(x, p, "pcmpeqw"));
    RCASE(0x76, DisOpPqQqVdqWdq(x, p, "pcmpeqd"));
    RCASE(0x77, "emms");
    RCASE(0x80 ... 0x8f, "jCC Jvds");
    RCASE(0x90 ... 0x9f, "setCC Jvds");
    RCASE(0xA0, "push %fs");
    RCASE(0xA1, "pop %fs");
    RCASE(0xA2, "cpuid");
    RCASE(0xA3, "bt Evqp %Gvqp");
    RCASE(0xA4, "shld Evqp %Gvqp Ib");
    RCASE(0xA5, "shld Evqp %Gvqp %cl");
    RCASE(0xA8, "push %gs");
    RCASE(0xA9, "pop %gs");
    RCASE(0xAB, "bts Evqp %Gvqp");
    RCASE(0xAC, "shrd Evqp %Gvqp Ib");
    RCASE(0xAD, "shrd Evqp %Gvqp %cl");
    RCASE(0xAF, "imul %Gvqp Evqp");
    RCASE(0xB0, "cmpxchg Eb %Gb");
    RCASE(0xB1, "cmpxchg Evqp %Gvqp");
    RCASE(0xB3, "btr Evqp %Gvqp");
    RCASE(0xB6, "movzbWLQ %Gvqp Eb");
    RCASE(0xB7, "movzwWLQ %Gvqp Ew");
    RCASE(0xB9, "ud %Gvqp Evqp");
    RCASE(0xBB, "btc Evqp %Gvqp");
    RCASE(0xBE, "movsbWLQ %Gvqp Eb");
    RCASE(0xBF, "movswWLQ %Gvqp Ew");
    RCASE(0xC0, "xadd Eb %Gb");
    RCASE(0xC1, "xadd Evqp %Gvqp");
    RCASE(0xC2, DisOpVpsWpsVssWssVpdWpdVsdWsd(x, p, "cmp"));
    RCASE(0xC3, "movnti Mdqp %Gdqp");
    RCASE(0xC8 ... 0xCF, "bswap %Zvqp");
    RCASE(0xD1, DisOpPqQqVdqWdq(x, p, "psrlw"));
    RCASE(0xD2, DisOpPqQqVdqWdq(x, p, "psrld"));
    RCASE(0xD3, DisOpPqQqVdqWdq(x, p, "psrlq"));
    RCASE(0xD4, DisOpPqQqVdqWdq(x, p, "paddq"));
    RCASE(0xD5, DisOpPqQqVdqWdq(x, p, "pmullw"));
    RCASE(0xD7, Osz(x->op.rde) ? "pmovmskb %Gdqp %Udq" : "pmovmskb %Gdqp %Nq");
    RCASE(0xD8, DisOpPqQqVdqWdq(x, p, "psubusb"));
    RCASE(0xD9, DisOpPqQqVdqWdq(x, p, "psubusw"));
    RCASE(0xDA, DisOpPqQqVdqWdq(x, p, "pminub"));
    RCASE(0xDB, DisOpPqQqVdqWdq(x, p, "pand"));
    RCASE(0xDC, DisOpPqQqVdqWdq(x, p, "paddusb"));
    RCASE(0xDD, DisOpPqQqVdqWdq(x, p, "paddusw"));
    RCASE(0xDE, DisOpPqQqVdqWdq(x, p, "pmaxub"));
    RCASE(0xDF, DisOpPqQqVdqWdq(x, p, "pandn"));
    RCASE(0xE0, DisOpPqQqVdqWdq(x, p, "pavgb"));
    RCASE(0xE1, DisOpPqQqVdqWdq(x, p, "psrawv"));
    RCASE(0xE2, DisOpPqQqVdqWdq(x, p, "psradv"));
    RCASE(0xE3, DisOpPqQqVdqWdq(x, p, "pavgw"));
    RCASE(0xE4, DisOpPqQqVdqWdq(x, p, "pmulhuw"));
    RCASE(0xE5, DisOpPqQqVdqWdq(x, p, "pmulhw"));
    RCASE(0xE7, Osz(x->op.rde) ? "movntdq Mdq %Vdq" : "movntq Mq %Pq");
    RCASE(0xE8, DisOpPqQqVdqWdq(x, p, "psubsb"));
    RCASE(0xE9, DisOpPqQqVdqWdq(x, p, "psubsw"));
    RCASE(0xEA, DisOpPqQqVdqWdq(x, p, "pminsw"));
    RCASE(0xEB, DisOpPqQqVdqWdq(x, p, "por"));
    RCASE(0xEC, DisOpPqQqVdqWdq(x, p, "paddsb"));
    RCASE(0xED, DisOpPqQqVdqWdq(x, p, "paddsw"));
    RCASE(0xEE, DisOpPqQqVdqWdq(x, p, "pmaxsw"));
    RCASE(0xEF, DisOpPqQqVdqWdq(x, p, "pxor"));
    RCASE(0xF0, "lddqu %Vdq Mdq");
    RCASE(0xF1, DisOpPqQqVdqWdq(x, p, "psllwv"));
    RCASE(0xF2, DisOpPqQqVdqWdq(x, p, "pslldv"));
    RCASE(0xF3, DisOpPqQqVdqWdq(x, p, "psllqv"));
    RCASE(0xF4, DisOpPqQqVdqWdq(x, p, "pmuludq"));
    RCASE(0xF5, DisOpPqQqVdqWdq(x, p, "pmaddwd"));
    RCASE(0xF6, DisOpPqQqVdqWdq(x, p, "psadbw"));
    RCASE(0xF8, DisOpPqQqVdqWdq(x, p, "psubb"));
    RCASE(0xF9, DisOpPqQqVdqWdq(x, p, "psubw"));
    RCASE(0xFA, DisOpPqQqVdqWdq(x, p, "psubd"));
    RCASE(0xFB, DisOpPqQqVdqWdq(x, p, "psubq"));
    RCASE(0xFC, DisOpPqQqVdqWdq(x, p, "paddb"));
    RCASE(0xFD, DisOpPqQqVdqWdq(x, p, "paddw"));
    RCASE(0xFE, DisOpPqQqVdqWdq(x, p, "paddd"));
    RCASE(0xFF, "ud0 %Gvqp Evqp");
    case 0xBC:
      if (Rep(x->op.rde) == 3) {
        return "tzcnt %Gvqp Evqp";
      } else {
        return "bsf %Gvqp Evqp";
      }
    case 0xBD:
      if (Rep(x->op.rde) == 3) {
        return "lzcnt %Gvqp Evqp";
      } else {
        return "bsr %Gvqp Evqp";
      }
    case 0x01:
      switch (ModrmReg(x->op.rde)) {
        case 0:
          if (!isreg) {
            return "sgdt Ms";
          } else {
            switch (ModrmRm(x->op.rde)) {
              case 1:
                return "vmcall";
              case 2:
                return "vmlaunch";
              case 3:
                return "vmresume";
              case 4:
                return "vmxoff";
              default:
                return UNKNOWN;
            }
          }
          break;
        case 1:
          if (!isreg) {
            return "sidt Ms";
          } else {
            switch (ModrmRm(x->op.rde)) {
              case 0:
                return "monitor";
              case 1:
                return "mwait";
              default:
                return UNKNOWN;
            }
          }
          break;
        case 2:
          if (!isreg) {
            return "lgdt Ms";
          } else if (ModrmRm(x->op.rde) == 0) {
            return "xgetbv";
          } else if (ModrmRm(x->op.rde) == 1) {
            return "xsetbv";
          } else {
            return UNKNOWN;
          }
          break;
        case 3:
          if (!isreg) {
            return "lidt Ms";
          } else {
            return UNKNOWN;
          }
        case 4:
          return "smsw Ew";
        case 6:
          return "lmsw Ew";
        case 7:
          if (!isreg) {
            return "invlpg M";
          } else {
            switch (ModrmRm(x->op.rde)) {
              case 0:
                return "swapgs";
              case 1:
                return "rdtscp";
              default:
                return UNKNOWN;
            }
          }
        default:
          return UNKNOWN;
      }
    case 0x1F:
      if (ModrmMod(x->op.rde) == 1 && ModrmReg(x->op.rde) == 0 &&
          ModrmRm(x->op.rde) == 0b101) {
        return "bofram Jb";
      } else {
        return "nop Ev";
      }
      break;
    case 0x70:
      switch (Rep(x->op.rde) | Osz(x->op.rde)) {
        RCASE(0, "pshufw %Pq Qq Ib");
        RCASE(1, "pshufd %Vdq Wdq Ib");
        RCASE(2, "pshuflw %Vdq Wdq Ib");
        RCASE(3, "pshufhw %Vdq Wdq Ib");
      }
      break;
    case 0x71:
      switch (ModrmReg(x->op.rde)) {
        RCASE(2, DisOpNqIbUdqIb(x, p, "psrlw"));
        RCASE(4, DisOpNqIbUdqIb(x, p, "psraw"));
        RCASE(6, DisOpNqIbUdqIb(x, p, "psllw"));
      }
      break;
    case 0x72:
      switch (ModrmReg(x->op.rde)) {
        RCASE(2, DisOpNqIbUdqIb(x, p, "psrld"));
        RCASE(4, DisOpNqIbUdqIb(x, p, "psrad"));
        RCASE(6, DisOpNqIbUdqIb(x, p, "pslld"));
      }
      break;
    case 0x73:
      switch (ModrmReg(x->op.rde)) {
        RCASE(2, DisOpNqIbUdqIb(x, p, "psrlq"));
        RCASE(3, DisOpNqIbUdqIb(x, p, "psrldq"));
        RCASE(6, DisOpNqIbUdqIb(x, p, "psllq"));
        RCASE(7, DisOpNqIbUdqIb(x, p, "pslldq"));
      }
      break;
    case 0xAE:
      switch (ModrmReg(x->op.rde)) {
        case 0:
          if (isreg) {
            return "rdfsbase %Rdqp";
          } else {
            return "fxsave M";
          }
        case 1:
          if (isreg) {
            return "rdgsbase %Rdqp";
          } else {
            return "fxrstor M";
          }
        case 2:
          if (isreg) {
            return "wrfsbase %Rdqp";
          } else {
            return "ldmxcsr Md";
          }
        case 3:
          if (isreg) {
            return "wrgsbase %Rdqp";
          } else {
            return "stmxcsr Md";
          }
        case 4:
          if (isreg) {
            return UNKNOWN;
          } else {
            return "xsave M %edx %eax";
          }
        case 5:
          return "lfence";
        case 6:
          return "mfence";
        case 7:
          if (isreg && ModrmReg(x->op.rde) == 0b111) {
            return "sfence";
          } else {
            return "clflush";
          }
      }
      break;
    case 0xBA:
      switch (ModrmReg(x->op.rde)) {
        RCASE(4, "btWLQ Evqp Ib");
        RCASE(5, "btsWLQ Evqp Ib");
        RCASE(6, "btrWLQ Evqp Ib");
        RCASE(7, "btcWLQ Evqp Ib");
      }
      break;
    case 0x10:
      if (Rep(x->op.rde) == 3) {
        return "movss %Vss Wss";
      } else if (Rep(x->op.rde) == 2) {
        return "movsd %Vsd Wsd";
      } else if (Osz(x->op.rde)) {
        return "movupd %Vpd Wpd";
      } else {
        return "movups %Vps Wps";
      }
      break;
    case 0x11:
      if (Rep(x->op.rde) == 3) {
        return "movss Wss %Vss";
      } else if (Rep(x->op.rde) == 2) {
        return "movsd Wsd %Vsd";
      } else if (Osz(x->op.rde)) {
        return "movupd Wpd %Vpd";
      } else {
        return "movups Wps %Vps";
      }
      break;
    case 0xC4:
      if (!Osz(x->op.rde)) {
        if (isreg) {
          return "pinsrw %Pq %Rdqp Ib";
        } else {
          return "pinsrw %Pq Mw Ib";
        }
      } else {
        if (isreg) {
          return "pinsrw %Vdq %Rdqp Ib";
        } else {
          return "pinsrw %Vdq Mw Ib";
        }
      }
      break;
    case 0xC5:
      if (!Osz(x->op.rde)) {
        return "pextrw %Gdqp %Nq Ib";
      } else {
        return "pextrw %Gdqp %Udq Ib";
      }
      break;
    case 0xC6:
      if (!Osz(x->op.rde)) {
        return "shufps %Vps Wps Ib";
      } else {
        return "shufpd %Vpd Wpd Ib";
      }
      break;
    case 0xC7:
      switch (ModrmReg(x->op.rde)) {
        case 1:
          if (!isreg) {
            if (Rexw(x->op.rde)) {
              return "cmpxchg16b Mdq";
            } else {
              return "cmpxchg8b Mq";
            }
          } else {
            return UNKNOWN;
          }
          break;
        case 6:
          if (isreg) {
            return "rdrand %Rdqp";
          } else {
            return UNKNOWN;
          }
          break;
        case 7:
          if (isreg) {
            if (Rep(x->op.rde) == 3) {
              return "rdpid %Rdqp";
            } else {
              return "rdseed %Rdqp";
            }
          } else {
            return UNKNOWN;
          }
          break;
        default:
          return UNKNOWN;
      }
      break;
    case 0xD6:
      if (Osz(x->op.rde)) {
        return "movq Wq %Vq";
      } else if (Rep(x->op.rde) == 3) {
        return "movq2dq %Vdq %Nq";
      } else if (Rep(x->op.rde) == 2) {
        return "movq2dq %Pq %Uq";
      }
      break;
    case 0x12:
      switch (Rep(x->op.rde) | Osz(x->op.rde)) {
        case 0:
          if (isreg) {
            return "movhlps %Vq %Uq";
          } else {
            return "movlps %Vq Mq";
          }
          break;
        case 1:
          return "movlpd %Vq Mq";
        case 2:
          return "movddup %Vq Wq";
        case 3:
          return "movsldup %Vq Wq";
        default:
          unreachable;
      }
      break;
    case 0x13:
      if (Osz(x->op.rde)) {
        return "movlpd Mq %Vq";
      } else {
        return "movlps Mq %Vq";
      }
      break;
    case 0x16:
      switch (Rep(x->op.rde) | Osz(x->op.rde)) {
        case 0:
          if (isreg) {
            return "movlhps %Vq %Uq";
          } else {
            return "movhps %Vq Mq";
          }
          break;
        case 1:
          return "movhpd %Vq Mq";
        case 3:
          return "movshdup %Vq Wq";
        default:
          break;
      }
      break;
    case 0x17:
      if (Osz(x->op.rde)) {
        return "movhpd Mq %Vq";
      } else {
        return "movhps Mq %Vq";
      }
      break;
    case 0x2A:
      if (Rep(x->op.rde) == 3) {
        return "cvtsi2ss %Vss Edqp";
      } else if (Rep(x->op.rde) == 2) {
        return "cvtsi2sd %Vsd Edqp";
      } else if (Osz(x->op.rde)) {
        return "cvtpi2pd %Vpd Qpi";
      } else {
        return "cvtpi2ps %Vps Qpi";
      }
      break;
    case 0x2C:
      if (Rep(x->op.rde) == 3) {
        return "cvttss2si %Gdqp Wss";
      } else if (Rep(x->op.rde) == 2) {
        return "cvttsd2si %Gdqp Wsd";
      } else if (Osz(x->op.rde)) {
        return "cvttpd2pi %Ppi Wpd";
      } else {
        return "cvttps2pi %Ppi Wpsq";
      }
      break;
    case 0x2D:
      if (Rep(x->op.rde) == 3) {
        return "cvtss2si %Gdqp Wss";
      } else if (Rep(x->op.rde) == 2) {
        return "cvtsd2si %Gdqp Wsd";
      } else if (Osz(x->op.rde)) {
        return "cvtpd2pi %Ppi Wpd";
      } else {
        return "cvtps2pi %Ppi Wpsq";
      }
      break;
    case 0x5a:
      if (Rep(x->op.rde) == 3) {
        return "cvtss2sd %Vsd Wss";
      } else if (Rep(x->op.rde) == 2) {
        return "cvtsd2ss %Vss Wsd";
      } else if (Osz(x->op.rde)) {
        return "cvtpd2ps %Vps Wpd";
      } else {
        return "cvtps2pd %Vpd Wps";
      }
      break;
    case 0x5b:
      if (Rep(x->op.rde) == 3) {
        return "cvttps2dq %Vdq Wps";
      } else if (Osz(x->op.rde)) {
        return "cvtps2dq %Vdq Wps";
      } else {
        return "cvtdq2ps %Vps Wdq";
      }
      break;
    case 0x51:
      if (Rep(x->op.rde) == 3) {
        return "sqrtss %Vss Wss";
      } else if (Rep(x->op.rde) == 2) {
        return "sqrtsd %Vsd Wsd";
      } else if (Osz(x->op.rde)) {
        return "sqrtpd %Vpd Wpd";
      } else {
        return "sqrtps %Vps Wps";
      }
      break;
    case 0x6E:
      if (Osz(x->op.rde)) {
        if (Rexw(x->op.rde)) {
          return "movq %Vdq Eqp";
        } else {
          return "movd %Vdq Ed";
        }
      } else {
        if (Rexw(x->op.rde)) {
          return "movq %Pq Eqp";
        } else {
          return "movd %Pq Ed";
        }
      }
      break;
    case 0x6F:
      if (Rep(x->op.rde) == 3) {
        return "movdqu %Vdq Wdq";
      } else if (Osz(x->op.rde)) {
        return "movdqa %Vdq Wdq";
      } else {
        return "movq %Pq Qq";
      }
      break;
    case 0x7E:
      if (Rep(x->op.rde) == 3) {
        return "movq %Vq Wq";
      } else if (Osz(x->op.rde)) {
        if (Rexw(x->op.rde)) {
          return "movq Eqp %Vdq";
        } else {
          return "movd Ed %Vdq";
        }
      } else {
        if (Rexw(x->op.rde)) {
          return "movq Eqp %Pq";
        } else {
          return "movd Ed %Pq";
        }
      }
      break;
    case 0x7F:
      if (Rep(x->op.rde) == 3) {
        return "movdqu Wdq %Vdq";
      } else if (Osz(x->op.rde)) {
        return "movdqa Wdq %Vdq";
      } else {
        return "movq Qq %Pq";
      }
      break;
    case 0xE6:
      if (Rep(x->op.rde) == 2) {
        return "cvtpd2dq %Vdq Wpd";
      } else if (Osz(x->op.rde)) {
        return "cvttpd2dq %Vdq Wpd";
      } else if (Rep(x->op.rde) == 3) {
        return "cvtdq2pd %Vpd Wdq";
      }
      break;
  }
  return UNKNOWN;
}

const char *DisSpecMap2(struct XedDecodedInst *x, char *p) {
  switch (x->op.opcode & 0xff) {
    RCASE(0x00, DisOpPqQqVdqWdq(x, p, "pshufb"));
    RCASE(0x01, DisOpPqQqVdqWdq(x, p, "phaddw"));
    RCASE(0x02, DisOpPqQqVdqWdq(x, p, "phaddd"));
    RCASE(0x03, DisOpPqQqVdqWdq(x, p, "phaddsw"));
    RCASE(0x04, DisOpPqQqVdqWdq(x, p, "pmaddubsw"));
    RCASE(0x05, DisOpPqQqVdqWdq(x, p, "phsubw"));
    RCASE(0x06, DisOpPqQqVdqWdq(x, p, "phsubd"));
    RCASE(0x07, DisOpPqQqVdqWdq(x, p, "phsubsw"));
    RCASE(0x08, DisOpPqQqVdqWdq(x, p, "psignb"));
    RCASE(0x09, DisOpPqQqVdqWdq(x, p, "psignw"));
    RCASE(0x0A, DisOpPqQqVdqWdq(x, p, "psignd"));
    RCASE(0x0B, DisOpPqQqVdqWdq(x, p, "pmulhrsw"));
    RCASE(0x10, "pblendvb %Vdq Wdq");
    RCASE(0x14, "blendvps Vps Wps");
    RCASE(0x15, "blendvpd Vpd Wpd");
    RCASE(0x17, "ptest %Vdq Wdq");
    RCASE(0x1C, DisOpPqQqVdqWdq(x, p, "pabsb"));
    RCASE(0x1D, DisOpPqQqVdqWdq(x, p, "pabsw"));
    RCASE(0x1E, DisOpPqQqVdqWdq(x, p, "pabsd"));
    RCASE(0x20, "pmovsxbw %Vdq Mq");
    RCASE(0x21, "pmovsxbd %Vdq Md");
    RCASE(0x22, "pmovsxbq %Vdq Mw");
    RCASE(0x23, "pmovsxwd %Vdq Mq");
    RCASE(0x24, "pmovsxwq %Vdq Md");
    RCASE(0x25, "pmovsxdq %Vdq Mq");
    RCASE(0x28, "pmuldq %Vdq Wdq");
    RCASE(0x29, "pcmpeqq %Vdq Wdq");
    RCASE(0x2A, "movntdqa %Vdq Mdq");
    RCASE(0x2B, "packusdw %Vdq Wdq");
    RCASE(0x30, "pmovzxbw %Vdq Mq");
    RCASE(0x31, "pmovzxbd %Vdq Md");
    RCASE(0x32, "pmovzxbq %Vdq Mw");
    RCASE(0x33, "pmovzxwd %Vdq Mq");
    RCASE(0x34, "pmovzxwq %Vdq Md");
    RCASE(0x35, "pmovzxdq %Vdq Mq");
    RCASE(0x37, "pcmpgtq %Vdq Wdq");
    RCASE(0x38, "pminsb %Vdq Wdq");
    RCASE(0x39, "pminsd %Vdq Wdq");
    RCASE(0x3A, "pminuw %Vdq Wdq");
    RCASE(0x3B, "pminud %Vdq Wdq");
    RCASE(0x3C, "pmaxsb %Vdq Wdq");
    RCASE(0x3D, "pmaxsd %Vdq Wdq");
    RCASE(0x3E, "pmaxuw %Vdq Wdq");
    RCASE(0x3F, "pmaxud %Vdq Wdq");
    RCASE(0x40, "pmulld %Vdq Wdq");
    RCASE(0x41, "phminposuw %Vdq Wdq");
    RCASE(0x80, "invept %Gq Mdq");
    RCASE(0x81, "invvpid %Gq Mdq");
    case 0xF0:
      if (Rep(x->op.rde) == 2) {
        return "crc32 %Gvqp Eb";
      } else {
        return "movbe %Gvqp M";
      }
      break;
    case 0xF1:
      if (Rep(x->op.rde) == 2) {
        return "crc32 %Gvqp Evqp";
      } else {
        return "movbe M %Gvqp";
      }
      break;
    default:
      return UNKNOWN;
  }
}

const char *DisSpecMap3(struct XedDecodedInst *x, char *p) {
  switch (x->op.opcode & 0xff) {
    RCASE(0x0F, DisOpPqQqIbVdqWdqIb(x, p, "palignr"));
    default:
      return UNKNOWN;
  }
}

const char *DisSpec(struct XedDecodedInst *x, char *p) {
  switch (x->op.map & 7) {
    case XED_ILD_MAP0:
      return DisSpecMap0(x, p);
    case XED_ILD_MAP1:
      return DisSpecMap1(x, p);
    case XED_ILD_MAP2:
      return DisSpecMap2(x, p);
    case XED_ILD_MAP3:
      return DisSpecMap3(x, p);
    default:
      return UNKNOWN;
  }
}
