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
277 // specific to only one of the RISC processors and ensure it is legal in
278 // the current code section. 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
477 if ((*tok == KW_R14 || *tok == KW_R15) && (*(tok+1) != ')'))
478 indexed = (*tok - KW_R0);
482 // sy = lookup((char *)tok[1], LABEL, 0);
483 sy = lookup(string[tok[1]], LABEL, 0);
491 if (sy->sattre & EQUATEDREG)
493 if (((sy->svalue & 0x1F) == 14 || (sy->svalue & 0x1F) == 15)
494 && (*(tok+2) != ')'))
496 indexed = (sy->svalue & 0x1F);
504 reg1 = getregister(FU_REGONE);
514 parm = (WORD)(reg1 - 14 + 58);
517 if (*tok >= KW_R0 && *tok <= KW_R31)
524 // sy = lookup((char *)tok[1], LABEL, 0);
525 sy = lookup(string[tok[1]], LABEL, 0);
533 if (sy->sattre & EQUATEDREG)
541 reg1 = getregister(FU_REGONE);
545 if (expr(r_expr, &eval, &eattr, &esym) != OK)
551 tdb = (WORD)(eattr & TDB);
552 defined = (WORD)(eattr & DEFINED);
554 if ((challoc - ch_size) < 4)
559 error("constant expected");
561 //fixup(FU_WORD|FU_REGONE, sloc, r_expr);
570 reg1 = 14 + (parm - 58);
572 warn("NULL offset removed");
576 if (reg1 < 1 || reg1 > 32)
578 error("constant out of range");
585 parm = (WORD)(parm - 58 + 43);
593 reg1 = getregister(FU_REGONE);
602 reg2 = getregister(FU_REGTWO);
604 risc_instruction_word(parm, reg1, reg2);
606 // Rn,(Rn) = 47 / Rn,(R14/R15+n) = 49/50 / Rn,(R14/R15+Rn) = 60/61
609 reg1 = getregister(FU_REGONE);
612 if (*tok != '(') goto malformed;
617 if ((*tok == KW_R14 || *tok == KW_R15) && (*(tok+1) != ')'))
618 indexed = (*tok - KW_R0);
622 // sy = lookup((char *)tok[1], LABEL, 0);
623 sy = lookup(string[tok[1]], LABEL, 0);
631 if (sy->sattre & EQUATEDREG)
633 if (((sy->svalue & 0x1F) == 14 || (sy->svalue & 0x1F) == 15)
634 && (*(tok+2) != ')'))
636 indexed = (sy->svalue & 0x1F);
644 reg2 = getregister(FU_REGTWO);
654 parm = (WORD)(reg2 - 14 + 60);
657 if (*tok >= KW_R0 && *tok <= KW_R31)
664 // sy = lookup((char *)tok[1], LABEL, 0);
665 sy = lookup(string[tok[1]], LABEL, 0);
673 if (sy->sattre & EQUATEDREG)
681 reg2 = getregister(FU_REGTWO);
685 if (expr(r_expr, &eval, &eattr, &esym) != OK)
691 tdb = (WORD)(eattr & TDB);
692 defined = (WORD)(eattr & DEFINED);
694 if ((challoc - ch_size) < 4)
699 fixup(FU_WORD|FU_REGTWO, sloc, r_expr);
708 reg2 = 14 + (parm - 60);
710 warn("NULL offset removed");
714 if (reg2 < 1 || reg2 > 32)
716 error("constant out of range");
723 parm = (WORD)(parm - 60 + 49);
731 reg2 = getregister(FU_REGTWO);
740 risc_instruction_word(parm, reg2, reg1);
742 // LOADB/LOADP/LOADW (Rn),Rn
748 reg1 = getregister(FU_REGONE);
755 reg2 = getregister(FU_REGTWO);
757 risc_instruction_word(parm, reg1, reg2);
759 // STOREB/STOREP/STOREW Rn,(Rn)
761 reg1 = getregister(FU_REGONE);
768 reg2 = getregister(FU_REGTWO);
775 risc_instruction_word(parm, reg2, reg1);
777 case RI_JR: // Jump Relative - cc,n - n=-16..+15 words, reg2=cc
778 case RI_JUMP: // Jump Absolute - cc,(Rs) - reg2=cc
779 // Check to see if there is a comma in the token string. If not then the JR or JUMP should
780 // default to 0, Jump Always
790 { // Comma present in token string
792 { // CC using a constant number
798 else if (*tok == SYMBOL)
802 for(i=0; i<MAXINTERNCC; i++)
804 // strcpy(scratch, (char *)tok[1]);
805 strcpy(scratch, string[tok[1]]);
808 if (!strcmp(condname[i], scratch))
814 // ccsym = lookup((char *)tok[1], LABEL, 0);
815 ccsym = lookup(string[tok[1]], LABEL, 0);
817 if (ccsym && (ccsym->sattre & EQUATEDCC) && !(ccsym->sattre & UNDEF_CC))
823 error("unknown condition code");
831 else if (*tok == '(')
833 val = 0; // Jump always
838 val = 0; // Jump always
841 if (val < 0 || val > 31)
843 error("condition constant out of range");
848 reg1 = val; // Store condition code
853 if (expr(r_expr, &eval, &eattr, &esym) != OK)
857 tdb = (WORD)(eattr & TDB);
858 defined = (WORD)(eattr & DEFINED);
860 if ((challoc - ch_size) < 4)
867 fixup(FU_WORD|FU_MJR, sloc, r_expr);
871 fixup(FU_WORD|FU_JR, sloc, r_expr);
882 reg2 = ((int)(val - (orgaddr + 2))) / 2;
883 if ((reg2 < -16) || (reg2 > 15))
884 error("PC relative overflow");
889 reg2 = ((int)(val - (sloc + 2))) / 2;
890 if ((reg2 < -16) || (reg2 > 15))
891 error("PC relative overflow");
900 if (((locptr >= 0xF03000) && (locptr < 0xF04000)
901 && (val < 0xF03000)) || ((val >= 0xF03000)
902 && (val < 0xF04000) && (locptr < 0xF03000)))
904 warn("* cannot jump relative between main memory and local gpu ram");
908 page_jump = (locptr & 0xFFFFFF00) - (val & 0xFFFFFF00);
914 warn("* destination address not aligned for long page jump relative, "
915 "insert a \'nop\' before the destination label/address");
922 warn("* destination address not aligned for short page jump relative, "
923 "insert a \'nop\' before the destination label/address");
931 risc_instruction_word(parm, reg2, reg1);
939 reg2 = getregister(FU_REGTWO);
951 warn("* \'jump\' is not recommended for .gpumain as destination addresses "
952 "cannot be validated for alignment, use \'mjump\'");
953 locptr = (orgactive) ? orgaddr : sloc;
957 warn("* source address not aligned for long or short jump, "
958 "insert a \'nop\' before the \'jump\'");
965 locptr = (orgactive) ? orgaddr : sloc;
966 page_jump = (locptr & 0xFFFFFF00) - (mjump_dest & 0xFFFFFF00);
972 warn("* destination address not aligned for long page jump, "
973 "insert a \'nop\' before the destination label/address");
978 if (!(mjump_dest & 0x0000000F) || ((mjump_dest - 2) % 4))
980 warn("* destination address not aligned for short page jump, "
981 "insert a \'nop\' before the destination label/address");
987 locptr = (orgactive) ? orgaddr : sloc;
988 fwdjump[fwindex++] = locptr;
993 risc_instruction_word(parm, reg2, reg1);
997 // Should never get here :D
999 error("unknown risc opcode type");