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
18 #define DEF_MR // Declar keyword values
19 #include "risckw.h" // Incl generated risc keywords
21 #define DEF_KW // Declare keyword values
22 #include "kwtab.h" // Incl generated keyword tables & defs
24 unsigned altbankok = 0; // Ok to use alternate register bank
25 unsigned orgactive = 0; // RISC org directive active
26 unsigned orgaddr = 0; // Org'd address
27 unsigned orgwarning = 0; // Has an ORG warning been issued
29 unsigned previousop = 0; // Used for NOP padding checks
30 unsigned currentop = 0; // Used for NOP padding checks
31 unsigned mjump_defined, mjump_dest; // mjump macro flags, values etc
33 char reg_err[] = "missing register R0...R31";
35 // Jaguar Jump Condition Names
36 char condname[MAXINTERNCC][5] = {
37 "NZ", "Z", "NC", "NCNZ", "NCZ", "C", "CNZ", "CZ", "NN", "NNNZ", "NNZ", "N", "N_NZ", "N_Z ",
38 "T", "A", "NE", "EQ", "CC", "HS", "HI", "CS", "LO", "PL", "MI", "F"
41 // Jaguar Jump Condition Numbers
42 char condnumber[] = {1, 2, 4, 5, 6, 8, 9, 10, 20, 21, 22, 24, 25, 26,
43 0, 0, 1, 2, 4, 4, 5, 8, 8, 20, 24, 31};
45 struct opcoderecord roptbl[] = {
46 { MR_ADD, RI_TWO, 0 },
47 { MR_ADDC, RI_TWO, 1 },
48 { MR_ADDQ, RI_NUM_32, 2 },
49 { MR_ADDQT, RI_NUM_32, 3 },
50 { MR_SUB, RI_TWO, 4 },
51 { MR_SUBC, RI_TWO, 5 },
52 { MR_SUBQ, RI_NUM_32, 6 },
53 { MR_SUBQT, RI_NUM_32, 7 },
54 { MR_NEG, RI_ONE, 8 },
55 { MR_AND, RI_TWO, 9 },
56 { MR_OR, RI_TWO, 10 },
57 { MR_XOR, RI_TWO, 11 },
58 { MR_NOT, RI_ONE, 12 },
59 { MR_BTST, RI_NUM_31, 13 },
60 { MR_BSET, RI_NUM_31, 14 },
61 { MR_BCLR, RI_NUM_31, 15 },
62 { MR_MULT, RI_TWO, 16 },
63 { MR_IMULT, RI_TWO, 17 },
64 { MR_IMULTN, RI_TWO, 18 },
65 { MR_RESMAC, RI_ONE, 19 },
66 { MR_IMACN, RI_TWO, 20 },
67 { MR_DIV, RI_TWO, 21 },
68 { MR_ABS, RI_ONE, 22 },
69 { MR_SH, RI_TWO, 23 },
70 { MR_SHLQ, RI_NUM_32, 24 + SUB32 },
71 { MR_SHRQ, RI_NUM_32, 25 },
72 { MR_SHA, RI_TWO, 26 },
73 { MR_SHARQ, RI_NUM_32, 27 },
74 { MR_ROR, RI_TWO, 28 },
75 { MR_RORQ, RI_NUM_32, 29 },
76 { MR_ROLQ, RI_NUM_32, 29 + SUB32 },
77 { MR_CMP, RI_TWO, 30 },
78 { MR_CMPQ, RI_NUM_15, 31 },
79 { MR_SAT8, RI_ONE, 32 + GPUONLY },
80 { MR_SUBQMOD, RI_NUM_32, 32 + DSPONLY },
81 { MR_SAT16, RI_ONE, 33 + GPUONLY },
82 { MR_SAT16S, RI_ONE, 33 + DSPONLY },
83 { MR_MOVEQ, RI_NUM_31, 35 },
84 { MR_MOVETA, RI_TWO, 36 },
85 { MR_MOVEFA, RI_TWO, 37 },
86 { MR_MOVEI, RI_MOVEI, 38 },
87 { MR_LOADB, RI_LOADN, 39 },
88 { MR_LOADW, RI_LOADN, 40 },
89 { MR_LOADP, RI_LOADN, 42 + GPUONLY },
90 { MR_SAT32S, RI_ONE, 42 + DSPONLY },
91 { MR_STOREB, RI_STOREN, 45 },
92 { MR_STOREW, RI_STOREN, 46 },
93 { MR_STOREP, RI_STOREN, 48 + GPUONLY },
94 { MR_MIRROR, RI_ONE, 48 + DSPONLY },
95 { MR_JUMP, RI_JUMP, 52 },
97 { MR_MMULT, RI_TWO, 54 },
98 { MR_MTOI, RI_TWO, 55 },
99 { MR_NORMI, RI_TWO, 56 },
100 { MR_NOP, RI_NONE, 57 },
101 { MR_SAT24, RI_ONE, 62 },
102 { MR_UNPACK, RI_ONE, 63 + GPUONLY },
103 { MR_PACK, RI_ONE, 63 + GPUONLY },
104 { MR_ADDQMOD, RI_NUM_32, 63 + DSPONLY },
105 { MR_MOVE, RI_MOVE, 0 },
106 { MR_LOAD, RI_LOAD, 0 },
107 { MR_STORE, RI_STORE, 0 }
111 // --- Convert a String to Uppercase ---------------------------------------------------------------
114 void strtoupper(char *s) {
116 *s = (char)(toupper(*s));
122 // --- Build RISC Instruction Word -----------------------------------------------------------------
125 void risc_instruction_word(unsigned short parm, int reg1, int reg2) {
128 previousop = currentop; // Opcode tracking for nop padding
131 if(!orgwarning) { // Check for absolute address setting
132 if(!orgactive && !in_main) {
133 warn("GPU/DSP code outside of absolute section");
138 if(jpad) { // JPAD directive
140 if(((previousop == 52) || (previousop == 53)) && (currentop != 57))
141 D_word(value); // Insert NOP
144 if((previousop == 52) || (previousop == 53)) {
146 case 38: warn("NOP inserted before MOVEI instruction."); D_word(value); break;
147 case 53: warn("NOP inserted before JR instruction."); D_word(value); break;
148 case 52: warn("NOP inserted before JUMP instruction."); D_word(value); break;
149 case 51: warn("NOP inserted before MOVE PC instruction."); D_word(value); break;
156 if(currentop == 20) { // IMACN checks
157 if((previousop != 18) && (previousop != 20)) {
158 error("IMULTN/IMACN instruction must preceed IMACN instruction");
162 if(currentop == 19) { // RESMAC checks
163 if(previousop != 20) {
164 error("IMACN instruction must preceed RESMAC instruction");
168 value =((parm & 0x3F) << 10) + ((reg1 & 0x1F) << 5) + (reg2 & 0x1F);
173 // --- Get a RISC Register -------------------------------------------------------------------------
176 int getregister(WORD rattr) {
177 VALUE eval; // Expression value
178 WORD eattr; // Expression attributes
179 SYM *esym; // External symbol involved in expr.
180 TOKEN r_expr[EXPRSIZE]; // Expression token list
181 WORD defined; // Symbol defined flag
183 if(expr(r_expr, &eval, &eattr, &esym) != OK) {
184 error("malformed opcode");
187 defined = (WORD)(eattr & DEFINED);
188 if((challoc - ch_size) < 4)
191 fixup((WORD)(FU_WORD|rattr), sloc, r_expr);
194 if((eval >= 0) && (eval <= 31)) { // Check for specified register, r0->r31
207 // --- Do RISC Code Generation ---------------------------------------------------------------------
210 int risccg(int state) {
211 unsigned short parm; // Opcode parameters
212 unsigned type; // Opcode type
213 int reg1; // Register 1
214 int reg2; // Register 2
215 int val = 0; // Constructed value
222 unsigned locptr = 0; // Address location pointer
223 unsigned page_jump = 0; // Memory page jump flag
224 VALUE eval; // Expression value
225 WORD eattr; // Expression attributes
226 SYM *esym; // External symbol involved in expr.
227 TOKEN r_expr[EXPRSIZE]; // Expression token list
228 WORD defined; // Symbol defined flag
230 int indexed; // Indexed register flag
232 parm = (WORD)(roptbl[state-3000].parm); // Get opcode parameter and type
233 type = roptbl[state-3000].typ;
235 // Detect whether the opcode parmeter passed determines that the opcode is specific to only one
236 // of the RISC processors and ensure it is legal in the current code section.
237 // If not then error and return.
238 if(((parm & GPUONLY) && rdsp) || ((parm & DSPONLY) && rgpu) ) {
239 error("opcode is not valid in this code section");
243 // Process RISC opcode
245 // No operand instructions
248 risc_instruction_word(parm, 0, 0);
250 // Single operand instructions (Rd)
251 // ABS, MIRROR, NEG, NOT, PACK, RESMAC, SAT8, SAT16, SAT16S, SAT24, SAT32S, UNPACK
253 reg2 = getregister(FU_REGTWO);
255 risc_instruction_word(parm, parm >> 6, reg2);
257 // Two operand instructions (Rs,Rd)
258 // ADD, ADDC, AND, CMP, DIV, IMACN, IMULT, IMULTN, MOVEFA, MOVETA, MULT, MMULT,
259 // MTOI, NORMI, OR, ROR, SH, SHA, SUB, SUBC, XOR
261 if(parm == 37) altbankok = 1; // MOVEFA
262 reg1 = getregister(FU_REGONE);
264 if(parm == 36) altbankok = 1; // MOVETA
265 reg2 = getregister(FU_REGTWO);
267 risc_instruction_word(parm, reg1, reg2);
269 // Numeric operand (n,Rd) where n = -16..+15
272 // Numeric operand (n,Rd) where n = 0..31
273 // BCLR, BSET, BTST, MOVEQ
275 // Numeric operand (n,Rd) where n = 1..32
276 // ADDQ, ADDQMOD, ADDQT, SHARQ, SHLQ, SHRQ, SUBQ, SUBQMOD, SUBQT, ROLQ, RORQ
279 case RI_NUM_15: reg1 = -16; reg2 = 15; attrflg = FU_NUM15; break;
281 case RI_NUM_31: reg1 = 0; reg2 = 31; attrflg = FU_NUM31; break;
282 case RI_NUM_32: reg1 = 1; reg2 = 32; attrflg = FU_NUM32; break;
284 if(parm & SUB32) attrflg |= FU_SUB32;
287 if(expr(r_expr, &eval, &eattr, &esym) != OK)
290 defined = (WORD)(eattr & DEFINED);
291 if((challoc - ch_size) < 4)
294 fixup((WORD)(FU_WORD|attrflg), sloc, r_expr);
297 if((int)eval < reg1 || (int)eval > reg2) {
298 error("constant out of range");
303 else if(type == RI_NUM_32)
304 reg1 = (reg1 == 32) ? 0 : eval;
309 } else goto malformed;
311 reg2 = getregister(FU_REGTWO);
313 risc_instruction_word(parm, reg1, reg2);
315 // Move Immediate - n,Rn - n in Second Word
319 if(expr(r_expr, &eval, &eattr, &esym) != OK) {
321 error("malformed opcode");
324 // Opcode tracking for nop padding
325 previousop = currentop;
328 if((previousop == 52) || (previousop == 53) && !jpad) {
329 warn("NOP inserted before MOVEI instruction.");
332 tdb = (WORD)(eattr & TDB);
333 defined = (WORD)(eattr & DEFINED);
334 if((challoc - ch_size) < 4)
337 fixup(FU_LONG|FU_MOVEI, sloc + 2, r_expr);
341 rmark(cursect, sloc + 2, tdb, MLONG|MMOVEI, NULL);
345 // Store the defined flags and value of the movei when used in mjump
347 mjump_defined = defined;
351 } else goto malformed;
353 reg2 = getregister(FU_REGTWO);
355 D_word((((parm & 0x3F) << 10) + reg2));
356 val = ((val >> 16) & 0x0000FFFF) | ((val << 16) & 0xFFFF0000);
359 case RI_MOVE: // PC,Rd or Rs,Rd
366 reg1 = getregister(FU_REGONE);
369 reg2 = getregister(FU_REGTWO);
371 risc_instruction_word(parm, reg1, reg2);
373 // (Rn),Rn = 41 / (R14/R15+n),Rn = 43/44 / (R14/R15+Rn),Rn = 58/59
377 if(*tok != '(') goto malformed;
379 if((*tok == KW_R14 || *tok == KW_R15) && (*(tok+1) != ')'))
380 indexed = (*tok - KW_R0);
382 sy = lookup((char *)tok[1], LABEL, 0);
387 if(sy->sattre & EQUATEDREG)
388 if(((sy->svalue & 0x1F) == 14 || (sy->svalue & 0x1F) == 15) && (*(tok+2) != ')')) {
389 indexed = (sy->svalue & 0x1F);
394 reg1 = getregister(FU_REGONE);
400 parm = (WORD)(reg1 - 14 + 58);
402 if(*tok >= KW_R0 && *tok <= KW_R31) {
406 sy = lookup((char *)tok[1], LABEL, 0);
411 if(sy->sattre & EQUATEDREG) {
416 reg1 = getregister(FU_REGONE);
418 if(expr(r_expr, &eval, &eattr, &esym) != OK) {
421 tdb = (WORD)(eattr & TDB);
422 defined = (WORD)(eattr & DEFINED);
423 if((challoc - ch_size) < 4)
426 error("constant expected");
428 //fixup(FU_WORD|FU_REGONE, sloc, r_expr);
435 warn("NULL offset removed");
437 if(reg1 < 1 || reg1 > 32) {
438 error("constant out of range");
441 if(reg1 == 32) reg1 = 0;
442 parm = (WORD)(parm - 58 + 43);
448 reg1 = getregister(FU_REGONE);
451 if(*tok != ')') goto malformed;
454 reg2 = getregister(FU_REGTWO);
456 risc_instruction_word(parm, reg1, reg2);
458 // Rn,(Rn) = 47 / Rn,(R14/R15+n) = 49/50 / Rn,(R14/R15+Rn) = 60/61
461 reg1 = getregister(FU_REGONE);
463 if(*tok != '(') goto malformed;
466 if((*tok == KW_R14 || *tok == KW_R15) && (*(tok+1) != ')'))
467 indexed = (*tok - KW_R0);
469 sy = lookup((char *)tok[1], LABEL, 0);
474 if(sy->sattre & EQUATEDREG)
475 if(((sy->svalue & 0x1F) == 14 || (sy->svalue & 0x1F) == 15) && (*(tok+2) != ')')) {
476 indexed = (sy->svalue & 0x1F);
481 reg2 = getregister(FU_REGTWO);
487 parm = (WORD)(reg2 - 14 + 60);
489 if(*tok >= KW_R0 && *tok <= KW_R31) {
493 sy = lookup((char *)tok[1], LABEL, 0);
498 if(sy->sattre & EQUATEDREG) {
503 reg2 = getregister(FU_REGTWO);
505 if(expr(r_expr, &eval, &eattr, &esym) != OK) {
508 tdb = (WORD)(eattr & TDB);
509 defined = (WORD)(eattr & DEFINED);
510 if((challoc - ch_size) < 4)
513 fixup(FU_WORD|FU_REGTWO, sloc, r_expr);
520 warn("NULL offset removed");
522 if(reg2 < 1 || reg2 > 32) {
523 error("constant out of range");
526 if(reg2 == 32) reg2 = 0;
527 parm = (WORD)(parm - 60 + 49);
533 reg2 = getregister(FU_REGTWO);
536 if(*tok != ')') goto malformed;
539 risc_instruction_word(parm, reg2, reg1);
541 // LOADB/LOADP/LOADW (Rn),Rn
543 if(*tok != '(') goto malformed;
545 reg1 = getregister(FU_REGONE);
546 if(*tok != ')') goto malformed;
549 reg2 = getregister(FU_REGTWO);
551 risc_instruction_word(parm, reg1, reg2);
553 // STOREB/STOREP/STOREW Rn,(Rn)
555 reg1 = getregister(FU_REGONE);
557 if(*tok != '(') goto malformed;
559 reg2 = getregister(FU_REGTWO);
560 if(*tok != ')') goto malformed;
563 risc_instruction_word(parm, reg2, reg1);
565 case RI_JR: // Jump Relative - cc,n - n=-16..+15 words, reg2=cc
566 case RI_JUMP: // Jump Absolute - cc,(Rs) - reg2=cc
567 // Check to see if there is a comma in the token string. If not then the JR or JUMP should
568 // default to 0, Jump Always
575 if(c) { // Comma present in token string
576 if(*tok == CONST) { // CC using a constant number
581 } else if(*tok == SYMBOL) {
583 for(i = 0; i < MAXINTERNCC; i++) {
584 strcpy(scratch, (char *)tok[1]);
586 if(!strcmp(condname[i], scratch))
590 ccsym = lookup((char *)tok[1], LABEL, 0);
591 if(ccsym && (ccsym->sattre & EQUATEDCC) && !(ccsym->sattre & UNDEF_CC)) {
594 error("unknown condition code");
600 } else if(*tok == '(') {
601 val = 0; // Jump always
604 val = 0; // Jump always
607 if(val < 0 || val > 31) {
608 error("condition constant out of range");
611 reg1 = val; // Store condition code
613 if(type == RI_JR) { // JR cc,n
614 if(expr(r_expr, &eval, &eattr, &esym) != OK)
617 tdb = (WORD)(eattr & TDB);
618 defined = (WORD)(eattr & DEFINED);
619 if((challoc - ch_size) < 4)
623 fixup(FU_WORD|FU_MJR, sloc, r_expr);
625 fixup(FU_WORD|FU_JR, sloc, r_expr);
631 reg2 = ((int)(val - (orgaddr + 2))) / 2;
632 if((reg2 < -16) || (reg2 > 15))
633 error("PC relative overflow");
636 reg2 = ((int)(val - (sloc + 2))) / 2;
637 if((reg2 < -16) || (reg2 > 15))
638 error("PC relative overflow");
644 if(((locptr >= 0xF03000) && (locptr < 0xF04000) && (val < 0xF03000)) ||
645 ((val >= 0xF03000) && (val < 0xF04000) && (locptr < 0xF03000)) ) {
646 warn("* cannot jump relative between main memory and local gpu ram");
648 page_jump = (locptr & 0xFFFFFF00) - (val & 0xFFFFFF00);
651 warn("* destination address not aligned for long page jump relative, "
652 "insert a \'nop\' before the destination label/address");
656 warn("* destination address not aligned for short page jump relative, "
657 "insert a \'nop\' before the destination label/address");
664 risc_instruction_word(parm, reg2, reg1);
665 } else { // JUMP cc, (Rn)
666 if(*tok != '(') goto malformed;
668 reg2 = getregister(FU_REGTWO);
669 if(*tok != ')') goto malformed;
674 warn("* \'jump\' is not recommended for .gpumain as destination addresses "
675 "cannot be validated for alignment, use \'mjump\'");
676 locptr = (orgactive) ? orgaddr : sloc;
678 warn("* source address not aligned for long or short jump, "
679 "insert a \'nop\' before the \'jump\'");
683 locptr = (orgactive) ? orgaddr : sloc;
684 page_jump = (locptr & 0xFFFFFF00) - (mjump_dest & 0xFFFFFF00);
687 warn("* destination address not aligned for long page jump, "
688 "insert a \'nop\' before the destination label/address");
691 if(!(mjump_dest & 0x0000000F) || ((mjump_dest - 2) % 4)) {
692 warn("* destination address not aligned for short page jump, "
693 "insert a \'nop\' before the destination label/address");
697 locptr = (orgactive) ? orgaddr : sloc;
698 fwdjump[fwindex++] = locptr;
703 risc_instruction_word(parm, reg2, reg1);
706 // Should never get here :D
708 error("unknown risc opcode type");