/* cpu.c example cpu-description file */ /* (c) in 2002 by Volker Barthelmann */ #include "vasm.h" char *cpu_copyright="vasm c16x/st10 cpu backend 0.2c (c) in 2002-2005 Volker Barthelmann"; char *cpuname="c16x"; mnemonic mnemonics[]={ #include "opcodes.h" }; int mnemonic_cnt=sizeof(mnemonics)/sizeof(mnemonics[0]); int bitsperbyte=8; int bytespertaddr=4; static int JMPA,JMPR,JMPS,JNB,JB,JBC,JNBS,JMP; static int notrans,tojmpa; #define JMPCONV 256 #define INVCC(c) (((c)&1)?(c)-1:(c)+1) #define ISBIT 1 typedef struct sfr { struct sfr *next; int flags; unsigned int laddr,saddr,boffset; } sfr; sfr *first_sfr; #define SFRHTSIZE 1024 hashtable *sfrhash; static char *skip_reg(char *s,int *reg) { int r=-1; if(*s!='r'&&*s!='R'){ cpu_error(1); return s; } s++; if(*s<'0'||*s>'9'){ cpu_error(1); return s; } r=*s++-'0'; if(*s>='0'&&*s<='5') r=10*r+*s++-'0'; *reg=r; return s; } int parse_operand(char *p,int len,operand *op,int requires) { op->type=-1; op->mod=-1; p=skip(p); if(requires==OP_REL){ char *s=p; op->type=OP_REL; op->offset=parse_expr(&s); simplify_expr(op->offset); if(s==p) return 0; else return 1; } if(requires==OP_CC){ op->type=OP_CC; if(len<4||len>6||p[0]!='c'||p[1]!='c'||p[2]!='_') return 0; if(len==4){ if(p[3]=='z') op->cc=2; else if(p[3]=='v') op->cc=4; else if(p[3]=='n') op->cc=6; else if(p[3]=='c') op->cc=8; else return 0; }else if(len==5){ if(p[3]=='u'&&p[4]=='c') op->cc=0; else if(p[3]=='n'&&p[4]=='z') op->cc=3; else if(p[3]=='n'&&p[4]=='v') op->cc=5; else if(p[3]=='n'&&p[4]=='n') op->cc=7; else if(p[3]=='n'&&p[4]=='c') op->cc=0; else if(p[3]=='e'&&p[4]=='q') op->cc=2; else if(p[3]=='n'&&p[4]=='e') op->cc=3; else return 0; }else if(len==6){ if(!strncmp(p+3,"ult",3)) op->cc=8; else if(!strncmp(p+3,"ule",3)) op->cc=0xf; else if(!strncmp(p+3,"uge",3)) op->cc=0x9; else if(!strncmp(p+3,"ugt",3)) op->cc=0xe; else if(!strncmp(p+3,"slt",3)) op->cc=0xc; else if(!strncmp(p+3,"sle",3)) op->cc=0xb; else if(!strncmp(p+3,"sge",3)) op->cc=0xd; else if(!strncmp(p+3,"sgt",3)) op->cc=0xa; else if(!strncmp(p+3,"net",3)) op->cc=0x1; else return 0; } return 1; } if((p[0]=='r'||p[0]=='R')&&p[1]>='0'&&p[1]<='9'&&(len==2||p[2]=='.')){ op->type=OP_GPR; op->reg=p[1]-'0'; op->regsfr=op->reg+0xf0; if(len>2){ op->type=OP_BADDR; if(requires==OP_BADDR){ p=skip(p+3); op->boffset=parse_expr(&p); op->offset=number_expr(op->regsfr); } } }else if((p[0]=='r'||p[0]=='R')&&p[1]=='1'&&p[2]>='0'&&p[2]<='5'&&(len==3||p[3]=='.')){ op->type=OP_GPR; op->reg=(p[1]-'0')*10+p[2]-'0'; op->regsfr=op->reg+0xf0; if(len>3){ op->type=OP_BADDR; if(requires==OP_BADDR){ p=skip(p+4); op->boffset=parse_expr(&p); op->offset=number_expr(op->regsfr); } } }else if(len==3&&(p[0]=='r'||p[0]=='R')&&(p[1]=='l'||p[1]=='L')&&p[2]>='0'&&p[2]<='7'){ op->type=OP_BGPR; op->reg=(p[2]-'0')*2; op->regsfr=op->reg+0xf0; }else if(len==3&&(p[0]=='r'||p[0]=='R')&&(p[1]=='h'||p[1]=='H')&&p[2]>='0'&&p[2]<='7'){ op->type=OP_BGPR; op->reg=(p[2]-'0')*2+1; op->regsfr=op->reg+0xf0; }else if(p[0]=='#'){ op->type=OP_IMM16; p=skip(p+1); if((!strncmp("SOF",p,3)||!strncmp("sof",p,3))&&isspace((unsigned char)p[3])){op->mod=MOD_SOF;p=skip(p+3);} if((!strncmp("SEG",p,3)||!strncmp("seg",p,3))&&isspace((unsigned char)p[3])){op->mod=MOD_SEG;p=skip(p+3);} if((!strncmp("DPP0:",p,5)||!strncmp("dpp0:",p,5))){op->mod=MOD_DPP0;p=skip(p+5);} if((!strncmp("DPP1:",p,5)||!strncmp("dpp1:",p,5))){op->mod=MOD_DPP1;p=skip(p+5);} if((!strncmp("DPP2:",p,5)||!strncmp("dpp2:",p,5))){op->mod=MOD_DPP2;p=skip(p+5);} if((!strncmp("DPP3:",p,5)||!strncmp("dpp3:",p,5))){op->mod=MOD_DPP3;p=skip(p+5);} if((!strncmp("DPPX:",p,5)||!strncmp("dppx:",p,5))){op->mod=MOD_DPPX;p=skip(p+5);} op->offset=parse_expr(&p); simplify_expr(op->offset); #if 0 if(op->offset->type==NUM){ taddr val=op->offset->c.val; if(val>=0&&val<=7) op->type=OP_IMM3; else if(val>=0&&val<=15) op->type=OP_IMM4; else if(val>=0&&val<=127) op->type=OP_IMM7; else if(val>=0&&val<=255) op->type=OP_IMM8; } #endif }else if(*p=='['){ p=skip(p+1); if(*p=='-'){ p=skip(p+1); p=skip_reg(p,&op->reg); p=skip(p); if(*p!=']') cpu_error(0); if(op->reg<=3) op->type=OP_PREDEC03; else op->type=OP_PREDEC; }else{ p=skip_reg(p,&op->reg); p=skip(p); if(*p=='+'){ p=skip(p+1); if(*p==']'){ if(op->reg<=3) op->type=OP_POSTINC03; else op->type=OP_POSTINC; }else{ if(*p!='#') cpu_error(0); p=skip(p+1); op->offset=parse_expr(&p); p=skip(p); op->type=OP_REGDISP; } }else{ if(op->reg<=3) op->type=OP_REG03IND; else op->type=OP_REGIND; } if(*p!=']') cpu_error(0); } }else{ if(ISIDSTART(*p)){ char *name=p; hashdata data; while((p==name||ISIDCHAR(*p))&&*p!='.') p++; if(find_namelen(sfrhash,name,p-name,&data)){ sfr *sfr; sfr=data.ptr; if(sfr->flags&ISBIT){ op->offset=number_expr(sfr->saddr); op->type=OP_BADDR; op->boffset=number_expr(sfr->boffset); }else{ if(requires==OP_SFR||requires==OP_BSFR||requires==OP_BWORD){ op->offset=number_expr(sfr->saddr); op->type=requires; }else if(requires==OP_BADDR&&*p=='.'){ op->offset=number_expr(sfr->saddr); p=skip(p+1); op->boffset=parse_expr(&p); op->type=OP_BADDR; }else if(requires==OP_ABS||requires==OP_BABS){ op->type=requires; op->offset=number_expr((2*sfr->saddr)+(sfr->laddr<<8)); } } } if(op->type==-1) p=name; } if(op->type==-1){ if((!strncmp("SOF",p,3)||!strncmp("sof",p,3))&&isspace((unsigned char)p[3])){op->mod=MOD_SOF;p=skip(p+3);} if((!strncmp("SEG",p,3)||!strncmp("seg",p,3))&&isspace((unsigned char)p[3])){op->mod=MOD_SEG;p=skip(p+3);} if((!strncmp("DPP0:",p,5)||!strncmp("dpp0:",p,5))){op->mod=MOD_DPP0;p=skip(p+5);} if((!strncmp("DPP1:",p,5)||!strncmp("dpp1:",p,5))){op->mod=MOD_DPP1;p=skip(p+5);} if((!strncmp("DPP2:",p,5)||!strncmp("dpp2:",p,5))){op->mod=MOD_DPP2;p=skip(p+5);} if((!strncmp("DPP3:",p,5)||!strncmp("dpp3:",p,5))){op->mod=MOD_DPP3;p=skip(p+5);} if((!strncmp("DPPX:",p,5)||!strncmp("dppx:",p,5))){op->mod=MOD_DPPX;p=skip(p+5);} op->offset=parse_expr(&p); op->type=OP_ABS; } } if(requires==op->type) return 1; if(requires==OP_BWORD&&op->type==OP_SFR) return 1; if(op->type==OP_IMM16&&(requires>=OP_IMM2&&requires<=OP_IMM16)) return 1; if(op->type==OP_PREDEC03&&requires==OP_PREDEC) return 1; if(op->type==OP_POSTINC03&&requires==OP_POSTINC) return 1; if(op->type==OP_REG03IND&&requires==OP_REGIND) return 1; if((requires==OP_SFR&&op->type==OP_GPR)|| (requires==OP_BWORD&&op->type==OP_GPR)|| (requires==OP_BWORD&&op->type==OP_BGPR)|| (requires==OP_BSFR&&op->type==OP_BGPR)){ op->offset=number_expr(op->regsfr); return 1; } if(requires==OP_BSFR&&op->type==OP_BGPR) return 1; if(requires==OP_JADDR&&op->type==OP_ABS) return 1; if(requires==OP_BABS&&op->type==OP_ABS) return 1; /*FIXME*/ return 0; } static taddr reloffset(expr *tree,section *sec,taddr pc) { symbol *sym; int btype; taddr val; simplify_expr(tree); if(tree->type==NUM){ /* should we do it like this?? */ val=tree->c.val; }else{ btype=find_base(tree,&sym,sec,pc); if(btype!=BASE_OK||!LOCREF(sym)||sym->sec!=sec) val=0xffff; else{ eval_expr(tree,&val,sec,pc); val=val-pc; } } return val; } static taddr absoffset2(expr *tree,int mod,section *sec,taddr pc,rlist **relocs,int roffset,int size,taddr mask) { taddr val; if(mod==MOD_SOF){ if(mask!=0xffffffff&&mask!=0xffff) cpu_error(5); mask=0xffff; } if(mod==MOD_SEG){ if(mask!=0xff&&mask!=0xffff&&mask!=0xffffffff) cpu_error(6); mask<<=16; } if(mod==MOD_DPP0||mod==MOD_DPP1||mod==MOD_DPP2||mod==MOD_DPP3||mod==MOD_DPPX){ if(mask!=0xffffffff&&mask!=0xffff) cpu_error(7); mask=0x3fff; } 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(mod==MOD_DPP1) val|=0x4000; if(mod==MOD_DPP2) val|=0x8000; if(mod==MOD_DPP3) val|=0xc000; if(mod==MOD_DPPX){ static int dpplen; static char *dppname; char *id=base->name; symbol *dppsym; size-=2; if(strlen(id)+9>dpplen){ myfree(dppname); dppname=mymalloc(dpplen=strlen(id)+9); } strcpy(dppname,"___DPP_"); strcat(dppname,id); dppsym=new_import(dppname); if(dppsym->type==EXPRESSION){ if(!eval_expr(dppsym->expr,&val,0,0)) ierror(0); val<<=14; }else{ add_nreloc_masked(relocs,dppsym,0,REL_ABS,2,roffset+14,0x3); } } add_nreloc_masked(relocs,base,addend,REL_ABS,size,roffset,mask); return val; } val&=mask; if(mod==MOD_DPPX) cpu_error(7); if(mod==MOD_DPP1) val|=0x4000; if(mod==MOD_DPP2) val|=0x8000; if(mod==MOD_DPP3) val|=0xc000; if(mod==MOD_SEG) val>>=16; /*FIXME: range check */ #if 1 if(size==16) return val&0xffff; else return val&((1<4) cpu_error(3,2); return val; }else if(val<0||val>=(1<code; taddr val; /* choose one of jmpr/jmpa */ if(c==JMP||(!notrans&&(c==JMPA||c==JMPR||c==JB||c==JNB))){ val=reloffset(p->op[1]->offset,sec,pc); if(val<-256||val>254||val%2){ if(c==JB) return JNB|JMPCONV; if(c==JNB) return JB|JMPCONV; if(c==JMPA) return JMPA; if(tojmpa) return JMPA; if(p->op[0]->cc==0) return JMPS; else return JMPR|JMPCONV; }else{ if(c==JB||c==JNB) return c; return JMPR; } } /* choose between gpr,#imm3 and reg,#imm16 */ if(mnemonics[c].operand_type[1]==OP_IMM3){ if(!eval_expr(p->op[1]->offset,&val,sec,pc)||val<0||val>7){ if(!strcmp(mnemonics[c].name,mnemonics[c+1].name)) return c+1; } } /* choose between gpr,#imm4 and reg,#imm16 */ if(mnemonics[c].operand_type[1]==OP_IMM4){ if(!eval_expr(p->op[1]->offset,&val,sec,pc)||val<0||val>7){ if(!strcmp(mnemonics[c].name,mnemonics[c+1].name)) return c+1; } } 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 opcode,c,jmpconv=0,osize; unsigned long code; char *d; taddr val; rlist *relocs=0; operand *jmpaddr; c=translate(p,sec,pc); if(c&JMPCONV){ jmpconv=1;c&=~JMPCONV;} if((mnemonics[p->code].operand_type[0]==OP_GPR&&mnemonics[c].operand_type[0]==OP_SFR)|| (mnemonics[p->code].operand_type[0]==OP_BGPR&&mnemonics[c].operand_type[0]==OP_BSFR)) p->op[0]->offset=number_expr(p->op[0]->regsfr); db->size=osize=mnemonics[c].ext.len*2; if(jmpconv) db->size+=4; db->data=mymalloc(db->size); opcode=mnemonics[c].ext.opcode; switch(mnemonics[c].ext.encoding){ case 0: code=opcode<<16|(opcode>>8)<<8|opcode>>8; break; case 1: code=opcode; break; case 2: code=opcode|p->op[0]->reg<<4|p->op[1]->reg; break; case 3: code=opcode|p->op[0]->reg|p->op[1]->reg<<4; break; case 4: code=opcode|p->op[0]->reg<<4|p->op[1]->reg|8; break; case 5: code=opcode|p->op[0]->reg<<4|p->op[1]->reg|12; break; case 6: code=opcode|p->op[0]->reg<<4|absval(p->op[1]->offset,sec,pc,3); break; case 7: /* fall through */ case 8: code=opcode<<16|absoffset(p->op[0]->offset,p->op[0]->mod,sec,pc,&relocs,20,8)<<16|absoffset(p->op[1]->offset,p->op[1]->mod,sec,pc,&relocs,16,16); break; case 9: code=opcode|p->op[0]->reg|absval(p->op[1]->offset,sec,pc,4)<<4; break; case 10: /* rfi: reorder bmov operands */ code=opcode<<16| absoffset(p->op[1]->offset,p->op[1]->mod,sec,pc,&relocs,8,8)<<16| absoffset(p->op[0]->offset,p->op[0]->mod,sec,pc,&relocs,16,8)<<0| absoffset(p->op[1]->boffset,0,sec,pc,&relocs,24,4)<<12| absoffset(p->op[0]->boffset,0,sec,pc,&relocs,28,4)<<8; break; case 11: code=opcode|absoffset(p->op[0]->boffset,0,sec,pc,&relocs,0,4)<<12|absoffset(p->op[0]->offset,p->op[0]->mod,sec,pc,&relocs,8,8); break; case 12: code=opcode<<16| absoffset(p->op[0]->offset,p->op[0]->mod,sec,pc,&relocs,8,8)<<16| absoffset(p->op[2]->offset,p->op[2]->mod,sec,pc,&relocs,16,8)<<8| absoffset(p->op[1]->offset,p->op[1]->mod,sec,pc,&relocs,24,8); break; case 13: code=opcode<<16| absoffset(p->op[0]->offset,p->op[0]->mod,sec,pc,&relocs,8,8)<<16| absoffset(p->op[1]->offset,p->op[1]->mod,sec,pc,&relocs,16,8)<<8| absoffset(p->op[2]->offset,p->op[2]->mod,sec,pc,&relocs,24,8); break; case 14: code=opcode<<16|p->op[0]->cc<<20|absoffset(p->op[1]->offset,p->op[1]->mod,sec,pc,&relocs,16,16); break; case 15: code=opcode|p->op[0]->cc<<4|p->op[1]->reg; break; case 16: val=((reloffset(p->op[0]->offset,sec,pc)-2)>>1)&255; code=opcode|val; break; case 17: if(p->op[0]->type==OP_CC){ /* jmp cc_uc was converted to jmps */ code=opcode<<16|absoffset2(p->op[1]->offset,0,sec,pc,&relocs,8,8,0xffff0000)<<16|absoffset2(p->op[1]->offset,0,sec,pc,&relocs,16,16,0xffff); }else{ code=opcode<<16|absoffset2(p->op[0]->offset,0,sec,pc,&relocs,8,8,0xffff0000)<<16|absoffset2(p->op[0]->offset,0,sec,pc,&relocs,16,16,0xffff); } break; case 18: /* fall through */ case 19: code=opcode<<16|0xf<<20|p->op[0]->reg<<16|absoffset(p->op[1]->offset,p->op[1]->mod,sec,pc,&relocs,16,16); break; case 20: code=opcode|p->op[0]->reg<<4; break; case 21: code=opcode|p->op[0]->reg<<4|p->op[0]->reg; break; case 22: if(!jmpconv){ val=((reloffset(p->op[1]->offset,sec,pc)-4)>>1)&255; code=opcode<<16| absoffset(p->op[0]->offset,p->op[0]->mod,sec,pc,&relocs,8,8)<<16| absoffset(p->op[0]->boffset,0,sec,pc,&relocs,24,4)<<12| val; }else{ code=opcode<<16| absoffset(p->op[0]->offset,p->op[0]->mod,sec,pc,&relocs,8,8)<<16| absoffset(p->op[0]->boffset,0,sec,pc,&relocs,24,4)<<12| 2; jmpaddr=p->op[1]; } break; case 23: if(!jmpconv){ val=((reloffset(p->op[1]->offset,sec,pc)-2)>>1)&255; code=opcode|p->op[0]->cc<<12|val; }else{ code=opcode|INVCC(p->op[0]->cc)<<12|2; jmpaddr=p->op[1]; } break; case 24: code=opcode<<16|p->op[0]->reg<<20|p->op[1]->reg<<16|absoffset(p->op[1]->offset,p->op[1]->mod,sec,pc,&relocs,16,16); break; case 25: code=opcode<<16|p->op[0]->reg<<16|absoffset(p->op[1]->offset,p->op[1]->mod,sec,pc,&relocs,16,16); break; case 26: code=opcode|absoffset(p->op[0]->offset,p->op[0]->mod,sec,pc,&relocs,8,8); break; case 27: code=opcode|absval(p->op[0]->offset,sec,pc,7)<<1; break; case 28: code=opcode<<16|p->op[0]->reg<<16|p->op[1]->reg<<20|absoffset(p->op[0]->offset,p->op[0]->mod,sec,pc,&relocs,16,16); break; case 29: code=opcode<<16|absoffset(p->op[1]->offset,p->op[1]->mod,sec,pc,&relocs,8,8)<<16|absoffset(p->op[0]->offset,p->op[0]->mod,sec,pc,&relocs,16,16); break; case 30: code=opcode<<16|p->op[1]->reg<<16|absoffset(p->op[0]->offset,p->op[0]->mod,sec,pc,&relocs,16,16); break; case 31: code=opcode|((absval(p->op[0]->offset,sec,pc,2)-1)<<4); break; case 32: code=opcode|p->op[0]->reg|((absval(p->op[1]->offset,sec,pc,2)-1)<<4); break; case 34: code=opcode<<16|((absval(p->op[1]->offset,sec,pc,2)-1)<<20)|absoffset(p->op[0]->offset,p->op[0]->mod,sec,pc,&relocs,16,8); break; case 33: default: ierror(mnemonics[c].ext.encoding); } d=db->data; if(osize==4){ *d++=code>>24; *d++=code>>16; *d++=code; *d++=code>>8; }else{ *d++=code>>8; *d++=code; } if(jmpconv){ *d++=0xfa; *d++=absoffset2(jmpaddr->offset,0,sec,pc,&relocs,8+8*osize,8,0xffff0000); val=absoffset2(jmpaddr->offset,0,sec,pc,&relocs,16+8*osize,16,0xffff); *d++=val>>8; *d++=val; } 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(4); val=absoffset(op->offset,op->mod,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=translate(p,sec,pc),add=0; if(c&JMPCONV){ add=4;c&=~JMPCONV;} return mnemonics[c].ext.len*2+add; } operand *new_operand() { operand *new=mymalloc(sizeof(*new)); new->type=-1; return new; } /* return true, if initialization was successfull */ int init_cpu() { int i; for(i=0;inext=first_sfr; first_sfr=new; } new->flags=new->laddr=new->saddr=0; new->boffset=0; s=skip(s); if(*s!=',') cpu_error(0); else s=skip(s+1); tree=parse_expr(&s); simplify_expr(tree); if(!tree||tree->type!=NUM) cpu_error(0); else new->laddr=tree->c.val; s=skip(s); if(tree->c.val==0xfe||tree->c.val==0xf0){ if(*s!=',') cpu_error(0); else s=skip(s+1); free_expr(tree); tree=parse_expr(&s); simplify_expr(tree); if(!tree||tree->type!=NUM) cpu_error(0); else new->saddr=tree->c.val; free_expr(tree); s=skip(s); }else{ if(tree->c.val>=0xfe00) new->laddr=0xfe; else new->laddr=0xf0; new->saddr=(tree->c.val-(new->laddr<<8))/2; if((new->laddr<<8)+2*new->saddr!=tree->c.val) ierror(0); free_expr(tree); } if(*s==','){ s=skip(s+1); tree=parse_expr(&s); simplify_expr(tree); new->boffset=tree->c.val; new->flags|=ISBIT; free_expr(tree); } return skip(s); } } return merk; }