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",®)!=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