1 ////////////////////////////////////////////////////////////////////////////////////////////////////
2 // RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System
3 // RISCA.C - GPU/DSP Assembler
4 // Copyright (C) 199x Landon Dyer, 2011 Reboot and Friends
5 // RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986
6 // Source Utilised with the Kind Permission of Landon Dyer
17 #define DEF_MR // Declar keyword values
18 #include "risckw.h" // Incl generated risc keywords
20 #define DEF_KW // Declare keyword values
21 #include "kwtab.h" // Incl generated keyword tables & defs
23 unsigned altbankok = 0; // Ok to use alternate register bank
24 unsigned orgactive = 0; // RISC org directive active
25 unsigned orgaddr = 0; // Org'd address
26 unsigned orgwarning = 0; // Has an ORG warning been issued
28 unsigned previousop = 0; // Used for NOP padding checks
29 unsigned currentop = 0; // Used for NOP padding checks
30 unsigned mjump_defined, mjump_dest; // mjump macro flags, values etc
32 char reg_err[] = "missing register R0...R31";
34 // Jaguar Jump Condition Names
35 char condname[MAXINTERNCC][5] = {
36 "NZ", "Z", "NC", "NCNZ", "NCZ", "C", "CNZ", "CZ", "NN", "NNNZ", "NNZ", "N", "N_NZ", "N_Z ",
37 "T", "A", "NE", "EQ", "CC", "HS", "HI", "CS", "LO", "PL", "MI", "F"
40 // Jaguar Jump Condition Numbers
41 char condnumber[] = {1, 2, 4, 5, 6, 8, 9, 10, 20, 21, 22, 24, 25, 26,
42 0, 0, 1, 2, 4, 4, 5, 8, 8, 20, 24, 31};
44 struct opcoderecord roptbl[] = {
45 { MR_ADD, RI_TWO, 0 },
46 { MR_ADDC, RI_TWO, 1 },
47 { MR_ADDQ, RI_NUM_32, 2 },
48 { MR_ADDQT, RI_NUM_32, 3 },
49 { MR_SUB, RI_TWO, 4 },
50 { MR_SUBC, RI_TWO, 5 },
51 { MR_SUBQ, RI_NUM_32, 6 },
52 { MR_SUBQT, RI_NUM_32, 7 },
53 { MR_NEG, RI_ONE, 8 },
54 { MR_AND, RI_TWO, 9 },
55 { MR_OR, RI_TWO, 10 },
56 { MR_XOR, RI_TWO, 11 },
57 { MR_NOT, RI_ONE, 12 },
58 { MR_BTST, RI_NUM_31, 13 },
59 { MR_BSET, RI_NUM_31, 14 },
60 { MR_BCLR, RI_NUM_31, 15 },
61 { MR_MULT, RI_TWO, 16 },
62 { MR_IMULT, RI_TWO, 17 },
63 { MR_IMULTN, RI_TWO, 18 },
64 { MR_RESMAC, RI_ONE, 19 },
65 { MR_IMACN, RI_TWO, 20 },
66 { MR_DIV, RI_TWO, 21 },
67 { MR_ABS, RI_ONE, 22 },
68 { MR_SH, RI_TWO, 23 },
69 { MR_SHLQ, RI_NUM_32, 24 + SUB32 },
70 { MR_SHRQ, RI_NUM_32, 25 },
71 { MR_SHA, RI_TWO, 26 },
72 { MR_SHARQ, RI_NUM_32, 27 },
73 { MR_ROR, RI_TWO, 28 },
74 { MR_RORQ, RI_NUM_32, 29 },
75 { MR_ROLQ, RI_NUM_32, 29 + SUB32 },
76 { MR_CMP, RI_TWO, 30 },
77 { MR_CMPQ, RI_NUM_15, 31 },
78 { MR_SAT8, RI_ONE, 32 + GPUONLY },
79 { MR_SUBQMOD, RI_NUM_32, 32 + DSPONLY },
80 { MR_SAT16, RI_ONE, 33 + GPUONLY },
81 { MR_SAT16S, RI_ONE, 33 + DSPONLY },
82 { MR_MOVEQ, RI_NUM_31, 35 },
83 { MR_MOVETA, RI_TWO, 36 },
84 { MR_MOVEFA, RI_TWO, 37 },
85 { MR_MOVEI, RI_MOVEI, 38 },
86 { MR_LOADB, RI_LOADN, 39 },
87 { MR_LOADW, RI_LOADN, 40 },
88 { MR_LOADP, RI_LOADN, 42 + GPUONLY },
89 { MR_SAT32S, RI_ONE, 42 + DSPONLY },
90 { MR_STOREB, RI_STOREN, 45 },
91 { MR_STOREW, RI_STOREN, 46 },
92 { MR_STOREP, RI_STOREN, 48 + GPUONLY },
93 { MR_MIRROR, RI_ONE, 48 + DSPONLY },
94 { MR_JUMP, RI_JUMP, 52 },
96 { MR_MMULT, RI_TWO, 54 },
97 { MR_MTOI, RI_TWO, 55 },
98 { MR_NORMI, RI_TWO, 56 },
99 { MR_NOP, RI_NONE, 57 },
100 { MR_SAT24, RI_ONE, 62 },
101 { MR_UNPACK, RI_ONE, 63 + GPUONLY },
102 { MR_PACK, RI_ONE, 63 + GPUONLY },
103 { MR_ADDQMOD, RI_NUM_32, 63 + DSPONLY },
104 { MR_MOVE, RI_MOVE, 0 },
105 { MR_LOAD, RI_LOAD, 0 },
106 { MR_STORE, RI_STORE, 0 }
110 // --- Convert a String to Uppercase ---------------------------------------------------------------
113 void strtoupper(char *s) {
115 *s = (char)(toupper(*s));
121 // --- Build RISC Instruction Word -----------------------------------------------------------------
124 void risc_instruction_word(unsigned short parm, int reg1, int reg2) {
127 previousop = currentop; // Opcode tracking for nop padding
130 if(!orgwarning) { // Check for absolute address setting
131 if(!orgactive && !in_main) {
132 warn("GPU/DSP code outside of absolute section");
137 if(jpad) { // JPAD directive
139 if(((previousop == 52) || (previousop == 53)) && (currentop != 57))
140 D_word(value); // Insert NOP
143 if((previousop == 52) || (previousop == 53)) {
145 case 38: warn("NOP inserted before MOVEI instruction."); D_word(value); break;
146 case 53: warn("NOP inserted before JR instruction."); D_word(value); break;
147 case 52: warn("NOP inserted before JUMP instruction."); D_word(value); break;
148 case 51: warn("NOP inserted before MOVE PC instruction."); D_word(value); break;
155 if(currentop == 20) { // IMACN checks
156 if((previousop != 18) && (previousop != 20)) {
157 error("IMULTN/IMACN instruction must preceed IMACN instruction");
161 if(currentop == 19) { // RESMAC checks
162 if(previousop != 20) {
163 error("IMACN instruction must preceed RESMAC instruction");
167 value =((parm & 0x3F) << 10) + ((reg1 & 0x1F) << 5) + (reg2 & 0x1F);
172 // --- Get a RISC Register -------------------------------------------------------------------------
175 int getregister(WORD rattr) {
176 VALUE eval; // Expression value
177 WORD eattr; // Expression attributes
178 SYM *esym; // External symbol involved in expr.
179 TOKEN r_expr[EXPRSIZE]; // Expression token list
180 WORD defined; // Symbol defined flag
182 if(expr(r_expr, &eval, &eattr, &esym) != OK) {
183 error("malformed opcode");
186 defined = (WORD)(eattr & DEFINED);
187 if((challoc - ch_size) < 4)
190 fixup((WORD)(FU_WORD|rattr), sloc, r_expr);
193 if((eval >= 0) && (eval <= 31)) { // Check for specified register, r0->r31
206 // --- Do RISC Code Generation ---------------------------------------------------------------------
209 int risccg(int state) {
210 unsigned short parm; // Opcode parameters
211 unsigned type; // Opcode type
212 int reg1; // Register 1
213 int reg2; // Register 2
214 int val = 0; // Constructed value
221 unsigned locptr = 0; // Address location pointer
222 unsigned page_jump = 0; // Memory page jump flag
223 VALUE eval; // Expression value
224 WORD eattr; // Expression attributes
225 SYM *esym; // External symbol involved in expr.
226 TOKEN r_expr[EXPRSIZE]; // Expression token list
227 WORD defined; // Symbol defined flag
229 int indexed; // Indexed register flag
231 parm = (WORD)(roptbl[state-3000].parm); // Get opcode parameter and type
232 type = roptbl[state-3000].typ;
234 // Detect whether the opcode parmeter passed determines that the opcode is specific to only one
235 // of the RISC processors and ensure it is legal in the current code section.
236 // If not then error and return.
237 if(((parm & GPUONLY) && rdsp) || ((parm & DSPONLY) && rgpu) ) {
238 error("opcode is not valid in this code section");
242 // Process RISC opcode
244 // No operand instructions
247 risc_instruction_word(parm, 0, 0);
249 // Single operand instructions (Rd)
250 // ABS, MIRROR, NEG, NOT, PACK, RESMAC, SAT8, SAT16, SAT16S, SAT24, SAT32S, UNPACK
252 reg2 = getregister(FU_REGTWO);
254 risc_instruction_word(parm, parm >> 6, reg2);
256 // Two operand instructions (Rs,Rd)
257 // ADD, ADDC, AND, CMP, DIV, IMACN, IMULT, IMULTN, MOVEFA, MOVETA, MULT, MMULT,
258 // MTOI, NORMI, OR, ROR, SH, SHA, SUB, SUBC, XOR
260 if(parm == 37) altbankok = 1; // MOVEFA
261 reg1 = getregister(FU_REGONE);
263 if(parm == 36) altbankok = 1; // MOVETA
264 reg2 = getregister(FU_REGTWO);
266 risc_instruction_word(parm, reg1, reg2);
268 // Numeric operand (n,Rd) where n = -16..+15
271 // Numeric operand (n,Rd) where n = 0..31
272 // BCLR, BSET, BTST, MOVEQ
274 // Numeric operand (n,Rd) where n = 1..32
275 // ADDQ, ADDQMOD, ADDQT, SHARQ, SHLQ, SHRQ, SUBQ, SUBQMOD, SUBQT, ROLQ, RORQ
278 case RI_NUM_15: reg1 = -16; reg2 = 15; attrflg = FU_NUM15; break;
280 case RI_NUM_31: reg1 = 0; reg2 = 31; attrflg = FU_NUM31; break;
281 case RI_NUM_32: reg1 = 1; reg2 = 32; attrflg = FU_NUM32; break;
283 if(parm & SUB32) attrflg |= FU_SUB32;
286 if(expr(r_expr, &eval, &eattr, &esym) != OK)
289 defined = (WORD)(eattr & DEFINED);
290 if((challoc - ch_size) < 4)
293 fixup((WORD)(FU_WORD|attrflg), sloc, r_expr);
296 if((int)eval < reg1 || (int)eval > reg2) {
297 error("constant out of range");
302 else if(type == RI_NUM_32)
303 reg1 = (reg1 == 32) ? 0 : eval;
308 } else goto malformed;
310 reg2 = getregister(FU_REGTWO);
312 risc_instruction_word(parm, reg1, reg2);
314 // Move Immediate - n,Rn - n in Second Word
318 if(expr(r_expr, &eval, &eattr, &esym) != OK) {
320 error("malformed opcode");
323 // Opcode tracking for nop padding
324 previousop = currentop;
327 if((previousop == 52) || (previousop == 53) && !jpad) {
328 warn("NOP inserted before MOVEI instruction.");
331 tdb = (WORD)(eattr & TDB);
332 defined = (WORD)(eattr & DEFINED);
333 if((challoc - ch_size) < 4)
336 fixup(FU_LONG|FU_MOVEI, sloc + 2, r_expr);
340 rmark(cursect, sloc + 2, tdb, MLONG|MMOVEI, NULL);
344 // Store the defined flags and value of the movei when used in mjump
346 mjump_defined = defined;
350 } else goto malformed;
352 reg2 = getregister(FU_REGTWO);
354 D_word((((parm & 0x3F) << 10) + reg2));
355 val = ((val >> 16) & 0x0000FFFF) | ((val << 16) & 0xFFFF0000);
358 case RI_MOVE: // PC,Rd or Rs,Rd
365 reg1 = getregister(FU_REGONE);
368 reg2 = getregister(FU_REGTWO);
370 risc_instruction_word(parm, reg1, reg2);
372 // (Rn),Rn = 41 / (R14/R15+n),Rn = 43/44 / (R14/R15+Rn),Rn = 58/59
376 if(*tok != '(') goto malformed;
378 if((*tok == KW_R14 || *tok == KW_R15) && (*(tok+1) != ')'))
379 indexed = (*tok - KW_R0);
381 sy = lookup((char *)tok[1], LABEL, 0);
386 if(sy->sattre & EQUATEDREG)
387 if(((sy->svalue & 0x1F) == 14 || (sy->svalue & 0x1F) == 15) && (*(tok+2) != ')')) {
388 indexed = (sy->svalue & 0x1F);
393 reg1 = getregister(FU_REGONE);
399 parm = (WORD)(reg1 - 14 + 58);
401 if(*tok >= KW_R0 && *tok <= KW_R31) {
405 sy = lookup((char *)tok[1], LABEL, 0);
410 if(sy->sattre & EQUATEDREG) {
415 reg1 = getregister(FU_REGONE);
417 if(expr(r_expr, &eval, &eattr, &esym) != OK) {
420 tdb = (WORD)(eattr & TDB);
421 defined = (WORD)(eattr & DEFINED);
422 if((challoc - ch_size) < 4)
425 error("constant expected");
427 //fixup(FU_WORD|FU_REGONE, sloc, r_expr);
434 warn("NULL offset removed");
436 if(reg1 < 1 || reg1 > 32) {
437 error("constant out of range");
440 if(reg1 == 32) reg1 = 0;
441 parm = (WORD)(parm - 58 + 43);
447 reg1 = getregister(FU_REGONE);
450 if(*tok != ')') goto malformed;
453 reg2 = getregister(FU_REGTWO);
455 risc_instruction_word(parm, reg1, reg2);
457 // Rn,(Rn) = 47 / Rn,(R14/R15+n) = 49/50 / Rn,(R14/R15+Rn) = 60/61
460 reg1 = getregister(FU_REGONE);
462 if(*tok != '(') goto malformed;
465 if((*tok == KW_R14 || *tok == KW_R15) && (*(tok+1) != ')'))
466 indexed = (*tok - KW_R0);
468 sy = lookup((char *)tok[1], LABEL, 0);
473 if(sy->sattre & EQUATEDREG)
474 if(((sy->svalue & 0x1F) == 14 || (sy->svalue & 0x1F) == 15) && (*(tok+2) != ')')) {
475 indexed = (sy->svalue & 0x1F);
480 reg2 = getregister(FU_REGTWO);
486 parm = (WORD)(reg2 - 14 + 60);
488 if(*tok >= KW_R0 && *tok <= KW_R31) {
492 sy = lookup((char *)tok[1], LABEL, 0);
497 if(sy->sattre & EQUATEDREG) {
502 reg2 = getregister(FU_REGTWO);
504 if(expr(r_expr, &eval, &eattr, &esym) != OK) {
507 tdb = (WORD)(eattr & TDB);
508 defined = (WORD)(eattr & DEFINED);
509 if((challoc - ch_size) < 4)
512 fixup(FU_WORD|FU_REGTWO, sloc, r_expr);
519 warn("NULL offset removed");
521 if(reg2 < 1 || reg2 > 32) {
522 error("constant out of range");
525 if(reg2 == 32) reg2 = 0;
526 parm = (WORD)(parm - 60 + 49);
532 reg2 = getregister(FU_REGTWO);
535 if(*tok != ')') goto malformed;
538 risc_instruction_word(parm, reg2, reg1);
540 // LOADB/LOADP/LOADW (Rn),Rn
542 if(*tok != '(') goto malformed;
544 reg1 = getregister(FU_REGONE);
545 if(*tok != ')') goto malformed;
548 reg2 = getregister(FU_REGTWO);
550 risc_instruction_word(parm, reg1, reg2);
552 // STOREB/STOREP/STOREW Rn,(Rn)
554 reg1 = getregister(FU_REGONE);
556 if(*tok != '(') goto malformed;
558 reg2 = getregister(FU_REGTWO);
559 if(*tok != ')') goto malformed;
562 risc_instruction_word(parm, reg2, reg1);
564 case RI_JR: // Jump Relative - cc,n - n=-16..+15 words, reg2=cc
565 case RI_JUMP: // Jump Absolute - cc,(Rs) - reg2=cc
566 // Check to see if there is a comma in the token string. If not then the JR or JUMP should
567 // default to 0, Jump Always
574 if(c) { // Comma present in token string
575 if(*tok == CONST) { // CC using a constant number
580 } else if(*tok == SYMBOL) {
582 for(i = 0; i < MAXINTERNCC; i++) {
583 strcpy(scratch, (char *)tok[1]);
585 if(!strcmp(condname[i], scratch))
589 ccsym = lookup((char *)tok[1], LABEL, 0);
590 if(ccsym && (ccsym->sattre & EQUATEDCC) && !(ccsym->sattre & UNDEF_CC)) {
593 error("unknown condition code");
599 } else if(*tok == '(') {
600 val = 0; // Jump always
603 val = 0; // Jump always
606 if(val < 0 || val > 31) {
607 error("condition constant out of range");
610 reg1 = val; // Store condition code
612 if(type == RI_JR) { // JR cc,n
613 if(expr(r_expr, &eval, &eattr, &esym) != OK)
616 tdb = (WORD)(eattr & TDB);
617 defined = (WORD)(eattr & DEFINED);
618 if((challoc - ch_size) < 4)
622 fixup(FU_WORD|FU_MJR, sloc, r_expr);
624 fixup(FU_WORD|FU_JR, sloc, r_expr);
630 reg2 = ((int)(val - (orgaddr + 2))) / 2;
631 if((reg2 < -16) || (reg2 > 15))
632 error("PC relative overflow");
635 reg2 = ((int)(val - (sloc + 2))) / 2;
636 if((reg2 < -16) || (reg2 > 15))
637 error("PC relative overflow");
643 if(((locptr >= 0xF03000) && (locptr < 0xF04000) && (val < 0xF03000)) ||
644 ((val >= 0xF03000) && (val < 0xF04000) && (locptr < 0xF03000)) ) {
645 warn("* cannot jump relative between main memory and local gpu ram");
647 page_jump = (locptr & 0xFFFFFF00) - (val & 0xFFFFFF00);
650 warn("* destination address not aligned for long page jump relative, "
651 "insert a \'nop\' before the destination label/address");
655 warn("* destination address not aligned for short page jump relative, "
656 "insert a \'nop\' before the destination label/address");
663 risc_instruction_word(parm, reg2, reg1);
664 } else { // JUMP cc, (Rn)
665 if(*tok != '(') goto malformed;
667 reg2 = getregister(FU_REGTWO);
668 if(*tok != ')') goto malformed;
673 warn("* \'jump\' is not recommended for .gpumain as destination addresses "
674 "cannot be validated for alignment, use \'mjump\'");
675 locptr = (orgactive) ? orgaddr : sloc;
677 warn("* source address not aligned for long or short jump, "
678 "insert a \'nop\' before the \'jump\'");
682 locptr = (orgactive) ? orgaddr : sloc;
683 page_jump = (locptr & 0xFFFFFF00) - (mjump_dest & 0xFFFFFF00);
686 warn("* destination address not aligned for long page jump, "
687 "insert a \'nop\' before the destination label/address");
690 if(!(mjump_dest & 0x0000000F) || ((mjump_dest - 2) % 4)) {
691 warn("* destination address not aligned for short page jump, "
692 "insert a \'nop\' before the destination label/address");
696 locptr = (orgactive) ? orgaddr : sloc;
697 fwdjump[fwindex++] = locptr;
702 risc_instruction_word(parm, reg2, reg1);
705 // Should never get here :D
707 error("unknown risc opcode type");