1 /*
2  * cpu.c Jaguar RISC cpu description file
3  * (c) in 2014-2017 by Frank Wille
4  */
5 
6 #include "vasm.h"
7 
8 mnemonic mnemonics[] = {
9 #include "opcodes.h"
10 };
11 int mnemonic_cnt = sizeof(mnemonics) / sizeof(mnemonics[0]);
12 
13 char *cpu_copyright = "vasm Jaguar RISC cpu backend 0.4c (c) 2014-2017 Frank Wille";
14 char *cpuname = "jagrisc";
15 int bitsperbyte = 8;
16 int bytespertaddr = 4;
17 
18 int jag_big_endian = 1;  /* defaults to big-endian (Atari Jaguar 68000) */
19 
20 static uint8_t cpu_type = GPU|DSP;
21 static int OC_MOVEI,OC_UNPACK;
22 
23 /* condition codes */
24 static regsym cc_regsyms[] = {
25   {"T",  RTYPE_CC, 0, 0x00},
26   {"NE", RTYPE_CC, 0, 0x01},
27   {"EQ", RTYPE_CC, 0, 0x02},
28   {"CC", RTYPE_CC, 0, 0x04},
29   {"HI", RTYPE_CC, 0, 0x05},
30   {"CS", RTYPE_CC, 0, 0x08},
31   {"PL", RTYPE_CC, 0, 0x14},
32   {"MI", RTYPE_CC, 0, 0x18},
33   {"t",  RTYPE_CC, 0, 0x00},
34   {"ne", RTYPE_CC, 0, 0x01},
35   {"eq", RTYPE_CC, 0, 0x02},
36   {"cc", RTYPE_CC, 0, 0x04},
37   {"hi", RTYPE_CC, 0, 0x05},
38   {"cs", RTYPE_CC, 0, 0x08},
39   {"pl", RTYPE_CC, 0, 0x14},
40   {"mi", RTYPE_CC, 0, 0x18},
41   {NULL, 0, 0, 0}
42 };
43 
44 
init_cpu(void)45 int init_cpu(void)
46 {
47   int i;
48   regsym *r;
49 
50   for (i=0; i<mnemonic_cnt; i++) {
51     if (!strcmp(mnemonics[i].name,"movei"))
52       OC_MOVEI = i;
53     else if (!strcmp(mnemonics[i].name,"unpack"))
54       OC_UNPACK = i;
55   }
56 
57   /* define all condition code register symbols */
58   for (r=cc_regsyms; r->reg_name!=NULL; r++)
59     add_regsym(r);
60 
61   return 1;
62 }
63 
64 
cpu_args(char * p)65 int cpu_args(char *p)
66 {
67   if (!strncmp(p,"-m",2)) {
68     p += 2;
69     if (!stricmp(p,"gpu") || !stricmp(p,"tom"))
70       cpu_type = GPU;
71     else if (!stricmp(p,"dsp") || !stricmp(p,"jerry"))
72       cpu_type = DSP;
73     else if (!strcmp(p,"any"))
74       cpu_type = GPU|DSP;
75     else
76       return 0;
77   }
78   else if (!strcmp(p,"-big"))
79     jag_big_endian = 1;
80   else if (!strcmp(p,"-little"))
81     jag_big_endian = 0;
82   else
83     return 0;
84 
85   return 1;
86 }
87 
88 
parse_reg(char ** p)89 static int parse_reg(char **p)
90 {
91   int reg = -1;
92   char *rp = skip(*p);
93   char *s;
94 
95   if (s = skip_identifier(rp)) {
96     regsym *sym = find_regsym(rp,s-rp);
97 
98     if (sym!=NULL && sym->reg_type==RTYPE_R) {
99       reg = sym->reg_num;
100     }
101     else if (toupper((unsigned char)*rp++) == 'R') {
102       if (sscanf(rp,"%d",&reg)!=1 || reg<0 || reg>31)
103         reg = -1;
104     }
105 
106     if (reg >= 0)
107       *p = s;
108   }
109 
110   return reg;
111 }
112 
113 
parse_cc(char ** p)114 static expr *parse_cc(char **p)
115 {
116   char *end;
117 
118   *p = skip(*p);
119 
120   if (end = skip_identifier(*p)) {
121     regsym *sym = find_regsym(*p,end-*p);
122 
123     if (sym!=NULL && sym->reg_type==RTYPE_CC) {
124       *p = end;
125       return number_expr((taddr)sym->reg_num);
126     }
127   }
128 
129   /* otherwise the condition code is any expression */
130   return parse_expr(p);
131 }
132 
133 
jagswap32(char * d,int32_t w)134 static void jagswap32(char *d,int32_t w)
135 /* write a 32-bit word with swapped halfs (Jaguar MOVEI) */
136 {
137   if (jag_big_endian) {
138     *d++ = (w >> 8) & 0xff;
139     *d++ = w & 0xff;
140     *d++ = (w >> 24) & 0xff;
141     *d = (w >> 16) & 0xff;
142   }
143   else {
144     /* @@@ Need to verify this! */
145     *d++ = w & 0xff;
146     *d++ = (w >> 8) & 0xff;
147     *d++ = (w >> 16) & 0xff;
148     *d = (w >> 24) & 0xff;
149   }
150 }
151 
152 
parse_cpu_special(char * start)153 char *parse_cpu_special(char *start)
154 /* parse cpu-specific directives; return pointer to end of cpu-specific text */
155 {
156   char *name=start;
157   char *s;
158 
159   if (s = skip_identifier(name)) {
160     /* Atari MadMac compatibility directives */
161     if (*name == '.')  /* ignore leading dot */
162       name++;
163 
164     if (s-name==3 && !strnicmp(name,"dsp",3)) {
165       cpu_type = DSP;
166       eol(s);
167       return skip_line(s);
168     }
169 
170     else if (s-name==3 && !strnicmp(name,"gpu",3)) {
171       cpu_type = GPU;
172       eol(s);
173       return skip_line(s);
174     }
175 
176     else if (s-name==8 && !strnicmp(name,"regundef",8) ||
177              s-name==9 && !strnicmp(name,"equrundef",9)) {
178       /* undefine a register symbol */
179       s = skip(s);
180       if (name = parse_identifier(&s)) {
181         undef_regsym(name,0,RTYPE_R);
182         myfree(name);
183         eol(s);
184         return skip_line(s);
185       }
186     }
187 
188     else if (s-name==7 && !strnicmp(name,"ccundef",7)) {
189       /* undefine a condition code symbol */
190       s = skip(s);
191       if (name = parse_identifier(&s)) {
192         undef_regsym(name,0,RTYPE_CC);
193         myfree(name);
194         eol(s);
195         return skip_line(s);
196       }
197     }
198   }
199 
200   return start;
201 }
202 
203 
parse_cpu_label(char * labname,char ** start)204 int parse_cpu_label(char *labname,char **start)
205 /* parse cpu-specific directives following a label field,
206    return zero when no valid directive was recognized */
207 {
208   char *dir=*start;
209   char *s,*name;
210   hashdata data;
211 
212   if (*dir == '.')  /* ignore leading dot */
213     dir++;
214 
215   if (s = skip_identifier(dir)) {
216 
217     if (s-dir==6 && !strnicmp(dir,"regequ",6) ||
218         s-dir==4 && !strnicmp(dir,"equr",4)) {
219       /* label REGEQU Rn || label EQUR Rn */
220       int r;
221 
222       if ((r = parse_reg(&s)) >= 0)
223         new_regsym(0,0,labname,RTYPE_R,0,r);
224       else
225         cpu_error(3);  /* register expected */
226       eol(s);
227       *start = skip_line(s);
228       return 1;
229     }
230 
231     else if (s-dir==5 && !strnicmp(dir,"ccdef",5)) {
232       /* label CCDEF expr */
233       expr *ccexp;
234       taddr val;
235 
236       if ((ccexp = parse_cc(&s)) != NULL) {
237         if (eval_expr(ccexp,&val,NULL,0))
238           new_regsym(0,0,labname,RTYPE_CC,0,(int)val);
239         else
240           general_error(30);  /* expression must be a constant */
241       }
242       else
243         general_error(9);  /* @@@ */
244       eol(s);
245       *start = skip_line(s);
246       return 1;
247     }
248   }
249 
250   return 0;
251 }
252 
253 
new_operand(void)254 operand *new_operand(void)
255 {
256   operand *new = mymalloc(sizeof(*new));
257 
258   new->type = NO_OP;
259   return new;
260 }
261 
262 
jag_data_operand(int bits)263 int jag_data_operand(int bits)
264 /* return data operand type for these number of bits */
265 {
266   if (bits & OPSZ_SWAP)
267     return DATAI_OP;
268   return bits==64 ? DATA64_OP : DATA_OP;
269 }
270 
271 
parse_operand(char * p,int len,operand * op,int required)272 int parse_operand(char *p, int len, operand *op, int required)
273 {
274   int reg;
275 
276   switch (required) {
277     case IMM0:
278     case IMM1:
279     case IMM1S:
280     case SIMM:
281     case IMMLW:
282       if (*p == '#')
283         p = skip(p+1);  /* skip optional '#' */
284     case REL:
285     case DATA_OP:
286     case DATAI_OP:
287       if (required == IMM1S) {
288         op->val = make_expr(SUB,number_expr(32),parse_expr(&p));
289         required = IMM1;  /* turn into IMM1 32-val for SHLQ */
290       }
291       else
292         op->val = parse_expr(&p);
293       break;
294 
295     case DATA64_OP:
296       op->val = parse_expr_huge(&p);
297       break;
298 
299     case REG:  /* Rn */
300       op->reg = parse_reg(&p);
301       if (op->reg < 0)
302         return PO_NOMATCH;
303       break;
304 
305     case IREG:  /* (Rn) */
306       if (*p++ != '(')
307         return PO_NOMATCH;
308       op->reg = parse_reg(&p);
309       if (op->reg < 0)
310         return PO_NOMATCH;
311       if (*p != ')')
312         return PO_NOMATCH;
313       break;
314 
315     case IR14D:  /* (R14+d) */
316     case IR15D:  /* (R15+d) */
317       if (*p++ != '(')
318         return PO_NOMATCH;
319       reg = parse_reg(&p);
320       if ((required==IR14D && reg!=14) || (required==IR15D && reg!=15))
321         return PO_NOMATCH;
322       if (*p++ != '+')
323         return PO_NOMATCH;
324       p = skip(p);
325       op->val = parse_expr(&p);
326       p = skip(p);
327       if (*p != ')')
328         return PO_NOMATCH;
329       break;
330 
331     case IR14R:  /* (R14+Rn) */
332     case IR15R:  /* (R15+Rn) */
333       if (*p++ != '(')
334         return PO_NOMATCH;
335       reg = parse_reg(&p);
336       if ((required==IR14R && reg!=14) || (required==IR15R && reg!=15))
337         return PO_NOMATCH;
338       if (*p++ != '+')
339         return PO_NOMATCH;
340       op->reg = parse_reg(&p);
341       if (op->reg < 0)
342         return PO_NOMATCH;
343       if (*p != ')')
344         return PO_NOMATCH;
345       break;
346 
347     case CC:  /* condition code: t, eq, ne, mi, pl, cc, cs, ... */
348       op->val = parse_cc(&p);
349       break;
350 
351     case PC:  /* PC register */
352       if (toupper((unsigned char)*p) != 'P' ||
353           toupper((unsigned char)*(p+1)) != 'C' ||
354           ISIDCHAR(*(p+2)))
355         return PO_NOMATCH;
356       break;
357 
358     default:
359       return PO_NOMATCH;
360   }
361 
362   op->type = required;
363   return PO_MATCH;
364 }
365 
366 
eval_oper(instruction * ip,operand * op,section * sec,taddr pc,dblock * db)367 static int32_t eval_oper(instruction *ip,operand *op,section *sec,
368                          taddr pc,dblock *db)
369 {
370   symbol *base = NULL;
371   int optype = op->type;
372   int btype;
373   taddr val,loval,hival,mask;
374 
375   switch (optype) {
376     case PC:
377       return 0;
378 
379     case REG:
380     case IREG:
381     case IR14R:
382     case IR15R:
383       return op->reg;
384 
385     case IMM0:
386     case IMM1:
387     case SIMM:
388     case IMMLW:
389     case IR14D:
390     case IR15D:
391     case REL:
392     case CC:
393       mask = 0x1f;
394       if (!eval_expr(op->val,&val,sec,pc))
395         btype = find_base(op->val,&base,sec,pc);
396 
397       if (optype==IMM0 || optype==CC || optype==IMM1 || optype==SIMM) {
398         if (base != NULL) {
399           loval = -32;
400           hival = 32;
401           if (btype != BASE_ILLEGAL) {
402             if (db) {
403               add_extnreloc_masked(&db->relocs,base,val,
404                                    btype==BASE_PCREL?REL_PC:REL_ABS,
405                                    jag_big_endian?6:5,5,0,0x1f);
406               base = NULL;
407             }
408           }
409         }
410         else if (optype==IMM1) {
411           loval = 1;
412           hival = 32;
413         }
414         else if (optype==SIMM) {
415           loval = -16;
416           hival = 15;
417         }
418         else {
419           loval = 0;
420           hival = 31;
421         }
422       }
423       else if (optype==IR14D || optype==IR15D) {
424         if (base==NULL && val==0) {
425           /* Optimize (Rn+0) to (Rn). Assume that the "load/store (Rn+d)"
426              instructions follow directly after "load/store (Rn)". */
427           ip->code -= optype==IR14D ? 1 : 2;
428           op->type = IREG;
429           op->reg = optype==IR14D ? 14 : 15;
430           return op->reg;
431         }
432         loval = 1;
433         hival = 32;
434       }
435       else if (optype==IMMLW) {
436         mask = ~0;
437         if (base != NULL) {
438           if (btype != BASE_ILLEGAL) {
439             if (db) {
440               /* two relocations for LSW first, then MSW */
441               add_extnreloc_masked(&db->relocs,base,val,
442                                    btype==BASE_PCREL?REL_PC:REL_ABS,
443                                    0,16,2,0xffff);
444               add_extnreloc_masked(&db->relocs,base,val,
445                                    btype==BASE_PCREL?REL_PC:REL_ABS,
446                                    16,16,2,0xffff0000);
447               base = NULL;
448             }
449           }
450         }
451       }
452       else if (optype==REL) {
453         loval = -16;
454         hival = 15;
455         if (base!=NULL && btype==BASE_OK) {
456           if (is_pc_reloc(base,sec)) {
457             /* external label or from a different section (distance / 2) */
458             add_extnreloc_masked(&db->relocs,base,val-2,REL_PC,
459                                  jag_big_endian?6:5,5,0,0x3e);
460           }
461           else if (LOCREF(base)) {
462             /* known label from the same section doesn't need a reloc */
463             val = (val - (pc + 2)) / 2;
464           }
465           base = NULL;
466         }
467       }
468       else ierror(0);
469 
470       if (base != NULL)
471         general_error(38);  /* bad or unhandled reloc: illegal relocation */
472 
473       /* range check for this addressing mode */
474       if (mask!=~0 && (val<loval || val>hival))
475         cpu_error(1,(long)loval,(long)hival);
476       return val & mask;
477 
478     default:
479       ierror(0);
480       break;
481   }
482 
483   return 0;  /* default */
484 }
485 
486 
instruction_size(instruction * ip,section * sec,taddr pc)487 size_t instruction_size(instruction *ip, section *sec, taddr pc)
488 {
489   return ip->code==OC_MOVEI ? 6 : 2;
490 }
491 
492 
eval_instruction(instruction * ip,section * sec,taddr pc)493 dblock *eval_instruction(instruction *ip, section *sec, taddr pc)
494 {
495   dblock *db = new_dblock();
496   int32_t src=0,dst=0,extra;
497   int size = 2;
498   uint16_t inst;
499 
500   /* get source and destination argument, when present */
501   if (ip->op[0])
502     dst = eval_oper(ip,ip->op[0],sec,pc,db);
503   if (ip->op[1]) {
504     if (ip->code == OC_MOVEI) {
505       extra = dst;
506       size = 6;
507     }
508     else
509       src = dst;
510     dst = eval_oper(ip,ip->op[1],sec,pc,db);
511   }
512   else if (ip->code == OC_UNPACK)
513     src = 1;  /* pack(src=0)/unpack(src=1) use the same opcode */
514 
515   /* store and jump instructions need the second operand in the source field */
516   if (mnemonics[ip->code].ext.flags & OPSWAP) {
517     extra = src;
518     src = dst;
519     dst = extra;
520   }
521 
522   /* allocate dblock data for instruction */
523   db->size = size;
524   db->data = mymalloc(size);
525 
526   /* construct the instruction word out of opcode and source/dest. value */
527   inst = (mnemonics[ip->code].ext.opcode & 63) << 10;
528   inst |= ((src&31) << 5) | (dst & 31);
529 
530   /* write instruction */
531   if (jag_big_endian) {
532     db->data[0] = (inst >> 8) & 0xff;
533     db->data[1] = inst & 0xff;
534   }
535   else {
536     db->data[0] = inst & 0xff;
537     db->data[1] = (inst >> 8) & 0xff;
538   }
539 
540   /* extra words for MOVEI are always written in the order lo-word, hi-word */
541   if (size == 6)
542     jagswap32(&db->data[2],extra);
543 
544   return db;
545 }
546 
547 
eval_data(operand * op,size_t bitsize,section * sec,taddr pc)548 dblock *eval_data(operand *op, size_t bitsize, section *sec, taddr pc)
549 {
550   dblock *db = new_dblock();
551   taddr val;
552 
553   if (bitsize!=8 && bitsize!=16 && bitsize!=32 && bitsize!=64)
554     cpu_error(0,bitsize);  /* data size not supported */
555 
556   if (op->type!=DATA_OP && op->type!=DATA64_OP && op->type!=DATAI_OP)
557     ierror(0);
558 
559   db->size = bitsize >> 3;
560   db->data = mymalloc(db->size);
561 
562   if (op->type == DATA64_OP) {
563     thuge hval;
564 
565     if (!eval_expr_huge(op->val,&hval))
566       general_error(59);  /* cannot evaluate huge integer */
567     huge_to_mem(jag_big_endian,db->data,db->size,hval);
568   }
569   else {
570     if (!eval_expr(op->val,&val,sec,pc)) {
571       symbol *base;
572       int btype;
573 
574       btype = find_base(op->val,&base,sec,pc);
575       if (base!=NULL && btype!=BASE_ILLEGAL) {
576         if (op->type == DATAI_OP) {
577           /* swapped: two relocations for LSW first, then MSW */
578           add_extnreloc_masked(&db->relocs,base,val,
579                                btype==BASE_PCREL?REL_PC:REL_ABS,
580                                0,16,0,0xffff);
581           add_extnreloc_masked(&db->relocs,base,val,
582                                btype==BASE_PCREL?REL_PC:REL_ABS,
583                                16,16,0,0xffff0000);
584         }
585         else /* normal 8, 16, 32 bit relocation */
586           add_extnreloc(&db->relocs,base,val,
587                         btype==BASE_PCREL?REL_PC:REL_ABS,0,bitsize,0);
588       }
589       else if (btype != BASE_NONE)
590         general_error(38);  /* illegal relocation */
591     }
592 
593     switch (db->size) {
594       case 1:
595         db->data[0] = val & 0xff;
596         break;
597       case 2:
598       case 4:
599         if (op->type == DATAI_OP)
600           jagswap32(db->data,(int32_t)val);
601         else
602           setval(jag_big_endian,db->data,db->size,val);
603         break;
604       default:
605         ierror(0);
606         break;
607     }
608   }
609 
610   return db;
611 }
612 
613 
cpu_available(int idx)614 int cpu_available(int idx)
615 {
616   return (mnemonics[idx].ext.flags & cpu_type) != 0;
617 }
618