/* asm_adsp219x.c -- Radare2 architecture plugin for Analog Devices ADSP-219x Copyright (C) 2026 OpenClaw This file is part of our reverse engineering project for ADSP-219x. It implements the disassembler via the RArchPlugin interface. */ #include /* Register name definitions for ADSP-219x. */ 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", "RESERVED", "RESERVED" }; static const char *reg3[] = { "ASTAT", "MSTAT", "SSTAT", "LPSTACKP", "CCODE", "SE", "SB", "PX", "DMPG1", "DMPG2", "IOPG", "IJPG", "RES", "RES", "RES", "STACKP" }; /* Returns the name of a register given its group GP and index IDX. */ 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]; default: return "??"; } } /* Decodes the instruction at current OP position using MASK. */ 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; /* Basic opcode classification based on high bits. */ ut32 top_bits_2 = (ins >> 22) & 0x3; ut32 top_bits_8 = (ins >> 16) & 0xFF; /* Type 30/31: NOP / IDLE commands. */ if (top_bits_8 == 0x00) { if (ins == 0) { op->mnemonic = strdup ("NOP"); op->type = R_ANAL_OP_TYPE_NOP; } else { op->mnemonic = r_str_newf ("IDLE 0x%X", ins & 0xFF); op->type = R_ANAL_OP_TYPE_TRAP; } return true; } /* Type 1: Multifunction (11xxxx). */ if (top_bits_2 == 0x3) { op->mnemonic = r_str_newf ("compute_dm_pm 0x%06X", ins); return true; } /* Type 3: Direct Memory access (10xxxx). */ if (top_bits_2 == 0x2) { ut32 write_flag = (ins >> 20) & 0x1; ut32 address = (ins >> 4) & 0xFFFF; ut32 register_idx = ins & 0xF; op->mnemonic = r_str_newf ("%s %s DM(0x%04X)", reg0[register_idx], write_flag ? "=" : "=", address); return true; } /* Instructions starting with 01xxxx. */ if (top_bits_2 == 0x1) { ut32 top_bits_4 = (ins >> 20) & 0xF; if (top_bits_4 == 0x4) { /* Type 6: Dreg = Imm16. */ op->mnemonic = r_str_newf ("%s = 0x%04X", reg0[ins & 0xF], (ins >> 4) & 0xFFFF); return true; } if (top_bits_4 == 0x5) { /* Type 7: Reg1 = Imm16. */ op->mnemonic = r_str_newf ("%s = 0x%04X", reg1[ins & 0xF], (ins >> 4) & 0xFFFF); return true; } if (top_bits_8 == 0x6D) { /* Type 34: IO Register load. */ op->mnemonic = r_str_newf ("IO(0x%02X) = %s", (ins >> 4) & 0xFF, reg0[ins & 0xF]); return true; } if (top_bits_8 == 0x6C) { /* Type 35: System Register load. */ op->mnemonic = r_str_newf ("REG(0x%02X) = %s", (ins >> 4) & 0xFF, reg0[ins & 0xF]); return true; } } /* Instructions starting with 00xxxx. */ if (top_bits_2 == 0x0) { ut32 top_bits_4 = (ins >> 20) & 0xF; if (top_bits_4 == 0x3) { /* Type 7: Reg2 = Imm16. */ op->mnemonic = r_str_newf ("%s = 0x%04X", reg2[ins & 0xF], (ins >> 4) & 0xFFFF); return true; } if ((ins >> 19) == 0x3) { /* Type 10: Jump/Call. */ ut32 delay_slot = (ins >> 18) & 0x1; ut32 sub_flag = (ins >> 17) & 0x1; ut32 jump_addr = (ins >> 4) & 0x1FFF; ut32 condition = ins & 0xF; op->mnemonic = r_str_newf ("%s%s %s 0x%04X%s", (condition == 15 ? "" : "IF "), (condition == 15 ? "" : cond_str[condition]), (sub_flag ? "CALL" : "JUMP"), jump_addr, delay_slot ? " (DB)" : ""); if (delay_slot) op->delay = 1; return true; } if (top_bits_8 == 0x0D) { /* Type 17: Register-to-Register move. */ ut32 dest_gp = (ins >> 10) & 0x3; ut32 src_gp = (ins >> 8) & 0x3; ut32 dest_idx = (ins >> 4) & 0xF; ut32 src_idx = ins & 0xF; op->mnemonic = r_str_newf ("%s = %s", get_reg (dest_gp, dest_idx), get_reg (src_gp, src_idx)); return true; } } op->mnemonic = r_str_newf ("unk 0x%06X", ins); return true; } /* Returns the info corresponding to code alignment and op sizes. */ 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; } } /* Definition of the RArchPlugin structure for ADSP-219x. */ const RArchPlugin r_arch_plugin_adsp219x = { .meta = { .name = "adsp219x", .author = "OpenClaw", .desc = "ADSP-219x (GNU Style Layout)", .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