Subversion Repositories planix.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

#include <u.h>
#include <libc.h>
#include <bio.h>
#include <mach.h>

/*
 * Sparc64-specific debugger interface
 */

static  char    *sparc64excep(Map*, Rgetter);
static  int     sparc64foll(Map*, uvlong, Rgetter, uvlong*);
static  int     sparc64inst(Map*, uvlong, char, char*, int);
static  int     sparc64das(Map*, uvlong, char*, int);
static  int     sparc64instlen(Map*, uvlong);

Machdata sparc64mach =
{
        {0x91, 0xd0, 0x20, 0x01},       /* breakpoint: TA $1 */
        4,                      /* break point size */

        beswab,                 /* convert short to local byte order */
        beswal,                 /* convert long to local byte order */
        beswav,                 /* convert vlong to local byte order */
        risctrace,              /* C traceback */
        riscframe,              /* frame finder */
        sparc64excep,           /* print exception */
        0,                      /* breakpoint fixup */
        beieeesftos,            /* single precision float printer */
        beieeedftos,            /* double precision float printer */
        sparc64foll,            /* following addresses */
        sparc64inst,            /* print instruction */
        sparc64das,             /* dissembler */
        sparc64instlen,         /* instruction size */
};

static char *trapname[] =
{
        0,
        "power on reset",
        "watchdog reset",
        "external reset",
        "software reset",
        "RED",
        0, 0,
        "instruction access exception",
        "instruction access MMU miss",
        "instruction access error",
        0, 0, 0, 0, 0,
        "illegal instruction",
        "privileged opcode",
        "unimplemented LDD",
        "unimplemented STD",
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        "fp disabled",
        "fp exception ieee 754",
        "fp exception other",
        0, 0, 0, 0,
        "division by zero",
        "internal processor error",
        0, 0, 0, 0, 0, 0,
        "data access exception",
        "data access MMU miss",
        "data access error",
        "data access protection",
        "mem address not aligned",
        "LDDF mem address not aligned",
        "STDF mem address not aligned",
        "privileged action",
        "LDQF mem address nto aligned",
        "STQF mem address not aligned",
};

static char*
excname(ulong tt)
{
        static char buf[32];

        if(tt < sizeof trapname/sizeof(char*) && trapname[tt])
                return trapname[tt];
        if(tt >= 258)
                sprint(buf, "trap instruction %ld", tt-128);
        else if(65<=tt && tt<=79)
                sprint(buf, "interrupt level %ld", tt-64);
        else switch(tt){
        case 64:
                return "async data error";
        case 96:
                return "mondo interrupt";
        case 100:
                return "instruction access MMU miss";
        case 104:
                return "data access MMU miss";
        case 108:
                return "data access protection";
        case 256:
                return "syscall";
        case 257:
                return "breakpoint";
        default:
                sprint(buf, "unknown trap %ld", tt);
        }
        return buf;
}

static char*
sparc64excep(Map *map, Rgetter rget)
{
        long tt;

        tt = (*rget)(map, "TT");
        return excname(tt);
}

        /* Sparc disassembler and related functions */

struct opcode {
        char    *mnemonic;
        void    (*f)(struct instr*, char*);
        int     flag;
};

static  char FRAMENAME[] = ".frame";

typedef struct instr Instr;

struct instr {
        uchar   op;             /* bits 31-30 */
        uchar   rd;             /* bits 29-25 */
        uchar   op2;            /* bits 24-22 */
        uchar   a;              /* bit  29    */
        uchar   cond;           /* bits 28-25 */
        uchar   op3;            /* bits 24-19 */
        uchar   rs1;            /* bits 18-14 */
        uchar   i;              /* bit  13    */
        uchar   asi;            /* bits 12-05 */
        uchar   rs2;            /* bits 04-00 */
        short   simm13;         /* bits 12-00, signed */
        ushort  opf;            /* bits 13-05 */
        ulong   immdisp22;      /* bits 21-00 */
        ulong   simmdisp22;     /* bits 21-00, signed */
        ulong   disp30;         /* bits 30-00 */
        ulong   imm32;          /* SETHI+ADD constant */
        int     target;         /* SETHI+ADD dest reg */
        long    w0;
        long    w1;
        uvlong  addr;           /* pc of instruction */
        char    *curr;          /* current fill level in output buffer */
        char    *end;           /* end of buffer */
        int     size;           /* number of longs in instr */
        char    *err;           /* errmsg */
};

static  Map     *mymap;         /* disassembler context */
static  int     dascase;

static int      mkinstr(uvlong, Instr*);
static void     bra1(Instr*, char*, char*[]);
static void     bra(Instr*, char*);
static void     fbra(Instr*, char*);
static void     cbra(Instr*, char*);
static void     unimp(Instr*, char*);
static void     fpop(Instr*, char*);
static void     shift(Instr*, char*);
static void     sethi(Instr*, char*);
static void     load(Instr*, char*);
static void     loada(Instr*, char*);
static void     store(Instr*, char*);
static void     storea(Instr*, char*);
static void     add(Instr*, char*);
static void     cmp(Instr*, char*);
static void     wr(Instr*, char*);
static void     jmpl(Instr*, char*);
static void     rd(Instr*, char*);
static void     loadf(Instr*, char*);
static void     storef(Instr*, char*);
static void     loadc(Instr*, char*);
static void     loadcsr(Instr*, char*);
static void     trap(Instr*, char*);

static struct opcode sparc64op0[8] = {
        [0]     "UNIMP",        unimp,  0,      /* page 137 */
        [2]     "B",            bra,    0,      /* page 119 */
        [4]     "SETHI",        sethi,  0,      /* page 104 */
        [6]     "FB",           fbra,   0,      /* page 121 */
        [7]     "CB",           cbra,   0,      /* page 123 */
};

static struct opcode sparc64op2[64] = {
        [0x00]  "ADD",          add,    0,      /* page 108 */
        [0x10]  "ADDCC",        add,    0,
        [0x08]  "ADDX",         add,    0,
        [0x18]  "ADDXCC",       add,    0,

        [0x20]  "TADD",         add,    0,      /* page 109 */
        [0x22]  "TADDCCTV",     add,    0,

        [0x04]  "SUB",          add,    0,      /* page 110 */
        [0x14]  "SUBCC",        cmp,    0,
        [0x0C]  "SUBX",         add,    0,
        [0x1C]  "SUBXCC",       add,    0,

        [0x21]  "TSUB",         add,    0,      /* page 111 */
        [0x23]  "TSUBCCTV",     add,    0,

        [0x24]  "MULSCC",       add,    0,      /* page 112 */

        [0x0A]  "UMUL",         add,    0,      /* page 113 */
        [0x0B]  "SMUL",         add,    0,
        [0x1A]  "UMULCC",       add,    0,
        [0x1B]  "SMULCC",       add,    0,

        [0x0E]  "UDIV",         add,    0,      /* page 115 */
        [0x0F]  "SDIV",         add,    0,
        [0x1E]  "UDIVCC",       add,    0,
        [0x1F]  "SDIVCC",       add,    0,

        [0x01]  "AND",          add,    0,      /* page 106 */
        [0x11]  "ANDCC",        add,    0,
        [0x05]  "ANDN",         add,    0,
        [0x15]  "ANDNCC",       add,    0,
        [0x02]  "OR",           add,    0,
        [0x12]  "ORCC",         add,    0,
        [0x06]  "ORN",          add,    0,
        [0x16]  "ORNCC",        add,    0,
        [0x03]  "XOR",          add,    0,
        [0x13]  "XORCC",        add,    0,
        [0x07]  "XORN",         add,    0,
        [0x17]  "XORNCC",       add,    0,

        [0x25]  "SLL",          shift,  0,      /* page 107 */
        [0x26]  "SRL",          shift,  0,
        [0x27]  "SRA",          shift,  0,

        [0x3C]  "SAVE",         add,    0,      /* page 117 */
        [0x3D]  "RESTORE",      add,    0,

        [0x38]  "JMPL",         jmpl,   0,      /* page 126 */

        [0x39]  "RETT",         add,    0,      /* page 127 */

        [0x3A]  "T",            trap,   0,      /* page 129 */

        [0x28]  "rdy",          rd,     0,      /* page 131 */
        [0x29]  "rdpsr",        rd,     0,
        [0x2A]  "rdwim",        rd,     0,
        [0x2B]  "rdtbr",        rd,     0,

        [0x30]  "wry",          wr,     0,      /* page 133 */
        [0x31]  "wrpsr",        wr,     0,
        [0x32]  "wrwim",        wr,     0,
        [0x33]  "wrtbr",        wr,     0,

        [0x3B]  "flush",        add,    0,      /* page 138 */

        [0x34]  "FPOP",         fpop,   0,      /* page 140 */
        [0x35]  "FPOP",         fpop,   0,
};

static struct opcode sparc64op3[64]={
        [0x09]  "ldsb",         load,   0,      /* page 90 */
        [0x19]  "ldsba",        loada,  0,
        [0x0A]  "ldsh",         load,   0,
        [0x1A]  "ldsha",        loada,  0,
        [0x01]  "ldub",         load,   0,
        [0x11]  "lduba",        loada,  0,
        [0x02]  "lduh",         load,   0,
        [0x12]  "lduha",        loada,  0,
        [0x00]  "ld",           load,   0,
        [0x10]  "lda",          loada,  0,
        [0x03]  "ldd",          load,   0,
        [0x13]  "ldda",         loada,  0,

        [0x20]  "ldf",          loadf,  0,      /* page 92 */
        [0x23]  "lddf",         loadf,  0,
        [0x21]  "ldfsr",        loadf,0,

        [0x30]  "ldc",          loadc,  0,      /* page 94 */
        [0x33]  "lddc",         loadc,  0,
        [0x31]  "ldcsr",        loadcsr,0,

        [0x05]  "stb",          store,  0,      /* page 95 */
        [0x15]  "stba",         storea, 0,
        [0x06]  "sth",          store,  0,
        [0x16]  "stha",         storea, 0,
        [0x04]  "st",           store,  0,
        [0x14]  "sta",          storea, 0,
        [0x07]  "std",          store,  0,
        [0x17]  "stda",         storea, 0,

        [0x24]  "stf",          storef, 0,      /* page 97 */
        [0x27]  "stdf",         storef, 0,
        [0x25]  "stfsr",        storef,0,
        [0x26]  "stdfq",        storef,0,

        [0x34]  "stc",          loadc,  0,      /* page 99 */
        [0x37]  "stdc",         loadc,  0,
        [0x35]  "stcsr",        loadcsr,0,
        [0x36]  "stdcq",        loadcsr,0,

        [0x0D]  "ldstub",       store,  0,      /* page 101 */
        [0x1D]  "ldstuba",      storea, 0,

        [0x0F]  "swap",         load,   0,      /* page 102 */
        [0x1F]  "swapa",        loada,  0,
};

#pragma varargck        argpos  bprint  2
#pragma varargck        type    "T"     char*

/* convert to lower case from upper, according to dascase */
static int
Tfmt(Fmt *f)
{
        char buf[128];
        char *s, *t, *oa;

        oa = va_arg(f->args, char*);
        if(dascase){
                for(s=oa,t=buf; *t = *s; s++,t++)
                        if('A'<=*t && *t<='Z')
                                *t += 'a'-'A';
                return fmtstrcpy(f, buf);
        }
        return fmtstrcpy(f, oa);
}

static void
bprint(Instr *i, char *fmt, ...)
{
        va_list arg;

        va_start(arg, fmt);
        i->curr = vseprint(i->curr, i->end, fmt, arg);
        va_end(arg);
}

static int
decode(ulong pc, Instr *i)
{
        ulong w;

        if (get4(mymap, pc, &w) < 0) {
                werrstr("can't read instruction: %r");
                return -1;
        }
        i->op = (w >> 30) & 0x03;
        i->rd = (w >> 25) & 0x1F;
        i->op2 = (w >> 22) & 0x07;
        i->a = (w >> 29) & 0x01;
        i->cond = (w >> 25) & 0x0F;
        i->op3 = (w >> 19) & 0x3F;
        i->rs1 = (w >> 14) & 0x1F;
        i->i = (w >> 13) & 0x01;
        i->asi = (w >> 5) & 0xFF;
        i->rs2 = (w >> 0) & 0x1F;
        i->simm13 = (w >> 0) & 0x1FFF;
        if(i->simm13 & (1<<12))
                i->simm13 |= ~((1<<13)-1);
        i->opf = (w >> 5) & 0x1FF;
        i->immdisp22 = (w >> 0) & 0x3FFFFF;
        i->simmdisp22 = i->immdisp22;
        if(i->simmdisp22 & (1<<21))
                i->simmdisp22 |= ~((1<<22)-1);
        i->disp30 = (w >> 0) & 0x3FFFFFFF;
        i->w0 = w;
        i->target = -1;
        i->addr = pc;
        i->size = 1;
        return 1;
}

static int
mkinstr(uvlong pc, Instr *i)
{
        Instr xi;

        if (decode(pc, i) < 0)
                return -1;
        if(i->op==0 && i->op2==4 && !dascase){  /* SETHI */
                if (decode(pc+4, &xi) < 0)
                        return -1;
                if(xi.op==2 && xi.op3==0)               /* ADD */
                if(xi.i == 1 && xi.rs1 == i->rd){       /* immediate to same reg */
                        i->imm32 = xi.simm13 + (i->immdisp22<<10);
                        i->target = xi.rd;
                        i->w1 = xi.w0;
                        i->size++;
                        return 1;
                }
        }
        if(i->op==2 && i->opf==1 && !dascase){  /* FMOVS */
                if (decode(pc+4, &xi) < 0)
                        return -1;
                if(i->op==2 && i->opf==1)               /* FMOVS */
                if(xi.rd==i->rd+1 && xi.rs2==i->rs2+1){ /* next pair */
                        i->w1 = xi.w0;
                        i->size++;
                }
        }
        return 1;
}

static int
printins(Map *map, uvlong pc, char *buf, int n)
{
        Instr instr;
        void (*f)(Instr*, char*);

        mymap = map;
        memset(&instr, 0, sizeof(instr));
        instr.curr = buf;
        instr.end = buf+n-1;
        if (mkinstr(pc, &instr) < 0)
                return -1;
        switch(instr.op){
        case 0:
                f = sparc64op0[instr.op2].f;
                if(f)
                        (*f)(&instr, sparc64op0[instr.op2].mnemonic);
                else
                        bprint(&instr, "unknown %lux", instr.w0);
                break;

        case 1:
                bprint(&instr, "CALL\t");
                instr.curr += symoff(instr.curr, instr.end-instr.curr,
                                        pc+instr.disp30*4, CTEXT);
                if (!dascase)
                        bprint(&instr, "(SB)");
                break;

        case 2:
                f = sparc64op2[instr.op3].f;
                if(f)
                        (*f)(&instr, sparc64op2[instr.op3].mnemonic);
                else
                        bprint(&instr, "unknown %lux", instr.w0);
                break;

        case 3:
                f = sparc64op3[instr.op3].f;
                if(f)
                        (*f)(&instr, sparc64op3[instr.op3].mnemonic);
                else
                        bprint(&instr, "unknown %lux", instr.w0);
                break;
        }
        if (instr.err) {
                if (instr.curr != buf)
                        bprint(&instr, "\t\t;");
                bprint(&instr, instr.err);
        }
        return instr.size*4;
}

static int
sparc64inst(Map *map, uvlong pc, char modifier, char *buf, int n)
{
        static int fmtinstalled = 0;

                /* a modifier of 'I' toggles the dissassembler type */
        if (!fmtinstalled) {
                fmtinstalled = 1;
                fmtinstall('T', Tfmt);
        }
        if ((asstype == ASUNSPARC && modifier == 'i')
                || (asstype == ASPARC && modifier == 'I'))
                dascase = 'a'-'A';
        else
                dascase = 0;
        return printins(map, pc, buf, n);
}

static int
sparc64das(Map *map, uvlong pc, char *buf, int n)
{
        Instr instr;

        mymap = map;
        memset(&instr, 0, sizeof(instr));
        instr.curr = buf;
        instr.end = buf+n-1;
        if (mkinstr(pc, &instr) < 0)
                return -1;
        if (instr.end-instr.curr > 8)
                instr.curr = _hexify(instr.curr, instr.w0, 7);
        if (instr.end-instr.curr > 9 && instr.size == 2) {
                *instr.curr++ = ' ';
                instr.curr = _hexify(instr.curr, instr.w1, 7);
        }
        *instr.curr = 0;
        return instr.size*4;
}

static int
sparc64instlen(Map *map, uvlong pc)
{
        Instr i;

        mymap = map;
        if (mkinstr(pc, &i) < 0)
                return -1;
        return i.size*4;
}

static int
plocal(Instr *i)
{
        long offset;
        Symbol s;

        if (!findsym(i->addr, CTEXT, &s) || !findlocal(&s, FRAMENAME, &s))
                return -1;
        if (s.value > i->simm13) {
                if(getauto(&s, s.value-i->simm13, CAUTO, &s)) {
                        bprint(i, "%s+%lld(SP)", s.name, s.value);
                        return 1;
                }
        } else {
                offset = i->simm13-s.value;
                if (getauto(&s, offset-4, CPARAM, &s)) {
                        bprint(i, "%s+%ld(FP)", s.name, offset);
                        return 1;
                }
        }
        return -1;
}

static void
address(Instr *i)
{
        Symbol s, s2;
        uvlong off, off1;

        if (i->rs1 == 1 && plocal(i) >= 0)
                return;
        off = mach->sb+i->simm13;
        if(i->rs1 == 2  && findsym(off, CANY, &s)
                        && s.value-off < 4096
                        && (s.class == CDATA || s.class == CTEXT)) {
                if(off==s.value && s.name[0]=='$'){
                        off1 = 0;
                        geta(mymap, s.value, &off1);
                        if(off1 && findsym(off1, CANY, &s2) && s2.value == off1){
                                bprint(i, "$%s(SB)", s2.name);
                                return;
                        }
                }
                bprint(i, "%s", s.name);
                if (s.value != off)
                        bprint(i, "+%llux", s.value-off);
                bprint(i, "(SB)");
                return;
        }
        bprint(i, "%ux(R%d)", i->simm13, i->rs1);
}

static void
unimp(Instr *i, char *m)
{
        bprint(i, "%T", m);
}

static char     *bratab[16] = {         /* page 91 */
        [0X8]   "A",
        [0X0]   "N",
        [0X9]   "NE",
        [0X1]   "E",
        [0XA]   "G",
        [0X2]   "LE",
        [0XB]   "GE",
        [0X3]   "L",
        [0XC]   "GU",
        [0X4]   "LEU",
        [0XD]   "CC",
        [0X5]   "CS",
        [0XE]   "POS",
        [0X6]   "NEG",
        [0XF]   "VC",
        [0X7]   "VS",
};

static char     *fbratab[16] = {        /* page 91 */
        [0X8]   "A",
        [0X0]   "N",
        [0X7]   "U",
        [0X6]   "G",
        [0X5]   "UG",
        [0X4]   "L",
        [0X3]   "UL",
        [0X2]   "LG",
        [0X1]   "NE",
        [0X9]   "E",
        [0XA]   "UE",
        [0XB]   "GE",
        [0XC]   "UGE",
        [0XD]   "LE",
        [0XE]   "ULE",
        [0XF]   "O",
};

static char     *cbratab[16] = {        /* page 91 */
        [0X8]   "A",
        [0X0]   "N",
        [0X7]   "3",
        [0X6]   "2",
        [0X5]   "23",
        [0X4]   "1",
        [0X3]   "13",
        [0X2]   "12",
        [0X1]   "123",
        [0X9]   "0",
        [0XA]   "03",
        [0XB]   "02",
        [0XC]   "023",
        [0XD]   "01",
        [0XE]   "013",
        [0XF]   "012",
};

static void
bra1(Instr *i, char *m, char *tab[])
{
        long imm;

        imm = i->simmdisp22;
        if(i->a)
                bprint(i, "%T%T.%c\t", m, tab[i->cond], 'A'+dascase);
        else
                bprint(i, "%T%T\t", m, tab[i->cond]);
        i->curr += symoff(i->curr, i->end-i->curr, i->addr+4*imm, CTEXT);
        if (!dascase)
                bprint(i, "(SB)");
}

static void
bra(Instr *i, char *m)                  /* page 91 */
{
        bra1(i, m, bratab);
}

static void
fbra(Instr *i, char *m)                 /* page 93 */
{
        bra1(i, m, fbratab);
}

static void
cbra(Instr *i, char *m)                 /* page 95 */
{
        bra1(i, m, cbratab);
}

static void
trap(Instr *i, char *m)                 /* page 101 */
{
        if(i->i == 0)
                bprint(i, "%T%T\tR%d+R%d", m, bratab[i->cond], i->rs2, i->rs1);
        else
                bprint(i, "%T%T\t$%ux+R%d", m, bratab[i->cond], i->simm13, i->rs1);
}

static void
sethi(Instr *i, char *m)                /* page 89 */
{
        ulong imm;

        imm = i->immdisp22<<10;
        if(dascase){
                bprint(i, "%T\t%lux, R%d", m, imm, i->rd);
                return;
        }
        if(imm==0 && i->rd==0){
                bprint(i, "NOP");
                return;
        }
        if(i->target < 0){
                bprint(i, "MOVW\t$%lux, R%d", imm, i->rd);
                return;
        }
        bprint(i, "MOVW\t$%lux, R%d", i->imm32, i->target);
}

static char ldtab[] = {
        'W',
        'B',
        'H',
        'D',
};

static char*
moveinstr(int op3, char *m)
{
        char *s;
        int c;
        static char buf[8];

        if(!dascase){
                /* batshit cases */
                if(op3 == 0xF || op3 == 0x1F)
                        return "SWAP";
                if(op3 == 0xD || op3 == 0x1D)
                        return "TAS";   /* really LDSTUB */
                c = ldtab[op3&3];
                s = "";
                if((op3&11)==1 || (op3&11)==2)
                        s="U";
                sprint(buf, "MOV%c%s", c, s);
                return buf;
        }
        return m;
}

static void
load(Instr *i, char *m)                 /* page 68 */
{
        m = moveinstr(i->op3, m);
        if(i->i == 0)
                bprint(i, "%s\t(R%d+R%d), R%d", m, i->rs1, i->rs2, i->rd);
        else{
                bprint(i, "%s\t", m);
                address(i);
                bprint(i, ", R%d", i->rd);
        }
}

static void
loada(Instr *i, char *m)                /* page 68 */
{
        m = moveinstr(i->op3, m);
        if(i->i == 0)
                bprint(i, "%s\t(R%d+R%d, %d), R%d", m, i->rs1, i->rs2, i->asi, i->rd);
        else
                bprint(i, "unknown ld asi %lux", i->w0);
}

static void
store(Instr *i, char *m)                /* page 74 */
{
        m = moveinstr(i->op3, m);
        if(i->i == 0)
                bprint(i, "%s\tR%d, (R%d+R%d)",
                                m, i->rd, i->rs1, i->rs2);
        else{
                bprint(i, "%s\tR%d, ", m, i->rd);
                address(i);
        }
}

static void
storea(Instr *i, char *m)               /* page 74 */
{
        m = moveinstr(i->op3, m);
        if(i->i == 0)
                bprint(i, "%s\tR%d, (R%d+R%d, %d)", m, i->rd, i->rs1, i->rs2, i->asi);
        else
                bprint(i, "%s\tR%d, %d(R%d, %d), ???", m, i->rd, i->simm13, i->rs1, i->asi);
}

static void
shift(Instr *i, char *m)                /* page 88 */
{
        if(i->i == 0){
                if(i->rs1 == i->rd)
                        if(dascase)
                                bprint(i, "%T\tR%d, R%d", m, i->rs1, i->rs2);
                        else
                                bprint(i, "%T\tR%d, R%d", m, i->rs2, i->rs1);
                else
                        if(dascase)
                                bprint(i, "%T\tR%d, R%d, R%d", m, i->rs1, i->rs2, i->rd);
                        else
                                bprint(i, "%T\tR%d, R%d, R%d", m, i->rs2, i->rs1, i->rd);
        }else{
                if(i->rs1 == i->rd)
                        if(dascase)
                                bprint(i, "%T\t$%d,R%d", m, i->simm13&0x1F, i->rs1);
                        else
                                bprint(i, "%T\tR%d, $%d", m,  i->rs1, i->simm13&0x1F);
                else
                        if(dascase)
                                bprint(i, "%T\tR%d, $%d, R%d",m,i->rs1,i->simm13&0x1F,i->rd);
                        else
                                bprint(i, "%T\t$%d, R%d, R%d",m,i->simm13&0x1F,i->rs1,i->rd);
        }
}

static void
add(Instr *i, char *m)                  /* page 82 */
{
        if(i->i == 0){
                if(dascase)
                        bprint(i, "%T\tR%d, R%d", m, i->rs1, i->rs2);
                else
                        if(i->op3==2 && i->rs1==0 && i->rd)  /* OR R2, R0, R1 */
                                bprint(i, "MOVW\tR%d", i->rs2);
                        else
                                bprint(i, "%T\tR%d, R%d", m, i->rs2, i->rs1);
        }else{
                if(dascase)
                        bprint(i, "%T\tR%d, $%ux", m, i->rs1, i->simm13);
                else
                        if(i->op3==0 && i->rd && i->rs1==0)     /* ADD $x, R0, R1 */
                                bprint(i, "MOVW\t$%ux", i->simm13);
                        else if(i->op3==0 && i->rd && i->rs1==2){
                                /* ADD $x, R2, R1 -> MOVW $x(SB), R1 */
                                bprint(i, "MOVW\t$");
                                address(i);
                        } else
                                bprint(i, "%T\t$%ux, R%d", m, i->simm13, i->rs1);
        }
        if(i->rs1 != i->rd)
                bprint(i, ", R%d", i->rd);
}

static void
cmp(Instr *i, char *m)
{
        if(dascase || i->rd){
                add(i, m);
                return;
        }
        if(i->i == 0)
                bprint(i, "CMP\tR%d, R%d", i->rs1, i->rs2);
        else
                bprint(i, "CMP\tR%d, $%ux", i->rs1, i->simm13);
}

static char *regtab[4] = {
        "Y",
        "PSTATE",
        "WIM",  /* XXX not any more */
        "TT",
};

static void
wr(Instr *i, char *m)                   /* page 82 */
{
        if(dascase){
                if(i->i == 0)
                        bprint(i, "%s\tR%d, R%d", m, i->rs1, i->rs2);
                else
                        bprint(i, "%s\tR%d, $%ux", m, i->rs1, i->simm13);
        }else{
                if(i->i && i->simm13==0)
                        bprint(i, "MOVW\tR%d", i->rs1);
                else if(i->i == 0)
                        bprint(i, "wr\tR%d, R%d", i->rs2, i->rs1);
                else
                        bprint(i, "wr\t$%ux, R%d", i->simm13, i->rs1);
        }
        bprint(i, ", %s", regtab[i->op3&3]);
}

static void
rd(Instr *i, char *m)                   /* page 103 */
{
        if(i->rs1==15 && i->rd==0){
                m = "stbar";
                if(!dascase)
                        m = "STBAR";
                bprint(i, "%s", m);
        }else{
                if(!dascase)
                        m = "MOVW";
                bprint(i, "%s\t%s, R%d", m, regtab[i->op3&3], i->rd);
        }
}

static void
jmpl(Instr *i, char *m)                 /* page 82 */
{
        if(i->i == 0){
                if(i->rd == 15)
                        bprint(i, "%T\t(R%d+R%d)", "CALL", i->rs2, i->rs1);
                else
                        bprint(i, "%T\t(R%d+R%d), R%d", m, i->rs2, i->rs1, i->rd);
        }else{
                if(!dascase && i->simm13==8 && i->rs1==15 && i->rd==0)
                        bprint(i, "RETURN");
                else{
                        bprint(i, "%T\t", m);
                        address(i);
                        bprint(i, ", R%d", i->rd);
                }
        }
}

static void
loadf(Instr *i, char *m)                /* page 70 */
{
        if(!dascase){
                m = "FMOVD";
                if(i->op3 == 0x20)
                        m = "FMOVF";
                else if(i->op3 == 0x21)
                        m = "MOVW";
        }
        if(i->i == 0)
                bprint(i, "%s\t(R%d+R%d)", m, i->rs1, i->rs2);
        else{
                bprint(i, "%s\t", m);
                address(i);
        }
        if(i->op3 == 0x21)
                bprint(i, ", FSR");
        else
                bprint(i, ", R%d", i->rd);
}

static
void storef(Instr *i, char *m)          /* page 70 */
{
        if(!dascase){
                m = "FMOVD";
                if(i->op3 == 0x25 || i->op3 == 0x26)
                        m = "MOVW";
                else if(i->op3 == 0x20)
                        m = "FMOVF";
        }
        bprint(i, "%s\t", m);
        if(i->op3 == 0x25)
                bprint(i, "FSR, ");
        else if(i->op3 == 0x26)
                bprint(i, "FQ, ");
        else
                bprint(i, "R%d, ", i->rd);
        if(i->i == 0)
                bprint(i, "(R%d+R%d)", i->rs1, i->rs2);
        else
                address(i);
}

static
void loadc(Instr *i, char *m)           /* page 72 */
{
        if(i->i == 0)
                bprint(i, "%s\t(R%d+R%d), C%d", m, i->rs1, i->rs2, i->rd);
        else{
                bprint(i, "%s\t", m);
                address(i);
                bprint(i, ", C%d", i->rd);
        }
}

static
void loadcsr(Instr *i, char *m)         /* page 72 */
{
        if(i->i == 0)
                bprint(i, "%s\t(R%d+R%d), CSR", m, i->rs1, i->rs2);
        else{
                bprint(i, "%s\t", m);
                address(i);
                bprint(i, ", CSR");
        }
}

static struct{
        int     opf;
        char    *name;
} fptab1[] = {                          /* ignores rs1 */
        0xC4,   "FITOS",                /* page 109 */
        0xC8,   "FITOD",
        0xCC,   "FITOX",

        0xD1,   "FSTOI",                /* page 110 */
        0xD2,   "FDTOI",
        0xD3,   "FXTOI",

        0xC9,   "FSTOD",                /* page 111 */
        0xCD,   "FSTOX",
        0xC6,   "FDTOS",
        0xCE,   "FDTOX",
        0xC7,   "FXTOS",
        0xCB,   "FXTOD",

        0x01,   "FMOVS",                /* page 112 */
        0x05,   "FNEGS",
        0x09,   "FABSS",

        0x29,   "FSQRTS",               /* page 113 */
        0x2A,   "FSQRTD",
        0x2B,   "FSQRTX",

        0,      0,
};

static struct{
        int     opf;
        char    *name;
} fptab2[] = {                          /* uses rs1 */

        0x41,   "FADDS",                /* page 114 */
        0x42,   "FADDD",
        0x43,   "FADDX",
        0x45,   "FSUBS",
        0x46,   "FSUBD",
        0x47,   "FSUBX",

        0x49,   "FMULS",                /* page 115 */
        0x4A,   "FMULD",
        0x4B,   "FMULX",
        0x4D,   "FDIVS",
        0x4E,   "FDIVD",
        0x4F,   "FDIVX",

        0x51,   "FCMPS",                /* page 116 */
        0x52,   "FCMPD",
        0x53,   "FCMPX",
        0x55,   "FCMPES",
        0x56,   "FCMPED",
        0x57,   "FCMPEX",

        0, 0
};

static void
fpop(Instr *i, char *m)                 /* page 108-116 */
{
        int j;

        if(dascase==0 && i->size==2){
                bprint(i, "FMOVD\tF%d, F%d", i->rs2, i->rd);
                return;
        }
        for(j=0; fptab1[j].name; j++)
                if(fptab1[j].opf == i->opf){
                        bprint(i, "%T\tF%d, F%d", fptab1[j].name, i->rs2, i->rd);
                        return;
                }
        for(j=0; fptab2[j].name; j++)
                if(fptab2[j].opf == i->opf){
                        bprint(i, "%T\tF%d, F%d, F%d", fptab2[j].name, i->rs1, i->rs2, i->rd);
                        return;
                }
        bprint(i, "%T%ux\tF%d, F%d, F%d", m, i->opf, i->rs1, i->rs2, i->rd);
}

static int
sparc64foll(Map *map, uvlong pc, Rgetter rget, uvlong *foll)
{
        ulong w, r1, r2;
        char buf[8];
        Instr i;

        mymap = map;
        if (mkinstr(pc, &i) < 0)
                return -1;
        w = i.w0;
        switch(w & 0xC1C00000){
        case 0x00800000:                /* branch on int cond */
        case 0x01800000:                /* branch on fp cond */
        case 0x01C00000:                /* branch on copr cond */
                foll[0] = pc+8;
                foll[1] = pc + (i.simmdisp22<<2);
                return 2;
        }

        if((w&0xC0000000) == 0x40000000){       /* CALL */
                foll[0] = pc + (i.disp30<<2);
                return 1;
        }

        if((w&0xC1F80000) == 0x81C00000){       /* JMPL */
                sprint(buf, "R%ld", (w>>14)&0xF);
                r1 = (*rget)(map, buf);
                if(w & 0x2000)                  /* JMPL R1+simm13 */
                        r2 = i.simm13;
                else{                           /* JMPL R1+R2 */
                        sprint(buf, "R%ld", w&0xF);
                        r2 = (*rget)(map, buf);
                }
                foll[0] = r1 + r2;
                return 1;
        }
        foll[0] = pc+i.size*4;
        return 1;
}