/* cpu.c example cpu-description file */ /* (c) in 2002 by Volker Barthelmann */ #include "vasm.h" char *cpu_copyright="vasm test cpu backend (c) in 2002 Volker Barthelmann"; /* example machine. valid Registers: R0-R3 valid extensions: .b/.w (default .w) Instruction format: XCCCCCCC 11112222 [32bit op1] [32bit op2] X: set if byte-extension C: instruction code 1/2: operand 1/2 type 0-3 register 4-7 register indirect (32bit offset follows) 8 absolute (32bit value follows) 9 immediate (32bit value follows) Special case for addq: 1111: immediate 0-15 Special case for bra: 11112222: 0-255 relative offset */ char *cpuname="test"; int bitsperbyte=8; int bytespertaddr=4; mnemonic mnemonics[]={ "move",{OP_ALL,OP_REG},{CPU_ALL,0x0}, "move",{OP_REG,OP_ALL_DEST},{CPU_ALL,0x1}, "add",{OP_ALL,OP_REG},{CPU_ALL,0x2}, "add",{OP_REG,OP_ALL_DEST},{CPU_ALL,0x3}, "add",{OP_IMM32,OP_MEM},{CPU_ALL,0x4}, "addq",{OP_IMM32,OP_ALL_DEST},{CPU_ALL,0x5}, "jmp",{OP_ABS,0},{CPU_ALL,0x6}, "bra",{OP_ABS,0},{CPU_ALL,0x7}, }; int mnemonic_cnt=sizeof(mnemonics)/sizeof(mnemonics[0]); 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; while (*s && *s!='.' && !isspace((unsigned char)*s)) s++; *inst_len = s - inst; if (*s =='.') { /* extension present */ ext[*ext_cnt] = ++s; while (*s && *s!='.' && !isspace((unsigned char)*s)) s++; ext_len[*ext_cnt] = s - ext[*ext_cnt]; *ext_cnt += 1; } return s; } int set_default_qualifiers(char **q,int *q_len) /* fill in pointers to default qualifiers, return number of qualifiers */ { q[0] = "w"; q_len[0] = 1; return 1; } /* Does not do much useful parsing yet. */ int parse_operand(char *p,int len,operand *op,int requires) { p=skip(p); if(len==2&&(p[0]=='r'||p[0]=='R')&&p[1]>='0'&&p[1]<='3'){ op->type=OP_REG; op->basereg=p[1]-'0'; }else if(p[0]=='#'){ op->type=OP_IMM32; p=skip(p+1); op->offset=parse_expr(&p); }else{ int parent=0; expr *tree; op->type=-1; if(*p=='('){ parent=1; p=skip(p+1); } tree=parse_expr(&p); if(!tree) return 0; p=skip(p); if(parent){ if(*p==','){ p=skip(p+1); if((*p!='r'&&*p!='R')||p[1]<'0'||p[1]>'3'){ cpu_error(0); return 0; } op->type=OP_REGIND; op->basereg=p[1]-'0'; p=skip(p+2); } if(*p!=')'){ cpu_error(0); return 0; }else p=skip(p+1); } if(op->type!=OP_REGIND) op->type=OP_ABS; op->offset=tree; } if(requires==op->type) return 1; if(requires==OP_ALL_DEST&&op->type!=OP_IMM32) return 1; if(requires==OP_MEM&&OP_ISMEM(op->type)) return 1; if(requires==OP_ALL) return 1; return 0; } /* Return new instruction code, if instruction can be optimized to another one. */ static int opt_inst(instruction *p,section *sec,taddr pc) { /* Ganz simples Beispiel. */ /* add->addq */ if((p->code==2||p->code==4)&&p->op[0]->type==OP_IMM32){ taddr val; if(eval_expr(p->op[0]->offset,&val,sec,pc)&&val<16) return 5; } /* jmp->bra */ if(p->code==6){ expr *tree=p->op[0]->offset; if(tree->type==SYM&&tree->c.sym->sec==sec&&LOCREF(tree->c.sym)&& tree->c.sym->pc-pc>=-128&&tree->c.sym->pc-pc<=127) return 7; } return p->code; } static int operand_code(operand *p) { if(!p) return 0; if(p->type==OP_REG) return p->basereg; if(p->type==OP_REGIND) return 4+p->basereg; if(p->type==OP_ABS) return 8; if(p->type==OP_IMM32) return 9; ierror(0); } static char *fill_operand(operand *p,section *sec,taddr pc,char *d,rlist **relocs,int roffset) { taddr val; if(!p||p->type==OP_REG) return d; /* FIXME: Test for valid operand, create reloc */ if(!eval_expr(p->offset,&val,sec,pc)){ symbol *base; if (find_base(p->offset,&base,sec,pc)!=BASE_OK) general_error(38); else add_nreloc(relocs,base,0,REL_ABS,16,roffset*8); } *d++=val>>24; *d++=val>>16; *d++=val>>8; *d++=val; return d; } /* Convert an instruction into a DATA atom including relocations, if necessary. */ dblock *eval_instruction(instruction *p,section *sec,taddr pc) { /* Auch simpel. Fehlerchecks fehlen. */ size_t size=instruction_size(p,sec,pc); dblock *db=new_dblock(); int c=opt_inst(p,sec,pc); unsigned char *d; taddr val; db->size=size; d=db->data=mymalloc(size); *d=c; if(p->qualifiers[0]){ if(c>5) cpu_error(1,p->qualifiers[0]); else if(!strcmp(p->qualifiers[0],"b")) *d|=128; else if(strcmp(p->qualifiers[0],"w")) cpu_error(1,p->qualifiers[0]); } d++; if(c==5){ /* addq */ taddr val; if(!eval_expr(p->op[0]->offset,&val,sec,pc)||val>15) cpu_error(0); *d=((val<<4)|operand_code(p->op[1])); return db; } if(c==7){ expr *tree=p->op[0]->offset; if(!(tree->type==SYM&&tree->c.sym->sec==sec&&LOCREF(tree->c.sym)&& tree->c.sym->pc-pc>=-128&&tree->c.sym->pc-pc<=127)) cpu_error(0); else *d=tree->c.sym->pc-pc; return db; } *d=((operand_code(p->op[0])<<4)|operand_code(p->op[1])); d++; d=fill_operand(p->op[0],sec,pc,d,&db->relocs,d-db->data); d=fill_operand(p->op[1],sec,pc,d,&db->relocs,d-db->data); 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); if(!eval_expr(op->offset,&val,sec,pc)&&bitsize!=32) general_error(38); if(bitsize==8){ new->data[0]=val; }else if(bitsize==16){ new->data[0]=val>>8; new->data[1]=val; }else if(bitsize==32){ fill_operand(op,sec,pc,new->data,&new->relocs,0); } return new; } static taddr opsize(operand *p) { if(!p) return 0; if(p->type!=OP_REG) return 4; else return 0; } /* 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; size_t size=2; size+=opsize(p->op[0]); size+=opsize(p->op[1]); c=opt_inst(p,sec,pc); if(c==5||c==7){ /* addq/bra */ size-=4; } return size; } 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) { /* no args */ return 0; } /* parse cpu-specific directives; return pointer to end of cpu-specific text */ char *parse_cpu_special(char *s) { /* no specials */ return s; }