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
25 unsigned altbankok = 0; // Ok to use alternate register bank
26 unsigned orgactive = 0; // RISC org directive active
27 unsigned orgaddr = 0; // Org'd address
28 unsigned orgwarning = 0; // Has an ORG warning been issued
30 char reg_err[] = "missing register R0...R31";
32 // Jaguar Jump Condition Names
33 char condname[MAXINTERNCC][5] = {
34 "NZ", "Z", "NC", "NCNZ", "NCZ", "C", "CNZ", "CZ", "NN", "NNNZ", "NNZ",
35 "N", "N_NZ", "N_Z ", "T", "A", "NE", "EQ", "CC", "HS", "HI", "CS", "LO",
39 // Jaguar Jump Condition Numbers
41 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
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 }
112 // Convert a String to Uppercase
114 void strtoupper(char * s)
119 *s = (char)(toupper(*s));
130 // Function to return "malformed expression" error
131 // This is done mainly to remove a bunch of GOTO statements in the parser
133 static inline int MalformedOpcode(int signal)
136 sprintf(buf, "%02X", signal);
137 errors("Malformed opcode [internal $%s]", buf);
143 // Build RISC Instruction Word
145 void BuildRISCIntructionWord(unsigned short opcode, int reg1, int reg2)
147 // Check for absolute address setting
148 if (!orgwarning && !orgactive)
150 warn("GPU/DSP code outside of absolute section");
154 int value = ((opcode & 0x3F) << 10) + ((reg1 & 0x1F) << 5) + (reg2 & 0x1F);
160 // Get a RISC Register
162 int GetRegister(WORD rattr)
164 VALUE eval; // Expression value
165 WORD eattr; // Expression attributes
166 SYM * esym; // External symbol involved in expr.
167 TOKEN r_expr[EXPRSIZE]; // Expression token list
169 // Evaluate what's in the global "tok" buffer
170 if (expr(r_expr, &eval, &eattr, &esym) != OK)
171 // Hmm, the evaluator should report the error to us...
172 // return MalformedOpcode(0x00);
175 if ((challoc - ch_size) < 4)
178 if (!(eattr & DEFINED))
180 fixup((WORD)(FU_WORD | rattr), sloc, r_expr);
184 // If we got a register in range (0-31), return it
185 if ((eval >= 0) && (eval <= 31))
188 // Otherwise, it's out of range & we flag an error
195 // Do RISC Code Generation
197 int GenerateRISCCode(int state)
199 int reg1; // Register 1
200 int reg2; // Register 2
201 int val = 0; // Constructed value
208 int indexed; // Indexed register flag
210 VALUE eval; // Expression value
211 WORD eattr; // Expression attributes
212 SYM * esym; // External symbol involved in expr.
213 TOKEN r_expr[EXPRSIZE]; // Expression token list
215 // Get opcode parameter and type
216 unsigned short parm = (WORD)(roptbl[state - 3000].parm);
217 unsigned type = roptbl[state - 3000].typ;
219 // Detect whether the opcode parmeter passed determines that the opcode is
220 // specific to only one of the RISC processors and ensure it is legal in
221 // the current code section. If not then show error and return.
222 if (((parm & GPUONLY) && rdsp) || ((parm & DSPONLY) && rgpu))
224 error("Opcode is not valid in this code section");
228 // Process RISC opcode
231 // No operand instructions
234 BuildRISCIntructionWord(parm, 0, 0);
237 // Single operand instructions (Rd)
238 // ABS, MIRROR, NEG, NOT, PACK, RESMAC, SAT8, SAT16, SAT16S, SAT24, SAT32S, UNPACK
240 reg2 = GetRegister(FU_REGTWO);
242 BuildRISCIntructionWord(parm, parm >> 6, reg2);
245 // Two operand instructions (Rs,Rd)
246 // ADD, ADDC, AND, CMP, DIV, IMACN, IMULT, IMULTN, MOVEFA, MOVETA, MULT, MMULT,
247 // MTOI, NORMI, OR, ROR, SH, SHA, SUB, SUBC, XOR
250 altbankok = 1; // MOVEFA
252 reg1 = GetRegister(FU_REGONE);
256 altbankok = 1; // MOVETA
258 reg2 = GetRegister(FU_REGTWO);
260 BuildRISCIntructionWord(parm, reg1, reg2);
263 // Numeric operand (n,Rd) where n = -16..+15
267 // Numeric operand (n,Rd) where n = 0..31
268 // BCLR, BSET, BTST, MOVEQ
271 // Numeric operand (n,Rd) where n = 1..32
272 // ADDQ, ADDQMOD, ADDQT, SHARQ, SHLQ, SHRQ, SUBQ, SUBQMOD, SUBQT, ROLQ, RORQ
277 reg1 = -16; reg2 = 15; attrflg = FU_NUM15;
281 reg1 = 0; reg2 = 31; attrflg = FU_NUM31;
284 reg1 = 1; reg2 = 32; attrflg = FU_NUM32;
292 return MalformedOpcode(0x01);
296 if (expr(r_expr, &eval, &eattr, &esym) != OK)
297 return MalformedOpcode(0x02);
299 if ((challoc - ch_size) < 4)
302 if (!(eattr & DEFINED))
304 fixup((WORD)(FU_WORD | attrflg), sloc, r_expr);
309 if ((int)eval < reg1 || (int)eval > reg2)
311 error("constant out of range");
317 else if (type == RI_NUM_32)
318 reg1 = (reg1 == 32 ? 0 : eval);
324 reg2 = GetRegister(FU_REGTWO);
326 BuildRISCIntructionWord(parm, reg1, reg2);
329 // Move Immediate--n,Rn--n in Second Word
332 return MalformedOpcode(0x03);
336 if (expr(r_expr, &eval, &eattr, &esym) != OK)
337 return MalformedOpcode(0x04);
339 if ((challoc - ch_size) < 4)
342 if (!(eattr & DEFINED))
344 fixup(FU_LONG | FU_MOVEI, sloc + 2, r_expr);
351 //printf("risca: Doing rmark for RI_MOVEI (tdb=$%X)...\n", eattr & TDB);
352 rmark(cursect, sloc + 2, (eattr & TDB), (MLONG | MMOVEI), NULL);
356 val = ((eval >> 16) & 0x0000FFFF) | ((eval << 16) & 0xFFFF0000);
358 reg2 = GetRegister(FU_REGTWO);
360 D_word((((parm & 0x3F) << 10) + reg2));
375 reg1 = GetRegister(FU_REGONE);
379 reg2 = GetRegister(FU_REGTWO);
381 BuildRISCIntructionWord(parm, reg1, reg2);
384 // (Rn),Rn = 41 / (R14/R15+n),Rn = 43/44 / (R14/R15+Rn),Rn = 58/59
390 return MalformedOpcode(0x05);
394 if ((*tok == KW_R14 || *tok == KW_R15) && (*(tok + 1) != ')'))
395 indexed = (*tok - KW_R0);
399 // sy = lookup((char *)tok[1], LABEL, 0);
400 sy = lookup(string[tok[1]], LABEL, 0);
408 if (sy->sattre & EQUATEDREG)
410 if (((sy->svalue & 0x1F) == 14 || (sy->svalue & 0x1F) == 15)
411 && (*(tok + 2) != ')'))
413 indexed = (sy->svalue & 0x1F);
421 reg1 = GetRegister(FU_REGONE);
431 parm = (WORD)(reg1 - 14 + 58);
434 if (*tok >= KW_R0 && *tok <= KW_R31)
439 // sy = lookup((char *)tok[1], LABEL, 0);
440 sy = lookup(string[tok[1]], LABEL, 0);
448 if (sy->sattre & EQUATEDREG)
454 reg1 = GetRegister(FU_REGONE);
458 if (expr(r_expr, &eval, &eattr, &esym) != OK)
459 return MalformedOpcode(0x06);
461 if ((challoc - ch_size) < 4)
464 if (!(eattr & DEFINED))
466 error("constant expected");
474 reg1 = 14 + (parm - 58);
476 warn("NULL offset removed");
480 if (reg1 < 1 || reg1 > 32)
482 error("constant out of range");
489 parm = (WORD)(parm - 58 + 43);
495 reg1 = GetRegister(FU_REGONE);
500 return MalformedOpcode(0x07);
504 reg2 = GetRegister(FU_REGTWO);
506 BuildRISCIntructionWord(parm, reg1, reg2);
509 // Rn,(Rn) = 47 / Rn,(R14/R15+n) = 49/50 / Rn,(R14/R15+Rn) = 60/61
512 reg1 = GetRegister(FU_REGONE);
516 return MalformedOpcode(0x08);
521 if ((*tok == KW_R14 || *tok == KW_R15) && (*(tok + 1) != ')'))
522 indexed = (*tok - KW_R0);
526 // sy = lookup((char *)tok[1], LABEL, 0);
527 sy = lookup(string[tok[1]], LABEL, 0);
535 if (sy->sattre & EQUATEDREG)
537 if (((sy->svalue & 0x1F) == 14 || (sy->svalue & 0x1F) == 15)
538 && (*(tok + 2) != ')'))
540 indexed = (sy->svalue & 0x1F);
548 reg2 = GetRegister(FU_REGTWO);
558 parm = (WORD)(reg2 - 14 + 60);
561 if (*tok >= KW_R0 && *tok <= KW_R31)
566 // sy = lookup((char *)tok[1], LABEL, 0);
567 sy = lookup(string[tok[1]], LABEL, 0);
575 if (sy->sattre & EQUATEDREG)
581 reg2 = GetRegister(FU_REGTWO);
585 if (expr(r_expr, &eval, &eattr, &esym) != OK)
586 return MalformedOpcode(0x09);
588 if ((challoc - ch_size) < 4)
591 if (!(eattr & DEFINED))
593 fixup(FU_WORD | FU_REGTWO, sloc, r_expr);
602 reg2 = 14 + (parm - 60);
604 warn("NULL offset removed");
608 if (reg2 < 1 || reg2 > 32)
610 error("constant out of range");
617 parm = (WORD)(parm - 60 + 49);
624 reg2 = GetRegister(FU_REGTWO);
629 return MalformedOpcode(0x0A);
633 BuildRISCIntructionWord(parm, reg2, reg1);
636 // LOADB/LOADP/LOADW (Rn),Rn
639 return MalformedOpcode(0x0B);
642 reg1 = GetRegister(FU_REGONE);
645 return MalformedOpcode(0x0C);
649 reg2 = GetRegister(FU_REGTWO);
651 BuildRISCIntructionWord(parm, reg1, reg2);
654 // STOREB/STOREP/STOREW Rn,(Rn)
656 reg1 = GetRegister(FU_REGONE);
660 return MalformedOpcode(0x0D);
663 reg2 = GetRegister(FU_REGTWO);
666 return MalformedOpcode(0x0E);
670 BuildRISCIntructionWord(parm, reg2, reg1);
673 // Jump Relative - cc,n - n=-16..+15 words, reg2=cc
676 // Jump Absolute - cc,(Rs) - reg2=cc
678 // Check to see if there is a comma in the token string. If not then
679 // the JR or JUMP should default to 0, Jump Always
682 for(t=tok; *t!=EOL; t++)
695 // CC using a constant number
701 else if (*tok == SYMBOL)
704 // strcpy(scratch, (char *)tok[1]);
705 strcpy(scratch, string[tok[1]]);
708 for(i=0; i<MAXINTERNCC; i++)
710 // Look for the condition code & break if found
711 if (strcmp(condname[i], scratch) == 0)
718 // Standard CC was not found, look for an equated one
721 // ccsym = lookup((char *)tok[1], LABEL, 0);
722 ccsym = lookup(string[tok[1]], LABEL, 0);
724 if (ccsym && (ccsym->sattre & EQUATEDCC) && !(ccsym->sattre & UNDEF_CC))
730 error("unknown condition code");
738 else if (*tok == '(')
740 // Set CC to "Jump Always"
746 // Set CC to "Jump Always"
750 if (val < 0 || val > 31)
752 error("condition constant out of range");
756 // Store condition code
762 if (expr(r_expr, &eval, &eattr, &esym) != OK)
763 return MalformedOpcode(0x0F);
765 if ((challoc - ch_size) < 4)
768 if (!(eattr & DEFINED))
770 fixup(FU_WORD | FU_JR, sloc, r_expr);
775 reg2 = ((int)(eval - ((orgactive ? orgaddr : sloc) + 2))) / 2;
777 if ((reg2 < -16) || (reg2 > 15))
778 error("PC relative overflow");
781 BuildRISCIntructionWord(parm, reg2, reg1);
787 return MalformedOpcode(0x10);
790 reg2 = GetRegister(FU_REGTWO);
793 return MalformedOpcode(0x11);
797 BuildRISCIntructionWord(parm, reg2, reg1);
802 // Should never get here :-D
804 error("Unknown risc opcode type");