/* This file is in the public domain. */ /* * AVR support. The AVR is a Harvard machine; this works on code space * only. */ #include #include #include #include #include extern const char *__progname; #include "sortsearch.h" #include "fnprintf.h" #include "machine.h" typedef enum { OPS_B7, OPS_BO, OPS_DR, OPS_IO5_B0, OPS_IO6_RD5, OPS_KPLUS, OPS_KPLUS_RD5, OPS_MINUSX_RD5, OPS_MINUSYZ_RD5, OPS_NONE, OPS_NONE_ZPLUS, OPS_OP, OPS_RD5, OPS_RD5_B0, OPS_RD5_DUP, OPS_RD5_IO6, OPS_RD5_KPLUS, OPS_RD5_MINUSX, OPS_RD5_MINUSYZ, OPS_RD5_RR5, OPS_RD5_X, OPS_RD5_XPLUS, OPS_RD5_YZPLUS, OPS_RD5_YZ_Q, OPS_RD5_Z, OPS_RD5_ZPLUS, OPS_RDH_K8, OPS_RDH_RRH, OPS_RDQ_RRQ, OPS_RPD4_RPR0, OPS_RPD8_K6, OPS_XPLUS_RD5, OPS_X_RD5, OPS_YZPLUS_RD5, OPS_YZ_Q_RD5, OPS_Z_RD5, } INSTROP; typedef struct instr INSTR; struct instr { const char *name; unsigned short int fixedbits; INSTROP ops; int prio; } ; /* * ~r5 = reg number in 020f bits * ~d5 = reg number in 01f0 bits * ~rp = reg pair number in 000f bits * ~dp = reg pair number in 00f0 bits * ~rh = reg number in 16..31 in 0x000f bits * ~dh = reg number in 16..31 in 0x00f0 bits * ~rq = reg number in 16..23 in 0x0007 bits * ~dq = reg number in 16..23 in 0x0070 bits * ~d8 = reg number in 24/26/28/30 in 0x0030 bits * ~k8 = immediate data in 0x0f0f bits * ~k7 = 0x070f bits mangled to an 8-bit address (page 117) * ~k6 = 6-bit unsigned immediate data in 0x00cf bits * ~q6 = 6-bit unsigned offset in 0x2c07 bits * ~k+ = 16-bit address in second word * ~yz = Y if 0x0008 bit is set, Z if not * ~sc = "cl" if 0x0080 bit is set, "se" if not * ~c4 = status bit name based on 0x0070 bits: cznvshti[bits] * ~c0 = status bit name based on 0x0007 bits: cznvshti[bits] * ~b0 = bit number in 0x0007 bits * ~DR = DES round number in 0x00f0 bits * ~K+ = 22-bit address in 0x01f1 bits and second word * ~?(test) ~| ~. = conditional; (test) can be * rd true if ~r5 value equals ~d5 value * q0 true if ~q6 value is zero * ~BO = branch offset in 0x0fff bits * ~B7 = branch offset in 0x03f8 bits * ~i5 = 5-bit I/O register number in 0x00f8 bits * ~i6 = 6-bit I/O register number in 0x060f bits */ /* * STD Y+k,Rd and the short forms of STS and LDS collide. I don't know * whether they're just never both implemented in the same part or * whether the PDF is broken or what. For now I'm commenting out the * short form of STS and LDS. */ static INSTR Instrs[] = { { "adc", 0x1c00, OPS_RD5_RR5 }, // 030 { "add", 0x0c00, OPS_RD5_RR5 }, // 032 { "adiw", 0x9600, OPS_RPD8_K6 }, // 033 { "and", 0x2000, OPS_RD5_RR5 }, // 035 { "andi", 0x7000, OPS_RDH_K8 }, // 036 { "asr", 0x9405, OPS_RD5 }, // 037 { "bld", 0xf800, OPS_RD5_B0 }, // 039 { "brcc", 0xf400, OPS_B7 }, // 042 { "brcs", 0xf000, OPS_B7 }, // 043 { "break", 0x9598, OPS_NONE }, // 044 { "breq", 0xf001, OPS_B7 }, // 045 { "brge", 0xf404, OPS_B7 }, // 046 { "brhc", 0xf405, OPS_B7 }, // 047 { "brhs", 0xf005, OPS_B7 }, // 048 { "brid", 0xf407, OPS_B7 }, // 049 { "brie", 0xf007, OPS_B7 }, // 050 { "brlt", 0xf004, OPS_B7 }, // 052 { "brmi", 0xf002, OPS_B7 }, // 053 { "brne", 0xf401, OPS_B7 }, // 054 { "brpl", 0xf402, OPS_B7 }, // 055 { "brtc", 0xf406, OPS_B7 }, // 057 { "brts", 0xf006, OPS_B7 }, // 058 { "brvc", 0xf403, OPS_B7 }, // 059 { "brvs", 0xf003, OPS_B7 }, // 060 { "bst", 0xfa00, OPS_RD5_B0 }, // 062 { "call", 0x940e, OPS_KPLUS }, // 063 { "cbi", 0x9800, OPS_IO5_B0 }, // 065 { "clc", 0x9488, OPS_NONE }, // 067 { "clh", 0x94d8, OPS_NONE }, // 068 { "cli", 0x94f8, OPS_NONE }, // 069 { "cln", 0x94a8, OPS_NONE }, // 070 { "clr", 0x2400, OPS_RD5_DUP, 1 }, // 071 { "cls", 0x94c8, OPS_NONE }, // 072 { "clt", 0x94e8, OPS_NONE }, // 073 { "clv", 0x94b8, OPS_NONE }, // 074 { "clz", 0x9498, OPS_NONE }, // 075 { "com", 0x9400, OPS_RD5 }, // 076 { "cp", 0x1400, OPS_RD5_RR5 }, // 077 { "cpc", 0x0400, OPS_RD5_RR5 }, // 079 { "cpi", 0x3000, OPS_RDH_K8 }, // 081 { "cpse", 0x1000, OPS_RD5_RR5 }, // 083 { "dec", 0x940a, OPS_RD5 }, // 084 { "des", 0x940b, OPS_DR }, // 086 { "eicall", 0x9519, OPS_NONE }, // 087 { "eijmp", 0x9419, OPS_NONE }, // 088 { "elpm", 0x9006, OPS_RD5_Z }, // 089 { "elpm", 0x9007, OPS_RD5_ZPLUS }, // 089 { "elpm", 0x95d8, OPS_NONE }, // 089 { "eor", 0x2400, OPS_RD5_RR5 }, // 091 { "fmul", 0x0308, OPS_RDQ_RRQ }, // 092 { "fmuls", 0x0380, OPS_RDQ_RRQ }, // 094 { "fmulsu", 0x0388, OPS_RDQ_RRQ }, // 096 { "icall", 0x9509, OPS_NONE }, // 098 { "ijmp", 0x9409, OPS_NONE }, // 099 { "in", 0xb000, OPS_RD5_IO6 }, // 100 { "inc", 0x9403, OPS_RD5 }, // 101 { "jmp", 0x940c, OPS_KPLUS }, // 103 { "lac", 0x9206, OPS_Z_RD5 }, // 104 { "las", 0x9205, OPS_Z_RD5 }, // 105 { "lat", 0x9207, OPS_Z_RD5 }, // 106 { "ld", 0x9001, OPS_RD5_YZPLUS }, // 109/112 { "ld", 0x9002, OPS_RD5_MINUSYZ }, // 109/112 { "ld", 0x900c, OPS_RD5_X }, // 107 { "ld", 0x900d, OPS_RD5_XPLUS }, // 107 { "ld", 0x900e, OPS_RD5_MINUSX }, // 107 { "ldd", 0x8000, OPS_RD5_YZ_Q }, // 109/112 { "ldi", 0xe000, OPS_RDH_K8 }, // 115 { "lds", 0x9000, OPS_RD5_KPLUS }, // 116 { "lpm", 0x9004, OPS_RD5_Z }, // 118 { "lpm", 0x9005, OPS_RD5_ZPLUS }, // 118 { "lpm", 0x95c8, OPS_NONE }, // 118 { "lsl", 0x0c00, OPS_RD5_DUP, 1 }, // 120 { "lsr", 0x9406, OPS_RD5 }, // 122 { "mov", 0x2c00, OPS_RD5_RR5 }, // 123 { "movw", 0x0100, OPS_RPD4_RPR0 }, // 124 { "mul", 0x9c00, OPS_RD5_RR5 }, // 125 { "muls", 0x0200, OPS_RDH_RRH }, // 126 { "mulsu", 0x0300, OPS_RDQ_RRQ }, // 127 { "neg", 0x9401, OPS_RD5 }, // 129 { "nop", 0x0000, OPS_NONE }, // 131 { "or", 0x2800, OPS_RD5_RR5 }, // 132 { "ori", 0x6000, OPS_RDH_K8 }, // 133 { "out", 0xb800, OPS_IO6_RD5 }, // 134 { "pop", 0x900f, OPS_RD5 }, // 135 { "push", 0x920f, OPS_RD5 }, // 136 { "rcall", 0xd000, OPS_BO }, // 137 { "ret", 0x9508, OPS_NONE }, // 139 { "reti", 0x9518, OPS_NONE }, // 140 { "rjmp", 0xc000, OPS_BO }, // 142 { "rol", 0x1c00, OPS_RD5_DUP, 1 }, // 143 { "ror", 0x9407, OPS_RD5 }, // 145 { "sbc", 0x0800, OPS_RD5_RR5 }, // 147 { "sbci", 0x4000, OPS_RDH_K8 }, // 149 { "sbi", 0x9a00, OPS_IO5_B0 }, // 151 { "sbic", 0x9900, OPS_IO5_B0 }, // 152 { "sbis", 0x9b00, OPS_IO5_B0 }, // 153 { "sbiw", 0x9700, OPS_RPD8_K6 }, // 154 { "sbrc", 0xfc00, OPS_RD5_B0 }, // 157 { "sbrs", 0xfe00, OPS_RD5_B0 }, // 158 { "sec", 0x9408, OPS_NONE }, // 159 { "seh", 0x9458, OPS_NONE }, // 160 { "sei", 0x9478, OPS_NONE }, // 161 { "sen", 0x9428, OPS_NONE }, // 162 { "ses", 0x9448, OPS_NONE }, // 164 { "set", 0x9468, OPS_NONE }, // 165 { "sev", 0x9438, OPS_NONE }, // 166 { "sez", 0x9418, OPS_NONE }, // 167 { "sleep", 0x9588, OPS_NONE }, // 168 { "spm", 0x95e8, OPS_NONE }, // 171 { "spm", 0x95f8, OPS_NONE_ZPLUS }, // 169/171 { "st", 0x9201, OPS_YZPLUS_RD5 }, // 175/177 { "st", 0x9202, OPS_MINUSYZ_RD5 }, // 175/177 { "st", 0x920c, OPS_X_RD5 }, // 173 { "st", 0x920d, OPS_XPLUS_RD5 }, // 173 { "st", 0x920e, OPS_MINUSX_RD5 }, // 173 { "std", 0x8200, OPS_YZ_Q_RD5 }, // 175/177 { "sts", 0x9200, OPS_KPLUS_RD5 }, // 179 { "sub", 0x1800, OPS_RD5_RR5 }, // 181 { "subi", 0x5000, OPS_RDH_K8 }, // 183 { "swap", 0x9402, OPS_RD5 }, // 185 { "tst", 0x2000, OPS_RD5_DUP, 1 }, // 186 { "wdr", 0x95a8, OPS_NONE }, // 187 { "xch", 0x9204, OPS_Z_RD5 }, // 188 }; // { // { 0xfc00, 0xf000, "brbs ~c0, ~B7" }, // { 0xfc00, 0xf400, "brbc ~c0, ~B7" }, // { 0xf800, 0xa000, "lds R~dh, ~k7" }, // { 0xf800, 0xa800, "sts ~k7, R~dh" }, // } #define NINSTRS (sizeof(Instrs)/sizeof(Instrs[0])) static INSTR badinst = { "unrecognized", 0, OPS_OP }; static INSTR *instmap[65536]; static int opc_d5(unsigned short int opc) { return((opc>>4)&0x1f); } static int opc_r5(unsigned short int opc) { return(((opc>>5)&0x10)|(opc&0xf)); } static int opc_q6(unsigned short int opc) { return(((opc>>8)&0x20)|((opc>>7)&0x18)|(opc&7)); } static int opc_B7(unsigned short int opc) { int v; v = (opc >> 3) & 0x7f; if (v & 0x40) v -= 0x80; return(v); } static int opc_BO(unsigned short int opc) { int v; v = opc & 0xfff; if (v & 0x800) v -= 0x1000; return(v); } static int opc_DR(unsigned short int opc) { return((opc>>4)&15); } static int opc_Kplus1(unsigned short int opc) { return(((opc>>3)&0x3e)|(opc&1)); } static int opc_b0(unsigned short int opc) { return(opc&7); } static int opc_d8(unsigned short int opc) { return(((opc>>3)&6)+24); } static int opc_dh(unsigned short int opc) { return(((opc>>4)&15)+16); } static int opc_dp(unsigned short int opc) { return((opc>>3)&0x1e); } static int opc_dq(unsigned short int opc) { return(((opc>>4)&7)+16); } static int opc_i5(unsigned short int opc) { return((opc>>3)&0x1f); } static int opc_i6(unsigned short int opc) { return(((opc>>5)&0x30)|(opc&0xf)); } static int opc_k6(unsigned short int opc) { return(((opc>>2)&0x30)|(opc&0xf)); } #if 0 // Used only for ATtiny parts (16-bit LDS/STS) static int opc_k7(unsigned short int opc) { return( (((opc >> 1) & 0x80) ^ 0x80) | ((opc >> 2) & 0x40) | ((opc >> 5) & 0x30) | (opc & 0xf) ); } #endif static int opc_k8(unsigned short int opc) { return(((opc>>4)&0xf0)|(opc&0xf)); } static int opc_rh(unsigned short int opc) { return((opc&0xf)+16); } static int opc_rp(unsigned short int opc) { return((opc<<1)&0x1e); } static int opc_rq(unsigned short int opc) { return((opc&7)+16); } static int opmatch(INSTROP op, unsigned short int opc, unsigned short int fixed) { unsigned short int mask; switch (op) { default: abort(); break; case OPS_B7: mask = 0xfc07; break; case OPS_BO: case OPS_RDH_K8: mask = 0xf000; break; case OPS_DR: mask = 0xff0f; break; case OPS_IO5_B0: case OPS_RDH_RRH: case OPS_RPD4_RPR0: case OPS_RPD8_K6: mask = 0xff00; break; case OPS_IO6_RD5: case OPS_RD5_IO6: mask = 0xf800; break; case OPS_KPLUS: mask = 0xfe0e; break; case OPS_KPLUS_RD5: case OPS_MINUSX_RD5: case OPS_RD5: case OPS_RD5_KPLUS: case OPS_RD5_MINUSX: case OPS_RD5_X: case OPS_RD5_XPLUS: case OPS_RD5_Z: case OPS_RD5_ZPLUS: case OPS_XPLUS_RD5: case OPS_X_RD5: case OPS_Z_RD5: mask = 0xfe0f; break; case OPS_MINUSYZ_RD5: case OPS_RD5_MINUSYZ: case OPS_RD5_YZPLUS: case OPS_YZPLUS_RD5: mask = 0xfe07; break; case OPS_NONE: case OPS_NONE_ZPLUS: mask = 0xffff; break; case OPS_RD5_B0: mask = 0xfe08; break; case OPS_RD5_DUP: if (fixed & 0x03ff) abort(); return( ((opc&0xfc00) == fixed) && !(((opc>>4)^opc) & 0xf) && ((9 >> ((opc>>8) & 3)) & 1) ); break; case OPS_RD5_RR5: mask = 0xfc00; break; case OPS_RD5_YZ_Q: case OPS_YZ_Q_RD5: mask = 0xd200; break; case OPS_RDQ_RRQ: mask = 0xff88; break; } if (fixed & ~mask) abort(); return((opc&mask)==fixed); } static void print_operands(void (*fn)(char), INSTROP pattern, unsigned short int opc, ADDR addr, unsigned short int (*second)(void)) { unsigned int q; unsigned int a; unsigned int b; switch (pattern) { case OPS_B7: print_symbol_or_hex(fn,addr+(2*opc_B7(opc))); break; case OPS_BO: print_symbol_or_hex(fn,addr+(2*opc_BO(opc))); break; case OPS_DR: fnprintf(fn,"%d",opc_DR(opc)); break; case OPS_IO5_B0: fnprintf(fn,"%#x, %d",opc_i5(opc),opc_b0(opc)); break; case OPS_IO6_RD5: fnprintf(fn,"%#x, R%d",opc_i6(opc),opc_d5(opc)); break; case OPS_KPLUS: if (second) { print_symbol_or_hex(fn,(opc_Kplus1(opc)<<16)|(*second)()); } else { fnprintf(fn,"?"); } break; case OPS_KPLUS_RD5: if (second) { print_symbol_or_hex(fn,(*second)()); } else { fnprintf(fn,"?"); } fnprintf(fn,", R%d",opc_d5(opc)); break; case OPS_MINUSX_RD5: fnprintf(fn,"-X, R%d",opc_d5(opc)); break; case OPS_MINUSYZ_RD5: fnprintf(fn,"-%c, R%d",(opc&0x0008)?'Y':'Z',opc_d5(opc)); break; case OPS_NONE: break; case OPS_NONE_ZPLUS: fnprintf(fn,"Z+"); break; case OPS_OP: fnprintf(fn,"<%04x>",opc); break; case OPS_RD5: case OPS_RD5_DUP: fnprintf(fn,"R%d",opc_d5(opc)); break; case OPS_RD5_B0: fnprintf(fn,"R%d, %d",opc_d5(opc),opc_b0(opc)); break; case OPS_RD5_IO6: fnprintf(fn,"R%d, %#x",opc_d5(opc),opc_i6(opc)); break; case OPS_RD5_KPLUS: fnprintf(fn,"R%d, ",opc_d5(opc)); if (second) { print_symbol_or_hex(fn,(*second)()); } else { fnprintf(fn,"?"); } break; case OPS_RD5_MINUSX: fnprintf(fn,"R%d, -X",opc_d5(opc)); break; case OPS_RD5_MINUSYZ: fnprintf(fn,"R%d, -%c",opc_d5(opc),(opc&0x0008)?'Y':'Z'); break; case OPS_RD5_RR5: fnprintf(fn,"R%d, R%d",opc_d5(opc),opc_r5(opc)); break; case OPS_RD5_X: fnprintf(fn,"R%d, X",opc_d5(opc)); break; case OPS_RD5_XPLUS: fnprintf(fn,"R%d, X+",opc_d5(opc)); break; case OPS_RD5_YZPLUS: fnprintf(fn,"R%d, %c+",opc_d5(opc),(opc&0x0008)?'Y':'Z'); break; case OPS_RD5_YZ_Q: q = opc_q6(opc); if (q) { fnprintf(fn,"R%d, %c+%#x",opc_d5(opc),(opc&0x0008)?'Y':'Z',q); } else { fnprintf(fn,"R%d, %c",opc_d5(opc),(opc&0x0008)?'Y':'Z'); } break; case OPS_RD5_Z: fnprintf(fn,"R%d, Z",opc_d5(opc)); break; case OPS_RD5_ZPLUS: fnprintf(fn,"R%d, Z+",opc_d5(opc)); break; case OPS_RDH_K8: fnprintf(fn,"R%d, %#x",opc_dh(opc),opc_k8(opc)); break; case OPS_RDH_RRH: fnprintf(fn,"R%d, R%d",opc_dh(opc),opc_rh(opc)); break; case OPS_RDQ_RRQ: fnprintf(fn,"R%d, R%d",opc_dq(opc),opc_rq(opc)); break; case OPS_RPD4_RPR0: a = opc_dp(opc); b = opc_rp(opc); fnprintf(fn,"R%d:R%d, R%d:R%d",a+1,a,b+1,b); break; case OPS_RPD8_K6: a = opc_d8(opc); fnprintf(fn,"R%d:R%d, %#x",a+1,a,opc_k6(opc)); break; case OPS_XPLUS_RD5: fnprintf(fn,"X+, R%d",opc_d5(opc)); break; case OPS_X_RD5: fnprintf(fn,"X, R%d",opc_d5(opc)); break; case OPS_YZPLUS_RD5: fnprintf(fn,"%c+, R%d",(opc&0x0008)?'Y':'Z',opc_d5(opc)); break; case OPS_YZ_Q_RD5: q = opc_q6(opc); if (q) { fnprintf(fn,"%c+%#x, R%d",(opc&0x0008)?'Y':'Z',q,opc_d5(opc)); } else { fnprintf(fn,"%c, R%d",(opc&0x0008)?'Y':'Z',opc_d5(opc)); } break; case OPS_Z_RD5: fnprintf(fn,"Z, R%d",opc_d5(opc)); break; } } static void print_decoded_instr_err(FILE *to, INSTR *i, unsigned short int opc) { char obuf[80]; int x; void ch(char c) { if (x < sizeof(obuf)-1) obuf[x++] = c; } x = 0; fnprintf(&ch,"%-7s ",i->name); print_operands(&ch,i->ops,opc,0,0); while ((x > 0) && (obuf[x-1] == ' ')) x --; fprintf(to,"%*s",x,&obuf[0]); } static void avr_init(void) { int i; int j; int k; int p; for (i=65535;i>=0;i--) { k = -1; for (j=NINSTRS-1;j>=0;j--) { if (opmatch(Instrs[j].ops,i,Instrs[j].fixedbits)) { if ((k >= 0) && (Instrs[j].prio == Instrs[k].prio)) { fprintf(stderr,"%s: instructions collide for %04x\n",__progname,i); fprintf(stderr,"\t"); print_decoded_instr_err(stderr,&Instrs[j],i); fprintf(stderr,"\n\t"); print_decoded_instr_err(stderr,&Instrs[k],i); fprintf(stderr,"\n"); exit(1); } if ((k < 0) || (Instrs[j].prio > Instrs[k].prio)) { k = j; p = Instrs[j].prio; continue; } } } instmap[i] = (k < 0) ? &badinst : &Instrs[k]; } } static int avr_cmd(char ch __attribute__((__unused__))) { return(0); } static int do_dis_inst(ADDR addr, void (*fn)(char)) { ADDR orig; unsigned short int opc; INSTR *im; unsigned short int getsecond(void) { addr += 2; return(corefetchu(addr-2,2)); } addr -= corebase; if (addr+2 > coresize) return(-1); orig = addr; opc = corefetchu(addr,2); addr += 2; im = instmap[opc]; fnprintf(fn,"%-7s ",im->name); print_operands(fn,im->ops,opc,addr+corebase,(addr+4>coresize)?0:&getsecond); return(addr-orig); } static int avr_dis(ADDR addr, void (*fn)(char)) { int n; int i; if ((addr-corebase) & 1) { fnprintf(fn," %02x",(unsigned int)corefetchu(addr,1)); return(1); } else if (ISINST(flags[addr-corebase])) { n = do_dis_inst(addr,fn); for (i=n-1;i>0;i--) flags[addr+i-corebase] = CF_PREV; return(n); } else { fnprintf(fn,"",(unsigned long int)flags[addr-corebase]); return(1); } } static unsigned long int avr_fetch(const void *base, int size) #define bp(n) ((unsigned long int)(((const unsigned char *)base)[(n)])) { switch (size) { case 1: return(bp(0)); break; case 2: return((bp(1)<<8)|bp(0)); break; case 4: return((bp(3)<<24)|(bp(2)<<16)|(bp(1)<<8)|bp(0)); break; } abort(); } #undef bp static const char *avr_names[] = { "AVR", "Avr", "avr", 0 }; static const COLONCMD avr_colons[] = { { 0 } }; MACHINE machine_avr = { &avr_names[0], 2, &avr_init, &avr_cmd, &avr_dis, &avr_fetch, &avr_colons[0] };