#include        "l.h"

short   opa[20];
short   *op;

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)
               diag("entry not text: %s", s->name);
       return s->value;
}

void
asmb(void)
{
       Prog *p;
       long v;
       int a;
       short *op1;

       if(debug['v'])
               Bprint(&bso, "%5.2f asmb\n", cputime());
       Bflush(&bso);

       seek(cout, HEADR, 0);
       pc = INITTEXT;
       curp = firstp;
       for(p = firstp; p != P; p = p->link) {
               if(p->as == ATEXT)
                       curtext = p;
               if(p->pc != pc) {
                       if(!debug['a'])
                               print("%P\n", curp);
                       diag("phase error %lux sb %lux in %s", p->pc, pc, TNAME);
                       pc = p->pc;
               }
               curp = p;
               if(debug['a'])
                       Bprint(&bso, "%lux:%P\n", pc, curp);
               asmins(p);
               if(cbc < sizeof(opa))
                       cflush();
               for(op1 = opa; op1 < op; op1++) {
                       a = *op1;
                       *cbp++ = a >> 8;
                       *cbp++ = a;
               }
               a = 2*(op - opa);
               pc += a;
               cbc -= a;
               if(debug['a']) {
                       for(op1 = opa; op1 < op; op1++)
                               if(op1 == opa)
                                       Bprint(&bso, "\t\t%4ux", *op1 & 0xffff);
                               else
                                       Bprint(&bso, " %4ux", *op1 & 0xffff);
                       if(op != opa)
                               Bprint(&bso, "\n");
               }
       }
       cflush();
       switch(HEADTYPE) {
       case 0: /* this is garbage */
               seek(cout, rnd(HEADR+textsize, 8192), 0);
               break;
       case 1: /* plan9 boot data goes into text */
               seek(cout, rnd(HEADR+textsize, INITRND), 0);
               break;
       case 2: /* plan 9 */
               seek(cout, HEADR+textsize, 0);
               break;
       case 3: /* next boot */
               seek(cout, HEADR+rnd(textsize, INITRND), 0);
               break;
       case 4: /* preprocess pilot */
               seek(cout, HEADR+textsize, 0);
               break;
       }

       if(debug['v'])
               Bprint(&bso, "%5.2f datblk\n", cputime());
       Bflush(&bso);

       for(v = 0; v < datsize; v += sizeof(buf)-100) {
               if(datsize-v > sizeof(buf)-100)
                       datblk(v, sizeof(buf)-100);
               else
                       datblk(v, datsize-v);
       }

       symsize = 0;
       spsize = 0;
       lcsize = 0;

       Bflush(&bso);

       switch(HEADTYPE) {
       default:
               seek(cout, rnd(HEADR+textsize, 8192)+datsize, 0);
               break;
       case 1: /* plan9 boot data goes into text */
               seek(cout, rnd(HEADR+textsize, INITRND)+datsize, 0);
               break;
       case 2: /* plan 9 */
               seek(cout, HEADR+textsize+datsize, 0);
               break;
       case 3: /* next boot */
               seek(cout, HEADR+rnd(textsize, INITRND)+datsize, 0);
               break;
       }
       if(!debug['s']) {
               if(debug['v'])
                       Bprint(&bso, "%5.2f sym\n", cputime());
               asmsym();
       }
       Bflush(&bso);
       if(!debug['s']) {
               if(debug['v'])
                       Bprint(&bso, "%5.2f sp\n", cputime());
               asmsp();
       }
       Bflush(&bso);
       if(!debug['s']) {
               if(debug['v'])
                       Bprint(&bso, "%5.2f pc\n", cputime());
               asmlc();
       }
       cflush();

       if(debug['v'])
               Bprint(&bso, "%5.2f headr\n", cputime());
       Bflush(&bso);
       seek(cout, 0L, 0);
       switch(HEADTYPE) {
       default:
               lput(0x160L<<16);               /* magic and sections */
               lput(0L);                       /* time and date */
               lput(rnd(HEADR+textsize, 4096)+datsize);
               lput(symsize);                  /* nsyms */
               lput((0x38L<<16)|7L);           /* size of optional hdr and flags */
               lput((0413<<16)|0437L);         /* magic and version */
               lput(rnd(HEADR+textsize, 4096));        /* sizes */
               lput(datsize);
               lput(bsssize);
               lput(entryvalue());             /* va of entry */
               lput(INITTEXT-HEADR);           /* va of base of text */
               lput(INITDAT);                  /* va of base of data */
               lput(INITDAT+datsize);          /* va of base of bss */
               lput(~0L);                      /* gp reg mask */
               lput(0L);
               lput(0L);
               lput(0L);
               lput(0L);
               lput(~0L);                      /* gp value ?? */
               break;
       case 1: /* plan9 boot data goes into text */
               lput(0407);                     /* magic */
               lput(rnd(HEADR+textsize, INITRND)-HEADR+datsize);               /* sizes */
               lput(0);
               lput(bsssize);
               lput(symsize);                  /* nsyms */
               lput(entryvalue());             /* va of entry */
               lput(spsize);                   /* sp offsets */
               lput(lcsize);                   /* line offsets */
               break;
       case 2: /* plan 9 */
               lput(0407);                     /* magic */
               lput(textsize);                 /* sizes */
               lput(datsize);
               lput(bsssize);
               lput(symsize);                  /* nsyms */
               lput(entryvalue());             /* va of entry */
               lput(spsize);                   /* sp offsets */
               lput(lcsize);                   /* line offsets */
               break;
       case 3: /* next boot */
               /* header */
               lput(0xfeedfaceL);                      /* magic */
               lput(6);                                /* 68040 */
               lput(1);                                /* more 68040 */
               lput(5);                                /* file type 'boot' */
               lput(HEADTYPE);                         /* number commands */
               lput(HEADR-7*4);                        /* sizeof commands */
               lput(1);                                /* no undefineds */
               /* command 1 text */
               lput(1);                                /* command = 'segment' */
               lput(124);                              /* command size */
               s16put("__TEXT");
                       /* botch?? entryvalue() */
               lput(INITTEXT);                         /* va of start */
               lput(rnd(textsize, 8192));              /* va size */
               lput(HEADR);                            /* file offset */
               lput(rnd(textsize, 8192));              /* file size */
               lput(7);                                /* max prot */
               lput(7);                                /* init prot */
               lput(1);                                /* number of sections */
               lput(0);                                /* flags */
               /* text section */
               s16put("__text");
               s16put("__TEXT");
                       /* botch?? entryvalue() */
               lput(INITTEXT);                         /* va of start */
               lput(textsize);                         /* va size */
               lput(HEADR);                            /* file offset */
               lput(2);                                /* align */
               lput(0);                                /* reloff */
               lput(0);                                /* nreloc */
               lput(0);                                /* flags */
               lput(0);                                /* reserved1 */
               lput(0);                                /* reserved2 */
               /* command 1 data */
               lput(1);                                /* command = 'segment' */
               lput(192);                              /* command size */
               s16put("__DATA");
               lput(INITDAT);                          /* va of start */
               lput(rnd(datsize, 8192));               /* va size */
               lput(HEADR+rnd(textsize, 8192));        /* file offset */
               lput(rnd(datsize, 8192));               /* file size */
               lput(7);                                /* max prot */
               lput(7);                                /* init prot */
               lput(2);                                /* number of sections */
               lput(0);                                /* flags */
               /* data section */
               s16put("__data");
               s16put("__DATA");
               lput(INITDAT);                          /* va of start */
               lput(datsize);                          /* va size */
               lput(HEADR+rnd(textsize, 8192));        /* file offset */
               lput(2);                                /* align */
               lput(0);                                /* reloff */
               lput(0);                                /* nreloc */
               lput(0);                                /* flags */
               lput(0);                                /* reserved1 */
               lput(0);                                /* reserved2 */
               /* bss section */
               s16put("__bss");
               s16put("__DATA");
               lput(INITDAT+datsize);                  /* va of start */
               lput(bsssize);                          /* va size */
               lput(0);                                /* file offset */
               lput(2);                                /* align */
               lput(0);                                /* reloff */
               lput(0);                                /* nreloc */
               lput(1);                                /* flags = zero fill */
               lput(0);                                /* reserved1 */
               lput(0);                                /* reserved2 */
               /* command 2 symbol */
               lput(2);                                /* command = 'symbol' */
               lput(24);                               /* command size */
               lput(HEADR+rnd(textsize, INITRND)
                       +datsize);                      /* symoff */
               lput(symsize);                          /* nsyms */
               lput(spsize);                           /* sp offsets */
               lput(lcsize);                           /* line offsets */
               break;
       }
       cflush();
}

void
asmins(Prog *p)
{
       Optab *o;
       int t, a, b;
       long v;
       Prog *q;

       op = opa + 1;
       if(special[p->from.type])
       switch(p->from.type) {

       case D_CCR:
               if(p->as != AMOVW)
                       goto bad;
               a = asmea(p, &p->to);
               if((a & 0170) == 010)
                       goto bad;
               opa[0] = 0x42c0 | a;            /* mov from ccr */
               return;

       case D_SR:
               if(p->as != AMOVW)
                       goto bad;
               a = asmea(p, &p->to);
               if((a & 0170) == 010)
                       goto bad;
               opa[0] = 0x40c0 | a;            /* mov from sr */
               return;

       case D_USP:
               if(p->as != AMOVL)
                       goto bad;
               a = asmea(p, &p->to);
               if((a & 0170) == 010) {
                       opa[0] = 0x4e68|(a&7);  /* mov usp An */
                       return;
               }
               t = 0x800;
               goto movec1;

       case D_SFC:
               t = 0x000;
               goto movec1;

       case D_DFC:
               t = 0x001;
               goto movec1;

       case D_CACR:
               t = 0x002;
               goto movec1;

       case D_TC:
               t = 0x003;
               goto movec1;

       case D_ITT0:
               t = 0x004;
               goto movec1;

       case D_ITT1:
               t = 0x005;
               goto movec1;

       case D_DTT0:
               t = 0x006;
               goto movec1;

       case D_DTT1:
               t = 0x007;
               goto movec1;

       case D_VBR:
               t = 0x801;
               goto movec1;

       case D_CAAR:
               t = 0x802;
               goto movec1;

       case D_MSP:
               t = 0x803;
               goto movec1;

       case D_ISP:
               t = 0x804;
               goto movec1;

       case D_MMUSR:
               t = 0x805;
               goto movec1;

       case D_URP:
               t = 0x806;
               goto movec1;

       case D_SRP:
               t = 0x807;
               goto movec1;

       movec1:
               if(p->as != AMOVL)
                       goto bad;
               opa[0] = 0x4e7a;                        /* mov spc Dn */
               a = asmea(p, &p->to);
               b = a & 0170;
               if(b == 0 || b == 010) {
                       *op++ = (a<<12) | t;
                       return;
               }
               goto bad;

       case D_FPCR:
               t = 0xb000;
               goto movec3;

       case D_FPSR:
               t = 0xa800;
               goto movec3;

       case D_FPIAR:
               t = 0xa400;

       movec3:
               if(p->as != AMOVL)
                       goto bad;
               op++;
               a = asmea(p, &p->to);
               opa[0] = optab[AFMOVEL].opcode0 | a;
               opa[1] = t;
               return;
       }
       if(special[p->to.type])
       switch(p->to.type) {

       case D_CCR:
               if(p->as != AMOVW)              /* botch, needs and, eor etc. */
                       goto bad;
               a = asmea(p, &p->from);
               if((a & 0170) == 010)
                       goto bad;
               opa[0] = 0x44c0 | a;            /* mov to ccr */
               return;

       case D_SR:
               if(p->as != AMOVW)              /* botch, needs and, eor etc. */
                       goto bad;
               a = asmea(p, &p->from);
               if((a & 0170) == 010)
                       goto bad;
               opa[0] = 0x46c0 | a;            /* mov to sr */
               return;

       case D_USP:
               if(p->as != AMOVL)
                       goto bad;
               a = asmea(p, &p->from);
               if((a & 0170) == 010) {
                       opa[0] = 0x4e60|(a&7);  /* mov An usp */
                       return;
               }
               t = 0x800;
               goto movec2;

       case D_SFC:
               t = 0x000;
               goto movec2;

       case D_DFC:
               t = 0x001;
               goto movec2;

       case D_CACR:
               t = 0x002;
               goto movec2;

       case D_TC:
               t = 0x003;
               goto movec2;

       case D_ITT0:
               t = 0x004;
               goto movec2;

       case D_ITT1:
               t = 0x005;
               goto movec2;

       case D_DTT0:
               t = 0x006;
               goto movec2;

       case D_DTT1:
               t = 0x007;
               goto movec2;

       case D_VBR:
               t = 0x801;
               goto movec2;

       case D_CAAR:
               t = 0x802;
               goto movec2;

       case D_MSP:
               t = 0x803;
               goto movec2;

       case D_ISP:
               t = 0x804;
               goto movec2;

       case D_MMUSR:
               t = 0x805;
               goto movec2;

       case D_URP:
               t = 0x806;
               goto movec2;

       case D_SRP:
               t = 0x807;
               goto movec2;

       movec2:
               if(p->as != AMOVL)
                       goto bad;
               opa[0] = 0x4e7b;                        /* mov Dn spc */
               a = asmea(p, &p->from);
               b = a & 0170;
               if(b == 0 || b == 010) {
                       *op++ = (a<<12) | t;
                       return;
               }
               goto bad;

       case D_FPCR:
               t = 0x9000;
               goto movec4;

       case D_FPSR:
               t = 0x8800;
               goto movec4;

       case D_FPIAR:
               t = 0x8400;

       movec4:
               if(p->as != AMOVL)
                       goto bad;
               op++;
               a = asmea(p, &p->from);
               opa[0] = optab[AFMOVEL].opcode0 | a;
               opa[1] = t;
               return;
       }

       o = &optab[p->as];
       t = o->opcode0;
       switch(o->optype) {
       case 0:         /* pseudo ops */
               if(p->as != ATEXT && p->as != ANOP) {
                       if(!debug['a'])
                               print("%P\n", p);
                       diag("unimplemented instruction in %s", TNAME);
                       return;
               }
               op = opa;
               return;

       case 1:         /* branches */
               if(p->to.type != D_BRANCH)
                       goto bad;
               a = asmea(p, &p->to);
               /* hack to turn 3-word bsr into 2-word jsr */
               if(a == 0xff && p->as == ABSR &&
                  p->pcond->pc < 32768L && p->pcond->pc >= 0) {
                       op = opa + 1;
                       t = o->opcode1;
                       *op++ = p->pcond->pc;
                       break;
               }
               t |= a;
               break;

       case 2:         /* move */
               a = asmea(p, &p->from);
               b = asmea(p, &p->to);
               if((a & 0170) == 0110) { /* src quick */
                       t = o->opcode1;
                       if((b & 0170) != 0)
                               goto bad;
                       t |= a >> 7;
                       t |= b << 9;
                       break;
               }
               t |= a;
               t |= (b&7) << 9;
               t |= (b&070) << 3;
               break;

       case 3:         /* add */
               a = asmea(p, &p->from);
               b = asmea(p, &p->to);
               if((a & 0170) == 0110) { /* src quick */
                       t = o->opcode1;
                       t |= (a&01600) << 2;
                       t |= b;
                       break;
               }
               if((b & 0170) == 0) { /* dst Dn */
                       t |= a;
                       t |= (b & 7) << 9;
                       break;
               }
               if((b & 0170) == 010) { /* dst An */
                       if((t & 0xc0) == 0)
                               goto bad;
                       t = o->opcode2;
                       t |= a;
                       t |= (b & 7) << 9;
                       break;
               }
               if((a & 0170) == 0) { /* src Dn */
                       t |= 0x100;
                       t |= (a & 7) << 9;
                       t |= b;
                       break;
               }
               if((a & 0177) == 074) { /* src immed */
                       t = o->opcode3;
                       t |= b;
                       break;
               }
               goto bad;

       case 4:         /* no operands */
               break;

       case 5:         /* tst */
               t |= asmea(p, &p->to);
               if((t&0170) == 010)
                       goto bad;
               break;

       case 6:         /* lea */
               a = asmea(p, &p->from);
               b = asmea(p, &p->to);
               if((b & 0170) != 010)
                       goto bad;
               t |= a;
               t |= (b & 7) << 9;
               break;

       case 7:         /* cmp */
               b = asmea(p, &p->to);
               a = asmea(p, &p->from);
               if((a & 0170) == 010) { /* dst An */
                       t = o->opcode1;
                       if(t == 0)      /* cmpb illegal */
                               goto bad;
                       t |= 0xc0;
                       t |= b;
                       t |= (a & 7) << 9;
                       break;
               }
               if((b & 0177) == 074) { /* src immed */
                       t = o->opcode2;
                       t |= a;
                       break;
               }
               if((a & 0170) == 0) {   /* dst Dn */
                       t |= b;
                       t |= (a&7) << 9;
                       break;
               }
               if((b&0170) == 030 && (a&0170) == 030) { /* (A)+,(A)+ */
                       t = o->opcode3;
                       t |= b & 7;
                       t |= (a & 7) << 9;
                       break;
               }
               goto bad;

       case 8:         /* svc */
               *op++ = optab[ARTS].opcode0;
               break;

       case 9:         /* and */
               a = asmea(p, &p->from);
               b = asmea(p, &p->to);
               if((a & 0170) == 010)
                       goto bad;
               if((b & 0170) == 0) { /* dst Dn */
                       t |= a;
                       t |= (b&7) << 9;
                       break;
               }
               if((a & 0170) == 0) { /* src Dn */
                       t = o->opcode1;
                       t |= b;
                       t |= (a&7) << 9;
                       break;
               }
               if((a & 0177) == 074) { /* src immed */
                       t = o->opcode2;
                       t |= b;
                       break;
               }
               goto bad;

       case 10:        /* eor */
               a = asmea(p, &p->from);
               b = asmea(p, &p->to);
               if((a & 0170) == 010)
                       goto bad;
               if((a & 0170) == 0) { /* src Dn */
                       t |= b;
                       t |= (a&7) << 9;
                       break;
               }
               if((a & 0177) == 074) { /* src immed */
                       t = o->opcode1;
                       t |= b;
                       break;
               }
               goto bad;

       case 11:        /* ext */
               b = asmea(p, &p->to);
               if((b & 0170) == 0) { /* dst Dn */
                       t |= b;
                       break;
               }
               goto bad;

       case 12:        /* shift */
               a = asmea(p, &p->from);
               b = asmea(p, &p->to);
               if((b & 0170) == 0) { /* dst Dn */
                       if((a & 0177) == 0110) { /* src quick */
                               t |= (a & 01600) << 2;
                               t |= b;
                               break;
                       }
                       if((a & 0170) == 0) { /* src Dn */
                               t |= 0x20;
                               t |= a << 9;
                               t |= b;
                               break;
                       }
                       goto bad;
               }
               goto bad;

       case 13:        /* mul, div short */
               a = asmea(p, &p->from);
               b = asmea(p, &p->to);
               if((b & 0170) == 0) { /* dst Dn */
                       if((a & 0170) == 010)
                               goto bad;
                       t |= a;
                       t |= b << 9;
                       break;
               }
               goto bad;

       case 14:        /* mul, div long */
               *op++ = o->opcode1;
               a = asmea(p, &p->from);
               b = asmea(p, &p->to);
               if((b & 0170) == 0) { /* dst Dn */
                       if((a & 0170) == 010)
                               goto bad;
                       t |= a;
                       opa[1] |= b << 12;
                       opa[1] |= b+1;
                       break;
               }
               goto bad;

       case 15:        /* dec and branch */
               if(p->to.type != D_BRANCH)
                       goto bad;
               v = p->pcond->pc - p->pc - 2;
               if(v < -32768L || v >= 32768L)
                       goto bad;
               *op++ = v;
               a = asmea(p, &p->from);
               if((a & 0170) != 0)
                       goto bad;
               t |= a;
               break;

       case 16:        /* fmove */
               *op++ = o->opcode1;
               a = asmea(p, &p->from);
               b = asmea(p, &p->to);
               if((a & 0170) == 0100) { /* src Fn */
                       if((b & 0170) == 0100) { /* both Fn */
                               opa[1] |= (a&7) << 10;
                               opa[1] |= (b&7) << 7;
                               break;
                       }
                       t |= b;
                       opa[1] = o->opcode2;
                       opa[1] |= (a&7) << 7;
                       break;
               }
               if((b & 0170) != 0100) /* dst Fn */
                       goto bad;
               t |= a;
               opa[1] = o->opcode3;
               opa[1] |= (b&7) << 7;
               break;

       case 17:        /* floating ea,Fn */
               *op++ = o->opcode1;
               a = asmea(p, &p->from);
               b = asmea(p, &p->to);
               if((b & 0170) != 0100) /* dst Fn */
                       goto bad;
               if((a & 0170) == 0100) { /* both Fn */
                       opa[1] |= (a&7) << 10;
                       opa[1] |= (b&7) << 7;
                       break;
               }
               t |= a;
               opa[1] = o->opcode2;
               opa[1] |= (b&7) << 7;
               break;

       case 18:        /* floating branchs */
               if(p->to.type != D_BRANCH)
                       goto bad;
               v = p->pcond->pc - p->pc - 2;
               if(v < -32768L || v >= 32768L)
                       goto bad;
               *op++ = v;
               break;

       case 19:        /* floating dec and branch */
               if(p->to.type != D_BRANCH)
                       goto bad;
               *op++ = o->opcode1;
               v = p->pcond->pc - p->pc - 2;
               if(v < -32768L || v >= 32768L)
                       goto bad;
               *op++ = v;
               a = asmea(p, &p->from);
               if((a & 0170) != 0)
                       goto bad;
               t |= a;
               break;

       case 20:        /* ftst ea */
               *op++ = o->opcode1;
               if(p->from.type != D_NONE)
                       goto bad;
               a = asmea(p, &p->to);
               if((a & 0170) == 0100) { /* Fn */
                       opa[1] |= (a&7) << 10;
                       break;
               }
               t |= a;
               opa[1] = o->opcode2;
               break;

       case 21:        /* fneg */
               *op++ = o->opcode1;
               if(p->from.type == D_NONE) {
                       b = asmea(p, &p->to);
                       a = b;
               } else {
                       a = asmea(p, &p->from);
                       b = asmea(p, &p->to);
               }
               if((b & 0170) != 0100) /* dst Fn */
                       goto bad;
               if((a & 0170) == 0100) { /* both Fn */
                       opa[1] |= (a&7) << 10;
                       opa[1] |= (b&7) << 7;
                       break;
               }
               t |= a;
               opa[1] = o->opcode2;
               opa[1] |= (b&7) << 7;
               break;

       case 22:        /* floating cmp Fn,ea */
               *op++ = o->opcode1;
               a = asmea(p, &p->from);
               b = asmea(p, &p->to);
               if((a & 0170) != 0100) /* dst Fn */
                       goto bad;
               if((b & 0170) == 0100) { /* both Fn */
                       opa[1] |= (b&7) << 10;
                       opa[1] |= (a&7) << 7;
                       break;
               }
               t |= b;
               opa[1] = o->opcode2;
               opa[1] |= (a&7) << 7;
               break;

       case 23:        /* word, long */
               op = opa;
               a = asmea(p, &p->to);
               if(a == ((7<<3)|4))
                       return;
               if(a == ((7<<3)|1)) {
                       if(p->as == AWORD) {
                               op = opa;
                               *op++ = opa[1];
                       }
                       return;
               }
               if(a == ((7<<3)|0)) {
                       if(p->as == ALONG) {
                               *op++ = opa[0];
                               opa[0] = 0;
                       }
                       return;
               }
               goto bad;

       case 24:        /* bit field */
               a = ((p->to.field&31)<<6) | (p->from.field&31);
               if(p->as == ABFINS) {
                       b = asmea(p, &p->from);
                       if((b&0170) != 0)
                               goto bad;
                       a |= b<<12;
                       *op++ = a;
                       a = asmea(p, &p->to);
               } else {
                       if(p->to.type != D_NONE) {
                               b = asmea(p, &p->to);
                               if((b&0170) != 0)
                                       goto bad;
                               a |= b<<12;
                       }
                       *op++ = a;
                       a = asmea(p, &p->from);
               }
               t |= a;
               a &= 0170;
               if(a == 010 || a == 030 || a == 040 || a == 074)
                       goto bad;
               break;

       case 25:        /* movem */
               if(p->from.type == D_CONST) { /* registers -> memory */
                       asmea(p, &p->from);
                       a = asmea(p, &p->to);
                       if(a == 074)
                               goto bad;
                       b = a & 0170;
                       if(b == 000 || b == 010 || b == 030)
                               goto bad;
                       t |= a;
                       break;
               }
               if(p->to.type == D_CONST) { /* memory -> registers */
                       t |= 0x400;
                       asmea(p, &p->to);
                       a = asmea(p, &p->from);
                       if(a == 074)
                               goto bad;
                       b = a & 0170;
                       if(b == 000 || b == 010 || b == 040)
                               goto bad;
                       t |= a;
                       break;
               }
               goto bad;

       case 26:        /* chk */
               a = asmea(p, &p->from);
               if((a&0170) == 010)
                       goto bad;
               b = asmea(p, &p->to);
               if((b&0170) != 0)
                       goto bad;
               t |= a;
               t |= b<<9;
               break;

       case 27:        /* btst */
               a = asmea(p, &p->from);
               if(a == 074) {
                       t = o->opcode1;
               } else
               if((a&0170) != 0)
                       goto bad;
               b = asmea(p, &p->to);
               if(b == 074 || (b&0170) == 010)
                       goto bad;
               t |= b;
               break;

       case 28:        /* fmovem */
               if(p->from.type == D_CONST) { /* registers -> memory */
                       b = p->from.offset & 0xff;
                       b |= 0xf000;            /* control or postinc */
                       *op++ = b;
                       a = asmea(p, &p->to);
                       if(a == 074)
                               goto bad;
                       b = a & 0170;
                       if(b == 000 || b == 010 || b == 030)
                               goto bad;
                       if(b == 040)
                               op[-1] &= ~0x1000;      /* predec */
                       t |= a;
                       break;
               }
               if(p->to.type == D_CONST) { /* memory -> registers */
                       b = p->to.offset & 0xff;
                       b |= 0xd000;            /* control or postinc */
                       *op++ = b;
                       a = asmea(p, &p->from);
                       if(a == 074)
                               goto bad;
                       b = a & 0170;
                       if(b == 000 || b == 010 || b == 040)
                               goto bad;
                       t |= a;
                       break;
               }
               goto bad;

       case 29:        /* fmovemc */
               if(p->from.type == D_CONST) { /* registers -> memory */
                       b = (p->from.offset & 0x7) << 10;
                       b |= 0xa000;
                       *op++ = b;
                       a = asmea(p, &p->to);
                       if(a == 074)
                               goto bad;
                       b = a & 0170;
                       if(b == 000 || b == 010 || b == 030)
                               goto bad;
                       t |= a;
                       break;
               }
               if(p->to.type == D_CONST) { /* memory -> registers */
                       b = (p->to.offset & 0x7) << 10;
                       b |= 0x8000;
                       *op++ = b;
                       a = asmea(p, &p->from);
                       if(a == 074)
                               goto bad;
                       b = a & 0170;
                       if(b == 000 || b == 010 || b == 040)
                               goto bad;
                       t |= a;
                       break;
               }
               goto bad;

       case 30:        /* trap */
               if(p->to.type == D_CONST) {
                       t |= p->to.offset & 0xf;
                       break;
               }
               goto bad;

       case 31:        /* chk2, cmp2 */
               b = asmea(p, &p->to);
               a = b & 0170;
               if(a == 000 || a == 010) {
                       *op++ = o->opcode1 | (b << 12);
                       t |= asmea(p, &p->from);
                       break;
               }
               goto bad;

       case 32:        /* casew */
               /* jmp (0,pc,r0.w*1) */
               casepc = p->pc;
               *op++ = o->opcode1;
               break;

       case 33:        /* bcase */
               q = copyp(p);
               q->as = ADATA;
               q->to.type = D_CONST;
               q->to.offset = p->pcond->pc - casepc - 2;
               q->from.displace = 2;
               q->link = datap;
               datap = q;
               if(debug['a'])
                       Bprint(&bso, "%P\n", q);
               op = opa;
               return;

       case 34:        /* moves */
               op++;
               a = asmea(p, &p->from);
               b = a & 0170;
               if(b == 0 || b == 010) {
                       opa[1] = (a << 12) | 0x800;
                       b = asmea(p, &p->to);
                       a = b & 0170;
                       if(a == 0 || a == 010)
                               goto bad;
                       t |= b;
                       break;
               }
               t |= a;
               b = asmea(p, &p->to);
               a = b & 0170;
               if(a != 0 && a != 010)
                       goto bad;
               opa[1] = (b << 12);
               break;

       case 35:        /* swap */
               a = asmea(p, &p->to);
               if((a & 0170) == 0) {
                       t |= a;
                       break;
               }
               goto bad;
       }
       opa[0] = t;
       return;

bad:
       if(!debug['a'])
               print("%P\n", p);
       diag("bad combination of addressing in %s", TNAME);
       opa[0] = 0;
}

int
asmea(Prog *p, Adr *a)
{
       Optab *o;
       int f, t, r, i;
       short *top;
       long v;

       if(a->index != D_NONE)
               goto index;
       t = a->type;
       r = simple[t];
       v = a->offset;
       if(r != 0177) {
               if(v == 0)
                       return r;
               if((r & 070) != 020)
                       return r;
               if(v >= -32768L && v < 32768L) {
                       *op++ = v;
                       return t-D_A0-I_INDIR+050;      /* d(Ax) */
               }
               *op++ = 0x170;                  /* is, no indirect */
               *op++ = v>>16;
               *op++ = v;
               return t-D_A0-I_INDIR+060;      /* (d,Ax) */
       }
       f = 0;
       if(a == &p->from)
               f++;
       o = &optab[p->as];
       switch(t) {
       case D_TOS:
               if(f) {
                       if(o->srcsp)
                               return (3<<3) | 7;      /* (A7)+ */
               } else
                       if(o->dstsp)
                               return (4<<3) | 7;      /* -(A7) */
               return (2<<3) | 7;                      /* (A7) */

       case D_BRANCH:
               v = p->pcond->pc - p->pc - 2;
               if(v < -32768L || v >= 32768L) {
                       *op++ = v>>16;
                       *op++ = v;
                       return 0xff;
               }
               if(v < -128 || v >= 128 || p->mark == 4) {
                       *op++ = v;
                       return 0;
               }
               return v & 0xff;

       case I_ADDR|D_STATIC:
       case I_ADDR|D_EXTERN:
               t = a->sym->type;
               if(t == 0 || t == SXREF) {
                       diag("undefined external: %s in %s",
                               a->sym->name, TNAME);
                       a->sym->type = SDATA;
               }
               v = a->sym->value + a->offset;
               if(t != STEXT)
                       v += INITDAT;

       case D_CONST:
               switch(f? o->srcsp: o->dstsp) {
               case 4:
                       *op++ = v>>16;

               case 2:
                       *op++ = v;
                       break;

               default:
                       diag("unknown srcsp asmea in %s", TNAME);
               }
               return (7<<3) | 4;

       case D_FCONST:
               r = f? o->srcsp: o->dstsp;
               for(i=0; i<r; i++)
                       ((char*)op)[i] = gnuxi(&a->ieee, i, r);
               op += r/2;
               return (7<<3) | 4;

       case D_QUICK:
               v = a->offset & 0xff;
               return 0110 | (v<<7);

       case D_STACK:
       case D_AUTO:
       case D_PARAM:
               if(v == 0)
                       return (2<<3) | 7;      /* (A7) */
               if(v >= -32768L && v < 32768L) {
                       *op++ = v;
                       return (5<<3) | 7;      /* d(A7) */
               }
               *op++ = 0x170;                  /* is, no indirect */
               *op++ = v>>16;
               *op++ = v;
               return (6<<3) | 7;              /* (d,A7) */

       case I_INDIR|D_CONST:
               goto adr;

       case D_STATIC:
       case D_EXTERN:
               t = a->sym->type;
               if(t == 0 || t == SXREF) {
                       diag("undefined external: %s in %s",
                               a->sym->name, TNAME);
                       a->sym->type = SDATA;
               }
               if(t == STEXT) {
                       v = a->sym->value + a->offset;
                       *op++ = v>>16;
                       *op++ = v;
                       return (7<<3) | 1;
               }
               v = a->sym->value + a->offset - A6OFFSET;
               if(debug['6']) {
                       v += INITDAT + A6OFFSET;
                       goto adr;
               }
               if(v == 0)
                       return (2<<3) | 6;
               if(v >= -32768L && v < 32768L) {
                       *op++ = v;
                       return (5<<3) | 6;
               }
               v += INITDAT + A6OFFSET;

       adr:
               if(v >= -32768L && v < 32768L) {
                       *op++ = v;
                       return (7<<3) | 0;
               }
               *op++ = v>>16;
               *op++ = v;
               return (7<<3) | 1;
       }
       if(!debug['a'])
               print("%P\n", p);
       diag("unknown addressing mode: %d in %s", t, TNAME);
       return 0;

index:
       top = op++;
       t = a->index & D_MASK;
       f = (a->scale & 7) << 9;        /* w/l scale */
       f |= (t & 7) << 12;             /* register */
       if(t < D_R0 || t >= D_R0+8) {
               if(t >= D_A0 && t < D_A0+8) {
                       f |= 0x8000;    /* d/a */
               } else
               if(t == D_NONE)
                       f = 0x40;       /* is */
               else
                       goto bad;
       }

       t = a->type;
       r = t & 7;
       v = a->offset;
       if(t < (I_INDIR|D_A0) || t >= (I_INDIR|(D_A0+8)))
       switch(t) {
       default:
               goto bad;

       case I_INDIR|D_NONE:
               f |= 0x80;              /* bs */
               r = 0;
               break;

       case D_AUTO:
       case D_PARAM:
       case D_STACK:
               r = 7;
               break;

       case D_STATIC:
       case D_EXTERN:
               t = a->sym->type;
               if(t == 0 || t == SXREF) {
                       diag("undefined external: %s in %s",
                               a->sym->name, TNAME);
                       a->sym->type = SDATA;
               }
               r = 6;
               v += a->sym->value - A6OFFSET;
               if(t == STEXT) {
                       f |= 0x80;      /* bs */
                       r = 0;
                       v += A6OFFSET;
                       goto bdlong;
               }
               if(debug['6']) {
                       f |= 0x80;      /* bs */
                       r = 0;
                       v += INITDAT + A6OFFSET;
               }
       }
       if(v == 0)
               f |= 0x10;              /* bd size = null */
       else
       if(v >= -32768L && v < 32768L) {
               f |= 0x20;              /* bd size = word */
               *op++ = v;
       } else {
       bdlong:
               f |= 0x30;              /* bd size = long */
               *op++ = v>>16;
               *op++ = v;
       }
       v = a->displace;
       t = a->index & I_MASK;
       if(t != I_INDEX1) {             /* non-memory index */
               if(t == I_INDEX2)
                       f |= 5;         /* post indexing */
               else
               if(t == I_INDEX3)
                       f |= 1;         /* pre indexing */
               else
                       goto bad;
       }
       if(v != 0) {
               if(v >= -32768L && v < 32768L) {
                       f++;            /* is size = word */
                       *op++ = v;
               } else {
                       f += 2;         /* is size = long */
                       *op++ = v>>16;
                       *op++ = v;
               }
       }
       *top = f | 0x100;
       return (6<<3) | r;

bad:
       if(!debug['a'])
               print("%P\n", p);
       diag("bad operand in %s", TNAME);
       return 0;
}

void
lput(long l)
{

       CPUT(l>>24)
       CPUT(l>>16)
       CPUT(l>>8)
       CPUT(l)
}

void
s16put(char *n)
{
       char name[16];
       int i;

       strncpy(name, n, sizeof(name));
       for(i=0; i<sizeof(name); i++)
               CPUT(name[i])
}

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
datblk(long s, long n)
{
       Prog *p;
       char *cast;
       long l, fl, j;
       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->from.displace;
               i = 0;
               if(l < 0) {
                       if(l+c <= 0)
                               continue;
                       while(l < 0) {
                               l++;
                               i++;
                       }
               }
               if(l >= n)
                       continue;
               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) {
               case D_FCONST:
                       switch(c) {
                       default:
                       case 4:
                               fl = ieeedtof(&p->to.ieee);
                               cast = (char*)&fl;
                               if(debug['a'] && i == 0) {
                                       Bprint(&bso, "%lux:%P\n\t\t", l+s+INITDAT, curp);
                                       for(j=0; j<c; j++)
                                               Bprint(&bso, "%.2ux", cast[fnuxi8[j+4]] & 0xff);
                                       Bprint(&bso, "\n");
                               }
                               for(; i<c; i++) {
                                       buf.dbuf[l] = cast[fnuxi8[i+4]];
                                       l++;
                               }
                               break;
                       case 8:
                               cast = (char*)&p->to.ieee;
                               if(debug['a'] && i == 0) {
                                       Bprint(&bso, "%lux:%P\n\t\t", l+s+INITDAT, curp);
                                       for(j=0; j<c; j++)
                                               Bprint(&bso, "%.2ux", cast[fnuxi8[j]] & 0xff);
                                       Bprint(&bso, "\n");
                               }
                               for(; i<c; i++) {
                                       buf.dbuf[l] = cast[fnuxi8[i]];
                                       l++;
                               }
                               break;
                       }
                       break;

               case D_SCONST:
                       if(debug['a'] && i == 0) {
                               Bprint(&bso, "%lux:%P\n\t\t", l+s+INITDAT, curp);
                               for(j=0; j<c; j++)
                                       Bprint(&bso, "%.2ux", p->to.scon[j] & 0xff);
                               Bprint(&bso, "\n");
                       }
                       for(; i<c; i++) {
                               buf.dbuf[l] = p->to.scon[i];
                               l++;
                       }
                       break;
               default:
                       fl = p->to.offset;
                       if(p->to.sym) {
                               if(p->to.sym->type == STEXT)
                                       fl += p->to.sym->value;
                               if(p->to.sym->type == SDATA)
                                       fl += p->to.sym->value + INITDAT;
                               if(p->to.sym->type == SBSS)
                                       fl += p->to.sym->value + INITDAT;
                       }

                       cast = (char*)&fl;
                       switch(c) {
                       default:
                               diag("bad nuxi %d %d\n%P", c, i, curp);
                               break;
                       case 1:
                               if(debug['a'] && i == 0) {
                                       Bprint(&bso, "%lux:%P\n\t\t", l+s+INITDAT, curp);
                                       for(j=0; j<c; j++)
                                               Bprint(&bso, "%.2ux",cast[inuxi1[j]] & 0xff);
                                       Bprint(&bso, "\n");
                               }
                               for(; i<c; i++) {
                                       buf.dbuf[l] = cast[inuxi1[i]];
                                       l++;
                               }
                               break;
                       case 2:
                               if(debug['a'] && i == 0) {
                                       Bprint(&bso, "%lux:%P\n\t\t", l+s+INITDAT, curp);
                                       for(j=0; j<c; j++)
                                               Bprint(&bso, "%.2ux",cast[inuxi2[j]] & 0xff);
                                       Bprint(&bso, "\n");
                               }
                               for(; i<c; i++) {
                                       buf.dbuf[l] = cast[inuxi2[i]];
                                       l++;
                               }
                               break;
                       case 4:
                               if(debug['a'] && i == 0) {
                                       Bprint(&bso, "%lux:%P\n\t\t", l+s+INITDAT, curp);
                                       for(j=0; j<c; j++)
                                               Bprint(&bso, "%.2ux",cast[inuxi4[j]] & 0xff);
                                       Bprint(&bso, "\n");
                               }
                               for(; i<c; i++) {
                                       buf.dbuf[l] = cast[inuxi4[i]];
                                       l++;
                               }
                               break;
                       }
                       break;
               }
       }
       write(cout, buf.dbuf, n);
}

int
gnuxi(Ieee *d, int i, int c)
{
       char *p;
       long l;

       switch(c) {
       default:
               diag("bad nuxi %d %d\n%P", c, i, curp);
               return 0;

               /*
                * 2301 vax
                * 0123 68k
                */
       case 4:
               l = ieeedtof(d);
               p = (char*)&l;
               i = gnuxi8[i+4];
               break;

               /*
                * 67452301 vax
                * 45670123 68k
                */
       case 8:
               p = (char*)d;
               i = gnuxi8[i];
               break;
       }
       return p[i];
}

long
rnd(long v, long r)
{
       long c;

       if(r <= 0)
               return v;
       v += r - 1;
       c = v % r;
       if(c < 0)
               c += r;
       v -= c;
       return v;
}