1 /* cpu.c example cpu-description file */
2 /* (c) in 2002 by Volker Barthelmann */
3
4 #include "vasm.h"
5
6 char *cpu_copyright="vasm test cpu backend (c) in 2002 Volker Barthelmann";
7
8 /* example machine.
9 valid Registers: R0-R3
10 valid extensions: .b/.w (default .w)
11
12 Instruction format:
13 XCCCCCCC 11112222 [32bit op1] [32bit op2]
14
15 X: set if byte-extension
16 C: instruction code
17 1/2: operand 1/2 type
18 0-3 register
19 4-7 register indirect (32bit offset follows)
20 8 absolute (32bit value follows)
21 9 immediate (32bit value follows)
22 Special case for addq: 1111: immediate 0-15
23 Special case for bra: 11112222: 0-255 relative offset
24 */
25
26 char *cpuname="test";
27 int bitsperbyte=8;
28 int bytespertaddr=4;
29
30 mnemonic mnemonics[]={
31 "move",{OP_ALL,OP_REG},{CPU_ALL,0x0},
32 "move",{OP_REG,OP_ALL_DEST},{CPU_ALL,0x1},
33 "add",{OP_ALL,OP_REG},{CPU_ALL,0x2},
34 "add",{OP_REG,OP_ALL_DEST},{CPU_ALL,0x3},
35 "add",{OP_IMM32,OP_MEM},{CPU_ALL,0x4},
36 "addq",{OP_IMM32,OP_ALL_DEST},{CPU_ALL,0x5},
37 "jmp",{OP_ABS,0},{CPU_ALL,0x6},
38 "bra",{OP_ABS,0},{CPU_ALL,0x7},
39 };
40
41 int mnemonic_cnt=sizeof(mnemonics)/sizeof(mnemonics[0]);
42
43
parse_instruction(char * s,int * inst_len,char ** ext,int * ext_len,int * ext_cnt)44 char *parse_instruction(char *s,int *inst_len,char **ext,int *ext_len,
45 int *ext_cnt)
46 /* parse instruction and save extension locations */
47 {
48 char *inst = s;
49
50 while (*s && *s!='.' && !isspace((unsigned char)*s))
51 s++;
52 *inst_len = s - inst;
53 if (*s =='.') {
54 /* extension present */
55 ext[*ext_cnt] = ++s;
56 while (*s && *s!='.' && !isspace((unsigned char)*s))
57 s++;
58 ext_len[*ext_cnt] = s - ext[*ext_cnt];
59 *ext_cnt += 1;
60 }
61 return s;
62 }
63
set_default_qualifiers(char ** q,int * q_len)64 int set_default_qualifiers(char **q,int *q_len)
65 /* fill in pointers to default qualifiers, return number of qualifiers */
66 {
67 q[0] = "w";
68 q_len[0] = 1;
69 return 1;
70 }
71
72 /* Does not do much useful parsing yet. */
parse_operand(char * p,int len,operand * op,int requires)73 int parse_operand(char *p,int len,operand *op,int requires)
74 {
75 p=skip(p);
76 if(len==2&&(p[0]=='r'||p[0]=='R')&&p[1]>='0'&&p[1]<='3'){
77 op->type=OP_REG;
78 op->basereg=p[1]-'0';
79 }else if(p[0]=='#'){
80 op->type=OP_IMM32;
81 p=skip(p+1);
82 op->offset=parse_expr(&p);
83 }else{
84 int parent=0;
85 expr *tree;
86 op->type=-1;
87 if(*p=='('){
88 parent=1;
89 p=skip(p+1);
90 }
91 tree=parse_expr(&p);
92 if(!tree)
93 return 0;
94 p=skip(p);
95 if(parent){
96 if(*p==','){
97 p=skip(p+1);
98 if((*p!='r'&&*p!='R')||p[1]<'0'||p[1]>'3'){
99 cpu_error(0);
100 return 0;
101 }
102 op->type=OP_REGIND;
103 op->basereg=p[1]-'0';
104 p=skip(p+2);
105 }
106 if(*p!=')'){
107 cpu_error(0);
108 return 0;
109 }else
110 p=skip(p+1);
111 }
112 if(op->type!=OP_REGIND)
113 op->type=OP_ABS;
114 op->offset=tree;
115 }
116 if(requires==op->type)
117 return 1;
118 if(requires==OP_ALL_DEST&&op->type!=OP_IMM32)
119 return 1;
120 if(requires==OP_MEM&&OP_ISMEM(op->type))
121 return 1;
122 if(requires==OP_ALL)
123 return 1;
124 return 0;
125 }
126
127 /* Return new instruction code, if instruction can be optimized
128 to another one. */
opt_inst(instruction * p,section * sec,taddr pc)129 static int opt_inst(instruction *p,section *sec,taddr pc)
130 {
131 /* Ganz simples Beispiel. */
132
133 /* add->addq */
134 if((p->code==2||p->code==4)&&p->op[0]->type==OP_IMM32){
135 taddr val;
136 if(eval_expr(p->op[0]->offset,&val,sec,pc)&&val<16)
137 return 5;
138 }
139 /* jmp->bra */
140 if(p->code==6){
141 expr *tree=p->op[0]->offset;
142 if(tree->type==SYM&&tree->c.sym->sec==sec&&LOCREF(tree->c.sym)&&
143 tree->c.sym->pc-pc>=-128&&tree->c.sym->pc-pc<=127)
144 return 7;
145 }
146 return p->code;
147 }
148
operand_code(operand * p)149 static int operand_code(operand *p)
150 {
151 if(!p)
152 return 0;
153 if(p->type==OP_REG)
154 return p->basereg;
155 if(p->type==OP_REGIND)
156 return 4+p->basereg;
157 if(p->type==OP_ABS)
158 return 8;
159 if(p->type==OP_IMM32)
160 return 9;
161 ierror(0);
162 }
fill_operand(operand * p,section * sec,taddr pc,char * d,rlist ** relocs,int roffset)163 static char *fill_operand(operand *p,section *sec,taddr pc,char *d,rlist **relocs,int roffset)
164 {
165 taddr val;
166 if(!p||p->type==OP_REG)
167 return d;
168 /* FIXME: Test for valid operand, create reloc */
169 if(!eval_expr(p->offset,&val,sec,pc)){
170 symbol *base;
171 if (find_base(p->offset,&base,sec,pc)!=BASE_OK)
172 general_error(38);
173 else
174 add_nreloc(relocs,base,0,REL_ABS,16,roffset*8);
175 }
176 *d++=val>>24;
177 *d++=val>>16;
178 *d++=val>>8;
179 *d++=val;
180 return d;
181 }
182
183 /* Convert an instruction into a DATA atom including relocations,
184 if necessary. */
eval_instruction(instruction * p,section * sec,taddr pc)185 dblock *eval_instruction(instruction *p,section *sec,taddr pc)
186 {
187 /* Auch simpel. Fehlerchecks fehlen. */
188 size_t size=instruction_size(p,sec,pc);
189 dblock *db=new_dblock();
190 int c=opt_inst(p,sec,pc);
191 unsigned char *d;
192 taddr val;
193 db->size=size;
194 d=db->data=mymalloc(size);
195 *d=c;
196 if(p->qualifiers[0]){
197 if(c>5)
198 cpu_error(1,p->qualifiers[0]);
199 else if(!strcmp(p->qualifiers[0],"b"))
200 *d|=128;
201 else if(strcmp(p->qualifiers[0],"w"))
202 cpu_error(1,p->qualifiers[0]);
203 }
204 d++;
205 if(c==5){
206 /* addq */
207 taddr val;
208 if(!eval_expr(p->op[0]->offset,&val,sec,pc)||val>15)
209 cpu_error(0);
210 *d=((val<<4)|operand_code(p->op[1]));
211 return db;
212 }
213 if(c==7){
214 expr *tree=p->op[0]->offset;
215 if(!(tree->type==SYM&&tree->c.sym->sec==sec&&LOCREF(tree->c.sym)&&
216 tree->c.sym->pc-pc>=-128&&tree->c.sym->pc-pc<=127))
217 cpu_error(0);
218 else
219 *d=tree->c.sym->pc-pc;
220 return db;
221 }
222
223 *d=((operand_code(p->op[0])<<4)|operand_code(p->op[1]));
224 d++;
225 d=fill_operand(p->op[0],sec,pc,d,&db->relocs,d-db->data);
226 d=fill_operand(p->op[1],sec,pc,d,&db->relocs,d-db->data);
227 return db;
228 }
229
230 /* Create a dblock (with relocs, if necessary) for size bits of data. */
eval_data(operand * op,size_t bitsize,section * sec,taddr pc)231 dblock *eval_data(operand *op,size_t bitsize,section *sec,taddr pc)
232 {
233 dblock *new=new_dblock();
234 taddr val;
235 new->size=(bitsize+7)/8;
236 new->data=mymalloc(new->size);
237 if(op->type!=OP_ABS)
238 ierror(0);
239 if(bitsize!=8&&bitsize!=16&&bitsize!=32)
240 cpu_error(2);
241 if(!eval_expr(op->offset,&val,sec,pc)&&bitsize!=32)
242 general_error(38);
243 if(bitsize==8){
244 new->data[0]=val;
245 }else if(bitsize==16){
246 new->data[0]=val>>8;
247 new->data[1]=val;
248 }else if(bitsize==32){
249 fill_operand(op,sec,pc,new->data,&new->relocs,0);
250 }
251 return new;
252 }
253
opsize(operand * p)254 static taddr opsize(operand *p)
255 {
256 if(!p)
257 return 0;
258 if(p->type!=OP_REG)
259 return 4;
260 else
261 return 0;
262 }
263
264 /* Calculate the size of the current instruction; must be identical
265 to the data created by eval_instruction. */
instruction_size(instruction * p,section * sec,taddr pc)266 size_t instruction_size(instruction *p,section *sec,taddr pc)
267 {
268 int c;
269 size_t size=2;
270 size+=opsize(p->op[0]);
271 size+=opsize(p->op[1]);
272 c=opt_inst(p,sec,pc);
273 if(c==5||c==7){
274 /* addq/bra */
275 size-=4;
276 }
277 return size;
278 }
279
new_operand()280 operand *new_operand()
281 {
282 operand *new=mymalloc(sizeof(*new));
283 new->type=-1;
284 return new;
285 }
286
287 /* return true, if initialization was successfull */
init_cpu()288 int init_cpu()
289 {
290 return 1;
291 }
292
293 /* return true, if the passed argument is understood */
cpu_args(char * p)294 int cpu_args(char *p)
295 {
296 /* no args */
297 return 0;
298 }
299
300 /* parse cpu-specific directives; return pointer to end of
301 cpu-specific text */
parse_cpu_special(char * s)302 char *parse_cpu_special(char *s)
303 {
304 /* no specials */
305 return s;
306 }
307