This is disasm.c in view mode; [Download] [Up]
/* disasm.c - 68000 instruction disassembler for NeXT * * Copyright (C) 1989 by Bill Spitzak * See Copyright notice in makefile * */ /* $Log: disasm.c,v $ * Revision 1.9 94/08/24 22:41:58 ediger * reformatted some static arrays of strings * * Revision 1.8 94/08/22 22:30:50 ediger * small formatting of disasmblock() arguments * * Revision 1.7 94/05/30 23:07:30 ediger * "readability" changes * * Revision 1.6 94/01/25 23:37:10 ediger * clean, reformatting, add comments removed non-symbolic "magic" constants * * Revision 1.5 94/01/19 00:12:02 ediger * bug fixes, reformatting * * Revision 1.4 94/01/16 21:10:57 ediger * fixed bug in parameters sent to disasmblock() * * Revision 1.3 94/01/16 17:23:05 ediger * got rid of phony struct relocation_info definition * * Revision 1.2 94/01/16 17:17:44 ediger * ditched phony nlist struct def * * Revision 1.1 94/01/16 16:48:13 ediger * Initial revision * */ static char rcsident[] = "$Id: disasm.c,v 1.9 94/08/24 22:41:58 ediger Exp Locker: ediger $"; #include "dis.h" #include "disasm.h" /* Semi-table-lookup. An exact match must be found in this table, an exact match means (inst>=code && (inst&(~mask))==code). This exact match is found by finding the largest entry <= inst, then searching backwards. This means you can special-case sub-instructions that match some more general instruction. If the special instruction has some 1 bits set in the general instruction's masked-off area, it can be put after it. If the masked-off bits are all zero, it must be placed before the general instruction, and the general instruction modified to have some 1 bits in it's masked-off area so it represents the lowest number that is a legal version of that instruction. If no match is found, it is an illegal instruction, which the disassembler prints as a word of data. [since this would normally require a backwards search of the whole table, it assummes the mask will never be larger than 0x0fff, so it can quit when the top 4 bits don't match]. You can also mark specific instructions as illegal by putting in an entry with no name. This can mark illegal addressing modes. When an entry is found, it prints the name. It then calls the "extension" routine, which can print more name (such as a w or a condition code or a coprocessor instruction). If there is a second routine a tab is printed and that is called, it is supposed to print the source (or only) argument. If there is a third routine a comma is printed and it is called, it is supposed to print the destination. All these routines are passed the original code, and they should advance the pc to after any data they use. */ extern int decimalrange; /* disasm switch, largest decimal value */ typedef void parsefunc(unsigned short); struct instruction { unsigned short code; unsigned short mask; /* mask is "inverted" so zero means full match */ char *symbol; /* opcode mnemonic */ parsefunc *extension; parsefunc *source; parsefunc *dest; }; /* global disasm state: */ static flag illegal; /* set true if instruction is illegal */ static int targettype; /* 0 or disasmtype of args to instruction */ static void *reloc; /* pointer to address 0 (not necessarily real) */ static void *pc; /* pointer into mapped area */ static void *endpc; /* never disasm something past here! */ static unsigned short inst2; /* second word of long instructions */ static char *cptr; /* pointer into buffer for unparsed inst */ static flag branched; /* set by an unconditional branch */ static struct nlist *sym; /* source symbol, if any, for comments */ #define cput(c) (*cptr++ = (c)) static void sput(char *c) { while (*c) *cptr++ = *c++; } static void putf(char *fmt, int x) { int l; sprintf(cptr, fmt, x); l = strlen(cptr); cptr += l; /* cptr += sprintf(cptr, fmt, x); */ } /* eventually this will print aliases for the registers: */ static void printregister(n) unsigned int n; { n &= 0xf; switch (n) { case 0xf: sput("sp"); break; case 0xe: sput("fp"); break; default: cput(n > 7 ? 'a' : 'd'); cput((n & 0x7) + '0'); break; } } /* symbol extension routines: */ void branch(unsigned short inst) { targettype = CODE; branched = TRUE; } void sizebyte(unsigned short inst) { targettype = CHAR; cput('b'); } void sizeword(unsigned short inst) { targettype = SHORT; cput('w'); } void sizelong(unsigned short inst) { targettype = LONG; cput('l'); } void size(unsigned short inst) { switch ((inst >> 6) & 0x3) { case 0: sizebyte(inst); break; case 1: sizeword(inst); break; case 2: sizelong(inst); break; case 3: illegal = TRUE; break; } } void brsizew(unsigned short inst) { branch(inst); sizeword(inst); } void condition(unsigned short inst) { static char *table[16] = { "t", "f", "hi", "ls", "cc", "cs", "ne", "eq", "vc", "vs", "pl", "mi", "ge", "lt", "gt", "le"}; sput(table[(inst >> 8) & 0xf]); } /* get rest of large inst */ void i2(unsigned short inst) { inst2 = *((signed short *)pc)++; } /* get mask and size of movem inst */ void mmtype(unsigned short inst) { inst2 = *((signed short *)pc)++; inst & 0x40 ? sizelong(inst) : sizeword(inst); } /* add a s or u to the long multiply/divide instructions */ void multype(unsigned short inst) { inst2 = *((signed short *)pc)++; cput(inst2 & 0x800 ? 's' : 'u'); sizelong(inst); } /* argument printing routines: */ void putlabel(struct nlist * sym) { putf(sym->n_un.n_name != NULL ? sym->n_un.n_name : "D%x", sym->n_value); } void printlabel(struct nlist * sym) { fprint(sym->n_un.n_name != NULL ? sym->n_un.n_name : "D%x", sym->n_value); } void putoffset(signed long i, int sign) { if (sign && i > 0) cput('+'); if (i > decimalrange) putf("0x%x", i); else if (i < -decimalrange) putf("-0x%x", -i); else if (!sign || i) putf("%d", i); } void printoffset(signed long i, int sign) { if (sign && i > 0) cprint('+'); if (i > decimalrange || (!sign && i < -decimalrange)) fprint("0x%x", i); else if (i < -decimalrange) fprint("-0x%x", -i); else if (!sign || i) fprint("%d", i); } void putasciichar(int c) { switch (c) { case '\t': sput("\\t"); break; case '\n': sput("\\n"); break; case '\r': sput("\\r"); break; case '\\': sput("\\\\"); break; case '\"': sput("\\\""); break; case '\'': sput("\\\'"); break; default: if (c >= ' ') cput(c); else putf("\\0%o", c); break; } } /* most recent symbol, for case tables */ static struct nlist *lastrefsymbol = NULL; void immediate(unsigned short inst) { signed long i; void *rpc; rpc = pc; cput('#'); sym = 0; switch (targettype) { case LONG: i = *((long *)pc)++; sym = getlabel((long unsigned *)&i, rpc, FALSE); if (sym) putlabel(sym); break; case SHORT: i = *((signed short *)pc)++; break; case CHAR: i = (signed char)*((signed short *)pc)++; if (i >= ' ' && i < 127) { cput('\''); putasciichar(i); cput('\''); return; } break; case FLOAT: cptr += sprintf(cptr, "%g", *((float *)pc)++); return; case DOUBLE: cptr += sprintf(cptr, "%g", *((double *)pc)++); return; case FLOAT12: sput("0x"); putf("%08x", *((long *)pc)++); putf("%08x", *((long *)pc)++); putf("%08x", *((long *)pc)++); return; default: illegal = TRUE; return; } putoffset(i, sym != 0); } /* parse an effective address into the following syntax: * The name of a register, or * register@(register.s*scale+base){@(register.s*scale+offset)} or * (label+register.s*scale+base){@(register.s*scale+offset)} or * label{@(register.s*scale+offset)} */ void source(unsigned short inst) { short unsigned i = 0; int baseregister = 0; /* 0 = no register, else low bits are reg number */ int indexregister = 0; long int base = 0; /* if no base register, figure out label from this! */ long int postoffset = 0; flag memindirect = 0; flag postindex = 0; void *rpc = 0; /* pointer to potential relocation */ flag pcrel = 0; /* true if address is naturally relocated */ flag lparen; switch ((inst >> 3) & 7) { case 1: if (targettype == UCHAR) illegal = TRUE; case 0: printregister(inst & 15); return; case 2: printregister((inst & 7) + 8); cput('@'); return; case 3: printregister(inst & 15); cput('@'); cput('+'); return; case 4: cput('-'); printregister((inst & 7) + 8); cput('@'); return; case 5: baseregister = (inst & 0xf); rpc = pc; base = *((signed short *)pc)++; break; case 6: baseregister = (inst & 0x7) | 8; /* a0-a7 */ indirect: i = *((short unsigned *)pc)++; indexregister = ((i >> 12) & 15) | 16; if (!(i & 0x100)) { rpc = pc - 1; base += (signed char)i; } else { if (i & 0x80) { baseregister = 0; base = 0; pcrel = 0; } if (i & 0x40) indexregister = 0; switch (i & 0x30) { case 0x00: illegal = TRUE; case 0x10: /* base+=0; */ break; case 0x20: rpc = pc; base += *((signed short *)pc)++; break; case 0x30: rpc = pc; base += *((long *)pc)++; break; } if (i & 4) postindex = TRUE; memindirect = TRUE; switch (i & 3) { case 0: memindirect = FALSE; break; case 1: postoffset = 0; break; /* should check for relocation, but we don't: */ case 2: postoffset = *((signed short *)pc)++; break; case 3: postoffset = *((long *)pc)++; break; } } break; case 7: switch (inst & 7) { case 0: rpc = pc; base = *((signed short *)pc)++; break; case 1: rpc = pc; base = *((long *)pc)++; break; case 2: pcrel = TRUE; rpc = pc; base = (rpc - reloc) + *((signed short *)pc)++; break; case 3: pcrel = TRUE; base = (pc - reloc); baseregister = 0; goto indirect; case 4: return (immediate(inst)); default: illegal = TRUE; return; } break; } lparen = 0; if (baseregister || memindirect) { if (baseregister) { printregister(baseregister); cput('@'); } cput('('); lparen = TRUE; sym = 0; } else sym = rpc ? getlabel((long unsigned *)&base, rpc, pcrel) : 0; if (sym) { settype(sym, targettype, 0); putlabel(sym); } PMI: if (indexregister && !postindex) { if (sym) cput('+'); printregister(indexregister); cput('.'); cput((i & 0x800) ? 'l' : 'w'); switch (i & 0x600) { case 0x0000: break; case 0x0200: sput("*2"); break; case 0x0400: sput("*4"); break; case 0x0600: sput("*8"); break; } } putoffset(base, sym || (indexregister && !postindex)); if (lparen) cput(')'); if (memindirect) { postindex = !postindex; base = postoffset; memindirect = FALSE; sym = 0; cput('@'); cput('('); lparen = TRUE; goto PMI; } } void data(unsigned short inst) { /* a0-a7 illegal */ if ((inst & 0x38) == 8) illegal = TRUE; source(inst); } void memory(unsigned short inst) { /* a0-a7, d0-d7 illegal */ if ((inst & 0x38) < 0x10) illegal = TRUE; source(inst); } void control(unsigned short inst) { switch ((inst >> 3) & 7) { /* registers, incr/decr, immediate illegal */ case 0: case 1: case 3: case 4: illegal = TRUE; break; case 7: if ((inst & 7) == 4) illegal = TRUE; break; } source(inst); } void alterable(unsigned short inst) { /* pc relative and imm illegal */ if ((inst & 0x3f) > 0x39) illegal = TRUE; source(inst); } void dest(unsigned short inst) { /* data && alterable */ if ((inst & 0x3f) > 0x39) illegal = TRUE; if ((inst & 0x38) == 8) illegal = TRUE; source(inst); } void destmem(unsigned short inst) { /* memory && alterable */ if ((inst & 0x38) < 0x10) illegal = TRUE; if ((inst & 0x38) > 0x39) illegal = TRUE; source(inst); } void bitfield(void) { cput('{'); if (inst2 & 0x800) printregister((inst2 >> 6) & 7); else putf("%d", (inst2 >> 6) & 31); cput(':'); if (inst2 & 0x20) printregister(inst2 & 7); else putf("%d", (inst2 & 31)); cput('}'); } void bfdata(unsigned short inst) { data(inst); bitfield(); } void bfdest(unsigned short inst) { dest(inst); bitfield(); } void bfreg(unsigned short inst) { printregister((inst2 >> 12)); } /* 8,16,32 bit code displacement, as used by bra instruction */ void disp(unsigned short inst) { long n, disp; signed char c; short unsigned *rpc; struct nlist *sym; /* local, because we don't want label comment */ n = pc - reloc; /* where program counter really would be at */ c = (inst & 0xff); rpc = pc; /* * calculate displacement, and advance program counter past displacement. * what kind of displacement is encoded in low-byte of instruction. */ switch (c) { case 0xff: /* 32 bit displacement in the next 4 bytes */ disp = *((long unsigned *)pc)++; break; case 0: /* 16 bit displacement in the next 2 bytes */ disp = *((signed short *)pc)++; break; default: /* 8 bit displacement right in the instruction */ disp = c; rpc = (unsigned short *)pc - 1; /* I guess pc doesn't need to be advanced? */ break; } n += disp; sym = getlabel((long unsigned *)&n, rpc, TRUE); settype(sym, CODE, (inst & 0xFF00) == 0x6100 ? "F%x" : (disp > 0 ? "L%x" : "LP%x")); if (sym != NULL) putlabel(sym); putoffset(n, sym != NULL); } void dispw(unsigned short inst) { disp(0); } /* print 2nd word in hex for badly disassembled instructions */ void word2(unsigned short inst) { putf("#0x%x", *((unsigned short *)pc)++); } /* >>6 and swap 3-bit halves to get a dest ea in move instruction */ void dest6(unsigned short inst) { inst = ((inst >> 9) & 7) | ((inst >> 3) & 0x38); alterable(inst); } void sr(unsigned short inst) { sput("sr"); } void ccr(unsigned short inst) { sput("ccr"); } void usp(unsigned short inst) { sput("usp"); } void mask(unsigned short inst) { /* movem mask, also check pre-decrement in ea for order */ unsigned short mask; int i; flag divider = FALSE; mask = inst2; for (i = 0; i < 16; i++) { if (mask & 1) { if (divider) cput('/'); divider = TRUE; printregister(((inst & 0x38) == 0x20) ? 15 - i : i); } mask >>= 1; } } void dreg(unsigned short inst) { printregister(inst & 7); } void areg(unsigned short inst) { printregister((inst & 7) + 8); } void dreg9(unsigned short inst) { printregister((inst >> 9) & 7); } void areg9(unsigned short inst) { printregister(((inst >> 9) & 7) + 8); } void aregdisp(unsigned short inst) { printregister((inst & 7) + 8); putf("@(0x%x)", *((signed short *)pc)++); } void aregpd(unsigned short inst) { cput('-'); printregister((inst & 7) + 8); cput('@'); } void aregpi(unsigned short inst) { printregister((inst & 7) + 8); cput('@'); cput('+'); } void aregpd9(unsigned short inst) { cput('-'); printregister(((inst >> 9) & 7) + 8); cput('@'); } void aregpi9(unsigned short inst) { printregister(((inst >> 9) & 7) + 8); cput('@'); cput('+'); } void qbits(unsigned short inst) { /* number from the addq or subq instructions */ int i = (inst >> 9) & 7; if (!i) i = 8; putf("#%d", i); } void bits4(unsigned short inst) { putf("#%d", inst & 15); } /* trap #n */ void bits3(unsigned short inst) { putf("#%d", inst & 7); } /* bkpt #n */ void bits8(unsigned short inst) { /* moveq #n */ cput('#'); putoffset((signed char)inst, FALSE); } void mulregs(unsigned short inst) { /* print one or two register destination for * long mul/div */ if (inst2 & 0x400) { printregister(inst2 & 7); cput(':'); } printregister((inst2 >> 12) & 7); } void floatdisp(short unsigned inst); void fbranch(short unsigned inst); void floatccw(short unsigned inst); void floatccl(short unsigned inst); void floatcc(short unsigned inst); void floatop(short unsigned inst); void floatsrc(short unsigned inst); void floatdest(short unsigned inst); static struct instruction thetable[] = { {0x0000, 0x00ff, "or", size, immediate, dest}, {0x003c, 0x0000, "or", size, immediate, ccr}, {0x007c, 0x0000, "or", size, immediate, sr}, {0x00c0, 0x0037, "cmp/chk2", sizebyte, word2, control}, {0x0100, 0x0e3f, "btst", sizebyte, dreg9, data}, {0x0108, 0x0e07, "movep", sizeword, aregdisp, dreg9}, {0x0140, 0x0e3f, "bchg", sizebyte, dreg9, dest}, {0x0148, 0x0e07, "movep", sizelong, aregdisp, dreg9}, {0x0180, 0x0e3f, "bclr", sizebyte, dreg9, dest}, {0x0188, 0x0e07, "movep", sizeword, dreg9, aregdisp}, {0x01c0, 0x0e3f, "bset", sizebyte, dreg9, dest}, {0x01c8, 0x0e07, "movep", sizelong, dreg9, aregdisp}, {0x0200, 0x00ff, "and", size, immediate, dest}, {0x023c, 0x0000, "and", size, immediate, ccr}, {0x027c, 0x0000, "and", size, immediate, sr}, {0x02c0, 0x0037, "cmp/chk2", sizeword, word2, control}, {0x0400, 0x00ff, "sub", size, immediate, dest}, {0x04c0, 0x0037, "cmp/chk2", sizelong, word2, control}, {0x0600, 0x00ff, "add", size, immediate, dest}, {0x06c0, 0x0007, "rtm", branch, dreg, 0}, {0x06c8, 0x0007, "rtm", branch, areg, 0}, {0x06d0, 0x0037, "callm", 0, word2, control}, {0x0800, 0x003f, "btst", sizebyte, immediate, data}, {0x0840, 0x003f, "bchg", sizebyte, immediate, dest}, {0x0880, 0x003f, "bclr", sizebyte, immediate, dest}, {0x08C0, 0x003f, "bset", sizebyte, immediate, dest}, {0x0a00, 0x00ff, "eor", size, immediate, dest}, {0x0ac0, 0x003f, "cas", sizebyte, word2, destmem}, {0x0aff, 0x0000, "cas2", sizebyte, word2, word2}, {0x0c00, 0x00ff, "cmp", size, immediate, dest}, {0x0cc0, 0x003f, "cas", sizeword, word2, destmem}, {0x0cff, 0x0000, "cas2", sizeword, word2, word2}, {0x0e00, 0x00ff, "moves", size, word2, destmem}, {0x0ec0, 0x003f, "cas", sizelong, word2, destmem}, {0x0eff, 0x0000, "cas2", sizelong, word2, word2}, {0x1000, 0x0fff, "move", sizebyte, source, dest6}, {0x2000, 0x0fff, "move", sizelong, source, dest6}, /* {0x2f00,0x003f, "push", sizelong, source, 0}, */ {0x3000, 0x0fff, "move", sizeword, source, dest6}, {0x4000, 0x00ff, "negx", size, dest, 0}, {0x40c0, 0x003f, "move", sizeword, sr, dest}, {0x4100, 0x0e3f, "chk", sizelong, data, dreg9}, {0x4180, 0x0e3f, "chk", sizeword, data, dreg9}, {0x41c0, 0x0e3f, "lea", 0, control, areg9}, {0x4200, 0x00ff, "clr", size, dest, 0}, {0x42c0, 0x003f, "move", sizeword, ccr, dest}, {0x4400, 0x00ff, "neg", size, dest, 0}, {0x44c0, 0x003f, "move", sizeword, data, ccr}, {0x4600, 0x00ff, "not", size, dest, 0}, {0x46c0, 0x003f, "move", sizeword, data, sr}, {0x4800, 0x003f, "nbcd", 0 /* byte */ , dest, 0}, {0x4808, 0x0007, "link", sizelong, areg, immediate}, {0x4840, 0x0007, "swap", 0, dreg, 0}, {0x4848, 0x0007, "bkpt", 0, bits3, 0}, {0x4850, 0x003f, "pea", 0, control, 0}, {0x4880, 0x0007, "extw", 0, dreg, 0}, {0x4890, 0x007f, "movem", mmtype, mask, dest}, {0x48c0, 0x0007, "extl", 0, dreg, 0}, {0x49c0, 0x0007, "extbl", 0, dreg, 0}, {0x4a00, 0x00ff, "tst", size, source, 0}, {0x4ac0, 0x003f, "tas", sizebyte, dest, 0}, /* "illegal" is 0x4afc, 0x4afa and 0x4afb also specified as illegal */ {0x4c00, 0x007f, "mul", multype, data, mulregs}, {0x4c40, 0x007f, "div", multype, data, mulregs}, {0x4c80, 0x007f, "movem", mmtype, memory, mask}, {0x4e40, 0x000f, "trap", 0, bits4, 0}, {0x4e50, 0x0007, "link", sizeword, areg, immediate}, {0x4e58, 0x0007, "unlk", 0, areg, 0}, {0x4e60, 0x0007, "movel", 0, areg, usp}, {0x4e68, 0x0007, "movel", 0, usp, areg}, {0x4e70, 0x0000, "reset", 0, 0, 0}, {0x4e71, 0x0000, "nop", 0, 0, 0}, {0x4e72, 0x0000, "stop", 0, word2, 0}, {0x4e73, 0x0000, "rte", branch, 0, 0}, {0x4e74, 0x0000, "rtd", brsizew, immediate, 0}, {0x4e75, 0x0000, "rts", branch, 0, 0}, {0x4e76, 0x0000, "trapv", 0, 0, 0}, {0x4e77, 0x0000, "rtr", branch, 0, 0}, {0x4e7a, 0x0001, "movec", 0, word2, 0}, {0x4e80, 0x003f, "jsr", 0, control, 0}, {0x4ec0, 0x003f, "jmp", branch, control, 0}, {0x5000, 0x0eff, "addq", size, qbits, alterable}, {0x50c0, 0x0f3f, "s", condition, dest, 0}, {0x50c8, 0x0f07, "db", condition, dreg, dispw}, {0x50fa, 0x0f00, "trap", condition, 0, 0}, {0x50fb, 0x0f00, "trap", condition, word2, 0}, {0x50fc, 0x0f00, "trap", condition, word2, word2}, /* long arg */ {0x5100, 0x0eff, "subq", size, qbits, alterable}, {0x51c0, 0x0f3f, "s", condition, dest, 0}, {0x51c8, 0x0f07, "db", condition, dreg, dispw}, {0x6000, 0x00ff, "bra", branch, disp, 0}, {0x6100, 0x00ff, "bsr", 0, disp, 0}, {0x6200, 0x0fff, "b", condition, disp, 0}, {0x7000, 0x0fff, "moveq", 0, bits8, dreg9}, {0x8000, 0x0eff, "or", size, data, dreg9}, {0x80c0, 0x0e3f, "divu", sizeword, data, dreg9}, {0x8100, 0x0e07, "sbcd", 0, dreg, dreg9}, {0x8108, 0x0e07, "sbcd", 0, aregpd, aregpd9}, {0x8110, 0x0eff, "or", size, dreg9, destmem}, {0x8140, 0x0e07, "pack", word2, dreg, dreg9}, {0x8148, 0x0e07, "pack", word2, aregpd, aregpd9}, {0x8180, 0x0e07, "unpk", 0, dreg, dreg9}, {0x8188, 0x0e07, "unpk", 0, aregpd, aregpd9}, {0x81c0, 0x0e3f, "divs", sizeword, data, dreg9}, {0x9000, 0x0eff, "sub", size, source, dreg9}, {0x90c0, 0x0e3f, "sub", sizeword, source, areg9}, {0x9100, 0x0ec7, "subx", size, dreg, dreg9}, {0x9108, 0x0ec7, "subx", size, aregpd, aregpd9}, {0x9110, 0x0eff, "sub", size, dreg9, destmem}, {0x91c0, 0x0e3f, "sub", sizelong, source, areg9}, {0xb000, 0x0eff, "cmp", size, source, dreg9}, {0xb0c0, 0x0e3f, "cmp", sizeword, source, areg9}, {0xb100, 0x0eff, "eor", size, dreg9, dest}, {0xb108, 0x0ec7, "cmp", size, aregpi, aregpi9}, {0xb1c0, 0x0e3f, "cmp", sizelong, source, areg9}, {0xc000, 0x0eff, "and", size, data, dreg9}, {0xc0c0, 0x0e3f, "mulu", sizeword, data, dreg9}, {0xc100, 0x0e07, "abcd", 0, dreg, dreg9}, {0xc108, 0x0e07, "abcd", 0, aregpd, aregpd9}, {0xc110, 0x0eff, "and", size, dreg9, destmem}, {0xc140, 0x0e07, "exg", 0, dreg, dreg9}, {0xc148, 0x0e07, "exg", 0, areg, areg9}, {0xc188, 0x0e07, "exg", 0, areg, dreg9}, {0xc1c0, 0x0e3f, "muls", sizeword, data, dreg9}, {0xd000, 0x0eff, "add", size, source, dreg9}, {0xd0c0, 0x0e3f, "add", sizeword, source, areg9}, {0xd100, 0x0ec7, "addx", size, dreg, dreg9}, {0xd108, 0x0ec7, "addx", size, aregpd, aregpd9}, {0xd110, 0x0eff, "add", size, dreg9, destmem}, {0xd1c0, 0x0e3f, "add", sizelong, source, areg9}, {0xe000, 0x0ec7, "asr", size, qbits, dreg}, {0xe008, 0x0ec7, "lsr", size, qbits, dreg}, {0xe010, 0x0ec7, "roxr", size, qbits, dreg}, {0xe018, 0x0ec7, "ror", size, qbits, dreg}, {0xe020, 0x0ec7, "asr", size, dreg9, dreg}, {0xe028, 0x0ec7, "lsr", size, dreg9, dreg}, {0xe030, 0x0ec7, "roxr", size, dreg9, dreg}, {0xe038, 0x0ec7, "ror", size, dreg9, dreg}, {0xe0c0, 0x003f, "asr", sizeword, destmem, 0}, {0xe100, 0x0ec7, "asl", size, qbits, dreg}, {0xe108, 0x0ec7, "lsl", size, qbits, dreg}, {0xe110, 0x0ec7, "roxl", size, qbits, dreg}, {0xe118, 0x0ec7, "rol", size, qbits, dreg}, {0xe120, 0x0ec7, "asl", size, dreg9, dreg}, {0xe128, 0x0ec7, "lsl", size, dreg9, dreg}, {0xe130, 0x0ec7, "roxl", size, dreg9, dreg}, {0xe138, 0x0ec7, "rol", size, dreg9, dreg}, {0xe1c0, 0x003f, "asl", sizeword, destmem, 0}, {0xe2c0, 0x003f, "lsr", sizeword, destmem, 0}, {0xe3c0, 0x003f, "lsl", sizeword, destmem, 0}, {0xe4c0, 0x003f, "roxr", sizeword, destmem, 0}, {0xe5c0, 0x003f, "roxl", sizeword, destmem, 0}, {0xe6c0, 0x003f, "ror", sizeword, destmem, 0}, {0xe7c0, 0x003f, "rol", sizeword, destmem, 0}, {0xe8c0, 0x003f, "bftst", i2, bfdata, 0}, {0xe9c0, 0x003f, "bfextu", i2, bfdata, bfreg}, {0xeac0, 0x003f, "bfchg", i2, bfdest, 0}, {0xebc0, 0x003f, "bfexts", i2, bfdata, bfreg}, {0xecc0, 0x003f, "bfclr", i2, bfdest, 0}, {0xedc0, 0x003f, "bfffo", i2, bfdata, bfreg}, {0xeec0, 0x003f, "bfset", i2, bfdest, 0}, {0xefc0, 0x003f, "bfins", i2, bfreg, bfdest}, {0xf200, 0x003f, "f", floatop, floatsrc, floatdest}, {0xf240, 0x003f, "fs", floatcc, dest, 0}, {0xf248, 0x0007, "fdb", floatcc, dreg, dispw}, {0xf27a, 0x0000, "ftrap", floatccw, immediate, 0}, {0xf27b, 0x0000, "ftrap", floatccl, immediate, 0}, {0xf27c, 0x0000, "ftrap", floatcc, 0, 0}, {0xf280, 0x007f, "fb", fbranch, floatdisp, 0}, {0xf300, 0x003f, "fsave", 0, destmem, 0}, {0xf340, 0x003f, "frestore", 0, source, 0} }; /*----- floating point internals -------*/ static parsefunc *fsrc, *fdest; void floatsrc(short unsigned inst) { (*fsrc) (inst); } void floatdest(short unsigned inst) { (*fdest) (inst); } void nodest(short unsigned inst) { cptr--; } /* delete the comma */ void fp7(short unsigned inst) { sput("fp"); cput('0' + ((inst2 >> 7) & 7)); } void fp10(short unsigned inst) { sput("fp"); cput('0' + ((inst2 >> 10) & 7)); } void fextension(short unsigned inst) { putf("#0x%x", inst2 & 0x7f); } void fpcr(short unsigned inst) { flag divider = FALSE; if (inst2 & 0x1000) { sput("fpcr"); divider = TRUE; } if (inst2 & 0x0800) { if (divider) cput('/'); sput("fpsr"); divider = TRUE; } if (inst2 & 0x0400) { if (divider) cput('/'); sput("fpiar"); } } void fmask(short unsigned inst) { /* movem mask, also check pre-decrement in ea for order */ unsigned short mask; int i; flag divider = FALSE; mask = inst2; for (i = 0; i < 8; i++) { if (mask & 1) { if (divider) cput('/'); divider = TRUE; sput("fp"); cput('0' + (((inst & 0x38) == 0x20) ? 7 - i : i)); } mask >>= 1; } } static char *floatops[0x3B] = { "move", "int", "sinh", "intrz", "sqrt", 0, "lognp1", 0, "etoxm1", "tanh", "atan", 0, "asin", "atanh", "sin", "tan", "etox", "twotox", "tentox", 0, "logn", "log10", "log2", 0, "abs", "cosh", "neg", 0, "acos", "cos", "getexp", "getman", "div", "mod", "add", "mul", "sgldiv", "rem", "scale", "sglmul", "sub", 0, 0, 0, 0, 0, 0, 0, "sincos", "sincos", "sincos", "sincos", "sincos", "sincos", "sincos", "sincos", "cmp", 0, "tst" }; void floatop(short unsigned inst) { int i; inst2 = *((unsigned short *)pc)++; switch ((inst2 >> 13) & 7) { case 0: i = inst2 & 0x7f; if (i > 0x3A || !floatops[i]) { illegal = TRUE; return; } sput(floatops[i]); fsrc = fp10; fdest = fp7; break; case 2: fdest = fp7; if ((inst2 & 0x1c00) == 0x1c00) { sput("movecr"); fsrc = fextension; break; } fsrc = source; i = inst2 & 0x7f; if (i > 0x3A || !floatops[i]) { illegal = TRUE; return; } sput(floatops[i]); SIZEIT: switch ((inst2 >> 10) & 7) { case 0: sizelong(inst); break; case 1: cput('s'); targettype = FLOAT; break; case 2: cput('x'); targettype = FLOAT12; break; case 3: cput('p'); targettype = FLOAT12; break; case 4: sizeword(inst); break; case 5: cput('d'); targettype = DOUBLE; break; case 6: sizebyte(inst); break; } break; case 3: sput("move"); fsrc = fp7; fdest = dest; goto SIZEIT; case 4: sput("movem"); fsrc = source; fdest = fpcr; break; case 5: sput("movem"); fsrc = fpcr; fdest = dest; break; case 6: sput("movem"); fsrc = memory; fdest = fmask; break; case 7: sput("movem"); fsrc = fmask; fdest = destmem; break; } } static char *fcond[32] = { "f", "eq", "ogt", "oge", "olt", "ole", "ogl", "or", "un", "ueq", "ugt", "uge", "ult", "ule", "ne", "t", "sf", "seq", "gt", "ge", "lt", "le", "gl", "gle", "ngle", "ngl", "nle", "nlt", "nge", "ngt", "sne", "st" }; void floatcc(short unsigned inst) { int i; i = (*((short unsigned *)pc)++) & 0x3f; if (i > 31) illegal = TRUE; else sput(fcond[i]); } void floatccw(short unsigned inst) { floatcc(inst); sizeword(inst); } void floatccl(short unsigned inst) { floatcc(inst); sizelong(inst); } void fbranch(short unsigned inst) { int i; i = inst & 0x3f; if (i > 31) illegal = TRUE; else sput(fcond[i]); } void floatdisp(short unsigned inst) { /* displacement is long or short depending on bit 6 */ disp((inst & 0x40) ? -1 : 0); } /*==================== The disassembler ======================*/ /* Multiple passes are done by setting newlabels to zero, disasmblock for each section of the program, and if newlabels is not zero, doing it again. If newlabels is zero, we are done, turn on printit and disasmblock again. Disasmblock cuts up the code at each symbol, and calls a routine for each of these symbol sections, depending on the type of symbol. pc points at the start, endpc points at the end of the section. The routine may quit earlier if it feels the type it is disassembling is not allowed (in particular it can not move pc at all) but be very careful in how guesstype is done so all possible states will eventually be disassembled, it is extremely easy to make a loop! */ int newlabels; /* incremented only by labels that require a pass */ extern flag printit; /* set true during printing pass */ flag incode; /* true for text segment to encourage code guessing */ static struct relocation_info *rtab; static struct relocation_info *lastr; static struct relocation_info *curr; static int cursection; static unsigned curstart; /* used to address relocation entries */ static flag samelineflag; /* true if label small enough to be on same line */ static unsigned nextsymat; /* set by created symbols */ static unsigned mapstart, mapend; static void tab() { /* tab over to the disassemble column */ if (samelineflag) samelineflag = FALSE; /* tab after label */ else startline(pc - reloc); cprint('\t'); } /* our own disassembly types: */ #define ZEROBLOCK -1 /* a set of zeros */ #define CASEWTABLE -2 /* a gcc case table, word offsets from start */ /* * Return pointer to next relocatable thing after pc. * or endpc if none. * The table is expected to be in high to low order, and is * linearlly searched backwards from the last entry found. * (the reason it is sorted backwards is that gcc produces * them that way, and it saves some time to not have to * reverse it) */ void * nextreloc() { void *ret; struct relocation_info *x; unsigned a = pc - reloc - curstart; for (x = curr; x >= rtab; x--) { if (x->r_address >= a) { ret = x->r_address + curstart + reloc; return (ret < endpc ? ret : endpc); } } return (endpc); } extern flag globalscode; int guesstype(void) { signed char *p; void *end = nextreloc(); int n = end - pc; if (!n) return (ULONG); /* get pointer at start of block */ for (p = (signed char *)pc; !*p++;) if ((void *)p >= end) return (ZEROBLOCK); /* see if it looks like code: */ if (incode && !(((unsigned)pc | (unsigned)end) & 1) && n > 2) { if (*(unsigned short *)pc == 0x4e56 && !(*((unsigned short *)pc + 1) & 3)) return (CODE); /* link a6,n*4 */ if (*(unsigned short *)pc == 0x204f) return (CODE); /* move sp,a0 */ } /* see if it looks like a string: */ p = (signed char *)pc; for (p = (signed char *)pc; p < (signed char *)end; p++) { if (*p >= ' ') { if ((void *)p - pc >= 8) return (STRTY); } else if (*p == '\n' || *p == '\r' || *p == '\t') ; else if (!*p && (void *)p > pc) return (STRTY); else break; } if ((unsigned)pc & 1 || n == 1) return (UCHAR); if (incode || n < 4) return (USHORT); return (ULONG); } void disasmcode(void) { void *savepc; unsigned short inst; int i, s, e; struct nlist *sourcesym; char buf[256]; if (((unsigned)pc & 1) || nextreloc() == pc) return; branched = FALSE; lastrefsymbol = 0; while (pc < endpc) { savepc = pc; inst = *((unsigned short *)pc)++; s = 0; e = sizeof(thetable) / sizeof(struct instruction); while (i = (s + e) / 2, i > s) { if (thetable[i].code > inst) e = i; else s = i; } while ((inst ^ thetable[i].code) & ~thetable[i].mask) { i--; if (i < 0 || (inst & 0xf000) != (thetable[i].code & 0xf000)) { pc = savepc; return; } } cptr = buf; sput(thetable[i].symbol); /* put opcode mnemonic in buffer */ illegal = FALSE; targettype = 0; sym = 0; if (thetable[i].extension) (thetable[i].extension)(inst); if (illegal || pc > endpc) { pc = savepc; break; } if (thetable[i].source) { cput('\t'); (thetable[i].source)(inst); } if (illegal || pc > endpc) { pc = savepc; break; } sourcesym = sym; if (thetable[i].dest) { cput(','); (thetable[i].dest)(inst); } if (illegal || pc > endpc) { pc = savepc; break; } *cptr = 0; /* null terminate the string concocted in the buffer */ if (printit) { if (samelineflag) samelineflag = FALSE; /* tab after label */ else startline(savepc - reloc); cprint('\t'); sprint(buf); printlabelcomment(sourcesym); } if (branched) { if (lastrefsymbol == NULL) { ; /* detect new case tables: */ } else if ((inst & 0xfff0) == 0x4ed0) { /* jmp aN@ */ settype(lastrefsymbol, PTR | CODE, 0); /* detect older Gnu case tables: */ } else if (inst == 0x4efb && lastrefsymbol->n_desc == SHORT) { /* jmp pc+x.w, assumme table at pc */ lastrefsymbol->n_desc = CASEWTABLE; } break; } } } /* this is not called unless we already know it is all zeros */ void disasmzeros(void) { if (printit && endpc > pc) { tab(); if (endpc == pc + 1 && (((unsigned)pc) & 1)) fprint(".even"); else fprint(".skip %d", endpc - pc); } pc = endpc; } /* find more than 1 trailing zero on block, return pointer to it or end */ void * findtail(int size, void *e) { void *p; if (e <= pc) return (pc); for (p = e - 1; !*(char *)p && p >= pc; p--) ; /* find last non-zero byte */ /* point at sized object after it */ p = pc + ((p + size - pc) / size) * size; if (p > e) return (p - size); if (p + size > e) return (p); if (p + size + size > e) return (p + size); return (p); } /* Characters that don't look like strings are strongly discouraged, * as I only print one and then let it guesstype again. Chiefly this * eats odd addresses. */ void disasmchar(int type) { if (findtail(1, nextreloc()) > pc) { tab(); fprint(".byte "); printoffset(*((signed char *)pc)++, FALSE); } } /* Shorts will continue until the next relocatable (assumed to be long) */ void disasmshort(int type) { void *e; if (((unsigned)pc) & 1) return; /* odd address is n.g */ e = nextreloc(); if (((unsigned)e) & 1) e--; if (printit) { e = findtail(2, e); while (pc < e) { tab(); fprint(".word "); printoffset(*((signed short *)pc)++, FALSE); } } pc = e; } /* Longs will continue until we hit non-multiple of 4: Pointers are very similar to longs, but checks are made to see if they are legitimate. In particular they should all relocate. Even addresses are also insisted on unless pointed to type is CHAR */ void disasmlong(int type) { void *e, *f; unsigned long i; struct nlist *sym; if (((unsigned)pc) & 1) return; /* odd address is n.g */ e = findtail(4, endpc); while (pc < e) { f = nextreloc(); if (f > pc && f < pc + 4) return; i = *((unsigned long *)pc); if ((i & 1) && (type & PTR) && (type & 15) != CHAR && (type & 15) != UCHAR) return; sym = getlabel(&i, pc, FALSE); if (type & PTR) { if (!sym) return; settype(sym, type & 15, "C%x"); } if (printit) { tab(); fprint(".long "); if (sym) printlabel(sym); printoffset(i, sym != 0); printlabelcomment(sym); } ((unsigned long *)pc)++; } } /* Floating numbers go until next relocatable. It would probably be a good idea to quit on NaN's */ void disasmfloat(void) { void *e; if (((unsigned)pc) & 1) return; /* odd address is n.g */ e = nextreloc(); e -= (e - pc) & 3; if (printit) { e = findtail(4, e); while (pc < e) { tab(); fprint(".float %g", *((float *)pc)++); } } pc = e; } void disasmdouble(void) { void *e; if (((unsigned)pc) & 1) return; /* odd address is n.g */ e = nextreloc(); e -= (e - pc) & 7; if (printit) { e = findtail(8, e); while (pc < e) { tab(); fprint(".double %g", *((double *)pc)++); } } pc = e; } /* strings print thru the next null or to a relocatable or symbol: * this routine prints one line, cutting at \n or 60 characters: */ int printascii(void *e) { signed char c; if (e - pc > 62) { e = pc + 60; if (*(signed char *)e == 0) e++; } cprint('\"'); while (pc < e) { c = *((signed char *)pc)++; switch (c) { case 0: sprint("\\0\""); return (0); case '\t': sprint("\\t"); break; case '\n': sprint("\\n"); if (!*(signed char *)pc) break; cprint('\"'); return (TRUE); case '\r': sprint("\\r"); break; case '\\': sprint("\\\\"); break; case '\"': sprint("\\\""); break; default: if (c >= ' ') cprint(c); else fprint("\\%03o", (int)c & 0xff); break; } } cprint('\"'); return (TRUE); } void disasmstring(void) { void *e; e = nextreloc(); if (printit) { while (pc < e && (tab(), sprint(".ascii "), printascii(e))) ; } else while (pc < endpc && *((signed char *)pc)++) ; } /* Gnu cc produces tables of forward offsets relative to the start of the table for case statements. This will print them. It detects the end of the table by either running into some labelled code, or by an odd address */ void disasmcasewtable(void) { long unsigned table, base; signed short *p; int i; struct nlist *tablesym, *sym; table = pc - reloc; tablesym = findlabel(table); if (!tablesym) return; /* no reference to table! */ i = 0; while (pc < endpc) { base = table + *((signed short *)pc); if ( /* base<pc-reloc || */ base & 1) return; for (p = (signed short *)pc + 1; (void *)p < endpc && table + *p == base; p++) ; sym = settype(getlabel(&base, pc, TRUE), CODE, "C%x"); if (printit) { tab(); sprint(".word "); printlabel(sym); cprint('-'); printlabel(tablesym); ((signed short *)pc)++; fprint("\t|case %d", i); i += (p - (signed short *)pc) + 1; if (pc < (void *)p) fprint("..%d", i - 1); while (pc < (void *)p) { tab(); sprint(".word "); printlabel(sym); cprint('-'); printlabel(tablesym); ((signed short *)pc)++; } } pc = (void *)p; } } void disasmblock( struct segment_command *p, struct section *q, int sectn, char *bpMap ) { struct nlist *n; int disasmtype; unsigned a; unsigned start, end; /* address to start at, address to end at */ extern unsigned symaddress; /* fill in some globals as appropriate for this section */ curstart = start = q->addr; /* address to start at */ end = q->addr + q->size; /* address to end at */ /* where what address 0 would be, is memory mapped in this addr space */ reloc = (void *)((unsigned)bpMap + p->fileoff - p->vmaddr); /* sorted array of relocation info for this section */ rtab = (struct relocation_info *)(bpMap + q->reloff); /* address of last relocation info array entry */ lastr = rtab + q->nreloc; /* current relocation info entry (sorted in rev order?) */ curr = lastr - 1; cursection = sectn; /* first address memory mapped ??? */ mapstart = p->vmaddr; /* last address memory mapped ??? */ mapend = q->offset ? p->vmaddr + p->vmsize : 0; if (!printit) { if (start < mapstart) start = mapstart; if (end > mapend) end = mapend; } n = firstsymbol(start); pc = reloc + start; while ((a = pc - reloc) < end) { if (nextsymat) n = firstsymbol(nextsymat); nextsymat = 0; disasmtype = 0; /* handles all the symbols with the same "address". */ while (n && symaddress == a) { if (printit) samelineflag = printsymbol(n); if (n->n_sect) { if (n->n_desc && !(n->n_type & N_STAB)) disasmtype = n->n_desc; /* not a debugging entry */ else if (n->n_type == N_SLINE) disasmtype = CODE; else if (incode && globalscode && n->n_type == (N_SECT | 1)) disasmtype = CODE; } n = nextsymbol(); } a = (n && symaddress < end) ? symaddress : end; if (pc - reloc < mapstart && a > mapstart) a = mapstart; if (pc - reloc < mapstart || pc - reloc >= mapend) { tab(); fprint(".skip %d", a - (pc - reloc)); pc = reloc + a; continue; } if (a > mapend) a = mapend; endpc = reloc + a; pushsymbol(); if (!disasmtype) disasmtype = guesstype(); switch (disasmtype) { case CASEWTABLE: disasmcasewtable(); break; case ZEROBLOCK: disasmzeros(); break; case CODE: disasmcode(); break; case CHAR: case UCHAR: disasmchar(disasmtype); break; case SHORT: case USHORT: disasmshort(disasmtype); break; case FLOAT: disasmfloat(); break; case DOUBLE: disasmdouble(); break; case STRTY: disasmstring(); break; default: disasmlong(disasmtype); break; } popsymbol(); } } /*==================== Label finding/creation ===================*/ /* If something interesting can be said about a label, print it as * a comment. This version will print any constant strings it points * at. */ void printlabelcomment(struct nlist * n) { void *savepc, *saveendpc; flag saveincode; if (!printit || !n || !n->n_sect || n->n_value < mapstart || n->n_value >= mapend) return; savepc = pc; saveendpc = endpc; saveincode = incode; pc = reloc + n->n_value; endpc = pc + 100; incode = TRUE; if (n->n_desc == STRTY || (!n->n_desc && guesstype() == STRTY)) { sprint("\t|"); printascii(endpc); } pc = savepc; endpc = saveendpc; incode = saveincode; } struct nlist * getlabel(unsigned long *address, void *pc, int pcrel) { struct nlist *n; struct relocation_info *r; unsigned a; extern struct nlist *sourcesymtab; extern long unsigned symaddress; /* first see if there is an entry in the relocation table: */ a = pc - reloc - curstart; for (r = 0; curr >= rtab; curr--) { if (curr->r_address == a) { if (pcrel ? curr->r_pcrel : !curr->r_pcrel) r = curr; break; } if (curr->r_address > a) break; } /* if relocated relative to an external symbol, use that: */ if (r && r->r_extern) { /* note that address is relative already */ return (lastrefsymbol = sourcesymtab + r->r_symbolnum); } /* * If there is any relocation table, anything that is not relocated * must be an absolute value: */ if (lastr != rtab) { if (!r && !pcrel) return (0); } else { /* Otherwise, we guess that values near zero are absolute: */ if (!pcrel && (*address <= 0x2000 || *address >= 0x100000 || (*address & 0xFFF) == 0xFFF || (*address & 0xFFF) == 0)) return (0); } /* otherwise it is a local label, find it: */ if (n = findlabel(*address)) { *address = 0; /* modify address to relative value */ return (lastrefsymbol = n); } else if (symaddress > *address - 4 && (n = findlabel(symaddress))) { /* fix use of bytes to test parts of long symbols: */ if (n->n_desc == ULONG || n->n_desc == LONG || (symaddress > *address - 2 && (n->n_desc == USHORT || n->n_desc == SHORT))) { *address -= symaddress; return (n); } } /* otherwise make a label. Give it the type needed: */ n = createlabel(*address, r ? r->r_symbolnum : cursection, 0, 0); if (*address <= pc - reloc) { newlabels++; /* increment for earlier code */ } else if (*address < endpc - reloc) { /* make it be the next symbol */ endpc = reloc + *address; nextsymat = *address; } *address = 0; return (lastrefsymbol = n); } struct nlist * settype(struct nlist *s, int type, char *name) { if (s != NULL) { if (s->n_desc == 0) s->n_desc = type; if (s->n_un.n_name == NULL) s->n_un.n_name = name; } return s; }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.