From 62b9363c13ec066b0fcad74e098aff1b375c8902 Mon Sep 17 00:00:00 2001 From: "Dr. Christian Giessen" Date: Wed, 22 Apr 2026 19:34:01 +0000 Subject: [PATCH] Implement Types 8, 9, 10a, 11, 15, 17, 18, 19, 20, 21, 23, 24, 25, 26, 33, 37 - Type 8: Compute + Dreg move - Type 9/9a: Standalone ALU/MAC compute (conditional/unconditional) - Type 10a: Unconditional 16-bit jump/call (fixed bitfield decode) - Type 10: Conditional 13-bit jump (fixed COND field) - Type 18: Mode control (ENA/DIS AS, MM, BR, etc.) - Type 19: Indirect jump/call via Ireg - Type 20: RTS/RTI with correct COND extraction (fixed bug) - Type 21: MODIFY(Ireg += Mreg) - Type 23: DIVQ - Type 24: DIVS - Type 25: SAT MR/SR - Type 26: Push/Pop/Flush Cache - Type 33: Reg3 = Data12 (short constant load) - Type 37: SETINT/CLRINT - Type 15: Sign-extended shift exponent --- r2plugin/asm_adsp219x.c | 273 +++++++++++++++++++++++++++++++++++++-- r2plugin/asm_adsp219x.so | Bin 20048 -> 20288 bytes 2 files changed, 264 insertions(+), 9 deletions(-) diff --git a/r2plugin/asm_adsp219x.c b/r2plugin/asm_adsp219x.c index ea66cce..f85aff1 100644 --- a/r2plugin/asm_adsp219x.c +++ b/r2plugin/asm_adsp219x.c @@ -109,7 +109,42 @@ decode (RArchSession *as, RAnalOp *op, RAnalOpMask mask) { 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)); + 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; } @@ -117,7 +152,6 @@ decode (RArchSession *as, RAnalOp *op, RAnalOpMask mask) /* Bits 23-18 = 001010 (Z=0) or 001011 (Z=1) */ if ((ins >> 18) == 0x0A || (ins >> 18) == 0x0B) { - ut32 z = (ins >> 18) & 1; 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) */ @@ -144,16 +178,237 @@ decode (RArchSession *as, RAnalOp *op, RAnalOpMask mask) 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) */ - if ((ins >> 19) == 0x3) /* Type 10/10a Jumps */ + /* Type 10a: bits 23-18 = 000111 (unconditional 16-bit) */ + if ((ins >> 18) == 0x07) { - ut32 db = (ins >> 18) & 1, s = (ins >> 17) & 1, cond = ins & 0xF; - op->mnemonic = r_str_newf ("%s%s %s 0x%X%s", (cond==15?"":"IF "), (cond==15?"":cond_str[cond]), s?"CALL":"JUMP", (ins>>4)&0x1FFF, db?" (DB)":""); + /* 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; } - 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 17 */ - if ((ins >> 16) == 0x0A) { op->mnemonic = r_str_newf ("%s%s %s", (ins&0xF)==15?"":cond_str[ins&0xF], (ins&0xF)==15?"":" ", (ins>>13)&1?"RTI":"RTS"); return true; } /* Type 20 */ - if ((ins >> 16) == 0x16) { op->mnemonic = r_str_newf ("DO 0x%X UNTIL %s", (ins>>4)&0xFFF, cond_str[ins&0xF]); return true; } /* Type 11 */ - if ((ins >> 16) == 0x0F) { op->mnemonic = r_str_newf ("SR = %s %s BY 0x%X", sf_names[(ins>>12)&0xF], reg0[(ins>>8)&0xF], ins&0xFF); return true; } /* Type 15 */ } op->mnemonic = r_str_newf ("unk 0x%06X", ins); diff --git a/r2plugin/asm_adsp219x.so b/r2plugin/asm_adsp219x.so index 33c60bc4965946d9b48decccee735a1eede2dec7..feccfca0d8c4bac04ede5a0534a35e8cfbd54eb2 100755 GIT binary patch literal 20288 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 literal 20048 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