Generate symbolic names for ldi on register pairs

This commit is contained in:
Stefan Rueger
2024-07-27 13:32:58 +01:00
parent e594863f95
commit 95bc4db4b9

View File

@@ -20,11 +20,16 @@
/* $Id$ */
/*
* The disassembly code originates from the avrdisas disassembler written in
* 2007 by Johannes Bauer. This code has been rewritten by Stefan Rueger to
* This disassembly code originates from the avrdisas disassembler written in
* 2007 by Johannes Bauer. It has been rewritten by Stefan Rueger to
* - Enable disassembly of small memory chunks in AVRDUDE's terminal
* - Drive disassembly from the avr_opcodes[] table alone
* - Generate a compilable source
* - Find symbolic values for ldi constants that initialise register pais
*
* Like the ship of Theseus there is little of the avrdisas orginal code that
* has remained, but it is fair to say that without it the AVRDUDE disasm
* command would not have happened.
*/
#include <stdio.h>
@@ -44,6 +49,7 @@
#define TYPE_ASTRING 3 // Autoaligned string
#define TYPE_STRING 4 // String
#define buf2op16(i) ((buf[i] & 0xff) | (buf[(i)+1] & 0xff)<<8)
static void zap_symbols() {
if(cx->dis_symbols) {
@@ -57,15 +63,25 @@ static void zap_symbols() {
cx->dis_symbolN = 0;
}
static int type_order(int type) {
switch(type) {
case 'I': return '1';
case 'M': return '2';
case 'L': return '3';
case 'P': return '4';
default: return type;
}
}
static int symbol_sort(const void *v1, const void *v2) {
const Disasm_symbol *p1 = v1, *p2 = v2;
int diff;
if((diff = p1->type - p2->type))
if((diff = type_order(p1->type) - type_order(p2->type)))
return diff;
return p1->address - p2->address;
}
static int symbol_qsort_stable(const void *v1, const void *v2) {
static int symbol_stable_qsort(const void *v1, const void *v2) {
int diff = symbol_sort(v1, v2);
if(diff)
return diff;
@@ -78,7 +94,7 @@ static char *cleanup(char *str) {
return str;
}
// Width of memory a symbol covers (only valid for I/O and memory symbols)
// Width of memory a symbol covers
static int symbol_width(Disasm_symbol *s) {
return s->count * (s->subtype == TYPE_WORD? 2: 1);
}
@@ -113,6 +129,7 @@ static void add_symbol(int address, int type, int subtype, int count, const char
cx->dis_symbols[N].subtype = subtype;
cx->dis_symbols[N].count = count;
cx->dis_symbols[N].used = 0;
cx->dis_symbols[N].printed = 0;
cx->dis_symbols[N].name = name? cleanup(mmt_strdup(name)): NULL;
cx->dis_symbols[N].comment = comment? mmt_strdup(comment): NULL;
}
@@ -271,7 +288,7 @@ static void init_regfile(const AVRPART *p) {
add_register(io_off, rf[i].addr+k, rname, k);
}
}
qsort(cx->dis_symbols, cx->dis_symbolN, sizeof(Disasm_symbol), symbol_qsort_stable);
qsort(cx->dis_symbols, cx->dis_symbolN, sizeof(Disasm_symbol), symbol_stable_qsort);
}
}
@@ -299,7 +316,7 @@ int disasm_init_tagfile(const AVRPART *p, const char *fname) {
}
fclose(inf);
qsort(cx->dis_symbols, cx->dis_symbolN, sizeof(Disasm_symbol), symbol_qsort_stable);
qsort(cx->dis_symbols, cx->dis_symbolN, sizeof(Disasm_symbol), symbol_stable_qsort);
return 0;
error:
@@ -330,11 +347,12 @@ static const char *cycles(int mnemo) {
return ret;
}
static const char *get_label_name(int destination, const char **comment) {
static const char *get_label_name(int destination, const char **commentp) {
Disasm_symbol *s = find_symbol('L', destination);
if(s) {
if(comment)
*comment = s->comment;
if(s && s->name) {
if(commentp)
*commentp = s->comment;
s->printed = 1; // Will be printed in pass 2
return s->name;
}
@@ -395,9 +413,6 @@ static int is_jumpable(int address) {
static void lineout(const char *code, const char *comment,
int mnemo, int oplen, const char *buf, int pos, int addr, int showlabel) {
if(cx->dis_pass == 1)
return;
int here = disasm_wrap(pos + addr), codewidth = 27;
if(cx->dis_opts.labels && showlabel) {
@@ -408,16 +423,16 @@ static void lineout(const char *code, const char *comment,
if(cx->dis_jumpcalls[i].to == here) {
if(!match++)
cx->dis_para = 1;
disasm_out("; Referenced from L%0*x by %s\n", cx->dis_addrwidth,
cx->dis_jumpcalls[i].from, avr_opcodes[cx->dis_jumpcalls[i].mnemo].opcode);
disasm_out("; %s from L%0*x\n", avr_opcodes[cx->dis_jumpcalls[i].mnemo].opcode,
cx->dis_addrwidth, cx->dis_jumpcalls[i].from);
}
}
if(match && (name = get_label_name(here, &comment))) {
if(comment == NULL)
disasm_out("%s:\n", name);
else
if(comment)
disasm_out("%-*s ; %s\n", codecol() + codewidth, str_ccprintf("%s:", name), comment);
else
disasm_out("%s:\n", name);
}
}
@@ -448,9 +463,8 @@ static int process_num(const char *buf, int buflen, int nbytes, int pos, int off
const char *str =
nbytes == 1? str_ccprintf(".byte 0x%02x", buf[pos] & 0xff):
nbytes == 2? str_ccprintf(".word 0x%02x%02x", buf[pos+1] & 0xff, buf[pos] & 0xff):
nbytes == 4? str_ccprintf(".long 0x%02x%02x%02x%02x", buf[pos+3] & 0xff, buf[pos+2] & 0xff,
buf[pos+1] & 0xff, buf[pos] & 0xff): "nbytes?";
nbytes == 2? str_ccprintf(".word 0x%04x", buf2op16(pos)):
nbytes == 4? str_ccprintf(".long 0x%04x%04x", buf2op16(pos+2), buf2op16(pos)): "nbytes?";
lineout(str, NULL, -1, 1, buf, pos, offset, 0);
return nbytes;
@@ -505,13 +519,14 @@ static int process_data(const char *buf, int buflen, int pos, int offset) {
k &= ~1;
return !k || k-pos < 4? 0: process_fill0xff(buf, buflen, k-pos, pos, offset);
}
// Found PGM label at odd address, print byte and continue
// Found PGM label at odd address, print byte before label and continue
process_num(buf, buflen, 1, pos, offset);
ret = 1;
}
if(s->name) {
cx->dis_para = 1;
s->printed = 1; // Will be printed in pass 2
disasm_out("%s:\n", s->name);
}
@@ -539,20 +554,19 @@ static int process_data(const char *buf, int buflen, int pos, int offset) {
return ret;
}
void emit_used_io_registers() {
int maxlen = 0;
static void emit_used_symbols() {
Disasm_symbol *s = cx->dis_symbols;
int len, maxlen = 0;
for(int i = 0; i < cx->dis_symbolN; i++)
if(cx->dis_symbols[i].used) {
int len = strlen(cx->dis_symbols[i].name);
if(len > maxlen)
if(s[i].used && !s[i].printed)
if((len = strlen(s[i].name)) > maxlen)
maxlen = len;
}
for(int i = 0; i < cx->dis_symbolN; i++)
if(cx->dis_symbols[i].used && (cx->dis_io_offset || cx->dis_symbols[i].type == 'M'))
disasm_out(".equ %s,%*s 0x%02x\n", cx->dis_symbols[i].name,
(int) (maxlen-strlen(cx->dis_symbols[i].name)), "", cx->dis_symbols[i].address);
if(s[i].used && !s[i].printed)
disasm_out(".equ %s,%*s 0x%02x\n", s[i].name,
(int) (maxlen-strlen(s[i].name)), "", s[i].address);
}
void disasm_zap_jumpcalls() {
@@ -561,7 +575,7 @@ void disasm_zap_jumpcalls() {
cx->dis_jumpcallN = 0;
}
static void register_jumpcall(int from, int to, int mnemo, unsigned char is_func) {
static void register_jumpcall(int from, int to, int mnemo, int is_func) {
if(cx->dis_opts.labels) {
Disasm_jumpcall *jc = cx->dis_jumpcalls;
int N = cx->dis_jumpcallN;
@@ -608,6 +622,8 @@ static int jumpcall_sort(const void *v1, const void *v2) {
int diff;
if((diff = p1->to - p2->to))
return diff;
if((diff = p1->mnemo - p2->mnemo))
return diff;
return p1->from - p2->from;
}
@@ -681,7 +697,58 @@ static unsigned bitcount(unsigned n) {
return ret;
}
static void disassemble(const char *buf, int addr, int opcode, AVR_mnemo mnemo, Disasm_line *line) {
typedef struct {
int from, is_func, is_lpm, preop, postop, zwd;
} Op_context;
static const char *get_ldi_name(int op1, int op2, Op_context *oxp) {
Disasm_symbol *s;
char buf[2];
int ra = ldi_Rd(op1), rb = ldi_Rd(op2);
if((ra ^ rb) == 1) { // Two successive ldi opcodes initialise a register pair
buf[ra & 1] = ldi_K(op1);
buf[rb & 1] = ldi_K(op2);
int addr = buf2op16(0); // Address of register pair
// Assume address width is 2 if ldi acts on Z and is followed by e/icall within 5 opcodes
int awidth = (ra | 1) == 31 && oxp->zwd == 2? 2: 1;
for(const char *c = awidth == 2 || oxp->is_lpm? "LP": "MLP"; *c; c++)
if((s = find_symbol(*c, addr*awidth)))
break;
if(s && s->name) { // Label matches the address loaded into register pair
s->used = 1;
return str_ccprintf("%s%s(%s)", awidth == 2? "pm_": "", ra & 1? "hi8": "lo8", s->name);
}
if((ra | 1) == 31 && oxp->is_lpm && addr >= cx->dis_start && addr < cx->dis_end) {
if(cx->dis_pass == 1)
register_jumpcall(oxp->from, addr, MNEMO_ldi, 0);
const char *name = get_label_name(addr, NULL);
if(name && cx->dis_opts.labels && is_jumpable(addr))
return str_ccprintf("%s(%s)", ra & 1? "hi8": "lo8", name);
} else if(awidth == 2 && 2*addr >= cx->dis_start && 2*addr < cx->dis_end) {
if(cx->dis_pass == 1)
register_jumpcall(oxp->from, 2*addr, MNEMO_ldi, oxp->is_func);
const char *name = get_label_name(2*addr, NULL);
if(name && cx->dis_opts.labels && is_jumpable(2*addr))
return str_ccprintf("pm_%s(%s)", ra & 1? "hi8": "lo8", name);
}
}
return NULL;
}
static const char *get_ldi_context(Op_context *oxp, int opcode) {
const char *ret;
if(oxp->preop >= 0 && (ret = get_ldi_name(opcode, oxp->preop, oxp)))
return ret;
if(oxp->postop >= 0 && (ret = get_ldi_name(opcode, oxp->postop, oxp)))
return ret;
return NULL;
}
static void disassemble(const char *buf, int addr, int opcode, AVR_mnemo mnemo,
Op_context *oxp, Disasm_line *line) {
memset(line, 0, sizeof*line);
if(mnemo < 0) {
add_comment(line, "Invalid opcode");
@@ -705,7 +772,7 @@ static void disassemble(const char *buf, int addr, int opcode, AVR_mnemo mnemo,
if(oc->nwords == 2) {
bits['k'] += 16;
regs['k'] <<= 16;
regs['k'] |= (buf[2] & 0xff) | (buf[3] & 0xff)<<8;
regs['k'] |= buf2op16(2);
}
// Some sanity checks for things the code relies on
@@ -755,7 +822,7 @@ static void disassemble(const char *buf, int addr, int opcode, AVR_mnemo mnemo,
int target = 0, offset = 0, is_jumpcall = 0, is_relative = 0;
int is_function = !!(oc->type & OTY_EXTERNAL); // call/rcall affects stack memory
const char *kmemaddr = NULL, *amemaddr = NULL, *regname = NA? resolve_address('I', RA): NULL;
const char *name, *ksym = NULL, *asym = NULL, *rsym = NA? resolve_address('I', RA): NULL;
if(Na) {
/*
@@ -764,7 +831,7 @@ static void disassemble(const char *buf, int addr, int opcode, AVR_mnemo mnemo,
* ADDR[7:0] ← (/a[4], a[4], a[6], a[5], a[3], a[2], a[1], a[0])
*/
Ra = (Ra & 0xf) | ((Ra >> 1) & 0x30) | ((Ra & 0x10) << 2) | (((Ra & 0x10) ^ 0x10) << 3);
amemaddr = resolve_address('M', Ra);
asym = resolve_address('M', Ra);
}
switch(Nk) {
@@ -787,7 +854,7 @@ static void disassemble(const char *buf, int addr, int opcode, AVR_mnemo mnemo,
is_relative = 1;
break;
case 16: // lds/sts
kmemaddr = resolve_address('M', Rk);
ksym = resolve_address('M', Rk);
break;
case 22:
if(cx->dis_flashsz && 2*Rk > cx->dis_flashsz)
@@ -800,9 +867,6 @@ static void disassemble(const char *buf, int addr, int opcode, AVR_mnemo mnemo,
break;
}
if(cx->dis_pass == 1)
return;
snprintf(line->code, LINE_N, "%-7s ", oc->opcode);
char *lc = line->code + strlen(line->code);
#define add_operand(lc, ...) snprintf((lc), LINE_N - ((lc) - line->code), __VA_ARGS__)
@@ -832,20 +896,20 @@ static void disassemble(const char *buf, int addr, int opcode, AVR_mnemo mnemo,
*lc++ = *o, *lc = 0;
break;
case 'A':
if(regname)
add_operand(lc, "%s", regname);
if(rsym)
add_operand(lc, "%s", rsym);
else
add_operand(lc, "0x%02x", RA);
break;
case 'a':
if(amemaddr)
add_operand(lc, "%s", amemaddr);
if(asym)
add_operand(lc, "%s", asym);
else
add_operand(lc, "0x%02x", Ra);
break;
case 'k':
if(is_jumpcall) {
const char *name = get_label_name(target, NULL);
name = get_label_name(target, NULL);
if(name && target != disasm_wrap(addr+2) && cx->dis_opts.labels && is_jumpable(target)) {
add_operand(lc, "%s", name);
add_comment(line, str_ccprintf("L%0*x", awd, target));
@@ -857,8 +921,8 @@ static void disassemble(const char *buf, int addr, int opcode, AVR_mnemo mnemo,
add_operand(lc, "0x%0*x", awd, target);
}
} else {
if(kmemaddr)
add_operand(lc, "%s", kmemaddr);
if(ksym)
add_operand(lc, "%s", ksym);
else
add_operand(lc, "0x%0*x", swd, Rk);
}
@@ -880,8 +944,13 @@ static void disassemble(const char *buf, int addr, int opcode, AVR_mnemo mnemo,
if(NK == 4)
add_operand(lc, "%d", RK);
else {
add_operand(lc, "0x%02x", RK);
add_comment(line, str_ccprintf("%d", RK));
if(op16_is_mnemo(opcode, MNEMO_ldi) && (name = get_ldi_context(oxp, opcode))) {
add_operand(lc, "%s", name);
add_comment(line, str_ccprintf("0x%02x", RK));
} else {
add_operand(lc, "0x%02x", RK);
add_comment(line, str_ccprintf("%d", RK));
}
}
break;
case 'q':
@@ -913,20 +982,47 @@ static int have_own_main() {
return 0;
}
static void set_context(Op_context *oxp, const char *buf, int pos, int buflen, int addr, int leadin, int leadout) {
// Compute initial context structure: the opcode before and the following one
oxp->from = disasm_wrap(pos+addr);
oxp->is_func = 0; // the next Z-opcode ahead is an icall/eicall
oxp->is_lpm = 0; // the next Z-opcode ahead is a lpm/elpm
oxp->preop = pos+leadin > 1? buf2op16(pos-2): -1;
oxp->postop = -1;
oxp->zwd = 0; // 2: the next Z-opcode ahead uses Z as word addr, 1: as byte addr
int k = 0, op16, i = pos + op_width(buf2op16(pos));
if(i < buflen+leadout-1) {
AVR_mnemo zm = 0;
oxp->postop = op16 = buf2op16(i);
// Check whether there is an opcode ahead that uses the Z register
for(k=0, i += op_width(op16); k < 6 && i < buflen+leadout-1; k++, i += op_width(op16))
if(op16_is_mnemo(op16, MNEMO_rjmp) || op16_is_mnemo(op16, MNEMO_jmp) ||
op16_is_mnemo(op16, MNEMO_ret) || op16_is_mnemo(op16, MNEMO_reti) ||
op16_is_mnemo(op16, MNEMO_u_ret) || op16_is_mnemo(op16, MNEMO_u_reti) ||
(oxp->zwd = z_width((op16 = buf2op16(i)), &zm)))
break;
if(oxp->zwd == 2)
oxp->is_func = zm == MNEMO_icall || zm == MNEMO_eicall || zm == MNEMO_u_icall || zm == MNEMO_u_eicall;
else
oxp->is_lpm = zm >= MNEMO_lpm_0 && zm <= MNEMO_elpm_zp;
}
}
/*
* Disassemble buflen bytes at buf which corresponds to address addr
*
* - Caller is responsible that buflen does not split an opcode
* - Before(!) the location buf there are leadin bytes available (0-2)
* - After the location buf+readlen there are leadout bytes available (0-4)
* - After the location buf+readlen there are leadout bytes available (0-16)
*/
int disasm(const char *buf, int buflen, int addr, int leadin, int leadout) {
int pos, opcode, mnemo, oplen;
Disasm_line line;
Disasm_line line = {0};
Op_context ox = {0};
for(int i = 0; i < cx->dis_symbolN; i++) // Clear used-state of symbols
if(cx->dis_symbols[i].type == 'I')
cx->dis_symbols[i].used = 0;
for(int i = 0; i < cx->dis_symbolN; i++) // Clear used/printed state of symbols
cx->dis_symbols[i].used = cx->dis_symbols[i].printed = 0;
cx->dis_jumpable = mmt_malloc((buflen+1)/2/8); // Allocate one bit per word address
cx->dis_start = addr, cx->dis_end = addr + buflen - 1;
@@ -937,10 +1033,10 @@ int disasm(const char *buf, int buflen, int addr, int leadin, int leadout) {
cx->dis_para = 0;
enumerate_labels();
if(cx->dis_opts.avrgcc_style)
emit_used_io_registers();
emit_used_symbols();
if(cx->dis_opts.gcc_source) {
cx->dis_para=1;
disasm_out(".text%s\n", have_own_main()? "": "main:\n");
disasm_out(".text\n%s", have_own_main()? "": "main:\n");
}
}
for(pos = 0; pos < buflen; pos += oplen) {
@@ -955,13 +1051,15 @@ int disasm(const char *buf, int buflen, int addr, int leadin, int leadout) {
continue;
}
opcode = (buf[pos] & 0xff) | (buf[pos+1] & 0xff)<<8;
opcode = buf2op16(pos);
mnemo = opcode_mnemo(opcode, cx->dis_opts.avrlevel);
oplen = mnemo < 0? 2: 2*avr_opcodes[mnemo].nwords;
disassemble(buf + pos, disasm_wrap(pos + addr), opcode, mnemo, &line);
if(op16_is_mnemo(opcode, MNEMO_ldi))
set_context(&ox, buf, pos, buflen, addr, leadin, leadout);
disassemble(buf + pos, disasm_wrap(pos + addr), opcode, mnemo, &ox, &line);
lineout(line.code, line.comment, mnemo, oplen, buf, pos, addr, 1);
if(cx->dis_pass == 1) { // Mark this position as one that can be a jump/call destination
if(cx->dis_pass == 1) { // Mark this position as potential jump/call destination
int n = sizeof(int)*8, idx = pos/2;
cx->dis_jumpable[idx/n] |= (1<<(idx%n));
}
@@ -986,7 +1084,7 @@ int disasm_init(const AVRPART *p) {
cx->dis_flashsz = 0; // Flash size
cx->dis_flashsz2 = 0; // Flash size rounded up to next power of two
cx->dis_addrwidth = 4; // Number of hex digits needed for flash addresses
cx->dis_sramwidth = 3; // Number of hex digits needed for sram addresses
cx->dis_sramwidth = 4; // Number of hex digits needed for sram addresses
if((mem = avr_locate_flash(p)) && mem->size > 1) {
int nbits = intlog2(mem->size - 1) + 1;