1 /*
2 ** cpu.c 650x/651x cpu-description file
3 ** (c) in 2002,2006,2008-2012,2014-2017 by Frank Wille
4 */
5
6 #include "vasm.h"
7
8 mnemonic mnemonics[] = {
9 #include "opcodes.h"
10 };
11
12 int mnemonic_cnt=sizeof(mnemonics)/sizeof(mnemonics[0]);
13
14 char *cpu_copyright="vasm 6502 cpu backend 0.7d (c) 2002,2006,2008-2012,2014-2017 Frank Wille";
15 char *cpuname = "6502";
16 int bitsperbyte = 8;
17 int bytespertaddr = 2;
18
19 static uint16_t cpu_type = M6502;
20 static int branchopt = 0;
21 static int modifier; /* set by find_base() */
22
23
ext_unary_eval(int type,taddr val,taddr * result,int cnst)24 int ext_unary_eval(int type,taddr val,taddr *result,int cnst)
25 {
26 switch (type) {
27 case LOBYTE:
28 *result = cnst ? (val & 0xff) : val;
29 return 1;
30 case HIBYTE:
31 *result = cnst ? ((val >> 8) & 0xff) : val;
32 return 1;
33 default:
34 break;
35 }
36 return 0; /* unknown type */
37 }
38
39
ext_find_base(symbol ** base,expr * p,section * sec,taddr pc)40 int ext_find_base(symbol **base,expr *p,section *sec,taddr pc)
41 {
42 /* addr/256 equals >addr, addr%256 and addr&255 equal <addr */
43 if (p->type==DIV || p->type==MOD) {
44 if (p->right->type==NUM && p->right->c.val==256)
45 p->type = p->type == DIV ? HIBYTE : LOBYTE;
46 }
47 else if (p->type==BAND && p->right->type==NUM && p->right->c.val==255)
48 p->type = LOBYTE;
49
50 if (p->type==LOBYTE || p->type==HIBYTE) {
51 modifier = p->type;
52 return find_base(p->left,base,sec,pc);
53 }
54 return BASE_ILLEGAL;
55 }
56
57
parse_operand(char * p,int len,operand * op,int required)58 int parse_operand(char *p,int len,operand *op,int required)
59 {
60 char *start = p;
61 int indir = 0;
62
63 p = skip(p);
64 if (len>0 && required!=DATAOP && check_indir(p,start+len)) {
65 indir = 1;
66 p = skip(p+1);
67 }
68
69 switch (required) {
70 case IMMED:
71 if (*p++ != '#')
72 return PO_NOMATCH;
73 p = skip(p);
74 break;
75 case INDIR:
76 case INDIRX:
77 case INDX:
78 case INDY:
79 case DPINDIR:
80 if (!indir)
81 return PO_NOMATCH;
82 break;
83 default:
84 if (indir)
85 return PO_NOMATCH;
86 break;
87 }
88
89 if (required < ACCU)
90 op->value = parse_expr(&p);
91 else
92 op->value = NULL;
93
94 switch (required) {
95 case INDX:
96 case INDIRX:
97 if (*p++ == ',') {
98 p = skip(p);
99 if (toupper((unsigned char)*p++) != 'X')
100 return PO_NOMATCH;
101 }
102 else
103 return PO_NOMATCH;
104 break;
105 case ACCU:
106 if (len != 0) {
107 if (len!=1 || toupper((unsigned char)*p++) != 'A')
108 return PO_NOMATCH;
109 }
110 break;
111 case DUMX:
112 if (toupper((unsigned char)*p++) != 'X')
113 return PO_NOMATCH;
114 break;
115 case DUMY:
116 if (toupper((unsigned char)*p++) != 'Y')
117 return PO_NOMATCH;
118 break;
119 }
120
121 if (required==INDIR || required==INDX || required==INDY
122 || required==DPINDIR || required==INDIRX) {
123 p = skip(p);
124 if (*p++ != ')') {
125 cpu_error(2); /* missing closing parenthesis */
126 return PO_CORRUPT;
127 }
128 }
129
130 p = skip(p);
131 if (p-start < len)
132 cpu_error(1); /* trailing garbage in operand */
133 op->type = required;
134 return PO_MATCH;
135 }
136
137
parse_cpu_special(char * start)138 char *parse_cpu_special(char *start)
139 {
140 return start;
141 }
142
143
copy_instruction(instruction * ip)144 static instruction *copy_instruction(instruction *ip)
145 /* copy an instruction and its operands */
146 {
147 static instruction newip;
148 static operand newop;
149
150 newip.code = ip->code;
151 if (ip->op[0] != NULL) {
152 newip.op[0] = &newop;
153 *newip.op[0] = *ip->op[0];
154 }
155 else
156 newip.op[0] = NULL;
157
158 return &newip;
159 }
160
161
optimize_instruction(instruction * ip,section * sec,taddr pc,int final)162 static void optimize_instruction(instruction *ip,section *sec,
163 taddr pc,int final)
164 {
165 mnemonic *mnemo = &mnemonics[ip->code];
166 operand *op = ip->op[0];
167 taddr val;
168
169 if (op != NULL) {
170 if (op->value != NULL) {
171 if (eval_expr(op->value,&val,sec,pc)) {
172 if ((op->type==ABS || op->type==ABSX || op->type==ABSY)
173 && (val>=0 && val<=0xff) && mnemo->ext.zp_opcode!=0) {
174 /* we can use a zero page addressing mode */
175 op->type += ZPAGE-ABS;
176 }
177 }
178 else {
179 symbol *base;
180
181 if (find_base(op->value,&base,sec,pc) == BASE_OK) {
182 if ((op->type==ABS || op->type==ABSX || op->type==ABSY)
183 && base->sec!=NULL && (base->sec->flags & ABSOLUTE)
184 && (val>=0 && val<=0xff) && mnemo->ext.zp_opcode!=0) {
185 /* we can use a zero page addressing mode */
186 op->type += ZPAGE-ABS;
187 }
188 else if (op->type==REL && LOCREF(base) && base->sec==sec) {
189 taddr bd = val - (pc + 2);
190
191 if ((bd<-0x80 || bd>0x7f) && branchopt) {
192 /* branch dest. out of range: use a B!cc/JMP combination */
193 op->type = RELJMP;
194 }
195 }
196 }
197 }
198 }
199 }
200 }
201
202
get_inst_size(instruction * ip)203 static size_t get_inst_size(instruction *ip)
204 {
205 if (ip->op[0] != NULL) {
206 switch (ip->op[0]->type) {
207 case ACCU:
208 case IMPLIED:
209 return 1;
210 case REL:
211 case INDX:
212 case INDY:
213 case DPINDIR:
214 case IMMED:
215 case ZPAGE:
216 case ZPAGEX:
217 case ZPAGEY:
218 return 2;
219 case ABS:
220 case ABSX:
221 case ABSY:
222 case INDIR:
223 case INDIRX:
224 return 3;
225 case RELJMP:
226 return 5;
227 default:
228 ierror(0);
229 break;
230 }
231 }
232 return 1;
233 }
234
235
instruction_size(instruction * ip,section * sec,taddr pc)236 size_t instruction_size(instruction *ip,section *sec,taddr pc)
237 {
238 instruction *ipcopy;
239
240 if (ip->op[0]!=NULL && ip->op[1]!=NULL) {
241 /* combine DUMX/DUMY operands into real addressing modes first */
242 if (ip->op[0]->type == ABS) {
243 if (ip->op[1]->type == DUMX)
244 ip->op[0]->type = ABSX;
245 else if (ip->op[1]->type == DUMY)
246 ip->op[0]->type = ABSY;
247 else
248 ierror(0);
249 }
250 else if (ip->op[0]->type == INDIR) {
251 if (ip->op[1]->type == DUMY)
252 ip->op[0]->type = INDY;
253 else
254 ierror(0);
255 }
256 else
257 ierror(0);
258 myfree(ip->op[1]);
259 ip->op[1] = NULL;
260 }
261
262 ipcopy = copy_instruction(ip);
263 optimize_instruction(ipcopy,sec,pc,0);
264 return get_inst_size(ipcopy);
265 }
266
267
rangecheck(taddr val,int type)268 static void rangecheck(taddr val,int type)
269 {
270 switch (type) {
271 case INDX:
272 case INDY:
273 case DPINDIR:
274 case ZPAGE:
275 case ZPAGEX:
276 case ZPAGEY:
277 case IMMED:
278 if (val<-0x80 || val>0xff)
279 cpu_error(5); /* operand doesn't fit into 8-bits */
280 break;
281 case REL:
282 if (val<-0x80 || val>0x7f)
283 cpu_error(6); /* branch destination out of range */
284 break;
285 }
286 }
287
288
eval_instruction(instruction * ip,section * sec,taddr pc)289 dblock *eval_instruction(instruction *ip,section *sec,taddr pc)
290 {
291 dblock *db = new_dblock();
292 unsigned char *d;
293 int optype = 0;
294 taddr val;
295
296 optimize_instruction(ip,sec,pc,1); /* really execute optimizations now */
297 db->size = get_inst_size(ip);
298 d = db->data = mymalloc(db->size);
299
300 if (ip->op[0] != NULL) {
301 operand *op = ip->op[0];
302 symbol *base;
303
304 optype = (int)op->type;
305 if (op->value != NULL) {
306 if (!eval_expr(op->value,&val,sec,pc)) {
307 modifier = 0;
308 if (find_base(op->value,&base,sec,pc) == BASE_OK) {
309 if (optype==REL && !is_pc_reloc(base,sec)) {
310 /* relative branch requires no relocation */
311 val = val - (pc + 2);
312 }
313 else {
314 int type=REL_ABS,offs=1,size;
315 rlist *rl;
316
317 switch (optype) {
318 case ABS:
319 case ABSX:
320 case ABSY:
321 case INDIR:
322 case INDIRX:
323 size = 16;
324 break;
325 case INDX:
326 case INDY:
327 case DPINDIR:
328 case ZPAGE:
329 case ZPAGEX:
330 case ZPAGEY:
331 case IMMED:
332 size = 8;
333 break;
334 case RELJMP:
335 size = 16;
336 offs = 3;
337 break;
338 case REL:
339 type = REL_PC;
340 size = 8;
341 break;
342 default:
343 ierror(0);
344 break;
345 }
346 rl = add_extnreloc(&db->relocs,base,val,type,0,size,offs);
347 switch (modifier) {
348 case LOBYTE:
349 if (rl)
350 ((nreloc *)rl->reloc)->mask = 0xff;
351 val = val & 0xff;
352 break;
353 case HIBYTE:
354 if (rl)
355 ((nreloc *)rl->reloc)->mask = 0xff00;
356 val = (val >> 8) & 0xff;
357 break;
358 }
359 }
360 }
361 else
362 general_error(38); /* illegal relocation */
363 }
364 rangecheck(val,op->type);
365 }
366 }
367
368 /* write code */
369 if (optype==ZPAGE || optype==ZPAGEX || optype==ZPAGEY)
370 *d++ = mnemonics[ip->code].ext.zp_opcode;
371 else if (optype==RELJMP)
372 *d++ = mnemonics[ip->code].ext.opcode ^ 0x20; /* B!cc branch */
373 else
374 *d++ = mnemonics[ip->code].ext.opcode;
375
376 switch (optype) {
377 case ABSX:
378 case ABSY:
379 if (*(d-1) == 0) /* STX/STY allow only ZeroPage addressing mode */
380 cpu_error(5); /* operand doesn't fit into 8-bits */
381 case ABS:
382 case INDIR:
383 case INDIRX:
384 case DPINDIR:
385 *d++ = val & 0xff;
386 *d = (val>>8) & 0xff;
387 break;
388 case INDX:
389 case INDY:
390 case ZPAGE:
391 case ZPAGEX:
392 case ZPAGEY:
393 case IMMED:
394 case REL:
395 *d = val & 0xff;
396 break;
397 case RELJMP:
398 *d++ = 3; /* B!cc *+3 */
399 *d++ = 0x4c; /* JMP */
400 *d++ = val & 0xff;
401 *d = (val>>8) & 0xff;
402 break;
403 }
404
405 return db;
406 }
407
408
eval_data(operand * op,size_t bitsize,section * sec,taddr pc)409 dblock *eval_data(operand *op,size_t bitsize,section *sec,taddr pc)
410 {
411 dblock *db = new_dblock();
412 taddr val;
413
414 if (bitsize!=8 && bitsize!=16)
415 cpu_error(3,bitsize); /* data size not supported */
416
417 db->size = bitsize >> 3;
418 db->data = mymalloc(db->size);
419 if (!eval_expr(op->value,&val,sec,pc)) {
420 symbol *base;
421 int btype;
422 rlist *rl;
423
424 modifier = 0;
425 btype = find_base(op->value,&base,sec,pc);
426 if (btype==BASE_OK || (btype==BASE_PCREL && modifier==0)) {
427 rl = add_extnreloc(&db->relocs,base,val,
428 btype==BASE_PCREL?REL_PC:REL_ABS,0,bitsize,0);
429 switch (modifier) {
430 case LOBYTE:
431 if (rl)
432 ((nreloc *)rl->reloc)->mask = 0xff;
433 val = val & 0xff;
434 break;
435 case HIBYTE:
436 if (rl)
437 ((nreloc *)rl->reloc)->mask = 0xff00;
438 val = (val >> 8) & 0xff;
439 break;
440 }
441 }
442 else if (btype != BASE_NONE)
443 general_error(38); /* illegal relocation */
444 }
445 if (bitsize < 16) {
446 if (val<-0x80 || val>0xff)
447 cpu_error(5); /* operand doesn't fit into 8-bits */
448 }
449
450 switch (db->size) {
451 case 2:
452 db->data[1] = (val>>8) & 0xff;
453 case 1:
454 db->data[0] = val & 0xff;
455 break;
456 default:
457 ierror(0);
458 break;
459 }
460
461 return db;
462 }
463
464
new_operand()465 operand *new_operand()
466 {
467 operand *new = mymalloc(sizeof(*new));
468 new->type = -1;
469 return new;
470 }
471
472
cpu_available(int idx)473 int cpu_available(int idx)
474 {
475 return (mnemonics[idx].ext.available & cpu_type) != 0;
476 }
477
478
init_cpu()479 int init_cpu()
480 {
481 return 1;
482 }
483
484
cpu_args(char * p)485 int cpu_args(char *p)
486 {
487 if (!strcmp(p,"-opt-branch"))
488 branchopt = 1;
489 else if (!strcmp(p,"-illegal"))
490 cpu_type |= ILL;
491 else if (!strcmp(p,"-dtv"))
492 cpu_type |= DTV;
493 else if (!strcmp(p,"-c02"))
494 cpu_type = M6502 | M65C02;
495 else
496 return 0;
497
498 return 1;
499 }
500