Subversion Repositories planix.SVN

Rev

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

#include        "l.h"

#define LPUT(c)\
        {\
                cbp[0] = (c)>>24;\
                cbp[1] = (c)>>16;\
                cbp[2] = (c)>>8;\
                cbp[3] = (c);\
                cbp += 4;\
                cbc -= 4;\
                if(cbc <= 0)\
                        cflush();\
        }

#define CPUT(c)\
        {\
                cbp[0] = (c);\
                cbp++;\
                cbc--;\
                if(cbc <= 0)\
                        cflush();\
        }

long
entryvalue(void)
{
        char *a;
        Sym *s;

        a = INITENTRY;
        if(*a >= '0' && *a <= '9')
                return atolwhex(a);
        s = lookup(a, 0);
        if(s->type == 0)
                return INITTEXT;
        if(s->type != STEXT && s->type != SLEAF)
                diag("entry not text: %s", s->name);
        return s->value;
}

void
asmb(void)
{
        Prog *p;
        long t;
        Optab *o;

        if(debug['v'])
                Bprint(&bso, "%5.2f asm\n", cputime());
        Bflush(&bso);
        seek(cout, HEADR, 0);
        pc = INITTEXT;
        for(p = firstp; p != P; p = p->link) {
                if(p->as == ATEXT) {
                        curtext = p;
                        autosize = p->to.offset + 4;
                }
                if(p->pc != pc) {
                        diag("phase error %lux sb %lux",
                                p->pc, pc);
                        if(!debug['a'])
                                prasm(curp);
                        pc = p->pc;
                }
                curp = p;
                o = oplook(p);  /* could probably avoid this call */
                if(asmout(p, o, 0)) {
                        p = p->link;
                        pc += 4;
                }
                pc += o->size;
        }
        if(debug['a'])
                Bprint(&bso, "\n");
        Bflush(&bso);
        cflush();

        curtext = P;
        switch(HEADTYPE) {
        case 0:
        case 3:
                seek(cout, HEADR+textsize, 0);
                break;
        case 1:
        case 2:
                seek(cout, HEADR+textsize, 0);
                break;
        }
        for(t = 0; t < datsize; t += sizeof(buf)-100) {
                if(datsize-t > sizeof(buf)-100)
                        datblk(t, sizeof(buf)-100);
                else
                        datblk(t, datsize-t);
        }

        symsize = 0;
        lcsize = 0;
        if(!debug['s']) {
                if(debug['v'])
                        Bprint(&bso, "%5.2f sym\n", cputime());
                Bflush(&bso);
                switch(HEADTYPE) {
                case 0:
                case 3:
                        seek(cout, HEADR+textsize+datsize, 0);
                        break;
                case 2:
                case 1:
                        seek(cout, HEADR+textsize+datsize, 0);
                        break;
                }
                if(!debug['s'])
                        asmsym();
                if(debug['v'])
                        Bprint(&bso, "%5.2f sp\n", cputime());
                Bflush(&bso);
                if(!debug['s'])
                        asmlc();
                 /* round up file length for boot image */
                if(HEADTYPE == 0 || HEADTYPE == 3)
                        if((symsize+lcsize) & 1)
                                CPUT(0);
                cflush();
        }

        seek(cout, 0L, 0);
        switch(HEADTYPE) {
        case 0:
                lput(0x1030107);                /* magic and sections */
                lput(textsize);                 /* sizes */
                lput(datsize);
                lput(bsssize);
                lput(symsize);                  /* nsyms */
                lput(entryvalue());             /* va of entry */
                lput(0L);
                lput(lcsize);
                break;
        case 1:
                break;
        case 2:
                lput(4*13*13+7);                /* magic */
                lput(textsize);                 /* sizes */
                lput(datsize);
                lput(bsssize);
                lput(symsize);                  /* nsyms */
                lput(entryvalue());             /* va of entry */
                lput(0L);
                lput(lcsize);
                break;
        case 3:
                lput(0x1030107);                /* magic and sections */
                lput(0x90100000);
#define SPARC_NOOP 0x01000000
                lput(SPARC_NOOP);
                lput(SPARC_NOOP);
                lput(SPARC_NOOP);
                lput(SPARC_NOOP);
                lput(SPARC_NOOP);
                lput(SPARC_NOOP);
                break;
        }
        cflush();
}

void
lput(long l)
{

        LPUT(l);
}

void
cflush(void)
{
        int n;

        n = sizeof(buf.cbuf) - cbc;
        if(n)
                write(cout, buf.cbuf, n);
        cbp = buf.cbuf;
        cbc = sizeof(buf.cbuf);
}

void
asmsym(void)
{
        Prog *p;
        Auto *a;
        Sym *s;
        int h;

        s = lookup("etext", 0);
        if(s->type == STEXT)
                putsymb(s->name, 'T', s->value, s->version);

        for(h=0; h<NHASH; h++)
                for(s=hash[h]; s!=S; s=s->link)
                        switch(s->type) {
                        case SCONST:
                                putsymb(s->name, 'D', s->value, s->version);
                                continue;

                        case SDATA:
                                putsymb(s->name, 'D', s->value+INITDAT, s->version);
                                continue;

                        case SBSS:
                                putsymb(s->name, 'B', s->value+INITDAT, s->version);
                                continue;

                        case SFILE:
                                putsymb(s->name, 'f', s->value, s->version);
                                continue;
                        }

        for(p=textp; p!=P; p=p->cond) {
                s = p->from.sym;
                if(s->type != STEXT && s->type != SLEAF)
                        continue;

                /* filenames first */
                for(a=p->to.autom; a; a=a->link)
                        if(a->type == D_FILE)
                                putsymb(a->asym->name, 'z', a->aoffset, 0);
                        else
                        if(a->type == D_FILE1)
                                putsymb(a->asym->name, 'Z', a->aoffset, 0);

                if(s->type == STEXT)
                        putsymb(s->name, 'T', s->value, s->version);
                else
                        putsymb(s->name, 'L', s->value, s->version);

                /* frame, auto and param after */
                putsymb(".frame", 'm', p->to.offset+4, 0);
                for(a=p->to.autom; a; a=a->link)
                        if(a->type == D_AUTO)
                                putsymb(a->asym->name, 'a', -a->aoffset, 0);
                        else
                        if(a->type == D_PARAM)
                                putsymb(a->asym->name, 'p', a->aoffset, 0);
        }
        if(debug['v'] || debug['n'])
                Bprint(&bso, "symsize = %lud\n", symsize);
        Bflush(&bso);
}

void
putsymb(char *s, int t, long v, int ver)
{
        int i, f;

        if(t == 'f')
                s++;
        LPUT(v);
        if(ver)
                t += 'a' - 'A';
        CPUT(t+0x80);                   /* 0x80 is variable length */

        if(t == 'Z' || t == 'z') {
                CPUT(s[0]);
                for(i=1; s[i] != 0 || s[i+1] != 0; i += 2) {
                        CPUT(s[i]);
                        CPUT(s[i+1]);
                }
                CPUT(0);
                CPUT(0);
                i++;
        }
        else {
                for(i=0; s[i]; i++)
                        CPUT(s[i]);
                CPUT(0);
        }
        symsize += 4 + 1 + i + 1;

        if(debug['n']) {
                if(t == 'z' || t == 'Z') {
                        Bprint(&bso, "%c %.8lux ", t, v);
                        for(i=1; s[i] != 0 || s[i+1] != 0; i+=2) {
                                f = ((s[i]&0xff) << 8) | (s[i+1]&0xff);
                                Bprint(&bso, "/%x", f);
                        }
                        Bprint(&bso, "\n");
                        return;
                }
                if(ver)
                        Bprint(&bso, "%c %.8lux %s<%d>\n", t, v, s, ver);
                else
                        Bprint(&bso, "%c %.8lux %s\n", t, v, s);
        }
}

#define MINLC   4
void
asmlc(void)
{
        long oldpc, oldlc;
        Prog *p;
        long v, s;

        oldpc = INITTEXT;
        oldlc = 0;
        for(p = firstp; p != P; p = p->link) {
                if(p->line == oldlc || p->as == ATEXT || p->as == ANOP) {
                        if(p->as == ATEXT)
                                curtext = p;
                        if(debug['V'])
                                Bprint(&bso, "%6lux %P\n",
                                        p->pc, p);
                        continue;
                }
                if(debug['V'])
                        Bprint(&bso, "\t\t%6ld", lcsize);
                v = (p->pc - oldpc) / MINLC;
                while(v) {
                        s = 127;
                        if(v < 127)
                                s = v;
                        CPUT(s+128);    /* 129-255 +pc */
                        if(debug['V'])
                                Bprint(&bso, " pc+%ld*%d(%ld)", s, MINLC, s+128);
                        v -= s;
                        lcsize++;
                }
                s = p->line - oldlc;
                oldlc = p->line;
                oldpc = p->pc + MINLC;
                if(s > 64 || s < -64) {
                        CPUT(0);        /* 0 vv +lc */
                        CPUT(s>>24);
                        CPUT(s>>16);
                        CPUT(s>>8);
                        CPUT(s);
                        if(debug['V']) {
                                if(s > 0)
                                        Bprint(&bso, " lc+%ld(%d,%ld)\n",
                                                s, 0, s);
                                else
                                        Bprint(&bso, " lc%ld(%d,%ld)\n",
                                                s, 0, s);
                                Bprint(&bso, "%6lux %P\n",
                                        p->pc, p);
                        }
                        lcsize += 5;
                        continue;
                }
                if(s > 0) {
                        CPUT(0+s);      /* 1-64 +lc */
                        if(debug['V']) {
                                Bprint(&bso, " lc+%ld(%ld)\n", s, 0+s);
                                Bprint(&bso, "%6lux %P\n",
                                        p->pc, p);
                        }
                } else {
                        CPUT(64-s);     /* 65-128 -lc */
                        if(debug['V']) {
                                Bprint(&bso, " lc%ld(%ld)\n", s, 64-s);
                                Bprint(&bso, "%6lux %P\n",
                                        p->pc, p);
                        }
                }
                lcsize++;
        }
        while(lcsize & 1) {
                s = 129;
                CPUT(s);
                lcsize++;
        }
        if(debug['v'] || debug['V'])
                Bprint(&bso, "lcsize = %ld\n", lcsize);
        Bflush(&bso);
}

void
datblk(long s, long n)
{
        Prog *p;
        char *cast;
        long l, fl, j, d;
        int i, c;

        memset(buf.dbuf, 0, n+100);
        for(p = datap; p != P; p = p->link) {
                curp = p;
                l = p->from.sym->value + p->from.offset - s;
                c = p->reg;
                i = 0;
                if(l < 0) {
                        if(l+c <= 0)
                                continue;
                        while(l < 0) {
                                l++;
                                i++;
                        }
                }
                if(l >= n)
                        continue;
                if(p->as != AINIT && p->as != ADYNT) {
                        for(j=l+(c-i)-1; j>=l; j--)
                                if(buf.dbuf[j]) {
                                        print("%P\n", p);
                                        diag("multiple initialization");
                                        break;
                                }
                }
                switch(p->to.type) {
                default:
                        diag("unknown mode in initialization\n%P", p);
                        break;

                case D_FCONST:
                        switch(c) {
                        default:
                        case 4:
                                fl = ieeedtof(&p->to.ieee);
                                cast = (char*)&fl;
                                for(; i<c; i++) {
                                        buf.dbuf[l] = cast[fnuxi8[i+4]];
                                        l++;
                                }
                                break;
                        case 8:
                                cast = (char*)&p->to.ieee;
                                for(; i<c; i++) {
                                        buf.dbuf[l] = cast[fnuxi8[i]];
                                        l++;
                                }
                                break;
                        }
                        break;

                case D_SCONST:
                        for(; i<c; i++) {
                                buf.dbuf[l] = p->to.sval[i];
                                l++;
                        }
                        break;

                case D_CONST:
                        d = p->to.offset;
                        if(p->to.sym) {
                                if(p->to.sym->type == STEXT ||
                                   p->to.sym->type == SLEAF)
                                        d += p->to.sym->value;
                                if(p->to.sym->type == SDATA)
                                        d += p->to.sym->value + INITDAT;
                                if(p->to.sym->type == SBSS)
                                        d += p->to.sym->value + INITDAT;
                        }
                        cast = (char*)&d;
                        switch(c) {
                        default:
                                diag("bad nuxi %d %d\n%P", c, i, curp);
                                break;
                        case 1:
                                for(; i<c; i++) {
                                        buf.dbuf[l] = cast[inuxi1[i]];
                                        l++;
                                }
                                break;
                        case 2:
                                for(; i<c; i++) {
                                        buf.dbuf[l] = cast[inuxi2[i]];
                                        l++;
                                }
                                break;
                        case 4:
                                for(; i<c; i++) {
                                        buf.dbuf[l] = cast[inuxi4[i]];
                                        l++;
                                }
                                break;
                        }
                        break;
                }
        }
        write(cout, buf.dbuf, n);
}

#define OP2(x)  (0x80000000|((x)<<19))
#define OP3(x)  (0xc0000000|((x)<<19))
#define OPB(x)  (0x00800000|((x)<<25))
#define OPT(x)  (0x81d02000|((x)<<25))
#define OPF1(x) (0x81a00000|((x)<<5))
#define OPF2(x) (0x81a80000|((x)<<5))
#define OPFB(x) (0x01800000|((x)<<25))

#define OP_RRR(op,r1,r2,r3)\
        (0x00000000 | op |\
        (((r1)&31L)<<0) |\
        (((r2)&31L)<<14) |\
        (((r3)&31L)<<25))
#define OP_IRR(op,i,r2,r3)\
        (0x00002000L | (op) |\
        (((i)&0x1fffL)<<0) |\
        (((r2)&31L)<<14) |\
        (((r3)&31L)<<25))
#define OP_BRA(op,pc)\
        ((op) |\
        (((pc)&0x3fffff)<<0))

int
asmout(Prog *p, Optab *o, int aflag)
{
        long o1, o2, o3, o4, o5, v;
        Prog *ct;
        int r;

        o1 = 0;
        o2 = 0;
        o3 = 0;
        o4 = 0;
        o5 = 0;

        switch(o->type) {
        default:
                if(aflag)
                        return 0;
                diag("unknown type %d", o->type);
                if(!debug['a'])
                        prasm(p);
                break;

        case 0:          /* pseudo ops */
                if(aflag) {
                        if(p->link) {
                                if(p->as == ATEXT) {
                                        ct = curtext;
                                        o2 = autosize;
                                        curtext = p;
                                        autosize = p->to.offset + 4;
                                        o1 = asmout(p->link, oplook(p->link), aflag);
                                        curtext = ct;
                                        autosize = o2;
                                } else
                                        o1 = asmout(p->link, oplook(p->link), aflag);
                        }
                        return o1;
                }
                break;

        case 1:         /* mov r1,r2 ==> OR r1,r0,r2 */
                o1 = OP_RRR(opcode(AOR), p->from.reg, REGZERO, p->to.reg);
                break;

        case 2:         /* mov $c,r ==> add r1,r0,r2 */
                r = p->from.reg;
                if(r == NREG)
                        r = o->param;
                v = regoff(&p->from);
                o1 = OP_IRR(opcode(AADD), v, r, p->to.reg);
                break;

        case 3:         /* mov soreg, r */
                r = p->from.reg;
                if(r == NREG)
                        r = o->param;
                v = regoff(&p->from);
                if(v == 0 && p->reg != NREG)
                        o1 = OP_RRR(opcode(p->as), p->reg, r, p->to.reg);
                else
                        o1 = OP_IRR(opcode(p->as), v, r, p->to.reg);
                break;

        case 4:         /* mov r, soreg */
                r = p->to.reg;
                if(r == NREG)
                        r = o->param;
                v = regoff(&p->to);
                if(v == 0 && p->reg != NREG)
                        o1 = OP_RRR(opcode(p->as+AEND), p->reg, r, p->from.reg);
                else
                        o1 = OP_IRR(opcode(p->as+AEND), v, r, p->from.reg);
                break;

        case 5:         /* mov $lcon, reg => sethi, add */
                v = regoff(&p->from);
                o1 = 0x1000000 | (REGTMP<<25) | ((v>>10) & 0x3fffff);   /* sethi */
                o2 = OP_IRR(opcode(AADD), (v&0x3ff), REGTMP, p->to.reg);
                break;

        case 6:         /* mov asi, r[+r] */
                o1 = OP_RRR(opcode(p->as), p->reg, p->from.reg, p->to.reg);
                o1 |= (1<<23) | ((p->from.offset&0xff)<<5);
                break;

        case 7:         /* mov [+r]r, asi */
                o1 = OP_RRR(opcode(p->as+AEND), p->reg, p->to.reg, p->from.reg);
                o1 |= (1<<23) | ((p->to.offset&0xff)<<5);
                break;

        case 8:         /* mov r, preg and mov preg, r */
                if(p->to.type == D_PREG) {
                        r = p->from.reg;
                        switch(p->to.reg)
                        {
                        default:
                                diag("unknown register P%d", p->to.reg);
                        case D_Y:
                                o1 = OP2(48);   /* wry */
                                break;
                        case D_PSR:
                                o1 = OP2(49);   /* wrpsr */
                                break;
                        case D_WIM:
                                o1 = OP2(50);   /* wrwim */
                                break;
                        case D_TBR:
                                o1 = OP2(51);   /* wrtbr */
                                break;
                        }
                        o1 = OP_IRR(o1, 0, r, 0);
                        break;
                }
                if(p->from.type == D_PREG) {
                        r = p->to.reg;
                        switch(p->from.reg)
                        {
                        default:
                                diag("unknown register P%d", p->to.reg);
                        case D_Y:
                                o1 = OP2(40);   /* rdy */
                                break;
                        case D_PSR:
                                o1 = OP2(41);   /* rdpsr */
                                break;
                        case D_WIM:
                                o1 = OP2(42);   /* rdwim */
                                break;
                        case D_TBR:
                                o1 = OP2(43);   /* rdtbr */
                                break;
                        }
                        o1 = OP_RRR(o1, 0, 0, r);
                        break;
                }
                break;

        case 9:         /* movb r,r */
                v = 24;
                if(p->as == AMOVH || p->as == AMOVHU)
                        v = 16;
                r = ASRA;
                if(p->as == AMOVBU || p->as == AMOVHU)
                        r = ASRL;
                o1 = OP_IRR(opcode(ASLL), v, p->from.reg, p->to.reg);
                o2 = OP_IRR(opcode(r), v, p->to.reg, p->to.reg);
                break;

        case 10:        /* mov $loreg, reg */
                r = p->from.reg;
                if(r == NREG)
                        r = o->param;
                v = regoff(&p->from);
                o1 = 0x1000000 | (REGTMP<<25) | ((v>>10) & 0x3fffff);   /* sethi */
                o2 = OP_RRR(opcode(AADD), r, REGTMP, REGTMP);
                o3 = OP_IRR(opcode(AADD), (v&0x3ff), REGTMP, p->to.reg);
                break;

        case 11:        /* mov loreg, r */
                r = p->from.reg;
                if(r == NREG)
                        r = o->param;
                v = regoff(&p->from);
                o1 = 0x1000000 | (REGTMP<<25) | ((v>>10) & 0x3fffff);   /* sethi */
                o2 = OP_RRR(opcode(AADD), r, REGTMP, REGTMP);
                o3 = OP_IRR(opcode(p->as), (v&0x3ff), REGTMP, p->to.reg);
                break;

        case 12:        /* mov r, loreg */
                r = p->to.reg;
                if(r == NREG)
                        r = o->param;
                v = regoff(&p->to);
                o1 = 0x1000000 | (REGTMP<<25) | ((v>>10) & 0x3fffff);   /* sethi */
                o2 = OP_RRR(opcode(AADD), r, REGTMP, REGTMP);
                o3 = OP_IRR(opcode(p->as+AEND), (v&0x3ff), REGTMP, p->from.reg);
                break;

        case 13:        /* mov $ucon, r */
                v = regoff(&p->from);
                o1 = 0x1000000 | (p->to.reg<<25) | ((v>>10) & 0x3fffff);        /* sethi */
                break;

        case 20:        /* op $scon,r */
                v = regoff(&p->from);
                r = p->reg;
                if(r == NREG)
                        r = p->to.reg;
                o1 = OP_IRR(opcode(p->as), v, r, p->to.reg);
                break;

        case 21:        /* op r1,r2 */
                r = p->reg;
                if(r == NREG)
                        r = p->to.reg;
                o1 = OP_RRR(opcode(p->as), p->from.reg, r, p->to.reg);
                break;

        case 22:        /* op $lcon,r */
                v = regoff(&p->from);
                r = p->reg;
                if(r == NREG)
                        r = p->to.reg;
                o1 = 0x1000000 | (REGTMP<<25) | ((v>>10) & 0x3fffff);   /* sethi */
                o2 = OP_IRR(opcode(AADD), (v&0x3ff), REGTMP, REGTMP);
                o3 = OP_RRR(opcode(p->as), REGTMP, r, p->to.reg);
                break;

        case 23:        /* cmp r,r */
                o1 = OP_RRR(opcode(ASUBCC), p->to.reg, p->from.reg, REGZERO);
                break;

        case 24:        /* cmp r,$c */
                v = regoff(&p->to);
                o1 = OP_IRR(opcode(ASUBCC), v, p->from.reg, REGZERO);
                break;

        case 25:        /* cmp $c,r BOTCH, fix compiler */
                v = regoff(&p->from);
                o1 = OP_IRR(opcode(AADD), v, NREG, REGTMP);
                o2 = OP_RRR(opcode(ASUBCC), p->to.reg, REGTMP, REGZERO);
                break;

        case 26:        /* op $ucon,r */
                v = regoff(&p->from);
                r = p->reg;
                if(r == NREG)
                        r = p->to.reg;
                o1 = 0x1000000 | (REGTMP<<25) | ((v>>10) & 0x3fffff);   /* sethi */
                o2 = OP_RRR(opcode(p->as), REGTMP, r, p->to.reg);
                break;

        case 30:        /* jmp/jmpl soreg */
                if(aflag)
                        return 0;
                v = regoff(&p->to);
                r = p->reg;
                if(r == NREG && p->as == AJMPL)
                        r = 15;
                o1 = OP_IRR(opcode(AJMPL), v, p->to.reg, r);
                break;

        case 31:        /* ba jmp */
                if(aflag)
                        return 0;
                r = p->as;
                if(r == AJMP)
                        r = ABA;
                v = 0;
                if(p->cond)
                        v = p->cond->pc - p->pc;
                o1 = OP_BRA(opcode(r), v/4);
                if(r == ABA && p->link && p->cond && isnop(p->link)) {
                        o2 = asmout(p->cond, oplook(p->cond), 1);
                        if(o2) {
                                o1 += 1;
                                if(debug['a'])
                                        Bprint(&bso, " %.8lux: %.8lux %.8lux%P\n", p->pc, o1, o2, p);
                                LPUT(o1);
                                LPUT(o2);
                                return 1;
                        }
                        /* cant set annul here because pc has already been counted */
                }
                break;

        case 32:        /* jmpl lbra */
                if(aflag)
                        return 0;
                v = 0;
                if(p->cond)
                        v = p->cond->pc - p->pc;
                r = p->reg;
                if(r != NREG && r != 15)
                        diag("cant jmpl other than R15");
                o1 = 0x40000000 | ((v/4) & 0x3fffffffL);        /* call */
                if(p->link && p->cond && isnop(p->link)) {
                        o2 = asmout(p->cond, oplook(p->cond), 1);
                        if(o2) {
                                o1 += 1;
                                if(debug['a'])
                                        Bprint(&bso, " %.8lux: %.8lux %.8lux%P\n", p->pc, o1, o2, p);
                                LPUT(o1);
                                LPUT(o2);
                                return 1;
                        }
                }
                break;

        case 33:        /* trap r */
                if(aflag)
                        return 0;
                o1 = opcode(p->as) | (p->from.reg<<14);
                break;

        case 34:        /* rett r1,r2 -> jmpl (r1); rett (r2) */
                if(aflag)
                        return 0;
                o1 = OP_IRR(opcode(AJMPL), 0, p->from.reg, REGZERO);
                o2 = OP_IRR(opcode(ARETT), 0, p->to.reg, REGZERO);
                break;

        case 40:        /* ldfsr, stfsr, stdq */
                if(p->to.type == D_PREG) {
                        r = p->from.reg;
                        if(r == NREG)
                                r = o->param;
                        v = regoff(&p->from);
                        if(p->to.reg == D_FSR) {
                                o1 = OP_IRR(OP3(33), v, r, 0);
                                break;
                        }
                        diag("unknown reg load %d", p->to.reg);
                } else {
                        r = p->to.reg;
                        if(r == NREG)
                                r = o->param;
                        v = regoff(&p->to);
                        if(p->from.reg == D_FSR) {
                                o1 = OP_IRR(OP3(37), v, r, 0);
                                break;
                        }
                        if(p->as == AMOVD && p->from.reg == D_FPQ) {
                                o1 = OP_IRR(OP3(38), v, r, 0);
                                break;
                        }
                        diag("unknown reg store %d", p->from.reg);
                }
                break;

        case 41:        /* ldf,ldd */
                r = p->from.reg;
                if(r == NREG)
                        r = o->param;
                v = regoff(&p->from);
                if(p->as == AFMOVF || p->as == AMOVW) {
                        o1 = OP_IRR(OP3(32), v, r, p->to.reg);
                        break;
                }
                if(p->as == AMOVD || p->as == AFMOVD) {
                        o1 = OP_IRR(OP3(35), v, r, p->to.reg);
                        break;
                }
                diag("only MOVD and MOVW to FREG");
                break;

        case 42:        /* ldd -> ldf,ldf */
                /* note should be ldd with proper allignment */
                r = p->from.reg;
                if(r == NREG)
                        r = o->param;
                v = regoff(&p->from);
                o1 = OP_IRR(OP3(32), v, r, p->to.reg);
                o2 = OP_IRR(OP3(32), v+4, r, p->to.reg+1);
                break;

        case 43:        /* stf */
                r = p->to.reg;
                if(r == NREG)
                        r = o->param;
                v = regoff(&p->to);
                if(p->as == AFMOVF || p->as == AMOVW) {
                        o1 = OP_IRR(OP3(36), v, r, p->from.reg);
                        break;
                }
                if(p->as == AMOVD || p->as == AFMOVD) {
                        o1 = OP_IRR(OP3(39), v, r, p->from.reg);
                        break;
                }
                diag("only MOVD and MOVW from FREG");
                break;

        case 44:        /* std -> stf,stf */
                /* note should be std with proper allignment */
                r = p->to.reg;
                if(r == NREG)
                        r = o->param;
                v = regoff(&p->to);
                o1 = OP_IRR(OP3(36), v, r, p->from.reg);
                o2 = OP_IRR(OP3(36), v+4, r, p->from.reg+1);
                break;

        case 45:        /* ldf lorg */
                r = p->from.reg;
                if(r == NREG)
                        r = o->param;
                v = regoff(&p->from);
                o1 = 0x1000000 | (REGTMP<<25) | ((v>>10) & 0x3fffff);   /* sethi */
                o2 = OP_RRR(opcode(AADD), r, REGTMP, REGTMP);
                o3 = OP_IRR(OP3(32), v&0x3ff, REGTMP, p->to.reg);
                break;

        case 46:        /* ldd lorg -> ldf,ldf */
                /* note should be ldd with proper allignment */
                r = p->from.reg;
                if(r == NREG)
                        r = o->param;
                v = regoff(&p->from);
                o1 = 0x1000000 | (REGTMP<<25) | ((v>>10) & 0x3fffff);   /* sethi */
                o2 = OP_RRR(opcode(AADD), r, REGTMP, REGTMP);
                o3 = OP_IRR(OP3(32), (v&0x3ff), REGTMP, p->to.reg);
                o4 = OP_IRR(OP3(32), (v&0x3ff)+4, REGTMP, p->to.reg+1);
                break;

        case 47:        /* stf lorg */
                r = p->to.reg;
                if(r == NREG)
                        r = o->param;
                v = regoff(&p->to);
                o1 = 0x1000000 | (REGTMP<<25) | ((v>>10) & 0x3fffff);   /* sethi */
                o2 = OP_RRR(opcode(AADD), r, REGTMP, REGTMP);
                o3 = OP_IRR(OP3(36), v&0x3ff, REGTMP, p->from.reg);
                break;

        case 48:        /* std lorg -> stf,stf */
                /* note should be std with proper allignment */
                r = p->to.reg;
                if(r == NREG)
                        r = o->param;
                v = regoff(&p->to);
                o1 = 0x1000000 | (REGTMP<<25) | ((v>>10) & 0x3fffff);   /* sethi */
                o2 = OP_RRR(opcode(AADD), r, REGTMP, REGTMP);
                o3 = OP_IRR(OP3(36), (v&0x3ff), REGTMP, p->from.reg);
                o4 = OP_IRR(OP3(36), (v&0x3ff)+4, REGTMP, p->from.reg+1);
                break;

        case 49:        /* fmovd -> fmovf,fmovf */
                o1 = OP_RRR(opcode(AFMOVF), p->from.reg, 0, p->to.reg);
                o2 = OP_RRR(opcode(AFMOVF), p->from.reg+1, 0, p->to.reg+1);
                break;

        case 50:        /* fcmp */
                o1 = OP_RRR(opcode(p->as), p->to.reg, p->from.reg, 0);
                break;

        case 51:        /* word */
                if(aflag)
                        return 0;
                o1 = regoff(&p->from);
                break;

        case 52:        /* div */
                r = p->reg;
                if(r == NREG)
                        r = p->to.reg;
                o1 = OP_IRR(opcode(ASRA), 31, r, REGTMP);
                o2 = OP_IRR(OP2(48), 0, REGTMP, 0);
                o3 = OP_RRR(opcode(ADIV), p->from.reg, r, p->to.reg);
                break;

        case 53:        /* divl */
                r = p->reg;
                if(r == NREG)
                        r = p->to.reg;
                o1 = OP_IRR(OP2(48), 0, REGZERO, 0);
                o2 = OP_RRR(opcode(ADIVL), p->from.reg, r, p->to.reg);
                break;

        case 54:        /* mod */
                r = p->reg;
                if(r == NREG)
                        r = p->to.reg;
                o1 = OP_IRR(opcode(ASRA), 31, r, REGTMP);
                o2 = OP_IRR(OP2(48), 0, REGTMP, 0);
                o3 = OP_RRR(opcode(ADIV), p->from.reg, r, REGTMP);
                o4 = OP_RRR(opcode(AMUL), p->from.reg, REGTMP, REGTMP);
                o5 = OP_RRR(opcode(ASUB), REGTMP, r, p->to.reg);
                break;

        case 55:        /* modl */
                r = p->reg;
                if(r == NREG)
                        r = p->to.reg;
                o1 = OP_IRR(OP2(48), 0, REGZERO, 0);
                o2 = OP_RRR(opcode(ADIVL), p->from.reg, r, REGTMP);
                o3 = OP_RRR(opcode(AMUL), p->from.reg, REGTMP, REGTMP);
                o4 = OP_RRR(opcode(ASUB), REGTMP, r, p->to.reg);
                break;

        case 56:        /* b(cc) -- annullable */
                if(aflag)
                        return 0;
                r = p->as;
                v = 0;
                if(p->cond)
                        v = p->cond->pc - p->pc;
                o1 = OP_BRA(opcode(r), v/4);
                if(p->link && p->cond && isnop(p->link))
                if(!debug['A']) {
                        o2 = asmout(p->cond, oplook(p->cond), 2);
                        if(o2) {
                                o1 |= 1<<29;    /* annul */
                                o1 += 1;
                                if(debug['a'])
                                        Bprint(&bso, " %.8lux: %.8lux %.8lux%P\n", p->pc, o1, o2, p);
                                LPUT(o1);
                                LPUT(o2);
                                return 1;
                        }
                }
                break;

        case 57:        /* op r1,r2 with reserved rs1 */
                r = 0;
                o1 = OP_RRR(opcode(p->as), p->from.reg, r, p->to.reg);
                break;
        }
        if(aflag)
                return o1;
        v = p->pc;
        switch(o->size) {
        default:
                if(debug['a'])
                        Bprint(&bso, " %.8lux:\t\t%P\n", v, p);
                break;
        case 4:
                if(debug['a'])
                        Bprint(&bso, " %.8lux: %.8lux\t%P\n", v, o1, p);
                LPUT(o1);
                break;
        case 8:
                if(debug['a'])
                        Bprint(&bso, " %.8lux: %.8lux %.8lux%P\n", v, o1, o2, p);
                LPUT(o1);
                LPUT(o2);
                break;
        case 12:
                if(debug['a'])
                        Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux%P\n", v, o1, o2, o3, p);
                LPUT(o1);
                LPUT(o2);
                LPUT(o3);
                break;
        case 16:
                if(debug['a'])
                        Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux %.8lux%P\n",
                                v, o1, o2, o3, o4, p);
                LPUT(o1);
                LPUT(o2);
                LPUT(o3);
                LPUT(o4);
                break;
        case 20:
                if(debug['a'])
                        Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux %.8lux %.8lux%P\n",
                                v, o1, o2, o3, o4, o5, p);
                LPUT(o1);
                LPUT(o2);
                LPUT(o3);
                LPUT(o4);
                LPUT(o5);
                break;
        }
        return 0;
}

int
isnop(Prog *p)
{
        if(p->as != AORN)
                return 0;
        if(p->reg != REGZERO && p->reg != NREG)
                return 0;
        if(p->from.type != D_REG || p->from.reg != REGZERO)
                return 0;
        if(p->to.type != D_REG || p->to.reg != REGZERO)
                return 0;
        return 1;
}

long
opcode(int a)
{
        switch(a) {
        case AADD:      return OP2(0);
        case AADDCC:    return OP2(16);
        case AADDX:     return OP2(8);
        case AADDXCC:   return OP2(24);

        case AMUL:      return OP2(10);
        case ADIV:      return OP2(15);
        case ADIVL:     return OP2(14);

        case ATADDCC:   return OP2(32);
        case ATADDCCTV: return OP2(34);

        case ASUB:      return OP2(4);
        case ASUBCC:    return OP2(20);
        case ASUBX:     return OP2(12);
        case ASUBXCC:   return OP2(28);

        case ATSUBCC:   return OP2(33);
        case ATSUBCCTV: return OP2(35);

        case AMULSCC:   return OP2(36);
        case ASAVE:     return OP2(60);
        case ARESTORE:  return OP2(61);

        case AAND:      return OP2(1);
        case AANDCC:    return OP2(17);
        case AANDN:     return OP2(5);
        case AANDNCC:   return OP2(21);

        case AOR:       return OP2(2);
        case AORCC:     return OP2(18);
        case AORN:      return OP2(6);
        case AORNCC:    return OP2(22);

        case AXOR:      return OP2(3);
        case AXORCC:    return OP2(19);
        case AXNOR:     return OP2(7);
        case AXNORCC:   return OP2(23);

        case ASLL:      return OP2(37);
        case ASRL:      return OP2(38);
        case ASRA:      return OP2(39);

        case AJMPL:
        case AJMP:      return OP2(56);
        case ARETT:     return OP2(57);

        case AMOVBU:    return OP3(1);  /* ldub */
        case AMOVB:     return OP3(9);  /* ldsb */
        case AMOVHU:    return OP3(2);  /* lduh */
        case AMOVH:     return OP3(10); /* ldsh */
        case AMOVW:     return OP3(0);  /* ld */
        case AMOVD:     return OP3(3);  /* ldd */

        case AMOVBU+AEND:
        case AMOVB+AEND:return OP3(5);  /* stb */

        case AMOVHU+AEND:
        case AMOVH+AEND:return OP3(6);  /* sth */

        case AMOVW+AEND:return OP3(4);  /* st */

        case AMOVD+AEND:return OP3(7);  /* std */

        case ASWAP:                     /* swap is symmetric */
        case ASWAP+AEND:return OP3(15);

        case ATAS:      return OP3(13); /* tas is really ldstub */

        case ABN:       return OPB(0);
        case ABE:       return OPB(1);
        case ABLE:      return OPB(2);
        case ABL:       return OPB(3);
        case ABLEU:     return OPB(4);
        case ABCS:      return OPB(5);
        case ABNEG:     return OPB(6);
        case ABVS:      return OPB(7);
        case ABA:       return OPB(8);
        case ABNE:      return OPB(9);
        case ABG:       return OPB(10);
        case ABGE:      return OPB(11);
        case ABGU:      return OPB(12);
        case ABCC:      return OPB(13);
        case ABPOS:     return OPB(14);
        case ABVC:      return OPB(15);

        case AFBA:      return OPFB(8);
        case AFBE:      return OPFB(9);
        case AFBG:      return OPFB(6);
        case AFBGE:     return OPFB(11);
        case AFBL:      return OPFB(4);
        case AFBLE:     return OPFB(13);
        case AFBLG:     return OPFB(2);
        case AFBN:      return OPFB(0);
        case AFBNE:     return OPFB(1);
        case AFBO:      return OPFB(15);
        case AFBU:      return OPFB(7);
        case AFBUE:     return OPFB(10);
        case AFBUG:     return OPFB(5);
        case AFBUGE:    return OPFB(12);
        case AFBUL:     return OPFB(3);
        case AFBULE:    return OPFB(14);

        case ATN:       return OPT(0);
        case ATE:       return OPT(1);
        case ATLE:      return OPT(2);
        case ATL:       return OPT(3);
        case ATLEU:     return OPT(4);
        case ATCS:      return OPT(5);
        case ATNEG:     return OPT(6);
        case ATVS:      return OPT(7);
        case ATA:       return OPT(8);
        case ATNE:      return OPT(9);
        case ATG:       return OPT(10);
        case ATGE:      return OPT(11);
        case ATGU:      return OPT(12);
        case ATCC:      return OPT(13);
        case ATPOS:     return OPT(14);
        case ATVC:      return OPT(15);

        case AFADDF:    return OPF1(65);
        case AFADDD:    return OPF1(66);
        case AFADDX:    return OPF1(67);
        case AFSUBF:    return OPF1(69);
        case AFSUBD:    return OPF1(70);
        case AFSUBX:    return OPF1(71);
        case AFMULF:    return OPF1(73);
        case AFMULD:    return OPF1(74);
        case AFMULX:    return OPF1(75);
        case AFDIVF:    return OPF1(77);
        case AFDIVD:    return OPF1(78);
        case AFDIVX:    return OPF1(79);

        case AFMOVF:    return OPF1(1);
        case AFNEGF:    return OPF1(5);
        case AFABSF:    return OPF1(9);

        case AFSQRTF:   return OPF1(41);
        case AFSQRTD:   return OPF1(42);
        case AFSQRTX:   return OPF1(43);

        case AFMOVWF:   return OPF1(196);
        case AFMOVWD:   return OPF1(200);
        case AFMOVWX:   return OPF1(204);
        case AFMOVFW:   return OPF1(209);
        case AFMOVDW:   return OPF1(210);
        case AFMOVXW:   return OPF1(211);
        case AFMOVFD:   return OPF1(201);
        case AFMOVFX:   return OPF1(205);
        case AFMOVDF:   return OPF1(198);
        case AFMOVDX:   return OPF1(206);
        case AFMOVXF:   return OPF1(199);
        case AFMOVXD:   return OPF1(203);

        case AFCMPF:    return OPF2(81);
        case AFCMPD:    return OPF2(82);
        case AFCMPX:    return OPF2(83);
        case AFCMPEF:   return OPF2(85);
        case AFCMPED:   return OPF2(86);
        case AFCMPEX:   return OPF2(87);

        case AUNIMP:    return 0;
        }
        diag("bad opcode %A", a);
        return 0;
}