From dbcf04eb62eeb71c8f1681d05c694b237992910b Mon Sep 17 00:00:00 2001 From: "Dr. Christian Giessen" Date: Wed, 22 Apr 2026 19:57:35 +0000 Subject: [PATCH] Fix Types 3, 4, 29, 32; add relative addressing; truncation guards Type 3: Fix D-bit read/write direction (was always write). DM-only (no PM), use reg1[] for Ireg/Mreg variant. Type 4: Add D-bit for read vs write direction (was always read). Properly distinguish DM(I+=M)=Dreg vs Dreg=DM(I+=M). Type 29: G selects DAG register group, not memory bus. Always DM (was incorrectly PM when G=1). Type 32: MS selects bus (DM/PM), G selects DAG group only. (was: MS||G for bus, causing false PM on G=1 MS=0). Type 10/10a/11: Relative offsets resolved to absolute byte addresses using PC + (offset * 3). op->jump set for r2 xref analysis. Type 36: op->jump set for LJUMP/LCALL. 2-word instructions: Truncation guard when buffer < 6 bytes. Emits 'trunc ...' instead of decoding with zeroed second word. Zero warnings, full regression pass on isa_test/fir/iir. --- r2plugin/asm_adsp219x.c | 124 +++++++++++++++++++++++++++++---------- r2plugin/asm_adsp219x.so | Bin 28512 -> 28512 bytes 2 files changed, 93 insertions(+), 31 deletions(-) diff --git a/r2plugin/asm_adsp219x.c b/r2plugin/asm_adsp219x.c index 746e2a6..23ef21f 100644 --- a/r2plugin/asm_adsp219x.c +++ b/r2plugin/asm_adsp219x.c @@ -98,23 +98,47 @@ decode (RArchSession *as, RAnalOp *op, RAnalOpMask mask) return true; } - /* Type 3: Direct Memory (10xxxx) */ + /* Type 3: Direct Memory (10xxxx) + bit21: 1=Ireg/Mreg, 0=Dreg. D=bit20: 0=read, 1=write. + Addr16=bits19-4. Reg=bits3-0. DM only (no PM). */ 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]); + ut32 d = (ins >> 20) & 1; + ut32 addr = (ins >> 4) & 0xFFFF; + ut32 reg = ins & 0xF; + const char *rname = ((ins >> 21) & 1) + ? reg1[reg] : reg0[reg]; + if (d) + op->mnemonic = r_str_newf ("DM(0x%04X) = %s", + addr, rname); + else + op->mnemonic = r_str_newf ("%s = DM(0x%04X)", + rname, addr); return true; } - /* Type 4: Compute | DM/PM Postmodify (011xxx) */ + /* Type 4: Compute | DM/PM Postmodify (011xxx) + G=bit20 (0=DM/DAG1, 1=PM/DAG2), D=bit19 (0=read, 1=write), + Z=bit18, AMF=bits17-13, YOP=bits12-11, XOP=bits10-8, + DREG=bits7-4, I=bits3-2, M=bits1-0. */ 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)); + ut32 g = (ins >> 20) & 1; + ut32 d = (ins >> 19) & 1; + ut32 amf = (ins >> 13) & 0x1F; + const char *f = (amf < 16) ? amf_mac[amf] : amf_alu[amf - 16]; + ut32 dreg = (ins >> 4) & 0xF; + ut32 ireg = ((ins >> 2) & 3) | (g ? 4 : 0); + ut32 mreg = (ins & 3) | (g ? 4 : 0); + char mem = g ? 'P' : 'D'; + if (d) + op->mnemonic = r_str_newf ("%s, %cM(I%d += M%d) = %s", + f, mem, ireg, mreg, + reg0[dreg]); + else + op->mnemonic = r_str_newf ("%s, %s = %cM(I%d += M%d)", + f, reg0[dreg], mem, + ireg, mreg); return true; } @@ -208,11 +232,18 @@ decode (RArchSession *as, RAnalOp *op, RAnalOpMask mask) /* Type 36: Long Jump/Call (2-word, bits 23-16 = 00000101) */ if ((ins >> 16) == 0x05) { + if (op->size < 6) + { + op->mnemonic = r_str_newf ("trunc L%s 0x%06X", + ((ins >> 12) & 1) ? "CALL" : "JUMP", ins); + return true; + } ut32 s_bit = (ins >> 12) & 1; ut32 cond = ins & 0xF; ut32 addr_hi = (ins >> 4) & 0xFF; ut32 addr_lo = ins2 & 0xFFFFFF; ut32 addr = (addr_hi << 16) | (addr_lo & 0xFFFF); + /* 24-bit absolute address for long jump/call */ op->size = 6; if (cond == 15) op->mnemonic = r_str_newf ("%s 0x%06X", @@ -223,12 +254,19 @@ decode (RArchSession *as, RAnalOp *op, RAnalOpMask mask) s_bit ? "LCALL" : "LJUMP", addr); op->type = s_bit ? R_ANAL_OP_TYPE_CALL : R_ANAL_OP_TYPE_JMP; + op->jump = addr; return true; } /* Type 22: DM/PM = Data16 (2-word, bits 23-13 = 00000011110) */ if ((ins >> 13) == 0x1E && !((ins >> 12) & 1)) { + if (op->size < 6) + { + op->mnemonic = r_str_newf ("trunc DM_IMM16 0x%06X", + ins); + return true; + } ut32 g = (ins >> 12) & 1; ut32 ireg = (ins >> 2) & 0x3; ut32 mreg = ins & 0x3; @@ -245,6 +283,12 @@ decode (RArchSession *as, RAnalOp *op, RAnalOpMask mask) /* Type 22a: PM = Data24 (2-word, bits 23-13 = 00000011111) */ if ((ins >> 13) == 0x1F) { + if (op->size < 6) + { + op->mnemonic = r_str_newf ("trunc PM_IMM24 0x%06X", + ins); + return true; + } ut32 g = (ins >> 12) & 1; ut32 ireg = (ins >> 2) & 0x3; ut32 mreg = ins & 0x3; @@ -258,36 +302,45 @@ decode (RArchSession *as, RAnalOp *op, RAnalOpMask mask) ireg + base, mreg + base, data); return true; } - /* Type 10a: bits 23-18 = 000111 (unconditional 16-bit) */ + /* Type 10a: bits 23-18 = 000111 (unconditional 16-bit rel) */ if ((ins >> 18) == 0x07) { /* addr = bits 17-4 (14 bits) | bits 1-0 (2 MSBs) */ - ut32 addr = ((ins >> 4) & 0x3FFF) | ((ins & 0x3) << 14); + ut32 rel = ((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", + /* Sign-extend 16-bit relative offset */ + int32_t srel = (int32_t)(int16_t) rel; + ut64 target = op->addr + (srel * 3); + op->mnemonic = r_str_newf ("%s 0x%06" PFMT64x "%s", s_bit ? "CALL" : "JUMP", - addr, + target, b_bit ? " (DB)" : ""); op->type = s_bit ? R_ANAL_OP_TYPE_CALL : R_ANAL_OP_TYPE_JMP; + op->jump = target; return true; } - /* Type 10: bits 23-19 = 00011, bit18 = 0 (conditional 13-bit) */ + /* Type 10: bits 23-19 = 00011, bit18 = 0 (conditional 13-bit rel) */ if ((ins >> 19) == 0x03 && !((ins >> 18) & 1)) { ut32 b_bit = (ins >> 17) & 1; - ut32 addr = (ins >> 4) & 0x1FFF; + ut32 rel = (ins >> 4) & 0x1FFF; ut32 cond = ins & 0xF; + /* Sign-extend 13-bit relative offset */ + int32_t srel = (rel & 0x1000) + ? (int32_t)(rel | 0xFFFFE000) : (int32_t) rel; + ut64 target = op->addr + (srel * 3); if (cond == 15) - op->mnemonic = r_str_newf ("JUMP 0x%04X%s", - addr, + op->mnemonic = r_str_newf ("JUMP 0x%06" PFMT64x "%s", + target, b_bit ? " (DB)" : ""); else - op->mnemonic = r_str_newf ("IF %s JUMP 0x%04X%s", - cond_str[cond], addr, + op->mnemonic = r_str_newf ("IF %s JUMP 0x%06" PFMT64x "%s", + cond_str[cond], target, b_bit ? " (DB)" : ""); op->type = R_ANAL_OP_TYPE_CJMP; + op->jump = target; return true; } /* Type 17: Reg = Reg (bits 23-16 = 00001101) */ @@ -370,13 +423,17 @@ decode (RArchSession *as, RAnalOp *op, RAnalOpMask mask) return true; } - /* Type 11: DO UNTIL (bits 23-16 = 00010110) */ + /* Type 11: DO UNTIL (bits 23-16 = 00010110, 12-bit rel) */ if ((ins >> 16) == 0x16) { - ut32 addr = (ins >> 4) & 0xFFF; + ut32 rel = (ins >> 4) & 0xFFF; ut32 term = ins & 0xF; - op->mnemonic = r_str_newf ("DO 0x%03X UNTIL %s", - addr, cond_str[term]); + int32_t srel = (rel & 0x800) + ? (int32_t)(rel | 0xFFFFF000) : (int32_t) rel; + ut64 target = op->addr + (srel * 3); + op->mnemonic = r_str_newf ( + "DO 0x%06" PFMT64x " UNTIL %s", + target, cond_str[term]); return true; } @@ -533,7 +590,11 @@ decode (RArchSession *as, RAnalOp *op, RAnalOpMask mask) return true; } - /* Type 29: Dreg <-> DM imm modify (bits 23-18 = 000010) */ + /* Type 29: Dreg <-> DM imm modify (bits 23-18 = 000010) + Always DM. G=bit13 selects DAG group (I0-3 vs I4-7), + not the memory bus. U=bit16 (post/pre), D=bit12 (r/w), + DRU=bits15-14, DRL=bits1-0, DREG=(DRU<<2)|DRL, + MOD=bits11-4 (signed), I=bits3-2. */ if ((ins >> 18) == 0x02) { ut32 u = (ins >> 16) & 1; @@ -546,15 +607,14 @@ decode (RArchSession *as, RAnalOp *op, RAnalOpMask mask) ut32 dreg = (dru << 2) | drl; int8_t smod = (int8_t) mod; int base = g ? 4 : 0; - const char *mem = g ? "PM" : "DM"; const char *op_str = u ? "+=" : "+"; if (d) - op->mnemonic = r_str_newf ("%s(I%d %s %d) = %s", - mem, ireg + base, op_str, smod, + op->mnemonic = r_str_newf ("DM(I%d %s %d) = %s", + ireg + base, op_str, smod, reg0[dreg]); else - op->mnemonic = r_str_newf ("%s = %s(I%d %s %d)", - reg0[dreg], mem, ireg + base, op_str, + op->mnemonic = r_str_newf ("%s = DM(I%d %s %d)", + reg0[dreg], ireg + base, op_str, smod); return true; } @@ -571,7 +631,9 @@ decode (RArchSession *as, RAnalOp *op, RAnalOpMask mask) ut32 ireg = (ins >> 2) & 0x3; ut32 mreg = ins & 0x3; int base = g ? 4 : 0; - const char *mem = (ms || g) ? "PM" : "DM"; + /* MS selects bus (0=DM 16-bit, 1=PM 24-bit). + G selects DAG group only. */ + const char *mem = ms ? "PM" : "DM"; const char *mod = u_bit ? "+=" : "+"; const char *rname = get_reg (rgp, reg); if (d) diff --git a/r2plugin/asm_adsp219x.so b/r2plugin/asm_adsp219x.so index 4744074cfab47d8f8cb612ef0bfde434a27975ab..198f093324eb1b144d7cb9989c3e5b817895098b 100755 GIT binary patch literal 28512 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 F|8E;noyh