/* asm_adsp219x.c -- Full Radare2 arch plugin for Analog Devices ADSP-219x
   Copyright (C) 2026 Dr. Christian Giessen

   This file implements the complete ADSP-219x Instruction Set (Types 1-37).  */

#include <r_arch.h>

/* --- Tables from Chapter 9 --- */

static const char *cond_str[] =
{ "EQ", "NE", "GT", "LE", "LT", "GE", "AV", "NOT AV", "AC", "NOT AC", "SWCOND", "NOT SWCOND", "MV", "NOT MV", "NOT CE", "TRUE" };

static const char *reg0[] =
{ "AX0", "AX1", "MX0", "MX1", "AY0", "AY1", "MY0", "MY1", "MR2", "SR2", "AR", "SI", "MR1", "SR1", "MR0", "SR0" };

static const char *reg1[] =
{ "I0", "I1", "I2", "I3", "M0", "M1", "M2", "M3", "L0", "L1", "L2", "L3", "IMASK", "IRPTL", "ICNTL", "CNTR" };

static const char *reg2[] =
{ "I4", "I5", "I6", "I7", "M4", "M5", "M6", "M7", "L4", "L5", "L6", "L7", "STACKA", "LPCSTACKA", "RES", "RES" };

static const char *reg3[] =
{ "ASTAT", "MSTAT", "SSTAT", "LPSTACKP", "CCODE", "SE", "SB", "PX", "DMPG1", "DMPG2", "IOPG", "IJPG", "RES", "RES", "RES", "STACKP" };

static const char *amf_mac[] =
{ "NOP", "X*Y (RND)", "MR+X*Y (RND)", "MR-X*Y (RND)", "X*Y (SS)", "X*Y (SU)", "X*Y (US)", "X*Y (UU)",
  "MR+X*Y (SS)", "MR+X*Y (SU)", "MR+X*Y (US)", "MR+X*Y (UU)", "MR-X*Y (SS)", "MR-X*Y (SU)", "MR-X*Y (US)", "MR-X*Y (UU)" };

static const char *amf_alu[] =
{ "Y", "Y+1", "X+Y+C", "X+Y", "NOT Y", "-Y", "X-Y+C-1", "X-Y", "Y-1", "Y-X", "Y-X+C-1", "NOT X", "X AND Y", "X OR Y", "X XOR Y", "ABS X" };

static const char *sf_names[] =
{ "LSHIFT (HI)", "LSHIFT (HI, OR)", "LSHIFT (LO)", "LSHIFT (LO, OR)", "ASHIFT (HI)", "ASHIFT (HI, OR)", "ASHIFT (LO)", "ASHIFT (LO, OR)",
  "NORM (HI)", "NORM (HI, OR)", "NORM (LO)", "NORM (LO, OR)", "EXP (HI)", "EXP (HIX)", "EXP (LO)", "Derive Block Exponent" };

static const char *xop_alu[] = { "AX0", "AX1", "AR", "MR0", "MR1", "MR2", "SR0", "SR1" };
static const char *xop_mac[] = { "MX0", "MX1", "AR", "MR0", "MR1", "MR2", "SR0", "SR1" };
static const char *xop_shift[] = { "SI", "SR2", "AR", "MR0", "MR1", "MR2", "SR0", "SR1" };
static const char *yop_alu[] = { "AY0", "AY1", "AF", "0" };
static const char *yop_mac[] = { "MY0", "MY1", "SR1", "0" };

/* --- Helpers --- */

static const char *
get_reg (int gp, int idx)
{
  switch (gp & 0x3)
    {
    case 0: return reg0[idx & 0xF];
    case 1: return reg1[idx & 0xF];
    case 2: return reg2[idx & 0xF];
    case 3: return reg3[idx & 0xF];
    }
  return "??";
}

/* --- Decoder --- */

static bool
decode (RArchSession *as, RAnalOp *op, RAnalOpMask mask)
{
  ut32 ins;
  const ut8 *b = op->bytes;
  if (op->size < 3) return false;

  ins = ((ut32) b[0] << 16) | ((ut32) b[1] << 8) | (ut32) b[2];
  op->size = 3;
  op->type = R_ANAL_OP_TYPE_UNK;
  if (!(mask & R_ARCH_OP_MASK_DISASM)) return true;

  /* Priority check: High bits */
  ut32 b23_22 = (ins >> 22) & 0x3;
  ut32 b21_20 = (ins >> 20) & 0x3;

  /* Type 30/31: NOP / IDLE */
  if ((ins >> 16) == 0x00)
    {
      if (ins == 0) { op->mnemonic = strdup ("NOP"); op->type = R_ANAL_OP_TYPE_NOP; }
      else if ((ins >> 8) == 0x02) { op->mnemonic = r_str_newf ("IDLE 0x%X", ins & 0xFF); op->type = R_ANAL_OP_TYPE_TRAP; }
      return true;
    }

  /* Type 1: Compute | DM | PM (11xxxx) */
  if (b23_22 == 3)
    {
      ut32 amf = (ins >> 12) & 0x1F;
      const char *f = (amf < 16) ? amf_mac[amf] : amf_alu[amf-16];
      int dmi = (ins >> 2) & 3, dmm = ins & 3;
      int pmi = (ins >> 6) & 3, pmm = (ins >> 4) & 3;
      int dd = (ins >> 17) & 3, pd = (ins >> 19) & 3;
      op->mnemonic = r_str_newf ("%s, %s=DM(I%d+=M%d), %s=PM(I%d+=M%d)",
                                 f, reg0[dd], dmi, dmm, reg0[pd+4], pmi+4, pmm+4);
      return true;
    }

  /* Type 3: Direct Memory (10xxxx) */
  if (b23_22 == 2)
    {
      ut32 d = (ins >> 20) & 1, addr = (ins >> 4) & 0xFFFF, reg = ins & 0xF;
      if ((ins >> 21) & 1) /* Ireg/Mreg */
        op->mnemonic = r_str_newf ("%s(0x%04X) = %s%d", d?"DM":"DM", addr, (reg<8?"I":"M"), reg&7);
      else /* Dreg */
        op->mnemonic = r_str_newf ("%s(0x%04X) = %s", d?"DM":"DM", addr, reg0[reg]);
      return true;
    }

  /* Type 4: Compute | DM/PM Postmodify (011xxx) */
  if ((ins >> 21) == 0x3)
    {
      ut32 g = (ins >> 20) & 1, amf = (ins >> 13) & 0x1F;
      const char *f = (amf < 16) ? amf_mac[amf] : amf_alu[amf-16];
      op->mnemonic = r_str_newf ("%s, %s = %cM(I%d += M%d)", f, reg0[(ins>>4)&0xF], g?'P':'D', ((ins>>2)&3)|(g?4:0), (ins&3)|(g?4:0));
      return true;
    }

  /* Type 9/9a: Compute (001000 / 001001) */
  /* Bits 23-19 = 00100 */
  if ((ins >> 19) == 0x04)
    {
      ut32 amf = (ins >> 13) & 0x1F;
      ut32 xop_i = (ins >> 8) & 0x7;
      ut32 yop_i = (ins >> 11) & 0x3;
      ut32 cond = ins & 0xF;
      const char *f;
      const char *dst;
      const char *x;
      const char *y;
      if (amf < 16)
        {
          f = amf_mac[amf];
          dst = "MR";
          x = xop_mac[xop_i];
          y = yop_mac[yop_i];
        }
      else
        {
          f = amf_alu[amf - 16];
          dst = "AR";
          x = xop_alu[xop_i];
          y = yop_alu[yop_i];
        }
      if (cond == 15)
        op->mnemonic = r_str_newf ("%s = %s(%s, %s)",
                                   dst, f, x, y);
      else
        op->mnemonic = r_str_newf ("IF %s %s = %s(%s, %s)",
                                   cond_str[cond], dst, f, x, y);
      return true;
    }

  /* Type 8: Compute | Dreg1 <- Dreg2 (00101Z) */
  /* Bits 23-18 = 001010 (Z=0) or 001011 (Z=1) */
  if ((ins >> 18) == 0x0A || (ins >> 18) == 0x0B)
    {
      ut32 amf = (ins >> 12) & 0x1F;
      const char *f = (amf < 16) ? amf_mac[amf] : amf_alu[amf-16];
      /* DDREG = bits 8-6 (dest), SDREG = bits 5-0 (src, masked) */
      ut32 ddreg = (ins >> 6) & 0x7;
      ut32 sdreg = ins & 0x3F;
      /* For NONE=ALU case: SDREG = 0x2A (101010) */
      if (sdreg == 0x2A)
        op->mnemonic = r_str_newf ("NONE = %s", f);
      else
        op->mnemonic = r_str_newf ("%s, %s = %s", f, xop_alu[ddreg], reg0[sdreg & 0xF]);
      return true;
    }

  /* Type 6/7/IO/System (010xxx / 011xxx) */
  if (b23_22 == 1)
    {
      if (b21_20 == 0) { op->mnemonic = r_str_newf ("%s = 0x%04X", reg0[ins&0xF], (ins>>4)&0xFFFF); return true; } /* Type 6 */
      if (b21_20 == 1) { op->mnemonic = r_str_newf ("%s = 0x%04X", reg1[ins&0xF], (ins>>4)&0xFFFF); return true; } /* Type 7 */
      if ((ins >> 16) == 0x6D) { op->mnemonic = r_str_newf ("IO(0x%X) = %s", (ins>>4)&0xFF, reg0[ins&0xF]); return true; } /* Type 34 */
      if ((ins >> 16) == 0x6C) { op->mnemonic = r_str_newf ("REG(0x%X) = %s", (ins>>4)&0xFF, reg0[ins&0xF]); return true; } /* Type 35 */
    }

  /* Type 8/9/10/11/17... (00xxxx) */
  if (b23_22 == 0)
    {
      if (b21_20 == 3) { op->mnemonic = r_str_newf ("%s = 0x%04X", reg2[ins&0xF], (ins>>4)&0xFFFF); return true; } /* Type 7 (Reg2) */
      /* Type 10a: bits 23-18 = 000111 (unconditional 16-bit) */
      if ((ins >> 18) == 0x07)
        {
          /* addr = bits 17-4 (14 bits) | bits 1-0 (2 MSBs) */
          ut32 addr = ((ins >> 4) & 0x3FFF) | ((ins & 0x3) << 14);
          ut32 b_bit = (ins >> 3) & 1;
          ut32 s_bit = (ins >> 2) & 1;
          op->mnemonic = r_str_newf ("%s 0x%04X%s",
                                     s_bit ? "CALL" : "JUMP",
                                     addr,
                                     b_bit ? " (DB)" : "");
          op->type = s_bit ? R_ANAL_OP_TYPE_CALL
                          : R_ANAL_OP_TYPE_JMP;
          return true;
        }
      /* Type 10: bits 23-19 = 00011, bit18 = 0 (conditional 13-bit) */
      if ((ins >> 19) == 0x03 && !((ins >> 18) & 1))
        {
          ut32 b_bit = (ins >> 17) & 1;
          ut32 addr = (ins >> 4) & 0x1FFF;
          ut32 cond = ins & 0xF;
          if (cond == 15)
            op->mnemonic = r_str_newf ("JUMP 0x%04X%s",
                                       addr,
                                       b_bit ? " (DB)" : "");
          else
            op->mnemonic = r_str_newf ("IF %s JUMP 0x%04X%s",
                                       cond_str[cond], addr,
                                       b_bit ? " (DB)" : "");
          op->type = R_ANAL_OP_TYPE_CJMP;
          return true;
        }
      /* Type 17: Reg = Reg (bits 23-16 = 00001101) */
      if ((ins >> 16) == 0x0D)
        {
          op->mnemonic = r_str_newf ("%s = %s",
            get_reg ((ins >> 10) & 3, (ins >> 4) & 0xF),
            get_reg ((ins >> 8) & 3, ins & 0xF));
          return true;
        }

      /* Type 20: RTS/RTI (bits 23-16 = 00001010) */
      if ((ins >> 16) == 0x0A)
        {
          ut32 b_bit = (ins >> 15) & 1;
          ut32 t_bit = (ins >> 14) & 1;
          ut32 cond = (ins >> 4) & 0xF;
          const char *ret = t_bit ? "RTI" : "RTS";
          if (cond == 15)
            op->mnemonic = r_str_newf ("%s%s", ret,
                                       b_bit ? " (DB)" : "");
          else
            op->mnemonic = r_str_newf ("IF %s %s%s",
                                       cond_str[cond], ret,
                                       b_bit ? " (DB)" : "");
          op->type = R_ANAL_OP_TYPE_RET;
          return true;
        }

      /* Type 11: DO UNTIL (bits 23-16 = 00010110) */
      if ((ins >> 16) == 0x16)
        {
          ut32 addr = (ins >> 4) & 0xFFF;
          ut32 term = ins & 0xF;
          op->mnemonic = r_str_newf ("DO 0x%03X UNTIL %s",
                                     addr, cond_str[term]);
          return true;
        }

      /* Type 15: Shift Imm (bits 23-16 = 00001111) */
      if ((ins >> 16) == 0x0F)
        {
          ut32 sf = (ins >> 12) & 0xF;
          ut32 xreg = (ins >> 8) & 0xF;
          int8_t exp = (int8_t)(ins & 0xFF);
          op->mnemonic = r_str_newf ("SR = %s %s BY %d",
                                     sf_names[sf], reg0[xreg],
                                     exp);
          return true;
        }

      /* Type 18: Mode Control (bits 23-16 = 00001100) */
      if ((ins >> 16) == 0x0C)
        {
          static const char *mbits[] =
            { "INT", "SD", "SR", "BR", "OL", "AS", "MM", "TI" };
          char buf[128];
          int pos = 0, i;
          for (i = 0; i < 8; i++)
            {
              ut32 v = (ins >> (i * 2)) & 0x3;
              if (v == 1)
                pos += snprintf (buf + pos,
                  sizeof (buf) - pos, "%sDIS %s",
                  pos ? ", " : "", mbits[i]);
              else if (v == 3)
                pos += snprintf (buf + pos,
                  sizeof (buf) - pos, "%sENA %s",
                  pos ? ", " : "", mbits[i]);
            }
          op->mnemonic = pos ? strdup (buf)
                             : strdup ("MODE NOP");
          return true;
        }

      /* Type 25: SAT MR/SR (bits 23-14 = 0000001100) */
      if ((ins >> 14) == 0x0C)
        {
          ut32 r = (ins >> 13) & 1;
          op->mnemonic = r_str_newf ("SAT %s",
                                     r ? "SR" : "MR");
          return true;
        }

      /* Type 33: Reg3 = Data12 (bits 23-16 = 00010000) */
      if ((ins >> 16) == 0x10)
        {
          ut32 data = (ins >> 4) & 0xFFF;
          ut32 reg = ins & 0xF;
          op->mnemonic = r_str_newf ("%s = 0x%03X",
                                     reg3[reg], data);
          return true;
        }

      /* Type 19: Indirect Jump/Call (bits 23-16 = 00001011) */
      if ((ins >> 16) == 0x0B)
        {
          ut32 b_bit = (ins >> 15) & 1;
          ut32 s_bit = (ins >> 14) & 1;
          ut32 g = (ins >> 13) & 1;
          ut32 cond = (ins >> 4) & 0xF;
          ut32 ireg = ins & 0x3;
          int base = g ? 4 : 0;
          if (cond == 15)
            op->mnemonic = r_str_newf ("%s (I%d)%s",
              s_bit ? "CALL" : "JUMP",
              ireg + base,
              b_bit ? " (DB)" : "");
          else
            op->mnemonic = r_str_newf ("IF %s %s (I%d)%s",
              cond_str[cond],
              s_bit ? "CALL" : "JUMP",
              ireg + base,
              b_bit ? " (DB)" : "");
          op->type = s_bit ? R_ANAL_OP_TYPE_CALL
                          : R_ANAL_OP_TYPE_JMP;
          return true;
        }

      /* Type 21: MODIFY (bits 23-16 = 00000001, bit15=1) */
      if ((ins >> 16) == 0x01 && ((ins >> 15) & 1))
        {
          ut32 g = (ins >> 12) & 1;
          ut32 ireg = ins & 0x3;
          ut32 mreg = (ins >> 2) & 0x3;
          int base = g ? 4 : 0;
          op->mnemonic = r_str_newf ("MODIFY(I%d += M%d)",
                                     ireg + base, mreg + base);
          return true;
        }

      /* Type 23: DIVQ (bits 23-12 = 000000111101) */
      if ((ins >> 12) == 0x03D)
        {
          ut32 xop_i = (ins >> 8) & 0x7;
          op->mnemonic = r_str_newf ("DIVQ %s",
                                     xop_alu[xop_i]);
          return true;
        }

      /* Type 24: DIVS (bits 23-13 = 00000011100) */
      if ((ins >> 13) == 0x1C)
        {
          ut32 yop_i = (ins >> 11) & 0x3;
          ut32 xop_i = (ins >> 8) & 0x7;
          op->mnemonic = r_str_newf ("DIVS %s, %s",
                                     yop_alu[yop_i],
                                     xop_alu[xop_i]);
          return true;
        }

      /* Type 26: Push/Pop/Cache (bits 23-16 = 00001000) */
      if ((ins >> 16) == 0x08)
        {
          ut32 cf = (ins >> 7) & 1;
          ut32 ppp = (ins >> 4) & 0x7;
          ut32 lpp = (ins >> 2) & 0x3;
          ut32 spp = ins & 0x3;
          char buf[64];
          int pos = 0;
          if (cf)
            pos += snprintf (buf + pos,
              sizeof (buf) - pos, "FLUSH CACHE");
          if (ppp == 1)
            pos += snprintf (buf + pos,
              sizeof (buf) - pos, "%sPUSH PC",
              pos ? ", " : "");
          else if (ppp == 2)
            pos += snprintf (buf + pos,
              sizeof (buf) - pos, "%sPOP PC",
              pos ? ", " : "");
          if (lpp == 1)
            pos += snprintf (buf + pos,
              sizeof (buf) - pos, "%sPUSH LOOP",
              pos ? ", " : "");
          else if (lpp == 2)
            pos += snprintf (buf + pos,
              sizeof (buf) - pos, "%sPOP LOOP",
              pos ? ", " : "");
          if (spp == 1)
            pos += snprintf (buf + pos,
              sizeof (buf) - pos, "%sPUSH STS",
              pos ? ", " : "");
          else if (spp == 2)
            pos += snprintf (buf + pos,
              sizeof (buf) - pos, "%sPOP STS",
              pos ? ", " : "");
          op->mnemonic = pos ? strdup (buf)
                             : strdup ("STACK NOP");
          return true;
        }

      /* Type 37: SETINT/CLRINT (bits 23-15 = 000001110) */
      if ((ins >> 15) == 0x0E)
        {
          ut32 c = (ins >> 5) & 1;
          ut32 bit = ins & 0xF;
          op->mnemonic = r_str_newf ("%s %d",
            c ? "CLRINT" : "SETINT", bit);
          return true;
        }
    }

  op->mnemonic = r_str_newf ("unk 0x%06X", ins);
  return true;
}

static int archinfo (RArchSession *s, ut32 q)
{
  switch (q) {
  case R_ARCH_INFO_CODE_ALIGN: return 3;
  case R_ARCH_INFO_MINOP_SIZE: case R_ARCH_INFO_MAXOP_SIZE: return 3;
  default: return -1;
  }
}

const RArchPlugin r_arch_plugin_adsp219x = {
  .meta = { .name = "adsp219x", .author = "Dr. Christian Giessen", .desc = "ADSP-219x Master Plugin", .license = "LGPL-3.0-only" },
  .arch = "adsp219x", .bits = R_SYS_BITS_PACK(24), .endian = R_SYS_ENDIAN_BIG, .info = archinfo, .decode = (RArchPluginDecodeCallback)decode,
};

#ifndef R2_PLUGIN_INCORE
R_API RLibStruct radare_plugin = { .type = R_LIB_TYPE_ARCH, .data = (void *)&r_arch_plugin_adsp219x, .version = R2_VERSION };
#endif
