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",
38 "N", "N_NZ", "N_Z ", "T", "A", "NE", "EQ", "CC", "HS", "HI", "CS", "LO",
42 // Jaguar Jump Condition Numbers
43 char condnumber[] = {1, 2, 4, 5, 6, 8, 9, 10, 20, 21, 22, 24, 25, 26,
44 0, 0, 1, 2, 4, 4, 5, 8, 8, 20, 24, 31};
46 struct opcoderecord roptbl[] = {
47 { MR_ADD, RI_TWO, 0 },
48 { MR_ADDC, RI_TWO, 1 },
49 { MR_ADDQ, RI_NUM_32, 2 },
50 { MR_ADDQT, RI_NUM_32, 3 },
51 { MR_SUB, RI_TWO, 4 },
52 { MR_SUBC, RI_TWO, 5 },
53 { MR_SUBQ, RI_NUM_32, 6 },
54 { MR_SUBQT, RI_NUM_32, 7 },
55 { MR_NEG, RI_ONE, 8 },
56 { MR_AND, RI_TWO, 9 },
57 { MR_OR, RI_TWO, 10 },
58 { MR_XOR, RI_TWO, 11 },
59 { MR_NOT, RI_ONE, 12 },
60 { MR_BTST, RI_NUM_31, 13 },
61 { MR_BSET, RI_NUM_31, 14 },
62 { MR_BCLR, RI_NUM_31, 15 },
63 { MR_MULT, RI_TWO, 16 },
64 { MR_IMULT, RI_TWO, 17 },
65 { MR_IMULTN, RI_TWO, 18 },
66 { MR_RESMAC, RI_ONE, 19 },
67 { MR_IMACN, RI_TWO, 20 },
68 { MR_DIV, RI_TWO, 21 },
69 { MR_ABS, RI_ONE, 22 },
70 { MR_SH, RI_TWO, 23 },
71 { MR_SHLQ, RI_NUM_32, 24 + SUB32 },
72 { MR_SHRQ, RI_NUM_32, 25 },
73 { MR_SHA, RI_TWO, 26 },
74 { MR_SHARQ, RI_NUM_32, 27 },
75 { MR_ROR, RI_TWO, 28 },
76 { MR_RORQ, RI_NUM_32, 29 },
77 { MR_ROLQ, RI_NUM_32, 29 + SUB32 },
78 { MR_CMP, RI_TWO, 30 },
79 { MR_CMPQ, RI_NUM_15, 31 },
80 { MR_SAT8, RI_ONE, 32 + GPUONLY },
81 { MR_SUBQMOD, RI_NUM_32, 32 + DSPONLY },
82 { MR_SAT16, RI_ONE, 33 + GPUONLY },
83 { MR_SAT16S, RI_ONE, 33 + DSPONLY },
84 { MR_MOVEQ, RI_NUM_31, 35 },
85 { MR_MOVETA, RI_TWO, 36 },
86 { MR_MOVEFA, RI_TWO, 37 },
87 { MR_MOVEI, RI_MOVEI, 38 },
88 { MR_LOADB, RI_LOADN, 39 },
89 { MR_LOADW, RI_LOADN, 40 },
90 { MR_LOADP, RI_LOADN, 42 + GPUONLY },
91 { MR_SAT32S, RI_ONE, 42 + DSPONLY },
92 { MR_STOREB, RI_STOREN, 45 },
93 { MR_STOREW, RI_STOREN, 46 },
94 { MR_STOREP, RI_STOREN, 48 + GPUONLY },
95 { MR_MIRROR, RI_ONE, 48 + DSPONLY },
96 { MR_JUMP, RI_JUMP, 52 },
98 { MR_MMULT, RI_TWO, 54 },
99 { MR_MTOI, RI_TWO, 55 },
100 { MR_NORMI, RI_TWO, 56 },
101 { MR_NOP, RI_NONE, 57 },
102 { MR_SAT24, RI_ONE, 62 },
103 { MR_UNPACK, RI_ONE, 63 + GPUONLY },
104 { MR_PACK, RI_ONE, 63 + GPUONLY },
105 { MR_ADDQMOD, RI_NUM_32, 63 + DSPONLY },
106 { MR_MOVE, RI_MOVE, 0 },
107 { MR_LOAD, RI_LOAD, 0 },
108 { MR_STORE, RI_STORE, 0 }
113 // Convert a String to Uppercase
115 void strtoupper(char * s)
119 *s = (char)(toupper(*s));
126 // Build RISC Instruction Word
128 void risc_instruction_word(unsigned short parm, int reg1, int reg2)
132 previousop = currentop; // Opcode tracking for nop padding
136 { // Check for absolute address setting
137 if (!orgactive && !in_main)
139 warn("GPU/DSP code outside of absolute section");
147 if (((previousop == 52) || (previousop == 53)) && (currentop != 57))
148 D_word(value); // Insert NOP
153 if ((previousop == 52) || (previousop == 53))
158 warn("NOP inserted before MOVEI instruction.");
162 warn("NOP inserted before JR instruction.");
166 warn("NOP inserted before JUMP instruction.");
170 warn("NOP inserted before MOVE PC instruction.");
181 if ((previousop != 18) && (previousop != 20))
183 error("IMULTN/IMACN instruction must preceed IMACN instruction");
189 if (previousop != 20)
191 error("IMACN instruction must preceed RESMAC instruction");
195 value =((parm & 0x3F) << 10) + ((reg1 & 0x1F) << 5) + (reg2 & 0x1F);
201 // Get a RISC Register
203 int getregister(WORD rattr)
205 VALUE eval; // Expression value
206 WORD eattr; // Expression attributes
207 SYM * esym; // External symbol involved in expr.
208 TOKEN r_expr[EXPRSIZE]; // Expression token list
209 WORD defined; // Symbol defined flag
211 if (expr(r_expr, &eval, &eattr, &esym) != OK)
213 error("malformed opcode");
218 defined = (WORD)(eattr & DEFINED);
220 if ((challoc - ch_size) < 4)
225 fixup((WORD)(FU_WORD|rattr), sloc, r_expr);
230 // Check for specified register, r0->r31
231 if ((eval >= 0) && (eval <= 31))
248 // Do RISC Code Generation
250 int risccg(int state)
252 unsigned short parm; // Opcode parameters
253 unsigned type; // Opcode type
254 int reg1; // Register 1
255 int reg2; // Register 2
256 int val = 0; // Constructed value
263 unsigned locptr = 0; // Address location pointer
264 unsigned page_jump = 0; // Memory page jump flag
265 VALUE eval; // Expression value
266 WORD eattr; // Expression attributes
267 SYM * esym; // External symbol involved in expr.
268 TOKEN r_expr[EXPRSIZE]; // Expression token list
269 WORD defined; // Symbol defined flag
271 int indexed; // Indexed register flag
273 parm = (WORD)(roptbl[state-3000].parm); // Get opcode parameter and type
274 type = roptbl[state-3000].typ;
276 // Detect whether the opcode parmeter passed determines that the opcode is specific to only one
277 // of the RISC processors and ensure it is legal in the current code section.
278 // If not then error and return.
279 if (((parm & GPUONLY) && rdsp) || ((parm & DSPONLY) && rgpu))
281 error("opcode is not valid in this code section");
285 // Process RISC opcode
288 // No operand instructions
291 risc_instruction_word(parm, 0, 0);
293 // Single operand instructions (Rd)
294 // ABS, MIRROR, NEG, NOT, PACK, RESMAC, SAT8, SAT16, SAT16S, SAT24, SAT32S, UNPACK
296 reg2 = getregister(FU_REGTWO);
298 risc_instruction_word(parm, parm >> 6, reg2);
300 // Two operand instructions (Rs,Rd)
301 // ADD, ADDC, AND, CMP, DIV, IMACN, IMULT, IMULTN, MOVEFA, MOVETA, MULT, MMULT,
302 // MTOI, NORMI, OR, ROR, SH, SHA, SUB, SUBC, XOR
305 altbankok = 1; // MOVEFA
307 reg1 = getregister(FU_REGONE);
311 altbankok = 1; // MOVETA
313 reg2 = getregister(FU_REGTWO);
315 risc_instruction_word(parm, reg1, reg2);
317 // Numeric operand (n,Rd) where n = -16..+15
320 // Numeric operand (n,Rd) where n = 0..31
321 // BCLR, BSET, BTST, MOVEQ
323 // Numeric operand (n,Rd) where n = 1..32
324 // ADDQ, ADDQMOD, ADDQT, SHARQ, SHLQ, SHRQ, SUBQ, SUBQMOD, SUBQT, ROLQ, RORQ
329 reg1 = -16; reg2 = 15; attrflg = FU_NUM15;
333 reg1 = 0; reg2 = 31; attrflg = FU_NUM31;
336 reg1 = 1; reg2 = 32; attrflg = FU_NUM32;
340 if (parm & SUB32) attrflg |= FU_SUB32;
346 if (expr(r_expr, &eval, &eattr, &esym) != OK)
350 defined = (WORD)(eattr & DEFINED);
352 if ((challoc - ch_size) < 4)
357 fixup((WORD)(FU_WORD|attrflg), sloc, r_expr);
362 if ((int)eval < reg1 || (int)eval > reg2)
364 error("constant out of range");
370 else if (type == RI_NUM_32)
371 reg1 = (reg1 == 32) ? 0 : eval;
382 reg2 = getregister(FU_REGTWO);
384 risc_instruction_word(parm, reg1, reg2);
386 // Move Immediate - n,Rn - n in Second Word
391 if (expr(r_expr, &eval, &eattr, &esym) != OK)
394 error("malformed opcode");
399 // Opcode tracking for nop padding
400 previousop = currentop;
404 if ((previousop == 52) || (previousop == 53) && !jpad)
406 warn("NOP inserted before MOVEI instruction.");
410 tdb = (WORD)(eattr & TDB);
411 defined = (WORD)(eattr & DEFINED);
413 if ((challoc - ch_size) < 4)
418 fixup(FU_LONG|FU_MOVEI, sloc + 2, r_expr);
425 rmark(cursect, sloc + 2, tdb, MLONG|MMOVEI, NULL);
431 // Store the defined flags and value of the movei when used in mjump
434 mjump_defined = defined;
443 reg2 = getregister(FU_REGTWO);
445 D_word((((parm & 0x3F) << 10) + reg2));
446 val = ((val >> 16) & 0x0000FFFF) | ((val << 16) & 0xFFFF0000);
449 case RI_MOVE: // PC,Rd or Rs,Rd
459 reg1 = getregister(FU_REGONE);
463 reg2 = getregister(FU_REGTWO);
465 risc_instruction_word(parm, reg1, reg2);
467 // (Rn),Rn = 41 / (R14/R15+n),Rn = 43/44 / (R14/R15+Rn),Rn = 58/59
476 if ((*tok == KW_R14 || *tok == KW_R15) && (*(tok+1) != ')'))
477 indexed = (*tok - KW_R0);
481 sy = lookup((char *)tok[1], LABEL, 0);
488 if (sy->sattre & EQUATEDREG)
490 if (((sy->svalue & 0x1F) == 14 || (sy->svalue & 0x1F) == 15)
491 && (*(tok+2) != ')'))
493 indexed = (sy->svalue & 0x1F);
501 reg1 = getregister(FU_REGONE);
511 parm = (WORD)(reg1 - 14 + 58);
514 if (*tok >= KW_R0 && *tok <= KW_R31)
521 sy = lookup((char *)tok[1], LABEL, 0);
529 if (sy->sattre & EQUATEDREG)
537 reg1 = getregister(FU_REGONE);
541 if (expr(r_expr, &eval, &eattr, &esym) != OK)
547 tdb = (WORD)(eattr & TDB);
548 defined = (WORD)(eattr & DEFINED);
550 if ((challoc - ch_size) < 4)
555 error("constant expected");
557 //fixup(FU_WORD|FU_REGONE, sloc, r_expr);
566 reg1 = 14 + (parm - 58);
568 warn("NULL offset removed");
572 if (reg1 < 1 || reg1 > 32)
574 error("constant out of range");
581 parm = (WORD)(parm - 58 + 43);
589 reg1 = getregister(FU_REGONE);
598 reg2 = getregister(FU_REGTWO);
600 risc_instruction_word(parm, reg1, reg2);
602 // Rn,(Rn) = 47 / Rn,(R14/R15+n) = 49/50 / Rn,(R14/R15+Rn) = 60/61
605 reg1 = getregister(FU_REGONE);
608 if (*tok != '(') goto malformed;
613 if ((*tok == KW_R14 || *tok == KW_R15) && (*(tok+1) != ')'))
614 indexed = (*tok - KW_R0);
618 sy = lookup((char *)tok[1], LABEL, 0);
626 if (sy->sattre & EQUATEDREG)
628 if (((sy->svalue & 0x1F) == 14 || (sy->svalue & 0x1F) == 15)
629 && (*(tok+2) != ')'))
631 indexed = (sy->svalue & 0x1F);
639 reg2 = getregister(FU_REGTWO);
649 parm = (WORD)(reg2 - 14 + 60);
652 if (*tok >= KW_R0 && *tok <= KW_R31)
659 sy = lookup((char *)tok[1], LABEL, 0);
667 if (sy->sattre & EQUATEDREG)
675 reg2 = getregister(FU_REGTWO);
679 if (expr(r_expr, &eval, &eattr, &esym) != OK)
685 tdb = (WORD)(eattr & TDB);
686 defined = (WORD)(eattr & DEFINED);
688 if ((challoc - ch_size) < 4)
693 fixup(FU_WORD|FU_REGTWO, sloc, r_expr);
702 reg2 = 14 + (parm - 60);
704 warn("NULL offset removed");
708 if (reg2 < 1 || reg2 > 32)
710 error("constant out of range");
717 parm = (WORD)(parm - 60 + 49);
725 reg2 = getregister(FU_REGTWO);
734 risc_instruction_word(parm, reg2, reg1);
736 // LOADB/LOADP/LOADW (Rn),Rn
742 reg1 = getregister(FU_REGONE);
749 reg2 = getregister(FU_REGTWO);
751 risc_instruction_word(parm, reg1, reg2);
753 // STOREB/STOREP/STOREW Rn,(Rn)
755 reg1 = getregister(FU_REGONE);
762 reg2 = getregister(FU_REGTWO);
769 risc_instruction_word(parm, reg2, reg1);
771 case RI_JR: // Jump Relative - cc,n - n=-16..+15 words, reg2=cc
772 case RI_JUMP: // Jump Absolute - cc,(Rs) - reg2=cc
773 // Check to see if there is a comma in the token string. If not then the JR or JUMP should
774 // default to 0, Jump Always
784 { // Comma present in token string
786 { // CC using a constant number
792 else if (*tok == SYMBOL)
796 for(i=0; i<MAXINTERNCC; i++)
798 strcpy(scratch, (char *)tok[1]);
801 if (!strcmp(condname[i], scratch))
807 ccsym = lookup((char *)tok[1], LABEL, 0);
809 if (ccsym && (ccsym->sattre & EQUATEDCC) && !(ccsym->sattre & UNDEF_CC))
815 error("unknown condition code");
823 else if (*tok == '(')
825 val = 0; // Jump always
830 val = 0; // Jump always
833 if (val < 0 || val > 31)
835 error("condition constant out of range");
840 reg1 = val; // Store condition code
845 if (expr(r_expr, &eval, &eattr, &esym) != OK)
849 tdb = (WORD)(eattr & TDB);
850 defined = (WORD)(eattr & DEFINED);
852 if ((challoc - ch_size) < 4)
859 fixup(FU_WORD|FU_MJR, sloc, r_expr);
863 fixup(FU_WORD|FU_JR, sloc, r_expr);
874 reg2 = ((int)(val - (orgaddr + 2))) / 2;
875 if ((reg2 < -16) || (reg2 > 15))
876 error("PC relative overflow");
881 reg2 = ((int)(val - (sloc + 2))) / 2;
882 if ((reg2 < -16) || (reg2 > 15))
883 error("PC relative overflow");
892 if (((locptr >= 0xF03000) && (locptr < 0xF04000)
893 && (val < 0xF03000)) || ((val >= 0xF03000)
894 && (val < 0xF04000) && (locptr < 0xF03000)))
896 warn("* cannot jump relative between main memory and local gpu ram");
900 page_jump = (locptr & 0xFFFFFF00) - (val & 0xFFFFFF00);
906 warn("* destination address not aligned for long page jump relative, "
907 "insert a \'nop\' before the destination label/address");
914 warn("* destination address not aligned for short page jump relative, "
915 "insert a \'nop\' before the destination label/address");
923 risc_instruction_word(parm, reg2, reg1);
931 reg2 = getregister(FU_REGTWO);
943 warn("* \'jump\' is not recommended for .gpumain as destination addresses "
944 "cannot be validated for alignment, use \'mjump\'");
945 locptr = (orgactive) ? orgaddr : sloc;
949 warn("* source address not aligned for long or short jump, "
950 "insert a \'nop\' before the \'jump\'");
957 locptr = (orgactive) ? orgaddr : sloc;
958 page_jump = (locptr & 0xFFFFFF00) - (mjump_dest & 0xFFFFFF00);
964 warn("* destination address not aligned for long page jump, "
965 "insert a \'nop\' before the destination label/address");
970 if (!(mjump_dest & 0x0000000F) || ((mjump_dest - 2) % 4))
972 warn("* destination address not aligned for short page jump, "
973 "insert a \'nop\' before the destination label/address");
979 locptr = (orgactive) ? orgaddr : sloc;
980 fwdjump[fwindex++] = locptr;
985 risc_instruction_word(parm, reg2, reg1);
989 // Should never get here :D
991 error("unknown risc opcode type");