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
28 //unsigned previousop = 0; // Used for NOP padding checks
29 //unsigned currentop = 0; // Used for NOP padding checks
31 char reg_err[] = "missing register R0...R31";
33 // Jaguar Jump Condition Names
34 char condname[MAXINTERNCC][5] = {
35 "NZ", "Z", "NC", "NCNZ", "NCZ", "C", "CNZ", "CZ", "NN", "NNNZ", "NNZ",
36 "N", "N_NZ", "N_Z ", "T", "A", "NE", "EQ", "CC", "HS", "HI", "CS", "LO",
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 }
111 // Convert a String to Uppercase
113 void strtoupper(char * s)
117 *s = (char)(toupper(*s));
124 // Build RISC Instruction Word
126 void risc_instruction_word(unsigned short parm, int reg1, int reg2)
128 // int value = 0xE400;
130 // Opcode tracking for nop padding
131 // previousop = currentop;
134 if (!orgwarning && !orgactive)
136 // Check for absolute address setting
137 // if (!orgactive && !in_main)
139 warn("GPU/DSP code outside of absolute section");
148 if (((previousop == 52) || (previousop == 53)) && (currentop != 57))
149 D_word(value); // Insert NOP
154 if ((previousop == 52) || (previousop == 53))
159 warn("NOP inserted before MOVEI instruction.");
163 warn("NOP inserted before JR instruction.");
167 warn("NOP inserted before JUMP instruction.");
171 warn("NOP inserted before MOVE PC instruction.");
182 if ((previousop != 18) && (previousop != 20))
183 error("IMULTN/IMACN instruction must preceed IMACN instruction");
188 if (previousop != 20)
189 error("IMACN instruction must preceed RESMAC instruction");
193 int value = ((parm & 0x3F) << 10) + ((reg1 & 0x1F) << 5) + (reg2 & 0x1F);
199 // Get a RISC Register
201 int getregister(WORD rattr)
203 VALUE eval; // Expression value
204 WORD eattr; // Expression attributes
205 SYM * esym; // External symbol involved in expr.
206 TOKEN r_expr[EXPRSIZE]; // Expression token list
208 // Evaluate what's in the global "tok" buffer
209 if (expr(r_expr, &eval, &eattr, &esym) != OK)
211 error("Malformed opcode");
215 if ((challoc - ch_size) < 4)
218 if (!(eattr & DEFINED))
220 fixup((WORD)(FU_WORD | rattr), sloc, r_expr);
224 // If we got a register in range (0-31), return it
225 if ((eval >= 0) && (eval <= 31))
228 // Otherwise, it's out of range & we flag an error
235 // Do RISC Code Generation
237 int risccg(int state)
239 // unsigned short parm; // Opcode parameters
240 // unsigned type; // Opcode type
241 int reg1; // Register 1
242 int reg2; // Register 2
243 int val = 0; // Constructed value
250 unsigned locptr = 0; // Address location pointer
251 unsigned page_jump = 0; // Memory page jump flag
252 VALUE eval; // Expression value
253 WORD eattr; // Expression attributes
254 SYM * esym; // External symbol involved in expr.
255 TOKEN r_expr[EXPRSIZE]; // Expression token list
256 WORD defined; // Symbol defined flag
258 int indexed; // Indexed register flag
260 // Get opcode parameter and type
261 unsigned short parm = (WORD)(roptbl[state - 3000].parm);
262 unsigned type = roptbl[state - 3000].typ;
264 // Detect whether the opcode parmeter passed determines that the opcode is
265 // specific to only one of the RISC processors and ensure it is legal in
266 // the current code section. If not then error and return.
267 if (((parm & GPUONLY) && rdsp) || ((parm & DSPONLY) && rgpu))
269 error("Opcode is not valid in this code section");
273 // Process RISC opcode
276 // No operand instructions
279 risc_instruction_word(parm, 0, 0);
282 // Single operand instructions (Rd)
283 // ABS, MIRROR, NEG, NOT, PACK, RESMAC, SAT8, SAT16, SAT16S, SAT24, SAT32S, UNPACK
285 reg2 = getregister(FU_REGTWO);
287 risc_instruction_word(parm, parm >> 6, reg2);
290 // Two operand instructions (Rs,Rd)
291 // ADD, ADDC, AND, CMP, DIV, IMACN, IMULT, IMULTN, MOVEFA, MOVETA, MULT, MMULT,
292 // MTOI, NORMI, OR, ROR, SH, SHA, SUB, SUBC, XOR
295 altbankok = 1; // MOVEFA
297 reg1 = getregister(FU_REGONE);
301 altbankok = 1; // MOVETA
303 reg2 = getregister(FU_REGTWO);
305 risc_instruction_word(parm, reg1, reg2);
308 // Numeric operand (n,Rd) where n = -16..+15
312 // Numeric operand (n,Rd) where n = 0..31
313 // BCLR, BSET, BTST, MOVEQ
316 // Numeric operand (n,Rd) where n = 1..32
317 // ADDQ, ADDQMOD, ADDQT, SHARQ, SHLQ, SHRQ, SUBQ, SUBQMOD, SUBQT, ROLQ, RORQ
322 reg1 = -16; reg2 = 15; attrflg = FU_NUM15;
326 reg1 = 0; reg2 = 31; attrflg = FU_NUM31;
329 reg1 = 1; reg2 = 32; attrflg = FU_NUM32;
333 if (parm & SUB32) attrflg |= FU_SUB32;
339 if (expr(r_expr, &eval, &eattr, &esym) != OK)
343 defined = (WORD)(eattr & DEFINED);
345 if ((challoc - ch_size) < 4)
350 fixup((WORD)(FU_WORD | attrflg), sloc, r_expr);
355 if ((int)eval < reg1 || (int)eval > reg2)
357 error("constant out of range");
363 else if (type == RI_NUM_32)
364 reg1 = (reg1 == 32 ? 0 : eval);
375 reg2 = getregister(FU_REGTWO);
377 risc_instruction_word(parm, reg1, reg2);
380 // Move Immediate--n,Rn--n in Second Word
388 if (expr(r_expr, &eval, &eattr, &esym) != OK)
391 error("malformed opcode");
396 // Opcode tracking for nop padding
397 // previousop = currentop;
402 if ((previousop == 52) || (previousop == 53) && !jpad)
404 warn("NOP inserted before MOVEI instruction.");
409 tdb = (WORD)(eattr & TDB);
410 defined = (WORD)(eattr & DEFINED);
412 if ((challoc - ch_size) < 4)
417 fixup(FU_LONG | FU_MOVEI, sloc + 2, r_expr);
423 rmark(cursect, sloc + 2, tdb, MLONG | MMOVEI, NULL);
429 // Store the defined flags and value of the movei when used in mjump
432 mjump_defined = defined;
442 reg2 = getregister(FU_REGTWO);
444 D_word((((parm & 0x3F) << 10) + reg2));
445 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);
468 // (Rn),Rn = 41 / (R14/R15+n),Rn = 43/44 / (R14/R15+Rn),Rn = 58/59
478 if ((*tok == KW_R14 || *tok == KW_R15) && (*(tok+1) != ')'))
479 indexed = (*tok - KW_R0);
483 // sy = lookup((char *)tok[1], LABEL, 0);
484 sy = lookup(string[tok[1]], LABEL, 0);
492 if (sy->sattre & EQUATEDREG)
494 if (((sy->svalue & 0x1F) == 14 || (sy->svalue & 0x1F) == 15)
495 && (*(tok+2) != ')'))
497 indexed = (sy->svalue & 0x1F);
505 reg1 = getregister(FU_REGONE);
515 parm = (WORD)(reg1 - 14 + 58);
518 if (*tok >= KW_R0 && *tok <= KW_R31)
525 // sy = lookup((char *)tok[1], LABEL, 0);
526 sy = lookup(string[tok[1]], LABEL, 0);
534 if (sy->sattre & EQUATEDREG)
542 reg1 = getregister(FU_REGONE);
546 if (expr(r_expr, &eval, &eattr, &esym) != OK)
552 tdb = (WORD)(eattr & TDB);
553 defined = (WORD)(eattr & DEFINED);
555 if ((challoc - ch_size) < 4)
560 error("constant expected");
562 //fixup(FU_WORD|FU_REGONE, sloc, r_expr);
571 reg1 = 14 + (parm - 58);
573 warn("NULL offset removed");
577 if (reg1 < 1 || reg1 > 32)
579 error("constant out of range");
586 parm = (WORD)(parm - 58 + 43);
594 reg1 = getregister(FU_REGONE);
603 reg2 = getregister(FU_REGTWO);
605 risc_instruction_word(parm, reg1, reg2);
608 // Rn,(Rn) = 47 / Rn,(R14/R15+n) = 49/50 / Rn,(R14/R15+Rn) = 60/61
611 reg1 = getregister(FU_REGONE);
614 if (*tok != '(') goto malformed;
619 if ((*tok == KW_R14 || *tok == KW_R15) && (*(tok + 1) != ')'))
620 indexed = (*tok - KW_R0);
624 // sy = lookup((char *)tok[1], LABEL, 0);
625 sy = lookup(string[tok[1]], LABEL, 0);
633 if (sy->sattre & EQUATEDREG)
635 if (((sy->svalue & 0x1F) == 14 || (sy->svalue & 0x1F) == 15)
636 && (*(tok + 2) != ')'))
638 indexed = (sy->svalue & 0x1F);
646 reg2 = getregister(FU_REGTWO);
656 parm = (WORD)(reg2 - 14 + 60);
659 if (*tok >= KW_R0 && *tok <= KW_R31)
666 // sy = lookup((char *)tok[1], LABEL, 0);
667 sy = lookup(string[tok[1]], LABEL, 0);
675 if (sy->sattre & EQUATEDREG)
683 reg2 = getregister(FU_REGTWO);
687 if (expr(r_expr, &eval, &eattr, &esym) != OK)
693 tdb = (WORD)(eattr & TDB);
694 defined = (WORD)(eattr & DEFINED);
696 if ((challoc - ch_size) < 4)
701 fixup(FU_WORD | FU_REGTWO, sloc, r_expr);
710 reg2 = 14 + (parm - 60);
712 warn("NULL offset removed");
716 if (reg2 < 1 || reg2 > 32)
718 error("constant out of range");
725 parm = (WORD)(parm - 60 + 49);
733 reg2 = getregister(FU_REGTWO);
742 risc_instruction_word(parm, reg2, reg1);
745 // LOADB/LOADP/LOADW (Rn),Rn
751 reg1 = getregister(FU_REGONE);
758 reg2 = getregister(FU_REGTWO);
760 risc_instruction_word(parm, reg1, reg2);
763 // STOREB/STOREP/STOREW Rn,(Rn)
765 reg1 = getregister(FU_REGONE);
772 reg2 = getregister(FU_REGTWO);
779 risc_instruction_word(parm, reg2, reg1);
782 case RI_JR: // Jump Relative - cc,n - n=-16..+15 words, reg2=cc
783 case RI_JUMP: // Jump Absolute - cc,(Rs) - reg2=cc
784 // Check to see if there is a comma in the token string. If not then
785 // the JR or JUMP should default to 0, Jump Always
800 // Comma present in token string
803 // CC using a constant number
809 else if (*tok == SYMBOL)
812 // strcpy(scratch, (char *)tok[1]);
813 strcpy(scratch, string[tok[1]]);
816 for(i=0; i<MAXINTERNCC; i++)
818 // Look for the condition code & break if found
819 if (strcmp(condname[i], scratch) == 0)
826 // Standard CC was not found, look for an equated one
829 // ccsym = lookup((char *)tok[1], LABEL, 0);
830 ccsym = lookup(string[tok[1]], LABEL, 0);
832 if (ccsym && (ccsym->sattre & EQUATEDCC) && !(ccsym->sattre & UNDEF_CC))
838 error("unknown condition code");
846 else if (*tok == '(')
858 if (val < 0 || val > 31)
860 error("condition constant out of range");
865 // Store condition code
872 if (expr(r_expr, &eval, &eattr, &esym) != OK)
876 tdb = (WORD)(eattr & TDB);
877 defined = (WORD)(eattr & DEFINED);
879 if ((challoc - ch_size) < 4)
887 fixup(FU_WORD|FU_MJR, sloc, r_expr);
892 fixup(FU_WORD | FU_JR, sloc, r_expr);
903 reg2 = ((int)(val - (orgaddr + 2))) / 2;
905 if ((reg2 < -16) || (reg2 > 15))
906 error("PC relative overflow");
912 reg2 = ((int)(val - (sloc + 2))) / 2;
914 if ((reg2 < -16) || (reg2 > 15))
915 error("PC relative overflow");
926 if (((locptr >= 0xF03000) && (locptr < 0xF04000)
927 && (val < 0xF03000)) || ((val >= 0xF03000)
928 && (val < 0xF04000) && (locptr < 0xF03000)))
930 warn("* cannot jump relative between main memory and local gpu ram");
934 page_jump = (locptr & 0xFFFFFF00) - (val & 0xFFFFFF00);
940 warn("* destination address not aligned for long page jump relative, "
941 "insert a \'nop\' before the destination label/address");
948 warn("* destination address not aligned for short page jump relative, "
949 "insert a \'nop\' before the destination label/address");
958 risc_instruction_word(parm, reg2, reg1);
967 reg2 = getregister(FU_REGTWO);
980 warn("* \'jump\' is not recommended for .gpumain as destination addresses "
981 "cannot be validated for alignment, use \'mjump\'");
982 locptr = (orgactive) ? orgaddr : sloc;
986 warn("* source address not aligned for long or short jump, "
987 "insert a \'nop\' before the \'jump\'");
994 locptr = (orgactive) ? orgaddr : sloc;
995 page_jump = (locptr & 0xFFFFFF00) - (mjump_dest & 0xFFFFFF00);
1001 warn("* destination address not aligned for long page jump, "
1002 "insert a \'nop\' before the destination label/address");
1007 if (!(mjump_dest & 0x0000000F) || ((mjump_dest - 2) % 4))
1009 warn("* destination address not aligned for short page jump, "
1010 "insert a \'nop\' before the destination label/address");
1016 locptr = (orgactive) ? orgaddr : sloc;
1017 fwdjump[fwindex++] = locptr;
1023 risc_instruction_word(parm, reg2, reg1);
1027 // Should never get here :D
1029 error("unknown risc opcode type");