/* cpu.c example cpu-description file */
/* (c) in 2013 by Volker Barthelmann */

#include "vasm.h"

char *cpu_copyright="VideoCore IV cpu backend 0.1 (c) in 2013 Volker Barthelmann";
char *cpuname="vidcore";

mnemonic mnemonics[]={
#include "opcodes.h"
};

int mnemonic_cnt=sizeof(mnemonics)/sizeof(mnemonics[0]);

int bitsperbyte=8;
int bytespertaddr=4;

static char *ccs[]={"eq","ne","cs","cc","mi","pl","vs","vc",
		    "hi","ls","ge","lt","gt","le","???","f"};

static char *vflags[]={"all","none","ifz","ifnz","ifn","ifnn","ifc","ifnc"};

static char *accs[]={
  "???", "sub", "wba", "???", "clra", "???", "???", "???",
  "sign", "???", "???", "???", "???", "???", "???", "???",
  "high", "???", "???", "???", "???", "???", "???", "???",
  "???", "???", "???", "???", "???", "???", "???", "???",
  "uadd", "usub", "uacc", "udec", "???", "???", "???", "???",
  "sadd", "ssub", "sacc", "sdec", "???", "???", "???", "???",
  "uaddh", "usubh", "uacch", "udech", "???", "???", "???", "???",
  "saddh", "ssubh", "sacch", "sdech", "???", "???", "???", "???"};

static char *sru[]={
  "sumu", "sums", "???", "imin", "???", "imax", "???", "max"};

static size_t oplen(int op)
{
  if(op<=EN_MEMDISP16)
    return 2;
  else if(op<=EN_ARITHI32)
    return 4;
  else if(op<=EN_VARITHI48)
    return 6;
  else if(op<=EN_ADDCMPB64)
    return 8;
  else if(op<=EN_VARITHI80)
    return 10;
  ierror(0);
}

static char *skip_reg(char *s,int *reg)
{
  int r=-1;
  char* orig=s;
  if(s[0]=='p'&&s[1]=='c'){
    *reg=31;
    return s+2;
  }
  if(s[0]=='s'&&s[1]=='r'){
    *reg=30;
    return s+2;
  }
  if(s[0]=='l'&&s[1]=='r'){
    *reg=26;
    return s+2;
  }
  if(s[0]=='s'&&s[1]=='p'){
    *reg=25;
    return s+2;
  }
  if(*s!='r'){
    return s;
  }
  s++;
  if(*s<'0'||*s>'9'){
    return orig;
  }
  r=*s++-'0';
  if(*s>='0'&&*s<='9')
    r=10*r+*s++-'0';
  if(isalnum(*s)||*s=='_'){
    return orig;
  }
  *reg=r;
  return s;
}

int parse_operand(char *p,int len,operand *op,int req)
{
  int r;
  char *s,*start=p;

  op->type=-1;
  p=skip(p);

  if(req>=OP_REG&&req<=OP_LR){
    int reg;
    s=skip_reg(p,&r);
    if(s!=p){
      if((req==OP_REG&&r<=31)||
	 (req==OP_SREG&&r<=15)||
	 (req==OP_PC&&r==31)||
	 (req==OP_LR&&r==26)||
	 (req==OP_MREG&&(r==0||r==6||r==16||r==24))){
	op->type=OP_REG;
	op->reg=r;
      }else
	return 0;
    }else
      return 0;
    p=skip(s);
  }else if(req==OP_MREG){
    int first;
    s=skip_reg(p,&r);
    if(s==p)
      return 0;
    if(r==0)
      op->dreg=0;
    else if(r==6)
      op->dreg=1;
    else if(r==16)
      op->dreg=2;
    else if(r==24)
      op->dreg=3;
    else
      return 0;
    s=skip(s);
    if(*s!='-')
      return 0;
    p=skip(s+1);
    first=r;
    s=skip_reg(p,&r);
    if(s==p) 
      return 0;
    p=skip(s);
    op->reg=(r-first)&31;
  }else if(req==OP_IND){
    p=skip(p);
    if(*p!='(') return 0;
    p=skip(p+1);
    s=skip_reg(p,&r);
    if(s==p) return 0;
    if(r>15) return 0;
    p=skip(s);
    op->reg=r;
    if(*p!=')') return 0;
    p=skip(p+1);
    op->type=OP_IND;
  }else if(req==OP_VIND){
    int nodisp=0;
    p=skip(p);
    if(*p=='('){
      char *m;
      m=s=skip(p+1);
      s=skip_reg(s,&r);
      if((m!=s)||(*s=='0')) nodisp=1;
    }
    if(!nodisp){
      op->offset=parse_expr(&p);
      if(!op->offset) return 0;
    }else
      op->offset=0;
    if(*p!='(') return 0;
    p=skip(p+1);
    s=skip_reg(p,&r);
    if (s==p){
      /* Special case: Allow '0' instead of a register */
      if (*p=='0'){
	p=skip(p+1);
	op->reg=15;
      }
      else return 0;
    }else{
      if(r>14) return 0;
      p=skip(s);
      op->reg=r;
    }
    op->dreg=15;
    if(*p=='+'&&p[1]=='='){
      p=skip(p+2);
      s=skip_reg(p,&r);
      if(s==p) return 0;
      if(r>14) return 0;
      p=skip(s);
      op->dreg=r;
    }
    if(*p!=')') return 0;
    p=skip(p+1);
    op->type=OP_IND;
  }else if(req==OP_PREDEC){
    p=skip(p);
    if(p[0]!='-'||p[1]!='-') return 0;
    p=skip(p+2);
    if(*p!='(') return 0;
    p=skip(p+1);
    s=skip_reg(p,&r);
    if(s==p) return 0;
    p=skip(s);
    if(*p!=')') return 0;
    p=skip(p+1);
    op->reg=r;
    op->type=OP_PREDEC;
  }else if(req==OP_POSTINC){
    p=skip(p);
    if(*p!='(') return 0;
    p=skip(p+1);
    s=skip_reg(p,&r);
    if(s==p) return 0;
    p=skip(s);
    if(*p!=')') return 0;
    p=skip(p+1);
    if(*p++!='+') return 0;
    if(*p!='+') return 0;
    p=skip(p+1);
    op->reg=r;
    op->type=OP_POSTINC;
  }else if(req==OP_REGIND){
    p=skip(p);
    if(*p!='(') return 0;
    p=skip(p+1);
    s=skip_reg(p,&op->reg);
    if(s==p) return 0;  
    p=skip(s);
    if(*p!=',') return 0;
    p=skip(p+1);
    s=skip_reg(p,&op->dreg);
    if(s==p) return 0;
    p=skip(s);
    if(*p!=')') return 0;
    op->type=OP_REGIND;
    p=skip(p+1);
  }else if(req==OP_IMMIND||
	   req==OP_IMMINDSP||
	   req==OP_IMMINDSD||
	   req==OP_IMMINDR0||
	   req==OP_IMMINDS||
	   req==OP_IMMINDPC){
    op->offset=0;
    if(*p=='('){
      char *p2;
      p2=skip(p+1);
      s=skip_reg(p2,&r);
      if(s!=p2){
	p=skip(s);
	if(*p==')'){
	  p=skip(p+1);
	  op->offset=number_expr(0);
	}
      }
    }
    if(!op->offset){
      op->offset=parse_expr(&p);
      if(!op->offset) return 0;
      p=skip(p);
      if(*p!='(') return 0;
      p=skip(p+1);
      s=skip_reg(p,&r);
      if(s==p) return 0;
      p=skip(s);
      if(*p!=')') return 0;
    }
    if(req==OP_IMMINDSP&&r!=25) return 0;
    if(req==OP_IMMINDPC&&r!=31) return 0;
    if(req==OP_IMMINDSD&&r!=24) return 0;
    if(req==OP_IMMINDR0&&r!=0) return 0;
    if(req==OP_IMMINDS&&r>15) return 0;
    op->reg=r;
    op->type=OP_IMMIND;
    p=skip(p+1);
  }else if(req>=OP_IMM4&&req<=OP_IMM32&&skip_reg(p,&r)==p){
    op->type=OP_IMM32;
    op->offset=parse_expr(&p);
    simplify_expr(op->offset);
    p=skip(p);
  }else if((req==OP_ABS||req==OP_REL)&&skip_reg(p,&r)==p){
    op->type=req;
    op->offset=parse_expr(&p);
    simplify_expr(op->offset);
    p=skip(p);
  }else if(req==OP_VREG||req==OP_VREGM||req==OP_VREGMM||req==OP_VREGA80){
    s=skip_reg(p,&r);
    op->offset=0;
    if(s!=p){
      if (*s=='+'||*s=='-'){
        if(req==OP_VREG) return 0;
        op->offset=parse_expr(&s);
      }
      p=skip(s);
      op->type=OP_VREG;
      if(req==OP_VREG){
	op->reg=(0xE<<6)|r;
	op->dreg=0x3c;
      }else{
	op->reg=(0xE<<6);
	op->dreg=r<<2;
      }
    }else if(*p=='-'){
      p=skip(p+1);
      op->type=OP_VREG;
      op->reg=(0xF<<6);
      op->dreg=0x3c;
    }else{
      int size,vert,x,y,inc=15<<2;
      expr *e;
      if(*p=='H'||*p=='h')
	vert=0;
      // Disable vertical registers in 48-bit instructions for now
      else if((req!=OP_VREG)&&(*p=='V'||*p=='v'))
	vert=1;
      else
	return 0;
      p=skip(p+1);
      if(*p=='X'||*p=='x'){
	size=2;
	p=skip(p+1);
      }else if(*p=='1'&&p[1]=='6'){
	size=2;
	p=skip(p+2);
      }else if(*p=='Y'||*p=='y'){
	size=4;
	p=skip(p+1);
      }else if(*p=='3'&&p[1]=='2'){
	size=4;
	p=skip(p+2);
      }else if(*p=='8'){
	size=1;
	p=skip(p+1);
      }else{
	size=1;
	p=skip(p);
      }
      if(*p!='(') return 0;
      p=skip(p+1);
      e=parse_expr(&p);
      if(e->type!=NUM) return 0;
      y=e->c.val;
      p=skip(p);
#if 1
      if(req!=OP_VREG&&*p=='+'){
	p++;
	if(*p!='+') return 0;
	if(vert) {
	  cpu_error(4);
	  return 0;
	}
	p=skip(p+1);
	inc|=2;
      }
#endif
      if(*p!=',') return 0;
      p=skip(p+1);
      if((*p=='c')||(*p=='C'))
      {
	p++;
	if (*p!='b'&&*p!='B') return 0;
	p++;
	if (*p!='+') return 0;
	inc|=1;
      }
      e=parse_expr(&p);
      if(e->type!=NUM) return 0;
      x=e->c.val;
      p=skip(p);
#if 1
      if(req!=OP_VREG&&*p=='+'){
	p++;
	if(*p!='+') return 0;
	if(!vert) {
	  cpu_error(4);
	  return 0;
	}
	p=skip(p+1);
	inc|=2;
      }
#endif
      if(*p!=')') return 0;
      p=skip(p+1);
      if(req!=OP_VREG&&*p=='*'){
	p=skip(p+1);
	inc|=1;
      }
      if(req!=OP_VREG&&*p=='+'){
	p=skip(p+1);
	s=skip_reg(p,&r);
	if(s==p) return 0;
	p=skip(s);
	inc&=~0x3c;
	inc|=(r<<2);
      }
      if(req==OP_VREGA80){
	if(size==1){
	  op->reg=(vert<<6)|((x&48)<<3);
	}else if(size==2){
	  if(x&16) return 0;
	  op->reg=(vert<<6)|(8<<6)|((x&32)<<2);
	}else{
	  if(x&48) return 0;
	  op->reg=(vert<<6)|(12<<6);
	}
	if(y<0||y>63) return 0;
	op->reg|=y&63;
	inc|=(x&15)<<6;
      }else{
	if(vert){
	  if(size==1){
	    op->reg=(1<<6)|((x&48)<<3);
	  }else if(size==2){
	    if(x&16) return 0;
	    op->reg=(9<<6)|((x&32)<<2);
	  }else{
	    if(x&48) return 0;
	    op->reg=13<<6;
	  }
	  if(y<0||y>63||(y&15)) return 0;
	  op->reg|=(y&48)|(x&15);
	}
	else{
	  if(size==1){
	    if(x&15) return 0;
	    op->reg=(x&48)<<3;
	  }else if(size==2){
	    if(x&31) return 0;
	    op->reg=(8<<6)|((x&32)<<2);
	  }else{
	    if(x) return 0;
	    op->reg=12<<6;
	  }
	if(y<0||y>63) return 0;
	op->reg|=y&63;
	}
      }
      op->dreg=inc;
      op->type=OP_VREG;
    }
  }else
    return 0;

  if(req==OP_VREGMM||req==OP_IMM32M||req==OP_VIND){
    int vecmod=0,i;
    while(1){
      if(!strncasecmp(p,"rep",3)&&isspace((unsigned char)p[3])){
	expr *e;
	taddr x;
	p=skip(p+3);
	if((*p=='r')&&(p[1]=='0')&&!isdigit(p[2])){
	  p=skip(p+2);
	  vecmod|=7<<16;
	  i=0;
	}else{
	  e=parse_expr(&p);
	  if(e->type!=NUM) return 0;
	  x=e->c.val;
	  for(i=0;i<8;i++){
	    if((1<<i)==x){
	      vecmod|=i<<16;
	      break;
	    }
	  }
	}
	if(i>=8) return 0;
	p=skip(p);
	continue;
      }
      for(i=0;i<sizeof(vflags)/sizeof(vflags[0]);i++){
	if((!strncasecmp(p,vflags[i],strlen(vflags[i])))&&
	   (!isalpha(p[strlen(vflags[i])]))){
	  if(vecmod&0x380) return 0;
	  vecmod|=i<<7;
	  p=skip(p+strlen(vflags[i]));
	  continue;
	}
      }
      for (i=0;i<sizeof(sru)/sizeof(sru[0]);i++){
	if(!strncasecmp(p,sru[i],strlen(sru[i]))){
	  p=skip(p+strlen(sru[i]));
	  p=skip_reg(p,&r);
	  if((r<0)||(r>7)) return 0;
	  vecmod|=(1<<6)|(i<<3)|r;
	  continue;
	}
      }
      for (i=0;i<sizeof(accs)/sizeof(accs[0]);i++){
	if((!strncasecmp(p,accs[i],strlen(accs[i])))&&
	   (!isalpha(p[strlen(accs[i])]))){
	  if(vecmod&0x40) return 0;
	  vecmod|=i;
	  p=skip(p+strlen(accs[i]));
	  continue;
	}
      }
      if(!strncasecmp(p,"ena",3)){
	if(vecmod&0x40) return 0;
	p=skip(p+3);
	vecmod|=32;
      }
      if(!strncasecmp(p,"setf",4)){
	p=skip(p+4);
	vecmod|=1<<20;
	continue;
      }
      break;
    }

    op->vecmod=vecmod;
  }

  if(p>=start+len)
    return 1;
  else
    return 0;
}

static taddr dooffset(int rel,expr *tree,section *sec,taddr pc,rlist **relocs,int roffset,int size,taddr mask)
{
  taddr val;
  if(!eval_expr(tree,&val,sec,pc)){
    taddr addend=val;
    symbol *base;
    if(find_base(tree,&base,sec,pc)!=BASE_OK){
      general_error(38);
      return val;
    }
    if(rel==REL_PC){
      val-=pc;
      addend+=roffset/8;
    }
    if(rel!=REL_PC||!LOCREF(base)||base->sec!=sec){
      add_nreloc_masked(relocs,base,addend,rel,size,roffset,mask);
      return 0;
    }
  }
#if 0
  if(val<-(1<<(size-1))||val>((1<<(size-1))-1))
    cpu_error(1,size);
#endif
  return val&mask;
}

static taddr reloffset(expr *tree,section *sec,taddr pc,rlist **relocs,int roffset,int size,taddr mask)
{
  /* taddr mask=size==32?0xffffffff:((((taddr)1)<<size)-1);*/
  return dooffset(REL_PC,tree,sec,pc,relocs,roffset,size,mask);
}

static taddr absoffset(expr *tree,section *sec,taddr pc,rlist **relocs,int roffset,int size)
{
  /* taddr mask=size==32?0xffffffff:((((taddr)1)<<size)-1);*/
  return dooffset(REL_ABS,tree,sec,pc,relocs,roffset,size,0xffffffff);
}

static taddr absval(expr *tree,section *sec,taddr pc,int bits,int sgn)
{
  taddr val;
  if(!eval_expr(tree,&val,sec,pc))
    cpu_error(0);
  if(!sgn){
    if(val<0||val>=(1<<bits))
      cpu_error(1,bits);
  }else{
    if(val<-(1<<(bits-1))||val>=(1<<(bits-1)))
      cpu_error(1,bits);
  }
  return val&((1<<bits)-1);
}

static int chkval(expr *tree,section *sec,taddr pc,int bits,int sgn)
{
  taddr val;
  if(!eval_expr(tree,&val,sec,pc))
    cpu_error(0);
  if(!sgn){
    if(val<0||val>=(1<<bits))
      return 0;
  }else{
    if(val<-(1<<(bits-1))||val>=(1<<(bits-1)))
      return 0;
  }
  return 1;
}

/* replace instruction by one with encoding enc */
static int replace(int c,int enc)
{
  int i=c+1;
  while(!strcmp(mnemonics[c].name,mnemonics[i].name)){
    if(mnemonics[i].ext.encoding==enc){
      c=i;
      break;
    }
    i++;
  }
  if(c!=i) ierror(0);  
  return c;
}
static int translate(instruction *p,section *sec,taddr pc)
{
  int c=p->code,e=mnemonics[c].ext.encoding;
  taddr val;

  if(p->qualifiers[0]){
    /* extend to larger variants if ccs are required */
    if(e==EN_MEMDISP16)
      c=replace(c,EN_MEMDISP32);
    if(e==EN_ARITHR16)
      c=replace(c,EN_ARITHR32);
    if(e==EN_ARITHI16)
      c=replace(c,EN_ARITHI32);
  }
  if(e==EN_ARITHI32){
    if(p->op[2]){
      if(!chkval(p->op[2]->offset,sec,pc,6,1)&&(!strcmp("add",mnemonics[c].name)||!strcmp("sub",mnemonics[c].name)))
	c=replace(c,EN_ADD48);
    }else{
      if((mnemonics[c].ext.code<32)&&(!chkval(p->op[1]->offset,sec,pc,6,1)))
	c=replace(c,EN_ARITHI48);
    }
  }
  if(e==EN_ARITHI16){
    if(!chkval(p->op[1]->offset,sec,pc,5,0))
      c=replace(c,EN_ARITHI48);
  }
  if(e==EN_RBRANCH16){
    symbol *base;
    if(find_base(p->op[0]->offset,&base,sec,pc)!=BASE_OK||!LOCREF(base)||base->sec!=sec)
      c=replace(c,EN_RBRANCH32);
    else{
      int i;
      i=eval_expr(p->op[0]->offset,&val,sec,pc);
      val-=pc;
      if(val>126||val<-128)
	c=replace(c,EN_RBRANCH32);
    }
  }
  if(e==EN_ADDCMPB32){
    int i;
    i=eval_expr(p->op[3]->offset,&val,sec,pc);
    val-=pc;
    if(val>1022||val<-1024)
      c+=4;
    else if(p->op[2]->type!=OP_REG&&(val>254||val<-256))
      c+=4;
  }
  if((e==EN_MEMDISP16||e==EN_MEMDISP32||e==EN_MEM12DISP32||e==EN_MEM16DISP32)){
    if(!eval_expr(p->op[1]->offset,&val,sec,pc)){
      c=replace(c,EN_MEM48);
    }else{
      if(e==EN_MEMDISP16&&(val<0||val>54||(val&3))){
	if(val<=4095&&val>=-4096)
	  c=replace(c,EN_MEM12DISP32);
	else
	  c=replace(c,EN_MEM48);
      }
    }
  }
  /* todo */
  return c;
}

/* Convert an instruction into a DATA atom including relocations,
   if necessary. */
dblock *eval_instruction(instruction *p,section *sec,taddr pc)
{
  dblock *db=new_dblock();
  int c,sz,enc,cc,ww,m=0;
  unsigned long code,code2,code3;
  rlist *relocs=0;

  if(p->qualifiers[0]){
    int i;
    for(i=0;i<15;i++){
      if(!strcmp(p->qualifiers[0],ccs[i])){
	cc=i;
	break;
      }
    }
    if(i>15) ierror(0);
  }else
    cc=14;

  c=translate(p,sec,pc);
  enc=mnemonics[c].ext.encoding;

  db->size=sz=oplen(enc);
  db->data=mymalloc(db->size);

  code=mnemonics[c].ext.code;

  switch(enc){
  case EN_ARITHR16:
    if(cc!=14) cpu_error(3);
    code=0x40000000|(code<<24);
    code|=(p->op[0]->reg<<16)|(p->op[1]->reg<<20);
    break;
  case EN_ARITHI16:
    if(cc!=14) cpu_error(3);
    code=0x60000000|((code>>1)<<25);
    code|=(p->op[0]->reg<<16)|(absval(p->op[1]->offset,sec,pc,5,0)<<20);
    break;
  case EN_FIX16:
    if(cc!=14) cpu_error(3);
    break;
  case EN_IBRANCH16:
    if(cc!=14) cpu_error(3);
    code|=(p->op[0]->reg<<16);
    break;
  case EN_RBRANCH16:
    code=0x18000000;
    code|=cc<<23;
    code|=((reloffset(p->op[0]->offset,sec,pc,&relocs,0,7,0xfe)>>1)&127)<<16;
    break;
  case EN_MREG16:
    if(cc!=14) cpu_error(3);
    code|=(p->op[0]->dreg<<21)|(p->op[0]->reg<<16);
    break;
  case EN_RBRANCH32:
    if(code==0x90000000)
      code|=cc<<24;
    else
      code|=((reloffset(p->op[0]->offset,sec,pc,&relocs,8,4,0xF000000)>>24)&0xf)<<24;
    code|=((reloffset(p->op[0]->offset,sec,pc,&relocs,16,16,0x1fffe)>>1)&0x7fffff);
    code|=((reloffset(p->op[0]->offset,sec,pc,&relocs,0,7,0xfe0000)>>17)&0x7f)<<16;
    break;
  case EN_FUNARY32:
    code=0xC0000040|(code<<21);
    code|=p->op[0]->reg<<16;
    code|=p->op[1]->reg<<11;
    code|=cc<<7;
    break;
  case EN_ARITHR32:
    code=0xC0000000|(code<<21);
    code|=p->op[0]->reg<<16;
    if(p->op[2]){
      code|=p->op[1]->reg<<11;
      code|=p->op[2]->reg;
    }else{
      code|=p->op[0]->reg<<11;
      code|=p->op[1]->reg;
    }      
    code|=cc<<7;
    break;
  case EN_ARITHI32:
    code=0xC0000040|(code<<21);
    code|=p->op[0]->reg<<16;
    if(p->op[2]){
      code|=p->op[1]->reg<<11;
      code|=absval(p->op[2]->offset,sec,pc,6,1);
    }else{
      code|=p->op[0]->reg<<11;
      code|=absval(p->op[1]->offset,sec,pc,6,1);
    }
    code|=cc<<7;
    break;
  case EN_ADDCMPB64:
    // Large offset: invert CC, place a long branch after
    // so we really generate the following:
    //   addcmpb<~cc> d, a, b, skip
    //   b <destination>
    // skip:
    //   <rest of code>
    m=1;
    if(cc&1) cc--; else cc++;
  case EN_ADDCMPB32:
    code=0x80000000|(code<<14);
    code|=cc<<24;
    code|=p->op[0]->reg<<16;
    if(code&0x4000){
      code|=absval(p->op[1]->offset,sec,pc,4,1)<<20;
    }else{
      code|=p->op[1]->reg<<20;
    }
    if(code&0x8000){
      code|=absval(p->op[2]->offset,sec,pc,6,0)<<8;
      if(m)
	code|=4;
      else
	code|=((reloffset(p->op[3]->offset,sec,pc,&relocs,16,9,0x1ff)>>1)&0xff);
    }else{
      code|=p->op[2]->reg<<10;
      if(m)
	code|=4;
      else
	code|=((reloffset(p->op[3]->offset,sec,pc,&relocs,16,11,0x7ff)>>1)&0x3ff);
    }
    if(m){
      code2=0x90000000|(14<<24);
      code2|=((reloffset(p->op[3]->offset,sec,pc+4,&relocs,48,16,0x1fffe)>>1)&0x7fffff);
      code2|=((reloffset(p->op[3]->offset,sec,pc+4,&relocs,32,7,0xfe0000)>>17)&0x7f)<<16;
    }
    break;
  case EN_ARITHI48:
    if(cc!=14) cpu_error(3);
    code=0xE8000000|(code<<21);
    code|=p->op[0]->reg<<16;
    code2=absoffset(p->op[1]->offset,sec,pc,&relocs,16,32);
    break;
  case EN_ADD48:
    if(cc!=14) cpu_error(3);
    if(p->op[2])
      code2=absoffset(p->op[2]->offset,sec,pc,&relocs,16,32);
    else
      code2=absoffset(p->op[1]->offset,sec,pc,&relocs,16,32);
    /* pseudo sub */
    if(code==6)
      code2=-code2;
    code=0xEC000000;
    code|=p->op[0]->reg<<16;
    if(p->op[2])
      code|=p->op[1]->reg<<21;
    else
      code|=p->op[0]->reg<<21;
    break;
  case EN_MEMREG16:
    if(cc!=14) cpu_error(3);
    code=0x08000000|(code<<24);
    code|=p->op[0]->reg<<16;
    code|=p->op[1]->reg<<20;
    ww=0; /*FIXME*/
    code|=ww<<25;
    break;
  case EN_MEMSTACK16:
    if(cc!=14) cpu_error(3);
    code=0x04000000|(code<<25);
    code|=p->op[0]->reg<<16;
    code|=(absval(p->op[1]->offset,sec,pc,7,1)>>2)<<20;
    break;
  case EN_MEMDISP16:
    if(cc!=14) cpu_error(3);
    code=0x20000000|(code<<28);
    code|=p->op[0]->reg<<16;
    code|=(absval(p->op[1]->offset,sec,pc,6,0)>>2)<<24;
    code|=p->op[1]->reg<<20;
    break;
  case EN_LEA16:
    if(cc!=14) cpu_error(3);
    code=0x10000000;
    code|=p->op[0]->reg<<16;
    code|=(absval(p->op[1]->offset,sec,pc,8,1)>>2)<<21;
    break;
  case EN_LEA48:
    if(cc!=14) cpu_error(3);
    code=0xE5000000|(p->op[0]->reg<<16);
    code2=reloffset(p->op[1]->offset,sec,pc,&relocs,16,32,0xffffffff);
    break;
  case EN_MEM48:
    if(cc!=14) cpu_error(3);
    code=0xE6000000|(code<<21);
    code|=p->op[0]->reg<<16;
    if(p->op[1]->reg==31)
      code2=reloffset(p->op[1]->offset,sec,pc,&relocs,16,27,0xffffffff)&0x7ffffff;
    else
      /* TODO: other relocs */
      code2=absoffset(p->op[1]->offset,sec,pc,&relocs,16,27)&0x7ffffff;
    code2|=p->op[1]->reg<<27;
    break;
  case EN_MEMREG32:
    code=0xA0000000|(code<<21);
    code|=p->op[0]->reg<<16;
    code|=p->op[1]->reg<<11;
    code|=p->op[1]->dreg;
    code|=cc<<7;
    break;
  case EN_MEMDISP32:
    code=0xA0000040|(code<<21);
    code|=p->op[0]->reg<<16;
    code|=p->op[1]->reg<<11;
    code|=absval(p->op[1]->offset,sec,pc,5,1);
    code|=cc<<7;
    break;
  case EN_MEMPREDEC:
  case EN_MEMPOSTINC:
    code<<=21;
    if(p->op[1]->type==OP_POSTINC)
      code|=0xA5000000;
    else
      code|=0xA4000000;
    code|=p->op[0]->reg<<16;
    code|=p->op[1]->reg<<11;
    code|=cc<<7;
    break;
  case EN_MEM12DISP32:
    if(cc!=14) cpu_error(3);
    code=0xA2000000|(code<<21);
    code|=p->op[0]->reg<<16;
    code|=p->op[1]->reg<<11;
    code|=absval(p->op[1]->offset,sec,pc,12,1)&0x7ff;
    code|=(absval(p->op[1]->offset,sec,pc,12,1)&0x800)<<13;
    break;
  case EN_MEM16DISP32:
    if(cc!=14) cpu_error(3);
    code|=p->op[0]->reg<<16;
    if((code&0xff000000)==0xaa000000)
      code|=reloffset(p->op[1]->offset,sec,pc,&relocs,16,16,0xffff)&0xffff;
    else
      code|=absval(p->op[1]->offset,sec,pc,16,1);
    break;
  case EN_VLOAD48:
    code=0xF0000000|(code<<19);
    code2=(p->op[0]->reg<<22)|(7<<7)|p->op[1]->reg;
    break;
  case EN_VSTORE48:
    code=0xF0000000|(code<<19);
    code2=(p->op[0]->reg<<12)|(7<<7)|p->op[1]->reg;
    break;
  case EN_VREAD48:
    code=0xF0000000|(code<<19);
    code2=(p->op[0]->reg<<22)|p->op[1]->reg;
    break;
  case EN_VWRITE48:
    code=0xF0000000|(code<<19);
    code2=(p->op[0]->reg<<12)|p->op[1]->reg;
    break;
  case EN_VREADI48:
    code=0xF0000000|(code<<19);
    code2=(p->op[0]->reg<<22)|absval(p->op[1]->offset,sec,pc,6,0)|(1<<10);
    break;
  case EN_VWRITEI48:
    code=0xF0000000|(code<<19);
    code2=(p->op[0]->reg<<12)|absval(p->op[1]->offset,sec,pc,6,0)|(1<<10);
    break;
  case EN_VARITHR48:
    if(code<48) code|=64;
    code=0xF4000000|(code<<19);
    if(p->op[2]){
      code2=(p->op[0]->reg<<22)|(p->op[1]->reg<<12)|p->op[2]->reg;
    }else{
      code2=(p->op[0]->reg<<22)|p->op[1]->reg;
    }
    break;
  case EN_VARITHI48:
    if(code<48) code|=64;
    code=0xF4000000|(code<<19);
    if(p->op[2]){
      code2=(p->op[0]->reg<<22)|(p->op[1]->reg<<12)|absval(p->op[2]->offset,sec,pc,6,0)|(1<<10);
    }else{
      code2=(p->op[0]->reg<<22)|absval(p->op[1]->offset,sec,pc,6,0)|(1<<10);
    }
    break;
  // TODO: Check these!
  case EN_VLOAD80:
    code=0xF8000000|(code<<19)|(p->op[1]->vecmod&0x70000);
    code2=(p->op[0]->reg<<22);
    code3=(p->op[1]->reg<<2)|(p->op[0]->dreg<<26)|((p->op[1]->dreg)<<22);
    if(p->op[1]->offset){
      code2|=(absval(p->op[1]->offset,sec,pc,16,1)&0x7f);
      code3|=(absval(p->op[1]->offset,sec,pc,16,1)>>7)&3;
      code3|=((absval(p->op[1]->offset,sec,pc,16,1)>>9)&0x7f)<<6;
    }
    break;
  case EN_VSTORE80:
    code=0xF8000000|(code<<19)|(p->op[1]->vecmod&0x70000);
    code2=(p->op[0]->reg<<12);
    code3=(p->op[1]->reg<<2)|(p->op[0]->dreg<<20)|((p->op[1]->dreg)<<28);
    if(p->op[1]->offset){
      code2|=(absval(p->op[1]->offset,sec,pc,16,1)&0x7f);
      code3|=(absval(p->op[1]->offset,sec,pc,16,1)>>7)&3;
      code3|=((absval(p->op[1]->offset,sec,pc,16,1)>>9)&0x7f)<<6;
    }
    break;
  case EN_VREAD80:
    if(p->op[1]->offset){
      p->op[1]->reg|=(absval(p->op[1]->offset,sec,pc,9,1)&0x7f);
      p->op[1]->dreg|=(absval(p->op[1]->offset,sec,pc,9,1)>>7)&3;
    }
    code=0xF8000000|(code<<19)|(p->op[1]->vecmod&0x70000);
    code2=(p->op[0]->reg<<22)|(p->op[1]->reg);
    if(p->op[1]->vecmod&(1<<20)) code2|=(1<<11);
    code3=(p->op[0]->dreg<<26)|(p->op[1]->dreg);
    code3|=(p->op[1]->vecmod&0x3fff)<<6;
    break;
  case EN_VWRITE80:
    if(p->op[1]->offset){
      p->op[1]->reg|=(absval(p->op[1]->offset,sec,pc,9,1)&0x7f);
      p->op[1]->dreg|=(absval(p->op[1]->offset,sec,pc,9,1)>>7)&3;
    }
    code=0xF8000000|(code<<19)|(p->op[1]->vecmod&0x70000);
    code2=(p->op[0]->reg<<12)|(p->op[1]->reg);
    if(p->op[1]->vecmod&(1<<20)) code2|=(1<<11);
    code3=((p->op[0]->dreg&0x3f)<<20)|((p->op[0]->dreg&0x3c0)<<10)|(p->op[1]->dreg);
    code3|=(p->op[1]->vecmod&0x3fff)<<6;
    break;
  case EN_VREADI80:
    if(p->op[1]->offset){
      p->op[1]->reg|=(absval(p->op[1]->offset,sec,pc,9,1)&0x7f);
      p->op[1]->dreg|=(absval(p->op[1]->offset,sec,pc,9,1)>>7)&3;
    }
    code=0xF8000000|(code<<19)|(p->op[1]->vecmod&0x70000);
    if(p->op[1]->vecmod&(1<<20)) code2|=(1<<11);
    code2=(p->op[0]->reg<<22)|(1<<10)|(absval(p->op[1]->offset,sec,pc,16,0)&0x3ff);
    code3=(p->op[0]->dreg<<26)|((absval(p->op[1]->offset,sec,pc,16,0)>>10)&0x3f);
    code3|=(p->op[1]->vecmod&0x3fff)<<6;
    break;
  case EN_VWRITEI80:
    if(p->op[1]->offset){
      p->op[1]->reg|=(absval(p->op[1]->offset,sec,pc,9,1)&0x7f);
      p->op[1]->dreg|=(absval(p->op[1]->offset,sec,pc,9,1)>>7)&3;
    }
    code=0xF8000000|(code<<19)|(p->op[1]->vecmod&0x70000);
    if(p->op[1]->vecmod&(1<<20)) code2|=(1<<11);
    code2=(p->op[0]->reg<<12)|(1<<10)|(absval(p->op[1]->offset,sec,pc,16,0)&0x3ff);
    code3=((p->op[0]->dreg&0x3f)<<20)|((p->op[0]->dreg&0x3c0)<<10)|((absval(p->op[1]->offset,sec,pc,16,0)>>10)&0x3f);
    code3|=(p->op[1]->vecmod&0x3fff)<<6;
    break;
  case EN_VARITHR80:
    if(code<48) code|=64;
    if(p->op[2]){
      if(p->op[2]->offset){
	p->op[2]->reg|=(absval(p->op[2]->offset,sec,pc,9,1)&0x7f);
	p->op[2]->dreg|=(absval(p->op[2]->offset,sec,pc,9,1)>>7)&3;
      }
      code=0xFC000000|(code<<19)|(p->op[2]->vecmod&0x70000);
      code2=(p->op[0]->reg<<22)|(p->op[1]->reg<<12)|(p->op[2]->reg);
      if(p->op[2]->vecmod&(1<<20)) code2|=(1<<11);
      code3=(p->op[0]->dreg<<26)|((p->op[1]->dreg&0x3f)<<20)|((p->op[1]->dreg&0x3c0)<<10)|(p->op[2]->dreg);
      code3|=(p->op[2]->vecmod&0x3fff)<<6;
    }else{
      if(p->op[1]->offset){
	p->op[1]->reg|=(absval(p->op[1]->offset,sec,pc,9,1)&0x7f);
	p->op[1]->dreg|=(absval(p->op[1]->offset,sec,pc,9,1)>>7)&3;
      }
      code=0xFC000000|(code<<19)|(p->op[1]->vecmod&0x70000);
      code2=(p->op[0]->reg<<22)|(p->op[1]->reg);
      if(p->op[1]->vecmod&(1<<20)) code2|=(1<<11);
      code3=(p->op[0]->dreg<<26)|(p->op[1]->dreg);
      code3|=(p->op[1]->vecmod&0x3fff)<<6;
    }
    break;
  case EN_VARITHI80:
    if(code<48) code|=64;
    if(p->op[2]){
      if(p->op[2]->offset){
	p->op[2]->reg|=(absval(p->op[2]->offset,sec,pc,9,1)&0x7f);
	p->op[2]->dreg|=(absval(p->op[2]->offset,sec,pc,9,1)>>7);
      }
      code=0xFC000000|(code<<19)|(p->op[2]->vecmod&0x70000);
      if(p->op[2]->vecmod&(1<<20)) code2|=(1<<11);
      code2=(p->op[0]->reg<<22)|(p->op[1]->reg<<12)|(1<<10)|(absval(p->op[2]->offset,sec,pc,16,0)&0x3ff);
      code3=(p->op[0]->dreg<<26)|((p->op[1]->dreg&0x3f)<<20)|((p->op[1]->dreg&0x3c0)<<10)|((absval(p->op[2]->offset,sec,pc,16,0)>>10)&0x3f);
      code3|=(p->op[2]->vecmod&0x3fff)<<6;
    }else{
      if(p->op[1]->offset){
	p->op[1]->reg|=(absval(p->op[1]->offset,sec,pc,9,1)&0x7f);
	p->op[1]->dreg|=(absval(p->op[1]->offset,sec,pc,9,1)>>7);
      }
      code=0xFC000000|(code<<19)|(p->op[1]->vecmod&0x70000);
      if(p->op[1]->vecmod&(1<<20)) code2|=(1<<11);
      code2=(p->op[0]->reg<<22)|(1<<10)|(absval(p->op[1]->offset,sec,pc,16,0)&0x3ff);
      code3=(p->op[0]->dreg<<26)|((absval(p->op[1]->offset,sec,pc,16,0)>>10)&0x3f);
      code3|=(p->op[1]->vecmod&0x3fff)<<6;
    }
    break;
  default:
    ierror(0);
  }

  if(enc>=EN_VLOAD48&&enc<=EN_VARITHI48){
    db->data[0]=(code>>16)&0xff;
    db->data[1]=(code>>24)&0xff;
    db->data[3]=(code2>>24)&0xff;
    db->data[2]=(code2>>16)&0xff;
    db->data[5]=(code2>>8)&0xff;
    db->data[4]=(code2>>0)&0xff;
  }else if(sz==2){
    db->data[0]=(code>>16)&0xff;
    db->data[1]=(code>>24)&0xff;
  }else if(sz==4){
    db->data[1]=(code>>24)&0xff;
    db->data[0]=(code>>16)&0xff;
    db->data[3]=(code>>8)&0xff;
    db->data[2]=(code)&0xff;
  }else if(sz==6){
    db->data[0]=(code>>16)&0xff;
    db->data[1]=(code>>24)&0xff;
    db->data[5]=(code2>>24)&0xff;
    db->data[4]=(code2>>16)&0xff;
    db->data[3]=(code2>>8)&0xff;
    db->data[2]=(code2>>0)&0xff;
  }
  else if(sz==8){
    db->data[1]=(code>>24)&0xff;
    db->data[0]=(code>>16)&0xff;
    db->data[3]=(code>>8)&0xff;
    db->data[2]=(code)&0xff;
    db->data[5]=(code2>>24)&0xff;
    db->data[4]=(code2>>16)&0xff;
    db->data[7]=(code2>>8)&0xff;
    db->data[6]=(code2)&0xff;
  }else if(sz==10){
    db->data[0]=(code>>16)&0xff;
    db->data[1]=(code>>24)&0xff;
    db->data[3]=(code2>>24)&0xff;
    db->data[2]=(code2>>16)&0xff;
    db->data[5]=(code2>>8)&0xff;
    db->data[4]=(code2>>0)&0xff;
    db->data[7]=(code3>>24)&0xff;
    db->data[6]=(code3>>16)&0xff;
    db->data[9]=(code3>>8)&0xff;
    db->data[8]=(code3>>0)&0xff;
  }
  db->relocs=relocs;
  return db;
}

/* Create a dblock (with relocs, if necessary) for size bits of data. */
dblock *eval_data(operand *op,size_t bitsize,section *sec,taddr pc)
{
  dblock *new=new_dblock();
  taddr val;
  new->size=(bitsize+7)/8;
  new->data=mymalloc(new->size);
  if(op->type!=OP_ABS)
    ierror(0);
  if(bitsize!=8&&bitsize!=16&&bitsize!=32)
    cpu_error(2);
  val=absoffset(op->offset,sec,pc,&new->relocs,0,bitsize);
  if(bitsize==32){
    new->data[3]=val>>24;
    new->data[2]=val>>16;    
    new->data[1]=val>>8;
    new->data[0]=val;        
  }else if(bitsize==16){
    new->data[1]=val>>8;
    new->data[0]=val;    
  }else
    new->data[0]=val;
  return new;
}                                     


/* Calculate the size of the current instruction; must be identical
   to the data created by eval_instruction. */
size_t instruction_size(instruction *p,section *sec,taddr pc)
{
  int c;
  c=translate(p,sec,pc);
  return oplen(mnemonics[c].ext.encoding);
}

operand *new_operand()
{
  operand *new=mymalloc(sizeof(*new));
  new->type=-1;
  return new;
}

/* return true, if initialization was successfull */
int init_cpu()
{
  return 1;
}

/* return true, if the passed argument is understood */
int cpu_args(char *p)
{
  return 0;
}

/* parse cpu-specific directives; return pointer to end of
   cpu-specific text */
char *parse_cpu_special(char *s)
{
  char *name=s,*merk=s;

  return merk;
}

int set_default_qualifiers(char **q,int *q_len)
/* fill in pointers to default qualifiers, return number of qualifiers */
{
  q[0]="u32";
  q_len[0]=3;
  q[1]="";
  q_len[1]=0;
  return 2;
}

char *parse_instruction(char *s,int *inst_len,char **ext,int *ext_len,
                        int *ext_cnt)
/* parse instruction and save extension locations */
{
  char *inst = s, l, i;
  int cnt = 0;

  while (*s && *s!='.' && !isspace((unsigned char)*s))
    s++;
  l = s - inst;

  if(strncasecmp("divs",inst,l)&&strncmp("vreadacc",inst,l)&&
     strncasecmp("vcmpge",inst,l)){
    for(i=0;i<16;i++){
      int cl=strlen(ccs[i]);
      if(l>cl&&!strncasecmp(s-cl,ccs[i],cl)){
	ext[0]=s-cl;
	ext_len[0]=cl;
	cnt=1;
	l-=cl;
	break;
      }
    }
  }

  *ext_cnt = cnt;

  *inst_len=l;

  return s;
}
