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